knockout-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +134 -0
  4. data/HISTORY.md +12 -0
  5. data/README.md +106 -0
  6. data/Rakefile +37 -0
  7. data/knockout-rails.gemspec +27 -0
  8. data/lib/assets/javascripts/knockout/bindings.js.coffee +1 -0
  9. data/lib/assets/javascripts/knockout/model.js.coffee +112 -0
  10. data/lib/assets/javascripts/knockout/observables.js.coffee +1 -0
  11. data/lib/knockout-rails.rb +5 -0
  12. data/lib/knockout-rails/engine.rb +6 -0
  13. data/lib/knockout-rails/version.rb +3 -0
  14. data/lib/tasks/knockout-rails_tasks.rake +4 -0
  15. data/spec/dummy/Rakefile +7 -0
  16. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  17. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  18. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  20. data/spec/dummy/app/mailers/.gitkeep +0 -0
  21. data/spec/dummy/app/models/.gitkeep +0 -0
  22. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  23. data/spec/dummy/config.ru +4 -0
  24. data/spec/dummy/config/application.rb +53 -0
  25. data/spec/dummy/config/boot.rb +10 -0
  26. data/spec/dummy/config/database.yml +25 -0
  27. data/spec/dummy/config/environment.rb +5 -0
  28. data/spec/dummy/config/environments/development.rb +30 -0
  29. data/spec/dummy/config/environments/production.rb +60 -0
  30. data/spec/dummy/config/environments/test.rb +39 -0
  31. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  32. data/spec/dummy/config/initializers/inflections.rb +10 -0
  33. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  34. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  35. data/spec/dummy/config/initializers/session_store.rb +8 -0
  36. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  37. data/spec/dummy/config/locales/en.yml +5 -0
  38. data/spec/dummy/config/routes.rb +58 -0
  39. data/spec/dummy/lib/assets/.gitkeep +0 -0
  40. data/spec/dummy/public/404.html +26 -0
  41. data/spec/dummy/public/422.html +26 -0
  42. data/spec/dummy/public/500.html +26 -0
  43. data/spec/dummy/public/favicon.ico +0 -0
  44. data/spec/dummy/script/rails +6 -0
  45. data/spec/javascripts/knockout/bindings_spec.js.coffee +2 -0
  46. data/spec/javascripts/knockout/model_spec.js.coffee +85 -0
  47. data/spec/javascripts/knockout/observables_spec.js.coffee +3 -0
  48. data/spec/javascripts/spec.css +3 -0
  49. data/spec/javascripts/spec.js.coffee +3 -0
  50. data/spec/javascripts/support/mock-ajax.js +207 -0
  51. data/spec/knockout_spec.rb +14 -0
  52. data/spec/spec_helper.rb +10 -0
  53. data/spec/support/global.rb +7 -0
  54. data/spec/support/matchers.rb +36 -0
  55. data/vendor/assets/javascripts/knockout.js +7 -0
  56. data/vendor/assets/javascripts/knockout/knockout.js +3153 -0
  57. data/vendor/assets/javascripts/knockout/knockout.mapping.js +676 -0
  58. data/vendor/assets/javascripts/knockout/sugar-1.1.1.js +5828 -0
  59. metadata +206 -0
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ *.swp
5
+ spec/dummy/db/*.sqlite3
6
+ spec/dummy/log/*.log
7
+ spec/dummy/tmp/
8
+ spec/dummy/public/assets/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'coffee-script'
6
+ group :development, :test do
7
+ gem 'pry'
8
+
9
+ gem 'jasminerice'
10
+ end
11
+
12
+ gem 'jquery-rails' # For the dummy app
data/Gemfile.lock ADDED
@@ -0,0 +1,134 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ knockout-rails (0.0.1)
5
+ execjs
6
+ jquery-rails
7
+ sprockets (>= 2.0.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ actionmailer (3.1.1)
13
+ actionpack (= 3.1.1)
14
+ mail (~> 2.3.0)
15
+ actionpack (3.1.1)
16
+ activemodel (= 3.1.1)
17
+ activesupport (= 3.1.1)
18
+ builder (~> 3.0.0)
19
+ erubis (~> 2.7.0)
20
+ i18n (~> 0.6)
21
+ rack (~> 1.3.2)
22
+ rack-cache (~> 1.1)
23
+ rack-mount (~> 0.8.2)
24
+ rack-test (~> 0.6.1)
25
+ sprockets (~> 2.0.2)
26
+ activemodel (3.1.1)
27
+ activesupport (= 3.1.1)
28
+ builder (~> 3.0.0)
29
+ i18n (~> 0.6)
30
+ activerecord (3.1.1)
31
+ activemodel (= 3.1.1)
32
+ activesupport (= 3.1.1)
33
+ arel (~> 2.2.1)
34
+ tzinfo (~> 0.3.29)
35
+ activeresource (3.1.1)
36
+ activemodel (= 3.1.1)
37
+ activesupport (= 3.1.1)
38
+ activesupport (3.1.1)
39
+ multi_json (~> 1.0)
40
+ arel (2.2.1)
41
+ builder (3.0.0)
42
+ coderay (0.9.8)
43
+ coffee-script (2.2.0)
44
+ coffee-script-source
45
+ execjs
46
+ coffee-script-source (1.1.3)
47
+ diff-lcs (1.1.3)
48
+ erubis (2.7.0)
49
+ execjs (1.2.9)
50
+ multi_json (~> 1.0)
51
+ haml (3.1.3)
52
+ hike (1.2.1)
53
+ i18n (0.6.0)
54
+ jasminerice (0.0.8)
55
+ haml
56
+ jquery-rails (1.0.16)
57
+ railties (~> 3.0)
58
+ thor (~> 0.14)
59
+ json (1.6.1)
60
+ mail (2.3.0)
61
+ i18n (>= 0.4.0)
62
+ mime-types (~> 1.16)
63
+ treetop (~> 1.4.8)
64
+ method_source (0.6.7)
65
+ ruby_parser (>= 2.3.1)
66
+ mime-types (1.17.2)
67
+ multi_json (1.0.3)
68
+ polyglot (0.3.3)
69
+ pry (0.9.7.4)
70
+ coderay (~> 0.9.8)
71
+ method_source (~> 0.6.7)
72
+ ruby_parser (>= 2.3.1)
73
+ slop (~> 2.1.0)
74
+ rack (1.3.5)
75
+ rack-cache (1.1)
76
+ rack (>= 0.4)
77
+ rack-mount (0.8.3)
78
+ rack (>= 1.0.0)
79
+ rack-ssl (1.3.2)
80
+ rack
81
+ rack-test (0.6.1)
82
+ rack (>= 1.0)
83
+ rails (3.1.1)
84
+ actionmailer (= 3.1.1)
85
+ actionpack (= 3.1.1)
86
+ activerecord (= 3.1.1)
87
+ activeresource (= 3.1.1)
88
+ activesupport (= 3.1.1)
89
+ bundler (~> 1.0)
90
+ railties (= 3.1.1)
91
+ railties (3.1.1)
92
+ actionpack (= 3.1.1)
93
+ activesupport (= 3.1.1)
94
+ rack-ssl (~> 1.3.2)
95
+ rake (>= 0.8.7)
96
+ rdoc (~> 3.4)
97
+ thor (~> 0.14.6)
98
+ rake (0.9.2.2)
99
+ rdoc (3.11)
100
+ json (~> 1.4)
101
+ rspec (2.7.0)
102
+ rspec-core (~> 2.7.0)
103
+ rspec-expectations (~> 2.7.0)
104
+ rspec-mocks (~> 2.7.0)
105
+ rspec-core (2.7.1)
106
+ rspec-expectations (2.7.0)
107
+ diff-lcs (~> 1.1.2)
108
+ rspec-mocks (2.7.0)
109
+ ruby_parser (2.3.1)
110
+ sexp_processor (~> 3.0)
111
+ sexp_processor (3.0.8)
112
+ slop (2.1.0)
113
+ sprockets (2.0.3)
114
+ hike (~> 1.2)
115
+ rack (~> 1.0)
116
+ tilt (~> 1.1, != 1.3.0)
117
+ thor (0.14.6)
118
+ tilt (1.3.3)
119
+ treetop (1.4.10)
120
+ polyglot
121
+ polyglot (>= 0.3.1)
122
+ tzinfo (0.3.31)
123
+
124
+ PLATFORMS
125
+ ruby
126
+
127
+ DEPENDENCIES
128
+ coffee-script
129
+ jasminerice
130
+ jquery-rails
131
+ knockout-rails!
132
+ pry
133
+ rails (>= 3.1.1)
134
+ rspec
data/HISTORY.md ADDED
@@ -0,0 +1,12 @@
1
+ # 0.0.2 - in progress
2
+
3
+ - Support collections (fetch multiple records)
4
+ - Client side validation
5
+
6
+ # 0.0.1 - 17 November 2011
7
+ Initial release. Bare bones moved over from other project. Includes:
8
+
9
+ - persist view models RESTfully
10
+ - compliant with Rails inherited_resources response
11
+ - built in, bindable server side validation support (errors accessible vie `model.errors.attribute_name()`)
12
+
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # Knockout - easily use Knockout.js from the Rails app
2
+
3
+ If you have any questions please contact me [@dnagir](http://www.ApproachE.com).
4
+
5
+ This provides a set of conveniences for you to use more like Backbone or Spine, but still fully leveraging KnockoutJS.
6
+
7
+ # Install
8
+
9
+ Add it to your Rails application's `Gemfile`:
10
+
11
+ ```ruby
12
+ gem 'knockout'
13
+ ```
14
+
15
+ Then `bundle install`.
16
+
17
+ Reference `knockout` from your JavaScript as you normally do.
18
+
19
+
20
+ # Usage
21
+
22
+ ## Model
23
+
24
+ After you've referenced the `knockout` you can create your first persistent Model.
25
+
26
+ ```coffee
27
+ class Page extends ko.Model
28
+ @configure 'page' # This is enough to save the model RESTfully to `/pages/{id}` URL
29
+ ```
30
+
31
+ Too simple. This model conforms to the response of [inherited_resources](https://github.com/josevalim/inherited_resources) Gem.
32
+
33
+
34
+ Now you can create the model in your HTML.
35
+ *Note* that we don't do a roundtrip to fetch the data as we already have it when rendering the view.
36
+
37
+ ```haml
38
+ = content_for :script do
39
+ :javascript
40
+ jQuery(function(){
41
+ // Create the viewModel with prefilled data
42
+ window.page = new Page(#{@page.to_json});
43
+ ko.applyBindings(window.page); // And bind everything
44
+ });
45
+ ```
46
+
47
+ Of course you can manipulate the object as you wish:
48
+
49
+ ```coffee
50
+ page.name 'Updated page'
51
+ page.save() # saves it to the server using PUT: /pages/123
52
+ page.name '' # Assign an invalid value that is validated on the server
53
+ request = page.save() # returns the jQuery Deferred, so you can chain into it when necessary
54
+ request.always (xhr, status) ->
55
+ # The response is 422 with JSON: {name: ["invalid name", "should not be blank"]}
56
+ # And now we have the errors set automatically!
57
+ page.errors.name() # "invalid name, should not be blank"
58
+ # even more than that, errors are already bound and shown in the HTML (see the view below)
59
+ ```
60
+
61
+ Now let's see how we can show the validation errors on the page and bind everything together.
62
+
63
+ ```haml
64
+
65
+ %form.page.formtastic{:data => {:bind =>'submit: save'}}
66
+ %fieldset
67
+ %ol
68
+ %li.input.string
69
+ %label.label{:for=>:page_name} Name
70
+ %input#page_name{:type=>:text, :data=>{:bind=>'value: name'}}
71
+ %span.inline-error{:data=>{:bind=>'visible: errors.name, text: errors.name'}}
72
+ ```
73
+
74
+
75
+ # Development
76
+
77
+ ## Help
78
+
79
+ - Source hosted at [GitHub](https://github.com/dnagir/knockout-rails)
80
+ - Report issues and feature requests to [GitHub Issues](https://github.com/dnagir/knockout-rails/issues)
81
+ - Ping me on Twitter for quickly thing [@dnagir](https://twitter.com/#!/dnagir)
82
+ - Look at the `HISTORY.md` file for current TODO list and other details.
83
+
84
+
85
+ ## Setup
86
+
87
+ Assuming you already cloned the repo in cd-d into it:
88
+
89
+ ```bash
90
+ bundle install
91
+ # Now run the Ruby specs
92
+ bundle exec rspec spec/
93
+ # Now start JavaScript server for specs:
94
+ cd spec/dummy
95
+ bundle exec rails s
96
+ # go to http://localhost:3000/jasmine to see the results
97
+ ```
98
+
99
+ Now you can go to `spec/javascripts` and start writing your specs and then modify stuff in `lib/assets/javascripts` to pass those.
100
+
101
+
102
+ Pull requests are very welcome, but please include the specs! It's extremely easy to write those!
103
+
104
+ # License
105
+
106
+ [MIT] (http://www.opensource.org/licenses/mit-license.php)
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'KnockoutRails'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task :default => :test
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "knockout-rails/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "knockout-rails"
7
+ s.version = KnockoutRails::VERSION
8
+ s.authors = ["Dmytrii Nagirniak"]
9
+ s.email = ["dnagir@gmail.com"]
10
+ s.homepage = "http://github.com/dnagir/knockout-rails"
11
+ s.summary = %q{Knockout.JS library for Rails Assets Pipeline with convenient Backbone/Spine-like Rails extensions.}
12
+ s.description = %q{Include the knockout.js and some of its extensions so you can pick what you need. Adds the support for models and interation with the Rails backend.}
13
+
14
+ s.rubyforge_project = "knockout-rails"
15
+
16
+ s.add_dependency 'sprockets', '>= 2.0.0'
17
+ s.add_dependency 'execjs'
18
+ s.add_dependency 'jquery-rails'
19
+
20
+ s.add_development_dependency 'rspec'
21
+ s.add_development_dependency 'rails', '>= 3.1.1'
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1 @@
1
+ #TODO: Add additional bindings
@@ -0,0 +1,112 @@
1
+
2
+ # Module is taken from Spine.js
3
+ moduleKeywords = ['included', 'extended']
4
+ class Module
5
+ @include: (obj) ->
6
+ throw('include(obj) requires obj') unless obj
7
+ for key, value of obj when key not in moduleKeywords
8
+ @::[key] = value
9
+ obj.included?.apply(@)
10
+ @
11
+
12
+ @extend: (obj) ->
13
+ throw('extend(obj) requires obj') unless obj
14
+ for key, value of obj when key not in moduleKeywords
15
+ @[key] = value
16
+ obj.extended?.apply(@)
17
+ @
18
+
19
+
20
+ Ajax =
21
+ ClassMethods:
22
+ configure: (@className) ->
23
+ @getUrl ||= (model) ->
24
+ return model.getUrl(model) if model and model.getUrl
25
+ collectionUrl = "/#{className.toLowerCase()}s"
26
+ collectionUrl += "/#{model.id()}" if model?.id()
27
+ collectionUrl
28
+ extended: -> @include Ajax.InstanceMethods
29
+
30
+
31
+ InstanceMethods:
32
+ ignore: -> []
33
+ mapping: ->
34
+ return @__ko_mapping__ if @__ko_mapping__
35
+ mappable =
36
+ ignore: @ignore()
37
+ for k, v of this
38
+ mappable.ignore.push k unless ko.isObservable(v)
39
+ @__ko_mapping__ = mappable
40
+
41
+ toJSON: -> ko.mapping.toJS @, @mapping()
42
+
43
+ save: ->
44
+ data = {}
45
+ data[@constructor.className] =@toJSON()
46
+ params =
47
+ type: if @persisted() then 'PUT' else 'POST'
48
+ dataType: 'json'
49
+ beforeSend: (xhr)->
50
+ token = $('meta[name="csrf-token"]').attr('content')
51
+ xhr.setRequestHeader('X-CSRF-Token', token) if token
52
+ url: @constructor.getUrl(@)
53
+ contentType: 'application/json'
54
+ context: this
55
+ processData: false # jQuery tries to serialize to much, including constructor data
56
+ data: JSON.stringify data
57
+ statusCode:
58
+ 422: (xhr, status, errorThrown)->
59
+ errorData = JSON.parse xhr.responseText
60
+ console.debug("Validation error: ", errorData) if console?.debug?
61
+ @updateErrors(errorData)
62
+
63
+ $.ajax(params)
64
+ #.fail (xhr, status, errorThrown)-> console.error "fail: ", this
65
+ .done (resp, status, xhr)-> @updateErrors {}
66
+ #.always (xhr, status) -> console.info "always: ", this
67
+
68
+
69
+
70
+ class Model extends Module
71
+ @extend Ajax.ClassMethods
72
+
73
+ constructor: (json) ->
74
+ me = this
75
+ @set json
76
+ @id ||= ko.observable()
77
+ @mapping().ignore.exclude('constructor').filter (v)->
78
+ not v.startsWith('_') and Object.isFunction me[v]
79
+ .forEach (fn) ->
80
+ original = me[fn]
81
+ me[fn] = original.bind me
82
+ me._originals ||= {}
83
+ me._originals[fn] = original
84
+
85
+ @persisted = ko.dependentObservable -> !!me.id()
86
+
87
+ proxy: -> @mapping().ignore
88
+
89
+ set: (json) ->
90
+ ko.mapping.fromJS json, @mapping(), @
91
+ me = this
92
+ @errors ||= {}
93
+ ignores = @mapping().ignore
94
+ for key, value of this
95
+ @errors[key] ||= ko.observable() unless ignores.indexOf(key) >= 0
96
+ @
97
+
98
+ updateErrors: (errorData) ->
99
+ for key, setter of @errors
100
+ field = @errors[key]
101
+ error = errorData[key]
102
+ message = if error and error.join
103
+ error.join(", ")
104
+ else
105
+ error
106
+ setter( message ) if field
107
+ @
108
+
109
+
110
+ # Export it all:
111
+ ko.Module = Module
112
+ ko.Model = Model