Browse files

first commit

  • Loading branch information...
0 parents commit d095b6b3510363185cc8fb2c7da2dfb522a72887 @maccman committed Jul 12, 2011
Showing with 12,717 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +32 −0 Gemfile
  3. +117 −0 Gemfile.lock
  4. +261 −0 README
  5. +7 −0 Rakefile
  6. BIN app/assets/.DS_Store
  7. BIN app/assets/images/rails.png
  8. BIN app/assets/javascripts/.DS_Store
  9. BIN app/assets/javascripts/app/.DS_Store
  10. +11 −0 app/assets/javascripts/app/app.coffee
  11. +73 −0 app/assets/javascripts/app/controllers/pages.coffee
  12. +7 −0 app/assets/javascripts/app/lib/jquery.ext.coffee
  13. +5 −0 app/assets/javascripts/app/models/page.coffee
  14. +1 −0 app/assets/javascripts/app/view/pages/show.tmpl
  15. +113 −0 app/assets/javascripts/vendor/spine.ajax.coffee
  16. +425 −0 app/assets/javascripts/vendor/spine.coffee
  17. +37 −0 app/assets/javascripts/vendor/spine.list.coffee
  18. +18 −0 app/assets/javascripts/vendor/spine.local.coffee
  19. +64 −0 app/assets/javascripts/vendor/spine.manager.coffee
  20. +104 −0 app/assets/javascripts/vendor/spine.relation.coffee
  21. +136 −0 app/assets/javascripts/vendor/spine.route.coffee
  22. +55 −0 app/assets/javascripts/vendor/spine.tabs.coffee
  23. +12 −0 app/assets/javascripts/vendor/spine.tmpl.coffee
  24. +7 −0 app/assets/stylesheets/application.css
  25. +5 −0 app/assets/stylesheets/pages.css.scss
  26. +3 −0 app/controllers/application_controller.rb
  27. +46 −0 app/controllers/pages_controller.rb
  28. +2 −0 app/helpers/application_helper.rb
  29. +2 −0 app/helpers/pages_helper.rb
  30. 0 app/mailers/.gitkeep
  31. 0 app/models/.gitkeep
  32. +3 −0 app/models/page.rb
  33. +24 −0 app/views/pages/index.html.erb
  34. +4 −0 config.ru
  35. +49 −0 config/application.rb
  36. +6 −0 config/boot.rb
  37. +25 −0 config/database.yml
  38. +5 −0 config/environment.rb
  39. +24 −0 config/environments/development.rb
  40. +52 −0 config/environments/production.rb
  41. +39 −0 config/environments/test.rb
  42. +7 −0 config/initializers/backtrace_silencers.rb
  43. +10 −0 config/initializers/inflections.rb
  44. +5 −0 config/initializers/mime_types.rb
  45. +7 −0 config/initializers/secret_token.rb
  46. +8 −0 config/initializers/session_store.rb
  47. +17 −0 config/initializers/stitch.rb
  48. +12 −0 config/initializers/wrap_parameters.rb
  49. +5 −0 config/locales/en.yml
  50. +61 −0 config/routes.rb
  51. +11 −0 db/migrate/20110616103504_create_pages.rb
  52. +23 −0 db/schema.rb
  53. +7 −0 db/seeds.rb
  54. +2 −0 doc/README_FOR_APP
  55. 0 lib/tasks/.gitkeep
  56. 0 log/.gitkeep
  57. +26 −0 public/404.html
  58. +26 −0 public/422.html
  59. +26 −0 public/500.html
  60. 0 public/favicon.ico
  61. +241 −0 public/index.html
  62. +8,981 −0 public/javascripts/jquery.js
  63. +205 −0 public/javascripts/spine.ajax.js
  64. +628 −0 public/javascripts/spine.js
  65. +59 −0 public/javascripts/spine.list.js
  66. +24 −0 public/javascripts/spine.local.js
  67. +81 −0 public/javascripts/spine.manager.js
  68. +146 −0 public/javascripts/spine.relation.js
  69. +183 −0 public/javascripts/spine.route.js
  70. +56 −0 public/javascripts/spine.tabs.js
  71. +16 −0 public/javascripts/spine.tmpl.js
  72. +5 −0 public/robots.txt
  73. +6 −0 script/rails
  74. 0 test/fixtures/.gitkeep
  75. +11 −0 test/fixtures/pages.yml
  76. 0 test/functional/.gitkeep
  77. +7 −0 test/functional/pages_controller_test.rb
  78. 0 test/integration/.gitkeep
  79. +12 −0 test/performance/browsing_test.rb
  80. +13 −0 test/test_helper.rb
  81. 0 test/unit/.gitkeep
  82. +4 −0 test/unit/helpers/pages_helper_test.rb
  83. +7 −0 test/unit/page_test.rb
  84. 0 vendor/assets/stylesheets/.gitkeep
  85. 0 vendor/plugins/.gitkeep
5 .gitignore
@@ -0,0 +1,5 @@
+.bundle
+db/*.sqlite3
+log/*.log
+tmp/
+.sass-cache/
32 Gemfile
@@ -0,0 +1,32 @@
+source 'http://rubygems.org'
+
+gem 'rails', '3.1.0.rc1'
+
+# Bundle edge Rails instead:
+# gem 'rails', :git => 'git://github.com/rails/rails.git'
+
+gem 'sqlite3'
+
+# Asset template engines
+gem 'sass'
+gem 'coffee-script'
+gem 'uglifier'
+
+gem 'jquery-rails'
+
+gem 'stitch-rb'
+gem 'thin'
+
+# Use unicorn as the web server
+# gem 'unicorn'
+
+# Deploy with Capistrano
+# gem 'capistrano'
+
+# To use debugger
+# gem 'ruby-debug19', :require => 'ruby-debug'
+
+group :test do
+ # Pretty printed test output
+ gem 'turn', :require => false
+end
117 Gemfile.lock
@@ -0,0 +1,117 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ actionmailer (3.1.0.rc1)
+ actionpack (= 3.1.0.rc1)
+ mail (~> 2.3.0)
+ actionpack (3.1.0.rc1)
+ activemodel (= 3.1.0.rc1)
+ activesupport (= 3.1.0.rc1)
+ builder (~> 3.0.0)
+ erubis (~> 2.7.0)
+ i18n (~> 0.6.0beta1)
+ rack (~> 1.3.0.beta2)
+ rack-cache (~> 1.0.1)
+ rack-mount (~> 0.8.1)
+ rack-test (~> 0.6.0)
+ sprockets (~> 2.0.0.beta.5)
+ tzinfo (~> 0.3.27)
+ activemodel (3.1.0.rc1)
+ activesupport (= 3.1.0.rc1)
+ bcrypt-ruby (~> 2.1.4)
+ builder (~> 3.0.0)
+ i18n (~> 0.6.0beta1)
+ activerecord (3.1.0.rc1)
+ activemodel (= 3.1.0.rc1)
+ activesupport (= 3.1.0.rc1)
+ arel (~> 2.1.1)
+ tzinfo (~> 0.3.27)
+ activeresource (3.1.0.rc1)
+ activemodel (= 3.1.0.rc1)
+ activesupport (= 3.1.0.rc1)
+ activesupport (3.1.0.rc1)
+ multi_json (~> 1.0)
+ ansi (1.2.5)
+ arel (2.1.1)
+ bcrypt-ruby (2.1.4)
+ builder (3.0.0)
+ coffee-script (2.2.0)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.1.1)
+ daemons (1.1.3)
+ erubis (2.7.0)
+ eventmachine (0.12.10)
+ execjs (1.1.3)
+ multi_json (~> 1.0)
+ hike (1.1.0)
+ i18n (0.6.0)
+ jquery-rails (1.0.11)
+ railties (~> 3.0)
+ thor (~> 0.14)
+ mail (2.3.0)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ multi_json (1.0.3)
+ polyglot (0.3.1)
+ rack (1.3.0)
+ rack-cache (1.0.2)
+ rack (>= 0.4)
+ rack-mount (0.8.1)
+ rack (>= 1.0.0)
+ rack-ssl (1.3.2)
+ rack
+ rack-test (0.6.0)
+ rack (>= 1.0)
+ rails (3.1.0.rc1)
+ actionmailer (= 3.1.0.rc1)
+ actionpack (= 3.1.0.rc1)
+ activerecord (= 3.1.0.rc1)
+ activeresource (= 3.1.0.rc1)
+ activesupport (= 3.1.0.rc1)
+ bundler (~> 1.0)
+ railties (= 3.1.0.rc1)
+ railties (3.1.0.rc1)
+ actionpack (= 3.1.0.rc1)
+ activesupport (= 3.1.0.rc1)
+ rack-ssl (~> 1.3.2)
+ rake (>= 0.8.7)
+ thor (~> 0.14.6)
+ rake (0.9.2)
+ sass (3.1.2)
+ sprockets (2.0.0.beta.10)
+ hike (~> 1.0)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ sqlite3 (1.3.3)
+ stitch-rb (0.0.2)
+ thin (1.2.11)
+ daemons (>= 1.0.9)
+ eventmachine (>= 0.12.6)
+ rack (>= 1.0.0)
+ thor (0.14.6)
+ tilt (1.3.2)
+ treetop (1.4.9)
+ polyglot (>= 0.3.1)
+ turn (0.8.2)
+ ansi (>= 1.2.2)
+ tzinfo (0.3.28)
+ uglifier (0.5.4)
+ execjs (>= 0.3.0)
+ multi_json (>= 1.0.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ coffee-script
+ jquery-rails
+ rails (= 3.1.0.rc1)
+ sass
+ sqlite3
+ stitch-rb
+ thin
+ turn
+ uglifier
261 README
@@ -0,0 +1,261 @@
+== Welcome to Rails
+
+Rails is a web-application framework that includes everything needed to create
+database-backed web applications according to the Model-View-Control pattern.
+
+This pattern splits the view (also called the presentation) into "dumb"
+templates that are primarily responsible for inserting pre-built data in between
+HTML tags. The model contains the "smart" domain objects (such as Account,
+Product, Person, Post) that holds all the business logic and knows how to
+persist themselves to a database. The controller handles the incoming requests
+(such as Save New Account, Update Product, Show Post) by manipulating the model
+and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails. You can read more about Action Pack in
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting Started
+
+1. At the command prompt, create a new Rails application:
+ <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
+
+2. Change directory to <tt>myapp</tt> and start the web server:
+ <tt>cd myapp; rails server</tt> (run with --help for options)
+
+3. Go to http://localhost:3000/ and you'll see:
+ "Welcome aboard: You're riding Ruby on Rails!"
+
+4. Follow the guidelines to start developing your application. You can find
+the following resources handy:
+
+* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
+* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
+
+
+== Debugging Rails
+
+Sometimes your application goes wrong. Fortunately there are a lot of tools that
+will help you debug it and get it back on the rails.
+
+First area to check is the application log files. Have "tail -f" commands
+running on the server.log and development.log. Rails will automatically display
+debugging and runtime information to these files. Debugging info will also be
+shown in the browser on requests from 127.0.0.1.
+
+You can also log your own messages directly into the log file from your code
+using the Ruby logger class from inside your controllers. Example:
+
+ class WeblogController < ActionController::Base
+ def destroy
+ @weblog = Weblog.find(params[:id])
+ @weblog.destroy
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
+ end
+ end
+
+The result will be a message in your log file along the lines of:
+
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
+
+More information on how to use the logger is at http://www.ruby-doc.org/core/
+
+Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
+several books available online as well:
+
+* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
+* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
+
+These two books will bring you up to speed on the Ruby language and also on
+programming in general.
+
+
+== Debugger
+
+Debugger support is available through the debugger command when you start your
+Mongrel or WEBrick server with --debugger. This means that you can break out of
+execution at any point in the code, investigate and change the model, and then,
+resume execution! You need to install ruby-debug to run the server in debugging
+mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.all
+ debugger
+ end
+ end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the server window. Here you can do things like:
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8
+ @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
+ #<Post:0x14a6620
+ @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
+ >> @posts.first.title = "hello from a debugger"
+ => "hello from a debugger"
+
+...and even better, you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you can enter "cont".
+
+
+== Console
+
+The console is a Ruby shell, which allows you to interact with your
+application's domain model. Here you'll have all parts of the application
+configured, just like it is when the application is running. You can inspect
+domain models, change values, and save to the database. Starting the script
+without arguments will launch it in the development environment.
+
+To start the console, run <tt>rails console</tt> from the application
+directory.
+
+Options:
+
+* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
+ made to the database.
+* Passing an environment name as an argument will load the corresponding
+ environment. Example: <tt>rails console production</tt>.
+
+To reload your controllers and models after launching the console run
+<tt>reload!</tt>
+
+More information about irb can be found at:
+link:http://www.rubycentral.org/pickaxe/irb.html
+
+
+== dbconsole
+
+You can go to the command line of your database directly through <tt>rails
+dbconsole</tt>. You would be connected to the database with the credentials
+defined in database.yml. Starting the script without arguments will connect you
+to the development database. Passing an argument will connect you to a different
+database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
+PostgreSQL and SQLite 3.
+
+== Description of Contents
+
+The default directory structure of a generated Ruby on Rails application:
+
+ |-- app
+ | |-- assets
+ | |-- images
+ | |-- javascripts
+ | `-- stylesheets
+ | |-- controllers
+ | |-- helpers
+ | |-- mailers
+ | |-- models
+ | `-- views
+ | `-- layouts
+ |-- config
+ | |-- environments
+ | |-- initializers
+ | `-- locales
+ |-- db
+ |-- doc
+ |-- lib
+ | `-- tasks
+ |-- log
+ |-- public
+ |-- script
+ |-- test
+ | |-- fixtures
+ | |-- functional
+ | |-- integration
+ | |-- performance
+ | `-- unit
+ |-- tmp
+ | |-- cache
+ | |-- pids
+ | |-- sessions
+ | `-- sockets
+ `-- vendor
+ |-- assets
+ `-- stylesheets
+ `-- plugins
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/assets
+ Contains subdirectories for images, stylesheets, and JavaScript files.
+
+app/controllers
+ Holds controllers that should be named like weblogs_controller.rb for
+ automated URL mapping. All controllers should descend from
+ ApplicationController which itself descends from ActionController::Base.
+
+app/models
+ Holds models that should be named like post.rb. Models descend from
+ ActiveRecord::Base by default.
+
+app/views
+ Holds the template files for the view that should be named like
+ weblogs/index.html.erb for the WeblogsController#index action. All views use
+ eRuby syntax by default.
+
+app/views/layouts
+ Holds the template files for layouts to be used with views. This models the
+ common header/footer method of wrapping views. In your views, define a layout
+ using the <tt>layout :default</tt> and create a file named default.html.erb.
+ Inside default.html.erb, call <% yield %> to render the view using this
+ layout.
+
+app/helpers
+ Holds view helpers that should be named like weblogs_helper.rb. These are
+ generated for you automatically when using generators for controllers.
+ Helpers can be used to wrap functionality for your views into methods.
+
+config
+ Configuration files for the Rails environment, the routing map, the database,
+ and other dependencies.
+
+db
+ Contains the database schema in schema.rb. db/migrate contains all the
+ sequence of Migrations for your schema.
+
+doc
+ This directory is where your application documentation will be stored when
+ generated using <tt>rake doc:app</tt>
+
+lib
+ Application specific libraries. Basically, any kind of custom code that
+ doesn't belong under controllers, models, or helpers. This directory is in
+ the load path.
+
+public
+ The directory available for the web server. Also contains the dispatchers and the
+ default HTML files. This should be set as the DOCUMENT_ROOT of your web
+ server.
+
+script
+ Helper scripts for automation and generation.
+
+test
+ Unit and functional tests along with fixtures. When using the rails generate
+ command, template test files will be generated for you and placed in this
+ directory.
+
+vendor
+ External libraries that the application depends on. Also includes the plugins
+ subdirectory. If the app has frozen rails, those gems also go here, under
+ vendor/rails/. This directory is in the load path.
7 Rakefile
@@ -0,0 +1,7 @@
+#!/usr/bin/env rake
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+
+SpineRails3::Application.load_tasks
BIN app/assets/.DS_Store
Binary file not shown.
BIN app/assets/images/rails.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN app/assets/javascripts/.DS_Store
Binary file not shown.
BIN app/assets/javascripts/app/.DS_Store
Binary file not shown.
11 app/assets/javascripts/app/app.coffee
@@ -0,0 +1,11 @@
+class App extends Spine.Controller
+ elements:
+ "#pages": "pagesEl"
+
+ constructor: ->
+ super
+
+ alert('Loaded!')
+
+
+module.exports = App
73 app/assets/javascripts/app/controllers/pages.coffee
@@ -0,0 +1,73 @@
+$ = jQuery
+Page = require("models/page")
+
+class PagesItem extends Spine.Controller
+ events:
+ "click .destroy": "destroy"
+ "click .edit": "edit"
+ "submit form": "update"
+
+ constructor: ->
+ throw("item required") unless @item
+ @item.bind("destroy", @remove)
+ @item.bind("update", @render)
+
+ render: =>
+ @html require("views/pages/show")(@item)
+
+ update: (e) ->
+ e.preventDefault()
+ @item.updateAttributes($(e.target).serializeForm())
+ @el.removeClass("edit")
+
+ edit: ->
+ @el.addClass("edit")
+
+ destroy: ->
+ @item.destroy()
+
+ remove: ->
+ @el.remove()
+
+class PagesList extends Spine.Controller
+ events:
+ "click .item": "show"
+ "click .create": "create"
+
+ constructor: ->
+ Page.bind("refresh", @addAll)
+ Page.bind("create", @addOne)
+
+ addAll: =>
+ @el.empty()
+ @addOne(page) for page in Page.all()
+
+ addOne: (page) =>
+ @append(new PagesItem(item: page).render())
+
+ show: (e) ->
+ item = $(e.target).item()
+ @navigate("/pages", item.id)
+
+ create: (e) ->
+ Page.create(name: "Sample page")
+
+class Pages extends Spine.Controller
+ constructor: ->
+ @list = new PagesList
+ @item = new PagesItem
+
+ @manager = new Spine.Manager(@list, @item)
+
+ @append(@list, @item)
+
+ @routes
+ "/pages": (params) ->
+ @list.active(params)
+ "/pages/:id": (params) ->
+ @item.change Page.find(params.id)
+ @item.active(params)
+
+ Page.fetch()
+
+module.exports = Pages
7 app/assets/javascripts/app/lib/jquery.ext.coffee
@@ -0,0 +1,7 @@
+$ = jQuery
+
+$::serializeForm = ->
+ result = {}
+ $.each $(@).serializeArray(), (i, item) ->
+ result[item.name] = item.value;
+ result
5 app/assets/javascripts/app/models/page.coffee
@@ -0,0 +1,5 @@
+class Page extends Spine.Model
+ @configure "Page", "name", "slug", "body"
+ @extend Spine.Ajax
+
+module.exports = Page
1 app/assets/javascripts/app/view/pages/show.tmpl
@@ -0,0 +1 @@
+<div class="item">${name}</div>
113 app/assets/javascripts/vendor/spine.ajax.coffee
@@ -0,0 +1,113 @@
+Spine or= require("spine")
+$ = Spine.$
+Model = Spine.Model
+
+Ajax = Spine.Ajax =
+ getUrl: (object) ->
+ return null unless object and object.url
+ object.url?() or object.url
+
+ methodMap:
+ "create": "POST"
+ "update": "PUT"
+ "destroy": "DELETE"
+ "read": "GET"
+
+ send: (record, method, params) ->
+ defaults =
+ type: @methodMap[method]
+ contentType: "application/json"
+ dataType: "json"
+ data: {}
+
+ params = $.extend({}, defaults, params)
+
+ if method is "create" and record.model
+ params.url = @getUrl(record.constructor)
+ else
+ params.url = @getUrl(record)
+
+ throw("Invalid URL") unless params.url
+
+ if method is "create" or method is "update"
+ params.data = JSON.stringify(record)
+ params.processData = false
+ params.success = (data, status, xhr) ->
+ return unless data
+
+ # Simple deep object comparison
+ return if JSON.stringify(record) is JSON.stringify(data)
+
+ # ID change, need to do some shifting
+ if data.id and record.id != data.id
+ records = record.constructor.records
+ records[data.id] = records[record.id]
+ delete records[record.id]
+ record.id = data.id
+
+ # Update with latest data
+ Ajax.disable ->
+ record.updateAttributes(data);
+
+ record.trigger("ajaxSuccess", record, status, xhr)
+
+ if method is "read" and not params.success
+ params.success = ->
+ (record.refresh or record.load).call(record, data)
+
+ success = params.success
+ params.success = ->
+ success.apply(Ajax, arguments) if success
+ Ajax.sendNext()
+
+ params.error = (xhr, s, e) ->
+ if record.trigger("ajaxError", record, xhr, s, e)
+ Ajax.sendNext()
+
+ $.ajax(params)
+
+ enabled: true
+ pending: false
+ requests: []
+
+ disable: (callback) ->
+ @enabled = false
+ callback()
+ @enabled = true
+
+ sendNext: ->
+ next = @requests.shift()
+ if next
+ @send.apply(@, next)
+ else
+ @pending = false
+
+ request: ->
+ return unless @enabled
+ if @pending
+ @requests.push(arguments)
+ else
+ @pending = true;
+ @send.apply(this, arguments)
+
+Include =
+ url: ->
+ base = Ajax.getUrl(@constructor)
+ base += "/" unless base.charAt(base.length - 1) is "/"
+ base += encodeURIComponent(@id)
+ return base;
+
+Model.Ajax =
+ extended: ->
+ @change ->
+ Ajax.request.apply(Ajax, arguments)
+ @fetch (params) =>
+ Ajax.request(@, "read", params)
+ @include Include
+
+ ajaxPrefix: false
+
+ url: ->
+ "/#{@className.toLowerCase()}s"
+
+module?.exports = Model.Ajax
425 app/assets/javascripts/vendor/spine.coffee
@@ -0,0 +1,425 @@
+$ = @jQuery or @Zepto or -> arguments[0]
+
+Events =
+ bind: (ev, callback) ->
+ evs = ev.split(" ")
+ calls = @hasOwnProperty("_callbacks") and @_callbacks or= {}
+
+ for name in evs
+ calls[name] or= []
+ calls[name].push(callback)
+ @
+
+ trigger: (args...) ->
+ ev = args.shift()
+
+ list = @hasOwnProperty("_callbacks") and @_callbacks?[ev]
+ return false unless list
+
+ for callback in list
+ if callback.apply(this, args) is false
+ break
+ true
+
+ unbind: (ev, callback) ->
+ unless ev
+ @_callbacks = {}
+ return @
+
+ list = @_callbacks?[ev]
+ return @ unless list
+
+ unless callback
+ delete @_callbacks[ev]
+ return @
+
+ for cb, i in list when cb is callback
+ list = list.slice()
+ list.splice(i, 1)
+ @_callbacks[ev] = list
+ break
+ @
+
+Log =
+ trace: true
+
+ logPrefix: "(App)"
+
+ log: (args...) ->
+ return unless @trace
+ return if typeof console is "undefined"
+ if @logPrefix then args.unshift(@logPrefix)
+ console.log(args...)
+ @
+
+moduleKeywords = ["included", "extended"]
+
+class Module
+ @include: (obj) ->
+ for key, value of obj when key not in moduleKeywords
+ @::[key] = value
+
+ included = obj.included
+ included.apply(this) if included
+ @
+
+ @extend: (obj) ->
+ for key, value of obj when key not in moduleKeywords
+ @[key] = value
+
+ extended = obj.extended
+ extended.apply(this) if extended
+ @
+
+ @proxy: (func) ->
+ => func.apply(@, arguments)
+
+ proxy: (func) ->
+ => func.apply(@, arguments)
+
+class Model extends Module
+ @records: {}
+ @attributes: []
+
+ @setup: ->
+ class Instance extends this
+ Instance.configure.apply(Instance, arguments)
+ Instance
+
+ @configure: (name, attributes...) ->
+ @className = name
+ @records = {}
+ @attributes = attributes if attributes.length
+ @attributes and= makeArray(@attributes)
+ @attributes or= []
+ @unbind()
+ @
+
+ @toString: -> "#{@className}(#{@attributes.join(", ")})"
+
+ @find: (id) ->
+ record = @records[id]
+ throw("Unknown record") unless record
+ record.clone()
+
+ @exists: (id) ->
+ try
+ return @find(id)
+ catch e
+ return false
+
+ @refresh: (values, options = {}) ->
+ @records = {} if options.clear
+
+ for record in @fromJSON(values)
+ record.newRecord = false
+ record.id or= guid()
+ @records[record.id] = record
+
+ @trigger("refresh")
+ @
+
+ @select: (callback) ->
+ result = (record for id, record of @records when callback(record))
+ @cloneArray(result)
+
+ @findByAttribute: (name, value) ->
+ for id, record of @records
+ if record[name] == value
+ return record.clone()
+ null
+
+ @findAllByAttribute: (name, value) ->
+ @select (item) ->
+ item[name] is value
+
+ @each: (callback) ->
+ for key, value of @records
+ callback(value)
+
+ @all: ->
+ @cloneArray(@recordsValues())
+
+ @first: ->
+ record = @recordsValues()[0]
+ record?.clone()
+
+ @last: ->
+ values = @recordsValues()
+ record = values[values.length - 1]
+ record?.clone()
+
+ @count: ->
+ @recordsValues().length
+
+ @deleteAll: ->
+ for key, value of @records
+ delete @records[key]
+
+ @destroyAll: ->
+ for key, value of @records
+ @records[key].destroy()
+
+ @update: (id, atts) ->
+ @find(id).updateAttributes(atts)
+
+ @create: (atts) ->
+ record = new @(atts)
+ record.save()
+
+ @destroy: (id) ->
+ @find(id).destroy()
+
+ @change: (callbackOrParams) ->
+ if typeof callbackOrParams is "function"
+ @bind("change", callbackOrParams)
+ else
+ @trigger("change", callbackOrParams)
+
+ @fetch: (callbackOrParams) ->
+ if typeof callbackOrParams is "function"
+ @bind("fetch", callbackOrParams)
+ else
+ @trigger("fetch", callbackOrParams)
+
+ @toJSON: ->
+ @recordsValues()
+
+ @fromJSON: (objects) ->
+ return unless objects
+ if typeof objects is "string"
+ objects = JSON.parse(objects)
+ if isArray(objects)
+ (new @(value) for value in objects)
+ else
+ new @(objects)
+
+ # Private
+
+ @recordsValues: ->
+ result = []
+ for key, value of @records
+ result.push(value)
+ result
+
+ @cloneArray: (array) ->
+ (value.clone() for value in array)
+
+ # Instance
+
+ model: true
+ newRecord: true
+
+ constructor: (atts) ->
+ super
+ @load atts if atts
+
+ isNew: () ->
+ @newRecord
+
+ isValid: () ->
+ not @validate()
+
+ validate: ->
+
+ load: (atts) ->
+ for key, value of atts
+ @[key] = value
+
+ attributes: ->
+ result = {}
+ result[key] = @[key] for key in @constructor.attributes
+ result.id = @id
+ result
+
+ eql: (rec) ->
+ rec and rec.id is @id and rec.constructor is @constructor
+
+ save: ->
+ error = @validate()
+ if error
+ @trigger("error", @, error)
+ return false
+
+ @trigger("beforeSave", @)
+ if @newRecord then @create() else @update()
+ @trigger("save", @)
+ return @
+
+ updateAttribute: (name, value) ->
+ @[name] = value
+ @save()
+
+ updateAttributes: (atts) ->
+ @load(atts)
+ @save()
+
+ destroy: ->
+ @trigger("beforeDestroy", @)
+ delete @constructor.records[@id]
+ @destroyed = true
+ @trigger("destroy", @)
+ @trigger("change", @, "destroy")
+
+ dup: (newRecord) ->
+ result = new @constructor(@attributes())
+ if newRecord is false
+ result.newRecord = @newRecord
+ else
+ delete result.id
+ result
+
+ clone: ->
+ Object.create(@)
+
+ reload: ->
+ return @ if @newRecord
+ original = @constructor.find(@id)
+ @load(original.attributes())
+ return original
+
+ toJSON: ->
+ @attributes()
+
+ toString: ->
+ "<#{@constructor.className} (#{JSON.stringify(@)})>"
+
+ exists: ->
+ @id && @id of @constructor.records
+
+ # Private
+
+ update: ->
+ @trigger("beforeUpdate", @)
+ records = @constructor.records
+ records[@id].load @attributes()
+ clone = records[@id].clone()
+ @trigger("update", clone)
+ @trigger("change", clone, "update")
+
+ create: ->
+ @trigger("beforeCreate", @)
+ @id = guid() unless @id
+ @newRecord = false
+ records = @constructor.records
+ records[@id] = @dup(false)
+ clone = records[@id].clone()
+ @trigger("create", clone)
+ @trigger("change", clone, "create")
+
+ bind: (events, callback) ->
+ @constructor.bind events, (record) =>
+ if record && @eql(record)
+ callback.apply(@, arguments)
+
+ trigger: ->
+ @constructor.trigger(arguments...)
+
+Model.extend(Events)
+
+class Controller extends Module
+ eventSplitter: /^(\w+)\s*(.*)$/
+ tag: "div"
+
+ constructor: (options) ->
+ @options = options
+
+ for key, value of @options
+ @[key] = value
+
+ @el = document.createElement(@tag) unless @el
+ @el = $(@el)
+
+ @events = @constructor.events unless @events
+ @elements = @constructor.elements unless @elements
+
+ @delegateEvents() if @events
+ @refreshElements() if @elements
+
+ $: (selector) -> $(selector, @el)
+
+ delegateEvents: ->
+ for key of @events
+ methodName = @events[key]
+ method = @proxy(@[methodName])
+
+ match = key.match(@eventSplitter)
+ eventName = match[1]
+ selector = match[2]
+
+ if selector is ''
+ @el.bind(eventName, method)
+ else
+ @el.delegate(selector, eventName, method)
+
+ refreshElements: ->
+ for key, value of @elements
+ @[value] = @$(key)
+
+ delay: (func, timeout) ->
+ setTimeout(@proxy(func), timeout || 0)
+
+ html: (element) -> @el.html(element.el or element)
+
+ append: (elements...) ->
+ elements = (e.el or e for e in elements)
+ @el.append(elements...)
+
+ appendTo: (element) ->
+ @el.appendTo(element.el or element)
+
+Controller.include(Events)
+Controller.include(Log)
+
+# Utilities & Shims
+
+unless typeof Object.create is "function"
+ Object.create = (o) ->
+ Func = ->
+ Func.prototype = o
+ new Func()
+
+isArray = (value) ->
+ Object::toString.call(value) is "[object Array]"
+
+makeArray = (args) ->
+ Array.prototype.slice.call(args, 0)
+
+guid = ->
+ 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
+ r = Math.random() * 16 | 0
+ v = if c is 'x' then r else r & 3 | 8
+ v.toString 16
+ .toUpperCase()
+
+# Globals
+
+if typeof exports is not "undefined"
+ Spine = exports
+else
+ Spine = @Spine = {}
+
+Spine.version = "2.0.0"
+Spine.isArray = isArray
+Spine.$ = $
+Spine.Events = Events
+Spine.Log = Log
+Spine.Module = Module
+Spine.Controller = Controller
+Spine.Model = Model
+
+# Backwards compatability
+
+Module.create = Module.sub =
+Controller.create = Controller.sub =
+Model.sub = (instance, static) ->
+ class result extends this
+ result.include(instance) if instance
+ result.extend(static) if static
+ result.unbind?()
+ result
+
+Module.init = Controller.init = Model.init = (a1, a2, a3, a4, a5) ->
+ new this(a1, a2, a3, a4, a5)
+
+Spine.Class = Module
37 app/assets/javascripts/vendor/spine.list.coffee
@@ -0,0 +1,37 @@
+Spine or= require("spine")
+$ = Spine.$
+
+class Spine.List extends Spine.Controller
+ events:
+ "click .item": "click"
+
+ selectFirst: false
+
+ constructor: ->
+ super
+ @bind("change", @change)
+
+ template: -> arguments[0]
+
+ change: (item) =>
+ return unless item
+ @current = item
+ @children().removeClass("active")
+ @children().forItem(@current).addClass("active")
+
+ render: (items) ->
+ @items = items if items
+ @html @template(@items)
+ @change @current
+ if @selectFirst
+ unless @children(".active").length or @current
+ @children(":first").click()
+
+ children: (sel) ->
+ @el.children(sel)
+
+ click: (e) ->
+ item = $(e.target).item()
+ @trigger("change", item)
+
+module?.exports = Spine.List
18 app/assets/javascripts/vendor/spine.local.coffee
@@ -0,0 +1,18 @@
+Spine or= require("spine")
+
+Spine.Model.Local =
+ extended: ->
+ this.change(this.proxy(this.saveLocal))
+ this.fetch(this.proxy(this.loadLocal))
+
+ saveLocal: ->
+ result = JSON.stringify(this)
+ localStorage[@className] = result
+
+ loadLocal: ->
+ result = localStorage[@className]
+ return unless result
+ result = JSON.parse(result)
+ this.refresh(result, clear: true)
+
+module?.exports = Spine.Model.Local
64 app/assets/javascripts/vendor/spine.manager.coffee
@@ -0,0 +1,64 @@
+# A Manager is basically a state machine that controls a set of controller's 'active' state.
+# In other words, you feed a manager controllers, and it'll make sure that only controller has an 'active' state at any one time.
+# This is useful whenever you're implementing tabs or separate views inside an application.
+#
+# By default, whenever a controller is activated, it's element receives a 'active' class.
+# You can use this class to show/hide views and tabs via CSS.
+# For example:
+#
+# var users = Users.init();
+# var groups = Groups.init();
+# Manager.init(users, groups);
+#
+# users.active();
+# assert( users.isActive() );
+# assert( users.el.hasClass("active") );
+# assert( ! groups.el.hasClass("active") );
+#
+# groups.active();
+# assert( groups.el.hasClass("active") );
+# assert( ! users.el.hasClass("active") );
+
+Spine or= require("spine")
+$ = Spine.$
+
+class Spine.Manager extends Spine.Module
+ constructor: ->
+ @add.apply(@, arguments)
+
+ add: (controllers...) ->
+ @addOne(cont) for cont in controllers
+
+ addOne: (controller) ->
+ @bind "change", (current, args) ->
+ if controller is current
+ controller.activate.apply(controller, args)
+ else
+ controller.deactivate.apply(controller, args)
+
+ controller.active (args...) =>
+ @trigger("change", controller, args)
+
+Spine.Manager.include(Spine.Events)
+
+Spine.Controller.include
+ active: (args...) ->
+ if typeof args[0] is "function"
+ @bind("active", args[0])
+ else
+ args.unshift("active")
+ @trigger.apply(@, args)
+ @
+
+ isActive: ->
+ @el.hasClass("active")
+
+ activate: ->
+ @el.addClass("active")
+ @
+
+ deactivate: ->
+ @el.removeClass("active");
+ @
+
+module?.exports = Spine.Manager
104 app/assets/javascripts/vendor/spine.relation.coffee
@@ -0,0 +1,104 @@
+class Collection
+ constructor: (options = {}) ->
+ for key, value of options
+ @[key] = value
+
+ all: ->
+ @model.select (rec) => @associated(rec)
+
+ first: ->
+ @all()[0]
+
+ last: ->
+ values = @all()
+ values[values.length - 1]
+
+ find: (id) ->
+ records = @model.select (rec) =>
+ @associated(rec) and rec.id is id
+ throw("Unknown record") unless records[0]
+ records[0]
+
+ select: (cb) ->
+ @model.select (rec) =>
+ @associated(rec) and cb(rec)
+
+ refresh: (values) ->
+ records = @all()
+ for record in records
+ delete @model.records[record.id]
+
+ values = @model.fromJSON(values)
+ for value in values
+ value.newRecord = false
+ value[@fkey] = @record.id
+ @model.records[value.id] = value
+
+ @model.trigger("refresh")
+
+ create: (record) ->
+ record[@fkey] = @record.id
+ @model.create(record)
+
+ # Private
+
+ associated: (record) ->
+ record[@fkey] is @record.id
+
+class Instance
+ constructor: (options = {}) ->
+ for key, value of options
+ @[key] = value
+
+ find: ->
+ @record[@fkey] && @model.find(@record[@fkey])
+
+ update: (value) ->
+ @record[@fkey] = value && value.id
+
+singularize = (str) ->
+ str.replace(/s$/, '')
+
+underscore = (str) ->
+ str.replace(/::/g, '/')
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
+ .replace(/-/g, '_')
+ .toLowerCase()
+
+Spine.Model.extend
+ many: (name, model, fkey) ->
+ fkey ?= "#{underscore(@className)}_id"
+
+ association = (record) ->
+ model = require(model) if typeof model is "string"
+
+ new Collection(
+ name: name, model: model,
+ record: record, fkey: fkey
+ )
+
+ @::__defineGetter__ name, ->
+ return association(@)
+
+ @::__defineSetter__ name, (value) ->
+ return association(@).refresh(value)
+
+ belongs: (name, model, fkey) ->
+ fkey ?= "#{singularize(name)}_id"
+
+ association = (record) ->
+ model = require(model) if typeof model is "string"
+
+ new Instance(
+ name: name, model: model,
+ record: record, fkey: fkey
+ )
+
+ @::__defineGetter__ name, ->
+ return association(@).find()
+
+ @::__defineSetter__ name, (value) ->
+ return association(@).update(value)
+
+ @attributes.push(fkey)
136 app/assets/javascripts/vendor/spine.route.coffee
@@ -0,0 +1,136 @@
+Spine or= require("spine")
+$ = Spine.$
+
+hashStrip = /^#*/
+namedParam = /:([\w\d]+)/g
+splatParam = /\*([\w\d]+)/g
+escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
+
+class Spine.Route extends Spine.Module
+ @historySupport: "history" of window
+
+ @routes: []
+
+ @options:
+ trigger: true
+ history: false
+ shim: false
+
+ @add: (path, callback) ->
+ if (typeof path is "object")
+ @add(key, value) for key, value of path
+ else
+ @routes.push(new @(path, callback))
+
+ @setup: (options = {}) ->
+ @options = $.extend({}, @options, options)
+
+ if (@options.history)
+ @history = @historySupport && @options.history
+
+ return if @options.shim
+
+ if @history
+ $(window).bind("popstate", @change)
+ else
+ $(window).bind("hashchange", @change)
+ @change()
+
+ @unbind: ->
+ if @history
+ $(window).unbind("popstate", @change)
+ else
+ $(window).unbind("hashchange", @change)
+
+ @navigate: (args...) ->
+ options = {}
+
+ lastArg = args[args.length - 1]
+ if typeof lastArg is "object"
+ options = args.pop()
+ else if typeof lastArg is "boolean"
+ options.trigger = args.pop()
+
+ options = $.extend({}, @options, options)
+
+ path = args.join("/")
+ return if @path is path
+ @path = path
+
+ @matchRoute(@path, options) if options.trigger
+
+ return if options.shim
+
+ if @history
+ history.pushState(
+ {},
+ document.title,
+ @getHost() + @path
+ )
+ else
+ window.location.hash = @path
+
+ # Private
+
+ @getPath: -> window.location.pathname
+
+ @getHash: -> window.location.hash
+
+ @getFragment: -> @getHash().replace(hashStrip, "")
+
+ @getHost: ->
+ (document.location + "").replace(@getPath() + @getHash(), "")
+
+ @change: ->
+ path = if @history then @getPath() else @getFragment()
+ return if path is @path
+ @path = path
+ @matchRoute(@path)
+
+ @matchRoute: (path, options) ->
+ for route in @routes
+ return route if route.match(path, options)
+
+ constructor: (path, callback) ->
+ @names = []
+ @callback = callback
+
+ if typeof path is "string"
+ while (match = namedParam.exec(path)) != null
+ @names.push(match[1])
+
+ path = path.replace(escapeRegExp, "\\$&")
+ .replace(namedParam, "([^\/]*)")
+ .replace(splatParam, "(.*?)")
+
+ @route = new RegExp('^' + path + '$')
+ else
+ @route = path
+
+ match: (path, options = {}) ->
+ match = @route.exec(path)
+ return false unless match
+ options.match = match
+ params = match.slice(1)
+
+ if @names.length
+ for param, i in params
+ options[@names[i]] = param
+
+ @callback.call(null, options)
+ true
+
+# Coffee-script bug
+Spine.Route.change = Spine.Route.proxy(Spine.Route.change)
+
+Spine.Controller.include
+ route: (path, callback) ->
+ Spine.Route.add(path, @proxy(callback))
+
+ routes: (routes) ->
+ @route(key, value) for key, value of routes
+
+ navigate: ->
+ Spine.Route.navigate.apply(Spine.Route, arguments)
+
+module?.exports = Spine.Route
55 app/assets/javascripts/vendor/spine.tabs.coffee
@@ -0,0 +1,55 @@
+#
+# Usage:
+#
+# <ul class="tabs">
+# <li data-name="users">Users</li>
+# <li data-name="groups">Groups</li>
+# </ul>
+#
+#
+# var users = Users.init();
+# var groups = Groups.init();
+# Manager.init(users, groups);
+#
+# var tabs = Spine.Tabs.init({el: $(".tabs")});
+# tabs.connect("users", users);
+# tabs.connect("groups", groups);
+#
+## Select first tab.
+# tabs.render();
+
+Spine or= require("spine")
+$ = Spine.$
+
+class Spine.Tabs extends Spine.Controller
+ events:
+ "click [data-name]": "click"
+
+ constructor: ->
+ super
+ @bind("change", @change)
+
+
+ change: (name) =>
+ return unless name
+ @current = name
+ @children().removeClass("active")
+ @children("[data-name='" + @current + "']").addClass("active")
+
+ render: ->
+ @change @current
+ unless @children(".active").length or @current
+ @children(":first").click()
+
+ children: (sel) ->
+ @el.children(sel)
+
+ click: (e) ->
+ name = $(e.target).attr("data-name")
+ @trigger("change", name)
+
+ connect: (tabName, controller) ->
+ @bind "change", (name) ->
+ controller.active() if name == tabName
+
+module?.exports = Spine.Tabs
12 app/assets/javascripts/vendor/spine.tmpl.coffee
@@ -0,0 +1,12 @@
+# jQuery.tmpl.js utilities
+
+$ = jQuery
+
+$.fn.item = ->
+ item = $(@).tmplItem().data
+ item.reload?()
+
+$.fn.forItem = ->
+ @filter ->
+ compare = $(@).item()
+ return item.eql?(compare) or item is compare
7 app/assets/stylesheets/application.css
@@ -0,0 +1,7 @@
+/*
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
+ *= require_self
+ *= require_tree .
+*/
5 app/assets/stylesheets/pages.css.scss
@@ -0,0 +1,5 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+ You can use Sass (SCSS) here: http://sass-lang.com/
+*/
3 app/controllers/application_controller.rb
@@ -0,0 +1,3 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery
+end
46 app/controllers/pages_controller.rb
@@ -0,0 +1,46 @@
+class PagesController < ApplicationController
+ respond_to :html, :json
+
+ def index
+ @pages = Page.all
+ respond_with @pages
+ end
+
+ def show
+ @page = Page.find(params[:id])
+ respond_with @page
+ end
+
+ def new
+ @page = Page.new
+ respond_with @page
+ end
+
+ def edit
+ @page = Page.find(params[:id])
+ end
+
+ def create
+ @page = Page.new(params[:page])
+ if @page.save
+ respond_with(@page, status: :created, location: @page)
+ else
+ respond_with(@page.errors, status: :unprocessable_entity)
+ end
+ end
+
+ def update
+ @page = Page.find(params[:id])
+ if @page.update_attributes(params[:page])
+ respond_with(@page)
+ else
+ respond_with(@page.errors, status: :unprocessable_entity)
+ end
+ end
+
+ def destroy
+ @page = Page.find(params[:id])
+ @page.destroy
+ head :ok
+ end
+end
2 app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
2 app/helpers/pages_helper.rb
@@ -0,0 +1,2 @@
+module PagesHelper
+end
0 app/mailers/.gitkeep
No changes.
0 app/models/.gitkeep
No changes.
3 app/models/page.rb
@@ -0,0 +1,3 @@
+class Page < ActiveRecord::Base
+ validates_presence_of :name, :slug, :body
+end
24 app/views/pages/index.html.erb
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Spine Rails3</title>
+ <%= stylesheet_link_tag "application" %>
+ <%= csrf_meta_tags %>
+
+ <script src="/javascripts/jquery.js" type="text/javascript" charset="utf-8"></script>
+ <script src="/javascripts/spine.js" type="text/javascript" charset="utf-8"></script>
+
+
+ <script src="/application.js" type="text/javascript" charset="utf-8"></script>
+ <script type="text/javascript" charset="utf-8">
+ var exports = this;
+ jQuery(function(){
+ var App = require("app");
+ exports.App = new App({el: $("body")});
+ });
+ </script>
+</head>
+<body>
+ <div id="pages"></div>
+</body>
+</html>
4 config.ru
@@ -0,0 +1,4 @@
+# This file is used by Rack-based servers to start the application.
+
+require ::File.expand_path('../config/environment', __FILE__)
+run SpineRails3::Application
49 config/application.rb
@@ -0,0 +1,49 @@
+require File.expand_path('../boot', __FILE__)
+
+require 'rails/all'
+
+# If you have a Gemfile, require the gems listed there, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(:default, Rails.env) if defined?(Bundler)
+
+module SpineRails3
+ class Application < Rails::Application
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+
+ # Custom directories with classes and modules you want to be autoloadable.
+ # config.autoload_paths += %W(#{config.root}/extras)
+
+ # Only load the plugins named here, in the order given (default is alphabetical).
+ # :all can be used as a placeholder for all plugins not explicitly named.
+ # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
+
+ # Activate observers that should always be running.
+ # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
+
+ # Please note that JavaScript expansions are *ignored altogether* if the asset
+ # pipeline is enabled (see config.assets.enabled below). Put your defaults in
+ # app/assets/javascripts/application.js in that case.
+ #
+ # JavaScript files you want as :defaults (application.js is always included).
+ # config.action_view.javascript_expansions[:defaults] = %w(prototype prototype_ujs)
+
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
+ # Configure sensitive parameters which will be filtered from the log file.
+ config.filter_parameters += [:password]
+
+ # Enable the asset pipeline
+ config.assets.enabled = true
+ end
+end
6 config/boot.rb
@@ -0,0 +1,6 @@
+require 'rubygems'
+
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+
+require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
25 config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+development:
+ adapter: sqlite3
+ database: db/development.sqlite3
+ pool: 5
+ timeout: 5000
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlite3
+ database: db/test.sqlite3
+ pool: 5
+ timeout: 5000
+
+production:
+ adapter: sqlite3
+ database: db/production.sqlite3
+ pool: 5
+ timeout: 5000
5 config/environment.rb
@@ -0,0 +1,5 @@
+# Load the rails application
+require File.expand_path('../application', __FILE__)
+
+# Initialize the rails application
+SpineRails3::Application.initialize!
24 config/environments/development.rb
@@ -0,0 +1,24 @@
+SpineRails3::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Log error messages when you accidentally call methods on nil.
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Don't care if the mailer can't send
+ config.action_mailer.raise_delivery_errors = false
+
+ # Print deprecation notices to the Rails logger
+ config.active_support.deprecation = :log
+
+ # Only use best-standards-support built into browsers
+ config.action_dispatch.best_standards_support = :builtin
+end
52 config/environments/production.rb
@@ -0,0 +1,52 @@
+SpineRails3::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # Code is not reloaded between requests
+ config.cache_classes = true
+
+ # Full error reports are disabled and caching is turned on
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Disable Rails's static asset server (Apache or nginx will already do this)
+ config.serve_static_assets = false
+
+ # Compress both stylesheets and JavaScripts
+ config.assets.js_compressor = :uglifier
+ config.assets.css_compressor = :scss
+
+ # Specifies the header that your server uses for sending files
+ # (comment out if your front-end server doesn't support this)
+ config.action_dispatch.x_sendfile_header = "X-Sendfile" # Use 'X-Accel-Redirect' for nginx
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # See everything in the log (default is :info)
+ # config.log_level = :debug
+
+ # Use a different logger for distributed setups
+ # config.logger = SyslogLogger.new
+
+ # Use a different cache store in production
+ # config.cache_store = :mem_cache_store
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server
+ # config.action_controller.asset_host = "http://assets.example.com"
+
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ # config.assets.precompile += %w( search.js )
+
+ # Disable delivery errors, bad email addresses will be ignored
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable threaded mode
+ # config.threadsafe!
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation can not be found)
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners
+ config.active_support.deprecation = :notify
+end
39 config/environments/test.rb
@@ -0,0 +1,39 @@
+SpineRails3::Application.configure do
+ # Settings specified here will take precedence over those in config/application.rb
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Configure static asset server for tests with Cache-Control for performance
+ config.serve_static_assets = true
+ config.static_cache_control = "public, max-age=3600"
+
+ # Log error messages when you accidentally call methods on nil
+ config.whiny_nils = true
+
+ # Show full error reports and disable caching
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment
+ config.action_controller.allow_forgery_protection = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Use SQL instead of Active Record's schema dumper when creating the test database.
+ # This is necessary if your schema can't be completely dumped by the schema dumper,
+ # like if you have constraints or database-specific column types
+ # config.active_record.schema_format = :sql
+
+ # Print deprecation notices to the stderr
+ config.active_support.deprecation = :stderr
+end
7 config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
10 config/initializers/inflections.rb
@@ -0,0 +1,10 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format
+# (all these examples are active by default):
+# ActiveSupport::Inflector.inflections do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
5 config/initializers/mime_types.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
+# Mime::Type.register_alias "text/html", :iphone
7 config/initializers/secret_token.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+SpineRails3::Application.config.secret_token = '877d90109fe035a16ac02b3e7ff4969bb5d53b8f58ca5e1b7ad0ce630b225318f0bf647e6fd8681274df726e1fae55815b815194f8bc93d873357d37dae4ac77'
8 config/initializers/session_store.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+SpineRails3::Application.config.session_store :cookie_store, key: '_spine.rails3_session'
+
+# Use the database for sessions instead of the cookie-based default,
+# which shouldn't be used to store highly confidential information
+# (create the session table with "rails generate session_migration")
+# SpineRails3::Application.config.session_store :active_record_store
17 config/initializers/stitch.rb
@@ -0,0 +1,17 @@
+class StitchApplication < Stitch::Server
+ def initialize
+ super(:paths => ["app/assets/javascripts/app", "app/assets/javascripts/lib"])
+ end
+end
+
+module Stitch
+ class TmplCompiler < Stitch::Compiler
+ extensions :tmpl
+
+ def compile(path)
+ content = File.read(path)
+ %{var template = jQuery.template(#{content.to_json});
+ module.exports = (function(data){ return jQuery.tmpl(template, data); });}
+ end
+ end
+end
12 config/initializers/wrap_parameters.rb
@@ -0,0 +1,12 @@
+# Be sure to restart your server when you modify this file.
+#
+# This file contains the settings for ActionController::ParametersWrapper
+# which will be enabled by default in the upcoming version of Ruby on Rails.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActionController::Base.wrap_parameters format: [:json]
+
+# Disable root element in JSON by default.
+if defined?(ActiveRecord)
+ ActiveRecord::Base.include_root_in_json = false
+end
5 config/locales/en.yml
@@ -0,0 +1,5 @@
+# Sample localization file for English. Add more files in this directory for other locales.
+# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+
+en:
+ hello: "Hello world"
61 config/routes.rb
@@ -0,0 +1,61 @@
+SpineRails3::Application.routes.draw do
+ # The priority is based upon order of creation:
+ # first created -> highest priority.
+
+ # Sample of regular route:
+ # match 'products/:id' => 'catalog#view'
+ # Keep in mind you can assign values other than :controller and :action
+
+ # Sample of named route:
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
+ # This route can be invoked with purchase_url(:id => product.id)
+
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # resources :products
+
+ # Sample resource route with options:
+ # resources :products do
+ # member do
+ # get 'short'
+ # post 'toggle'
+ # end
+ #
+ # collection do
+ # get 'sold'
+ # end
+ # end
+
+ # Sample resource route with sub-resources:
+ # resources :products do
+ # resources :comments, :sales
+ # resource :seller
+ # end
+
+ # Sample resource route with more complex sub-resources
+ # resources :products do
+ # resources :comments
+ # resources :sales do
+ # get 'recent', :on => :collection
+ # end
+ # end
+
+ # Sample resource route within a namespace:
+ # namespace :admin do
+ # # Directs /admin/products/* to Admin::ProductsController
+ # # (app/controllers/admin/products_controller.rb)
+ # resources :products
+ # end
+
+ # You can have the root of your site routed with "root"
+ # just remember to delete public/index.html.
+ # root :to => 'welcome#index'
+
+ # See how all your routes lay out with "rake routes"
+
+ resources :pages
+
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
+ # Note: This route will make all actions in every controller accessible via GET requests.
+ # match ':controller(/:action(/:id(.:format)))'
+ match '/application.js' => StitchApplication.new
+end
11 db/migrate/20110616103504_create_pages.rb
@@ -0,0 +1,11 @@
+class CreatePages < ActiveRecord::Migration
+ def change
+ create_table :pages do |t|
+ t.string :name
+ t.string :slug
+ t.text :body
+
+ t.timestamps
+ end
+ end
+end
23 db/schema.rb
@@ -0,0 +1,23 @@
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your
+# database schema. If you need to create the application database on another
+# system, you should be using db:schema:load, not running all the migrations
+# from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 20110616103504) do
+
+ create_table "pages", :force => true do |t|
+ t.string "name"
+ t.string "slug"
+ t.text "body"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+end
7 db/seeds.rb
@@ -0,0 +1,7 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
+# Mayor.create(name: 'Emanuel', city: cities.first)
2 doc/README_FOR_APP
@@ -0,0 +1,2 @@
+Use this README file to introduce your application and point to useful places in the API for learning more.
+Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
0 lib/tasks/.gitkeep
No changes.
0 log/.gitkeep
No changes.
26 public/404.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The page you were looking for doesn't exist (404)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/404.html -->
+ <div class="dialog">
+ <h1>The page you were looking for doesn't exist.</h1>
+ <p>You may have mistyped the address or the page may have moved.</p>
+ </div>
+</body>
+</html>
26 public/422.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The change you wanted was rejected (422)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/422.html -->
+ <div class="dialog">
+ <h1>The change you wanted was rejected.</h1>
+ <p>Maybe you tried to change something you didn't have access to.</p>
+ </div>
+</body>
+</html>
26 public/500.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>We're sorry, but something went wrong (500)</title>
+ <style type="text/css">
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+ div.dialog {
+ width: 25em;
+ padding: 0 4em;
+ margin: 4em auto 0 auto;
+ border: 1px solid #ccc;
+ border-right-color: #999;
+ border-bottom-color: #999;
+ }
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+ </style>
+</head>
+
+<body>
+ <!-- This file lives in public/500.html -->
+ <div class="dialog">
+ <h1>We're sorry, but something went wrong.</h1>
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
+ </div>
+</body>
+</html>
0 public/favicon.ico
No changes.
241 public/index.html
@@ -0,0 +1,241 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Ruby on Rails: Welcome aboard</title>
+ <style type="text/css" media="screen">
+ body {
+ margin: 0;