rasputin 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-