embient 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ test/dummy/db/*.sqlite3
5
+ test/dummy/log/*.log
6
+ test/dummy/tmp/
7
+ test/dummy/.sass-cache
8
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Declare your gem's dependencies in embient.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
@@ -0,0 +1,114 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ embient (0.0.1)
5
+ coffee-rails
6
+ emberjs-rails
7
+ jquery-rails
8
+ rails (~> 3.2.1)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ actionmailer (3.2.1)
14
+ actionpack (= 3.2.1)
15
+ mail (~> 2.4.0)
16
+ actionpack (3.2.1)
17
+ activemodel (= 3.2.1)
18
+ activesupport (= 3.2.1)
19
+ builder (~> 3.0.0)
20
+ erubis (~> 2.7.0)
21
+ journey (~> 1.0.1)
22
+ rack (~> 1.4.0)
23
+ rack-cache (~> 1.1)
24
+ rack-test (~> 0.6.1)
25
+ sprockets (~> 2.1.2)
26
+ activemodel (3.2.1)
27
+ activesupport (= 3.2.1)
28
+ builder (~> 3.0.0)
29
+ activerecord (3.2.1)
30
+ activemodel (= 3.2.1)
31
+ activesupport (= 3.2.1)
32
+ arel (~> 3.0.0)
33
+ tzinfo (~> 0.3.29)
34
+ activeresource (3.2.1)
35
+ activemodel (= 3.2.1)
36
+ activesupport (= 3.2.1)
37
+ activesupport (3.2.1)
38
+ i18n (~> 0.6)
39
+ multi_json (~> 1.0)
40
+ arel (3.0.0)
41
+ builder (3.0.0)
42
+ coffee-rails (3.2.2)
43
+ coffee-script (>= 2.2.0)
44
+ railties (~> 3.2.0)
45
+ coffee-script (2.2.0)
46
+ coffee-script-source
47
+ execjs
48
+ coffee-script-source (1.2.0)
49
+ emberjs-rails (2012.1.25)
50
+ hamlbars
51
+ rails (~> 3.1)
52
+ erubis (2.7.0)
53
+ execjs (1.3.0)
54
+ multi_json (~> 1.0)
55
+ haml (3.1.4)
56
+ hamlbars (2012.1.13)
57
+ haml
58
+ sprockets
59
+ tilt
60
+ hike (1.2.1)
61
+ i18n (0.6.0)
62
+ journey (1.0.1)
63
+ jquery-rails (2.0.0)
64
+ railties (>= 3.2.0.beta, < 5.0)
65
+ thor (~> 0.14)
66
+ json (1.6.5)
67
+ mail (2.4.1)
68
+ i18n (>= 0.4.0)
69
+ mime-types (~> 1.16)
70
+ treetop (~> 1.4.8)
71
+ mime-types (1.17.2)
72
+ multi_json (1.1.0)
73
+ polyglot (0.3.3)
74
+ rack (1.4.1)
75
+ rack-cache (1.1)
76
+ rack (>= 0.4)
77
+ rack-ssl (1.3.2)
78
+ rack
79
+ rack-test (0.6.1)
80
+ rack (>= 1.0)
81
+ rails (3.2.1)
82
+ actionmailer (= 3.2.1)
83
+ actionpack (= 3.2.1)
84
+ activerecord (= 3.2.1)
85
+ activeresource (= 3.2.1)
86
+ activesupport (= 3.2.1)
87
+ bundler (~> 1.0)
88
+ railties (= 3.2.1)
89
+ railties (3.2.1)
90
+ actionpack (= 3.2.1)
91
+ activesupport (= 3.2.1)
92
+ rack-ssl (~> 1.3.2)
93
+ rake (>= 0.8.7)
94
+ rdoc (~> 3.4)
95
+ thor (~> 0.14.6)
96
+ rake (0.9.2.2)
97
+ rdoc (3.12)
98
+ json (~> 1.4)
99
+ sprockets (2.1.2)
100
+ hike (~> 1.2)
101
+ rack (~> 1.0)
102
+ tilt (~> 1.1, != 1.3.0)
103
+ thor (0.14.6)
104
+ tilt (1.3.3)
105
+ treetop (1.4.10)
106
+ polyglot
107
+ polyglot (>= 0.3.1)
108
+ tzinfo (0.3.31)
109
+
110
+ PLATFORMS
111
+ ruby
112
+
113
+ DEPENDENCIES
114
+ embient!
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,15 @@
1
+ Embient
2
+ =======
3
+
4
+ Embient is an opinionated gem that pulls together Ember.js and its best
5
+ addons to be used in the Rails 3.1+ asset pipeline. It also provides
6
+ Require.js to build modular JavaScript applications and libraries with
7
+ and around Embient.
8
+
9
+ To use Embient in your Rails application simply include it in your Gemfile:
10
+
11
+ gem 'embient'
12
+
13
+ and run
14
+
15
+ bundle install
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Embient'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
@@ -0,0 +1,24 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ # Maintain your gem's version:
4
+ require "embient/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |s|
8
+ s.name = "embient"
9
+ s.version = Embient::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Dominik Guzei"]
12
+ s.email = ["dominik.guzei@gmail.com"]
13
+ s.homepage = "https://github.com/DominikGuzei/embient"
14
+ s.summary = "Opinionated Ember.js environment and addons for Ruby on Rails"
15
+ s.description = "Pulls together various existing ember/sproutcore addons and provides structure for future extensions."
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "rails", ">= 3.1.0"
22
+ s.add_dependency "ember-rails"
23
+
24
+ end
@@ -0,0 +1,4 @@
1
+ require "embient/engine"
2
+
3
+ module Embient
4
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'ember-rails'
3
+
4
+ module Embient
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Embient
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :embient do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,2176 @@
1
+
2
+ (function(exports) {
3
+ window.DS = Ember.Namespace.create();
4
+
5
+ })({});
6
+
7
+
8
+ (function(exports) {
9
+ DS.Adapter = Ember.Object.extend({
10
+ commit: function(store, commitDetails) {
11
+ commitDetails.updated.eachType(function(type, array) {
12
+ this.updateRecords(store, type, array.slice());
13
+ }, this);
14
+
15
+ commitDetails.created.eachType(function(type, array) {
16
+ this.createRecords(store, type, array.slice());
17
+ }, this);
18
+
19
+ commitDetails.deleted.eachType(function(type, array) {
20
+ this.deleteRecords(store, type, array.slice());
21
+ }, this);
22
+ },
23
+
24
+ createRecords: function(store, type, models) {
25
+ models.forEach(function(model) {
26
+ this.createRecord(store, type, model);
27
+ }, this);
28
+ },
29
+
30
+ updateRecords: function(store, type, models) {
31
+ models.forEach(function(model) {
32
+ this.updateRecord(store, type, model);
33
+ }, this);
34
+ },
35
+
36
+ deleteRecords: function(store, type, models) {
37
+ models.forEach(function(model) {
38
+ this.deleteRecord(store, type, model);
39
+ }, this);
40
+ },
41
+
42
+ findMany: function(store, type, ids) {
43
+ ids.forEach(function(id) {
44
+ this.find(store, type, id);
45
+ }, this);
46
+ }
47
+ });
48
+ })({});
49
+
50
+
51
+ (function(exports) {
52
+ DS.fixtureAdapter = DS.Adapter.create({
53
+ find: function(store, type, id) {
54
+ var fixtures = type.FIXTURES;
55
+
56
+ ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
57
+ if (fixtures.hasLoaded) { return; }
58
+
59
+ setTimeout(function() {
60
+ store.loadMany(type, fixtures);
61
+ fixtures.hasLoaded = true;
62
+ }, 300);
63
+ },
64
+
65
+ findMany: function() {
66
+ this.find.apply(this, arguments);
67
+ },
68
+
69
+ findAll: function(store, type) {
70
+ var fixtures = type.FIXTURES;
71
+
72
+ ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
73
+
74
+ var ids = fixtures.map(function(item, index, self){ return item.id; });
75
+ store.loadMany(type, ids, fixtures);
76
+ }
77
+
78
+ });
79
+
80
+ })({});
81
+
82
+
83
+ (function(exports) {
84
+ /*global jQuery*/
85
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
86
+
87
+ DS.RESTAdapter = DS.Adapter.extend({
88
+ createRecord: function(store, type, model) {
89
+ var root = this.rootForType(type);
90
+
91
+ var data = {};
92
+ data[root] = get(model, 'data');
93
+
94
+ this.ajax("/" + this.pluralize(root), "POST", {
95
+ data: data,
96
+ success: function(json) {
97
+ store.didCreateRecord(model, json[root]);
98
+ }
99
+ });
100
+ },
101
+
102
+ createRecords: function(store, type, models) {
103
+ if (get(this, 'bulkCommit') === false) {
104
+ return this._super(store, type, models);
105
+ }
106
+
107
+ var root = this.rootForType(type),
108
+ plural = this.pluralize(root);
109
+
110
+ var data = {};
111
+ data[plural] = models.map(function(model) {
112
+ return get(model, 'data');
113
+ });
114
+
115
+ this.ajax("/" + this.pluralize(root), "POST", {
116
+ data: data,
117
+ success: function(json) {
118
+ store.didCreateRecords(type, models, json[plural]);
119
+ }
120
+ });
121
+ },
122
+
123
+ updateRecord: function(store, type, model) {
124
+ var id = get(model, 'id');
125
+ var root = this.rootForType(type);
126
+
127
+ var data = {};
128
+ data[root] = get(model, 'data');
129
+
130
+ var url = ["", this.pluralize(root), id].join("/");
131
+
132
+ this.ajax(url, "PUT", {
133
+ data: data,
134
+ success: function(json) {
135
+ store.didUpdateRecord(model, json[root]);
136
+ }
137
+ });
138
+ },
139
+
140
+ updateRecords: function(store, type, models) {
141
+ if (get(this, 'bulkCommit') === false) {
142
+ return this._super(store, type, models);
143
+ }
144
+
145
+ var root = this.rootForType(type),
146
+ plural = this.pluralize(root);
147
+
148
+ var data = {};
149
+ data[plural] = models.map(function(model) {
150
+ return get(model, 'data');
151
+ });
152
+
153
+ this.ajax("/" + this.pluralize(root), "POST", {
154
+ data: data,
155
+ success: function(json) {
156
+ store.didUpdateRecords(models, json[plural]);
157
+ }
158
+ });
159
+ },
160
+
161
+ deleteRecord: function(store, type, model) {
162
+ var id = get(model, 'id');
163
+ var root = this.rootForType(type);
164
+
165
+ var url = ["", this.pluralize(root), id].join("/");
166
+
167
+ this.ajax(url, "DELETE", {
168
+ success: function(json) {
169
+ store.didDeleteRecord(model);
170
+ }
171
+ });
172
+ },
173
+
174
+ deleteRecords: function(store, type, models) {
175
+ if (get(this, 'bulkCommit') === false) {
176
+ return this._super(store, type, models);
177
+ }
178
+
179
+ var root = this.rootForType(type),
180
+ plural = this.pluralize(root);
181
+
182
+ var data = {};
183
+ data[plural] = models.map(function(model) {
184
+ return get(model, 'id');
185
+ });
186
+
187
+ this.ajax("/" + this.pluralize(root) + "/delete", "POST", {
188
+ data: data,
189
+ success: function(json) {
190
+ store.didDeleteRecords(models);
191
+ }
192
+ });
193
+ },
194
+
195
+ find: function(store, type, id) {
196
+ var root = this.rootForType(type);
197
+
198
+ var url = ["", this.pluralize(root), id].join("/");
199
+
200
+ this.ajax(url, "GET", {
201
+ success: function(json) {
202
+ store.load(type, json[root]);
203
+ }
204
+ });
205
+ },
206
+
207
+ findMany: function(store, type, ids) {
208
+ var root = this.rootForType(type), plural = this.pluralize(root);
209
+
210
+ this.ajax("/" + plural, "GET", {
211
+ data: { ids: ids },
212
+ success: function(json) {
213
+ store.loadMany(type, ids, json[plural]);
214
+ }
215
+ });
216
+ },
217
+
218
+ findAll: function(store, type) {
219
+ var root = this.rootForType(type), plural = this.pluralize(root);
220
+
221
+ this.ajax("/" + plural, "GET", {
222
+ success: function(json) {
223
+ store.loadMany(type, json[plural]);
224
+ }
225
+ });
226
+ },
227
+
228
+ findQuery: function(store, type, query, modelArray) {
229
+ var root = this.rootForType(type), plural = this.pluralize(root);
230
+
231
+ this.ajax("/" + plural, "GET", {
232
+ data: query,
233
+ success: function(json) {
234
+ modelArray.load(json[plural]);
235
+ }
236
+ });
237
+ },
238
+
239
+ // HELPERS
240
+
241
+ plurals: {},
242
+
243
+ // define a plurals hash in your subclass to define
244
+ // special-case pluralization
245
+ pluralize: function(name) {
246
+ return this.plurals[name] || name + "s";
247
+ },
248
+
249
+ rootForType: function(type) {
250
+ if (type.url) { return type.url; }
251
+
252
+ // use the last part of the name as the URL
253
+ var parts = type.toString().split(".");
254
+ var name = parts[parts.length - 1];
255
+ return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
256
+ },
257
+
258
+ ajax: function(url, type, hash) {
259
+ hash.url = url;
260
+ hash.type = type;
261
+ hash.dataType = "json";
262
+
263
+ jQuery.ajax(hash);
264
+ }
265
+ });
266
+
267
+
268
+ })({});
269
+
270
+
271
+ (function(exports) {
272
+ var get = Ember.get, set = Ember.set;
273
+
274
+ /**
275
+ A model array is an array that contains records of a certain type. The model
276
+ array materializes records as needed when they are retrieved for the first
277
+ time. You should not create model arrays yourself. Instead, an instance of
278
+ DS.ModelArray or its subclasses will be returned by your application's store
279
+ in response to queries.
280
+ */
281
+
282
+ DS.ModelArray = Ember.ArrayProxy.extend({
283
+
284
+ /**
285
+ The model type contained by this model array.
286
+
287
+ @type DS.Model
288
+ */
289
+ type: null,
290
+
291
+ // The array of client ids backing the model array. When a
292
+ // record is requested from the model array, the record
293
+ // for the client id at the same index is materialized, if
294
+ // necessary, by the store.
295
+ content: null,
296
+
297
+ // The store that created this model array.
298
+ store: null,
299
+
300
+ // for associations, the model that this association belongs to.
301
+ parentModel: null,
302
+
303
+ init: function() {
304
+ set(this, 'modelCache', Ember.A([]));
305
+ this._super();
306
+ },
307
+
308
+ // Overrides Ember.Array's replace method to implement
309
+ replace: function(index, removed, added) {
310
+ var parentRecord = get(this, 'parentRecord');
311
+ var pendingParent = parentRecord && !get(parentRecord, 'id');
312
+
313
+ added = added.map(function(item) {
314
+ ember_assert("You can only add items of " + (get(this, 'type') && get(this, 'type').toString()) + " to this association.", !get(this, 'type') || (get(this, 'type') === item.constructor));
315
+
316
+ if (pendingParent) { item.send('waitingOn', parentRecord); }
317
+ return item.get('clientId');
318
+ });
319
+
320
+ this._super(index, removed, added);
321
+ },
322
+
323
+ arrayDidChange: function(array, index, removed, added) {
324
+ var modelCache = get(this, 'modelCache');
325
+ modelCache.replace(index, 0, new Array(added));
326
+
327
+ this._super(array, index, removed, added);
328
+ },
329
+
330
+ arrayWillChange: function(array, index, removed, added) {
331
+ this._super(array, index, removed, added);
332
+
333
+ var modelCache = get(this, 'modelCache');
334
+ modelCache.replace(index, removed);
335
+ },
336
+
337
+ objectAtContent: function(index) {
338
+ var modelCache = get(this, 'modelCache');
339
+ var model = modelCache.objectAt(index);
340
+
341
+ if (!model) {
342
+ var store = get(this, 'store');
343
+ var content = get(this, 'content');
344
+
345
+ var contentObject = content.objectAt(index);
346
+
347
+ if (contentObject !== undefined) {
348
+ model = store.findByClientId(get(this, 'type'), contentObject);
349
+ modelCache.replace(index, 1, [model]);
350
+ }
351
+ }
352
+
353
+ return model;
354
+ }
355
+ });
356
+
357
+ })({});
358
+
359
+
360
+ (function(exports) {
361
+ var get = Ember.get;
362
+
363
+ DS.FilteredModelArray = DS.ModelArray.extend({
364
+ filterFunction: null,
365
+
366
+ updateFilter: Ember.observer(function() {
367
+ var store = get(this, 'store');
368
+ store.updateModelArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
369
+ }, 'filterFunction')
370
+ });
371
+
372
+ })({});
373
+
374
+
375
+ (function(exports) {
376
+ var get = Ember.get, set = Ember.set;
377
+
378
+ DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
379
+ query: null,
380
+ isLoaded: false,
381
+
382
+ load: function(array) {
383
+ var store = get(this, 'store'), type = get(this, 'type');
384
+
385
+ var clientIds = store.loadMany(type, array).clientIds;
386
+
387
+ this.beginPropertyChanges();
388
+ set(this, 'content', Ember.A(clientIds));
389
+ set(this, 'isLoaded', true);
390
+ this.endPropertyChanges();
391
+ }
392
+ });
393
+
394
+
395
+ })({});
396
+
397
+
398
+ (function(exports) {
399
+ })({});
400
+
401
+
402
+ (function(exports) {
403
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
404
+
405
+ var OrderedSet = Ember.Object.extend({
406
+ init: function() {
407
+ this.clear();
408
+ },
409
+
410
+ clear: function() {
411
+ this.set('presenceSet', {});
412
+ this.set('list', Ember.NativeArray.apply([]));
413
+ },
414
+
415
+ add: function(obj) {
416
+ var guid = Ember.guidFor(obj),
417
+ presenceSet = get(this, 'presenceSet'),
418
+ list = get(this, 'list');
419
+
420
+ if (guid in presenceSet) { return; }
421
+
422
+ presenceSet[guid] = true;
423
+ list.pushObject(obj);
424
+ },
425
+
426
+ remove: function(obj) {
427
+ var guid = Ember.guidFor(obj),
428
+ presenceSet = get(this, 'presenceSet'),
429
+ list = get(this, 'list');
430
+
431
+ delete presenceSet[guid];
432
+ list.removeObject(obj);
433
+ },
434
+
435
+ isEmpty: function() {
436
+ return getPath(this, 'list.length') === 0;
437
+ },
438
+
439
+ forEach: function(fn, self) {
440
+ // allow mutation during iteration
441
+ get(this, 'list').slice().forEach(function(item) {
442
+ fn.call(self, item);
443
+ });
444
+ },
445
+
446
+ toArray: function() {
447
+ return get(this, 'list').slice();
448
+ }
449
+ });
450
+
451
+ /**
452
+ A Hash stores values indexed by keys. Unlike JavaScript's
453
+ default Objects, the keys of a Hash can be any JavaScript
454
+ object.
455
+
456
+ Internally, a Hash has two data structures:
457
+
458
+ `keys`: an OrderedSet of all of the existing keys
459
+ `values`: a JavaScript Object indexed by the
460
+ Ember.guidFor(key)
461
+
462
+ When a key/value pair is added for the first time, we
463
+ add the key to the `keys` OrderedSet, and create or
464
+ replace an entry in `values`. When an entry is deleted,
465
+ we delete its entry in `keys` and `values`.
466
+ */
467
+
468
+ var Hash = Ember.Object.extend({
469
+ init: function() {
470
+ set(this, 'keys', OrderedSet.create());
471
+ set(this, 'values', {});
472
+ },
473
+
474
+ add: function(key, value) {
475
+ var keys = get(this, 'keys'), values = get(this, 'values');
476
+ var guid = Ember.guidFor(key);
477
+
478
+ keys.add(key);
479
+ values[guid] = value;
480
+
481
+ return value;
482
+ },
483
+
484
+ remove: function(key) {
485
+ var keys = get(this, 'keys'), values = get(this, 'values');
486
+ var guid = Ember.guidFor(key), value;
487
+
488
+ keys.remove(key);
489
+
490
+ value = values[guid];
491
+ delete values[guid];
492
+
493
+ return value;
494
+ },
495
+
496
+ fetch: function(key) {
497
+ var values = get(this, 'values');
498
+ var guid = Ember.guidFor(key);
499
+
500
+ return values[guid];
501
+ },
502
+
503
+ forEach: function(fn, binding) {
504
+ var keys = get(this, 'keys'),
505
+ values = get(this, 'values');
506
+
507
+ keys.forEach(function(key) {
508
+ var guid = Ember.guidFor(key);
509
+ fn.call(binding, key, values[guid]);
510
+ });
511
+ }
512
+ });
513
+
514
+ DS.Transaction = Ember.Object.extend({
515
+ init: function() {
516
+ set(this, 'dirty', {
517
+ created: Hash.create(),
518
+ updated: Hash.create(),
519
+ deleted: Hash.create()
520
+ });
521
+ },
522
+
523
+ createRecord: function(type, hash) {
524
+ var store = get(this, 'store');
525
+
526
+ return store.createRecord(type, hash, this);
527
+ },
528
+
529
+ add: function(model) {
530
+ var modelTransaction = get(model, 'transaction');
531
+ ember_assert("Models cannot belong to more than one transaction at a time.", !modelTransaction);
532
+
533
+ set(model, 'transaction', this);
534
+ },
535
+
536
+ modelBecameDirty: function(kind, model) {
537
+ var dirty = get(get(this, 'dirty'), kind),
538
+ type = model.constructor;
539
+
540
+ var models = dirty.fetch(type);
541
+
542
+ models = models || dirty.add(type, OrderedSet.create());
543
+ models.add(model);
544
+ },
545
+
546
+ modelBecameClean: function(kind, model) {
547
+ var dirty = get(get(this, 'dirty'), kind),
548
+ type = model.constructor;
549
+
550
+ var models = dirty.fetch(type);
551
+ models.remove(model);
552
+
553
+ set(model, 'transaction', null);
554
+ },
555
+
556
+ commit: function() {
557
+ var dirtyMap = get(this, 'dirty');
558
+
559
+ var iterate = function(kind, fn, binding) {
560
+ var dirty = get(dirtyMap, kind);
561
+
562
+ dirty.forEach(function(type, models) {
563
+ if (models.isEmpty()) { return; }
564
+
565
+ var array = [];
566
+
567
+ models.forEach(function(model) {
568
+ model.send('willCommit');
569
+
570
+ if (get(model, 'isPending') === false) {
571
+ array.push(model);
572
+ }
573
+ });
574
+
575
+ fn.call(binding, type, array);
576
+ });
577
+ };
578
+
579
+ var commitDetails = {
580
+ updated: {
581
+ eachType: function(fn, binding) { iterate('updated', fn, binding); }
582
+ },
583
+
584
+ created: {
585
+ eachType: function(fn, binding) { iterate('created', fn, binding); }
586
+ },
587
+
588
+ deleted: {
589
+ eachType: function(fn, binding) { iterate('deleted', fn, binding); }
590
+ }
591
+ };
592
+
593
+ var store = get(this, 'store');
594
+ var adapter = get(store, '_adapter');
595
+ if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
596
+ else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
597
+ }
598
+ });
599
+
600
+ })({});
601
+
602
+
603
+ (function(exports) {
604
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
605
+
606
+ var OrderedSet = Ember.Object.extend({
607
+ init: function() {
608
+ this.clear();
609
+ },
610
+
611
+ clear: function() {
612
+ this.set('presenceSet', {});
613
+ this.set('list', Ember.NativeArray.apply([]));
614
+ },
615
+
616
+ add: function(obj) {
617
+ var guid = Ember.guidFor(obj),
618
+ presenceSet = get(this, 'presenceSet'),
619
+ list = get(this, 'list');
620
+
621
+ if (guid in presenceSet) { return; }
622
+
623
+ presenceSet[guid] = true;
624
+ list.pushObject(obj);
625
+ },
626
+
627
+ remove: function(obj) {
628
+ var guid = Ember.guidFor(obj),
629
+ presenceSet = get(this, 'presenceSet'),
630
+ list = get(this, 'list');
631
+
632
+ delete presenceSet[guid];
633
+ list.removeObject(obj);
634
+ },
635
+
636
+ isEmpty: function() {
637
+ return getPath(this, 'list.length') === 0;
638
+ },
639
+
640
+ forEach: function(fn, self) {
641
+ get(this, 'list').forEach(function(item) {
642
+ fn.call(self, item);
643
+ });
644
+ }
645
+ });
646
+
647
+ // Implementors Note:
648
+ //
649
+ // The variables in this file are consistently named according to the following
650
+ // scheme:
651
+ //
652
+ // * +id+ means an identifier managed by an external source, provided inside the
653
+ // data hash provided by that source.
654
+ // * +clientId+ means a transient numerical identifier generated at runtime by
655
+ // the data store. It is important primarily because newly created objects may
656
+ // not yet have an externally generated id.
657
+ // * +type+ means a subclass of DS.Model.
658
+
659
+ /**
660
+ The store contains all of the hashes for data models loaded from the server.
661
+ It is also responsible for creating instances of DS.Model when you request one
662
+ of these data hashes, so that they can be bound to in your Handlebars templates.
663
+
664
+ Create a new store like this:
665
+
666
+ MyApp.store = DS.Store.create();
667
+
668
+ You can retrieve DS.Model instances from the store in several ways. To retrieve
669
+ a model for a specific id, use the `find()` method:
670
+
671
+ var model = MyApp.store.find(MyApp.Contact, 123);
672
+
673
+ By default, the store will talk to your backend using a standard REST mechanism.
674
+ You can customize how the store talks to your backend by specifying a custom adapter:
675
+
676
+ MyApp.store = DS.Store.create({
677
+ adapter: 'MyApp.CustomAdapter'
678
+ });
679
+
680
+ You can learn more about writing a custom adapter by reading the `DS.Adapter`
681
+ documentation.
682
+ */
683
+ DS.Store = Ember.Object.extend({
684
+
685
+ /**
686
+ Many methods can be invoked without specifying which store should be used.
687
+ In those cases, the first store created will be used as the default. If
688
+ an application has multiple stores, it should specify which store to use
689
+ when performing actions, such as finding records by id.
690
+
691
+ The init method registers this store as the default if none is specified.
692
+ */
693
+ init: function() {
694
+ if (!get(DS, 'defaultStore') || get(this, 'isDefaultStore')) {
695
+ set(DS, 'defaultStore', this);
696
+ }
697
+
698
+ set(this, 'data', []);
699
+ set(this, '_typeMap', {});
700
+ set(this, 'models', []);
701
+ set(this, 'modelArrays', []);
702
+ set(this, 'modelArraysByClientId', {});
703
+ set(this, 'defaultTransaction', DS.Transaction.create({ store: this }));
704
+
705
+ return this._super();
706
+ },
707
+
708
+ transaction: function() {
709
+ return DS.Transaction.create({ store: this });
710
+ },
711
+
712
+ modelArraysForClientId: function(clientId) {
713
+ var modelArrays = get(this, 'modelArraysByClientId');
714
+ var ret = modelArrays[clientId];
715
+
716
+ if (!ret) {
717
+ ret = modelArrays[clientId] = OrderedSet.create();
718
+ }
719
+
720
+ return ret;
721
+ },
722
+
723
+ /**
724
+ The adapter to use to communicate to a backend server or other persistence layer.
725
+
726
+ This can be specified as an instance, a class, or a property path that specifies
727
+ where the adapter can be located.
728
+
729
+ @property {DS.Adapter|String}
730
+ */
731
+ adapter: null,
732
+
733
+ _adapter: Ember.computed(function() {
734
+ var adapter = get(this, 'adapter');
735
+ if (typeof adapter === 'string') {
736
+ return getPath(this, adapter, false) || getPath(window, adapter);
737
+ }
738
+ return adapter;
739
+ }).property('adapter').cacheable(),
740
+
741
+ clientIdCounter: -1,
742
+
743
+ // ....................
744
+ // . CREATE NEW MODEL .
745
+ // ....................
746
+
747
+ createRecord: function(type, properties, transaction) {
748
+ properties = properties || {};
749
+
750
+ var id = properties[getPath(type, 'proto.primaryKey')] || null;
751
+
752
+ var model = type._create({
753
+ store: this,
754
+ transaction: transaction
755
+ });
756
+
757
+ var hash = {}, clientId;
758
+
759
+ clientId = this.pushHash(hash, id, type);
760
+ model.send('setData', hash);
761
+
762
+ var models = get(this, 'models');
763
+
764
+ set(model, 'clientId', clientId);
765
+ models[clientId] = model;
766
+
767
+ model.setProperties(properties);
768
+ this.updateModelArrays(type, clientId, hash);
769
+
770
+ return model;
771
+ },
772
+
773
+ // ................
774
+ // . DELETE MODEL .
775
+ // ................
776
+
777
+ deleteRecord: function(model) {
778
+ model.send('deleteRecord');
779
+ },
780
+
781
+ // ...............
782
+ // . FIND MODELS .
783
+ // ...............
784
+
785
+ /**
786
+ Finds a model by its id. If the data for that model has already been
787
+ loaded, an instance of DS.Model with that data will be returned
788
+ immediately. Otherwise, an empty DS.Model instance will be returned in
789
+ the loading state. As soon as the requested data is available, the model
790
+ will be moved into the loaded state and all of the information will be
791
+ available.
792
+
793
+ Note that only one DS.Model instance is ever created per unique id for a
794
+ given type.
795
+
796
+ Example:
797
+
798
+ var record = MyApp.store.find(MyApp.Person, 1234);
799
+
800
+ @param {DS.Model} type
801
+ @param {String|Number} id
802
+ */
803
+ find: function(type, id, query) {
804
+ if (id === undefined) {
805
+ return this.findAll(type);
806
+ }
807
+
808
+ if (query !== undefined) {
809
+ return this.findMany(type, id, query);
810
+ } else if (Ember.typeOf(id) === 'object') {
811
+ return this.findQuery(type, id);
812
+ }
813
+
814
+ if (Ember.isArray(id)) {
815
+ return this.findMany(type, id);
816
+ }
817
+
818
+ var clientId = this.clientIdForId(type, id);
819
+
820
+ return this.findByClientId(type, clientId, id);
821
+ },
822
+
823
+ findByClientId: function(type, clientId, id) {
824
+ var model;
825
+
826
+ var models = get(this, 'models');
827
+ var data = this.clientIdToHashMap(type);
828
+
829
+ // If there is already a clientId assigned for this
830
+ // type/id combination, try to find an existing
831
+ // model for that id and return. Otherwise,
832
+ // materialize a new model and set its data to the
833
+ // value we already have.
834
+ if (clientId !== undefined) {
835
+ model = models[clientId];
836
+
837
+ if (!model) {
838
+ // create a new instance of the model in the
839
+ // 'isLoading' state
840
+ model = this.materializeRecord(type, clientId);
841
+
842
+ // immediately set its data
843
+ model.send('setData', data[clientId] || null);
844
+ }
845
+ } else {
846
+ clientId = this.pushHash(null, id, type);
847
+
848
+ // create a new instance of the model in the
849
+ // 'isLoading' state
850
+ model = this.materializeRecord(type, clientId);
851
+
852
+ // let the adapter set the data, possibly async
853
+ var adapter = get(this, '_adapter');
854
+ if (adapter && adapter.find) { adapter.find(this, type, id); }
855
+ else { throw fmt("Adapter is either null or does not implement `find` method", this); }
856
+ }
857
+
858
+ return model;
859
+ },
860
+
861
+ /** @private
862
+ */
863
+ findMany: function(type, ids, query) {
864
+ var idToClientIdMap = this.idToClientIdMap(type);
865
+ var data = this.clientIdToHashMap(type), needed;
866
+
867
+ var clientIds = Ember.A([]);
868
+
869
+ if (ids) {
870
+ needed = [];
871
+
872
+ ids.forEach(function(id) {
873
+ var clientId = idToClientIdMap[id];
874
+ if (clientId === undefined || data[clientId] === undefined) {
875
+ clientId = this.pushHash(null, id, type);
876
+ needed.push(id);
877
+ }
878
+
879
+ clientIds.push(clientId);
880
+ }, this);
881
+ } else {
882
+ needed = null;
883
+ }
884
+
885
+ if ((needed && get(needed, 'length') > 0) || query) {
886
+ var adapter = get(this, '_adapter');
887
+ if (adapter && adapter.findMany) { adapter.findMany(this, type, needed, query); }
888
+ else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }
889
+ }
890
+
891
+ return this.createModelArray(type, clientIds);
892
+ },
893
+
894
+ findQuery: function(type, query) {
895
+ var array = DS.AdapterPopulatedModelArray.create({ type: type, content: Ember.A([]), store: this });
896
+ var adapter = get(this, '_adapter');
897
+ if (adapter && adapter.findQuery) { adapter.findQuery(this, type, query, array); }
898
+ else { throw fmt("Adapter is either null or does not implement `findQuery` method", this); }
899
+ return array;
900
+ },
901
+
902
+ findAll: function(type) {
903
+
904
+ var typeMap = this.typeMapFor(type),
905
+ findAllCache = typeMap.findAllCache;
906
+
907
+ if (findAllCache) { return findAllCache; }
908
+
909
+ var array = DS.ModelArray.create({ type: type, content: Ember.A([]), store: this });
910
+ this.registerModelArray(array, type);
911
+
912
+ var adapter = get(this, '_adapter');
913
+ if (adapter && adapter.findAll) { adapter.findAll(this, type); }
914
+
915
+ typeMap.findAllCache = array;
916
+ return array;
917
+ },
918
+
919
+ filter: function(type, filter) {
920
+ var array = DS.FilteredModelArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
921
+
922
+ this.registerModelArray(array, type, filter);
923
+
924
+ return array;
925
+ },
926
+
927
+ // ............
928
+ // . UPDATING .
929
+ // ............
930
+
931
+ hashWasUpdated: function(type, clientId) {
932
+ var clientIdToHashMap = this.clientIdToHashMap(type);
933
+ var hash = clientIdToHashMap[clientId];
934
+
935
+ this.updateModelArrays(type, clientId, hash);
936
+ },
937
+
938
+ // ..............
939
+ // . PERSISTING .
940
+ // ..............
941
+
942
+ commit: function() {
943
+ get(this, 'defaultTransaction').commit();
944
+ },
945
+
946
+ didUpdateRecords: function(array, hashes) {
947
+ if (arguments.length === 2) {
948
+ array.forEach(function(model, idx) {
949
+ this.didUpdateRecord(model, hashes[idx]);
950
+ }, this);
951
+ } else {
952
+ array.forEach(function(model) {
953
+ this.didUpdateRecord(model);
954
+ }, this);
955
+ }
956
+ },
957
+
958
+ didUpdateRecord: function(model, hash) {
959
+ if (arguments.length === 2) {
960
+ var clientId = get(model, 'clientId');
961
+ var data = this.clientIdToHashMap(model.constructor);
962
+
963
+ data[clientId] = hash;
964
+ model.send('setData', hash);
965
+ }
966
+
967
+ model.send('didCommit');
968
+ },
969
+
970
+ didDeleteRecords: function(array) {
971
+ array.forEach(function(model) {
972
+ model.send('didCommit');
973
+ });
974
+ },
975
+
976
+ didDeleteRecord: function(model) {
977
+ model.send('didCommit');
978
+ },
979
+
980
+ didCreateRecords: function(type, array, hashes) {
981
+ var id, clientId, primaryKey = getPath(type, 'proto.primaryKey');
982
+
983
+ var idToClientIdMap = this.idToClientIdMap(type);
984
+ var data = this.clientIdToHashMap(type);
985
+ var idList = this.idList(type);
986
+
987
+ for (var i=0, l=get(array, 'length'); i<l; i++) {
988
+ var model = array[i], hash = hashes[i];
989
+ id = hash[primaryKey];
990
+ clientId = get(model, 'clientId');
991
+
992
+ data[clientId] = hash;
993
+ model.send('setData', hash);
994
+
995
+ idToClientIdMap[id] = clientId;
996
+ idList.push(id);
997
+
998
+ model.send('didCommit');
999
+ }
1000
+ },
1001
+
1002
+ didCreateRecord: function(model, hash) {
1003
+ var type = model.constructor;
1004
+
1005
+ var id, clientId, primaryKey;
1006
+
1007
+ var idToClientIdMap = this.idToClientIdMap(type);
1008
+ var data = this.clientIdToHashMap(type);
1009
+ var idList = this.idList(type);
1010
+
1011
+ // The hash is optional, but if it is not provided, the client must have
1012
+ // provided a primary key.
1013
+
1014
+ primaryKey = getPath(type, 'proto.primaryKey');
1015
+
1016
+ // TODO: Make ember_assert more flexible and convert this into an ember_assert
1017
+ if (hash) {
1018
+ ember_assert("The server must provide a primary key: " + primaryKey, get(hash, primaryKey));
1019
+ } else {
1020
+ ember_assert("The server did not return data, and you did not create a primary key (" + primaryKey + ") on the client", get(get(model, 'data'), primaryKey));
1021
+ }
1022
+
1023
+ // If a hash was provided, index it under the model's client ID
1024
+ // and update the model.
1025
+ if (arguments.length === 2) {
1026
+ id = hash[primaryKey];
1027
+
1028
+ data[clientId] = hash;
1029
+ set(model, 'data', hash);
1030
+ }
1031
+
1032
+ clientId = get(model, 'clientId');
1033
+
1034
+ idToClientIdMap[id] = clientId;
1035
+ idList.push(id);
1036
+
1037
+ model.send('didCommit');
1038
+ },
1039
+
1040
+ recordWasInvalid: function(record, errors) {
1041
+ record.send('becameInvalid', errors);
1042
+ },
1043
+
1044
+ // ................
1045
+ // . MODEL ARRAYS .
1046
+ // ................
1047
+
1048
+ registerModelArray: function(array, type, filter) {
1049
+ var modelArrays = get(this, 'modelArrays');
1050
+
1051
+ modelArrays.push(array);
1052
+
1053
+ this.updateModelArrayFilter(array, type, filter);
1054
+ },
1055
+
1056
+ createModelArray: function(type, clientIds) {
1057
+ var array = DS.ModelArray.create({ type: type, content: clientIds, store: this });
1058
+
1059
+ clientIds.forEach(function(clientId) {
1060
+ var modelArrays = this.modelArraysForClientId(clientId);
1061
+ modelArrays.add(array);
1062
+ }, this);
1063
+
1064
+ return array;
1065
+ },
1066
+
1067
+ updateModelArrayFilter: function(array, type, filter) {
1068
+ var data = this.clientIdToHashMap(type);
1069
+ var allClientIds = this.clientIdList(type), clientId, hash;
1070
+
1071
+ for (var i=0, l=allClientIds.length; i<l; i++) {
1072
+ clientId = allClientIds[i];
1073
+
1074
+ hash = data[clientId];
1075
+
1076
+ if (hash) {
1077
+ this.updateModelArray(array, filter, type, clientId, hash);
1078
+ }
1079
+ }
1080
+ },
1081
+
1082
+ updateModelArrays: function(type, clientId, hash) {
1083
+ var modelArrays = get(this, 'modelArrays'),
1084
+ modelArrayType, filter;
1085
+
1086
+ modelArrays.forEach(function(array) {
1087
+ modelArrayType = get(array, 'type');
1088
+ filter = get(array, 'filterFunction');
1089
+
1090
+ if (type !== modelArrayType) { return; }
1091
+
1092
+ this.updateModelArray(array, filter, type, clientId, hash);
1093
+ }, this);
1094
+ },
1095
+
1096
+ updateModelArray: function(array, filter, type, clientId, hash) {
1097
+ var shouldBeInArray;
1098
+
1099
+ if (!filter) {
1100
+ shouldBeInArray = true;
1101
+ } else {
1102
+ shouldBeInArray = filter(hash);
1103
+ }
1104
+
1105
+ var content = get(array, 'content');
1106
+ var alreadyInArray = content.indexOf(clientId) !== -1;
1107
+
1108
+ var modelArrays = this.modelArraysForClientId(clientId);
1109
+
1110
+ if (shouldBeInArray && !alreadyInArray) {
1111
+ modelArrays.add(array);
1112
+ content.pushObject(clientId);
1113
+ } else if (!shouldBeInArray && alreadyInArray) {
1114
+ modelArrays.remove(array);
1115
+ content.removeObject(clientId);
1116
+ }
1117
+ },
1118
+
1119
+ removeFromModelArrays: function(model) {
1120
+ var clientId = get(model, 'clientId');
1121
+ var modelArrays = this.modelArraysForClientId(clientId);
1122
+
1123
+ modelArrays.forEach(function(array) {
1124
+ var content = get(array, 'content');
1125
+ content.removeObject(clientId);
1126
+ });
1127
+ },
1128
+
1129
+ // ............
1130
+ // . TYPE MAP .
1131
+ // ............
1132
+
1133
+ typeMapFor: function(type) {
1134
+ var ids = get(this, '_typeMap');
1135
+ var guidForType = Ember.guidFor(type);
1136
+
1137
+ var typeMap = ids[guidForType];
1138
+
1139
+ if (typeMap) {
1140
+ return typeMap;
1141
+ } else {
1142
+ return (ids[guidForType] =
1143
+ {
1144
+ idToCid: {},
1145
+ idList: [],
1146
+ cidList: [],
1147
+ cidToHash: {}
1148
+ });
1149
+ }
1150
+ },
1151
+
1152
+ idToClientIdMap: function(type) {
1153
+ return this.typeMapFor(type).idToCid;
1154
+ },
1155
+
1156
+ idList: function(type) {
1157
+ return this.typeMapFor(type).idList;
1158
+ },
1159
+
1160
+ clientIdList: function(type) {
1161
+ return this.typeMapFor(type).cidList;
1162
+ },
1163
+
1164
+ clientIdToHashMap: function(type) {
1165
+ return this.typeMapFor(type).cidToHash;
1166
+ },
1167
+
1168
+ /** @private
1169
+
1170
+ For a given type and id combination, returns the client id used by the store.
1171
+ If no client id has been assigned yet, `undefined` is returned.
1172
+
1173
+ @param {DS.Model} type
1174
+ @param {String|Number} id
1175
+ */
1176
+ clientIdForId: function(type, id) {
1177
+ return this.typeMapFor(type).idToCid[id];
1178
+ },
1179
+
1180
+ idForHash: function(type, hash) {
1181
+ var primaryKey = getPath(type, 'proto.primaryKey');
1182
+
1183
+ ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
1184
+ return hash[primaryKey];
1185
+ },
1186
+
1187
+ // ................
1188
+ // . LOADING DATA .
1189
+ // ................
1190
+
1191
+ /**
1192
+ Load a new data hash into the store for a given id and type combination.
1193
+ If data for that model had been loaded previously, the new information
1194
+ overwrites the old.
1195
+
1196
+ If the model you are loading data for has outstanding changes that have not
1197
+ yet been saved, an exception will be thrown.
1198
+
1199
+ @param {DS.Model} type
1200
+ @param {String|Number} id
1201
+ @param {Object} hash the data hash to load
1202
+ */
1203
+ load: function(type, id, hash) {
1204
+ if (hash === undefined) {
1205
+ hash = id;
1206
+ var primaryKey = getPath(type, 'proto.primaryKey');
1207
+ ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
1208
+ id = hash[primaryKey];
1209
+ }
1210
+
1211
+ var data = this.clientIdToHashMap(type);
1212
+ var models = get(this, 'models');
1213
+
1214
+ var clientId = this.clientIdForId(type, id);
1215
+
1216
+ if (clientId !== undefined) {
1217
+ data[clientId] = hash;
1218
+
1219
+ var model = models[clientId];
1220
+ if (model) {
1221
+ model.send('setData', hash);
1222
+ }
1223
+ } else {
1224
+ clientId = this.pushHash(hash, id, type);
1225
+ }
1226
+
1227
+ this.updateModelArrays(type, clientId, hash);
1228
+
1229
+ return { id: id, clientId: clientId };
1230
+ },
1231
+
1232
+ loadMany: function(type, ids, hashes) {
1233
+ var clientIds = Ember.A([]);
1234
+
1235
+ if (hashes === undefined) {
1236
+ hashes = ids;
1237
+ ids = [];
1238
+ var primaryKey = getPath(type, 'proto.primaryKey');
1239
+
1240
+ ids = hashes.map(function(hash) {
1241
+ ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
1242
+ return hash[primaryKey];
1243
+ });
1244
+ }
1245
+
1246
+ for (var i=0, l=get(ids, 'length'); i<l; i++) {
1247
+ var loaded = this.load(type, ids[i], hashes[i]);
1248
+ clientIds.pushObject(loaded.clientId);
1249
+ }
1250
+
1251
+ return { clientIds: clientIds, ids: ids };
1252
+ },
1253
+
1254
+ /** @private
1255
+
1256
+ Stores a data hash for the specified type and id combination and returns
1257
+ the client id.
1258
+
1259
+ @param {Object} hash
1260
+ @param {String|Number} id
1261
+ @param {DS.Model} type
1262
+ @returns {Number}
1263
+ */
1264
+ pushHash: function(hash, id, type) {
1265
+ var idToClientIdMap = this.idToClientIdMap(type);
1266
+ var clientIdList = this.clientIdList(type);
1267
+ var idList = this.idList(type);
1268
+ var data = this.clientIdToHashMap(type);
1269
+
1270
+ var clientId = this.incrementProperty('clientIdCounter');
1271
+
1272
+ data[clientId] = hash;
1273
+
1274
+ // if we're creating an item, this process will be done
1275
+ // later, once the object has been persisted.
1276
+ if (id) {
1277
+ idToClientIdMap[id] = clientId;
1278
+ idList.push(id);
1279
+ }
1280
+
1281
+ clientIdList.push(clientId);
1282
+
1283
+ return clientId;
1284
+ },
1285
+
1286
+ // .........................
1287
+ // . MODEL MATERIALIZATION .
1288
+ // .........................
1289
+
1290
+ materializeRecord: function(type, clientId) {
1291
+ var model;
1292
+
1293
+ get(this, 'models')[clientId] = model = type._create({ store: this, clientId: clientId });
1294
+ set(model, 'clientId', clientId);
1295
+ model.send('loadingData');
1296
+ return model;
1297
+ },
1298
+
1299
+ destroy: function() {
1300
+ if (get(DS, 'defaultStore') === this) {
1301
+ set(DS, 'defaultStore', null);
1302
+ }
1303
+
1304
+ return this._super();
1305
+ }
1306
+ });
1307
+
1308
+
1309
+ })({});
1310
+
1311
+
1312
+ (function(exports) {
1313
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath, guidFor = Ember.guidFor;
1314
+
1315
+ var stateProperty = Ember.computed(function(key) {
1316
+ var parent = get(this, 'parentState');
1317
+ if (parent) {
1318
+ return get(parent, key);
1319
+ }
1320
+ }).property();
1321
+
1322
+ var isEmptyObject = function(object) {
1323
+ for (var name in object) {
1324
+ if (object.hasOwnProperty(name)) { return false; }
1325
+ }
1326
+
1327
+ return true;
1328
+ };
1329
+
1330
+ DS.State = Ember.State.extend({
1331
+ isLoaded: stateProperty,
1332
+ isDirty: stateProperty,
1333
+ isSaving: stateProperty,
1334
+ isDeleted: stateProperty,
1335
+ isError: stateProperty,
1336
+ isNew: stateProperty,
1337
+ isValid: stateProperty,
1338
+ isPending: stateProperty,
1339
+
1340
+ // For states that are substates of a
1341
+ // DirtyState (updated or created), it is
1342
+ // useful to be able to determine which
1343
+ // type of dirty state it is.
1344
+ dirtyType: stateProperty
1345
+ });
1346
+
1347
+ var isEmptyObject = function(obj) {
1348
+ for (var prop in obj) {
1349
+ if (!obj.hasOwnProperty(prop)) { continue; }
1350
+ return false;
1351
+ }
1352
+
1353
+ return true;
1354
+ };
1355
+
1356
+ var setProperty = function(manager, context) {
1357
+ var key = context.key, value = context.value;
1358
+
1359
+ var model = get(manager, 'model'),
1360
+ data = get(model, 'data');
1361
+
1362
+ data[key] = value;
1363
+
1364
+ // At the end of the run loop, notify model arrays that
1365
+ // this record has changed so they can re-evaluate its contents
1366
+ // to determine membership.
1367
+ Ember.run.once(model, model.notifyHashWasUpdated);
1368
+ };
1369
+
1370
+ // The waitingOn event shares common functionality
1371
+ // between the different dirty states, but each is
1372
+ // treated slightly differently. This method is exposed
1373
+ // so that each implementation can invoke the common
1374
+ // behavior, and then implement the behavior specific
1375
+ // to the state.
1376
+ var waitingOn = function(manager, object) {
1377
+ var model = get(manager, 'model'),
1378
+ pendingQueue = get(model, 'pendingQueue'),
1379
+ objectGuid = guidFor(object);
1380
+
1381
+ var observer = function() {
1382
+ if (get(object, 'id')) {
1383
+ manager.send('doneWaitingOn', object);
1384
+ Ember.removeObserver(object, 'id', observer);
1385
+ }
1386
+ };
1387
+
1388
+ pendingQueue[objectGuid] = [object, observer];
1389
+ Ember.addObserver(object, 'id', observer);
1390
+ };
1391
+
1392
+ // Implementation notes:
1393
+ //
1394
+ // Each state has a boolean value for all of the following flags:
1395
+ //
1396
+ // * isLoaded: The record has a populated `data` property. When a
1397
+ // record is loaded via `store.find`, `isLoaded` is false
1398
+ // until the adapter sets it. When a record is created locally,
1399
+ // its `isLoaded` property is always true.
1400
+ // * isDirty: The record has local changes that have not yet been
1401
+ // saved by the adapter. This includes records that have been
1402
+ // created (but not yet saved) or deleted.
1403
+ // * isSaving: The record's transaction has been committed, but
1404
+ // the adapter has not yet acknowledged that the changes have
1405
+ // been persisted to the backend.
1406
+ // * isDeleted: The record was marked for deletion. When `isDeleted`
1407
+ // is true and `isDirty` is true, the record is deleted locally
1408
+ // but the deletion was not yet persisted. When `isSaving` is
1409
+ // true, the change is in-flight. When both `isDirty` and
1410
+ // `isSaving` are false, the change has persisted.
1411
+ // * isError: The adapter reported that it was unable to save
1412
+ // local changes to the backend. This may also result in the
1413
+ // record having its `isValid` property become false if the
1414
+ // adapter reported that server-side validations failed.
1415
+ // * isNew: The record was created on the client and the adapter
1416
+ // did not yet report that it was successfully saved.
1417
+ // * isValid: No client-side validations have failed and the
1418
+ // adapter did not report any server-side validation failures.
1419
+ // * isPending: A record `isPending` when it belongs to an
1420
+ // association on another record and that record has not been
1421
+ // saved. A record in this state cannot be saved because it
1422
+ // lacks a "foreign key" that will be supplied by its parent
1423
+ // association when the parent record has been created. When
1424
+ // the adapter reports that the parent has saved, the
1425
+ // `isPending` property on all children will become `false`
1426
+ // and the transaction will try to commit the records.
1427
+
1428
+
1429
+ // The dirty state is a abstract state whose functionality is
1430
+ // shared between the `created` and `updated` states.
1431
+ //
1432
+ // The deleted state shares the `isDirty` flag with the
1433
+ // subclasses of `DirtyState`, but with a very different
1434
+ // implementation.
1435
+ var DirtyState = DS.State.extend({
1436
+ initialState: 'uncommitted',
1437
+
1438
+ // FLAGS
1439
+ isDirty: true,
1440
+
1441
+ // SUBSTATES
1442
+
1443
+ // When a record first becomes dirty, it is `uncommitted`.
1444
+ // This means that there are local pending changes,
1445
+ // but they have not yet begun to be saved.
1446
+ uncommitted: DS.State.extend({
1447
+ // TRANSITIONS
1448
+ enter: function(manager) {
1449
+ var dirtyType = get(this, 'dirtyType'),
1450
+ model = get(manager, 'model');
1451
+
1452
+ model.withTransaction(function (t) {
1453
+ t.modelBecameDirty(dirtyType, model);
1454
+ });
1455
+ },
1456
+
1457
+ exit: function(manager) {
1458
+ var model = get(manager, 'model');
1459
+ manager.send('invokeLifecycleCallbacks', model);
1460
+ },
1461
+
1462
+ // EVENTS
1463
+ setProperty: setProperty,
1464
+
1465
+ deleteRecord: function(manager) {
1466
+ manager.goToState('deleted');
1467
+ },
1468
+
1469
+ waitingOn: function(manager, object) {
1470
+ waitingOn(manager, object);
1471
+ manager.goToState('pending');
1472
+ },
1473
+
1474
+ willCommit: function(manager) {
1475
+ manager.goToState('inFlight');
1476
+ }
1477
+ }),
1478
+
1479
+ // Once a record has been handed off to the adapter to be
1480
+ // saved, it is in the 'in flight' state. Changes to the
1481
+ // record cannot be made during this window.
1482
+ inFlight: DS.State.extend({
1483
+ // FLAGS
1484
+ isSaving: true,
1485
+
1486
+ // TRANSITIONS
1487
+ enter: function(manager) {
1488
+ var dirtyType = get(this, 'dirtyType'),
1489
+ model = get(manager, 'model');
1490
+
1491
+ model.withTransaction(function (t) {
1492
+ t.modelBecameClean(dirtyType, model);
1493
+ });
1494
+ },
1495
+
1496
+ // EVENTS
1497
+ didCommit: function(manager) {
1498
+ manager.goToState('loaded');
1499
+ },
1500
+
1501
+ becameInvalid: function(manager, errors) {
1502
+ var model = get(manager, 'model');
1503
+
1504
+ set(model, 'errors', errors);
1505
+ manager.goToState('invalid');
1506
+ },
1507
+
1508
+ setData: function(manager, hash) {
1509
+ var model = get(manager, 'model');
1510
+ set(model, 'data', hash);
1511
+ }
1512
+ }),
1513
+
1514
+ // If a record becomes associated with a newly created
1515
+ // parent record, it will be `pending` until the parent
1516
+ // record has successfully persisted. Once this happens,
1517
+ // this record can use the parent's primary key as its
1518
+ // foreign key.
1519
+ //
1520
+ // If the record's transaction had already started to
1521
+ // commit, the record will transition to the `inFlight`
1522
+ // state. If it had not, the record will transition to
1523
+ // the `uncommitted` state.
1524
+ pending: DS.State.extend({
1525
+ initialState: 'uncommitted',
1526
+
1527
+ // FLAGS
1528
+ isPending: true,
1529
+
1530
+ // SUBSTATES
1531
+
1532
+ // A pending record whose transaction has not yet
1533
+ // started to commit is in this state.
1534
+ uncommitted: DS.State.extend({
1535
+ // EVENTS
1536
+ setProperty: setProperty,
1537
+
1538
+ deleteRecord: function(manager) {
1539
+ var model = get(manager, 'model'),
1540
+ pendingQueue = get(model, 'pendingQueue'),
1541
+ tuple;
1542
+
1543
+ // since we are leaving the pending state, remove any
1544
+ // observers we have registered on other records.
1545
+ for (var prop in pendingQueue) {
1546
+ if (!pendingQueue.hasOwnProperty(prop)) { continue; }
1547
+
1548
+ tuple = pendingQueue[prop];
1549
+ Ember.removeObserver(tuple[0], 'id', tuple[1]);
1550
+ }
1551
+
1552
+ manager.goToState('deleted');
1553
+ },
1554
+
1555
+ willCommit: function(manager) {
1556
+ manager.goToState('committing');
1557
+ },
1558
+
1559
+ doneWaitingOn: function(manager, object) {
1560
+ var model = get(manager, 'model'),
1561
+ pendingQueue = get(model, 'pendingQueue'),
1562
+ objectGuid = guidFor(object);
1563
+
1564
+ delete pendingQueue[objectGuid];
1565
+
1566
+ if (isEmptyObject(pendingQueue)) {
1567
+ manager.send('doneWaiting');
1568
+ }
1569
+ },
1570
+
1571
+ doneWaiting: function(manager) {
1572
+ var dirtyType = get(this, 'dirtyType');
1573
+ manager.goToState(dirtyType + '.uncommitted');
1574
+ }
1575
+ }),
1576
+
1577
+ // A pending record whose transaction has started
1578
+ // to commit is in this state. Since it has not yet
1579
+ // been sent to the adapter, it is not `inFlight`
1580
+ // until all of its dependencies have been committed.
1581
+ committing: DS.State.extend({
1582
+ // FLAGS
1583
+ isSaving: true,
1584
+
1585
+ // EVENTS
1586
+ doneWaitingOn: function(manager, object) {
1587
+ var model = get(manager, 'model'),
1588
+ pendingQueue = get(model, 'pendingQueue'),
1589
+ objectGuid = guidFor(object);
1590
+
1591
+ delete pendingQueue[objectGuid];
1592
+
1593
+ if (isEmptyObject(pendingQueue)) {
1594
+ manager.send('doneWaiting');
1595
+ }
1596
+ },
1597
+
1598
+ doneWaiting: function(manager) {
1599
+ var dirtyType = get(this, 'dirtyType');
1600
+ manager.goToState(dirtyType + '.inFlight');
1601
+ }
1602
+ })
1603
+ }),
1604
+
1605
+ // A record is in the `invalid` state when its client-side
1606
+ // invalidations have failed, or if the adapter has indicated
1607
+ // the the record failed server-side invalidations.
1608
+ invalid: DS.State.extend({
1609
+ // FLAGS
1610
+ isValid: false,
1611
+
1612
+ // EVENTS
1613
+ deleteRecord: function(manager) {
1614
+ manager.goToState('deleted');
1615
+ },
1616
+
1617
+ setProperty: function(manager, context) {
1618
+ setProperty(manager, context);
1619
+
1620
+ var model = get(manager, 'model'),
1621
+ errors = get(model, 'errors'),
1622
+ key = context.key;
1623
+
1624
+ delete errors[key];
1625
+
1626
+ if (isEmptyObject(errors)) {
1627
+ manager.send('becameValid');
1628
+ }
1629
+ },
1630
+
1631
+ becameValid: function(manager) {
1632
+ manager.goToState('uncommitted');
1633
+ }
1634
+ })
1635
+ });
1636
+
1637
+ var states = {
1638
+ rootState: Ember.State.create({
1639
+ // FLAGS
1640
+ isLoaded: false,
1641
+ isDirty: false,
1642
+ isSaving: false,
1643
+ isDeleted: false,
1644
+ isError: false,
1645
+ isNew: false,
1646
+ isValid: true,
1647
+ isPending: false,
1648
+
1649
+ // SUBSTATES
1650
+
1651
+ // A record begins its lifecycle in the `empty` state.
1652
+ // If its data will come from the adapter, it will
1653
+ // transition into the `loading` state. Otherwise, if
1654
+ // the record is being created on the client, it will
1655
+ // transition into the `created` state.
1656
+ empty: DS.State.create({
1657
+ // EVENTS
1658
+ loadingData: function(manager) {
1659
+ manager.goToState('loading');
1660
+ },
1661
+
1662
+ setData: function(manager, hash) {
1663
+ var model = get(manager, 'model');
1664
+ set(model, 'data', hash);
1665
+ manager.goToState('loaded.created');
1666
+ }
1667
+ }),
1668
+
1669
+ // A record enters this state when the store askes
1670
+ // the adapter for its data. It remains in this state
1671
+ // until the adapter provides the requested data.
1672
+ //
1673
+ // Usually, this process is asynchronous, using an
1674
+ // XHR to retrieve the data.
1675
+ loading: DS.State.create({
1676
+ // TRANSITIONS
1677
+ exit: function(manager) {
1678
+ var model = get(manager, 'model');
1679
+ model.didLoad();
1680
+ },
1681
+
1682
+ // EVENTS
1683
+ setData: function(manager, data) {
1684
+ var model = get(manager, 'model');
1685
+
1686
+ model.beginPropertyChanges();
1687
+ set(model, 'data', data);
1688
+
1689
+ if (data !== null) {
1690
+ manager.send('loadedData');
1691
+ }
1692
+
1693
+ model.endPropertyChanges();
1694
+ },
1695
+
1696
+ loadedData: function(manager) {
1697
+ manager.goToState('loaded');
1698
+ }
1699
+ }),
1700
+
1701
+ // A record enters this state when its data is populated.
1702
+ // Most of a record's lifecycle is spent inside substates
1703
+ // of the `loaded` state.
1704
+ loaded: DS.State.create({
1705
+ initialState: 'saved',
1706
+
1707
+ // FLAGS
1708
+ isLoaded: true,
1709
+
1710
+ // SUBSTATES
1711
+
1712
+ // If there are no local changes to a record, it remains
1713
+ // in the `saved` state.
1714
+ saved: DS.State.create({
1715
+ // EVENTS
1716
+ setProperty: function(manager, context) {
1717
+ setProperty(manager, context);
1718
+ manager.goToState('updated');
1719
+ },
1720
+
1721
+ deleteRecord: function(manager) {
1722
+ manager.goToState('deleted');
1723
+ },
1724
+
1725
+ waitingOn: function(manager, object) {
1726
+ waitingOn(manager, object);
1727
+ manager.goToState('updated.pending');
1728
+ }
1729
+ }),
1730
+
1731
+ // A record is in this state after it has been locally
1732
+ // created but before the adapter has indicated that
1733
+ // it has been saved.
1734
+ created: DirtyState.create({
1735
+ dirtyType: 'created',
1736
+
1737
+ // FLAGS
1738
+ isNew: true,
1739
+
1740
+ // EVENTS
1741
+ invokeLifecycleCallbacks: function(manager, model) {
1742
+ model.didCreate();
1743
+ }
1744
+ }),
1745
+
1746
+ // A record is in this state if it has already been
1747
+ // saved to the server, but there are new local changes
1748
+ // that have not yet been saved.
1749
+ updated: DirtyState.create({
1750
+ dirtyType: 'updated',
1751
+
1752
+ // EVENTS
1753
+ invokeLifecycleCallbacks: function(manager, model) {
1754
+ model.didUpdate();
1755
+ }
1756
+ })
1757
+ }),
1758
+
1759
+ // A record is in this state if it was deleted from the store.
1760
+ deleted: DS.State.create({
1761
+ // FLAGS
1762
+ isDeleted: true,
1763
+ isLoaded: true,
1764
+ isDirty: true,
1765
+
1766
+ // TRANSITIONS
1767
+ enter: function(manager) {
1768
+ var model = get(manager, 'model');
1769
+ var store = get(model, 'store');
1770
+
1771
+ if (store) {
1772
+ store.removeFromModelArrays(model);
1773
+ }
1774
+
1775
+ model.withTransaction(function(t) {
1776
+ t.modelBecameDirty('deleted', model);
1777
+ });
1778
+ },
1779
+
1780
+ // SUBSTATES
1781
+
1782
+ // When a record is deleted, it enters the `start`
1783
+ // state. It will exit this state when the record's
1784
+ // transaction starts to commit.
1785
+ start: DS.State.create({
1786
+ willCommit: function(manager) {
1787
+ manager.goToState('inFlight');
1788
+ }
1789
+ }),
1790
+
1791
+ // After a record's transaction is committing, but
1792
+ // before the adapter indicates that the deletion
1793
+ // has saved to the server, a record is in the
1794
+ // `inFlight` substate of `deleted`.
1795
+ inFlight: DS.State.create({
1796
+ // FLAGS
1797
+ isSaving: true,
1798
+
1799
+ // TRANSITIONS
1800
+ exit: function(stateManager) {
1801
+ var model = get(stateManager, 'model');
1802
+
1803
+ model.withTransaction(function(t) {
1804
+ t.modelBecameClean('deleted', model);
1805
+ });
1806
+ },
1807
+
1808
+ // EVENTS
1809
+ didCommit: function(manager) {
1810
+ manager.goToState('saved');
1811
+ }
1812
+ }),
1813
+
1814
+ // Once the adapter indicates that the deletion has
1815
+ // been saved, the record enters the `saved` substate
1816
+ // of `deleted`.
1817
+ saved: DS.State.create({
1818
+ isDirty: false
1819
+ })
1820
+ }),
1821
+
1822
+ // If the adapter indicates that there was an unknown
1823
+ // error saving a record, the record enters the `error`
1824
+ // state.
1825
+ error: DS.State.create({
1826
+ isError: true
1827
+ })
1828
+ })
1829
+ };
1830
+
1831
+ DS.StateManager = Ember.StateManager.extend({
1832
+ model: null,
1833
+ initialState: 'rootState',
1834
+ states: states
1835
+ });
1836
+
1837
+ })({});
1838
+
1839
+
1840
+ (function(exports) {
1841
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
1842
+
1843
+ var retrieveFromCurrentState = Ember.computed(function(key) {
1844
+ return get(getPath(this, 'stateManager.currentState'), key);
1845
+ }).property('stateManager.currentState').cacheable();
1846
+
1847
+ DS.Model = Ember.Object.extend({
1848
+ isLoaded: retrieveFromCurrentState,
1849
+ isDirty: retrieveFromCurrentState,
1850
+ isSaving: retrieveFromCurrentState,
1851
+ isDeleted: retrieveFromCurrentState,
1852
+ isError: retrieveFromCurrentState,
1853
+ isNew: retrieveFromCurrentState,
1854
+ isPending: retrieveFromCurrentState,
1855
+ isValid: retrieveFromCurrentState,
1856
+
1857
+ clientId: null,
1858
+
1859
+ // because unknownProperty is used, any internal property
1860
+ // must be initialized here.
1861
+ primaryKey: 'id',
1862
+ id: Ember.computed(function(key, value) {
1863
+ var primaryKey = get(this, 'primaryKey'),
1864
+ data = get(this, 'data');
1865
+
1866
+ if (arguments.length === 2) {
1867
+ set(data, primaryKey, value);
1868
+ return value;
1869
+ }
1870
+
1871
+ return data && get(data, primaryKey);
1872
+ }).property('primaryKey', 'data'),
1873
+
1874
+ data: null,
1875
+ pendingQueue: null,
1876
+ transaction: null,
1877
+ errors: null,
1878
+
1879
+ didLoad: Ember.K,
1880
+ didUpdate: Ember.K,
1881
+ didCreate: Ember.K,
1882
+
1883
+ init: function() {
1884
+ var stateManager = DS.StateManager.create({
1885
+ model: this
1886
+ });
1887
+
1888
+ set(this, 'pendingQueue', {});
1889
+ set(this, 'stateManager', stateManager);
1890
+ stateManager.goToState('empty');
1891
+ },
1892
+
1893
+ destroy: function() {
1894
+ if (!get(this, 'isDeleted')) {
1895
+ this.deleteRecord();
1896
+ }
1897
+ this._super();
1898
+ },
1899
+
1900
+ send: function(name, context) {
1901
+ return get(this, 'stateManager').send(name, context);
1902
+ },
1903
+
1904
+ withTransaction: function(fn) {
1905
+ var transaction = get(this, 'transaction') || getPath(this, 'store.defaultTransaction');
1906
+ if (transaction) { fn(transaction); }
1907
+ },
1908
+
1909
+ setProperty: function(key, value) {
1910
+ this.send('setProperty', { key: key, value: value });
1911
+ },
1912
+
1913
+ deleteRecord: function() {
1914
+ this.send('deleteRecord');
1915
+ },
1916
+
1917
+ waitingOn: function(record) {
1918
+ this.send('waitingOn', record);
1919
+ },
1920
+
1921
+ notifyHashWasUpdated: function() {
1922
+ var store = get(this, 'store');
1923
+ if (store) {
1924
+ store.hashWasUpdated(this.constructor, get(this, 'clientId'));
1925
+ }
1926
+ },
1927
+
1928
+ unknownProperty: function(key) {
1929
+ var data = get(this, 'data');
1930
+
1931
+ if (data && key in data) {
1932
+ ember_assert("You attempted to access the " + key + " property on a model without defining an attribute.", false);
1933
+ }
1934
+ },
1935
+
1936
+ setUnknownProperty: function(key, value) {
1937
+ var data = get(this, 'data');
1938
+
1939
+ if (data && key in data) {
1940
+ ember_assert("You attempted to set the " + key + " property on a model without defining an attribute.", false);
1941
+ } else {
1942
+ return this._super(key, value);
1943
+ }
1944
+ }
1945
+ });
1946
+
1947
+ // Helper function to generate store aliases.
1948
+ // This returns a function that invokes the named alias
1949
+ // on the default store, but injects the class as the
1950
+ // first parameter.
1951
+ var storeAlias = function(methodName) {
1952
+ return function() {
1953
+ var store = get(DS, 'defaultStore'),
1954
+ args = [].slice.call(arguments);
1955
+
1956
+ args.unshift(this);
1957
+ return store[methodName].apply(store, args);
1958
+ };
1959
+ };
1960
+
1961
+ DS.Model.reopenClass({
1962
+ find: storeAlias('find'),
1963
+ filter: storeAlias('filter'),
1964
+
1965
+ _create: DS.Model.create,
1966
+
1967
+ create: function() {
1968
+ throw new Ember.Error("You should not call `create` on a model. Instead, call `createRecord` with the attributes you would like to set.");
1969
+ },
1970
+
1971
+ createRecord: storeAlias('createRecord')
1972
+ });
1973
+
1974
+ })({});
1975
+
1976
+
1977
+ (function(exports) {
1978
+ var get = Ember.get, getPath = Ember.getPath;
1979
+ DS.attr = function(type, options) {
1980
+ var transform = DS.attr.transforms[type];
1981
+ ember_assert("Could not find model attribute of type " + type, !!transform);
1982
+
1983
+ var transformFrom = transform.from;
1984
+ var transformTo = transform.to;
1985
+
1986
+ return Ember.computed(function(key, value) {
1987
+ var data = get(this, 'data');
1988
+
1989
+ key = (options && options.key) ? options.key : key;
1990
+
1991
+ if (value === undefined) {
1992
+ if (!data) { return; }
1993
+
1994
+ return transformFrom(data[key]);
1995
+ } else {
1996
+ ember_assert("You cannot set a model attribute before its data is loaded.", !!data);
1997
+
1998
+ value = transformTo(value);
1999
+ this.setProperty(key, value);
2000
+ return value;
2001
+ }
2002
+ }).property('data');
2003
+ };
2004
+ DS.attr.transforms = {
2005
+ string: {
2006
+ from: function(serialized) {
2007
+ return Ember.none(serialized) ? null : String(serialized);
2008
+ },
2009
+
2010
+ to: function(deserialized) {
2011
+ return Ember.none(deserialized) ? null : String(deserialized);
2012
+ }
2013
+ },
2014
+
2015
+ integer: {
2016
+ from: function(serialized) {
2017
+ return Ember.none(serialized) ? null : Number(serialized);
2018
+ },
2019
+
2020
+ to: function(deserialized) {
2021
+ return Ember.none(deserialized) ? null : Number(deserialized);
2022
+ }
2023
+ },
2024
+
2025
+ boolean: {
2026
+ from: function(serialized) {
2027
+ return Boolean(serialized);
2028
+ },
2029
+
2030
+ to: function(deserialized) {
2031
+ return Boolean(deserialized);
2032
+ }
2033
+ },
2034
+
2035
+ date: {
2036
+ from: function(serialized) {
2037
+ var type = typeof serialized;
2038
+
2039
+ if (type === "string" || type === "number") {
2040
+ return new Date(serialized);
2041
+ } else if (serialized === null || serialized === undefined) {
2042
+ // if the value is not present in the data,
2043
+ // return undefined, not null.
2044
+ return serialized;
2045
+ } else {
2046
+ return null;
2047
+ }
2048
+ },
2049
+
2050
+ to: function(date) {
2051
+ if (date instanceof Date) {
2052
+ var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2053
+ var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2054
+
2055
+ var pad = function(num) {
2056
+ return num < 10 ? "0"+num : ""+num;
2057
+ };
2058
+
2059
+ var utcYear = date.getUTCFullYear(),
2060
+ utcMonth = date.getUTCMonth(),
2061
+ utcDayOfMonth = date.getUTCDate(),
2062
+ utcDay = date.getUTCDay(),
2063
+ utcHours = date.getUTCHours(),
2064
+ utcMinutes = date.getUTCMinutes(),
2065
+ utcSeconds = date.getUTCSeconds();
2066
+
2067
+
2068
+ var dayOfWeek = days[utcDay];
2069
+ var dayOfMonth = pad(utcDayOfMonth);
2070
+ var month = months[utcMonth];
2071
+
2072
+ return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
2073
+ pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
2074
+ } else if (date === undefined) {
2075
+ return undefined;
2076
+ } else {
2077
+ return null;
2078
+ }
2079
+ }
2080
+ }
2081
+ };
2082
+
2083
+
2084
+ })({});
2085
+
2086
+
2087
+ (function(exports) {
2088
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
2089
+ DS.Model.reopenClass({
2090
+ typeForAssociation: function(association) {
2091
+ var type = this.metaForProperty(association).type;
2092
+ if (typeof type === 'string') {
2093
+ type = getPath(this, type, false) || getPath(window, type);
2094
+ }
2095
+ return type;
2096
+ }
2097
+ });
2098
+
2099
+
2100
+ var embeddedFindRecord = function(store, type, data, key, one) {
2101
+ var association = data ? get(data, key) : one ? null : [];
2102
+ if (one) {
2103
+ return association ? store.load(type, association).id : null;
2104
+ } else {
2105
+ return association ? store.loadMany(type, association).ids : [];
2106
+ }
2107
+ };
2108
+
2109
+ var referencedFindRecord = function(store, type, data, key, one) {
2110
+ return data ? get(data, key) : one ? null : [];
2111
+ };
2112
+
2113
+ var hasAssociation = function(type, options, one) {
2114
+ var embedded = options && options.embedded,
2115
+ findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
2116
+
2117
+ return Ember.computed(function(key) {
2118
+ var data = get(this, 'data'), ids, id, association,
2119
+ store = get(this, 'store');
2120
+
2121
+ if (typeof type === 'string') {
2122
+ type = getPath(this, type, false) || getPath(window, type);
2123
+ }
2124
+
2125
+ key = (options && options.key) ? options.key : key;
2126
+ if (one) {
2127
+ id = findRecord(store, type, data, key, true);
2128
+ association = id ? store.find(type, id) : null;
2129
+ } else {
2130
+ ids = findRecord(store, type, data, key);
2131
+ association = store.findMany(type, ids);
2132
+ set(association, 'parentRecord', this);
2133
+ }
2134
+
2135
+ return association;
2136
+ }).property('data').cacheable().meta({ type: type });
2137
+ };
2138
+
2139
+ DS.hasMany = function(type, options) {
2140
+ ember_assert("The type passed to DS.hasMany must be defined", !!type);
2141
+ return hasAssociation(type, options);
2142
+ };
2143
+
2144
+ DS.hasOne = function(type, options) {
2145
+ ember_assert("The type passed to DS.hasOne must be defined", !!type);
2146
+ return hasAssociation(type, options, true);
2147
+ };
2148
+
2149
+ })({});
2150
+
2151
+
2152
+ (function(exports) {
2153
+ })({});
2154
+
2155
+
2156
+ (function(exports) {
2157
+ //Copyright (C) 2011 by Living Social, Inc.
2158
+
2159
+ //Permission is hereby granted, free of charge, to any person obtaining a copy of
2160
+ //this software and associated documentation files (the "Software"), to deal in
2161
+ //the Software without restriction, including without limitation the rights to
2162
+ //use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
2163
+ //of the Software, and to permit persons to whom the Software is furnished to do
2164
+ //so, subject to the following conditions:
2165
+
2166
+ //The above copyright notice and this permission notice shall be included in all
2167
+ //copies or substantial portions of the Software.
2168
+
2169
+ //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2170
+ //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2171
+ //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2172
+ //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2173
+ //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2174
+ //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2175
+ //SOFTWARE.
2176
+ })({});