rasputin 0.15.0 → 0.16.0

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.
@@ -1,9 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rasputin (0.15.0)
4
+ rasputin (0.16.0)
5
5
  actionpack (~> 3.1)
6
- jquery-rails (>= 1.0)
7
6
  railties (~> 3.1)
8
7
  sprockets (~> 2.0)
9
8
 
@@ -31,11 +30,8 @@ GEM
31
30
  hike (1.2.1)
32
31
  i18n (0.6.0)
33
32
  journey (1.0.3)
34
- jquery-rails (2.0.2)
35
- railties (>= 3.2.0, < 5.0)
36
- thor (~> 0.14)
37
- json (1.6.6)
38
- multi_json (1.2.0)
33
+ json (1.7.3)
34
+ multi_json (1.3.6)
39
35
  rack (1.4.1)
40
36
  rack-cache (1.2)
41
37
  rack (>= 0.4)
@@ -53,7 +49,7 @@ GEM
53
49
  rake (0.9.2.2)
54
50
  rdoc (3.12)
55
51
  json (~> 1.4)
56
- sprockets (2.1.2)
52
+ sprockets (2.1.3)
57
53
  hike (~> 1.2)
58
54
  rack (~> 1.0)
59
55
  tilt (~> 1.1, != 1.3.0)
data/README.md CHANGED
@@ -1,59 +1,25 @@
1
1
  Rasputin
2
2
  ========
3
3
 
4
- Rasputin integrates Ember.js with Rails 3.1 assets pipeline.
4
+ # WARNING
5
+ Most of the features from rasputin were merged in to official [ember-rails](https://github.com/emberjs/ember-rails) gem.
6
+ I will discontinu support to all the ember related stuff in rasputin.
7
+ You should start to migrate to [ember-rails](https://github.com/emberjs/ember-rails).
8
+ The sprocket extention to use `require` is going to stay in rasputin.
5
9
 
6
- It requires the following ember packages :
10
+ If you use Rasputin for Ember.js integration with Rails 3.1 assets pipeline you should use [ember-rails](https://github.com/emberjs/ember-rails) gem.
7
11
 
8
- * ember
9
- * ember-data
10
-
11
- Rasputin also provide a sprockets engine for handlebars templates. Any template in your
12
- javascript assets folder with extension `handlebars` or `hbs` will be available in ember.
13
-
14
- Examples :
15
-
16
- todos/templates/item.handlebars >> Ember.TEMPLATES['todos/item']
17
- todos/ui/templates/stats.handlebars >> Ember.TEMPLATES['todos/ui/stats']
18
- todos/templates/collection.hbs >> Ember.TEMPLATES['todos/collection']
19
-
20
- If you want to keep using old naming scheme, put this in your rails configuration block :
21
-
22
- config.rasputin.template_name_separator = '_'
23
-
24
- The new default is '/'
25
-
26
- Precompilation :
27
-
28
- Rasputin can precompile your handlebars templates. Default behavior is to precompile templates only in production environment.
29
- If you don't want this behavior you can turn it off in your rails configuration block :
30
-
31
- config.rasputin.precompile_handlebars = false
32
-
33
- If you use Slim of Haml templates, you can use handlebars filter :
34
-
35
- handlebars:
36
- {{view Ember.Button}}OK{{/view}}
37
-
38
- It will be translated as :
39
-
40
- <script type="text/x-handlebars">
41
- {{view Ember.Button}}OK{{/view}}
42
- </script>
43
-
44
- Preprocessor :
45
-
46
- If you chouse to use "javascript native require" your application.js file will look like this :
12
+ Rasputin provide a preprocessor for javascript that let you use "require" directive in your files:
47
13
 
48
14
  require('jquery');
49
15
  require('ember');
50
16
  require('ember-data');
51
17
  require('app/**/*');
52
18
 
53
- There is two new settings :
19
+ Here is the two available settings :
54
20
 
55
- config.rasputin.use_javascript_require = true
56
- config.rasputin.strip_javascript_require = true
21
+ config.rasputin.enable = true
22
+ config.rasputin.strip_require_directives = true
57
23
 
58
24
  Install
59
25
  -------
@@ -62,18 +28,13 @@ In Gemfile:
62
28
 
63
29
  gem 'rasputin'
64
30
 
65
- In your javascript assets manifest (app/assets/javascripts/application.js) add the following:
66
-
67
- //= require jquery
68
- //= require ember
69
-
70
- And any of the following you want to include:
71
-
72
- //= require ember-data
73
-
74
31
  ChangeLog
75
32
  ----------
76
33
 
34
+ 0.16.0
35
+
36
+ * All Ember.js related stuff has moved to ember-rails
37
+
77
38
  0.15.0
78
39
 
79
40
  * Update to Ember.js 0.9.6
data/Rakefile CHANGED
@@ -1,10 +1 @@
1
1
  require 'bundler/gem_tasks'
2
-
3
- desc "Update normalize.css"
4
- task :update_normalize do
5
- require 'open-uri'
6
- res = open 'https://raw.github.com/necolas/normalize.css/master/normalize.css'
7
- File.open('vendor/assets/stylesheets/normalize.css', 'w') do |f|
8
- f << res.read
9
- end
10
- end
@@ -2,27 +2,16 @@ require 'tilt'
2
2
  require 'sprockets/engines'
3
3
 
4
4
  require "rasputin/version"
5
- require "rasputin/handlebars/compiler"
6
- require "rasputin/handlebars/template"
7
-
8
- require "rasputin/slim" if defined? Slim
9
- require "rasputin/haml" if defined? Haml
10
-
11
5
  require "rasputin/require_preprocessor"
12
6
 
13
7
  module Rasputin
14
8
  class Engine < ::Rails::Engine
15
9
  config.rasputin = ActiveSupport::OrderedOptions.new
16
- config.rasputin.precompile_handlebars = Rails.env.production?
17
- config.rasputin.template_name_separator = '/'
18
-
19
- config.rasputin.use_javascript_require = true
20
- config.rasputin.strip_javascript_require = true
10
+ config.rasputin.enable = true
11
+ config.rasputin.strip_require_directives = true
21
12
 
22
13
  initializer :setup_rasputin, :group => :all do |app|
23
14
  app.assets.register_preprocessor 'application/javascript', Rasputin::RequirePreprocessor
24
- app.assets.register_engine '.handlebars', Rasputin::HandlebarsTemplate
25
- app.assets.register_engine '.hbs', Rasputin::HandlebarsTemplate
26
15
  end
27
16
  end
28
17
  end
@@ -24,10 +24,10 @@ module Rasputin
24
24
  def prepare
25
25
  @pathname = Pathname.new(file)
26
26
 
27
- @use_javascript_require = Rails.configuration.rasputin.use_javascript_require
28
- @strip_javascript_require = Rails.configuration.rasputin.strip_javascript_require
27
+ @enable = Rails.configuration.rasputin.enable
28
+ @strip_require_directives = Rails.configuration.rasputin.strip_require_directives
29
29
 
30
- if @use_javascript_require
30
+ if @enable
31
31
  @header = data[HEADER_PATTERN, 0] || ""
32
32
  @body = $' || data
33
33
  # Ensure body ends in a new line
@@ -38,7 +38,7 @@ module Rasputin
38
38
  end
39
39
 
40
40
  def evaluate(context, locals, &block)
41
- if @use_javascript_require
41
+ if @enable
42
42
  @context = context
43
43
  process_directives
44
44
 
@@ -49,7 +49,7 @@ module Rasputin
49
49
  end
50
50
 
51
51
  def processed_header
52
- if @use_javascript_require && @strip_javascript_require
52
+ if @enable && @strip_require_directives
53
53
  lineno = 0
54
54
  @processed_header ||= header.lines.map { |line|
55
55
  lineno += 1
@@ -1,3 +1,3 @@
1
1
  module Rasputin
2
- VERSION = "0.15.0"
2
+ VERSION = "0.16.0"
3
3
  end
@@ -15,7 +15,6 @@ Gem::Specification.new do |s|
15
15
  s.add_runtime_dependency 'railties', '~> 3.1'
16
16
  s.add_runtime_dependency 'actionpack', '~> 3.1'
17
17
  s.add_runtime_dependency 'sprockets', '~> 2.0'
18
- s.add_runtime_dependency 'jquery-rails', '>= 1.0'
19
18
 
20
19
  s.files = `git ls-files`.split("\n")
21
20
  #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rasputin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-04 00:00:00.000000000 Z
12
+ date: 2012-06-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
16
- requirement: &70304619095480 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70304619095480
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.1'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: actionpack
27
- requirement: &70304619070380 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '3.1'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70304619070380
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.1'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: sprockets
38
- requirement: &70304619063820 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,18 +53,12 @@ dependencies:
43
53
  version: '2.0'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70304619063820
47
- - !ruby/object:Gem::Dependency
48
- name: jquery-rails
49
- requirement: &70304619057920 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
50
57
  none: false
51
58
  requirements:
52
- - - ! '>='
59
+ - - ~>
53
60
  - !ruby/object:Gem::Version
54
- version: '1.0'
55
- type: :runtime
56
- prerelease: false
57
- version_requirements: *70304619057920
61
+ version: '2.0'
58
62
  description: Ember.js for the Rails asset pipeline.
59
63
  email:
60
64
  - paul@chavard.net
@@ -68,17 +72,9 @@ files:
68
72
  - README.md
69
73
  - Rakefile
70
74
  - lib/rasputin.rb
71
- - lib/rasputin/haml.rb
72
- - lib/rasputin/handlebars/compiler.rb
73
- - lib/rasputin/handlebars/template.rb
74
75
  - lib/rasputin/require_preprocessor.rb
75
- - lib/rasputin/slim.rb
76
76
  - lib/rasputin/version.rb
77
77
  - rasputin.gemspec
78
- - vendor/assets/javascripts/ember-data.js
79
- - vendor/assets/javascripts/ember-precompiler.js
80
- - vendor/assets/javascripts/ember.js
81
- - vendor/assets/stylesheets/normalize.css
82
78
  homepage: http://github.com/tchak/rasputin
83
79
  licenses: []
84
80
  post_install_message:
@@ -93,7 +89,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
93
89
  version: '0'
94
90
  segments:
95
91
  - 0
96
- hash: -3394298845010604622
92
+ hash: 2418885600188385524
97
93
  required_rubygems_version: !ruby/object:Gem::Requirement
98
94
  none: false
99
95
  requirements:
@@ -102,10 +98,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
98
  version: '0'
103
99
  segments:
104
100
  - 0
105
- hash: -3394298845010604622
101
+ hash: 2418885600188385524
106
102
  requirements: []
107
103
  rubyforge_project: rasputin
108
- rubygems_version: 1.8.11
104
+ rubygems_version: 1.8.23
109
105
  signing_key:
110
106
  specification_version: 3
111
107
  summary: Ember.js adapter for the Rails asset pipeline.
@@ -1,11 +0,0 @@
1
- module Haml
2
- module Filters
3
- module Handlebars
4
- include Base
5
- def render_with_options(text, options)
6
- type = "type=#{options[:attr_wrapper]}text/x-handlebars#{options[:attr_wrapper]}"
7
- "<script #{type}>\n#{text.rstrip}\n</script>"
8
- end
9
- end
10
- end
11
- end
@@ -1,52 +0,0 @@
1
- require "execjs"
2
-
3
- module Rasputin
4
- module Handlebars
5
- module Source
6
-
7
- def self.precompiler_path
8
- assets_path("ember-precompiler.js")
9
- end
10
-
11
- def self.assets_path(name)
12
- File.expand_path(File.join(__FILE__, "..", "..", "..", "..", "vendor/assets/javascripts/#{name}"))
13
- end
14
-
15
- def self.bundled_path
16
- assets_path("ember.js")
17
- end
18
-
19
- def self.path
20
- @path ||= ENV["EMBER_SOURCE_PATH"] || bundled_path
21
- end
22
-
23
- def self.path=(path)
24
- @contents = @version = @context = nil
25
- @path = path
26
- end
27
-
28
- def self.contents
29
- @contents ||= [File.read(precompiler_path), File.read(path)].join("\n")
30
- end
31
-
32
- def self.version
33
- @version ||= contents[/^Handlebars.VERSION = "([^"]*)"/, 1]
34
- end
35
-
36
- def self.context
37
- @context ||= ExecJS.compile(contents)
38
- end
39
- end
40
-
41
- class << self
42
- def version
43
- Source.version
44
- end
45
-
46
- def compile(template)
47
- template = template.read if template.respond_to?(:read)
48
- Source.context.call("precompileEmberHandlebars", template)
49
- end
50
- end
51
- end
52
- end
@@ -1,30 +0,0 @@
1
- module Rasputin
2
- class HandlebarsTemplate < Tilt::Template
3
- def self.default_mime_type
4
- 'application/javascript'
5
- end
6
-
7
- def prepare; end
8
-
9
- def evaluate(scope, locals, &block)
10
- if Rails.configuration.rasputin.precompile_handlebars
11
- func = Rasputin::Handlebars.compile(data)
12
- "Ember.TEMPLATES[#{template_path(scope.logical_path).inspect}] = Ember.Handlebars.template(#{func});"
13
- else
14
- "Ember.TEMPLATES[#{template_path(scope.logical_path).inspect}] = Ember.Handlebars.compile(#{indent(data).inspect});"
15
- end
16
- end
17
-
18
- private
19
-
20
- def template_path(path)
21
- path = path.split('/')
22
- path.delete('templates')
23
- path.join(Rails.configuration.rasputin.template_name_separator)
24
- end
25
-
26
- def indent(string)
27
- string.gsub(/$(.)/m, "\\1 ").strip
28
- end
29
- end
30
- end
@@ -1,5 +0,0 @@
1
- module Slim
2
- class EmbeddedEngine
3
- register :handlebars, TagEngine, :tag => :script, :attributes => { :type => 'text/x-handlebars' }
4
- end
5
- end
@@ -1,3037 +0,0 @@
1
- (function() {
2
- window.DS = Ember.Namespace.create({
3
- CURRENT_API_REVISION: 4
4
- });
5
-
6
- })();
7
-
8
-
9
-
10
- (function() {
11
- var get = Ember.get, set = Ember.set;
12
-
13
- /**
14
- A model array is an array that contains records of a certain type. The model
15
- array materializes records as needed when they are retrieved for the first
16
- time. You should not create model arrays yourself. Instead, an instance of
17
- DS.ModelArray or its subclasses will be returned by your application's store
18
- in response to queries.
19
- */
20
-
21
- DS.ModelArray = Ember.ArrayProxy.extend({
22
-
23
- /**
24
- The model type contained by this model array.
25
-
26
- @type DS.Model
27
- */
28
- type: null,
29
-
30
- // The array of client ids backing the model array. When a
31
- // record is requested from the model array, the record
32
- // for the client id at the same index is materialized, if
33
- // necessary, by the store.
34
- content: null,
35
-
36
- // The store that created this model array.
37
- store: null,
38
-
39
- init: function() {
40
- set(this, 'modelCache', Ember.A([]));
41
- this._super();
42
- },
43
-
44
- arrayDidChange: function(array, index, removed, added) {
45
- var modelCache = get(this, 'modelCache');
46
- modelCache.replace(index, 0, new Array(added));
47
-
48
- this._super(array, index, removed, added);
49
- },
50
-
51
- arrayWillChange: function(array, index, removed, added) {
52
- this._super(array, index, removed, added);
53
-
54
- var modelCache = get(this, 'modelCache');
55
- modelCache.replace(index, removed);
56
- },
57
-
58
- objectAtContent: function(index) {
59
- var modelCache = get(this, 'modelCache');
60
- var model = modelCache.objectAt(index);
61
-
62
- if (!model) {
63
- var store = get(this, 'store');
64
- var content = get(this, 'content');
65
-
66
- var contentObject = content.objectAt(index);
67
-
68
- if (contentObject !== undefined) {
69
- model = store.findByClientId(get(this, 'type'), contentObject);
70
- modelCache.replace(index, 1, [model]);
71
- }
72
- }
73
-
74
- return model;
75
- }
76
- });
77
-
78
- })();
79
-
80
-
81
-
82
- (function() {
83
- var get = Ember.get;
84
-
85
- DS.FilteredModelArray = DS.ModelArray.extend({
86
- filterFunction: null,
87
-
88
- replace: function() {
89
- var type = get(this, 'type').toString();
90
- throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
91
- },
92
-
93
- updateFilter: Ember.observer(function() {
94
- var store = get(this, 'store');
95
- store.updateModelArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
96
- }, 'filterFunction')
97
- });
98
-
99
- })();
100
-
101
-
102
-
103
- (function() {
104
- var get = Ember.get, set = Ember.set;
105
-
106
- DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
107
- query: null,
108
- isLoaded: false,
109
-
110
- replace: function() {
111
- var type = get(this, 'type').toString();
112
- throw new Error("The result of a server query (on " + type + ") is immutable.");
113
- },
114
-
115
- load: function(array) {
116
- var store = get(this, 'store'), type = get(this, 'type');
117
-
118
- var clientIds = store.loadMany(type, array).clientIds;
119
-
120
- this.beginPropertyChanges();
121
- set(this, 'content', Ember.A(clientIds));
122
- set(this, 'isLoaded', true);
123
- this.endPropertyChanges();
124
- }
125
- });
126
-
127
-
128
- })();
129
-
130
-
131
-
132
- (function() {
133
- var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor;
134
-
135
- var Set = function() {
136
- this.hash = {};
137
- this.list = [];
138
- };
139
-
140
- Set.prototype = {
141
- add: function(item) {
142
- var hash = this.hash,
143
- guid = guidFor(item);
144
-
145
- if (hash.hasOwnProperty(guid)) { return; }
146
-
147
- hash[guid] = true;
148
- this.list.push(item);
149
- },
150
-
151
- remove: function(item) {
152
- var hash = this.hash,
153
- guid = guidFor(item);
154
-
155
- if (!hash.hasOwnProperty(guid)) { return; }
156
-
157
- delete hash[guid];
158
- var list = this.list,
159
- index = Ember.ArrayUtils.indexOf(this, item);
160
-
161
- list.splice(index, 1);
162
- },
163
-
164
- isEmpty: function() {
165
- return this.list.length === 0;
166
- }
167
- };
168
-
169
- var ManyArrayState = Ember.State.extend({
170
- recordWasAdded: function(manager, record) {
171
- var dirty = manager.dirty, observer;
172
- dirty.add(record);
173
-
174
- observer = function() {
175
- if (!get(record, 'isDirty')) {
176
- record.removeObserver('isDirty', observer);
177
- manager.send('childWasSaved', record);
178
- }
179
- };
180
-
181
- record.addObserver('isDirty', observer);
182
- },
183
-
184
- recordWasRemoved: function(manager, record) {
185
- var dirty = manager.dirty, observer;
186
- dirty.add(record);
187
-
188
- observer = function() {
189
- record.removeObserver('isDirty', observer);
190
- if (!get(record, 'isDirty')) { manager.send('childWasSaved', record); }
191
- };
192
-
193
- record.addObserver('isDirty', observer);
194
- }
195
- });
196
-
197
- var states = {
198
- clean: ManyArrayState.create({
199
- isDirty: false,
200
-
201
- recordWasAdded: function(manager, record) {
202
- this._super(manager, record);
203
- manager.goToState('dirty');
204
- },
205
-
206
- update: function(manager, clientIds) {
207
- var manyArray = manager.manyArray;
208
- set(manyArray, 'content', clientIds);
209
- }
210
- }),
211
-
212
- dirty: ManyArrayState.create({
213
- isDirty: true,
214
-
215
- childWasSaved: function(manager, child) {
216
- var dirty = manager.dirty;
217
- dirty.remove(child);
218
-
219
- if (dirty.isEmpty()) { manager.send('arrayBecameSaved'); }
220
- },
221
-
222
- arrayBecameSaved: function(manager) {
223
- manager.goToState('clean');
224
- }
225
- })
226
- };
227
-
228
- DS.ManyArrayStateManager = Ember.StateManager.extend({
229
- manyArray: null,
230
- initialState: 'clean',
231
- states: states,
232
-
233
- init: function() {
234
- this._super();
235
- this.dirty = new Set();
236
- }
237
- });
238
-
239
- })();
240
-
241
-
242
-
243
- (function() {
244
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
245
-
246
- DS.ManyArray = DS.ModelArray.extend({
247
- init: function() {
248
- set(this, 'stateManager', DS.ManyArrayStateManager.create({ manyArray: this }));
249
-
250
- return this._super();
251
- },
252
-
253
- parentRecord: null,
254
-
255
- isDirty: Ember.computed(function() {
256
- return getPath(this, 'stateManager.currentState.isDirty');
257
- }).property('stateManager.currentState').cacheable(),
258
-
259
- fetch: function() {
260
- var clientIds = get(this, 'content'),
261
- store = get(this, 'store'),
262
- type = get(this, 'type');
263
-
264
- var ids = clientIds.map(function(clientId) {
265
- return store.clientIdToId[clientId];
266
- });
267
-
268
- store.fetchMany(type, ids);
269
- },
270
-
271
- // Overrides Ember.Array's replace method to implement
272
- replace: function(index, removed, added) {
273
- var parentRecord = get(this, 'parentRecord');
274
- var pendingParent = parentRecord && !get(parentRecord, 'id');
275
- var stateManager = get(this, 'stateManager');
276
-
277
- added = added.map(function(record) {
278
-
279
-
280
- if (pendingParent) {
281
- record.send('waitingOn', parentRecord);
282
- }
283
-
284
- this.assignInverse(record, parentRecord);
285
-
286
- stateManager.send('recordWasAdded', record);
287
-
288
- return record.get('clientId');
289
- }, this);
290
-
291
- var store = this.store;
292
-
293
- var len = index+removed, record;
294
- for (var i = index; i < len; i++) {
295
- // TODO: null out inverse FK
296
- record = this.objectAt(i);
297
- this.assignInverse(record, parentRecord, true);
298
- stateManager.send('recordWasAdded', record);
299
- }
300
-
301
- this._super(index, removed, added);
302
- },
303
-
304
- assignInverse: function(record, parentRecord, remove) {
305
- var associationMap = get(record.constructor, 'associations'),
306
- possibleAssociations = associationMap.get(parentRecord.constructor),
307
- possible, actual;
308
-
309
- if (!possibleAssociations) { return; }
310
-
311
- for (var i = 0, l = possibleAssociations.length; i < l; i++) {
312
- possible = possibleAssociations[i];
313
-
314
- if (possible.kind === 'belongsTo') {
315
- actual = possible;
316
- break;
317
- }
318
- }
319
-
320
- if (actual) {
321
- set(record, actual.name, remove ? null : parentRecord);
322
- }
323
- }
324
- });
325
-
326
- })();
327
-
328
-
329
-
330
- (function() {
331
-
332
- })();
333
-
334
-
335
-
336
- (function() {
337
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
338
-
339
- DS.Transaction = Ember.Object.extend({
340
- init: function() {
341
- set(this, 'buckets', {
342
- clean: Ember.Map.create(),
343
- created: Ember.Map.create(),
344
- updated: Ember.Map.create(),
345
- deleted: Ember.Map.create()
346
- });
347
- },
348
-
349
- createRecord: function(type, hash) {
350
- var store = get(this, 'store');
351
-
352
- return store.createRecord(type, hash, this);
353
- },
354
-
355
- add: function(record) {
356
- // we could probably make this work if someone has a valid use case. Do you?
357
-
358
-
359
- var modelTransaction = get(record, 'transaction'),
360
- defaultTransaction = getPath(this, 'store.defaultTransaction');
361
-
362
-
363
- this.adoptRecord(record);
364
- },
365
-
366
- remove: function(record) {
367
- var defaultTransaction = getPath(this, 'store.defaultTransaction');
368
-
369
- defaultTransaction.adoptRecord(record);
370
- },
371
-
372
- /**
373
- @private
374
-
375
- This method moves a record into a different transaction without the normal
376
- checks that ensure that the user is not doing something weird, like moving
377
- a dirty record into a new transaction.
378
-
379
- It is designed for internal use, such as when we are moving a clean record
380
- into a new transaction when the transaction is committed.
381
-
382
- This method must not be called unless the record is clean.
383
- */
384
- adoptRecord: function(record) {
385
- var oldTransaction = get(record, 'transaction');
386
-
387
- if (oldTransaction) {
388
- oldTransaction.removeFromBucket('clean', record);
389
- }
390
-
391
- this.addToBucket('clean', record);
392
- set(record, 'transaction', this);
393
- },
394
-
395
- modelBecameDirty: function(kind, record) {
396
- this.removeFromBucket('clean', record);
397
- this.addToBucket(kind, record);
398
- },
399
-
400
- /** @private */
401
- addToBucket: function(kind, record) {
402
- var bucket = get(get(this, 'buckets'), kind),
403
- type = record.constructor;
404
-
405
- var records = bucket.get(type);
406
-
407
- if (!records) {
408
- records = Ember.OrderedSet.create();
409
- bucket.set(type, records);
410
- }
411
-
412
- records.add(record);
413
- },
414
-
415
- /** @private */
416
- removeFromBucket: function(kind, record) {
417
- var bucket = get(get(this, 'buckets'), kind),
418
- type = record.constructor;
419
-
420
- var records = bucket.get(type);
421
- records.remove(record);
422
- },
423
-
424
- modelBecameClean: function(kind, record) {
425
- this.removeFromBucket(kind, record);
426
-
427
- var defaultTransaction = getPath(this, 'store.defaultTransaction');
428
- defaultTransaction.adoptRecord(record);
429
- },
430
-
431
- commit: function() {
432
- var buckets = get(this, 'buckets');
433
-
434
- var iterate = function(kind, fn, binding) {
435
- var dirty = get(buckets, kind);
436
-
437
- dirty.forEach(function(type, models) {
438
- if (models.isEmpty()) { return; }
439
-
440
- var array = [];
441
-
442
- models.forEach(function(model) {
443
- model.send('willCommit');
444
-
445
- if (get(model, 'isPending') === false) {
446
- array.push(model);
447
- }
448
- });
449
-
450
- fn.call(binding, type, array);
451
- });
452
- };
453
-
454
- var commitDetails = {
455
- updated: {
456
- eachType: function(fn, binding) { iterate('updated', fn, binding); }
457
- },
458
-
459
- created: {
460
- eachType: function(fn, binding) { iterate('created', fn, binding); }
461
- },
462
-
463
- deleted: {
464
- eachType: function(fn, binding) { iterate('deleted', fn, binding); }
465
- }
466
- };
467
-
468
- var store = get(this, 'store');
469
- var adapter = get(store, '_adapter');
470
-
471
- var clean = get(buckets, 'clean');
472
- var defaultTransaction = get(store, 'defaultTransaction');
473
-
474
- clean.forEach(function(type, records) {
475
- records.forEach(function(record) {
476
- this.remove(record);
477
- }, this);
478
- }, this);
479
-
480
- if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
481
- else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
482
- }
483
- });
484
-
485
- })();
486
-
487
-
488
-
489
- (function() {
490
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
491
-
492
- var DATA_PROXY = {
493
- get: function(name) {
494
- return this.savedData[name];
495
- }
496
- };
497
-
498
- // These values are used in the data cache when clientIds are
499
- // needed but the underlying data has not yet been loaded by
500
- // the server.
501
- var UNLOADED = 'unloaded';
502
- var LOADING = 'loading';
503
-
504
- // Implementors Note:
505
- //
506
- // The variables in this file are consistently named according to the following
507
- // scheme:
508
- //
509
- // * +id+ means an identifier managed by an external source, provided inside the
510
- // data hash provided by that source.
511
- // * +clientId+ means a transient numerical identifier generated at runtime by
512
- // the data store. It is important primarily because newly created objects may
513
- // not yet have an externally generated id.
514
- // * +type+ means a subclass of DS.Model.
515
-
516
- /**
517
- The store contains all of the hashes for data models loaded from the server.
518
- It is also responsible for creating instances of DS.Model when you request one
519
- of these data hashes, so that they can be bound to in your Handlebars templates.
520
-
521
- Create a new store like this:
522
-
523
- MyApp.store = DS.Store.create();
524
-
525
- You can retrieve DS.Model instances from the store in several ways. To retrieve
526
- a model for a specific id, use the `find()` method:
527
-
528
- var model = MyApp.store.find(MyApp.Contact, 123);
529
-
530
- By default, the store will talk to your backend using a standard REST mechanism.
531
- You can customize how the store talks to your backend by specifying a custom adapter:
532
-
533
- MyApp.store = DS.Store.create({
534
- adapter: 'MyApp.CustomAdapter'
535
- });
536
-
537
- You can learn more about writing a custom adapter by reading the `DS.Adapter`
538
- documentation.
539
- */
540
- DS.Store = Ember.Object.extend({
541
-
542
- /**
543
- Many methods can be invoked without specifying which store should be used.
544
- In those cases, the first store created will be used as the default. If
545
- an application has multiple stores, it should specify which store to use
546
- when performing actions, such as finding records by id.
547
-
548
- The init method registers this store as the default if none is specified.
549
- */
550
- init: function() {
551
- // Enforce API revisioning. See BREAKING_CHANGES.md for more.
552
- var revision = get(this, 'revision');
553
-
554
- if (revision !== DS.CURRENT_API_REVISION && !Ember.ENV.TESTING) {
555
- throw new Error("Error: The Ember Data library has had breaking API changes since the last time you updated the library. Please review the list of breaking changes at https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md, then update your store's `revision` property to " + DS.CURRENT_API_REVISION);
556
- }
557
-
558
- if (!get(DS, 'defaultStore') || get(this, 'isDefaultStore')) {
559
- set(DS, 'defaultStore', this);
560
- }
561
-
562
- // internal bookkeeping; not observable
563
- this.typeMaps = {};
564
- this.recordCache = [];
565
- this.clientIdToId = {};
566
- this.modelArraysByClientId = {};
567
-
568
- set(this, 'defaultTransaction', this.transaction());
569
-
570
- return this._super();
571
- },
572
-
573
- /**
574
- Returns a new transaction scoped to this store.
575
-
576
- @see {DS.Transaction}
577
- @returns DS.Transaction
578
- */
579
- transaction: function() {
580
- return DS.Transaction.create({ store: this });
581
- },
582
-
583
- /**
584
- @private
585
-
586
- This is used only by the model's DataProxy. Do not use this directly.
587
- */
588
- dataForRecord: function(record) {
589
- var type = record.constructor,
590
- clientId = get(record, 'clientId'),
591
- typeMap = this.typeMapFor(type);
592
-
593
- return typeMap.cidToHash[clientId];
594
- },
595
-
596
- /**
597
- The adapter to use to communicate to a backend server or other persistence layer.
598
-
599
- This can be specified as an instance, a class, or a property path that specifies
600
- where the adapter can be located.
601
-
602
- @property {DS.Adapter|String}
603
- */
604
- adapter: null,
605
-
606
- /**
607
- @private
608
-
609
- This property returns the adapter, after resolving a possible String.
610
-
611
- @returns DS.Adapter
612
- */
613
- _adapter: Ember.computed(function() {
614
- var adapter = get(this, 'adapter');
615
- if (typeof adapter === 'string') {
616
- return getPath(this, adapter, false) || getPath(window, adapter);
617
- }
618
- return adapter;
619
- }).property('adapter').cacheable(),
620
-
621
- // A monotonically increasing number to be used to uniquely identify
622
- // data hashes and records.
623
- clientIdCounter: 1,
624
-
625
- // ....................
626
- // . CREATE NEW MODEL .
627
- // ....................
628
-
629
- /**
630
- Create a new record in the current store. The properties passed
631
- to this method are set on the newly created record.
632
-
633
- @param {subclass of DS.Model} type
634
- @param {Object} properties a hash of properties to set on the
635
- newly created record.
636
- @returns DS.Model
637
- */
638
- createRecord: function(type, properties, transaction) {
639
- properties = properties || {};
640
-
641
- // Create a new instance of the model `type` and put it
642
- // into the specified `transaction`. If no transaction is
643
- // specified, the default transaction will be used.
644
- //
645
- // NOTE: A `transaction` is specified when the
646
- // `transaction.createRecord` API is used.
647
- var record = type._create({
648
- store: this
649
- });
650
-
651
- transaction = transaction || get(this, 'defaultTransaction');
652
- transaction.adoptRecord(record);
653
-
654
- // Extract the primary key from the `properties` hash,
655
- // based on the `primaryKey` for the model type.
656
- var id = properties[get(record, 'primaryKey')] || null;
657
-
658
- var hash = {}, clientId;
659
-
660
- // Push the hash into the store. If present, associate the
661
- // extracted `id` with the hash.
662
- clientId = this.pushHash(hash, id, type);
663
-
664
- record.send('didChangeData');
665
-
666
- var recordCache = get(this, 'recordCache');
667
-
668
- // Now that we have a clientId, attach it to the record we
669
- // just created.
670
- set(record, 'clientId', clientId);
671
-
672
- // Store the record we just created in the record cache for
673
- // this clientId.
674
- recordCache[clientId] = record;
675
-
676
- // Set the properties specified on the record.
677
- record.setProperties(properties);
678
-
679
- this.updateModelArrays(type, clientId, get(record, 'data'));
680
-
681
- return record;
682
- },
683
-
684
- // ................
685
- // . DELETE MODEL .
686
- // ................
687
-
688
- /**
689
- For symmetry, a record can be deleted via the store.
690
-
691
- @param {DS.Model} record
692
- */
693
- deleteRecord: function(record) {
694
- record.send('deleteRecord');
695
- },
696
-
697
- // ...............
698
- // . FIND MODELS .
699
- // ...............
700
-
701
- /**
702
- This is the main entry point into finding records. The first
703
- parameter to this method is always a subclass of `DS.Model`.
704
-
705
- You can use the `find` method on a subclass of `DS.Model`
706
- directly if your application only has one store. For
707
- example, instead of `store.find(App.Person, 1)`, you could
708
- say `App.Person.find(1)`.
709
-
710
- ---
711
-
712
- To find a record by ID, pass the `id` as the second parameter:
713
-
714
- store.find(App.Person, 1);
715
- App.Person.find(1);
716
-
717
- If the record with that `id` had not previously been loaded,
718
- the store will return an empty record immediately and ask
719
- the adapter to find the data by calling its `find` method.
720
-
721
- The `find` method will always return the same object for a
722
- given type and `id`. To check whether the adapter has populated
723
- a record, you can check its `isLoaded` property.
724
-
725
- ---
726
-
727
- To find all records for a type, call `find` with no additional
728
- parameters:
729
-
730
- store.find(App.Person);
731
- App.Person.find();
732
-
733
- This will return a `ModelArray` representing all known records
734
- for the given type and kick off a request to the adapter's
735
- `findAll` method to load any additional records for the type.
736
-
737
- The `ModelArray` returned by `find()` is live. If any more
738
- records for the type are added at a later time through any
739
- mechanism, it will automatically update to reflect the change.
740
-
741
- ---
742
-
743
- To find a record by a query, call `find` with a hash as the
744
- second parameter:
745
-
746
- store.find(App.Person, { page: 1 });
747
- App.Person.find({ page: 1 });
748
-
749
- This will return a `ModelArray` immediately, but it will always
750
- be an empty `ModelArray` at first. It will call the adapter's
751
- `findQuery` method, which will populate the `ModelArray` once
752
- the server has returned results.
753
-
754
- You can check whether a query results `ModelArray` has loaded
755
- by checking its `isLoaded` property.
756
- */
757
- find: function(type, id, query) {
758
- if (id === undefined) {
759
- return this.findAll(type);
760
- }
761
-
762
- if (query !== undefined) {
763
- return this.findMany(type, id, query);
764
- } else if (Ember.typeOf(id) === 'object') {
765
- return this.findQuery(type, id);
766
- }
767
-
768
- if (Ember.isArray(id)) {
769
- return this.findMany(type, id);
770
- }
771
-
772
- var clientId = this.typeMapFor(type).idToCid[id];
773
-
774
- return this.findByClientId(type, clientId, id);
775
- },
776
-
777
- findByClientId: function(type, clientId, id) {
778
- var recordCache = get(this, 'recordCache'),
779
- dataCache = this.typeMapFor(type).cidToHash,
780
- model;
781
-
782
- // If there is already a clientId assigned for this
783
- // type/id combination, try to find an existing
784
- // model for that id and return. Otherwise,
785
- // materialize a new model and set its data to the
786
- // value we already have.
787
- if (clientId !== undefined) {
788
- model = recordCache[clientId];
789
-
790
- if (!model) {
791
- // create a new instance of the model in the
792
- // 'isLoading' state
793
- model = this.materializeRecord(type, clientId);
794
-
795
- if (typeof dataCache[clientId] === 'object') {
796
- model.send('didChangeData');
797
- }
798
- }
799
- } else {
800
- clientId = this.pushHash(LOADING, id, type);
801
-
802
- // create a new instance of the model in the
803
- // 'isLoading' state
804
- model = this.materializeRecord(type, clientId);
805
-
806
- // let the adapter set the data, possibly async
807
- var adapter = get(this, '_adapter');
808
- if (adapter && adapter.find) { adapter.find(this, type, id); }
809
- else { throw fmt("Adapter is either null or does not implement `find` method", this); }
810
- }
811
-
812
- return model;
813
- },
814
-
815
- /**
816
- @private
817
-
818
- Ask the adapter to fetch IDs that are not already loaded.
819
-
820
- This method will convert `id`s to `clientId`s, filter out
821
- `clientId`s that already have a data hash present, and pass
822
- the remaining `id`s to the adapter.
823
-
824
- @param {Class} type A model class
825
- @param {Array} ids An array of ids
826
- @param {Object} query
827
-
828
- @returns {Array} An Array of all clientIds for the
829
- specified ids.
830
- */
831
- fetchMany: function(type, ids, query) {
832
- var typeMap = this.typeMapFor(type),
833
- idToClientIdMap = typeMap.idToCid,
834
- dataCache = typeMap.cidToHash,
835
- data = typeMap.cidToHash,
836
- needed;
837
-
838
- var clientIds = Ember.A([]);
839
-
840
- if (ids) {
841
- needed = [];
842
-
843
- ids.forEach(function(id) {
844
- // Get the clientId for the given id
845
- var clientId = idToClientIdMap[id];
846
-
847
- // If there is no `clientId` yet
848
- if (clientId === undefined) {
849
- // Create a new `clientId`, marking its data hash
850
- // as loading. Once the adapter returns the data
851
- // hash, it will be updated
852
- clientId = this.pushHash(LOADING, id, type);
853
- needed.push(id);
854
-
855
- // If there is a clientId, but its data hash is
856
- // marked as unloaded (this happens when a
857
- // hasMany association creates clientIds for its
858
- // referenced ids before they were loaded)
859
- } else if (clientId && data[clientId] === UNLOADED) {
860
- // change the data hash marker to loading
861
- dataCache[clientId] = LOADING;
862
- needed.push(id);
863
- }
864
-
865
- // this method is expected to return a list of
866
- // all of the clientIds for the specified ids,
867
- // unconditionally add it.
868
- clientIds.push(clientId);
869
- }, this);
870
- } else {
871
- needed = null;
872
- }
873
-
874
- // If there are any needed ids, ask the adapter to load them
875
- if ((needed && get(needed, 'length') > 0) || query) {
876
- var adapter = get(this, '_adapter');
877
- if (adapter && adapter.findMany) { adapter.findMany(this, type, needed, query); }
878
- else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }
879
- }
880
-
881
- return clientIds;
882
- },
883
-
884
- /** @private
885
- */
886
- findMany: function(type, ids, query) {
887
- var clientIds = this.fetchMany(type, ids, query);
888
-
889
- return this.createManyArray(type, clientIds);
890
- },
891
-
892
- findQuery: function(type, query) {
893
- var array = DS.AdapterPopulatedModelArray.create({ type: type, content: Ember.A([]), store: this });
894
- var adapter = get(this, '_adapter');
895
- if (adapter && adapter.findQuery) { adapter.findQuery(this, type, query, array); }
896
- else { throw fmt("Adapter is either null or does not implement `findQuery` method", this); }
897
- return array;
898
- },
899
-
900
- findAll: function(type) {
901
-
902
- var typeMap = this.typeMapFor(type),
903
- findAllCache = typeMap.findAllCache;
904
-
905
- if (findAllCache) { return findAllCache; }
906
-
907
- var array = DS.ModelArray.create({ type: type, content: Ember.A([]), store: this });
908
- this.registerModelArray(array, type);
909
-
910
- var adapter = get(this, '_adapter');
911
- if (adapter && adapter.findAll) { adapter.findAll(this, type); }
912
-
913
- typeMap.findAllCache = array;
914
- return array;
915
- },
916
-
917
- filter: function(type, query, filter) {
918
- // allow an optional server query
919
- if (arguments.length === 3) {
920
- this.findQuery(type, query);
921
- } else if (arguments.length === 2) {
922
- filter = query;
923
- }
924
-
925
- var array = DS.FilteredModelArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
926
-
927
- this.registerModelArray(array, type, filter);
928
-
929
- return array;
930
- },
931
-
932
- // ............
933
- // . UPDATING .
934
- // ............
935
-
936
- hashWasUpdated: function(type, clientId, record) {
937
- this.updateModelArrays(type, clientId, get(record, 'data'));
938
- },
939
-
940
- // ..............
941
- // . PERSISTING .
942
- // ..............
943
-
944
- commit: function() {
945
- var defaultTransaction = get(this, 'defaultTransaction');
946
- set(this, 'defaultTransaction', this.transaction());
947
-
948
- defaultTransaction.commit();
949
- },
950
-
951
- didUpdateRecords: function(array, hashes) {
952
- if (hashes) {
953
- array.forEach(function(model, idx) {
954
- this.didUpdateRecord(model, hashes[idx]);
955
- }, this);
956
- } else {
957
- array.forEach(function(model) {
958
- this.didUpdateRecord(model);
959
- }, this);
960
- }
961
- },
962
-
963
- didUpdateRecord: function(model, hash) {
964
- if (hash) {
965
- var clientId = get(model, 'clientId'),
966
- dataCache = this.typeMapFor(model.constructor).cidToHash;
967
-
968
- dataCache[clientId] = hash;
969
- model.send('didChangeData');
970
- model.hashWasUpdated();
971
- }
972
-
973
- model.send('didCommit');
974
- },
975
-
976
- didDeleteRecords: function(array) {
977
- array.forEach(function(model) {
978
- model.send('didCommit');
979
- });
980
- },
981
-
982
- didDeleteRecord: function(model) {
983
- model.send('didCommit');
984
- },
985
-
986
- _didCreateRecord: function(record, hash, typeMap, clientId, primaryKey) {
987
- var recordData = get(record, 'data'), id, changes;
988
-
989
- if (hash) {
990
- typeMap.cidToHash[clientId] = hash;
991
-
992
- // If the server returns a hash, we assume that the server's version
993
- // of the data supercedes the local changes.
994
- record.beginPropertyChanges();
995
- record.send('didChangeData');
996
- recordData.adapterDidUpdate(hash);
997
- record.hashWasUpdated();
998
- record.endPropertyChanges();
999
-
1000
- id = hash[primaryKey];
1001
-
1002
- typeMap.idToCid[id] = clientId;
1003
- this.clientIdToId[clientId] = id;
1004
- } else {
1005
- recordData.commit();
1006
- }
1007
-
1008
- record.send('didCommit');
1009
- },
1010
-
1011
-
1012
- didCreateRecords: function(type, array, hashes) {
1013
- var primaryKey = type.proto().primaryKey,
1014
- typeMap = this.typeMapFor(type),
1015
- id, clientId;
1016
-
1017
- for (var i=0, l=get(array, 'length'); i<l; i++) {
1018
- var model = array[i], hash = hashes[i];
1019
- clientId = get(model, 'clientId');
1020
-
1021
- this._didCreateRecord(model, hash, typeMap, clientId, primaryKey);
1022
- }
1023
- },
1024
-
1025
- didCreateRecord: function(model, hash) {
1026
- var type = model.constructor,
1027
- typeMap = this.typeMapFor(type),
1028
- id, clientId, primaryKey;
1029
-
1030
- // The hash is optional, but if it is not provided, the client must have
1031
- // provided a primary key.
1032
-
1033
- primaryKey = type.proto().primaryKey;
1034
-
1035
- // TODO: Make ember_assert more flexible and convert this into an ember_assert
1036
- if (hash) {
1037
-
1038
- } else {
1039
-
1040
- }
1041
-
1042
- clientId = get(model, 'clientId');
1043
-
1044
- this._didCreateRecord(model, hash, typeMap, clientId, primaryKey);
1045
- },
1046
-
1047
- recordWasInvalid: function(record, errors) {
1048
- record.send('becameInvalid', errors);
1049
- },
1050
-
1051
- // ................
1052
- // . MODEL ARRAYS .
1053
- // ................
1054
-
1055
- registerModelArray: function(array, type, filter) {
1056
- var modelArrays = this.typeMapFor(type).modelArrays;
1057
-
1058
- modelArrays.push(array);
1059
-
1060
- this.updateModelArrayFilter(array, type, filter);
1061
- },
1062
-
1063
- createManyArray: function(type, clientIds) {
1064
- var array = DS.ManyArray.create({ type: type, content: clientIds, store: this });
1065
-
1066
- clientIds.forEach(function(clientId) {
1067
- var modelArrays = this.modelArraysForClientId(clientId);
1068
- modelArrays.add(array);
1069
- }, this);
1070
-
1071
- return array;
1072
- },
1073
-
1074
- updateModelArrayFilter: function(array, type, filter) {
1075
- var typeMap = this.typeMapFor(type),
1076
- dataCache = typeMap.cidToHash,
1077
- clientIds = typeMap.clientIds,
1078
- clientId, hash, proxy;
1079
-
1080
- var recordCache = get(this, 'recordCache'), record;
1081
-
1082
- for (var i=0, l=clientIds.length; i<l; i++) {
1083
- clientId = clientIds[i];
1084
-
1085
- hash = dataCache[clientId];
1086
- if (typeof hash === 'object') {
1087
- if (record = recordCache[clientId]) {
1088
- proxy = get(record, 'data');
1089
- } else {
1090
- DATA_PROXY.savedData = hash;
1091
- proxy = DATA_PROXY;
1092
- }
1093
-
1094
- this.updateModelArray(array, filter, type, clientId, proxy);
1095
- }
1096
- }
1097
- },
1098
-
1099
- updateModelArrays: function(type, clientId, dataProxy) {
1100
- var modelArrays = this.typeMapFor(type).modelArrays,
1101
- modelArrayType, filter;
1102
-
1103
- modelArrays.forEach(function(array) {
1104
- filter = get(array, 'filterFunction');
1105
- this.updateModelArray(array, filter, type, clientId, dataProxy);
1106
- }, this);
1107
- },
1108
-
1109
- updateModelArray: function(array, filter, type, clientId, dataProxy) {
1110
- var shouldBeInArray;
1111
-
1112
- if (!filter) {
1113
- shouldBeInArray = true;
1114
- } else {
1115
- shouldBeInArray = filter(dataProxy);
1116
- }
1117
-
1118
- var content = get(array, 'content');
1119
- var alreadyInArray = content.indexOf(clientId) !== -1;
1120
-
1121
- var modelArrays = this.modelArraysForClientId(clientId);
1122
-
1123
- if (shouldBeInArray && !alreadyInArray) {
1124
- modelArrays.add(array);
1125
- content.pushObject(clientId);
1126
- } else if (!shouldBeInArray && alreadyInArray) {
1127
- modelArrays.remove(array);
1128
- content.removeObject(clientId);
1129
- }
1130
- },
1131
-
1132
- removeFromModelArrays: function(model) {
1133
- var clientId = get(model, 'clientId');
1134
- var modelArrays = this.modelArraysForClientId(clientId);
1135
-
1136
- modelArrays.forEach(function(array) {
1137
- var content = get(array, 'content');
1138
- content.removeObject(clientId);
1139
- });
1140
- },
1141
-
1142
- // ............
1143
- // . INDEXING .
1144
- // ............
1145
-
1146
- modelArraysForClientId: function(clientId) {
1147
- var modelArrays = get(this, 'modelArraysByClientId');
1148
- var ret = modelArrays[clientId];
1149
-
1150
- if (!ret) {
1151
- ret = modelArrays[clientId] = Ember.OrderedSet.create();
1152
- }
1153
-
1154
- return ret;
1155
- },
1156
-
1157
- typeMapFor: function(type) {
1158
- var typeMaps = get(this, 'typeMaps');
1159
- var guidForType = Ember.guidFor(type);
1160
-
1161
- var typeMap = typeMaps[guidForType];
1162
-
1163
- if (typeMap) {
1164
- return typeMap;
1165
- } else {
1166
- return (typeMaps[guidForType] =
1167
- {
1168
- idToCid: {},
1169
- clientIds: [],
1170
- cidToHash: {},
1171
- modelArrays: []
1172
- });
1173
- }
1174
- },
1175
-
1176
- /** @private
1177
-
1178
- For a given type and id combination, returns the client id used by the store.
1179
- If no client id has been assigned yet, one will be created and returned.
1180
-
1181
- @param {DS.Model} type
1182
- @param {String|Number} id
1183
- */
1184
- clientIdForId: function(type, id) {
1185
- var clientId = this.typeMapFor(type).idToCid[id];
1186
-
1187
- if (clientId !== undefined) { return clientId; }
1188
-
1189
- return this.pushHash(UNLOADED, id, type);
1190
- },
1191
-
1192
- // ................
1193
- // . LOADING DATA .
1194
- // ................
1195
-
1196
- /**
1197
- Load a new data hash into the store for a given id and type combination.
1198
- If data for that model had been loaded previously, the new information
1199
- overwrites the old.
1200
-
1201
- If the model you are loading data for has outstanding changes that have not
1202
- yet been saved, an exception will be thrown.
1203
-
1204
- @param {DS.Model} type
1205
- @param {String|Number} id
1206
- @param {Object} hash the data hash to load
1207
- */
1208
- load: function(type, id, hash) {
1209
- if (hash === undefined) {
1210
- hash = id;
1211
- var primaryKey = type.proto().primaryKey;
1212
-
1213
- id = hash[primaryKey];
1214
- }
1215
-
1216
- var typeMap = this.typeMapFor(type),
1217
- dataCache = typeMap.cidToHash,
1218
- clientId = typeMap.idToCid[id],
1219
- recordCache = get(this, 'recordCache');
1220
-
1221
- if (clientId !== undefined) {
1222
- dataCache[clientId] = hash;
1223
-
1224
- var record = recordCache[clientId];
1225
- if (record) {
1226
- record.send('didChangeData');
1227
- }
1228
- } else {
1229
- clientId = this.pushHash(hash, id, type);
1230
- }
1231
-
1232
- DATA_PROXY.savedData = hash;
1233
- this.updateModelArrays(type, clientId, DATA_PROXY);
1234
-
1235
- return { id: id, clientId: clientId };
1236
- },
1237
-
1238
- loadMany: function(type, ids, hashes) {
1239
- var clientIds = Ember.A([]);
1240
-
1241
- if (hashes === undefined) {
1242
- hashes = ids;
1243
- ids = [];
1244
- var primaryKey = type.proto().primaryKey;
1245
-
1246
- ids = Ember.ArrayUtils.map(hashes, function(hash) {
1247
- return hash[primaryKey];
1248
- });
1249
- }
1250
-
1251
- for (var i=0, l=get(ids, 'length'); i<l; i++) {
1252
- var loaded = this.load(type, ids[i], hashes[i]);
1253
- clientIds.pushObject(loaded.clientId);
1254
- }
1255
-
1256
- return { clientIds: clientIds, ids: ids };
1257
- },
1258
-
1259
- /** @private
1260
-
1261
- Stores a data hash for the specified type and id combination and returns
1262
- the client id.
1263
-
1264
- @param {Object} hash
1265
- @param {String|Number} id
1266
- @param {DS.Model} type
1267
- @returns {Number}
1268
- */
1269
- pushHash: function(hash, id, type) {
1270
- var typeMap = this.typeMapFor(type);
1271
-
1272
- var idToClientIdMap = typeMap.idToCid,
1273
- clientIdToIdMap = this.clientIdToId,
1274
- clientIds = typeMap.clientIds,
1275
- dataCache = typeMap.cidToHash;
1276
-
1277
- var clientId = ++this.clientIdCounter;
1278
-
1279
- dataCache[clientId] = hash;
1280
-
1281
- // if we're creating an item, this process will be done
1282
- // later, once the object has been persisted.
1283
- if (id) {
1284
- idToClientIdMap[id] = clientId;
1285
- clientIdToIdMap[clientId] = id;
1286
- }
1287
-
1288
- clientIds.push(clientId);
1289
-
1290
- return clientId;
1291
- },
1292
-
1293
- // .........................
1294
- // . MODEL MATERIALIZATION .
1295
- // .........................
1296
-
1297
- materializeRecord: function(type, clientId) {
1298
- var model;
1299
-
1300
- get(this, 'recordCache')[clientId] = model = type._create({
1301
- store: this,
1302
- clientId: clientId
1303
- });
1304
-
1305
- get(this, 'defaultTransaction').adoptRecord(model);
1306
-
1307
- model.send('loadingData');
1308
- return model;
1309
- },
1310
-
1311
- destroy: function() {
1312
- if (get(DS, 'defaultStore') === this) {
1313
- set(DS, 'defaultStore', null);
1314
- }
1315
-
1316
- return this._super();
1317
- }
1318
- });
1319
-
1320
- })();
1321
-
1322
-
1323
-
1324
- (function() {
1325
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, guidFor = Ember.guidFor;
1326
-
1327
- var stateProperty = Ember.computed(function(key) {
1328
- var parent = get(this, 'parentState');
1329
- if (parent) {
1330
- return get(parent, key);
1331
- }
1332
- }).property();
1333
-
1334
- var isEmptyObject = function(object) {
1335
- for (var name in object) {
1336
- if (object.hasOwnProperty(name)) { return false; }
1337
- }
1338
-
1339
- return true;
1340
- };
1341
-
1342
- var hasDefinedProperties = function(object) {
1343
- for (var name in object) {
1344
- if (object.hasOwnProperty(name) && object[name]) { return true; }
1345
- }
1346
-
1347
- return false;
1348
- };
1349
-
1350
- DS.State = Ember.State.extend({
1351
- isLoaded: stateProperty,
1352
- isDirty: stateProperty,
1353
- isSaving: stateProperty,
1354
- isDeleted: stateProperty,
1355
- isError: stateProperty,
1356
- isNew: stateProperty,
1357
- isValid: stateProperty,
1358
- isPending: stateProperty,
1359
-
1360
- // For states that are substates of a
1361
- // DirtyState (updated or created), it is
1362
- // useful to be able to determine which
1363
- // type of dirty state it is.
1364
- dirtyType: stateProperty
1365
- });
1366
-
1367
- var setProperty = function(manager, context) {
1368
- var key = context.key, value = context.value;
1369
-
1370
- var model = get(manager, 'model'),
1371
- data = get(model, 'data');
1372
-
1373
- set(data, key, value);
1374
- };
1375
-
1376
- var setAssociation = function(manager, context) {
1377
- var key = context.key, value = context.value;
1378
-
1379
- var model = get(manager, 'model'),
1380
- data = get(model, 'data');
1381
-
1382
- data.setAssociation(key, value);
1383
- };
1384
-
1385
- var didChangeData = function(manager) {
1386
- var model = get(manager, 'model'),
1387
- data = get(model, 'data');
1388
-
1389
- data._savedData = null;
1390
- model.notifyPropertyChange('data');
1391
- };
1392
-
1393
- // The waitingOn event shares common functionality
1394
- // between the different dirty states, but each is
1395
- // treated slightly differently. This method is exposed
1396
- // so that each implementation can invoke the common
1397
- // behavior, and then implement the behavior specific
1398
- // to the state.
1399
- var waitingOn = function(manager, object) {
1400
- var model = get(manager, 'model'),
1401
- pendingQueue = get(model, 'pendingQueue'),
1402
- objectGuid = guidFor(object);
1403
-
1404
- var observer = function() {
1405
- if (get(object, 'id')) {
1406
- manager.send('doneWaitingOn', object);
1407
- Ember.removeObserver(object, 'id', observer);
1408
- }
1409
- };
1410
-
1411
- pendingQueue[objectGuid] = [object, observer];
1412
- Ember.addObserver(object, 'id', observer);
1413
- };
1414
-
1415
- // Implementation notes:
1416
- //
1417
- // Each state has a boolean value for all of the following flags:
1418
- //
1419
- // * isLoaded: The record has a populated `data` property. When a
1420
- // record is loaded via `store.find`, `isLoaded` is false
1421
- // until the adapter sets it. When a record is created locally,
1422
- // its `isLoaded` property is always true.
1423
- // * isDirty: The record has local changes that have not yet been
1424
- // saved by the adapter. This includes records that have been
1425
- // created (but not yet saved) or deleted.
1426
- // * isSaving: The record's transaction has been committed, but
1427
- // the adapter has not yet acknowledged that the changes have
1428
- // been persisted to the backend.
1429
- // * isDeleted: The record was marked for deletion. When `isDeleted`
1430
- // is true and `isDirty` is true, the record is deleted locally
1431
- // but the deletion was not yet persisted. When `isSaving` is
1432
- // true, the change is in-flight. When both `isDirty` and
1433
- // `isSaving` are false, the change has persisted.
1434
- // * isError: The adapter reported that it was unable to save
1435
- // local changes to the backend. This may also result in the
1436
- // record having its `isValid` property become false if the
1437
- // adapter reported that server-side validations failed.
1438
- // * isNew: The record was created on the client and the adapter
1439
- // did not yet report that it was successfully saved.
1440
- // * isValid: No client-side validations have failed and the
1441
- // adapter did not report any server-side validation failures.
1442
- // * isPending: A record `isPending` when it belongs to an
1443
- // association on another record and that record has not been
1444
- // saved. A record in this state cannot be saved because it
1445
- // lacks a "foreign key" that will be supplied by its parent
1446
- // association when the parent record has been created. When
1447
- // the adapter reports that the parent has saved, the
1448
- // `isPending` property on all children will become `false`
1449
- // and the transaction will try to commit the records.
1450
-
1451
- // This mixin is mixed into various uncommitted states. Make
1452
- // sure to mix it in *after* the class definition, so its
1453
- // super points to the class definition.
1454
- var Uncommitted = Ember.Mixin.create({
1455
- setProperty: setProperty,
1456
- setAssociation: setAssociation,
1457
-
1458
- deleteRecord: function(manager) {
1459
- this._super(manager);
1460
-
1461
- var model = get(manager, 'model'),
1462
- dirtyType = get(this, 'dirtyType');
1463
-
1464
- model.withTransaction(function(t) {
1465
- t.modelBecameClean(dirtyType, model);
1466
- });
1467
- }
1468
- });
1469
-
1470
- // These mixins are mixed into substates of the concrete
1471
- // subclasses of DirtyState.
1472
-
1473
- var CreatedUncommitted = Ember.Mixin.create({
1474
- deleteRecord: function(manager) {
1475
- this._super(manager);
1476
-
1477
- manager.goToState('deleted.saved');
1478
- }
1479
- });
1480
-
1481
- var UpdatedUncommitted = Ember.Mixin.create({
1482
- deleteRecord: function(manager) {
1483
- this._super(manager);
1484
-
1485
- var model = get(manager, 'model');
1486
-
1487
- model.withTransaction(function(t) {
1488
- t.modelBecameClean('created', model);
1489
- });
1490
-
1491
- manager.goToState('deleted');
1492
- }
1493
- });
1494
-
1495
- // The dirty state is a abstract state whose functionality is
1496
- // shared between the `created` and `updated` states.
1497
- //
1498
- // The deleted state shares the `isDirty` flag with the
1499
- // subclasses of `DirtyState`, but with a very different
1500
- // implementation.
1501
- var DirtyState = DS.State.extend({
1502
- initialState: 'uncommitted',
1503
-
1504
- // FLAGS
1505
- isDirty: true,
1506
-
1507
- // SUBSTATES
1508
-
1509
- // When a record first becomes dirty, it is `uncommitted`.
1510
- // This means that there are local pending changes,
1511
- // but they have not yet begun to be saved.
1512
- uncommitted: DS.State.extend({
1513
- // TRANSITIONS
1514
- enter: function(manager) {
1515
- var dirtyType = get(this, 'dirtyType'),
1516
- model = get(manager, 'model');
1517
-
1518
- model.withTransaction(function (t) {
1519
- t.modelBecameDirty(dirtyType, model);
1520
- });
1521
- },
1522
-
1523
- exit: function(manager) {
1524
- var model = get(manager, 'model');
1525
- manager.send('invokeLifecycleCallbacks', model);
1526
- },
1527
-
1528
- // EVENTS
1529
- deleteRecord: Ember.K,
1530
-
1531
- waitingOn: function(manager, object) {
1532
- waitingOn(manager, object);
1533
- manager.goToState('pending');
1534
- },
1535
-
1536
- willCommit: function(manager) {
1537
- manager.goToState('inFlight');
1538
- }
1539
- }, Uncommitted),
1540
-
1541
- // Once a record has been handed off to the adapter to be
1542
- // saved, it is in the 'in flight' state. Changes to the
1543
- // record cannot be made during this window.
1544
- inFlight: DS.State.extend({
1545
- // FLAGS
1546
- isSaving: true,
1547
-
1548
- // TRANSITIONS
1549
- enter: function(manager) {
1550
- var dirtyType = get(this, 'dirtyType'),
1551
- model = get(manager, 'model');
1552
-
1553
- model.withTransaction(function (t) {
1554
- t.modelBecameClean(dirtyType, model);
1555
- });
1556
- },
1557
-
1558
- // EVENTS
1559
- didCommit: function(manager) {
1560
- manager.goToState('loaded');
1561
- },
1562
-
1563
- becameInvalid: function(manager, errors) {
1564
- var model = get(manager, 'model');
1565
-
1566
- set(model, 'errors', errors);
1567
- manager.goToState('invalid');
1568
- },
1569
-
1570
- didChangeData: didChangeData
1571
- }),
1572
-
1573
- // If a record becomes associated with a newly created
1574
- // parent record, it will be `pending` until the parent
1575
- // record has successfully persisted. Once this happens,
1576
- // this record can use the parent's primary key as its
1577
- // foreign key.
1578
- //
1579
- // If the record's transaction had already started to
1580
- // commit, the record will transition to the `inFlight`
1581
- // state. If it had not, the record will transition to
1582
- // the `uncommitted` state.
1583
- pending: DS.State.extend({
1584
- initialState: 'uncommitted',
1585
-
1586
- // FLAGS
1587
- isPending: true,
1588
-
1589
- // SUBSTATES
1590
-
1591
- // A pending record whose transaction has not yet
1592
- // started to commit is in this state.
1593
- uncommitted: DS.State.extend({
1594
- // EVENTS
1595
- deleteRecord: function(manager) {
1596
- var model = get(manager, 'model'),
1597
- pendingQueue = get(model, 'pendingQueue'),
1598
- tuple;
1599
-
1600
- // since we are leaving the pending state, remove any
1601
- // observers we have registered on other records.
1602
- for (var prop in pendingQueue) {
1603
- if (!pendingQueue.hasOwnProperty(prop)) { continue; }
1604
-
1605
- tuple = pendingQueue[prop];
1606
- Ember.removeObserver(tuple[0], 'id', tuple[1]);
1607
- }
1608
- },
1609
-
1610
- willCommit: function(manager) {
1611
- manager.goToState('committing');
1612
- },
1613
-
1614
- doneWaitingOn: function(manager, object) {
1615
- var model = get(manager, 'model'),
1616
- pendingQueue = get(model, 'pendingQueue'),
1617
- objectGuid = guidFor(object);
1618
-
1619
- delete pendingQueue[objectGuid];
1620
-
1621
- if (isEmptyObject(pendingQueue)) {
1622
- manager.send('doneWaiting');
1623
- }
1624
- },
1625
-
1626
- doneWaiting: function(manager) {
1627
- var dirtyType = get(this, 'dirtyType');
1628
- manager.goToState(dirtyType + '.uncommitted');
1629
- }
1630
- }, Uncommitted),
1631
-
1632
- // A pending record whose transaction has started
1633
- // to commit is in this state. Since it has not yet
1634
- // been sent to the adapter, it is not `inFlight`
1635
- // until all of its dependencies have been committed.
1636
- committing: DS.State.extend({
1637
- // FLAGS
1638
- isSaving: true,
1639
-
1640
- // EVENTS
1641
- doneWaitingOn: function(manager, object) {
1642
- var model = get(manager, 'model'),
1643
- pendingQueue = get(model, 'pendingQueue'),
1644
- objectGuid = guidFor(object);
1645
-
1646
- delete pendingQueue[objectGuid];
1647
-
1648
- if (isEmptyObject(pendingQueue)) {
1649
- manager.send('doneWaiting');
1650
- }
1651
- },
1652
-
1653
- doneWaiting: function(manager) {
1654
- var model = get(manager, 'model'),
1655
- transaction = get(model, 'transaction');
1656
-
1657
- // Now that the model is no longer pending, schedule
1658
- // the transaction to commit.
1659
- Ember.run.once(transaction, transaction.commit);
1660
- },
1661
-
1662
- willCommit: function(manager) {
1663
- var dirtyType = get(this, 'dirtyType');
1664
- manager.goToState(dirtyType + '.inFlight');
1665
- }
1666
- })
1667
- }),
1668
-
1669
- // A record is in the `invalid` state when its client-side
1670
- // invalidations have failed, or if the adapter has indicated
1671
- // the the record failed server-side invalidations.
1672
- invalid: DS.State.extend({
1673
- // FLAGS
1674
- isValid: false,
1675
-
1676
- // EVENTS
1677
- deleteRecord: function(manager) {
1678
- manager.goToState('deleted');
1679
- },
1680
-
1681
- setAssociation: setAssociation,
1682
-
1683
- setProperty: function(manager, context) {
1684
- setProperty(manager, context);
1685
-
1686
- var model = get(manager, 'model'),
1687
- errors = get(model, 'errors'),
1688
- key = context.key;
1689
-
1690
- delete errors[key];
1691
-
1692
- if (!hasDefinedProperties(errors)) {
1693
- manager.send('becameValid');
1694
- }
1695
- },
1696
-
1697
- becameValid: function(manager) {
1698
- manager.goToState('uncommitted');
1699
- }
1700
- })
1701
- });
1702
-
1703
- // The created and updated states are created outside the state
1704
- // chart so we can reopen their substates and add mixins as
1705
- // necessary.
1706
-
1707
- var createdState = DirtyState.create({
1708
- dirtyType: 'created',
1709
-
1710
- // FLAGS
1711
- isNew: true,
1712
-
1713
- // EVENTS
1714
- invokeLifecycleCallbacks: function(manager, model) {
1715
- model.didCreate();
1716
- }
1717
- });
1718
-
1719
- var updatedState = DirtyState.create({
1720
- dirtyType: 'updated',
1721
-
1722
- // EVENTS
1723
- invokeLifecycleCallbacks: function(manager, model) {
1724
- model.didUpdate();
1725
- }
1726
- });
1727
-
1728
- // The created.uncommitted state and created.pending.uncommitted share
1729
- // some logic defined in CreatedUncommitted.
1730
- createdState.states.uncommitted.reopen(CreatedUncommitted);
1731
- createdState.states.pending.states.uncommitted.reopen(CreatedUncommitted);
1732
-
1733
- // The updated.uncommitted state and updated.pending.uncommitted share
1734
- // some logic defined in UpdatedUncommitted.
1735
- updatedState.states.uncommitted.reopen(UpdatedUncommitted);
1736
- updatedState.states.pending.states.uncommitted.reopen(UpdatedUncommitted);
1737
-
1738
- var states = {
1739
- rootState: Ember.State.create({
1740
- // FLAGS
1741
- isLoaded: false,
1742
- isDirty: false,
1743
- isSaving: false,
1744
- isDeleted: false,
1745
- isError: false,
1746
- isNew: false,
1747
- isValid: true,
1748
- isPending: false,
1749
-
1750
- // SUBSTATES
1751
-
1752
- // A record begins its lifecycle in the `empty` state.
1753
- // If its data will come from the adapter, it will
1754
- // transition into the `loading` state. Otherwise, if
1755
- // the record is being created on the client, it will
1756
- // transition into the `created` state.
1757
- empty: DS.State.create({
1758
- // EVENTS
1759
- loadingData: function(manager) {
1760
- manager.goToState('loading');
1761
- },
1762
-
1763
- didChangeData: function(manager) {
1764
- didChangeData(manager);
1765
-
1766
- manager.goToState('loaded.created');
1767
- }
1768
- }),
1769
-
1770
- // A record enters this state when the store askes
1771
- // the adapter for its data. It remains in this state
1772
- // until the adapter provides the requested data.
1773
- //
1774
- // Usually, this process is asynchronous, using an
1775
- // XHR to retrieve the data.
1776
- loading: DS.State.create({
1777
- // TRANSITIONS
1778
- exit: function(manager) {
1779
- var model = get(manager, 'model');
1780
- model.didLoad();
1781
- },
1782
-
1783
- // EVENTS
1784
- didChangeData: function(manager, data) {
1785
- didChangeData(manager);
1786
- manager.send('loadedData');
1787
- },
1788
-
1789
- loadedData: function(manager) {
1790
- manager.goToState('loaded');
1791
- }
1792
- }),
1793
-
1794
- // A record enters this state when its data is populated.
1795
- // Most of a record's lifecycle is spent inside substates
1796
- // of the `loaded` state.
1797
- loaded: DS.State.create({
1798
- initialState: 'saved',
1799
-
1800
- // FLAGS
1801
- isLoaded: true,
1802
-
1803
- // SUBSTATES
1804
-
1805
- // If there are no local changes to a record, it remains
1806
- // in the `saved` state.
1807
- saved: DS.State.create({
1808
- // EVENTS
1809
- setProperty: function(manager, context) {
1810
- setProperty(manager, context);
1811
- manager.goToState('updated');
1812
- },
1813
-
1814
- setAssociation: function(manager, context) {
1815
- setAssociation(manager, context);
1816
- manager.goToState('updated');
1817
- },
1818
-
1819
- didChangeData: didChangeData,
1820
-
1821
- deleteRecord: function(manager) {
1822
- manager.goToState('deleted');
1823
- },
1824
-
1825
- waitingOn: function(manager, object) {
1826
- waitingOn(manager, object);
1827
- manager.goToState('updated.pending');
1828
- }
1829
- }),
1830
-
1831
- // A record is in this state after it has been locally
1832
- // created but before the adapter has indicated that
1833
- // it has been saved.
1834
- created: createdState,
1835
-
1836
- // A record is in this state if it has already been
1837
- // saved to the server, but there are new local changes
1838
- // that have not yet been saved.
1839
- updated: updatedState
1840
- }),
1841
-
1842
- // A record is in this state if it was deleted from the store.
1843
- deleted: DS.State.create({
1844
- // FLAGS
1845
- isDeleted: true,
1846
- isLoaded: true,
1847
- isDirty: true,
1848
-
1849
- // SUBSTATES
1850
-
1851
- // When a record is deleted, it enters the `start`
1852
- // state. It will exit this state when the record's
1853
- // transaction starts to commit.
1854
- start: DS.State.create({
1855
- // TRANSITIONS
1856
- enter: function(manager) {
1857
- var model = get(manager, 'model');
1858
- var store = get(model, 'store');
1859
-
1860
- if (store) {
1861
- store.removeFromModelArrays(model);
1862
- }
1863
-
1864
- model.withTransaction(function(t) {
1865
- t.modelBecameDirty('deleted', model);
1866
- });
1867
- },
1868
-
1869
- // EVENTS
1870
- willCommit: function(manager) {
1871
- manager.goToState('inFlight');
1872
- }
1873
- }),
1874
-
1875
- // After a record's transaction is committing, but
1876
- // before the adapter indicates that the deletion
1877
- // has saved to the server, a record is in the
1878
- // `inFlight` substate of `deleted`.
1879
- inFlight: DS.State.create({
1880
- // FLAGS
1881
- isSaving: true,
1882
-
1883
- // TRANSITIONS
1884
- exit: function(stateManager) {
1885
- var model = get(stateManager, 'model');
1886
-
1887
- model.withTransaction(function(t) {
1888
- t.modelBecameClean('deleted', model);
1889
- });
1890
- },
1891
-
1892
- // EVENTS
1893
- didCommit: function(manager) {
1894
- manager.goToState('saved');
1895
- }
1896
- }),
1897
-
1898
- // Once the adapter indicates that the deletion has
1899
- // been saved, the record enters the `saved` substate
1900
- // of `deleted`.
1901
- saved: DS.State.create({
1902
- // FLAGS
1903
- isDirty: false
1904
- })
1905
- }),
1906
-
1907
- // If the adapter indicates that there was an unknown
1908
- // error saving a record, the record enters the `error`
1909
- // state.
1910
- error: DS.State.create({
1911
- isError: true
1912
- })
1913
- })
1914
- };
1915
-
1916
- DS.StateManager = Ember.StateManager.extend({
1917
- model: null,
1918
- initialState: 'rootState',
1919
- states: states
1920
- });
1921
-
1922
- })();
1923
-
1924
-
1925
-
1926
- (function() {
1927
- var get = Ember.get, set = Ember.set;
1928
-
1929
- // This object is a regular JS object for performance. It is only
1930
- // used internally for bookkeeping purposes.
1931
- var DataProxy = DS._DataProxy = function(record) {
1932
- this.record = record;
1933
- this.unsavedData = {};
1934
- this.associations = {};
1935
- };
1936
-
1937
- DataProxy.prototype = {
1938
- get: function(key) { return Ember.get(this, key); },
1939
- set: function(key, value) { return Ember.set(this, key, value); },
1940
-
1941
- setAssociation: function(key, value) {
1942
- this.associations[key] = value;
1943
- },
1944
-
1945
- savedData: function() {
1946
- var savedData = this._savedData;
1947
- if (savedData) { return savedData; }
1948
-
1949
- var record = this.record,
1950
- clientId = get(record, 'clientId'),
1951
- store = get(record, 'store');
1952
-
1953
- if (store) {
1954
- savedData = store.dataForRecord(record);
1955
- this._savedData = savedData;
1956
- return savedData;
1957
- }
1958
- },
1959
-
1960
- unknownProperty: function(key) {
1961
- var unsavedData = this.unsavedData,
1962
- associations = this.associations,
1963
- savedData = this.savedData(),
1964
- store;
1965
-
1966
- var value = unsavedData[key], association;
1967
-
1968
- // if this is a belongsTo association, this will
1969
- // be a clientId.
1970
- association = associations[key];
1971
-
1972
- if (association !== undefined) {
1973
- store = get(this.record, 'store');
1974
- return store.clientIdToId[association];
1975
- }
1976
-
1977
- if (savedData && value === undefined) {
1978
- value = savedData[key];
1979
- }
1980
-
1981
- return value;
1982
- },
1983
-
1984
- setUnknownProperty: function(key, value) {
1985
- var record = this.record,
1986
- unsavedData = this.unsavedData;
1987
-
1988
- unsavedData[key] = value;
1989
-
1990
- record.hashWasUpdated();
1991
-
1992
- return value;
1993
- },
1994
-
1995
- commit: function() {
1996
- var record = this.record;
1997
-
1998
- var unsavedData = this.unsavedData;
1999
- var savedData = this.savedData();
2000
-
2001
- for (var prop in unsavedData) {
2002
- if (unsavedData.hasOwnProperty(prop)) {
2003
- savedData[prop] = unsavedData[prop];
2004
- delete unsavedData[prop];
2005
- }
2006
- }
2007
-
2008
- record.notifyPropertyChange('data');
2009
- },
2010
-
2011
- rollback: function() {
2012
- this.unsavedData = {};
2013
- },
2014
-
2015
- adapterDidUpdate: function(data) {
2016
- this.unsavedData = {};
2017
- }
2018
- };
2019
-
2020
- })();
2021
-
2022
-
2023
-
2024
- (function() {
2025
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath, none = Ember.none;
2026
-
2027
- var retrieveFromCurrentState = Ember.computed(function(key) {
2028
- return get(getPath(this, 'stateManager.currentState'), key);
2029
- }).property('stateManager.currentState').cacheable();
2030
-
2031
- DS.Model = Ember.Object.extend({
2032
- isLoaded: retrieveFromCurrentState,
2033
- isDirty: retrieveFromCurrentState,
2034
- isSaving: retrieveFromCurrentState,
2035
- isDeleted: retrieveFromCurrentState,
2036
- isError: retrieveFromCurrentState,
2037
- isNew: retrieveFromCurrentState,
2038
- isPending: retrieveFromCurrentState,
2039
- isValid: retrieveFromCurrentState,
2040
-
2041
- clientId: null,
2042
- transaction: null,
2043
- stateManager: null,
2044
- pendingQueue: null,
2045
- errors: null,
2046
-
2047
- // because unknownProperty is used, any internal property
2048
- // must be initialized here.
2049
- primaryKey: 'id',
2050
- id: Ember.computed(function(key, value) {
2051
- var primaryKey = get(this, 'primaryKey'),
2052
- data = get(this, 'data');
2053
-
2054
- if (arguments.length === 2) {
2055
- set(data, primaryKey, value);
2056
- return value;
2057
- }
2058
-
2059
- return data && get(data, primaryKey);
2060
- }).property('primaryKey', 'data'),
2061
-
2062
- // The following methods are callbacks invoked by `getJSON`. You
2063
- // can override one of the callbacks to override specific behavior,
2064
- // or getJSON itself.
2065
- //
2066
- // If you override getJSON, you can invoke these callbacks manually
2067
- // to get the default behavior.
2068
-
2069
- /**
2070
- Add the record's primary key to the JSON hash.
2071
-
2072
- The default implementation uses the record's specified `primaryKey`
2073
- and the `id` computed property, which are passed in as parameters.
2074
-
2075
- @param {Object} json the JSON hash being built
2076
- @param {Number|String} id the record's id
2077
- @param {String} key the primaryKey for the record
2078
- */
2079
- addIdToJSON: function(json, id, key) {
2080
- if (id) { json[key] = id; }
2081
- },
2082
-
2083
- /**
2084
- Add the attributes' current values to the JSON hash.
2085
-
2086
- The default implementation gets the current value of each
2087
- attribute from the `data`, and uses a `defaultValue` if
2088
- specified in the `DS.attr` definition.
2089
-
2090
- @param {Object} json the JSON hash being build
2091
- @param {Ember.Map} attributes a Map of attributes
2092
- @param {DataProxy} data the record's data, accessed with `get` and `set`.
2093
- */
2094
- addAttributesToJSON: function(json, attributes, data) {
2095
- attributes.forEach(function(name, meta) {
2096
- var key = meta.key(this.constructor),
2097
- value = get(data, key);
2098
-
2099
- if (value === undefined) {
2100
- value = meta.options.defaultValue;
2101
- }
2102
-
2103
- json[key] = value;
2104
- }, this);
2105
- },
2106
-
2107
- /**
2108
- Add the value of a `hasMany` association to the JSON hash.
2109
-
2110
- The default implementation honors the `embedded` option
2111
- passed to `DS.hasMany`. If embedded, `toJSON` is recursively
2112
- called on the child records. If not, the `id` of each
2113
- record is added.
2114
-
2115
- Note that if a record is not embedded and does not
2116
- yet have an `id` (usually provided by the server), it
2117
- will not be included in the output.
2118
-
2119
- @param {Object} json the JSON hash being built
2120
- @param {DataProxy} data the record's data, accessed with `get` and `set`.
2121
- @param {Object} meta information about the association
2122
- @param {Object} options options passed to `toJSON`
2123
- */
2124
- addHasManyToJSON: function(json, data, meta, options) {
2125
- var key = meta.key,
2126
- manyArray = get(this, key),
2127
- records = [],
2128
- clientId, id;
2129
-
2130
- if (meta.options.embedded) {
2131
- // TODO: Avoid materializing embedded hashes if possible
2132
- manyArray.forEach(function(record) {
2133
- records.push(record.toJSON(options));
2134
- });
2135
- } else {
2136
- var clientIds = get(manyArray, 'content');
2137
-
2138
- for (var i=0, l=clientIds.length; i<l; i++) {
2139
- clientId = clientIds[i];
2140
- id = get(this, 'store').clientIdToId[clientId];
2141
-
2142
- if (id !== undefined) {
2143
- records.push(id);
2144
- }
2145
- }
2146
- }
2147
-
2148
- json[key] = records;
2149
- },
2150
-
2151
- /**
2152
- Add the value of a `belongsTo` association to the JSON hash.
2153
-
2154
- The default implementation always includes the `id`.
2155
-
2156
- @param {Object} json the JSON hash being built
2157
- @param {DataProxy} data the record's data, accessed with `get` and `set`.
2158
- @param {Object} meta information about the association
2159
- @param {Object} options options passed to `toJSON`
2160
- */
2161
- addBelongsToToJSON: function(json, data, meta, options) {
2162
- var key = meta.key, value, id;
2163
-
2164
- if (options.embedded) {
2165
- key = options.key || get(this, 'namingConvention').keyToJSONKey(key);
2166
- value = get(data.record, key);
2167
- json[key] = value ? value.toJSON(options) : null;
2168
- } else {
2169
- key = options.key || get(this, 'namingConvention').foreignKey(key);
2170
- id = data.get(key);
2171
- json[key] = none(id) ? null : id;
2172
- }
2173
- },
2174
- /**
2175
- Create a JSON representation of the record, including its `id`,
2176
- attributes and associations. Honor any settings defined on the
2177
- attributes or associations (such as `embedded` or `key`).
2178
- */
2179
- toJSON: function(options) {
2180
- var data = get(this, 'data'),
2181
- result = {},
2182
- type = this.constructor,
2183
- attributes = get(type, 'attributes'),
2184
- primaryKey = get(this, 'primaryKey'),
2185
- id = get(this, 'id'),
2186
- store = get(this, 'store'),
2187
- associations;
2188
-
2189
- options = options || {};
2190
-
2191
- // delegate to `addIdToJSON` callback
2192
- this.addIdToJSON(result, id, primaryKey);
2193
-
2194
- // delegate to `addAttributesToJSON` callback
2195
- this.addAttributesToJSON(result, attributes, data);
2196
-
2197
- associations = get(type, 'associationsByName');
2198
-
2199
- // add associations, delegating to `addHasManyToJSON` and
2200
- // `addBelongsToToJSON`.
2201
- associations.forEach(function(key, meta) {
2202
- if (options.associations && meta.kind === 'hasMany') {
2203
- this.addHasManyToJSON(result, data, meta, options);
2204
- } else if (meta.kind === 'belongsTo') {
2205
- this.addBelongsToToJSON(result, data, meta, options);
2206
- }
2207
- }, this);
2208
-
2209
- return result;
2210
- },
2211
-
2212
- data: Ember.computed(function() {
2213
- return new DS._DataProxy(this);
2214
- }).cacheable(),
2215
-
2216
- didLoad: Ember.K,
2217
- didUpdate: Ember.K,
2218
- didCreate: Ember.K,
2219
-
2220
- init: function() {
2221
- var stateManager = DS.StateManager.create({
2222
- model: this
2223
- });
2224
-
2225
- set(this, 'pendingQueue', {});
2226
-
2227
- set(this, 'stateManager', stateManager);
2228
- stateManager.goToState('empty');
2229
- },
2230
-
2231
- destroy: function() {
2232
- if (!get(this, 'isDeleted')) {
2233
- this.deleteRecord();
2234
- }
2235
- this._super();
2236
- },
2237
-
2238
- send: function(name, context) {
2239
- return get(this, 'stateManager').send(name, context);
2240
- },
2241
-
2242
- withTransaction: function(fn) {
2243
- var transaction = get(this, 'transaction');
2244
- if (transaction) { fn(transaction); }
2245
- },
2246
-
2247
- setProperty: function(key, value) {
2248
- this.send('setProperty', { key: key, value: value });
2249
- },
2250
-
2251
- deleteRecord: function() {
2252
- this.send('deleteRecord');
2253
- },
2254
-
2255
- waitingOn: function(record) {
2256
- this.send('waitingOn', record);
2257
- },
2258
-
2259
- notifyHashWasUpdated: function() {
2260
- var store = get(this, 'store');
2261
- if (store) {
2262
- store.hashWasUpdated(this.constructor, get(this, 'clientId'), this);
2263
- }
2264
- },
2265
-
2266
- unknownProperty: function(key) {
2267
- var data = get(this, 'data');
2268
-
2269
- if (data && key in data) {
2270
-
2271
- }
2272
- },
2273
-
2274
- setUnknownProperty: function(key, value) {
2275
- var data = get(this, 'data');
2276
-
2277
- if (data && key in data) {
2278
-
2279
- } else {
2280
- return this._super(key, value);
2281
- }
2282
- },
2283
-
2284
- namingConvention: {
2285
- keyToJSONKey: function(key) {
2286
- // TODO: Strip off `is` from the front. Example: `isHipster` becomes `hipster`
2287
- return Ember.String.decamelize(key);
2288
- },
2289
-
2290
- foreignKey: function(key) {
2291
- return Ember.String.decamelize(key) + '_id';
2292
- }
2293
- },
2294
-
2295
- /** @private */
2296
- hashWasUpdated: function() {
2297
- // At the end of the run loop, notify model arrays that
2298
- // this record has changed so they can re-evaluate its contents
2299
- // to determine membership.
2300
- Ember.run.once(this, this.notifyHashWasUpdated);
2301
- },
2302
-
2303
- dataDidChange: Ember.observer(function() {
2304
- var associations = get(this.constructor, 'associationsByName'),
2305
- data = get(this, 'data'), store = get(this, 'store'),
2306
- idToClientId = store.idToClientId,
2307
- cachedValue;
2308
-
2309
- associations.forEach(function(name, association) {
2310
- if (association.kind === 'hasMany') {
2311
- cachedValue = this.cacheFor(name);
2312
-
2313
- if (cachedValue) {
2314
- var ids = data.get(name) || [];
2315
- var clientIds = Ember.ArrayUtils.map(ids, function(id) {
2316
- return store.clientIdForId(association.type, id);
2317
- });
2318
-
2319
- set(cachedValue, 'content', Ember.A(clientIds));
2320
- cachedValue.fetch();
2321
- }
2322
- }
2323
- }, this);
2324
- }, 'data')
2325
- });
2326
-
2327
- // Helper function to generate store aliases.
2328
- // This returns a function that invokes the named alias
2329
- // on the default store, but injects the class as the
2330
- // first parameter.
2331
- var storeAlias = function(methodName) {
2332
- return function() {
2333
- var store = get(DS, 'defaultStore'),
2334
- args = [].slice.call(arguments);
2335
-
2336
- args.unshift(this);
2337
- return store[methodName].apply(store, args);
2338
- };
2339
- };
2340
-
2341
- DS.Model.reopenClass({
2342
- find: storeAlias('find'),
2343
- filter: storeAlias('filter'),
2344
-
2345
- _create: DS.Model.create,
2346
-
2347
- create: function() {
2348
- throw new Ember.Error("You should not call `create` on a model. Instead, call `createRecord` with the attributes you would like to set.");
2349
- },
2350
-
2351
- createRecord: storeAlias('createRecord')
2352
- });
2353
-
2354
- })();
2355
-
2356
-
2357
-
2358
- (function() {
2359
- var get = Ember.get, getPath = Ember.getPath;
2360
- DS.Model.reopenClass({
2361
- attributes: Ember.computed(function() {
2362
- var map = Ember.Map.create();
2363
-
2364
- this.eachComputedProperty(function(name, meta) {
2365
- if (meta.isAttribute) { map.set(name, meta); }
2366
- });
2367
-
2368
- return map;
2369
- }).cacheable(),
2370
-
2371
- processAttributeKeys: function() {
2372
- if (this.processedAttributeKeys) { return; }
2373
-
2374
- var namingConvention = this.proto().namingConvention;
2375
-
2376
- this.eachComputedProperty(function(name, meta) {
2377
- if (meta.isAttribute && !meta.options.key) {
2378
- meta.options.key = namingConvention.keyToJSONKey(name, this);
2379
- }
2380
- }, this);
2381
- }
2382
- });
2383
-
2384
- DS.attr = function(type, options) {
2385
- var transform = DS.attr.transforms[type];
2386
-
2387
-
2388
- var transformFrom = transform.from;
2389
- var transformTo = transform.to;
2390
-
2391
- options = options || {};
2392
-
2393
- var meta = {
2394
- type: type,
2395
- isAttribute: true,
2396
- options: options,
2397
-
2398
- // this will ensure that the key always takes naming
2399
- // conventions into consideration.
2400
- key: function(recordType) {
2401
- recordType.processAttributeKeys();
2402
- return options.key;
2403
- }
2404
- };
2405
-
2406
- return Ember.computed(function(key, value) {
2407
- var data;
2408
-
2409
- key = meta.key(this.constructor);
2410
-
2411
- if (arguments.length === 2) {
2412
- value = transformTo(value);
2413
- this.setProperty(key, value);
2414
- } else {
2415
- data = get(this, 'data');
2416
- value = get(data, key);
2417
-
2418
- if (value === undefined) {
2419
- value = options.defaultValue;
2420
- }
2421
- }
2422
-
2423
- return transformFrom(value);
2424
- // `data` is never set directly. However, it may be
2425
- // invalidated from the state manager's setData
2426
- // event.
2427
- }).property('data').cacheable().meta(meta);
2428
- };
2429
-
2430
- DS.attr.transforms = {
2431
- string: {
2432
- from: function(serialized) {
2433
- return Ember.none(serialized) ? null : String(serialized);
2434
- },
2435
-
2436
- to: function(deserialized) {
2437
- return Ember.none(deserialized) ? null : String(deserialized);
2438
- }
2439
- },
2440
-
2441
- number: {
2442
- from: function(serialized) {
2443
- return Ember.none(serialized) ? null : Number(serialized);
2444
- },
2445
-
2446
- to: function(deserialized) {
2447
- return Ember.none(deserialized) ? null : Number(deserialized);
2448
- }
2449
- },
2450
-
2451
- 'boolean': {
2452
- from: function(serialized) {
2453
- return Boolean(serialized);
2454
- },
2455
-
2456
- to: function(deserialized) {
2457
- return Boolean(deserialized);
2458
- }
2459
- },
2460
-
2461
- date: {
2462
- from: function(serialized) {
2463
- var type = typeof serialized;
2464
-
2465
- if (type === "string" || type === "number") {
2466
- return new Date(serialized);
2467
- } else if (serialized === null || serialized === undefined) {
2468
- // if the value is not present in the data,
2469
- // return undefined, not null.
2470
- return serialized;
2471
- } else {
2472
- return null;
2473
- }
2474
- },
2475
-
2476
- to: function(date) {
2477
- if (date instanceof Date) {
2478
- var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2479
- var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2480
-
2481
- var pad = function(num) {
2482
- return num < 10 ? "0"+num : ""+num;
2483
- };
2484
-
2485
- var utcYear = date.getUTCFullYear(),
2486
- utcMonth = date.getUTCMonth(),
2487
- utcDayOfMonth = date.getUTCDate(),
2488
- utcDay = date.getUTCDay(),
2489
- utcHours = date.getUTCHours(),
2490
- utcMinutes = date.getUTCMinutes(),
2491
- utcSeconds = date.getUTCSeconds();
2492
-
2493
-
2494
- var dayOfWeek = days[utcDay];
2495
- var dayOfMonth = pad(utcDayOfMonth);
2496
- var month = months[utcMonth];
2497
-
2498
- return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
2499
- pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
2500
- } else if (date === undefined) {
2501
- return undefined;
2502
- } else {
2503
- return null;
2504
- }
2505
- }
2506
- }
2507
- };
2508
-
2509
-
2510
- })();
2511
-
2512
-
2513
-
2514
- (function() {
2515
-
2516
- })();
2517
-
2518
-
2519
-
2520
- (function() {
2521
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath,
2522
- none = Ember.none;
2523
-
2524
- var embeddedFindRecord = function(store, type, data, key, one) {
2525
- var association = get(data, key);
2526
- return none(association) ? undefined : store.load(type, association).id;
2527
- };
2528
-
2529
- var referencedFindRecord = function(store, type, data, key, one) {
2530
- return get(data, key);
2531
- };
2532
-
2533
- var hasAssociation = function(type, options, one) {
2534
- options = options || {};
2535
-
2536
- var embedded = options.embedded,
2537
- findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
2538
-
2539
- var meta = { type: type, isAssociation: true, options: options, kind: 'belongsTo' };
2540
-
2541
- return Ember.computed(function(key, value) {
2542
- var data = get(this, 'data'), ids, id, association,
2543
- store = get(this, 'store');
2544
-
2545
- if (typeof type === 'string') {
2546
- type = getPath(this, type, false) || getPath(window, type);
2547
- }
2548
-
2549
- if (arguments.length === 2) {
2550
- key = options.key || get(this, 'namingConvention').foreignKey(key);
2551
- this.send('setAssociation', { key: key, value: value === null ? null : get(value, 'clientId') });
2552
- //data.setAssociation(key, get(value, 'clientId'));
2553
- // put the client id in `key` in the data hash
2554
- return value;
2555
- } else {
2556
- // Embedded belongsTo associations should not look for
2557
- // a foreign key.
2558
- if (embedded) {
2559
- key = options.key || key;
2560
-
2561
- // Non-embedded associations should look for a foreign key.
2562
- // For example, instead of person, we might look for person_id
2563
- } else {
2564
- key = options.key || get(this, 'namingConvention').foreignKey(key);
2565
- }
2566
- id = findRecord(store, type, data, key, true);
2567
- association = id ? store.find(type, id) : null;
2568
- }
2569
-
2570
- return association;
2571
- }).property('data').cacheable().meta(meta);
2572
- };
2573
-
2574
- DS.belongsTo = function(type, options) {
2575
-
2576
- return hasAssociation(type, options);
2577
- };
2578
-
2579
- })();
2580
-
2581
-
2582
-
2583
- (function() {
2584
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
2585
- var embeddedFindRecord = function(store, type, data, key) {
2586
- var association = get(data, key);
2587
- return association ? store.loadMany(type, association).ids : [];
2588
- };
2589
-
2590
- var referencedFindRecord = function(store, type, data, key, one) {
2591
- return get(data, key);
2592
- };
2593
-
2594
- var hasAssociation = function(type, options) {
2595
- options = options || {};
2596
-
2597
- var embedded = options.embedded,
2598
- findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
2599
-
2600
- var meta = { type: type, isAssociation: true, options: options, kind: 'hasMany' };
2601
-
2602
- return Ember.computed(function(key, value) {
2603
- var data = get(this, 'data'),
2604
- store = get(this, 'store'),
2605
- ids, id, association;
2606
-
2607
- if (typeof type === 'string') {
2608
- type = getPath(this, type, false) || getPath(window, type);
2609
- }
2610
-
2611
- key = options.key || key;
2612
- ids = findRecord(store, type, data, key);
2613
- association = store.findMany(type, ids);
2614
- set(association, 'parentRecord', this);
2615
-
2616
- return association;
2617
- }).property().cacheable().meta(meta);
2618
- };
2619
-
2620
- DS.hasMany = function(type, options) {
2621
-
2622
- return hasAssociation(type, options);
2623
- };
2624
-
2625
- })();
2626
-
2627
-
2628
-
2629
- (function() {
2630
- var get = Ember.get, getPath = Ember.getPath;
2631
-
2632
- DS.Model.reopenClass({
2633
- typeForAssociation: function(name) {
2634
- var association = get(this, 'associationsByName').get(name);
2635
- return association && association.type;
2636
- },
2637
-
2638
- associations: Ember.computed(function() {
2639
- var map = Ember.Map.create();
2640
-
2641
- this.eachComputedProperty(function(name, meta) {
2642
- if (meta.isAssociation) {
2643
- var type = meta.type,
2644
- typeList = map.get(type);
2645
-
2646
- if (typeof type === 'string') {
2647
- type = getPath(this, type, false) || getPath(window, type);
2648
- meta.type = type;
2649
- }
2650
-
2651
- if (!typeList) {
2652
- typeList = [];
2653
- map.set(type, typeList);
2654
- }
2655
-
2656
- typeList.push({ name: name, kind: meta.kind });
2657
- }
2658
- });
2659
-
2660
- return map;
2661
- }).cacheable(),
2662
-
2663
- associationsByName: Ember.computed(function() {
2664
- var map = Ember.Map.create(), type;
2665
-
2666
- this.eachComputedProperty(function(name, meta) {
2667
- if (meta.isAssociation) {
2668
- meta.key = name;
2669
- type = meta.type;
2670
-
2671
- if (typeof type === 'string') {
2672
- type = getPath(this, type, false) || getPath(window, type);
2673
- meta.type = type;
2674
- }
2675
-
2676
- map.set(name, meta);
2677
- }
2678
- });
2679
-
2680
- return map;
2681
- }).cacheable()
2682
- });
2683
-
2684
- })();
2685
-
2686
-
2687
-
2688
- (function() {
2689
-
2690
- })();
2691
-
2692
-
2693
-
2694
- (function() {
2695
- DS.Adapter = Ember.Object.extend({
2696
- commit: function(store, commitDetails) {
2697
- commitDetails.updated.eachType(function(type, array) {
2698
- this.updateRecords(store, type, array.slice());
2699
- }, this);
2700
-
2701
- commitDetails.created.eachType(function(type, array) {
2702
- this.createRecords(store, type, array.slice());
2703
- }, this);
2704
-
2705
- commitDetails.deleted.eachType(function(type, array) {
2706
- this.deleteRecords(store, type, array.slice());
2707
- }, this);
2708
- },
2709
-
2710
- createRecords: function(store, type, models) {
2711
- models.forEach(function(model) {
2712
- this.createRecord(store, type, model);
2713
- }, this);
2714
- },
2715
-
2716
- updateRecords: function(store, type, models) {
2717
- models.forEach(function(model) {
2718
- this.updateRecord(store, type, model);
2719
- }, this);
2720
- },
2721
-
2722
- deleteRecords: function(store, type, models) {
2723
- models.forEach(function(model) {
2724
- this.deleteRecord(store, type, model);
2725
- }, this);
2726
- },
2727
-
2728
- findMany: function(store, type, ids) {
2729
- ids.forEach(function(id) {
2730
- this.find(store, type, id);
2731
- }, this);
2732
- }
2733
- });
2734
-
2735
- })();
2736
-
2737
-
2738
-
2739
- (function() {
2740
- DS.fixtureAdapter = DS.Adapter.create({
2741
- find: function(store, type, id) {
2742
- var fixtures = type.FIXTURES;
2743
-
2744
- if (fixtures.hasLoaded) { return; }
2745
-
2746
- setTimeout(function() {
2747
- store.loadMany(type, fixtures);
2748
- fixtures.hasLoaded = true;
2749
- }, 300);
2750
- },
2751
-
2752
- findMany: function() {
2753
- this.find.apply(this, arguments);
2754
- },
2755
-
2756
- findAll: function(store, type) {
2757
- var fixtures = type.FIXTURES;
2758
-
2759
-
2760
- var ids = fixtures.map(function(item, index, self){ return item.id; });
2761
- store.loadMany(type, ids, fixtures);
2762
- }
2763
-
2764
- });
2765
-
2766
- })();
2767
-
2768
-
2769
-
2770
- (function() {
2771
- /*global jQuery*/
2772
-
2773
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
2774
-
2775
- DS.RESTAdapter = DS.Adapter.extend({
2776
- createRecord: function(store, type, model) {
2777
- var root = this.rootForType(type);
2778
-
2779
- var data = {};
2780
- data[root] = model.toJSON();
2781
-
2782
- this.ajax(this.buildURL(root), "POST", {
2783
- data: data,
2784
- success: function(json) {
2785
- this.sideload(store, type, json, root);
2786
- store.didCreateRecord(model, json[root]);
2787
- }
2788
- });
2789
- },
2790
-
2791
- createRecords: function(store, type, models) {
2792
- if (get(this, 'bulkCommit') === false) {
2793
- return this._super(store, type, models);
2794
- }
2795
-
2796
- var root = this.rootForType(type),
2797
- plural = this.pluralize(root);
2798
-
2799
- var data = {};
2800
- data[plural] = models.map(function(model) {
2801
- return model.toJSON();
2802
- });
2803
-
2804
- this.ajax(this.buildURL(root), "POST", {
2805
- data: data,
2806
-
2807
- success: function(json) {
2808
- this.sideload(store, type, json, plural);
2809
- store.didCreateRecords(type, models, json[plural]);
2810
- }
2811
- });
2812
- },
2813
-
2814
- updateRecord: function(store, type, model) {
2815
- var id = get(model, 'id');
2816
- var root = this.rootForType(type);
2817
-
2818
- var data = {};
2819
- data[root] = model.toJSON();
2820
-
2821
- this.ajax(this.buildURL(root, id), "PUT", {
2822
- data: data,
2823
- success: function(json) {
2824
- this.sideload(store, type, json, root);
2825
- store.didUpdateRecord(model, json && json[root]);
2826
- }
2827
- });
2828
- },
2829
-
2830
- updateRecords: function(store, type, models) {
2831
- if (get(this, 'bulkCommit') === false) {
2832
- return this._super(store, type, models);
2833
- }
2834
-
2835
- var root = this.rootForType(type),
2836
- plural = this.pluralize(root);
2837
-
2838
- var data = {};
2839
- data[plural] = models.map(function(model) {
2840
- return model.toJSON();
2841
- });
2842
-
2843
- this.ajax(this.buildURL(root, "bulk"), "PUT", {
2844
- data: data,
2845
- success: function(json) {
2846
- this.sideload(store, type, json, plural);
2847
- store.didUpdateRecords(models, json[plural]);
2848
- }
2849
- });
2850
- },
2851
-
2852
- deleteRecord: function(store, type, model) {
2853
- var id = get(model, 'id');
2854
- var root = this.rootForType(type);
2855
-
2856
- this.ajax(this.buildURL(root, id), "DELETE", {
2857
- success: function(json) {
2858
- if (json) { this.sideload(store, type, json); }
2859
- store.didDeleteRecord(model);
2860
- }
2861
- });
2862
- },
2863
-
2864
- deleteRecords: function(store, type, models) {
2865
- if (get(this, 'bulkCommit') === false) {
2866
- return this._super(store, type, models);
2867
- }
2868
-
2869
- var root = this.rootForType(type),
2870
- plural = this.pluralize(root);
2871
-
2872
- var data = {};
2873
- data[plural] = models.map(function(model) {
2874
- return get(model, 'id');
2875
- });
2876
-
2877
- this.ajax(this.buildURL(root, 'bulk'), "DELETE", {
2878
- data: data,
2879
- success: function(json) {
2880
- if (json) { this.sideload(store, type, json); }
2881
- store.didDeleteRecords(models);
2882
- }
2883
- });
2884
- },
2885
-
2886
- find: function(store, type, id) {
2887
- var root = this.rootForType(type);
2888
-
2889
- this.ajax(this.buildURL(root, id), "GET", {
2890
- success: function(json) {
2891
- store.load(type, json[root]);
2892
- this.sideload(store, type, json, root);
2893
- }
2894
- });
2895
- },
2896
-
2897
- findMany: function(store, type, ids) {
2898
- var root = this.rootForType(type), plural = this.pluralize(root);
2899
-
2900
- this.ajax(this.buildURL(root), "GET", {
2901
- data: { ids: ids },
2902
- success: function(json) {
2903
- store.loadMany(type, ids, json[plural]);
2904
- this.sideload(store, type, json, plural);
2905
- }
2906
- });
2907
- },
2908
-
2909
- findAll: function(store, type) {
2910
- var root = this.rootForType(type), plural = this.pluralize(root);
2911
-
2912
- this.ajax(this.buildURL(root), "GET", {
2913
- success: function(json) {
2914
- store.loadMany(type, json[plural]);
2915
- this.sideload(store, type, json, plural);
2916
- }
2917
- });
2918
- },
2919
-
2920
- findQuery: function(store, type, query, modelArray) {
2921
- var root = this.rootForType(type), plural = this.pluralize(root);
2922
-
2923
- this.ajax(this.buildURL(root), "GET", {
2924
- data: query,
2925
- success: function(json) {
2926
- modelArray.load(json[plural]);
2927
- this.sideload(store, type, json, plural);
2928
- }
2929
- });
2930
- },
2931
-
2932
- // HELPERS
2933
-
2934
- plurals: {},
2935
-
2936
- // define a plurals hash in your subclass to define
2937
- // special-case pluralization
2938
- pluralize: function(name) {
2939
- return this.plurals[name] || name + "s";
2940
- },
2941
-
2942
- rootForType: function(type) {
2943
- if (type.url) { return type.url; }
2944
-
2945
- // use the last part of the name as the URL
2946
- var parts = type.toString().split(".");
2947
- var name = parts[parts.length - 1];
2948
- return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
2949
- },
2950
-
2951
- ajax: function(url, type, hash) {
2952
- hash.url = url;
2953
- hash.type = type;
2954
- hash.dataType = 'json';
2955
- hash.contentType = 'application/json';
2956
- hash.context = this;
2957
-
2958
- if (hash.data && type !== 'GET') {
2959
- hash.data = JSON.stringify(hash.data);
2960
- }
2961
-
2962
- jQuery.ajax(hash);
2963
- },
2964
-
2965
- sideload: function(store, type, json, root) {
2966
- var sideloadedType, mappings;
2967
-
2968
- for (var prop in json) {
2969
- if (!json.hasOwnProperty(prop)) { continue; }
2970
- if (prop === root) { continue; }
2971
-
2972
- sideloadedType = type.typeForAssociation(prop);
2973
-
2974
- if (!sideloadedType) {
2975
- mappings = get(this, 'mappings');
2976
-
2977
-
2978
- sideloadedType = get(get(this, 'mappings'), prop);
2979
-
2980
- }
2981
-
2982
- this.loadValue(store, sideloadedType, json[prop]);
2983
- }
2984
- },
2985
-
2986
- loadValue: function(store, type, value) {
2987
- if (value instanceof Array) {
2988
- store.loadMany(type, value);
2989
- } else {
2990
- store.load(type, value);
2991
- }
2992
- },
2993
-
2994
- buildURL: function(model, suffix) {
2995
- var url = [""];
2996
-
2997
- if (this.namespace !== undefined) {
2998
- url.push(this.namespace);
2999
- }
3000
-
3001
- url.push(this.pluralize(model));
3002
- if (suffix !== undefined) {
3003
- url.push(suffix);
3004
- }
3005
-
3006
- return url.join("/");
3007
- }
3008
- });
3009
-
3010
-
3011
- })();
3012
-
3013
-
3014
-
3015
- (function() {
3016
- //Copyright (C) 2011 by Living Social, Inc.
3017
-
3018
- //Permission is hereby granted, free of charge, to any person obtaining a copy of
3019
- //this software and associated documentation files (the "Software"), to deal in
3020
- //the Software without restriction, including without limitation the rights to
3021
- //use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
3022
- //of the Software, and to permit persons to whom the Software is furnished to do
3023
- //so, subject to the following conditions:
3024
-
3025
- //The above copyright notice and this permission notice shall be included in all
3026
- //copies or substantial portions of the Software.
3027
-
3028
- //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3029
- //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3030
- //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3031
- //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3032
- //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3033
- //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3034
- //SOFTWARE.
3035
-
3036
- })();
3037
-