brainstem-js 0.2.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 (56) hide show
  1. data/.gitignore +8 -0
  2. data/.pairs +21 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.tm_properties +1 -0
  6. data/.travis.yml +12 -0
  7. data/Assetfile +79 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +50 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +143 -0
  12. data/Rakefile +25 -0
  13. data/brainstemjs.gemspec +24 -0
  14. data/lib/brainstem/js/engine.rb +5 -0
  15. data/lib/brainstem/js/version.rb +5 -0
  16. data/lib/brainstem/js.rb +10 -0
  17. data/spec/brainstem-collection-spec.coffee +141 -0
  18. data/spec/brainstem-model-spec.coffee +283 -0
  19. data/spec/brainstem-sync-spec.coffee +22 -0
  20. data/spec/brainstem-utils-spec.coffee +12 -0
  21. data/spec/brianstem-expectation-spec.coffee +209 -0
  22. data/spec/helpers/builders.coffee +80 -0
  23. data/spec/helpers/jquery-matchers.js +137 -0
  24. data/spec/helpers/models/post.coffee +14 -0
  25. data/spec/helpers/models/project.coffee +13 -0
  26. data/spec/helpers/models/task.coffee +14 -0
  27. data/spec/helpers/models/time-entry.coffee +13 -0
  28. data/spec/helpers/models/user.coffee +8 -0
  29. data/spec/helpers/spec-helper.coffee +79 -0
  30. data/spec/storage-manager-spec.coffee +613 -0
  31. data/spec/support/.DS_Store +0 -0
  32. data/spec/support/console-runner.js +103 -0
  33. data/spec/support/headless.coffee +47 -0
  34. data/spec/support/headless.html +60 -0
  35. data/spec/support/runner.html +85 -0
  36. data/spec/vendor/backbone-factory.js +62 -0
  37. data/spec/vendor/backbone.js +1571 -0
  38. data/spec/vendor/inflection.js +448 -0
  39. data/spec/vendor/jquery-1.7.js +9300 -0
  40. data/spec/vendor/jquery.cookie.js +47 -0
  41. data/spec/vendor/minispade.js +67 -0
  42. data/spec/vendor/sinon-1.3.4.js +3561 -0
  43. data/spec/vendor/underscore.js +1221 -0
  44. data/vendor/assets/.DS_Store +0 -0
  45. data/vendor/assets/javascripts/.DS_Store +0 -0
  46. data/vendor/assets/javascripts/brainstem/brainstem-collection.coffee +53 -0
  47. data/vendor/assets/javascripts/brainstem/brainstem-expectation.coffee +65 -0
  48. data/vendor/assets/javascripts/brainstem/brainstem-inflections.js +449 -0
  49. data/vendor/assets/javascripts/brainstem/brainstem-model.coffee +141 -0
  50. data/vendor/assets/javascripts/brainstem/brainstem-sync.coffee +76 -0
  51. data/vendor/assets/javascripts/brainstem/index.js +1 -0
  52. data/vendor/assets/javascripts/brainstem/iso8601.js +41 -0
  53. data/vendor/assets/javascripts/brainstem/loading-mixin.coffee +13 -0
  54. data/vendor/assets/javascripts/brainstem/storage-manager.coffee +275 -0
  55. data/vendor/assets/javascripts/brainstem/utils.coffee +35 -0
  56. metadata +198 -0
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /bin/
3
+ /build/
4
+ /dist/
5
+ /tmp/
6
+ /.idea/
7
+ projectFilesBackup
8
+ pkg
data/.pairs ADDED
@@ -0,0 +1,21 @@
1
+ pairs:
2
+ rn: Roger Neel
3
+ ac: Andrew Cantino; andrew
4
+ jm: Jeff Moore
5
+ cad: Chris Alvarado-Dryden
6
+ ml: Matthew LaRocca
7
+ sa: Sufyan Adam
8
+ bb: Bobby Brown
9
+ al: Andrew Langdon
10
+ kd: Katlyn Daniluk
11
+ jc: Juan-Carlos Medina
12
+ rg: Reid Gillette
13
+ aj: Alec Jacobs
14
+ mc: Michael Chen
15
+ sc: Sean Crafts
16
+ cm: Caleb Mingle
17
+ aa: André Arko
18
+ email:
19
+ prefix: pair
20
+ domain: mavenlink.com
21
+ global: true
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ brainstem-js
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p392
data/.tm_properties ADDED
@@ -0,0 +1 @@
1
+ exclude = {$exclude,bin,build,dist,tmp,.tm_properties}
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm: 1.9.3
3
+
4
+ before_install: gem install bundler
5
+
6
+ notifications:
7
+ hipchat: 09a2719b3284ea0d0d247355fa5542@Mavenlink Dev
8
+
9
+ git:
10
+ depth: 1
11
+
12
+ source_key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBOHBpRjdldDZKM2FmWCt0WDlWRG5HZEt4dDdZZVJVVUh3K29JQVJDekorejBvS3MxCnNwaEUxZlBSQksyUDFMUVV5cngyUThzalgxdnA1NUt6U3lnVWExMFRlUG90VnVWN3hZbVFqTG5FM2FueTJBNXIKMFhXZG4wS2k2cUt5TkVwd2ZJa3JYam8xSmY4SHJ0ajZ5Yi9VTFcySnZxbTBGTTZRNmU1TCtmUGdQN0JqeS9WSApUWHF4UWVRaTVaSVppa2JoeGxoK3RQNVZaM1JMRTF3bEhRdXpESUFZemRFdnFXeEFxYkhvNktyMjJSZzRhaGNPCnNEZ1FGK0drR0h2bjBKTkpmc05Cd3d2YUQ2Y3M5N2pnY045b1VrK1phOFNlbFZhazRjWVhRUk5WaTdPMVdkQnkKSE1xaVNPVjFZdEhSWWdwSFpPaHdYSUdxK3ZhUXVFR21SRzdBY1FJREFRQUJBb0lCQVFDOXBiQ2xjdjFXbG13MwpEd0wrK3RUL0llL2VmeGVnN1RzSjFBMlh6NWRPc2ZYM0dJRHM4ZzUxOTVuQi8zQytSbDB1dEMvOEJYVE1ta3o3CnhIbzNXY2pFdWNsOFBJOXZMQTBiT3RSdXZ0Y0F0bGZxd1ROV1ZvejNNSit0cjZ5Q0ptTlRaK1FvVUhhMkVtM08KS1QrOHNpTEx0S01IRXlGOVZwS0EzZEkxUDRwaUhodlFBdkgzQkdRR3piNW9UczV6RXJnRmZNbEZKWFc0S2NxcApYc1NnWDBjd3d3Nit3WDFLblRGY3lNak9QbTB2MWZSSTdHMGdtOG02VVJrakdVeXhpRXhkTVNEdm5yVEt4ajlvCitIK1NucU14RW5uczJRR1lnSEtnV1ZaSFFBUU9UenBNSVdvNndHODNMMDlyOUo3S1dzZHRwNStKeDNZWWE2eTMKU0tod3NQdGxBb0dCQVAvZTJ2SitCVmpkRXVOSjJibWo4YnlGK2lJd292Vm9FYW4xUExkZ3JJbTNQWkFrLytyMgpiSFV0d0xDVjBSR1l5Z1ZqaW42YmEzUitNM2ptanErM3Z1N0FsVlJNazdvcFVXcjUwanRzbDhhT1k4WkIxTlhuClRlRW5keEswTnBWaWs1VlFhNzBqcmh3NGtmekxhUk9xdUV1SGt6Qk95MXhJeStyUkNMNDMzT1ZmQW9HQkFQSzMKOHNXYm5YRzR0WjdHMU5QVjZ0YXJ5bEt4bFluRnhseG4rUmZueTFHbG1NejU4YW1Jb1F2SnJuSHlSR29xQ2dNOQo4eWJFMmVoVjJFM20wKzUwampDeFNrMjhxbkNybDdnbTdNcjhsVzdGQnZLSUw2NDFsM1pDT3pLSnBKWFdGN1BaCmdYVGplTmduRzliQkljMldBNHpzaGVDK05CRWxidmFkekRrT0hkd3ZBb0dBWkRlZ1lCdzE4ZkZkQllNV2NSeWkKZ1Nta3FDR09vam9wdVB6aDFCMWNWdkJiZjRyT1pmUXcxTkNmeVVwVXdlU3JNK01pQ3FiTE5xeDdjcDR6UXVYZwpOZGxlWTg4K2lVckhwZlBGZ1JydWM0bXYwS1pXTzVYR0xpcnIrM3AwYXB4YW04QU5BdDdud2d2eU9pWmR1S05FClhlanpJSmVzRlRBNkZuWGJTODNMaWxjQ2dZRUExai9kd3VUOGM3ZnlTZmVGUW9DZnpXTFRNMitpYW56ei9mbWgKZmFLVWJMdmFSNFdSOW02dWlmTTdVMFhoY2owdG5YTC93WWNlT3VJY0Q1ZmtGNmMzSkhBN0FLZTdZNzEwTFkvZQprY2VvT0tFZTR0T29Fd1VuYjdKREF2ZFJHeHBpemRUL1d5aTRNVVZFWTZzVHBaLzMvbHVDU2NKYnY0N2xoamdBClg1VEFjdTBDZ1lCRFhnNUF2VHVXTE5pRk91RW5RekhrRmp0N0lwRVVUN2lpZDByMHBJempMVmc1c08zSXRqTkQKNjlvWitIYUlCSVpMWkNQc2lpS2dmQkFIS0xXY3NEQTRnelY2ckpUWDdmdXYvcHZUTk9tdmJlcG5OK0JGd05xdApVU2c5UmxFZ0lFWEE3Q041aVFvb2l2SmFiT3VEREl0NUhiTS9Gd1M0V3QvQVdnQUZkYmpab3c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
data/Assetfile ADDED
@@ -0,0 +1,79 @@
1
+ require 'rake-pipeline-web-filters'
2
+ require 'jasmine-core'
3
+
4
+ output "build"
5
+
6
+ input "vendor/assets/javascripts/brainstem" do
7
+ match "*.coffee" do
8
+ coffee_script
9
+ end
10
+
11
+ match "*.js" do
12
+ minispade :rewrite_requires => true, :module_id_generator => proc { |input|
13
+ input.path.sub(/\.js$/, '')
14
+ }, :string => true
15
+ concat "brainstem.js"
16
+ end
17
+ end
18
+
19
+ input "spec" do
20
+ match "**/*.coffee" do
21
+ coffee_script
22
+ end
23
+
24
+ match "**/*.js" do
25
+ reject "support/**/*.js"
26
+ reject "vendor/**/*.js"
27
+ reject "helpers/**/*.js"
28
+ minispade :rewrite_requires => true, :module_id_generator => proc { |input|
29
+ input.path.sub(/\.js$/, '')
30
+ }, :string => true
31
+ concat "specs.js"
32
+ end
33
+ end
34
+
35
+ input "spec/support" do
36
+ match "runner.html" do
37
+ concat "index.html"
38
+ end
39
+
40
+ match "headless.html" do
41
+ concat
42
+ end
43
+
44
+ match "**/*.coffee" do
45
+ coffee_script
46
+ end
47
+
48
+ match "**/*.js" do
49
+ concat
50
+ end
51
+ end
52
+
53
+ input "spec/vendor" do
54
+ output "build/vendor"
55
+ match "*.js" do
56
+ concat
57
+ end
58
+ end
59
+
60
+ input "spec/helpers" do
61
+
62
+ match "**/*.coffee" do
63
+ coffee_script
64
+ end
65
+
66
+ match "*.js" do
67
+ minispade :rewrite_requires => true, :module_id_generator => proc { |input|
68
+ input.path.sub(/\.js$/, '')
69
+ }, :string => true
70
+ concat "helpers.js"
71
+ end
72
+ end
73
+
74
+ input Jasmine::Core.path do
75
+ output "build/jasmine"
76
+ match /^[^\/]+\.(js|css)$/ do
77
+ concat
78
+ end
79
+ end
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake-pipeline', :github => 'livingsocial/rake-pipeline', :branch => 'master'
6
+ gem 'rake-pipeline-web-filters', :github => 'wycats/rake-pipeline-web-filters', :branch => 'master'
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ GIT
2
+ remote: git://github.com/livingsocial/rake-pipeline.git
3
+ revision: 27718232227eddd029466b2968f0880c10a18fb4
4
+ branch: master
5
+ specs:
6
+ rake-pipeline (0.8.0)
7
+ json
8
+ rake (~> 10.0.0)
9
+ thor
10
+
11
+ GIT
12
+ remote: git://github.com/wycats/rake-pipeline-web-filters.git
13
+ revision: d83f54cc0d4e53f9833c31851964e3d8ba00f66a
14
+ branch: master
15
+ specs:
16
+ rake-pipeline-web-filters (0.6.0)
17
+ rack
18
+ rake-pipeline (~> 0.6)
19
+
20
+ PATH
21
+ remote: .
22
+ specs:
23
+ brainstem-js (0.2.1)
24
+
25
+ GEM
26
+ remote: https://rubygems.org/
27
+ specs:
28
+ coffee-script (2.2.0)
29
+ coffee-script-source
30
+ execjs
31
+ coffee-script-source (1.4.0)
32
+ execjs (1.4.0)
33
+ multi_json (~> 1.0)
34
+ jasmine-core (1.3.1)
35
+ json (1.7.6)
36
+ multi_json (1.5.0)
37
+ rack (1.5.0)
38
+ rake (10.0.3)
39
+ thor (0.16.0)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ brainstem-js!
46
+ bundler (~> 1.2)
47
+ coffee-script (~> 2.2)
48
+ jasmine-core (~> 1.3.1)
49
+ rake-pipeline!
50
+ rake-pipeline-web-filters!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mavenlink
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # Brainstem.js
2
+
3
+ [Brainstem](https://github.com/mavenlink/brainstem) is designed to power rich APIs in Rails. The Brainstem gem provides a presenter library that handles converting ActiveRecord objects into structured JSON and a set of API abstractions that allow users to request sorts, filters, and association loads, allowing for simpler implementations, fewer requests, and smaller responses.
4
+
5
+ The Brainstem.js library is a companion library for Backbone.js that makes integration with Brainstem APIs a breeze. Brainstem.js adds an identity map and relational models to Backbone.
6
+
7
+ ## Why Brainstem.js?
8
+
9
+ * Speaks natively to Brainstem APIs
10
+ * Adds relational models in Backbone, allowing you to setup has_one, has_many, and belongs_to relationships.
11
+ * Provides an Identity Map to avoid loading already-available records.
12
+ * Supports Brainstem side-loading of multiple objects for fast, single-request workflows.
13
+ * Interprets the Brainstem results array and hashes for you, abstracting away the JSON protocol.
14
+ * Written in CoffeeScript.
15
+
16
+ ## Usage
17
+
18
+ If you're in Rails, just add `brainstem-js` to your Gemfile and require `brainstem` in `application.js`.
19
+
20
+ We have a [comprehensive demo available](https://github.com/mavenlink/brainstem-demo-rails). We recommend that you start there.
21
+
22
+ What follows is an overview.
23
+
24
+ ### StorageManager
25
+
26
+ The `Brianstem.StorageManager` is in charge of loading data over the API, as well as returning already cached data. We recommend setting one up in a singleton App class.
27
+
28
+ class Application
29
+ constructor: ->
30
+ @data = new Brainstem.StorageManager()
31
+ @data.addCollection 'widgets', Collections.Widgets
32
+ @data.addCollection 'locations', Collections.Locations
33
+ @data.addCollection 'features', Collections.Features
34
+ @homeRouter = new Routers.WidgetsRouter()
35
+
36
+ $ ->
37
+ window.base = new Application()
38
+ Backbone.history.start(root: "/")
39
+
40
+ At the moment, the global StorageManager *must* be available at `window.base.data`. For this reason, we recommend making a singleton instance of an `Application` class for holding your `StorageManager` and any other shared resources.
41
+
42
+ ### Brainstem.Models and Brainstem.Collections
43
+
44
+ Once you have a StorageManager, you should setup some `Brainstem.Models` and `Brainstem.Collections`:
45
+
46
+ window.Models ?= {}
47
+ window.Collections ?= {}
48
+
49
+ class Models.Widget extends Brainstem.Model
50
+ paramRoot: 'widget'
51
+ brainstemKey: 'widgets'
52
+ urlRoot: '/api/v1/widgets'
53
+
54
+ @associations:
55
+ features: ["features"]
56
+ location: "locations"
57
+
58
+ class Collections.Widgets extends Brainstem.Collection
59
+ model: Models.Widget
60
+ url: '/api/v1/widgets'
61
+
62
+ class Models.Feature extends Brainstem.Model
63
+ paramRoot: 'feature'
64
+ brainstemKey: 'features'
65
+ urlRoot: '/api/v1/features'
66
+
67
+ @associations:
68
+ widgets: ["widgets"]
69
+
70
+ class Collections.Features extends Brainstem.Collection
71
+ model: Models.Feature
72
+ url: '/api/v1/features'
73
+
74
+ Use the `@associations` class method to declare the mapping between association names and `StorageManager` collections where the data can be located. Arrays indicate has_many relationships. Other than a few additions, these are just Backbone Models and Collections.
75
+
76
+ ### Backbone.Views
77
+
78
+ Now that you have models, collections, and a `StorageManager`, it's time to load some data (probably in Backbone.Views). For example:
79
+
80
+ class Views.Widgets.IndexView extends Backbone.View
81
+ template: JST["backbone/templates/widgets/index"]
82
+
83
+ initialize: ->
84
+ @collection = base.data.loadCollection "widgets", include: ["location", "features"], order: 'updated_at:desc'
85
+ @collection.bind 'reset', @addAll
86
+ @collection.bind 'remove', @addAll
87
+
88
+ render: =>
89
+ @$el.html @template()
90
+
91
+ if @collection.loaded
92
+ @addAll()
93
+ else
94
+ @$("#widgets-list").text "Just a moment..."
95
+
96
+ return this
97
+
98
+ addAll: =>
99
+ @$("#widgets-list").empty()
100
+ @collection.each(@addOne)
101
+ @addLocations()
102
+
103
+ addOne: (model) =>
104
+ view = new Views.Widgets.WidgetView(model: model)
105
+ @$("#widgets-list").append view.render().el
106
+
107
+ And finally, in your templates, you can access the relational data just like you'd normally access model data in Backbone.
108
+
109
+ @model.get('location').get('name')
110
+
111
+ for feature in @model.get('features').models:
112
+
113
+ Etc.
114
+
115
+ ## Development
116
+
117
+ We're always open to pull requests!
118
+
119
+ ### Dependencies
120
+
121
+ - PhantomJS. If you're on OS X, run `brew install phantomjs`.
122
+
123
+ ### Running Specs
124
+
125
+ To run the specs on the command line, run:
126
+
127
+ bundle exec rake
128
+
129
+ To run the specs in a server with live code reloading and compilation:
130
+
131
+ bundle exec rake server
132
+
133
+ To develop your application against a local checkout of brainstem-js, we suggest using Bundler's local gems:
134
+
135
+ bundle config local.brainstem-js ~/workspace/brainstem-js
136
+
137
+ And when you're done, run:
138
+
139
+ bundle config --delete local.brainstem-js
140
+
141
+ # License
142
+
143
+ Brainstem and Brainstem.js were created by Mavenlink, Inc. and are available under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ task :clean do
5
+ sh "rakep clean"
6
+ end
7
+
8
+ task :build do
9
+ sh "rakep build"
10
+ end
11
+
12
+ task :spec => :build do
13
+ begin
14
+ exec *%w(phantomjs build/headless.js build/headless.html)
15
+ rescue Errno::ENOENT => e
16
+ puts "Couldn't find phantomjs."
17
+ abort "You didn't run `brew install phantomjs`, did you?"
18
+ end
19
+ end
20
+
21
+ task :server => :clean do
22
+ exec "rakep server"
23
+ end
24
+
25
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'brainstem/js/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "brainstem-js"
8
+ gem.version = Brainstem::Js::VERSION
9
+ gem.authors = ["Mavenlink"]
10
+ gem.email = ["opensource@mavenlink.com"]
11
+ gem.description = %q{The Brainstem API adapter library for Backbone.js}
12
+ gem.summary = %q{Easily connect Backbone.js with Brainstem APIs. Get relational models in Backbone.}
13
+ gem.homepage = "http://github.com/mavenlink/brainstem-js"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'bundler', '~> 1.2'
21
+ gem.add_development_dependency 'coffee-script', '~> 2.2'
22
+ gem.add_development_dependency 'jasmine-core', '~> 1.3.1'
23
+ gem.add_development_dependency 'rake-pipeline-web-filters', '~> 0.6.0'
24
+ end
@@ -0,0 +1,5 @@
1
+ module Brainstemjs
2
+ class Rails < ::Rails::Engine
3
+ # So Rails will add vendor to the asset pipeline
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Brainstem
2
+ module Js
3
+ VERSION = "0.2.1"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require "brainstem/js/version"
2
+ require "brainstem/js/engine" if defined?(::Rails::Engine)
3
+
4
+ module Brainstem
5
+ module Js
6
+ def self.path
7
+ File.expand_path("../../../vendor/assets/javascripts/brainstem", __FILE__)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,141 @@
1
+ describe 'Brainstem.Collection', ->
2
+ collection = updateArray = null
3
+
4
+ beforeEach ->
5
+ collection = new Brainstem.Collection([{id: 2, title: "1"}, {id: 3, title: "2"}, {title: "3"}])
6
+ updateArray = [{id: 2, title: "1 new"}, {id: 4, title: "this is new"}]
7
+
8
+ describe 'update', ->
9
+ it "works with an array", ->
10
+ collection.update updateArray
11
+ expect(collection.get(2).get('title')).toEqual "1 new"
12
+ expect(collection.get(3).get('title')).toEqual "2"
13
+ expect(collection.get(4).get('title')).toEqual "this is new"
14
+
15
+ it "works with a collection", ->
16
+ newCollection = new Brainstem.Collection(updateArray)
17
+ collection.update newCollection
18
+ expect(collection.get(2).get('title')).toEqual "1 new"
19
+ expect(collection.get(3).get('title')).toEqual "2"
20
+ expect(collection.get(4).get('title')).toEqual "this is new"
21
+
22
+ it "should update copies of the model that are already in the collection", ->
23
+ model = collection.get(2)
24
+ spy = jasmine.createSpy()
25
+ model.bind "change:title", spy
26
+ collection.update updateArray
27
+ expect(model.get('title')).toEqual "1 new"
28
+ expect(spy).toHaveBeenCalled()
29
+
30
+ describe "loadNextPage", ->
31
+ it "loads the next page of data for a collection that has previously been loaded in the storage manager, returns the collection and whether it thinks there is another page or not", ->
32
+ respondWith server, "/api/time_entries?per_page=2&page=1", resultsFrom: "time_entries", data: { time_entries: [buildTimeEntry(), buildTimeEntry()] }
33
+ respondWith server, "/api/time_entries?per_page=2&page=2", resultsFrom: "time_entries", data: { time_entries: [buildTimeEntry(), buildTimeEntry()] }
34
+ respondWith server, "/api/time_entries?per_page=2&page=3", resultsFrom: "time_entries", data: { time_entries: [buildTimeEntry()] }
35
+ collection = base.data.loadCollection "time_entries", perPage: 2
36
+ expect(collection.length).toEqual 0
37
+ server.respond()
38
+ expect(collection.length).toEqual 2
39
+ expect(collection.lastFetchOptions.page).toEqual 1
40
+
41
+ spy = jasmine.createSpy()
42
+ collection.loadNextPage success: spy
43
+ server.respond()
44
+ expect(spy).toHaveBeenCalledWith(collection, true)
45
+ expect(collection.lastFetchOptions.page).toEqual 2
46
+ expect(collection.length).toEqual 4
47
+
48
+ spy = jasmine.createSpy()
49
+ collection.loadNextPage success: spy
50
+ expect(collection.length).toEqual 4
51
+ server.respond()
52
+ expect(spy).toHaveBeenCalledWith(collection, false)
53
+ expect(collection.lastFetchOptions.page).toEqual 3
54
+ expect(collection.length).toEqual 5
55
+
56
+ describe "reload", ->
57
+ it "reloads the collection with the original params", ->
58
+ respondWith server, "/api/posts?include=replies&parents_only=true&per_page=5&page=1", resultsFrom: "posts", data: { posts: [buildPost(message: "old post", reply_ids: [])] }
59
+ collection = base.data.loadCollection "posts", include: ["replies"], filters: { parents_only: "true" }, perPage: 5
60
+ server.respond()
61
+ expect(collection.lastFetchOptions.page).toEqual 1
62
+ expect(collection.lastFetchOptions.perPage).toEqual 5
63
+ expect(collection.lastFetchOptions.include).toEqual ["replies"]
64
+ server.responses = []
65
+ respondWith server, "/api/posts?include=replies&parents_only=true&per_page=5&page=1", resultsFrom: "posts", data: { posts: [buildPost(message: "new post", reply_ids: [])] }
66
+ expect(collection.models[0].get("message")).toEqual "old post"
67
+ resetCounter = jasmine.createSpy("resetCounter")
68
+ loadedCounter = jasmine.createSpy("loadedCounter")
69
+ callback = jasmine.createSpy("callback spy")
70
+ collection.bind "reset", resetCounter
71
+ collection.bind "loaded", loadedCounter
72
+
73
+ collection.reload success: callback
74
+
75
+ expect(collection.loaded).toBe false
76
+ expect(collection.length).toEqual 0
77
+ server.respond()
78
+ expect(collection.length).toEqual 1
79
+ expect(collection.models[0].get("message")).toEqual "new post"
80
+ expect(resetCounter.callCount).toEqual 1
81
+ expect(loadedCounter.callCount).toEqual 1
82
+ expect(callback).toHaveBeenCalledWith(collection)
83
+
84
+ describe "getWithAssocation", ->
85
+ it "defaults to the regular get", ->
86
+ spyOn(collection, 'get')
87
+ collection.getWithAssocation(10)
88
+ expect(collection.get).toHaveBeenCalledWith(10)
89
+
90
+ describe 'setLoaded', ->
91
+ it "should set the values of @loaded", ->
92
+ collection.setLoaded true
93
+ expect(collection.loaded).toEqual(true)
94
+ collection.setLoaded false
95
+ expect(collection.loaded).toEqual(false)
96
+
97
+ it "triggers 'loaded' when becoming true", ->
98
+ spy = jasmine.createSpy()
99
+ collection.bind "loaded", spy
100
+ collection.setLoaded false
101
+ expect(spy).not.toHaveBeenCalled()
102
+ collection.setLoaded true
103
+ expect(spy).toHaveBeenCalled()
104
+
105
+ it "doesn't trigger loaded if trigger: false is provided", ->
106
+ spy = jasmine.createSpy()
107
+ collection.bind "loaded", spy
108
+ collection.setLoaded true, trigger: false
109
+ expect(spy).not.toHaveBeenCalled()
110
+
111
+ it "returns self", ->
112
+ spy = jasmine.createSpy()
113
+ collection.bind "loaded", spy
114
+ collection.setLoaded true
115
+ expect(spy).toHaveBeenCalledWith(collection)
116
+
117
+ describe "toServerJSON", ->
118
+ it "calls through to toJSON", ->
119
+ spy = spyOn(collection, 'toJSON')
120
+ collection.toServerJSON()
121
+ expect(spy).toHaveBeenCalled()
122
+
123
+ describe "ordering and filtering", ->
124
+ beforeEach ->
125
+ collection = new Brainstem.Collection([
126
+ new Brainstem.Model(id: 2, title: "Alpha", updated_at: 2, cool: false),
127
+ new Brainstem.Model(id: 3, title: "Beta", updated_at: 10, cool: true),
128
+ new Brainstem.Model(id: 4, title: "Gamma", updated_at: 5, cool: false),
129
+ new Brainstem.Model(id: 6, title: "Gamma", updated_at: 5, cool: false),
130
+ new Brainstem.Model(id: 5, title: "Gamma", updated_at: 4, cool: true)
131
+ ])
132
+
133
+ describe "@getComparatorWithIdFailover", ->
134
+ it "returns a comparator that works for numerical ordering of unix timestamps, failing over to id when they're the same", ->
135
+ newCollection = new Brainstem.Collection collection.models, comparator: Brainstem.Collection.getComparatorWithIdFailover("updated_at:desc")
136
+ newCollection.sort()
137
+ expect(newCollection.pluck("id")).toEqual [3, 6, 4, 5, 2]
138
+
139
+ newCollection = new Brainstem.Collection collection.models, comparator: Brainstem.Collection.getComparatorWithIdFailover("updated_at:asc")
140
+ newCollection.sort()
141
+ expect(newCollection.pluck("id")).toEqual [2, 5, 4, 6, 3]