├── lib └── tasks │ └── .gitkeep ├── public ├── favicon.ico ├── images │ └── destroy.png ├── robots.txt ├── 422.html ├── 404.html ├── 500.html └── SpecRunner.html ├── .rspec ├── vendor ├── plugins │ └── .gitkeep └── assets │ ├── stylesheets │ └── jasmine.css │ └── javascripts │ ├── jasmine-html.js │ ├── underscore.js │ ├── backbone.js │ └── handlebars.js ├── app ├── assets │ ├── javascripts │ │ ├── foo.js │ │ ├── application.js.coffee │ │ ├── main.js.coffee │ │ ├── views │ │ │ ├── helpers.coffee │ │ │ └── todo_view.coffee │ │ └── models │ │ │ └── todo.coffee │ └── stylesheets │ │ ├── .gitkeep │ │ ├── application.css │ │ ├── scaffold.css │ │ └── todos.css ├── helpers │ └── application_helper.rb ├── controllers │ ├── application_controller.rb │ └── todos_controller.rb ├── models │ └── todo.rb └── views │ ├── layouts │ └── application.html.haml │ └── todos │ └── index.html.haml ├── config ├── initializers │ ├── backbone.rb │ ├── mime_types.rb │ ├── inflections.rb │ ├── backtrace_silencers.rb │ ├── session_store.rb │ └── secret_token.rb ├── environment.rb ├── locales │ └── en.yml ├── boot.rb ├── assets.yml ├── database.yml ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb ├── routes.rb └── application.rb ├── spec ├── assets │ └── javascripts │ │ ├── specs.js.coffee │ │ ├── spec_helper.coffee │ │ └── todo_spec.coffee ├── models │ └── todo_spec.rb ├── javascripts │ └── support │ │ ├── jasmine_runner.rb │ │ ├── jasmine_config.rb │ │ └── jasmine.yml └── spec_helper.rb ├── config.ru ├── .gitignore ├── doc └── README_FOR_APP ├── Rakefile ├── script └── rails ├── db ├── migrate │ └── 20101104122721_create_todos.rb ├── seeds.rb └── schema.rb ├── Gemfile ├── README.md └── Gemfile.lock /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/javascripts/foo.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /config/initializers/backbone.rb: -------------------------------------------------------------------------------- 1 | # for Backbone.js 2 | ActiveRecord::Base.include_root_in_json = false 3 | -------------------------------------------------------------------------------- /public/images/destroy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superchris/backbone_coffeescript_demo/HEAD/public/images/destroy.png -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /app/models/todo.rb: -------------------------------------------------------------------------------- 1 | class Todo < ActiveRecord::Base 2 | attr_accessible :content, :order, :done 3 | 4 | validates_presence_of :content 5 | 6 | end 7 | -------------------------------------------------------------------------------- /spec/assets/javascripts/specs.js.coffee: -------------------------------------------------------------------------------- 1 | #= require main 2 | #= require jasmine 3 | #= require jasmine-html 4 | #= require spec_helper 5 | #= require_tree . -------------------------------------------------------------------------------- /app/assets/javascripts/application.js.coffee: -------------------------------------------------------------------------------- 1 | window.TodoApp = {} 2 | 3 | $ -> 4 | TodoApp.appView = new TodoApp.AppView collection: new TodoApp.TodoList 5 | -------------------------------------------------------------------------------- /app/assets/javascripts/main.js.coffee: -------------------------------------------------------------------------------- 1 | #= require jquery 2 | #= require underscore 3 | #= require backbone 4 | #= require handlebars 5 | #= require application 6 | #= require_tree . -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run BackboneTodo::Application 5 | -------------------------------------------------------------------------------- /spec/assets/javascripts/spec_helper.coffee: -------------------------------------------------------------------------------- 1 | beforeEach -> 2 | @addMatchers 3 | toBeEmpty: -> this.actual.length == 0 4 | 5 | toInclude: (value) -> _.include @actual, value 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | .livereload 3 | .rvmrc 4 | .DS_Store 5 | db/*.sqlite3 6 | log/*.log 7 | tmp/**/* 8 | /public/javascripts/compiled 9 | /spec/javascripts/compiled 10 | .redcar 11 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | BackboneTodo::Application.initialize! 6 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Backbone Demo: Todos 5 | = stylesheet_link_tag "application" 6 | = javascript_include_tag "main" 7 | %body 8 | = yield 9 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /spec/models/todo_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Todo do 4 | describe "validations" do 5 | it "should not be valid without content" do 6 | todo = Todo.new 7 | todo.should_not be_valid 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake' 6 | 7 | BackboneTodo::Application.load_tasks 8 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /db/migrate/20101104122721_create_todos.rb: -------------------------------------------------------------------------------- 1 | class CreateTodos < ActiveRecord::Migration 2 | def self.up 3 | create_table :todos do |t| 4 | t.string :content 5 | t.integer :order 6 | t.boolean :done 7 | 8 | t.timestamps 9 | end 10 | end 11 | 12 | def self.down 13 | drop_table :todos 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) 7 | # Mayor.create(:name => 'Daley', :city => cities.first) 8 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | gemfile = File.expand_path('../../Gemfile', __FILE__) 5 | begin 6 | ENV['BUNDLE_GEMFILE'] = gemfile 7 | require 'bundler' 8 | Bundler.setup 9 | rescue Bundler::GemNotFound => e 10 | STDERR.puts e.message 11 | STDERR.puts "Try running `bundle install`." 12 | exit! 13 | end if File.exist?(gemfile) 14 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll automatically include all the stylesheets available in this directory 3 | * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at 4 | * the top of the compiled file, but it's generally better to create a new file per style scope. 5 | *= require_self 6 | *= require_tree . 7 | */ -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'rails', '>=3.1.rc' 4 | 5 | gem 'rake', '0.8.7' 6 | 7 | gem 'sqlite3', :require => 'sqlite3' 8 | 9 | gem 'haml' 10 | gem 'jquery-rails' 11 | gem 'json' 12 | gem 'sass' 13 | gem 'coffee-script' 14 | gem 'uglifier' 15 | 16 | group :development, :test do 17 | gem 'awesome_print', :require => 'ap' 18 | gem 'rspec-rails', '~> 2.0' 19 | gem 'jasmine' 20 | end 21 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | BackboneTodo::Application.config.session_store :cookie_store, :key => '_backbone_todo_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rake db:sessions:create") 8 | # BackboneTodo::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/assets.yml: -------------------------------------------------------------------------------- 1 | javascripts: 2 | all: 3 | - public/javascripts/vendor/jquery.js 4 | - public/javascripts/vendor/underscore.js 5 | - public/javascripts/vendor/backbone.js 6 | - public/javascripts/vendor/handlebars.js 7 | - public/javascripts/compiled/application.js 8 | - public/javascripts/compiled/views/helpers.js 9 | - public/javascripts/compiled/models/*.js 10 | - public/javascripts/compiled/views/*.js 11 | 12 | stylesheets: 13 | all: 14 | - public/stylesheets/todos.css 15 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | BackboneTodo::Application.config.secret_token = '9422d2583d9d5d904ef9dac500548208637b43057a9bffefdb33f79c34b395958a2730041646bc4dfd63da0d8acee64ef11a8ac13dd5dfd31642b1d7ffc65c3e' 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/views/helpers.coffee: -------------------------------------------------------------------------------- 1 | # Compile and store template function from DOM element with specified selector 2 | TodoApp.template = (selector) -> 3 | template = null 4 | -> 5 | template ?= Handlebars.compile(if selector.charAt(0) == "#" then $(selector).html() else selector) 6 | template.apply this, arguments 7 | 8 | Handlebars.registerHelper 'debug', (ctx) -> 9 | context = typeof ctx == "function" ? ctx.call this : ctx 10 | console.log context 11 | 12 | 13 | Handlebars.registerHelper 'pluralize', (count, word, fn) -> 14 | if count != 1 then word+"s" else word 15 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3-ruby (not necessary on OS X Leopard) 3 | development: 4 | adapter: sqlite3 5 | database: db/development.sqlite3 6 | pool: 5 7 | timeout: 5000 8 | 9 | # Warning: The database defined as "test" will be erased and 10 | # re-generated from your development database when you run "rake". 11 | # Do not set this db to the same as development or production. 12 | test: 13 | adapter: sqlite3 14 | database: db/test.sqlite3 15 | pool: 5 16 | timeout: 5000 17 | 18 | production: 19 | adapter: sqlite3 20 | database: db/production.sqlite3 21 | pool: 5 22 | timeout: 5000 23 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine_runner.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes 2 | 3 | require 'rubygems' 4 | require 'jasmine' 5 | jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb')) 6 | require jasmine_config_overrides if File.exists?(jasmine_config_overrides) 7 | 8 | jasmine_config = Jasmine::Config.new 9 | spec_builder = Jasmine::SpecBuilder.new(jasmine_config) 10 | 11 | should_stop = false 12 | 13 | Spec::Runner.configure do |config| 14 | config.after(:suite) do 15 | spec_builder.stop if should_stop 16 | end 17 | end 18 | 19 | spec_builder.start 20 | should_stop = true 21 | spec_builder.declare_suites -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /app/controllers/todos_controller.rb: -------------------------------------------------------------------------------- 1 | class TodosController < ApplicationController 2 | respond_to :html, :json 3 | 4 | def index 5 | respond_to do |format| 6 | format.html 7 | format.json { respond_with Todo.all } 8 | end 9 | end 10 | 11 | def show 12 | respond_with Todo.find(params[:id]) 13 | end 14 | 15 | def create 16 | todo = Todo.new(params) 17 | if todo.save 18 | respond_with todo 19 | else 20 | render :json => todo.errors, :status => :unprocessable_entity 21 | end 22 | end 23 | 24 | def update 25 | todo = Todo.find(params[:id]) 26 | 27 | if todo.update_attributes(params) 28 | respond_with todo 29 | else 30 | render :json => todo.errors, :status => :unprocessable_entity 31 | end 32 | end 33 | 34 | def destroy 35 | todo = Todo.find(params[:id]) 36 | todo.destroy 37 | render :json => nil 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended to check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(:version => 20101104122721) do 14 | 15 | create_table "todos", :force => true do |t| 16 | t.string "content" 17 | t.integer "order" 18 | t.boolean "done" 19 | t.datetime "created_at" 20 | t.datetime "updated_at" 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /public/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Jasmine Test Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= 'test' 3 | require File.expand_path("../../config/environment", __FILE__) 4 | require 'rspec/rails' 5 | 6 | # Requires supporting ruby files with custom matchers and macros, etc, 7 | # in spec/support/ and its subdirectories. 8 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 9 | 10 | RSpec.configure do |config| 11 | # == Mock Framework 12 | # 13 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 14 | # 15 | # config.mock_with :mocha 16 | # config.mock_with :flexmock 17 | # config.mock_with :rr 18 | config.mock_with :rspec 19 | 20 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 21 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 22 | 23 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 24 | # examples within a transaction, remove the following line or assign false 25 | # instead of true. 26 | config.use_transactional_fixtures = true 27 | end 28 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | BackboneTodo::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the webserver when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | # config.action_view.debug_rjs = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Don't care if the mailer can't send 18 | config.action_mailer.raise_delivery_errors = false 19 | 20 | # Print deprecation notices to the Rails logger 21 | config.active_support.deprecation = :log 22 | 23 | # Only use best-standards-support built into browsers 24 | config.action_dispatch.best_standards_support = :builtin 25 | end 26 | 27 | -------------------------------------------------------------------------------- /spec/assets/javascripts/todo_spec.coffee: -------------------------------------------------------------------------------- 1 | describe "Todo", -> 2 | todo = null 3 | ajaxCall = (param) -> jQuery.ajax.mostRecentCall.args[0][param] 4 | 5 | beforeEach -> 6 | todo = new TodoApp.Todo 7 | todos = new TodoApp.TodoList [todo] 8 | 9 | it "should initialize with empty content", -> 10 | expect(todo.get "content").toEqual "empty todo..." 11 | 12 | it "should initialize as not done", -> 13 | expect(todo.get "done").toBeFalsy() 14 | 15 | it "should save after toggle", -> 16 | spyOn jQuery, "ajax" 17 | todo.toggle() 18 | expect(ajaxCall "url").toEqual "/todos" 19 | expect(todo.get "done").toBeTruthy() 20 | 21 | describe "TodoList", -> 22 | attributes = [ 23 | content: "First" 24 | done: true 25 | , 26 | content: "Second" 27 | ] 28 | todos = null 29 | 30 | beforeEach -> 31 | todos = new TodoApp.TodoList attributes 32 | 33 | it "should return done todos", -> 34 | expect(_.invoke todos.done(), "toJSON").toEqual [attributes[0]] 35 | 36 | it "should return remaining todos", -> 37 | expect(_.invoke todos.remaining(), "toJSON").toEqual [attributes[1]] 38 | -------------------------------------------------------------------------------- /app/views/todos/index.html.haml: -------------------------------------------------------------------------------- 1 | #todoapp 2 | .title 3 | %h1 Todos 4 | .content 5 | #create-todo 6 | %input#new-todo{:placeholder => "What needs to be done?", :type => "text"}/ 7 | %span.ui-tooltip-top{:style => "display:none;"} Press Enter to save this task 8 | #todos 9 | %ul#todo-list 10 | #todo-stats 11 | %ul#instructions 12 | %li Double-click to edit a todo. 13 | 14 | %script#item-template{:type => "text/html"} 15 | .todo{:class => "{{#done}}done{{/done}}"} 16 | .display 17 | %input{:class => "check", :type => "checkbox", :"{{#done}}checked{{/done}}" => true} 18 | .todo-content 19 | %span.todo-destroy 20 | .edit 21 | %input.todo-input{:type => "text", :value => ""} 22 | 23 | %script#stats-template{:type => "text/html"} 24 | {{#if total}} 25 | %span.todo-count 26 | %span.number {{remaining}} 27 | %span.word {{pluralize remaining "item"}} 28 | left. 29 | {{/if}} 30 | {{#if done}} 31 | %span.todo-clear 32 | %a{:href => "#"} 33 | Clear 34 | %span.number-done {{done}} 35 | completed 36 | %span.word-done {{pluralize done "item"}} 37 | {{/if}} 38 | -------------------------------------------------------------------------------- /app/assets/stylesheets/scaffold.css: -------------------------------------------------------------------------------- 1 | body { background-color: #fff; color: #333; } 2 | 3 | body, p, ol, ul, td { 4 | font-family: verdana, arial, helvetica, sans-serif; 5 | font-size: 13px; 6 | line-height: 18px; 7 | } 8 | 9 | pre { 10 | background-color: #eee; 11 | padding: 10px; 12 | font-size: 11px; 13 | } 14 | 15 | a { color: #000; } 16 | a:visited { color: #666; } 17 | a:hover { color: #fff; background-color:#000; } 18 | 19 | div.field, div.actions { 20 | margin-bottom: 10px; 21 | } 22 | 23 | #notice { 24 | color: green; 25 | } 26 | 27 | .field_with_errors { 28 | padding: 2px; 29 | background-color: red; 30 | display: table; 31 | } 32 | 33 | #error_explanation { 34 | width: 450px; 35 | border: 2px solid red; 36 | padding: 7px; 37 | padding-bottom: 0; 38 | margin-bottom: 20px; 39 | background-color: #f0f0f0; 40 | } 41 | 42 | #error_explanation h2 { 43 | text-align: left; 44 | font-weight: bold; 45 | padding: 5px 5px 5px 15px; 46 | font-size: 12px; 47 | margin: -7px; 48 | margin-bottom: 0px; 49 | background-color: #c00; 50 | color: #fff; 51 | } 52 | 53 | #error_explanation ul li { 54 | font-size: 12px; 55 | list-style: square; 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | This is sample Rails application that demonstrates usage of 5 | 6 | * [Backbone.js](http://documentcloud.github.com/backbone/) 7 | * [CoffeeScript](http://jashkenas.github.com/coffee-script/) 8 | * [Jasmine](http://pivotal.github.com/jasmine/) 9 | * Rails 3.1 asset pipeline 10 | 11 | This code is from the demo used during [RailsWayCon 2011](http://railswaycon.com/2011/sessions#session-17838) conference presentation [Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine](http://www.slideshare.net/rsim/railslike-javascript-using-coffeescript-backbonejs-and-jasmine-8196890). 12 | Source code is based on original [Todos application](http://documentcloud.github.com/backbone/docs/todos.html) by [Jérôme Gravel-Niquet](https://github.com/jeromegn). 13 | 14 | In this fork, I've moved it around and upgraded it to rails 3.1 15 | 16 | Running application 17 | =================== 18 | 19 | Install gems, run migrations and start application with 20 | 21 | bundle install 22 | rake db:migrate 23 | rails server 24 | 25 | And then visit `http://localhost:3000`. 26 | 27 | Running tests 28 | ============= 29 | 30 | Run `rake jasmine` and then visit `http://localhost:8888` to execute Jasmine tests and see results. 31 | 32 | Application source code 33 | ======================= 34 | 35 | Backbone.js models and views written in CoffeeScript are located in `app/assets/javascripts` directory and Jasmine tests are located in `spec/coffeescripts` directory. 36 | -------------------------------------------------------------------------------- /app/assets/javascripts/models/todo.coffee: -------------------------------------------------------------------------------- 1 | # Todo Model 2 | # ---------- 3 | 4 | # Our basic **Todo** model has `content`, `order`, and `done` attributes. 5 | class TodoApp.Todo extends Backbone.Model 6 | 7 | # If you don't provide a todo, one will be provided for you. 8 | EMPTY: "empty todo..." 9 | 10 | # Ensure that each todo created has `content`. 11 | initialize: -> 12 | unless @get "content" 13 | @set content: @EMPTY 14 | 15 | # Toggle the `done` state of this todo item. 16 | toggle: -> 17 | @save done: not @get "done" 18 | 19 | 20 | # Todo Collection 21 | # --------------- 22 | # 23 | # The collection of todos is backed by TodosController 24 | # 25 | class TodoApp.TodoList extends Backbone.Collection 26 | 27 | # Reference to this collection's model. 28 | model: TodoApp.Todo 29 | 30 | # Save all of the todo items under the `"todos"` namespace. 31 | url: '/todos' 32 | 33 | # Filter down the list of all todo items that are finished. 34 | done: -> 35 | @filter (todo) -> todo.get 'done' 36 | 37 | # Filter down the list to only todo items that are still not finished. 38 | remaining: -> 39 | @without this.done()... 40 | 41 | # We keep the Todos in sequential order, despite being saved by unordered 42 | # GUID in the database. This generates the next order number for new items. 43 | nextOrder: -> 44 | if @length then @last().get('order') + 1 else 1 45 | 46 | # Todos are sorted by their original insertion order. 47 | comparator: (todo) -> 48 | todo.get 'order' 49 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine_config.rb: -------------------------------------------------------------------------------- 1 | require 'barista' 2 | require 'logger' 3 | 4 | require File.join(Rails.root, 'config/initializers/barista_config') 5 | Barista.configure do |c| 6 | c.env = 'test' 7 | c.logger = Logger.new(STDOUT) 8 | c.logger.level = Logger::INFO 9 | c.before_compilation do |path| 10 | relative_path = Pathname(path).relative_path_from(Rails.root) 11 | c.logger.info "[#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}] Barista: Compiling #{relative_path}" 12 | end 13 | end 14 | Barista.setup_defaults 15 | 16 | module Jasmine 17 | def self.app(config) 18 | Barista::Framework.register 'jasmine', File.expand_path('../coffeescripts', config.spec_dir) 19 | Barista::Framework['jasmine'].instance_variable_set('@output_root', Pathname(config.spec_dir).join('compiled')) 20 | 21 | Rack::Builder.app do 22 | use Rack::Head 23 | 24 | map('/run.html') { run Jasmine::Redirect.new('/') } 25 | map('/__suite__') { run Barista::Filter.new(Jasmine::FocusedSuite.new(config)) } 26 | 27 | map('/__JASMINE_ROOT__') { run Rack::File.new(Jasmine.root) } 28 | map(config.spec_path) { run Rack::File.new(config.spec_dir) } 29 | map(config.root_path) { run Rack::File.new(config.project_root) } 30 | 31 | map('/favicon.ico') { run Rack::File.new(File.join(Rails.root, 'public', 'favicon.ico')) } 32 | 33 | map('/') do 34 | run Rack::Cascade.new([ 35 | Rack::URLMap.new('/' => Rack::File.new(config.src_dir)), 36 | Barista::Filter.new(Jasmine::RunAdapter.new(config)) 37 | ]) 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | BackboneTodo::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Log error messages when you accidentally call methods on nil. 11 | config.whiny_nils = true 12 | 13 | # Show full error reports and disable caching 14 | config.consider_all_requests_local = true 15 | config.action_controller.perform_caching = false 16 | 17 | # Raise exceptions instead of rendering exception templates 18 | config.action_dispatch.show_exceptions = false 19 | 20 | # Disable request forgery protection in test environment 21 | config.action_controller.allow_forgery_protection = false 22 | 23 | # Tell Action Mailer not to deliver emails to the real world. 24 | # The :test delivery method accumulates sent emails in the 25 | # ActionMailer::Base.deliveries array. 26 | config.action_mailer.delivery_method = :test 27 | 28 | # Use SQL instead of Active Record's schema dumper when creating the test database. 29 | # This is necessary if your schema can't be completely dumped by the schema dumper, 30 | # like if you have constraints or database-specific column types 31 | # config.active_record.schema_format = :sql 32 | 33 | # Print deprecation notices to the stderr 34 | config.active_support.deprecation = :stderr 35 | end 36 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | # src_files 2 | # 3 | # Return an array of filepaths relative to src_dir to include before jasmine specs. 4 | # Default: [] 5 | # 6 | # EXAMPLE: 7 | # 8 | # src_files: 9 | # - lib/source1.js 10 | # - lib/source2.js 11 | # - dist/**/*.js 12 | # 13 | src_files: 14 | - public/javascripts/vendor/jquery.js 15 | - public/javascripts/vendor/underscore.js 16 | - public/javascripts/vendor/backbone.js 17 | - public/javascripts/vendor/handlebars.js 18 | - public/javascripts/compiled/application.js 19 | - public/javascripts/compiled/models/*.js 20 | 21 | # stylesheets 22 | # 23 | # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. 24 | # Default: [] 25 | # 26 | # EXAMPLE: 27 | # 28 | # stylesheets: 29 | # - css/style.css 30 | # - stylesheets/*.css 31 | # 32 | stylesheets: 33 | - stylesheets/**/*.css 34 | 35 | # helpers 36 | # 37 | # Return an array of filepaths relative to spec_dir to include before jasmine specs. 38 | # Default: ["helpers/**/*.js"] 39 | # 40 | # EXAMPLE: 41 | # 42 | # helpers: 43 | # - helpers/**/*.js 44 | # 45 | helpers: 46 | - compiled/helpers/**/*.js 47 | - helpers/**/*.js 48 | 49 | # spec_files 50 | # 51 | # Return an array of filepaths relative to spec_dir to include. 52 | # Default: ["**/*[sS]pec.js"] 53 | # 54 | # EXAMPLE: 55 | # 56 | # spec_files: 57 | # - **/*[sS]pec.js 58 | # 59 | spec_files: 60 | - **/*[sS]pec.js 61 | 62 | # src_dir 63 | # 64 | # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. 65 | # Default: project root 66 | # 67 | # EXAMPLE: 68 | # 69 | # src_dir: public 70 | # 71 | src_dir: 72 | 73 | # spec_dir 74 | # 75 | # Spec directory path. Your spec_files must be returned relative to this path. 76 | # Default: spec/javascripts 77 | # 78 | # EXAMPLE: 79 | # 80 | # spec_dir: spec/javascripts 81 | # 82 | spec_dir: spec/javascripts 83 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | BackboneTodo::Application.configure do 2 | # Settings specified here will take precedence over those in config/environment.rb 3 | 4 | # The production environment is meant for finished, "live" apps. 5 | # Code is not reloaded between requests 6 | config.cache_classes = true 7 | 8 | # Full error reports are disabled and caching is turned on 9 | config.consider_all_requests_local = false 10 | config.action_controller.perform_caching = true 11 | 12 | # Specifies the header that your server uses for sending files 13 | config.action_dispatch.x_sendfile_header = "X-Sendfile" 14 | 15 | # For nginx: 16 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' 17 | 18 | # If you have no front-end server that supports something like X-Sendfile, 19 | # just comment this out and Rails will serve the files 20 | 21 | # See everything in the log (default is :info) 22 | # config.log_level = :debug 23 | 24 | # Use a different logger for distributed setups 25 | # config.logger = SyslogLogger.new 26 | 27 | # Use a different cache store in production 28 | # config.cache_store = :mem_cache_store 29 | 30 | # Disable Rails's static asset server 31 | # In production, Apache or nginx will already do this 32 | config.serve_static_assets = false 33 | 34 | # Enable serving of images, stylesheets, and javascripts from an asset server 35 | # config.action_controller.asset_host = "http://assets.example.com" 36 | 37 | # Disable delivery errors, bad email addresses will be ignored 38 | # config.action_mailer.raise_delivery_errors = false 39 | 40 | # Enable threaded mode 41 | # config.threadsafe! 42 | 43 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 44 | # the I18n.default_locale when a translation can not be found) 45 | config.i18n.fallbacks = true 46 | 47 | # Send deprecation notices to registered listeners 48 | config.active_support.deprecation = :notify 49 | end 50 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | BackboneTodo::Application.routes.draw do 2 | root :to => "todos#index" 3 | 4 | resources :todos 5 | 6 | # The priority is based upon order of creation: 7 | # first created -> highest priority. 8 | 9 | # Sample of regular route: 10 | # match 'products/:id' => 'catalog#view' 11 | # Keep in mind you can assign values other than :controller and :action 12 | 13 | # Sample of named route: 14 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 15 | # This route can be invoked with purchase_url(:id => product.id) 16 | 17 | # Sample resource route (maps HTTP verbs to controller actions automatically): 18 | # resources :products 19 | 20 | # Sample resource route with options: 21 | # resources :products do 22 | # member do 23 | # get 'short' 24 | # post 'toggle' 25 | # end 26 | # 27 | # collection do 28 | # get 'sold' 29 | # end 30 | # end 31 | 32 | # Sample resource route with sub-resources: 33 | # resources :products do 34 | # resources :comments, :sales 35 | # resource :seller 36 | # end 37 | 38 | # Sample resource route with more complex sub-resources 39 | # resources :products do 40 | # resources :comments 41 | # resources :sales do 42 | # get 'recent', :on => :collection 43 | # end 44 | # end 45 | 46 | # Sample resource route within a namespace: 47 | # namespace :admin do 48 | # # Directs /admin/products/* to Admin::ProductsController 49 | # # (app/controllers/admin/products_controller.rb) 50 | # resources :products 51 | # end 52 | 53 | # You can have the root of your site routed with "root" 54 | # just remember to delete public/index.html. 55 | # root :to => "welcome#index" 56 | 57 | # See how all your routes lay out with "rake routes" 58 | 59 | # This is a legacy wild controller route that's not recommended for RESTful applications. 60 | # Note: This route will make all actions in every controller accessible via GET requests. 61 | # match ':controller(/:action(/:id(.:format)))' 62 | end 63 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # If you have a Gemfile, require the gems listed there, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(:default, Rails.env) if defined?(Bundler) 8 | 9 | module BackboneTodo 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Custom directories with classes and modules you want to be autoloadable. 16 | # config.autoload_paths += %W(#{config.root}/extras) 17 | 18 | # Only load the plugins named here, in the order given (default is alphabetical). 19 | # :all can be used as a placeholder for all plugins not explicitly named. 20 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 21 | 22 | # Activate observers that should always be running. 23 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 24 | 25 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 26 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 27 | # config.time_zone = 'Central Time (US & Canada)' 28 | 29 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 30 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 31 | # config.i18n.default_locale = :de 32 | 33 | # JavaScript files you want as :defaults (application.js is always included). 34 | # config.action_view.javascript_expansions[:defaults] = %w(jquery rails) 35 | 36 | # Configure the default encoding used in templates for Ruby 1.9. 37 | config.encoding = "utf-8" 38 | 39 | # Configure sensitive parameters which will be filtered from the log file. 40 | config.filter_parameters += [:password] 41 | 42 | config.generators do |g| 43 | g.test_framework :rspec, :fixture => false, 44 | :view_specs => false, :controller_specs => false, :helper_specs => false, 45 | :route_specs => false, :request_specs => false 46 | end 47 | 48 | config.assets.enabled = true 49 | config.assets.paths << Rails.root.join("spec", "assets", "javascripts").to_s 50 | config.assets.paths << Rails.root.join("spec", "assets", "stylesheets").to_s 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/jasmine.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; 3 | } 4 | 5 | 6 | .jasmine_reporter a:visited, .jasmine_reporter a { 7 | color: #303; 8 | } 9 | 10 | .jasmine_reporter a:hover, .jasmine_reporter a:active { 11 | color: blue; 12 | } 13 | 14 | .run_spec { 15 | float:right; 16 | padding-right: 5px; 17 | font-size: .8em; 18 | text-decoration: none; 19 | } 20 | 21 | .jasmine_reporter { 22 | margin: 0 5px; 23 | } 24 | 25 | .banner { 26 | color: #303; 27 | background-color: #fef; 28 | padding: 5px; 29 | } 30 | 31 | .logo { 32 | float: left; 33 | font-size: 1.1em; 34 | padding-left: 5px; 35 | } 36 | 37 | .logo .version { 38 | font-size: .6em; 39 | padding-left: 1em; 40 | } 41 | 42 | .runner.running { 43 | background-color: yellow; 44 | } 45 | 46 | 47 | .options { 48 | text-align: right; 49 | font-size: .8em; 50 | } 51 | 52 | 53 | 54 | 55 | .suite { 56 | border: 1px outset gray; 57 | margin: 5px 0; 58 | padding-left: 1em; 59 | } 60 | 61 | .suite .suite { 62 | margin: 5px; 63 | } 64 | 65 | .suite.passed { 66 | background-color: #dfd; 67 | } 68 | 69 | .suite.failed { 70 | background-color: #fdd; 71 | } 72 | 73 | .spec { 74 | margin: 5px; 75 | padding-left: 1em; 76 | clear: both; 77 | } 78 | 79 | .spec.failed, .spec.passed, .spec.skipped { 80 | padding-bottom: 5px; 81 | border: 1px solid gray; 82 | } 83 | 84 | .spec.failed { 85 | background-color: #fbb; 86 | border-color: red; 87 | } 88 | 89 | .spec.passed { 90 | background-color: #bfb; 91 | border-color: green; 92 | } 93 | 94 | .spec.skipped { 95 | background-color: #bbb; 96 | } 97 | 98 | .messages { 99 | border-left: 1px dashed gray; 100 | padding-left: 1em; 101 | padding-right: 1em; 102 | } 103 | 104 | .passed { 105 | background-color: #cfc; 106 | display: none; 107 | } 108 | 109 | .failed { 110 | background-color: #fbb; 111 | } 112 | 113 | .skipped { 114 | color: #777; 115 | background-color: #eee; 116 | display: none; 117 | } 118 | 119 | 120 | /*.resultMessage {*/ 121 | /*white-space: pre;*/ 122 | /*}*/ 123 | 124 | .resultMessage span.result { 125 | display: block; 126 | line-height: 2em; 127 | color: black; 128 | } 129 | 130 | .resultMessage .mismatch { 131 | color: black; 132 | } 133 | 134 | .stackTrace { 135 | white-space: pre; 136 | font-size: .8em; 137 | margin-left: 10px; 138 | max-height: 5em; 139 | overflow: auto; 140 | border: 1px inset red; 141 | padding: 1em; 142 | background: #eef; 143 | } 144 | 145 | .finished-at { 146 | padding-left: 1em; 147 | font-size: .6em; 148 | } 149 | 150 | .show-passed .passed, 151 | .show-skipped .skipped { 152 | display: block; 153 | } 154 | 155 | 156 | #jasmine_content { 157 | position:fixed; 158 | right: 100%; 159 | } 160 | 161 | .runner { 162 | border: 1px solid gray; 163 | display: block; 164 | margin: 5px 0; 165 | padding: 2px 0 2px 10px; 166 | } 167 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | actionmailer (3.1.0.rc1) 5 | actionpack (= 3.1.0.rc1) 6 | mail (~> 2.3.0) 7 | actionpack (3.1.0.rc1) 8 | activemodel (= 3.1.0.rc1) 9 | activesupport (= 3.1.0.rc1) 10 | builder (~> 3.0.0) 11 | erubis (~> 2.7.0) 12 | i18n (~> 0.6.0beta1) 13 | rack (~> 1.3.0.beta2) 14 | rack-cache (~> 1.0.1) 15 | rack-mount (~> 0.8.1) 16 | rack-test (~> 0.6.0) 17 | sprockets (~> 2.0.0.beta.5) 18 | tzinfo (~> 0.3.27) 19 | activemodel (3.1.0.rc1) 20 | activesupport (= 3.1.0.rc1) 21 | bcrypt-ruby (~> 2.1.4) 22 | builder (~> 3.0.0) 23 | i18n (~> 0.6.0beta1) 24 | activerecord (3.1.0.rc1) 25 | activemodel (= 3.1.0.rc1) 26 | activesupport (= 3.1.0.rc1) 27 | arel (~> 2.1.1) 28 | tzinfo (~> 0.3.27) 29 | activeresource (3.1.0.rc1) 30 | activemodel (= 3.1.0.rc1) 31 | activesupport (= 3.1.0.rc1) 32 | activesupport (3.1.0.rc1) 33 | multi_json (~> 1.0) 34 | arel (2.1.1) 35 | awesome_print (0.4.0) 36 | bcrypt-ruby (2.1.4) 37 | builder (3.0.0) 38 | childprocess (0.1.9) 39 | ffi (~> 1.0.6) 40 | coffee-script (2.2.0) 41 | coffee-script-source 42 | execjs 43 | coffee-script-source (1.1.1) 44 | diff-lcs (1.1.2) 45 | erubis (2.7.0) 46 | execjs (1.1.2) 47 | multi_json (~> 1.0) 48 | ffi (1.0.9) 49 | haml (3.1.1) 50 | hike (1.0.0) 51 | i18n (0.6.0) 52 | jasmine (1.0.2.1) 53 | json_pure (>= 1.4.3) 54 | rack (>= 1.1) 55 | rspec (>= 1.3.1) 56 | selenium-webdriver (>= 0.1.3) 57 | jquery-rails (1.0.9) 58 | railties (~> 3.0) 59 | thor (~> 0.14) 60 | json (1.5.1) 61 | json_pure (1.5.1) 62 | mail (2.3.0) 63 | i18n (>= 0.4.0) 64 | mime-types (~> 1.16) 65 | treetop (~> 1.4.8) 66 | mime-types (1.16) 67 | multi_json (1.0.3) 68 | polyglot (0.3.1) 69 | rack (1.3.0) 70 | rack-cache (1.0.2) 71 | rack (>= 0.4) 72 | rack-mount (0.8.1) 73 | rack (>= 1.0.0) 74 | rack-ssl (1.3.2) 75 | rack 76 | rack-test (0.6.0) 77 | rack (>= 1.0) 78 | rails (3.1.0.rc1) 79 | actionmailer (= 3.1.0.rc1) 80 | actionpack (= 3.1.0.rc1) 81 | activerecord (= 3.1.0.rc1) 82 | activeresource (= 3.1.0.rc1) 83 | activesupport (= 3.1.0.rc1) 84 | bundler (~> 1.0) 85 | railties (= 3.1.0.rc1) 86 | railties (3.1.0.rc1) 87 | actionpack (= 3.1.0.rc1) 88 | activesupport (= 3.1.0.rc1) 89 | rack-ssl (~> 1.3.2) 90 | rake (>= 0.8.7) 91 | thor (~> 0.14.6) 92 | rake (0.8.7) 93 | rspec (2.6.0) 94 | rspec-core (~> 2.6.0) 95 | rspec-expectations (~> 2.6.0) 96 | rspec-mocks (~> 2.6.0) 97 | rspec-core (2.6.3) 98 | rspec-expectations (2.6.0) 99 | diff-lcs (~> 1.1.2) 100 | rspec-mocks (2.6.0) 101 | rspec-rails (2.6.1) 102 | actionpack (~> 3.0) 103 | activesupport (~> 3.0) 104 | railties (~> 3.0) 105 | rspec (~> 2.6.0) 106 | rubyzip (0.9.4) 107 | sass (3.1.2) 108 | selenium-webdriver (0.2.1) 109 | childprocess (>= 0.1.7) 110 | ffi (>= 1.0.7) 111 | json_pure 112 | rubyzip 113 | sprockets (2.0.0.beta.9) 114 | hike (~> 1.0) 115 | rack (~> 1.0) 116 | tilt (~> 1.1, != 1.3.0) 117 | sqlite3 (1.3.3) 118 | thor (0.14.6) 119 | tilt (1.3.2) 120 | treetop (1.4.9) 121 | polyglot (>= 0.3.1) 122 | tzinfo (0.3.27) 123 | uglifier (0.5.4) 124 | execjs (>= 0.3.0) 125 | multi_json (>= 1.0.2) 126 | 127 | PLATFORMS 128 | ruby 129 | 130 | DEPENDENCIES 131 | awesome_print 132 | coffee-script 133 | haml 134 | jasmine 135 | jquery-rails 136 | json 137 | rails (>= 3.1.rc) 138 | rake (= 0.8.7) 139 | rspec-rails (~> 2.0) 140 | sass 141 | sqlite3 142 | uglifier 143 | -------------------------------------------------------------------------------- /app/assets/javascripts/views/todo_view.coffee: -------------------------------------------------------------------------------- 1 | # Todo Item View 2 | # -------------- 3 | 4 | # The DOM element for a todo item... 5 | class TodoApp.TodoView extends Backbone.View 6 | # ... is a list tag. 7 | tagName: "li" 8 | 9 | # Cache the template function for a single item. 10 | template: TodoApp.template '#item-template' 11 | 12 | # The DOM events specific to an item. 13 | events: 14 | "click .check" : "toggleDone" 15 | "dblclick div.todo-content" : "edit" 16 | "click span.todo-destroy" : "destroy" 17 | "keypress .todo-input" : "updateOnEnter" 18 | 19 | # The TodoView listens for changes to its model, re-rendering. Since there's 20 | # a one-to-one correspondence between a **Todo** and a **TodoView** in this 21 | # app, we set a direct reference on the model for convenience. 22 | initialize: -> 23 | _.bindAll this, 'render', 'close' 24 | @model.bind 'change', @render 25 | @model.bind 'destroy', => @remove() 26 | 27 | # Re-render the contents of the todo item. 28 | render: -> 29 | $(@el).html @template @model.toJSON() 30 | @setContent() 31 | this 32 | 33 | # To avoid XSS (not that it would be harmful in this particular app), 34 | # we use `jQuery.text` to set the contents of the todo item. 35 | setContent: -> 36 | content = @model.get 'content' 37 | @$('.todo-content').text content 38 | @input = @$('.todo-input') 39 | @input.blur @close 40 | @input.val content 41 | 42 | # Toggle the `"done"` state of the model. 43 | toggleDone: -> 44 | @model.toggle() 45 | 46 | # Switch this view into `"editing"` mode, displaying the input field. 47 | edit: -> 48 | $(@el).addClass "editing" 49 | @input.focus() 50 | 51 | # Close the `"editing"` mode, saving changes to the todo. 52 | close: -> 53 | @model.save content: @input.val() 54 | $(@el).removeClass "editing" 55 | 56 | # If you hit `enter`, we're through editing the item. 57 | updateOnEnter: (e) -> 58 | @close() if e.keyCode == 13 59 | 60 | # Destroy the model. 61 | destroy: -> 62 | @model.destroy() 63 | 64 | 65 | # The Application 66 | # --------------- 67 | 68 | # Our overall **AppView** is the top-level piece of UI. 69 | class TodoApp.AppView extends Backbone.View 70 | 71 | # Instead of generating a new element, bind to the existing skeleton of 72 | # the App already present in the HTML. 73 | el: "#todoapp" 74 | 75 | # Our template for the line of statistics at the bottom of the app. 76 | statsTemplate: TodoApp.template '#stats-template' 77 | 78 | # Delegated events for creating new items, and clearing completed ones. 79 | events: 80 | "keypress #new-todo" : "createOnEnter" 81 | "keyup #new-todo" : "showTooltip" 82 | "click .todo-clear a" : "clearCompleted" 83 | 84 | # At initialization we bind to the relevant events on the `Todos` 85 | # collection, when items are added or changed. Kick things off by 86 | # loading any preexisting todos that might be saved 87 | initialize: -> 88 | _.bindAll this, 'addOne', 'addAll', 'renderStats' 89 | 90 | @input = @$("#new-todo") 91 | 92 | @collection.bind 'add', @addOne 93 | @collection.bind 'refresh', @addAll 94 | @collection.bind 'all', @renderStats 95 | 96 | @collection.fetch() 97 | 98 | # Re-rendering the App just means refreshing the statistics -- the rest 99 | # of the app doesn't change. 100 | renderStats: -> 101 | @$('#todo-stats').html @statsTemplate 102 | total: @collection.length 103 | done: @collection.done().length 104 | remaining: @collection.remaining().length 105 | 106 | # Add a single todo item to the list by creating a view for it, and 107 | # appending its element to the `