main
1// Fetched from channel: release, with url http://builds.emberjs.com/beta/ember-data.js
2// Fetched on: 2014-11-11T00:40:24Z
3(function(global){
4var define, requireModule, require, requirejs;
5
6(function() {
7
8 var _isArray;
9 if (!Array.isArray) {
10 _isArray = function (x) {
11 return Object.prototype.toString.call(x) === "[object Array]";
12 };
13 } else {
14 _isArray = Array.isArray;
15 }
16
17 var registry = {}, seen = {}, state = {};
18 var FAILED = false;
19
20 define = function(name, deps, callback) {
21
22 if (!_isArray(deps)) {
23 callback = deps;
24 deps = [];
25 }
26
27 registry[name] = {
28 deps: deps,
29 callback: callback
30 };
31 };
32
33 function reify(deps, name, seen) {
34 var length = deps.length;
35 var reified = new Array(length);
36 var dep;
37 var exports;
38
39 for (var i = 0, l = length; i < l; i++) {
40 dep = deps[i];
41 if (dep === 'exports') {
42 exports = reified[i] = seen;
43 } else {
44 reified[i] = require(resolve(dep, name));
45 }
46 }
47
48 return {
49 deps: reified,
50 exports: exports
51 };
52 }
53
54 requirejs = require = requireModule = function(name) {
55 if (state[name] !== FAILED &&
56 seen.hasOwnProperty(name)) {
57 return seen[name];
58 }
59
60 if (!registry[name]) {
61 throw new Error('Could not find module ' + name);
62 }
63
64 var mod = registry[name];
65 var reified;
66 var module;
67 var loaded = false;
68
69 seen[name] = { }; // placeholder for run-time cycles
70
71 try {
72 reified = reify(mod.deps, name, seen[name]);
73 module = mod.callback.apply(this, reified.deps);
74 loaded = true;
75 } finally {
76 if (!loaded) {
77 state[name] = FAILED;
78 }
79 }
80
81 return reified.exports ? seen[name] : (seen[name] = module);
82 };
83
84 function resolve(child, name) {
85 if (child.charAt(0) !== '.') { return child; }
86
87 var parts = child.split('/');
88 var nameParts = name.split('/');
89 var parentBase;
90
91 if (nameParts.length === 1) {
92 parentBase = nameParts;
93 } else {
94 parentBase = nameParts.slice(0, -1);
95 }
96
97 for (var i = 0, l = parts.length; i < l; i++) {
98 var part = parts[i];
99
100 if (part === '..') { parentBase.pop(); }
101 else if (part === '.') { continue; }
102 else { parentBase.push(part); }
103 }
104
105 return parentBase.join('/');
106 }
107
108 requirejs.entries = requirejs._eak_seen = registry;
109 requirejs.clear = function(){
110 requirejs.entries = requirejs._eak_seen = registry = {};
111 seen = state = {};
112 };
113})();
114
115define("activemodel-adapter",
116 ["activemodel-adapter/system","exports"],
117 function(__dependency1__, __exports__) {
118 "use strict";
119 var ActiveModelAdapter = __dependency1__.ActiveModelAdapter;
120 var ActiveModelSerializer = __dependency1__.ActiveModelSerializer;
121
122 __exports__.ActiveModelAdapter = ActiveModelAdapter;
123 __exports__.ActiveModelSerializer = ActiveModelSerializer;
124 });
125define("activemodel-adapter/setup-container",
126 ["ember-data/system/container_proxy","activemodel-adapter/system/active_model_serializer","activemodel-adapter/system/active_model_adapter","exports"],
127 function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
128 "use strict";
129 var ContainerProxy = __dependency1__["default"];
130 var ActiveModelSerializer = __dependency2__["default"];
131 var ActiveModelAdapter = __dependency3__["default"];
132
133 __exports__["default"] = function setupActiveModelAdapter(container, application){
134 var proxy = new ContainerProxy(container);
135 proxy.registerDeprecations([
136 { deprecated: 'serializer:_ams', valid: 'serializer:-active-model' },
137 { deprecated: 'adapter:_ams', valid: 'adapter:-active-model' }
138 ]);
139
140 container.register('serializer:-active-model', ActiveModelSerializer);
141 container.register('adapter:-active-model', ActiveModelAdapter);
142 };
143 });
144define("activemodel-adapter/system",
145 ["activemodel-adapter/system/active_model_adapter","activemodel-adapter/system/active_model_serializer","exports"],
146 function(__dependency1__, __dependency2__, __exports__) {
147 "use strict";
148 var ActiveModelAdapter = __dependency1__["default"];
149 var ActiveModelSerializer = __dependency2__["default"];
150
151 __exports__.ActiveModelAdapter = ActiveModelAdapter;
152 __exports__.ActiveModelSerializer = ActiveModelSerializer;
153 });
154define("activemodel-adapter/system/active_model_adapter",
155 ["ember-data/adapters","ember-data/system/adapter","ember-inflector","exports"],
156 function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
157 "use strict";
158 var RESTAdapter = __dependency1__.RESTAdapter;
159 var InvalidError = __dependency2__.InvalidError;
160 var pluralize = __dependency3__.pluralize;
161
162 /**
163 @module ember-data
164 */
165
166 var forEach = Ember.EnumerableUtils.forEach;
167 var decamelize = Ember.String.decamelize,
168 underscore = Ember.String.underscore;
169
170 /**
171 The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
172 with a JSON API that uses an underscored naming convention instead of camelCasing.
173 It has been designed to work out of the box with the
174 [active_model_serializers](http://github.com/rails-api/active_model_serializers)
175 Ruby gem. This Adapter expects specific settings using ActiveModel::Serializers,
176 `embed :ids, include: true` which sideloads the records.
177
178 This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
179 decamelization and pluralization methods to normalize the serialized JSON into a
180 format that is compatible with a conventional Rails backend and Ember Data.
181
182 ## JSON Structure
183
184 The ActiveModelAdapter expects the JSON returned from your server to follow
185 the REST adapter conventions substituting underscored keys for camelcased ones.
186
187 Unlike the DS.RESTAdapter, async relationship keys must be the singular form
188 of the relationship name, followed by "_id" for DS.belongsTo relationships,
189 or "_ids" for DS.hasMany relationships.
190
191 ### Conventional Names
192
193 Attribute names in your JSON payload should be the underscored versions of
194 the attributes in your Ember.js models.
195
196 For example, if you have a `Person` model:
197
198 ```js
199 App.FamousPerson = DS.Model.extend({
200 firstName: DS.attr('string'),
201 lastName: DS.attr('string'),
202 occupation: DS.attr('string')
203 });
204 ```
205
206 The JSON returned should look like this:
207
208 ```js
209 {
210 "famous_person": {
211 "id": 1,
212 "first_name": "Barack",
213 "last_name": "Obama",
214 "occupation": "President"
215 }
216 }
217 ```
218
219 Let's imagine that `Occupation` is just another model:
220
221 ```js
222 App.Person = DS.Model.extend({
223 firstName: DS.attr('string'),
224 lastName: DS.attr('string'),
225 occupation: DS.belongsTo('occupation')
226 });
227
228 App.Occupation = DS.Model.extend({
229 name: DS.attr('string'),
230 salary: DS.attr('number'),
231 people: DS.hasMany('person')
232 });
233 ```
234
235 The JSON needed to avoid extra server calls, should look like this:
236
237 ```js
238 {
239 "people": [{
240 "id": 1,
241 "first_name": "Barack",
242 "last_name": "Obama",
243 "occupation_id": 1
244 }],
245
246 "occupations": [{
247 "id": 1,
248 "name": "President",
249 "salary": 100000,
250 "person_ids": [1]
251 }]
252 }
253 ```
254
255 @class ActiveModelAdapter
256 @constructor
257 @namespace DS
258 @extends DS.RESTAdapter
259 **/
260
261 var ActiveModelAdapter = RESTAdapter.extend({
262 defaultSerializer: '-active-model',
263 /**
264 The ActiveModelAdapter overrides the `pathForType` method to build
265 underscored URLs by decamelizing and pluralizing the object type name.
266
267 ```js
268 this.pathForType("famousPerson");
269 //=> "famous_people"
270 ```
271
272 @method pathForType
273 @param {String} type
274 @return String
275 */
276 pathForType: function(type) {
277 var decamelized = decamelize(type);
278 var underscored = underscore(decamelized);
279 return pluralize(underscored);
280 },
281
282 /**
283 The ActiveModelAdapter overrides the `ajaxError` method
284 to return a DS.InvalidError for all 422 Unprocessable Entity
285 responses.
286
287 A 422 HTTP response from the server generally implies that the request
288 was well formed but the API was unable to process it because the
289 content was not semantically correct or meaningful per the API.
290
291 For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
292 https://tools.ietf.org/html/rfc4918#section-11.2
293
294 @method ajaxError
295 @param {Object} jqXHR
296 @return error
297 */
298 ajaxError: function(jqXHR) {
299 var error = this._super(jqXHR);
300
301 if (jqXHR && jqXHR.status === 422) {
302 var response = Ember.$.parseJSON(jqXHR.responseText),
303 errors = {};
304
305 if (response.errors !== undefined) {
306 var jsonErrors = response.errors;
307
308 forEach(Ember.keys(jsonErrors), function(key) {
309 errors[Ember.String.camelize(key)] = jsonErrors[key];
310 });
311 }
312
313 return new InvalidError(errors);
314 } else {
315 return error;
316 }
317 }
318 });
319
320 __exports__["default"] = ActiveModelAdapter;
321 });
322define("activemodel-adapter/system/active_model_serializer",
323 ["ember-inflector","ember-data/serializers/rest_serializer","exports"],
324 function(__dependency1__, __dependency2__, __exports__) {
325 "use strict";
326 var singularize = __dependency1__.singularize;
327 var RESTSerializer = __dependency2__["default"];
328 /**
329 @module ember-data
330 */
331
332 var get = Ember.get,
333 forEach = Ember.EnumerableUtils.forEach,
334 camelize = Ember.String.camelize,
335 capitalize = Ember.String.capitalize,
336 decamelize = Ember.String.decamelize,
337 underscore = Ember.String.underscore;
338 /**
339 The ActiveModelSerializer is a subclass of the RESTSerializer designed to integrate
340 with a JSON API that uses an underscored naming convention instead of camelCasing.
341 It has been designed to work out of the box with the
342 [active_model_serializers](http://github.com/rails-api/active_model_serializers)
343 Ruby gem. This Serializer expects specific settings using ActiveModel::Serializers,
344 `embed :ids, include: true` which sideloads the records.
345
346 This serializer extends the DS.RESTSerializer by making consistent
347 use of the camelization, decamelization and pluralization methods to
348 normalize the serialized JSON into a format that is compatible with
349 a conventional Rails backend and Ember Data.
350
351 ## JSON Structure
352
353 The ActiveModelSerializer expects the JSON returned from your server
354 to follow the REST adapter conventions substituting underscored keys
355 for camelcased ones.
356
357 ### Conventional Names
358
359 Attribute names in your JSON payload should be the underscored versions of
360 the attributes in your Ember.js models.
361
362 For example, if you have a `Person` model:
363
364 ```js
365 App.FamousPerson = DS.Model.extend({
366 firstName: DS.attr('string'),
367 lastName: DS.attr('string'),
368 occupation: DS.attr('string')
369 });
370 ```
371
372 The JSON returned should look like this:
373
374 ```js
375 {
376 "famous_person": {
377 "id": 1,
378 "first_name": "Barack",
379 "last_name": "Obama",
380 "occupation": "President"
381 }
382 }
383 ```
384
385 Let's imagine that `Occupation` is just another model:
386
387 ```js
388 App.Person = DS.Model.extend({
389 firstName: DS.attr('string'),
390 lastName: DS.attr('string'),
391 occupation: DS.belongsTo('occupation')
392 });
393
394 App.Occupation = DS.Model.extend({
395 name: DS.attr('string'),
396 salary: DS.attr('number'),
397 people: DS.hasMany('person')
398 });
399 ```
400
401 The JSON needed to avoid extra server calls, should look like this:
402
403 ```js
404 {
405 "people": [{
406 "id": 1,
407 "first_name": "Barack",
408 "last_name": "Obama",
409 "occupation_id": 1
410 }],
411
412 "occupations": [{
413 "id": 1,
414 "name": "President",
415 "salary": 100000,
416 "person_ids": [1]
417 }]
418 }
419 ```
420
421 @class ActiveModelSerializer
422 @namespace DS
423 @extends DS.RESTSerializer
424 */
425 var ActiveModelSerializer = RESTSerializer.extend({
426 // SERIALIZE
427
428 /**
429 Converts camelCased attributes to underscored when serializing.
430
431 @method keyForAttribute
432 @param {String} attribute
433 @return String
434 */
435 keyForAttribute: function(attr) {
436 return decamelize(attr);
437 },
438
439 /**
440 Underscores relationship names and appends "_id" or "_ids" when serializing
441 relationship keys.
442
443 @method keyForRelationship
444 @param {String} key
445 @param {String} kind
446 @return String
447 */
448 keyForRelationship: function(rawKey, kind) {
449 var key = decamelize(rawKey);
450 if (kind === "belongsTo") {
451 return key + "_id";
452 } else if (kind === "hasMany") {
453 return singularize(key) + "_ids";
454 } else {
455 return key;
456 }
457 },
458
459 /*
460 Does not serialize hasMany relationships by default.
461 */
462 serializeHasMany: Ember.K,
463
464 /**
465 Underscores the JSON root keys when serializing.
466
467 @method serializeIntoHash
468 @param {Object} hash
469 @param {subclass of DS.Model} type
470 @param {DS.Model} record
471 @param {Object} options
472 */
473 serializeIntoHash: function(data, type, record, options) {
474 var root = underscore(decamelize(type.typeKey));
475 data[root] = this.serialize(record, options);
476 },
477
478 /**
479 Serializes a polymorphic type as a fully capitalized model name.
480
481 @method serializePolymorphicType
482 @param {DS.Model} record
483 @param {Object} json
484 @param {Object} relationship
485 */
486 serializePolymorphicType: function(record, json, relationship) {
487 var key = relationship.key;
488 var belongsTo = get(record, key);
489 var jsonKey = underscore(key + "_type");
490
491 if (Ember.isNone(belongsTo)) {
492 json[jsonKey] = null;
493 } else {
494 json[jsonKey] = capitalize(camelize(belongsTo.constructor.typeKey));
495 }
496 },
497
498 // EXTRACT
499
500 /**
501 Add extra step to `DS.RESTSerializer.normalize` so links are normalized.
502
503 If your payload looks like:
504
505 ```js
506 {
507 "post": {
508 "id": 1,
509 "title": "Rails is omakase",
510 "links": { "flagged_comments": "api/comments/flagged" }
511 }
512 }
513 ```
514
515 The normalized version would look like this
516
517 ```js
518 {
519 "post": {
520 "id": 1,
521 "title": "Rails is omakase",
522 "links": { "flaggedComments": "api/comments/flagged" }
523 }
524 }
525 ```
526
527 @method normalize
528 @param {subclass of DS.Model} type
529 @param {Object} hash
530 @param {String} prop
531 @return Object
532 */
533
534 normalize: function(type, hash, prop) {
535 this.normalizeLinks(hash);
536
537 return this._super(type, hash, prop);
538 },
539
540 /**
541 Convert `snake_cased` links to `camelCase`
542
543 @method normalizeLinks
544 @param {Object} data
545 */
546
547 normalizeLinks: function(data){
548 if (data.links) {
549 var links = data.links;
550
551 for (var link in links) {
552 var camelizedLink = camelize(link);
553
554 if (camelizedLink !== link) {
555 links[camelizedLink] = links[link];
556 delete links[link];
557 }
558 }
559 }
560 },
561
562 /**
563 Normalize the polymorphic type from the JSON.
564
565 Normalize:
566 ```js
567 {
568 id: "1"
569 minion: { type: "evil_minion", id: "12"}
570 }
571 ```
572
573 To:
574 ```js
575 {
576 id: "1"
577 minion: { type: "evilMinion", id: "12"}
578 }
579 ```
580
581 @method normalizeRelationships
582 @private
583 */
584 normalizeRelationships: function(type, hash) {
585
586 if (this.keyForRelationship) {
587 type.eachRelationship(function(key, relationship) {
588 var payloadKey, payload;
589 if (relationship.options.polymorphic) {
590 payloadKey = this.keyForAttribute(key);
591 payload = hash[payloadKey];
592 if (payload && payload.type) {
593 payload.type = this.typeForRoot(payload.type);
594 } else if (payload && relationship.kind === "hasMany") {
595 var self = this;
596 forEach(payload, function(single) {
597 single.type = self.typeForRoot(single.type);
598 });
599 }
600 } else {
601 payloadKey = this.keyForRelationship(key, relationship.kind);
602 if (!hash.hasOwnProperty(payloadKey)) { return; }
603 payload = hash[payloadKey];
604 }
605
606 hash[key] = payload;
607
608 if (key !== payloadKey) {
609 delete hash[payloadKey];
610 }
611 }, this);
612 }
613 }
614 });
615
616 __exports__["default"] = ActiveModelSerializer;
617 });
618define("ember-data",
619 ["ember-data/core","ember-data/ext/date","ember-data/system/promise_proxies","ember-data/system/store","ember-data/system/model","ember-data/system/adapter","ember-data/system/debug","ember-data/system/record_arrays","ember-data/system/record_array_manager","ember-data/adapters","ember-data/serializers/json_serializer","ember-data/serializers/rest_serializer","ember-inflector","ember-data/serializers/embedded_records_mixin","activemodel-adapter","ember-data/transforms","ember-data/system/relationships","ember-data/ember-initializer","ember-data/setup-container","ember-data/system/container_proxy","ember-data/system/relationships/relationship","exports"],
620 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __exports__) {
621 "use strict";
622 /**
623 Ember Data
624
625 @module ember-data
626 @main ember-data
627 */
628
629 // support RSVP 2.x via resolve, but prefer RSVP 3.x's Promise.cast
630 Ember.RSVP.Promise.cast = Ember.RSVP.Promise.cast || Ember.RSVP.resolve;
631
632 var DS = __dependency1__["default"];
633
634 var PromiseArray = __dependency3__.PromiseArray;
635 var PromiseObject = __dependency3__.PromiseObject;
636 var Store = __dependency4__.Store;
637 var Model = __dependency5__.Model;
638 var Errors = __dependency5__.Errors;
639 var RootState = __dependency5__.RootState;
640 var attr = __dependency5__.attr;
641 var InvalidError = __dependency6__.InvalidError;
642 var Adapter = __dependency6__.Adapter;
643 var DebugAdapter = __dependency7__["default"];
644 var RecordArray = __dependency8__.RecordArray;
645 var FilteredRecordArray = __dependency8__.FilteredRecordArray;
646 var AdapterPopulatedRecordArray = __dependency8__.AdapterPopulatedRecordArray;
647 var ManyArray = __dependency8__.ManyArray;
648 var RecordArrayManager = __dependency9__["default"];
649 var RESTAdapter = __dependency10__.RESTAdapter;
650 var FixtureAdapter = __dependency10__.FixtureAdapter;
651 var JSONSerializer = __dependency11__["default"];
652 var RESTSerializer = __dependency12__["default"];
653 var EmbeddedRecordsMixin = __dependency14__["default"];
654 var ActiveModelAdapter = __dependency15__.ActiveModelAdapter;
655 var ActiveModelSerializer = __dependency15__.ActiveModelSerializer;
656
657 var Transform = __dependency16__.Transform;
658 var DateTransform = __dependency16__.DateTransform;
659 var NumberTransform = __dependency16__.NumberTransform;
660 var StringTransform = __dependency16__.StringTransform;
661 var BooleanTransform = __dependency16__.BooleanTransform;
662
663 var hasMany = __dependency17__.hasMany;
664 var belongsTo = __dependency17__.belongsTo;
665 var setupContainer = __dependency19__["default"];
666
667 var ContainerProxy = __dependency20__["default"];
668 var Relationship = __dependency21__.Relationship;
669
670 DS.Store = Store;
671 DS.PromiseArray = PromiseArray;
672 DS.PromiseObject = PromiseObject;
673
674 DS.Model = Model;
675 DS.RootState = RootState;
676 DS.attr = attr;
677 DS.Errors = Errors;
678
679 DS.Adapter = Adapter;
680 DS.InvalidError = InvalidError;
681
682 DS.DebugAdapter = DebugAdapter;
683
684 DS.RecordArray = RecordArray;
685 DS.FilteredRecordArray = FilteredRecordArray;
686 DS.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray;
687 DS.ManyArray = ManyArray;
688
689 DS.RecordArrayManager = RecordArrayManager;
690
691 DS.RESTAdapter = RESTAdapter;
692 DS.FixtureAdapter = FixtureAdapter;
693
694 DS.RESTSerializer = RESTSerializer;
695 DS.JSONSerializer = JSONSerializer;
696
697 DS.Transform = Transform;
698 DS.DateTransform = DateTransform;
699 DS.StringTransform = StringTransform;
700 DS.NumberTransform = NumberTransform;
701 DS.BooleanTransform = BooleanTransform;
702
703 DS.ActiveModelAdapter = ActiveModelAdapter;
704 DS.ActiveModelSerializer = ActiveModelSerializer;
705 DS.EmbeddedRecordsMixin = EmbeddedRecordsMixin;
706
707 DS.belongsTo = belongsTo;
708 DS.hasMany = hasMany;
709
710 DS.Relationship = Relationship;
711
712 DS.ContainerProxy = ContainerProxy;
713
714 DS._setupContainer = setupContainer;
715
716 Ember.lookup.DS = DS;
717
718 __exports__["default"] = DS;
719 });
720define("ember-data/adapters",
721 ["ember-data/adapters/fixture_adapter","ember-data/adapters/rest_adapter","exports"],
722 function(__dependency1__, __dependency2__, __exports__) {
723 "use strict";
724 /**
725 @module ember-data
726 */
727
728 var FixtureAdapter = __dependency1__["default"];
729 var RESTAdapter = __dependency2__["default"];
730
731 __exports__.RESTAdapter = RESTAdapter;
732 __exports__.FixtureAdapter = FixtureAdapter;
733 });
734define("ember-data/adapters/fixture_adapter",
735 ["ember-data/system/adapter","exports"],
736 function(__dependency1__, __exports__) {
737 "use strict";
738 /**
739 @module ember-data
740 */
741
742 var get = Ember.get;
743 var fmt = Ember.String.fmt;
744 var indexOf = Ember.EnumerableUtils.indexOf;
745
746 var counter = 0;
747
748 var Adapter = __dependency1__["default"];
749
750 /**
751 `DS.FixtureAdapter` is an adapter that loads records from memory.
752 It's primarily used for development and testing. You can also use
753 `DS.FixtureAdapter` while working on the API but is not ready to
754 integrate yet. It is a fully functioning adapter. All CRUD methods
755 are implemented. You can also implement query logic that a remote
756 system would do. It's possible to develop your entire application
757 with `DS.FixtureAdapter`.
758
759 For information on how to use the `FixtureAdapter` in your
760 application please see the [FixtureAdapter
761 guide](/guides/models/the-fixture-adapter/).
762
763 @class FixtureAdapter
764 @namespace DS
765 @extends DS.Adapter
766 */
767 __exports__["default"] = Adapter.extend({
768 // by default, fixtures are already in normalized form
769 serializer: null,
770
771 /**
772 If `simulateRemoteResponse` is `true` the `FixtureAdapter` will
773 wait a number of milliseconds before resolving promises with the
774 fixture values. The wait time can be configured via the `latency`
775 property.
776
777 @property simulateRemoteResponse
778 @type {Boolean}
779 @default true
780 */
781 simulateRemoteResponse: true,
782
783 /**
784 By default the `FixtureAdapter` will simulate a wait of the
785 `latency` milliseconds before resolving promises with the fixture
786 values. This behavior can be turned off via the
787 `simulateRemoteResponse` property.
788
789 @property latency
790 @type {Number}
791 @default 50
792 */
793 latency: 50,
794
795 /**
796 Implement this method in order to provide data associated with a type
797
798 @method fixturesForType
799 @param {Subclass of DS.Model} type
800 @return {Array}
801 */
802 fixturesForType: function(type) {
803 if (type.FIXTURES) {
804 var fixtures = Ember.A(type.FIXTURES);
805 return fixtures.map(function(fixture){
806 var fixtureIdType = typeof fixture.id;
807 if(fixtureIdType !== "number" && fixtureIdType !== "string"){
808 throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture]));
809 }
810 fixture.id = fixture.id + '';
811 return fixture;
812 });
813 }
814 return null;
815 },
816
817 /**
818 Implement this method in order to query fixtures data
819
820 @method queryFixtures
821 @param {Array} fixture
822 @param {Object} query
823 @param {Subclass of DS.Model} type
824 @return {Promise|Array}
825 */
826 queryFixtures: function(fixtures, query, type) {
827 Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
828 },
829
830 /**
831 @method updateFixtures
832 @param {Subclass of DS.Model} type
833 @param {Array} fixture
834 */
835 updateFixtures: function(type, fixture) {
836 if(!type.FIXTURES) {
837 type.FIXTURES = [];
838 }
839
840 var fixtures = type.FIXTURES;
841
842 this.deleteLoadedFixture(type, fixture);
843
844 fixtures.push(fixture);
845 },
846
847 /**
848 Implement this method in order to provide json for CRUD methods
849
850 @method mockJSON
851 @param {Subclass of DS.Model} type
852 @param {DS.Model} record
853 */
854 mockJSON: function(store, type, record) {
855 return store.serializerFor(type).serialize(record, { includeId: true });
856 },
857
858 /**
859 @method generateIdForRecord
860 @param {DS.Store} store
861 @param {DS.Model} record
862 @return {String} id
863 */
864 generateIdForRecord: function(store) {
865 return "fixture-" + counter++;
866 },
867
868 /**
869 @method find
870 @param {DS.Store} store
871 @param {subclass of DS.Model} type
872 @param {String} id
873 @return {Promise} promise
874 */
875 find: function(store, type, id) {
876 var fixtures = this.fixturesForType(type);
877 var fixture;
878
879 Ember.assert("Unable to find fixtures for model type "+type.toString() +". If you're defining your fixtures using `Model.FIXTURES = ...`, please change it to `Model.reopenClass({ FIXTURES: ... })`.", fixtures);
880
881 if (fixtures) {
882 fixture = Ember.A(fixtures).findBy('id', id);
883 }
884
885 if (fixture) {
886 return this.simulateRemoteCall(function() {
887 return fixture;
888 }, this);
889 }
890 },
891
892 /**
893 @method findMany
894 @param {DS.Store} store
895 @param {subclass of DS.Model} type
896 @param {Array} ids
897 @return {Promise} promise
898 */
899 findMany: function(store, type, ids) {
900 var fixtures = this.fixturesForType(type);
901
902 Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
903
904 if (fixtures) {
905 fixtures = fixtures.filter(function(item) {
906 return indexOf(ids, item.id) !== -1;
907 });
908 }
909
910 if (fixtures) {
911 return this.simulateRemoteCall(function() {
912 return fixtures;
913 }, this);
914 }
915 },
916
917 /**
918 @private
919 @method findAll
920 @param {DS.Store} store
921 @param {subclass of DS.Model} type
922 @param {String} sinceToken
923 @return {Promise} promise
924 */
925 findAll: function(store, type) {
926 var fixtures = this.fixturesForType(type);
927
928 Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
929
930 return this.simulateRemoteCall(function() {
931 return fixtures;
932 }, this);
933 },
934
935 /**
936 @private
937 @method findQuery
938 @param {DS.Store} store
939 @param {subclass of DS.Model} type
940 @param {Object} query
941 @param {DS.AdapterPopulatedRecordArray} recordArray
942 @return {Promise} promise
943 */
944 findQuery: function(store, type, query, array) {
945 var fixtures = this.fixturesForType(type);
946
947 Ember.assert("Unable to find fixtures for model type " + type.toString(), fixtures);
948
949 fixtures = this.queryFixtures(fixtures, query, type);
950
951 if (fixtures) {
952 return this.simulateRemoteCall(function() {
953 return fixtures;
954 }, this);
955 }
956 },
957
958 /**
959 @method createRecord
960 @param {DS.Store} store
961 @param {subclass of DS.Model} type
962 @param {DS.Model} record
963 @return {Promise} promise
964 */
965 createRecord: function(store, type, record) {
966 var fixture = this.mockJSON(store, type, record);
967
968 this.updateFixtures(type, fixture);
969
970 return this.simulateRemoteCall(function() {
971 return fixture;
972 }, this);
973 },
974
975 /**
976 @method updateRecord
977 @param {DS.Store} store
978 @param {subclass of DS.Model} type
979 @param {DS.Model} record
980 @return {Promise} promise
981 */
982 updateRecord: function(store, type, record) {
983 var fixture = this.mockJSON(store, type, record);
984
985 this.updateFixtures(type, fixture);
986
987 return this.simulateRemoteCall(function() {
988 return fixture;
989 }, this);
990 },
991
992 /**
993 @method deleteRecord
994 @param {DS.Store} store
995 @param {subclass of DS.Model} type
996 @param {DS.Model} record
997 @return {Promise} promise
998 */
999 deleteRecord: function(store, type, record) {
1000 this.deleteLoadedFixture(type, record);
1001
1002 return this.simulateRemoteCall(function() {
1003 // no payload in a deletion
1004 return null;
1005 });
1006 },
1007
1008 /*
1009 @method deleteLoadedFixture
1010 @private
1011 @param type
1012 @param record
1013 */
1014 deleteLoadedFixture: function(type, record) {
1015 var existingFixture = this.findExistingFixture(type, record);
1016
1017 if (existingFixture) {
1018 var index = indexOf(type.FIXTURES, existingFixture);
1019 type.FIXTURES.splice(index, 1);
1020 return true;
1021 }
1022 },
1023
1024 /*
1025 @method findExistingFixture
1026 @private
1027 @param type
1028 @param record
1029 */
1030 findExistingFixture: function(type, record) {
1031 var fixtures = this.fixturesForType(type);
1032 var id = get(record, 'id');
1033
1034 return this.findFixtureById(fixtures, id);
1035 },
1036
1037 /*
1038 @method findFixtureById
1039 @private
1040 @param fixtures
1041 @param id
1042 */
1043 findFixtureById: function(fixtures, id) {
1044 return Ember.A(fixtures).find(function(r) {
1045 if (''+get(r, 'id') === ''+id) {
1046 return true;
1047 } else {
1048 return false;
1049 }
1050 });
1051 },
1052
1053 /*
1054 @method simulateRemoteCall
1055 @private
1056 @param callback
1057 @param context
1058 */
1059 simulateRemoteCall: function(callback, context) {
1060 var adapter = this;
1061
1062 return new Ember.RSVP.Promise(function(resolve) {
1063 var value = Ember.copy(callback.call(context), true);
1064 if (get(adapter, 'simulateRemoteResponse')) {
1065 // Schedule with setTimeout
1066 Ember.run.later(function() {
1067 resolve(value);
1068 }, get(adapter, 'latency'));
1069 } else {
1070 // Asynchronous, but at the of the runloop with zero latency
1071 Ember.run.schedule('actions', null, function() {
1072 resolve(value);
1073 });
1074 }
1075 }, "DS: FixtureAdapter#simulateRemoteCall");
1076 }
1077 });
1078 });
1079define("ember-data/adapters/rest_adapter",
1080 ["ember-data/system/adapter","ember-data/system/map","exports"],
1081 function(__dependency1__, __dependency2__, __exports__) {
1082 "use strict";
1083 /**
1084 @module ember-data
1085 */
1086
1087 var Adapter = __dependency1__.Adapter;
1088 var InvalidError = __dependency1__.InvalidError;
1089 var MapWithDefault = __dependency2__.MapWithDefault;
1090 var get = Ember.get;
1091 var forEach = Ember.ArrayPolyfills.forEach;
1092
1093 /**
1094 The REST adapter allows your store to communicate with an HTTP server by
1095 transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
1096 should use the REST adapter.
1097
1098 This adapter is designed around the idea that the JSON exchanged with
1099 the server should be conventional.
1100
1101 ## JSON Structure
1102
1103 The REST adapter expects the JSON returned from your server to follow
1104 these conventions.
1105
1106 ### Object Root
1107
1108 The JSON payload should be an object that contains the record inside a
1109 root property. For example, in response to a `GET` request for
1110 `/posts/1`, the JSON should look like this:
1111
1112 ```js
1113 {
1114 "post": {
1115 "id": 1,
1116 "title": "I'm Running to Reform the W3C's Tag",
1117 "author": "Yehuda Katz"
1118 }
1119 }
1120 ```
1121
1122 Similarly, in response to a `GET` request for `/posts`, the JSON should
1123 look like this:
1124
1125 ```js
1126 {
1127 "posts": [
1128 {
1129 "id": 1,
1130 "title": "I'm Running to Reform the W3C's Tag",
1131 "author": "Yehuda Katz"
1132 },
1133 {
1134 "id": 2,
1135 "title": "Rails is omakase",
1136 "author": "D2H"
1137 }
1138 ]
1139 }
1140 ```
1141
1142 ### Conventional Names
1143
1144 Attribute names in your JSON payload should be the camelCased versions of
1145 the attributes in your Ember.js models.
1146
1147 For example, if you have a `Person` model:
1148
1149 ```js
1150 App.Person = DS.Model.extend({
1151 firstName: DS.attr('string'),
1152 lastName: DS.attr('string'),
1153 occupation: DS.attr('string')
1154 });
1155 ```
1156
1157 The JSON returned should look like this:
1158
1159 ```js
1160 {
1161 "person": {
1162 "id": 5,
1163 "firstName": "Barack",
1164 "lastName": "Obama",
1165 "occupation": "President"
1166 }
1167 }
1168 ```
1169
1170 ## Customization
1171
1172 ### Endpoint path customization
1173
1174 Endpoint paths can be prefixed with a `namespace` by setting the namespace
1175 property on the adapter:
1176
1177 ```js
1178 DS.RESTAdapter.reopen({
1179 namespace: 'api/1'
1180 });
1181 ```
1182 Requests for `App.Person` would now target `/api/1/people/1`.
1183
1184 ### Host customization
1185
1186 An adapter can target other hosts by setting the `host` property.
1187
1188 ```js
1189 DS.RESTAdapter.reopen({
1190 host: 'https://api.example.com'
1191 });
1192 ```
1193
1194 ### Headers customization
1195
1196 Some APIs require HTTP headers, e.g. to provide an API key. Arbitrary
1197 headers can be set as key/value pairs on the `RESTAdapter`'s `headers`
1198 object and Ember Data will send them along with each ajax request.
1199
1200
1201 ```js
1202 App.ApplicationAdapter = DS.RESTAdapter.extend({
1203 headers: {
1204 "API_KEY": "secret key",
1205 "ANOTHER_HEADER": "Some header value"
1206 }
1207 });
1208 ```
1209
1210 `headers` can also be used as a computed property to support dynamic
1211 headers. In the example below, the `session` object has been
1212 injected into an adapter by Ember's container.
1213
1214 ```js
1215 App.ApplicationAdapter = DS.RESTAdapter.extend({
1216 headers: function() {
1217 return {
1218 "API_KEY": this.get("session.authToken"),
1219 "ANOTHER_HEADER": "Some header value"
1220 };
1221 }.property("session.authToken")
1222 });
1223 ```
1224
1225 In some cases, your dynamic headers may require data from some
1226 object outside of Ember's observer system (for example
1227 `document.cookie`). You can use the
1228 [volatile](/api/classes/Ember.ComputedProperty.html#method_volatile)
1229 function to set the property into a non-cached mode causing the headers to
1230 be recomputed with every request.
1231
1232 ```js
1233 App.ApplicationAdapter = DS.RESTAdapter.extend({
1234 headers: function() {
1235 return {
1236 "API_KEY": Ember.get(document.cookie.match(/apiKey\=([^;]*)/), "1"),
1237 "ANOTHER_HEADER": "Some header value"
1238 };
1239 }.property().volatile()
1240 });
1241 ```
1242
1243 @class RESTAdapter
1244 @constructor
1245 @namespace DS
1246 @extends DS.Adapter
1247 */
1248 __exports__["default"] = Adapter.extend({
1249 defaultSerializer: '-rest',
1250
1251 /**
1252 By default the RESTAdapter will send each find request coming from a `store.find`
1253 or from accessing a relationship separately to the server. If your server supports passing
1254 ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests
1255 within a single runloop.
1256
1257 For example, if you have an initial payload of
1258 ```javascript
1259 post: {
1260 id:1,
1261 comments: [1,2]
1262 }
1263 ```
1264
1265 By default calling `post.get('comments')` will trigger the following requests(assuming the
1266 comments haven't been loaded before):
1267
1268 ```
1269 GET /comments/1
1270 GET /comments/2
1271 ```
1272
1273 If you set coalesceFindRequests to `true` it will instead trigger the following request:
1274
1275 ```
1276 GET /comments?ids[]=1&ids[]=2
1277 ```
1278
1279 Setting coalesceFindRequests to `true` also works for `store.find` requests and `belongsTo`
1280 relationships accessed within the same runloop. If you set `coalesceFindRequests: true`
1281
1282 ```javascript
1283 store.find('comment', 1);
1284 store.find('comment', 2);
1285 ```
1286
1287 will also send a request to: `GET /comments?ids[]=1&ids[]=2`
1288
1289 Note: Requests coalescing rely on URL building strategy. So if you override `buildUrl` in your app
1290 `groupRecordsForFindMany` more likely should be overriden as well in order for coalescing to work.
1291
1292 @property coalesceFindRequests
1293 @type {boolean}
1294 */
1295 coalesceFindRequests: false,
1296
1297 /**
1298 Endpoint paths can be prefixed with a `namespace` by setting the namespace
1299 property on the adapter:
1300
1301 ```javascript
1302 DS.RESTAdapter.reopen({
1303 namespace: 'api/1'
1304 });
1305 ```
1306
1307 Requests for `App.Post` would now target `/api/1/post/`.
1308
1309 @property namespace
1310 @type {String}
1311 */
1312
1313 /**
1314 An adapter can target other hosts by setting the `host` property.
1315
1316 ```javascript
1317 DS.RESTAdapter.reopen({
1318 host: 'https://api.example.com'
1319 });
1320 ```
1321
1322 Requests for `App.Post` would now target `https://api.example.com/post/`.
1323
1324 @property host
1325 @type {String}
1326 */
1327
1328 /**
1329 Some APIs require HTTP headers, e.g. to provide an API
1330 key. Arbitrary headers can be set as key/value pairs on the
1331 `RESTAdapter`'s `headers` object and Ember Data will send them
1332 along with each ajax request. For dynamic headers see [headers
1333 customization](/api/data/classes/DS.RESTAdapter.html#toc_headers-customization).
1334
1335 ```javascript
1336 App.ApplicationAdapter = DS.RESTAdapter.extend({
1337 headers: {
1338 "API_KEY": "secret key",
1339 "ANOTHER_HEADER": "Some header value"
1340 }
1341 });
1342 ```
1343
1344 @property headers
1345 @type {Object}
1346 */
1347
1348 /**
1349 Called by the store in order to fetch the JSON for a given
1350 type and ID.
1351
1352 The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a
1353 promise for the resulting payload.
1354
1355 This method performs an HTTP `GET` request with the id provided as part of the query string.
1356
1357 @method find
1358 @param {DS.Store} store
1359 @param {subclass of DS.Model} type
1360 @param {String} id
1361 @param {DS.Model} record
1362 @return {Promise} promise
1363 */
1364 find: function(store, type, id, record) {
1365 return this.ajax(this.buildURL(type.typeKey, id, record), 'GET');
1366 },
1367
1368 /**
1369 Called by the store in order to fetch a JSON array for all
1370 of the records for a given type.
1371
1372 The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
1373 promise for the resulting payload.
1374
1375 @private
1376 @method findAll
1377 @param {DS.Store} store
1378 @param {subclass of DS.Model} type
1379 @param {String} sinceToken
1380 @return {Promise} promise
1381 */
1382 findAll: function(store, type, sinceToken) {
1383 var query;
1384
1385 if (sinceToken) {
1386 query = { since: sinceToken };
1387 }
1388
1389 return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
1390 },
1391
1392 /**
1393 Called by the store in order to fetch a JSON array for
1394 the records that match a particular query.
1395
1396 The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
1397 promise for the resulting payload.
1398
1399 The `query` argument is a simple JavaScript object that will be passed directly
1400 to the server as parameters.
1401
1402 @private
1403 @method findQuery
1404 @param {DS.Store} store
1405 @param {subclass of DS.Model} type
1406 @param {Object} query
1407 @return {Promise} promise
1408 */
1409 findQuery: function(store, type, query) {
1410 return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
1411 },
1412
1413 /**
1414 Called by the store in order to fetch several records together if `coalesceFindRequests` is true
1415
1416 For example, if the original payload looks like:
1417
1418 ```js
1419 {
1420 "id": 1,
1421 "title": "Rails is omakase",
1422 "comments": [ 1, 2, 3 ]
1423 }
1424 ```
1425
1426 The IDs will be passed as a URL-encoded Array of IDs, in this form:
1427
1428 ```
1429 ids[]=1&ids[]=2&ids[]=3
1430 ```
1431
1432 Many servers, such as Rails and PHP, will automatically convert this URL-encoded array
1433 into an Array for you on the server-side. If you want to encode the
1434 IDs, differently, just override this (one-line) method.
1435
1436 The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
1437 promise for the resulting payload.
1438
1439 @method findMany
1440 @param {DS.Store} store
1441 @param {subclass of DS.Model} type
1442 @param {Array} ids
1443 @param {Array} records
1444 @return {Promise} promise
1445 */
1446 findMany: function(store, type, ids, records) {
1447 return this.ajax(this.buildURL(type.typeKey, ids, records), 'GET', { data: { ids: ids } });
1448 },
1449
1450 /**
1451 Called by the store in order to fetch a JSON array for
1452 the unloaded records in a has-many relationship that were originally
1453 specified as a URL (inside of `links`).
1454
1455 For example, if your original payload looks like this:
1456
1457 ```js
1458 {
1459 "post": {
1460 "id": 1,
1461 "title": "Rails is omakase",
1462 "links": { "comments": "/posts/1/comments" }
1463 }
1464 }
1465 ```
1466
1467 This method will be called with the parent record and `/posts/1/comments`.
1468
1469 The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL.
1470 If the URL is host-relative (starting with a single slash), the
1471 request will use the host specified on the adapter (if any).
1472
1473 @method findHasMany
1474 @param {DS.Store} store
1475 @param {DS.Model} record
1476 @param {String} url
1477 @return {Promise} promise
1478 */
1479 findHasMany: function(store, record, url, relationship) {
1480 var host = get(this, 'host');
1481 var id = get(record, 'id');
1482 var type = record.constructor.typeKey;
1483
1484 if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') {
1485 url = host + url;
1486 }
1487
1488 return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
1489 },
1490
1491 /**
1492 Called by the store in order to fetch a JSON array for
1493 the unloaded records in a belongs-to relationship that were originally
1494 specified as a URL (inside of `links`).
1495
1496 For example, if your original payload looks like this:
1497
1498 ```js
1499 {
1500 "person": {
1501 "id": 1,
1502 "name": "Tom Dale",
1503 "links": { "group": "/people/1/group" }
1504 }
1505 }
1506 ```
1507
1508 This method will be called with the parent record and `/people/1/group`.
1509
1510 The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL.
1511
1512 @method findBelongsTo
1513 @param {DS.Store} store
1514 @param {DS.Model} record
1515 @param {String} url
1516 @return {Promise} promise
1517 */
1518 findBelongsTo: function(store, record, url, relationship) {
1519 var id = get(record, 'id');
1520 var type = record.constructor.typeKey;
1521
1522 return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
1523 },
1524
1525 /**
1526 Called by the store when a newly created record is
1527 saved via the `save` method on a model record instance.
1528
1529 The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request
1530 to a URL computed by `buildURL`.
1531
1532 See `serialize` for information on how to customize the serialized form
1533 of a record.
1534
1535 @method createRecord
1536 @param {DS.Store} store
1537 @param {subclass of DS.Model} type
1538 @param {DS.Model} record
1539 @return {Promise} promise
1540 */
1541 createRecord: function(store, type, record) {
1542 var data = {};
1543 var serializer = store.serializerFor(type.typeKey);
1544
1545 serializer.serializeIntoHash(data, type, record, { includeId: true });
1546
1547 return this.ajax(this.buildURL(type.typeKey, null, record), "POST", { data: data });
1548 },
1549
1550 /**
1551 Called by the store when an existing record is saved
1552 via the `save` method on a model record instance.
1553
1554 The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request
1555 to a URL computed by `buildURL`.
1556
1557 See `serialize` for information on how to customize the serialized form
1558 of a record.
1559
1560 @method updateRecord
1561 @param {DS.Store} store
1562 @param {subclass of DS.Model} type
1563 @param {DS.Model} record
1564 @return {Promise} promise
1565 */
1566 updateRecord: function(store, type, record) {
1567 var data = {};
1568 var serializer = store.serializerFor(type.typeKey);
1569
1570 serializer.serializeIntoHash(data, type, record);
1571
1572 var id = get(record, 'id');
1573
1574 return this.ajax(this.buildURL(type.typeKey, id, record), "PUT", { data: data });
1575 },
1576
1577 /**
1578 Called by the store when a record is deleted.
1579
1580 The `deleteRecord` method makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`.
1581
1582 @method deleteRecord
1583 @param {DS.Store} store
1584 @param {subclass of DS.Model} type
1585 @param {DS.Model} record
1586 @return {Promise} promise
1587 */
1588 deleteRecord: function(store, type, record) {
1589 var id = get(record, 'id');
1590
1591 return this.ajax(this.buildURL(type.typeKey, id, record), "DELETE");
1592 },
1593
1594 /**
1595 Builds a URL for a given type and optional ID.
1596
1597 By default, it pluralizes the type's name (for example, 'post'
1598 becomes 'posts' and 'person' becomes 'people'). To override the
1599 pluralization see [pathForType](#method_pathForType).
1600
1601 If an ID is specified, it adds the ID to the path generated
1602 for the type, separated by a `/`.
1603
1604 @method buildURL
1605 @param {String} type
1606 @param {String} id
1607 @param {DS.Model} record
1608 @return {String} url
1609 */
1610 buildURL: function(type, id, record) {
1611 var url = [],
1612 host = get(this, 'host'),
1613 prefix = this.urlPrefix();
1614
1615 if (type) { url.push(this.pathForType(type)); }
1616
1617 //We might get passed in an array of ids from findMany
1618 //in which case we don't want to modify the url, as the
1619 //ids will be passed in through a query param
1620 if (id && !Ember.isArray(id)) { url.push(encodeURIComponent(id)); }
1621
1622 if (prefix) { url.unshift(prefix); }
1623
1624 url = url.join('/');
1625 if (!host && url) { url = '/' + url; }
1626
1627 return url;
1628 },
1629
1630 /**
1631 @method urlPrefix
1632 @private
1633 @param {String} path
1634 @param {String} parentUrl
1635 @return {String} urlPrefix
1636 */
1637 urlPrefix: function(path, parentURL) {
1638 var host = get(this, 'host');
1639 var namespace = get(this, 'namespace');
1640 var url = [];
1641
1642 if (path) {
1643 // Absolute path
1644 if (path.charAt(0) === '/') {
1645 if (host) {
1646 path = path.slice(1);
1647 url.push(host);
1648 }
1649 // Relative path
1650 } else if (!/^http(s)?:\/\//.test(path)) {
1651 url.push(parentURL);
1652 }
1653 } else {
1654 if (host) { url.push(host); }
1655 if (namespace) { url.push(namespace); }
1656 }
1657
1658 if (path) {
1659 url.push(path);
1660 }
1661
1662 return url.join('/');
1663 },
1664
1665 _stripIDFromURL: function(store, record) {
1666 var type = store.modelFor(record);
1667 var url = this.buildURL(type.typeKey, record.get('id'), record);
1668
1669 var expandedURL = url.split('/');
1670 //Case when the url is of the format ...something/:id
1671 var lastSegment = expandedURL[ expandedURL.length - 1 ];
1672 var id = record.get('id');
1673 if (lastSegment === id) {
1674 expandedURL[expandedURL.length - 1] = "";
1675 } else if(endsWith(lastSegment, '?id=' + id)) {
1676 //Case when the url is of the format ...something?id=:id
1677 expandedURL[expandedURL.length - 1] = lastSegment.substring(0, lastSegment.length - id.length - 1);
1678 }
1679
1680 return expandedURL.join('/');
1681 },
1682
1683 /**
1684 Organize records into groups, each of which is to be passed to separate
1685 calls to `findMany`.
1686
1687 This implementation groups together records that have the same base URL but
1688 differing ids. For example `/comments/1` and `/comments/2` will be grouped together
1689 because we know findMany can coalesce them together as `/comments?ids[]=1&ids[]=2`
1690
1691 It also supports urls where ids are passed as a query param, such as `/comments?id=1`
1692 but not those where there is more than 1 query param such as `/comments?id=2&name=David`
1693 Currently only the query param of `id` is supported. If you need to support others, please
1694 override this or the `_stripIDFromURL` method.
1695
1696 It does not group records that have differing base urls, such as for example: `/posts/1/comments/2`
1697 and `/posts/2/comments/3`
1698
1699 @method groupRecordsForFindMany
1700 @param {Array} records
1701 @return {Array} an array of arrays of records, each of which is to be
1702 loaded separately by `findMany`.
1703 */
1704 groupRecordsForFindMany: function (store, records) {
1705 var groups = MapWithDefault.create({defaultValue: function(){return [];}});
1706 var adapter = this;
1707
1708 forEach.call(records, function(record){
1709 var baseUrl = adapter._stripIDFromURL(store, record);
1710 groups.get(baseUrl).push(record);
1711 });
1712
1713 function splitGroupToFitInUrl(group, maxUrlLength) {
1714 var baseUrl = adapter._stripIDFromURL(store, group[0]);
1715 var idsSize = 0;
1716 var splitGroups = [[]];
1717
1718 forEach.call(group, function(record) {
1719 var additionalLength = '&ids[]='.length + record.get('id.length');
1720 if (baseUrl.length + idsSize + additionalLength >= maxUrlLength) {
1721 idsSize = 0;
1722 splitGroups.push([]);
1723 }
1724
1725 idsSize += additionalLength;
1726
1727 var lastGroupIndex = splitGroups.length - 1;
1728 splitGroups[lastGroupIndex].push(record);
1729 });
1730
1731 return splitGroups;
1732 }
1733
1734 var groupsArray = [];
1735 groups.forEach(function(group, key){
1736 // http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
1737 var maxUrlLength = 2048;
1738 var splitGroups = splitGroupToFitInUrl(group, maxUrlLength);
1739
1740 forEach.call(splitGroups, function(splitGroup) {
1741 groupsArray.push(splitGroup);
1742 });
1743 });
1744
1745 return groupsArray;
1746 },
1747
1748 /**
1749 Determines the pathname for a given type.
1750
1751 By default, it pluralizes the type's name (for example,
1752 'post' becomes 'posts' and 'person' becomes 'people').
1753
1754 ### Pathname customization
1755
1756 For example if you have an object LineItem with an
1757 endpoint of "/line_items/".
1758
1759 ```js
1760 App.ApplicationAdapter = DS.RESTAdapter.extend({
1761 pathForType: function(type) {
1762 var decamelized = Ember.String.decamelize(type);
1763 return Ember.String.pluralize(decamelized);
1764 }
1765 });
1766 ```
1767
1768 @method pathForType
1769 @param {String} type
1770 @return {String} path
1771 **/
1772 pathForType: function(type) {
1773 var camelized = Ember.String.camelize(type);
1774 return Ember.String.pluralize(camelized);
1775 },
1776
1777 /**
1778 Takes an ajax response, and returns a relevant error.
1779
1780 Returning a `DS.InvalidError` from this method will cause the
1781 record to transition into the `invalid` state and make the
1782 `errors` object available on the record.
1783
1784 ```javascript
1785 App.ApplicationAdapter = DS.RESTAdapter.extend({
1786 ajaxError: function(jqXHR) {
1787 var error = this._super(jqXHR);
1788
1789 if (jqXHR && jqXHR.status === 422) {
1790 var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
1791
1792 return new DS.InvalidError(jsonErrors);
1793 } else {
1794 return error;
1795 }
1796 }
1797 });
1798 ```
1799
1800 Note: As a correctness optimization, the default implementation of
1801 the `ajaxError` method strips out the `then` method from jquery's
1802 ajax response (jqXHR). This is important because the jqXHR's
1803 `then` method fulfills the promise with itself resulting in a
1804 circular "thenable" chain which may cause problems for some
1805 promise libraries.
1806
1807 @method ajaxError
1808 @param {Object} jqXHR
1809 @param {Object} responseText
1810 @return {Object} jqXHR
1811 */
1812 ajaxError: function(jqXHR, responseText) {
1813 if (jqXHR && typeof jqXHR === 'object') {
1814 jqXHR.then = null;
1815 }
1816
1817 return jqXHR;
1818 },
1819
1820 /**
1821 Takes an ajax response, and returns the json payload.
1822
1823 By default this hook just returns the jsonPayload passed to it.
1824 You might want to override it in two cases:
1825
1826 1. Your API might return useful results in the request headers.
1827 If you need to access these, you can override this hook to copy them
1828 from jqXHR to the payload object so they can be processed in you serializer.
1829
1830
1831 2. Your API might return errors as successful responses with status code
1832 200 and an Errors text or object. You can return a DS.InvalidError from
1833 this hook and it will automatically reject the promise and put your record
1834 into the invald state.
1835
1836 @method ajaxError
1837 @param {Object} jqXHR
1838 @param {Object} jsonPayload
1839 @return {Object} jqXHR
1840 */
1841
1842 ajaxSuccess: function(jqXHR, jsonPayload) {
1843 return jsonPayload;
1844 },
1845
1846 /**
1847 Takes a URL, an HTTP method and a hash of data, and makes an
1848 HTTP request.
1849
1850 When the server responds with a payload, Ember Data will call into `extractSingle`
1851 or `extractArray` (depending on whether the original query was for one record or
1852 many records).
1853
1854 By default, `ajax` method has the following behavior:
1855
1856 * It sets the response `dataType` to `"json"`
1857 * If the HTTP method is not `"GET"`, it sets the `Content-Type` to be
1858 `application/json; charset=utf-8`
1859 * If the HTTP method is not `"GET"`, it stringifies the data passed in. The
1860 data is the serialized record in the case of a save.
1861 * Registers success and failure handlers.
1862
1863 @method ajax
1864 @private
1865 @param {String} url
1866 @param {String} type The request type GET, POST, PUT, DELETE etc.
1867 @param {Object} hash
1868 @return {Promise} promise
1869 */
1870 ajax: function(url, type, options) {
1871 var adapter = this;
1872
1873 return new Ember.RSVP.Promise(function(resolve, reject) {
1874 var hash = adapter.ajaxOptions(url, type, options);
1875
1876 hash.success = function(json, textStatus, jqXHR) {
1877 json = adapter.ajaxSuccess(jqXHR, json);
1878 if (json instanceof InvalidError) {
1879 Ember.run(null, reject, json);
1880 } else {
1881 Ember.run(null, resolve, json);
1882 }
1883 };
1884
1885 hash.error = function(jqXHR, textStatus, errorThrown) {
1886 Ember.run(null, reject, adapter.ajaxError(jqXHR, jqXHR.responseText));
1887 };
1888
1889 Ember.$.ajax(hash);
1890 }, "DS: RESTAdapter#ajax " + type + " to " + url);
1891 },
1892
1893 /**
1894 @method ajaxOptions
1895 @private
1896 @param {String} url
1897 @param {String} type The request type GET, POST, PUT, DELETE etc.
1898 @param {Object} hash
1899 @return {Object} hash
1900 */
1901 ajaxOptions: function(url, type, options) {
1902 var hash = options || {};
1903 hash.url = url;
1904 hash.type = type;
1905 hash.dataType = 'json';
1906 hash.context = this;
1907
1908 if (hash.data && type !== 'GET') {
1909 hash.contentType = 'application/json; charset=utf-8';
1910 hash.data = JSON.stringify(hash.data);
1911 }
1912
1913 var headers = get(this, 'headers');
1914 if (headers !== undefined) {
1915 hash.beforeSend = function (xhr) {
1916 forEach.call(Ember.keys(headers), function(key) {
1917 xhr.setRequestHeader(key, headers[key]);
1918 });
1919 };
1920 }
1921
1922 return hash;
1923 }
1924 });
1925
1926 //From http://stackoverflow.com/questions/280634/endswith-in-javascript
1927 function endsWith(string, suffix){
1928 if (typeof String.prototype.endsWith !== 'function') {
1929 return string.indexOf(suffix, string.length - suffix.length) !== -1;
1930 } else {
1931 return string.endsWith(suffix);
1932 }
1933 }
1934 });
1935define("ember-data/core",
1936 ["exports"],
1937 function(__exports__) {
1938 "use strict";
1939 /**
1940 @module ember-data
1941 */
1942
1943 /**
1944 All Ember Data methods and functions are defined inside of this namespace.
1945
1946 @class DS
1947 @static
1948 */
1949 var DS;
1950 if ('undefined' === typeof DS) {
1951 /**
1952 @property VERSION
1953 @type String
1954 @default '1.0.0-beta.11'
1955 @static
1956 */
1957 DS = Ember.Namespace.create({
1958 VERSION: '1.0.0-beta.11'
1959 });
1960
1961 if (Ember.libraries) {
1962 Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
1963 }
1964 }
1965
1966 __exports__["default"] = DS;
1967 });
1968define("ember-data/ember-initializer",
1969 ["ember-data/setup-container"],
1970 function(__dependency1__) {
1971 "use strict";
1972 var setupContainer = __dependency1__["default"];
1973
1974 var K = Ember.K;
1975
1976 /**
1977 @module ember-data
1978 */
1979
1980 /*
1981
1982 This code initializes Ember-Data onto an Ember application.
1983
1984 If an Ember.js developer defines a subclass of DS.Store on their application,
1985 as `App.ApplicationStore` (or via a module system that resolves to `store:application`)
1986 this code will automatically instantiate it and make it available on the
1987 router.
1988
1989 Additionally, after an application's controllers have been injected, they will
1990 each have the store made available to them.
1991
1992 For example, imagine an Ember.js application with the following classes:
1993
1994 App.ApplicationStore = DS.Store.extend({
1995 adapter: 'custom'
1996 });
1997
1998 App.PostsController = Ember.ArrayController.extend({
1999 // ...
2000 });
2001
2002 When the application is initialized, `App.ApplicationStore` will automatically be
2003 instantiated, and the instance of `App.PostsController` will have its `store`
2004 property set to that instance.
2005
2006 Note that this code will only be run if the `ember-application` package is
2007 loaded. If Ember Data is being used in an environment other than a
2008 typical application (e.g., node.js where only `ember-runtime` is available),
2009 this code will be ignored.
2010 */
2011
2012 Ember.onLoad('Ember.Application', function(Application) {
2013
2014 Application.initializer({
2015 name: "ember-data",
2016 initialize: setupContainer
2017 });
2018
2019 // Deprecated initializers to satisfy old code that depended on them
2020
2021 Application.initializer({
2022 name: "store",
2023 after: "ember-data",
2024 initialize: K
2025 });
2026
2027 Application.initializer({
2028 name: "activeModelAdapter",
2029 before: "store",
2030 initialize: K
2031 });
2032
2033 Application.initializer({
2034 name: "transforms",
2035 before: "store",
2036 initialize: K
2037 });
2038
2039 Application.initializer({
2040 name: "data-adapter",
2041 before: "store",
2042 initialize: K
2043 });
2044
2045 Application.initializer({
2046 name: "injectStore",
2047 before: "store",
2048 initialize: K
2049 });
2050 });
2051 });
2052define("ember-data/ext/date",
2053 [],
2054 function() {
2055 "use strict";
2056 /**
2057 @module ember-data
2058 */
2059
2060 /**
2061 Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
2062
2063 © 2011 Colin Snover <http://zetafleet.com>
2064
2065 Released under MIT license.
2066
2067 @class Date
2068 @namespace Ember
2069 @static
2070 */
2071 Ember.Date = Ember.Date || {};
2072
2073 var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
2074
2075 /**
2076 @method parse
2077 @param {Date} date
2078 @return {Number} timestamp
2079 */
2080 Ember.Date.parse = function (date) {
2081 var timestamp, struct, minutesOffset = 0;
2082
2083 // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
2084 // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
2085 // implementations could be faster
2086 // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
2087 if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
2088 // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
2089 for (var i = 0, k; (k = numericKeys[i]); ++i) {
2090 struct[k] = +struct[k] || 0;
2091 }
2092
2093 // allow undefined days and months
2094 struct[2] = (+struct[2] || 1) - 1;
2095 struct[3] = +struct[3] || 1;
2096
2097 if (struct[8] !== 'Z' && struct[9] !== undefined) {
2098 minutesOffset = struct[10] * 60 + struct[11];
2099
2100 if (struct[9] === '+') {
2101 minutesOffset = 0 - minutesOffset;
2102 }
2103 }
2104
2105 timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
2106 }
2107 else {
2108 timestamp = origParse ? origParse(date) : NaN;
2109 }
2110
2111 return timestamp;
2112 };
2113
2114 if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) {
2115 Date.parse = Ember.Date.parse;
2116 }
2117 });
2118define("ember-data/initializers/data_adapter",
2119 ["ember-data/system/debug/debug_adapter","exports"],
2120 function(__dependency1__, __exports__) {
2121 "use strict";
2122 var DebugAdapter = __dependency1__["default"];
2123
2124 /**
2125 Configures a container with injections on Ember applications
2126 for the Ember-Data store. Accepts an optional namespace argument.
2127
2128 @method initializeStoreInjections
2129 @param {Ember.Container} container
2130 */
2131 __exports__["default"] = function initializeDebugAdapter(container){
2132 container.register('data-adapter:main', DebugAdapter);
2133 };
2134 });
2135define("ember-data/initializers/store",
2136 ["ember-data/serializers","ember-data/adapters","ember-data/system/container_proxy","ember-data/system/store","exports"],
2137 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
2138 "use strict";
2139 var JSONSerializer = __dependency1__.JSONSerializer;
2140 var RESTSerializer = __dependency1__.RESTSerializer;
2141 var RESTAdapter = __dependency2__.RESTAdapter;
2142 var ContainerProxy = __dependency3__["default"];
2143 var Store = __dependency4__["default"];
2144
2145 /**
2146 Configures a container for use with an Ember-Data
2147 store. Accepts an optional namespace argument.
2148
2149 @method initializeStore
2150 @param {Ember.Container} container
2151 @param {Object} [application] an application namespace
2152 */
2153 __exports__["default"] = function initializeStore(container, application){
2154 Ember.deprecate('Specifying a custom Store for Ember Data on your global namespace as `App.Store` ' +
2155 'has been deprecated. Please use `App.ApplicationStore` instead.', !(application && application.Store));
2156
2157 container.register('store:main', container.lookupFactory('store:application') || (application && application.Store) || Store);
2158
2159 // allow older names to be looked up
2160
2161 var proxy = new ContainerProxy(container);
2162 proxy.registerDeprecations([
2163 { deprecated: 'serializer:_default', valid: 'serializer:-default' },
2164 { deprecated: 'serializer:_rest', valid: 'serializer:-rest' },
2165 { deprecated: 'adapter:_rest', valid: 'adapter:-rest' }
2166 ]);
2167
2168 // new go forward paths
2169 container.register('serializer:-default', JSONSerializer);
2170 container.register('serializer:-rest', RESTSerializer);
2171 container.register('adapter:-rest', RESTAdapter);
2172
2173 // Eagerly generate the store so defaultStore is populated.
2174 // TODO: Do this in a finisher hook
2175 container.lookup('store:main');
2176 };
2177 });
2178define("ember-data/initializers/store_injections",
2179 ["exports"],
2180 function(__exports__) {
2181 "use strict";
2182 /**
2183 Configures a container with injections on Ember applications
2184 for the Ember-Data store. Accepts an optional namespace argument.
2185
2186 @method initializeStoreInjections
2187 @param {Ember.Container} container
2188 */
2189 __exports__["default"] = function initializeStoreInjections(container){
2190 container.injection('controller', 'store', 'store:main');
2191 container.injection('route', 'store', 'store:main');
2192 container.injection('serializer', 'store', 'store:main');
2193 container.injection('data-adapter', 'store', 'store:main');
2194 };
2195 });
2196define("ember-data/initializers/transforms",
2197 ["ember-data/transforms","exports"],
2198 function(__dependency1__, __exports__) {
2199 "use strict";
2200 var BooleanTransform = __dependency1__.BooleanTransform;
2201 var DateTransform = __dependency1__.DateTransform;
2202 var StringTransform = __dependency1__.StringTransform;
2203 var NumberTransform = __dependency1__.NumberTransform;
2204
2205 /**
2206 Configures a container for use with Ember-Data
2207 transforms.
2208
2209 @method initializeTransforms
2210 @param {Ember.Container} container
2211 */
2212 __exports__["default"] = function initializeTransforms(container){
2213 container.register('transform:boolean', BooleanTransform);
2214 container.register('transform:date', DateTransform);
2215 container.register('transform:number', NumberTransform);
2216 container.register('transform:string', StringTransform);
2217 };
2218 });
2219define("ember-data/serializers",
2220 ["ember-data/serializers/json_serializer","ember-data/serializers/rest_serializer","exports"],
2221 function(__dependency1__, __dependency2__, __exports__) {
2222 "use strict";
2223 var JSONSerializer = __dependency1__["default"];
2224 var RESTSerializer = __dependency2__["default"];
2225
2226 __exports__.JSONSerializer = JSONSerializer;
2227 __exports__.RESTSerializer = RESTSerializer;
2228 });
2229define("ember-data/serializers/embedded_records_mixin",
2230 ["exports"],
2231 function(__exports__) {
2232 "use strict";
2233 var get = Ember.get;
2234 var forEach = Ember.EnumerableUtils.forEach;
2235 var camelize = Ember.String.camelize;
2236
2237 /**
2238 ## Using Embedded Records
2239
2240 `DS.EmbeddedRecordsMixin` supports serializing embedded records.
2241
2242 To set up embedded records, include the mixin when extending a serializer
2243 then define and configure embedded (model) relationships.
2244
2245 Below is an example of a per-type serializer ('post' type).
2246
2247 ```js
2248 App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
2249 attrs: {
2250 author: { embedded: 'always' },
2251 comments: { serialize: 'ids' }
2252 }
2253 });
2254 ```
2255 Note that this use of `{ embedded: 'always' }` is unrelated to
2256 the `{ embedded: 'always' }` that is defined as an option on `DS.attr` as part of
2257 defining a model while working with the ActiveModelSerializer. Nevertheless,
2258 using `{ embedded: 'always' }` as an option to DS.attr is not a valid way to setup
2259 embedded records.
2260
2261 The `attrs` option for a resource `{ embedded: 'always' }` is shorthand for:
2262
2263 ```js
2264 {
2265 serialize: 'records',
2266 deserialize: 'records'
2267 }
2268 ```
2269
2270 ### Configuring Attrs
2271
2272 A resource's `attrs` option may be set to use `ids`, `records` or false for the
2273 `serialize` and `deserialize` settings.
2274
2275 The `attrs` property can be set on the ApplicationSerializer or a per-type
2276 serializer.
2277
2278 In the case where embedded JSON is expected while extracting a payload (reading)
2279 the setting is `deserialize: 'records'`, there is no need to use `ids` when
2280 extracting as that is the default behavior without this mixin if you are using
2281 the vanilla EmbeddedRecordsMixin. Likewise, to embed JSON in the payload while
2282 serializing `serialize: 'records'` is the setting to use. There is an option of
2283 not embedding JSON in the serialized payload by using `serialize: 'ids'`. If you
2284 do not want the relationship sent at all, you can use `serialize: false`.
2285
2286
2287 ### EmbeddedRecordsMixin defaults
2288 If you do not overwrite `attrs` for a specific relationship, the `EmbeddedRecordsMixin`
2289 will behave in the following way:
2290
2291 BelongsTo: `{ serialize: 'id', deserialize: 'id' }`
2292 HasMany: `{ serialize: false, deserialize: 'ids' }`
2293
2294 ### Model Relationships
2295
2296 Embedded records must have a model defined to be extracted and serialized. Note that
2297 when defining any relationships on your model such as `belongsTo` and `hasMany`, you
2298 should not both specify `async:true` and also indicate through the serializer's
2299 `attrs` attribute that the related model should be embedded. If a model is
2300 declared embedded, then do not use `async:true`.
2301
2302 To successfully extract and serialize embedded records the model relationships
2303 must be setup correcty See the
2304 [defining relationships](/guides/models/defining-models/#toc_defining-relationships)
2305 section of the **Defining Models** guide page.
2306
2307 Records without an `id` property are not considered embedded records, model
2308 instances must have an `id` property to be used with Ember Data.
2309
2310 ### Example JSON payloads, Models and Serializers
2311
2312 **When customizing a serializer it is important to grok what the customizations
2313 are. Please read the docs for the methods this mixin provides, in case you need
2314 to modify it to fit your specific needs.**
2315
2316 For example review the docs for each method of this mixin:
2317 * [normalize](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_normalize)
2318 * [serializeBelongsTo](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeBelongsTo)
2319 * [serializeHasMany](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeHasMany)
2320
2321 @class EmbeddedRecordsMixin
2322 @namespace DS
2323 */
2324 var EmbeddedRecordsMixin = Ember.Mixin.create({
2325
2326 /**
2327 Normalize the record and recursively normalize/extract all the embedded records
2328 while pushing them into the store as they are encountered
2329
2330 A payload with an attr configured for embedded records needs to be extracted:
2331
2332 ```js
2333 {
2334 "post": {
2335 "id": "1"
2336 "title": "Rails is omakase",
2337 "comments": [{
2338 "id": "1",
2339 "body": "Rails is unagi"
2340 }, {
2341 "id": "2",
2342 "body": "Omakase O_o"
2343 }]
2344 }
2345 }
2346 ```
2347 @method normalize
2348 @param {subclass of DS.Model} type
2349 @param {Object} hash to be normalized
2350 @param {String} key the hash has been referenced by
2351 @return {Object} the normalized hash
2352 **/
2353 normalize: function(type, hash, prop) {
2354 var normalizedHash = this._super(type, hash, prop);
2355 return extractEmbeddedRecords(this, this.store, type, normalizedHash);
2356 },
2357
2358 keyForRelationship: function(key, type){
2359 if (this.hasDeserializeRecordsOption(key)) {
2360 return this.keyForAttribute(key);
2361 } else {
2362 return this._super(key, type) || key;
2363 }
2364 },
2365
2366 /**
2367 Serialize `belongsTo` relationship when it is configured as an embedded object.
2368
2369 This example of an author model belongs to a post model:
2370
2371 ```js
2372 Post = DS.Model.extend({
2373 title: DS.attr('string'),
2374 body: DS.attr('string'),
2375 author: DS.belongsTo('author')
2376 });
2377
2378 Author = DS.Model.extend({
2379 name: DS.attr('string'),
2380 post: DS.belongsTo('post')
2381 });
2382 ```
2383
2384 Use a custom (type) serializer for the post model to configure embedded author
2385
2386 ```js
2387 App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
2388 attrs: {
2389 author: {embedded: 'always'}
2390 }
2391 })
2392 ```
2393
2394 A payload with an attribute configured for embedded records can serialize
2395 the records together under the root attribute's payload:
2396
2397 ```js
2398 {
2399 "post": {
2400 "id": "1"
2401 "title": "Rails is omakase",
2402 "author": {
2403 "id": "2"
2404 "name": "dhh"
2405 }
2406 }
2407 }
2408 ```
2409
2410 @method serializeBelongsTo
2411 @param {DS.Model} record
2412 @param {Object} json
2413 @param {Object} relationship
2414 */
2415 serializeBelongsTo: function(record, json, relationship) {
2416 var attr = relationship.key;
2417 if (this.noSerializeOptionSpecified(attr)) {
2418 this._super(record, json, relationship);
2419 return;
2420 }
2421 var includeIds = this.hasSerializeIdsOption(attr);
2422 var includeRecords = this.hasSerializeRecordsOption(attr);
2423 var embeddedRecord = record.get(attr);
2424 var key;
2425 if (includeIds) {
2426 key = this.keyForRelationship(attr, relationship.kind);
2427 if (!embeddedRecord) {
2428 json[key] = null;
2429 } else {
2430 json[key] = get(embeddedRecord, 'id');
2431 }
2432 } else if (includeRecords) {
2433 key = this.keyForAttribute(attr);
2434 if (!embeddedRecord) {
2435 json[key] = null;
2436 } else {
2437 json[key] = embeddedRecord.serialize({includeId: true});
2438 this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, json[key]);
2439 }
2440 }
2441 },
2442
2443 /**
2444 Serialize `hasMany` relationship when it is configured as embedded objects.
2445
2446 This example of a post model has many comments:
2447
2448 ```js
2449 Post = DS.Model.extend({
2450 title: DS.attr('string'),
2451 body: DS.attr('string'),
2452 comments: DS.hasMany('comment')
2453 });
2454
2455 Comment = DS.Model.extend({
2456 body: DS.attr('string'),
2457 post: DS.belongsTo('post')
2458 });
2459 ```
2460
2461 Use a custom (type) serializer for the post model to configure embedded comments
2462
2463 ```js
2464 App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
2465 attrs: {
2466 comments: {embedded: 'always'}
2467 }
2468 })
2469 ```
2470
2471 A payload with an attribute configured for embedded records can serialize
2472 the records together under the root attribute's payload:
2473
2474 ```js
2475 {
2476 "post": {
2477 "id": "1"
2478 "title": "Rails is omakase",
2479 "body": "I want this for my ORM, I want that for my template language..."
2480 "comments": [{
2481 "id": "1",
2482 "body": "Rails is unagi"
2483 }, {
2484 "id": "2",
2485 "body": "Omakase O_o"
2486 }]
2487 }
2488 }
2489 ```
2490
2491 The attrs options object can use more specific instruction for extracting and
2492 serializing. When serializing, an option to embed `ids` or `records` can be set.
2493 When extracting the only option is `records`.
2494
2495 So `{embedded: 'always'}` is shorthand for:
2496 `{serialize: 'records', deserialize: 'records'}`
2497
2498 To embed the `ids` for a related object (using a hasMany relationship):
2499
2500 ```js
2501 App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
2502 attrs: {
2503 comments: {serialize: 'ids', deserialize: 'records'}
2504 }
2505 })
2506 ```
2507
2508 ```js
2509 {
2510 "post": {
2511 "id": "1"
2512 "title": "Rails is omakase",
2513 "body": "I want this for my ORM, I want that for my template language..."
2514 "comments": ["1", "2"]
2515 }
2516 }
2517 ```
2518
2519 @method serializeHasMany
2520 @param {DS.Model} record
2521 @param {Object} json
2522 @param {Object} relationship
2523 */
2524 serializeHasMany: function(record, json, relationship) {
2525 var attr = relationship.key;
2526 if (this.noSerializeOptionSpecified(attr)) {
2527 this._super(record, json, relationship);
2528 return;
2529 }
2530 var includeIds = this.hasSerializeIdsOption(attr);
2531 var includeRecords = this.hasSerializeRecordsOption(attr);
2532 var key;
2533 if (includeIds) {
2534 key = this.keyForRelationship(attr, relationship.kind);
2535 json[key] = get(record, attr).mapBy('id');
2536 } else if (includeRecords) {
2537 key = this.keyForAttribute(attr);
2538 json[key] = get(record, attr).map(function(embeddedRecord) {
2539 var serializedEmbeddedRecord = embeddedRecord.serialize({includeId: true});
2540 this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, serializedEmbeddedRecord);
2541 return serializedEmbeddedRecord;
2542 }, this);
2543 }
2544 },
2545
2546 /**
2547 When serializing an embedded record, modify the property (in the json payload)
2548 that refers to the parent record (foreign key for relationship).
2549
2550 Serializing a `belongsTo` relationship removes the property that refers to the
2551 parent record
2552
2553 Serializing a `hasMany` relationship does not remove the property that refers to
2554 the parent record.
2555
2556 @method removeEmbeddedForeignKey
2557 @param {DS.Model} record
2558 @param {DS.Model} embeddedRecord
2559 @param {Object} relationship
2560 @param {Object} json
2561 */
2562 removeEmbeddedForeignKey: function (record, embeddedRecord, relationship, json) {
2563 if (relationship.kind === 'hasMany') {
2564 return;
2565 } else if (relationship.kind === 'belongsTo') {
2566 var parentRecord = record.constructor.inverseFor(relationship.key);
2567 if (parentRecord) {
2568 var name = parentRecord.name;
2569 var embeddedSerializer = this.store.serializerFor(embeddedRecord.constructor);
2570 var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind);
2571 if (parentKey) {
2572 delete json[parentKey];
2573 }
2574 }
2575 }
2576 },
2577
2578 // checks config for attrs option to embedded (always) - serialize and deserialize
2579 hasEmbeddedAlwaysOption: function (attr) {
2580 var option = this.attrsOption(attr);
2581 return option && option.embedded === 'always';
2582 },
2583
2584 // checks config for attrs option to serialize ids
2585 hasSerializeRecordsOption: function(attr) {
2586 var alwaysEmbed = this.hasEmbeddedAlwaysOption(attr);
2587 var option = this.attrsOption(attr);
2588 return alwaysEmbed || (option && (option.serialize === 'records'));
2589 },
2590
2591 // checks config for attrs option to serialize records
2592 hasSerializeIdsOption: function(attr) {
2593 var option = this.attrsOption(attr);
2594 return option && (option.serialize === 'ids' || option.serialize === 'id');
2595 },
2596
2597 // checks config for attrs option to serialize records
2598 noSerializeOptionSpecified: function(attr) {
2599 var option = this.attrsOption(attr);
2600 return !(option && (option.serialize || option.embedded));
2601 },
2602
2603 // checks config for attrs option to deserialize records
2604 // a defined option object for a resource is treated the same as
2605 // `deserialize: 'records'`
2606 hasDeserializeRecordsOption: function(attr) {
2607 var alwaysEmbed = this.hasEmbeddedAlwaysOption(attr);
2608 var option = this.attrsOption(attr);
2609 return alwaysEmbed || (option && option.deserialize === 'records');
2610 },
2611
2612 attrsOption: function(attr) {
2613 var attrs = this.get('attrs');
2614 return attrs && (attrs[camelize(attr)] || attrs[attr]);
2615 }
2616 });
2617
2618 // chooses a relationship kind to branch which function is used to update payload
2619 // does not change payload if attr is not embedded
2620 function extractEmbeddedRecords(serializer, store, type, partial) {
2621
2622 type.eachRelationship(function(key, relationship) {
2623 if (serializer.hasDeserializeRecordsOption(key)) {
2624 var embeddedType = store.modelFor(relationship.type.typeKey);
2625 if (relationship.kind === "hasMany") {
2626 if (relationship.options.polymorphic) {
2627 extractEmbeddedHasManyPolymorphic(store, key, partial);
2628 }
2629 else {
2630 extractEmbeddedHasMany(store, key, embeddedType, partial);
2631 }
2632 }
2633 if (relationship.kind === "belongsTo") {
2634 extractEmbeddedBelongsTo(store, key, embeddedType, partial);
2635 }
2636 }
2637 });
2638
2639 return partial;
2640 }
2641
2642 // handles embedding for `hasMany` relationship
2643 function extractEmbeddedHasMany(store, key, embeddedType, hash) {
2644 if (!hash[key]) {
2645 return hash;
2646 }
2647
2648 var ids = [];
2649
2650 var embeddedSerializer = store.serializerFor(embeddedType.typeKey);
2651 forEach(hash[key], function(data) {
2652 var embeddedRecord = embeddedSerializer.normalize(embeddedType, data, null);
2653 store.push(embeddedType, embeddedRecord);
2654 ids.push(embeddedRecord.id);
2655 });
2656
2657 hash[key] = ids;
2658 return hash;
2659 }
2660
2661 function extractEmbeddedHasManyPolymorphic(store, key, hash) {
2662 if (!hash[key]) {
2663 return hash;
2664 }
2665
2666 var ids = [];
2667
2668 forEach(hash[key], function(data) {
2669 var typeKey = data.type;
2670 var embeddedSerializer = store.serializerFor(typeKey);
2671 var embeddedType = store.modelFor(typeKey);
2672 var primaryKey = get(embeddedSerializer, 'primaryKey');
2673
2674 var embeddedRecord = embeddedSerializer.normalize(embeddedType, data, null);
2675 store.push(embeddedType, embeddedRecord);
2676 ids.push({ id: embeddedRecord[primaryKey], type: typeKey });
2677 });
2678
2679 hash[key] = ids;
2680 return hash;
2681 }
2682
2683 function extractEmbeddedBelongsTo(store, key, embeddedType, hash) {
2684 if (!hash[key]) {
2685 return hash;
2686 }
2687
2688 var embeddedSerializer = store.serializerFor(embeddedType.typeKey);
2689 var embeddedRecord = embeddedSerializer.normalize(embeddedType, hash[key], null);
2690 store.push(embeddedType, embeddedRecord);
2691
2692 hash[key] = embeddedRecord.id;
2693 //TODO Need to add a reference to the parent later so relationship works between both `belongsTo` records
2694 return hash;
2695 }
2696
2697 __exports__["default"] = EmbeddedRecordsMixin;
2698 });
2699define("ember-data/serializers/json_serializer",
2700 ["exports"],
2701 function(__exports__) {
2702 "use strict";
2703 var get = Ember.get;
2704 var isNone = Ember.isNone;
2705 var map = Ember.ArrayPolyfills.map;
2706 var merge = Ember.merge;
2707
2708 /**
2709 In Ember Data a Serializer is used to serialize and deserialize
2710 records when they are transferred in and out of an external source.
2711 This process involves normalizing property names, transforming
2712 attribute values and serializing relationships.
2713
2714 For maximum performance Ember Data recommends you use the
2715 [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses.
2716
2717 `JSONSerializer` is useful for simpler or legacy backends that may
2718 not support the http://jsonapi.org/ spec.
2719
2720 @class JSONSerializer
2721 @namespace DS
2722 */
2723 __exports__["default"] = Ember.Object.extend({
2724 /**
2725 The primaryKey is used when serializing and deserializing
2726 data. Ember Data always uses the `id` property to store the id of
2727 the record. The external source may not always follow this
2728 convention. In these cases it is useful to override the
2729 primaryKey property to match the primaryKey of your external
2730 store.
2731
2732 Example
2733
2734 ```javascript
2735 App.ApplicationSerializer = DS.JSONSerializer.extend({
2736 primaryKey: '_id'
2737 });
2738 ```
2739
2740 @property primaryKey
2741 @type {String}
2742 @default 'id'
2743 */
2744 primaryKey: 'id',
2745
2746 /**
2747 The `attrs` object can be used to declare a simple mapping between
2748 property names on `DS.Model` records and payload keys in the
2749 serialized JSON object representing the record. An object with the
2750 property `key` can also be used to designate the attribute's key on
2751 the response payload.
2752
2753 Example
2754
2755 ```javascript
2756 App.Person = DS.Model.extend({
2757 firstName: DS.attr('string'),
2758 lastName: DS.attr('string'),
2759 occupation: DS.attr('string'),
2760 admin: DS.attr('boolean')
2761 });
2762
2763 App.PersonSerializer = DS.JSONSerializer.extend({
2764 attrs: {
2765 admin: 'is_admin',
2766 occupation: {key: 'career'}
2767 }
2768 });
2769 ```
2770
2771 You can also remove attributes by setting the `serialize` key to
2772 false in your mapping object.
2773
2774 Example
2775
2776 ```javascript
2777 App.PersonSerializer = DS.JSONSerializer.extend({
2778 attrs: {
2779 admin: {serialize: false},
2780 occupation: {key: 'career'}
2781 }
2782 });
2783 ```
2784
2785 When serialized:
2786
2787 ```javascript
2788 {
2789 "career": "magician"
2790 }
2791 ```
2792
2793 Note that the `admin` is now not included in the payload.
2794
2795 @property attrs
2796 @type {Object}
2797 */
2798
2799 /**
2800 Given a subclass of `DS.Model` and a JSON object this method will
2801 iterate through each attribute of the `DS.Model` and invoke the
2802 `DS.Transform#deserialize` method on the matching property of the
2803 JSON object. This method is typically called after the
2804 serializer's `normalize` method.
2805
2806 @method applyTransforms
2807 @private
2808 @param {subclass of DS.Model} type
2809 @param {Object} data The data to transform
2810 @return {Object} data The transformed data object
2811 */
2812 applyTransforms: function(type, data) {
2813 type.eachTransformedAttribute(function applyTransform(key, type) {
2814 if (!data.hasOwnProperty(key)) { return; }
2815
2816 var transform = this.transformFor(type);
2817 data[key] = transform.deserialize(data[key]);
2818 }, this);
2819
2820 return data;
2821 },
2822
2823 /**
2824 Normalizes a part of the JSON payload returned by
2825 the server. You should override this method, munge the hash
2826 and call super if you have generic normalization to do.
2827
2828 It takes the type of the record that is being normalized
2829 (as a DS.Model class), the property where the hash was
2830 originally found, and the hash to normalize.
2831
2832 You can use this method, for example, to normalize underscored keys to camelized
2833 or other general-purpose normalizations.
2834
2835 Example
2836
2837 ```javascript
2838 App.ApplicationSerializer = DS.JSONSerializer.extend({
2839 normalize: function(type, hash) {
2840 var fields = Ember.get(type, 'fields');
2841 fields.forEach(function(field) {
2842 var payloadField = Ember.String.underscore(field);
2843 if (field === payloadField) { return; }
2844
2845 hash[field] = hash[payloadField];
2846 delete hash[payloadField];
2847 });
2848 return this._super.apply(this, arguments);
2849 }
2850 });
2851 ```
2852
2853 @method normalize
2854 @param {subclass of DS.Model} type
2855 @param {Object} hash
2856 @return {Object}
2857 */
2858 normalize: function(type, hash) {
2859 if (!hash) { return hash; }
2860
2861 this.normalizeId(hash);
2862 this.normalizeAttributes(type, hash);
2863 this.normalizeRelationships(type, hash);
2864
2865 this.normalizeUsingDeclaredMapping(type, hash);
2866 this.applyTransforms(type, hash);
2867 return hash;
2868 },
2869
2870 /**
2871 You can use this method to normalize all payloads, regardless of whether they
2872 represent single records or an array.
2873
2874 For example, you might want to remove some extraneous data from the payload:
2875
2876 ```js
2877 App.ApplicationSerializer = DS.JSONSerializer.extend({
2878 normalizePayload: function(payload) {
2879 delete payload.version;
2880 delete payload.status;
2881 return payload;
2882 }
2883 });
2884 ```
2885
2886 @method normalizePayload
2887 @param {Object} payload
2888 @return {Object} the normalized payload
2889 */
2890 normalizePayload: function(payload) {
2891 return payload;
2892 },
2893
2894 /**
2895 @method normalizeAttributes
2896 @private
2897 */
2898 normalizeAttributes: function(type, hash) {
2899 var payloadKey;
2900
2901 if (this.keyForAttribute) {
2902 type.eachAttribute(function(key) {
2903 payloadKey = this.keyForAttribute(key);
2904 if (key === payloadKey) { return; }
2905 if (!hash.hasOwnProperty(payloadKey)) { return; }
2906
2907 hash[key] = hash[payloadKey];
2908 delete hash[payloadKey];
2909 }, this);
2910 }
2911 },
2912
2913 /**
2914 @method normalizeRelationships
2915 @private
2916 */
2917 normalizeRelationships: function(type, hash) {
2918 var payloadKey;
2919
2920 if (this.keyForRelationship) {
2921 type.eachRelationship(function(key, relationship) {
2922 payloadKey = this.keyForRelationship(key, relationship.kind);
2923 if (key === payloadKey) { return; }
2924 if (!hash.hasOwnProperty(payloadKey)) { return; }
2925
2926 hash[key] = hash[payloadKey];
2927 delete hash[payloadKey];
2928 }, this);
2929 }
2930 },
2931
2932 /**
2933 @method normalizeUsingDeclaredMapping
2934 @private
2935 */
2936 normalizeUsingDeclaredMapping: function(type, hash) {
2937 var attrs = get(this, 'attrs'), payloadKey, key;
2938
2939 if (attrs) {
2940 for (key in attrs) {
2941 payloadKey = this._getMappedKey(key);
2942 if (!hash.hasOwnProperty(payloadKey)) { continue; }
2943
2944 if (payloadKey !== key) {
2945 hash[key] = hash[payloadKey];
2946 delete hash[payloadKey];
2947 }
2948 }
2949 }
2950 },
2951
2952 /**
2953 @method normalizeId
2954 @private
2955 */
2956 normalizeId: function(hash) {
2957 var primaryKey = get(this, 'primaryKey');
2958
2959 if (primaryKey === 'id') { return; }
2960
2961 hash.id = hash[primaryKey];
2962 delete hash[primaryKey];
2963 },
2964
2965 /**
2966 Looks up the property key that was set by the custom `attr` mapping
2967 passed to the serializer.
2968
2969 @method _getMappedKey
2970 @private
2971 @param {String} key
2972 @return {String} key
2973 */
2974 _getMappedKey: function(key) {
2975 var attrs = get(this, 'attrs');
2976 var mappedKey;
2977 if (attrs && attrs[key]) {
2978 mappedKey = attrs[key];
2979 //We need to account for both the {title: 'post_title'} and
2980 //{title: {key: 'post_title'}} forms
2981 if (mappedKey.key){
2982 mappedKey = mappedKey.key;
2983 }
2984 if (typeof mappedKey === 'string'){
2985 key = mappedKey;
2986 }
2987 }
2988
2989 return key;
2990 },
2991
2992 /**
2993 Check attrs.key.serialize property to inform if the `key`
2994 can be serialized
2995
2996 @method _canSerialize
2997 @private
2998 @param {String} key
2999 @return {boolean} true if the key can be serialized
3000 */
3001 _canSerialize: function(key) {
3002 var attrs = get(this, 'attrs');
3003
3004 return !attrs || !attrs[key] || attrs[key].serialize !== false;
3005 },
3006
3007 // SERIALIZE
3008 /**
3009 Called when a record is saved in order to convert the
3010 record into JSON.
3011
3012 By default, it creates a JSON object with a key for
3013 each attribute and belongsTo relationship.
3014
3015 For example, consider this model:
3016
3017 ```javascript
3018 App.Comment = DS.Model.extend({
3019 title: DS.attr(),
3020 body: DS.attr(),
3021
3022 author: DS.belongsTo('user')
3023 });
3024 ```
3025
3026 The default serialization would create a JSON object like:
3027
3028 ```javascript
3029 {
3030 "title": "Rails is unagi",
3031 "body": "Rails? Omakase? O_O",
3032 "author": 12
3033 }
3034 ```
3035
3036 By default, attributes are passed through as-is, unless
3037 you specified an attribute type (`DS.attr('date')`). If
3038 you specify a transform, the JavaScript value will be
3039 serialized when inserted into the JSON hash.
3040
3041 By default, belongs-to relationships are converted into
3042 IDs when inserted into the JSON hash.
3043
3044 ## IDs
3045
3046 `serialize` takes an options hash with a single option:
3047 `includeId`. If this option is `true`, `serialize` will,
3048 by default include the ID in the JSON object it builds.
3049
3050 The adapter passes in `includeId: true` when serializing
3051 a record for `createRecord`, but not for `updateRecord`.
3052
3053 ## Customization
3054
3055 Your server may expect a different JSON format than the
3056 built-in serialization format.
3057
3058 In that case, you can implement `serialize` yourself and
3059 return a JSON hash of your choosing.
3060
3061 ```javascript
3062 App.PostSerializer = DS.JSONSerializer.extend({
3063 serialize: function(post, options) {
3064 var json = {
3065 POST_TTL: post.get('title'),
3066 POST_BDY: post.get('body'),
3067 POST_CMS: post.get('comments').mapBy('id')
3068 }
3069
3070 if (options.includeId) {
3071 json.POST_ID_ = post.get('id');
3072 }
3073
3074 return json;
3075 }
3076 });
3077 ```
3078
3079 ## Customizing an App-Wide Serializer
3080
3081 If you want to define a serializer for your entire
3082 application, you'll probably want to use `eachAttribute`
3083 and `eachRelationship` on the record.
3084
3085 ```javascript
3086 App.ApplicationSerializer = DS.JSONSerializer.extend({
3087 serialize: function(record, options) {
3088 var json = {};
3089
3090 record.eachAttribute(function(name) {
3091 json[serverAttributeName(name)] = record.get(name);
3092 })
3093
3094 record.eachRelationship(function(name, relationship) {
3095 if (relationship.kind === 'hasMany') {
3096 json[serverHasManyName(name)] = record.get(name).mapBy('id');
3097 }
3098 });
3099
3100 if (options.includeId) {
3101 json.ID_ = record.get('id');
3102 }
3103
3104 return json;
3105 }
3106 });
3107
3108 function serverAttributeName(attribute) {
3109 return attribute.underscore().toUpperCase();
3110 }
3111
3112 function serverHasManyName(name) {
3113 return serverAttributeName(name.singularize()) + "_IDS";
3114 }
3115 ```
3116
3117 This serializer will generate JSON that looks like this:
3118
3119 ```javascript
3120 {
3121 "TITLE": "Rails is omakase",
3122 "BODY": "Yep. Omakase.",
3123 "COMMENT_IDS": [ 1, 2, 3 ]
3124 }
3125 ```
3126
3127 ## Tweaking the Default JSON
3128
3129 If you just want to do some small tweaks on the default JSON,
3130 you can call super first and make the tweaks on the returned
3131 JSON.
3132
3133 ```javascript
3134 App.PostSerializer = DS.JSONSerializer.extend({
3135 serialize: function(record, options) {
3136 var json = this._super.apply(this, arguments);
3137
3138 json.subject = json.title;
3139 delete json.title;
3140
3141 return json;
3142 }
3143 });
3144 ```
3145
3146 @method serialize
3147 @param {subclass of DS.Model} record
3148 @param {Object} options
3149 @return {Object} json
3150 */
3151 serialize: function(record, options) {
3152 var json = {};
3153
3154 if (options && options.includeId) {
3155 var id = get(record, 'id');
3156
3157 if (id) {
3158 json[get(this, 'primaryKey')] = id;
3159 }
3160 }
3161
3162 record.eachAttribute(function(key, attribute) {
3163 this.serializeAttribute(record, json, key, attribute);
3164 }, this);
3165
3166 record.eachRelationship(function(key, relationship) {
3167 if (relationship.kind === 'belongsTo') {
3168 this.serializeBelongsTo(record, json, relationship);
3169 } else if (relationship.kind === 'hasMany') {
3170 this.serializeHasMany(record, json, relationship);
3171 }
3172 }, this);
3173
3174 return json;
3175 },
3176
3177 /**
3178 You can use this method to customize how a serialized record is added to the complete
3179 JSON hash to be sent to the server. By default the JSON Serializer does not namespace
3180 the payload and just sends the raw serialized JSON object.
3181 If your server expects namespaced keys, you should consider using the RESTSerializer.
3182 Otherwise you can override this method to customize how the record is added to the hash.
3183
3184 For example, your server may expect underscored root objects.
3185
3186 ```js
3187 App.ApplicationSerializer = DS.RESTSerializer.extend({
3188 serializeIntoHash: function(data, type, record, options) {
3189 var root = Ember.String.decamelize(type.typeKey);
3190 data[root] = this.serialize(record, options);
3191 }
3192 });
3193 ```
3194
3195 @method serializeIntoHash
3196 @param {Object} hash
3197 @param {subclass of DS.Model} type
3198 @param {DS.Model} record
3199 @param {Object} options
3200 */
3201 serializeIntoHash: function(hash, type, record, options) {
3202 merge(hash, this.serialize(record, options));
3203 },
3204
3205 /**
3206 `serializeAttribute` can be used to customize how `DS.attr`
3207 properties are serialized
3208
3209 For example if you wanted to ensure all your attributes were always
3210 serialized as properties on an `attributes` object you could
3211 write:
3212
3213 ```javascript
3214 App.ApplicationSerializer = DS.JSONSerializer.extend({
3215 serializeAttribute: function(record, json, key, attributes) {
3216 json.attributes = json.attributes || {};
3217 this._super(record, json.attributes, key, attributes);
3218 }
3219 });
3220 ```
3221
3222 @method serializeAttribute
3223 @param {DS.Model} record
3224 @param {Object} json
3225 @param {String} key
3226 @param {Object} attribute
3227 */
3228 serializeAttribute: function(record, json, key, attribute) {
3229 var type = attribute.type;
3230
3231 if (this._canSerialize(key)) {
3232 var value = get(record, key);
3233 if (type) {
3234 var transform = this.transformFor(type);
3235 value = transform.serialize(value);
3236 }
3237
3238 // if provided, use the mapping provided by `attrs` in
3239 // the serializer
3240 var payloadKey = this._getMappedKey(key);
3241
3242 if (payloadKey === key && this.keyForAttribute) {
3243 payloadKey = this.keyForAttribute(key);
3244 }
3245
3246 json[payloadKey] = value;
3247 }
3248 },
3249
3250 /**
3251 `serializeBelongsTo` can be used to customize how `DS.belongsTo`
3252 properties are serialized.
3253
3254 Example
3255
3256 ```javascript
3257 App.PostSerializer = DS.JSONSerializer.extend({
3258 serializeBelongsTo: function(record, json, relationship) {
3259 var key = relationship.key;
3260
3261 var belongsTo = get(record, key);
3262
3263 key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
3264
3265 json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON();
3266 }
3267 });
3268 ```
3269
3270 @method serializeBelongsTo
3271 @param {DS.Model} record
3272 @param {Object} json
3273 @param {Object} relationship
3274 */
3275 serializeBelongsTo: function(record, json, relationship) {
3276 var key = relationship.key;
3277
3278 if (this._canSerialize(key)) {
3279 var belongsTo = get(record, key);
3280
3281 // if provided, use the mapping provided by `attrs` in
3282 // the serializer
3283 var payloadKey = this._getMappedKey(key);
3284 if (payloadKey === key && this.keyForRelationship) {
3285 payloadKey = this.keyForRelationship(key, "belongsTo");
3286 }
3287
3288 //Need to check whether the id is there for new&async records
3289 if (isNone(belongsTo) || isNone(get(belongsTo, 'id'))) {
3290 json[payloadKey] = null;
3291 } else {
3292 json[payloadKey] = get(belongsTo, 'id');
3293 }
3294
3295 if (relationship.options.polymorphic) {
3296 this.serializePolymorphicType(record, json, relationship);
3297 }
3298 }
3299 },
3300
3301 /**
3302 `serializeHasMany` can be used to customize how `DS.hasMany`
3303 properties are serialized.
3304
3305 Example
3306
3307 ```javascript
3308 App.PostSerializer = DS.JSONSerializer.extend({
3309 serializeHasMany: function(record, json, relationship) {
3310 var key = relationship.key;
3311 if (key === 'comments') {
3312 return;
3313 } else {
3314 this._super.apply(this, arguments);
3315 }
3316 }
3317 });
3318 ```
3319
3320 @method serializeHasMany
3321 @param {DS.Model} record
3322 @param {Object} json
3323 @param {Object} relationship
3324 */
3325 serializeHasMany: function(record, json, relationship) {
3326 var key = relationship.key;
3327
3328 if (this._canSerialize(key)) {
3329 var payloadKey;
3330
3331 // if provided, use the mapping provided by `attrs` in
3332 // the serializer
3333 payloadKey = this._getMappedKey(key);
3334 if (payloadKey === key && this.keyForRelationship) {
3335 payloadKey = this.keyForRelationship(key, "hasMany");
3336 }
3337
3338 var relationshipType = record.constructor.determineRelationshipType(relationship);
3339
3340 if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') {
3341 json[payloadKey] = get(record, key).mapBy('id');
3342 // TODO support for polymorphic manyToNone and manyToMany relationships
3343 }
3344 }
3345 },
3346
3347 /**
3348 You can use this method to customize how polymorphic objects are
3349 serialized. Objects are considered to be polymorphic if
3350 `{polymorphic: true}` is pass as the second argument to the
3351 `DS.belongsTo` function.
3352
3353 Example
3354
3355 ```javascript
3356 App.CommentSerializer = DS.JSONSerializer.extend({
3357 serializePolymorphicType: function(record, json, relationship) {
3358 var key = relationship.key,
3359 belongsTo = get(record, key);
3360 key = this.keyForAttribute ? this.keyForAttribute(key) : key;
3361
3362 if (Ember.isNone(belongsTo)) {
3363 json[key + "_type"] = null;
3364 } else {
3365 json[key + "_type"] = belongsTo.constructor.typeKey;
3366 }
3367 }
3368 });
3369 ```
3370
3371 @method serializePolymorphicType
3372 @param {DS.Model} record
3373 @param {Object} json
3374 @param {Object} relationship
3375 */
3376 serializePolymorphicType: Ember.K,
3377
3378 // EXTRACT
3379
3380 /**
3381 The `extract` method is used to deserialize payload data from the
3382 server. By default the `JSONSerializer` does not push the records
3383 into the store. However records that subclass `JSONSerializer`
3384 such as the `RESTSerializer` may push records into the store as
3385 part of the extract call.
3386
3387 This method delegates to a more specific extract method based on
3388 the `requestType`.
3389
3390 Example
3391
3392 ```javascript
3393 var get = Ember.get;
3394 socket.on('message', function(message) {
3395 var modelName = message.model;
3396 var data = message.data;
3397 var type = store.modelFor(modelName);
3398 var serializer = store.serializerFor(type.typeKey);
3399 var record = serializer.extract(store, type, data, get(data, 'id'), 'single');
3400 store.push(modelName, record);
3401 });
3402 ```
3403
3404 @method extract
3405 @param {DS.Store} store
3406 @param {subclass of DS.Model} type
3407 @param {Object} payload
3408 @param {String or Number} id
3409 @param {String} requestType
3410 @return {Object} json The deserialized payload
3411 */
3412 extract: function(store, type, payload, id, requestType) {
3413 this.extractMeta(store, type, payload);
3414
3415 var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
3416 return this[specificExtract](store, type, payload, id, requestType);
3417 },
3418
3419 /**
3420 `extractFindAll` is a hook into the extract method used when a
3421 call is made to `DS.Store#findAll`. By default this method is an
3422 alias for [extractArray](#method_extractArray).
3423
3424 @method extractFindAll
3425 @param {DS.Store} store
3426 @param {subclass of DS.Model} type
3427 @param {Object} payload
3428 @param {String or Number} id
3429 @param {String} requestType
3430 @return {Array} array An array of deserialized objects
3431 */
3432 extractFindAll: function(store, type, payload, id, requestType){
3433 return this.extractArray(store, type, payload, id, requestType);
3434 },
3435 /**
3436 `extractFindQuery` is a hook into the extract method used when a
3437 call is made to `DS.Store#findQuery`. By default this method is an
3438 alias for [extractArray](#method_extractArray).
3439
3440 @method extractFindQuery
3441 @param {DS.Store} store
3442 @param {subclass of DS.Model} type
3443 @param {Object} payload
3444 @param {String or Number} id
3445 @param {String} requestType
3446 @return {Array} array An array of deserialized objects
3447 */
3448 extractFindQuery: function(store, type, payload, id, requestType){
3449 return this.extractArray(store, type, payload, id, requestType);
3450 },
3451 /**
3452 `extractFindMany` is a hook into the extract method used when a
3453 call is made to `DS.Store#findMany`. By default this method is
3454 alias for [extractArray](#method_extractArray).
3455
3456 @method extractFindMany
3457 @param {DS.Store} store
3458 @param {subclass of DS.Model} type
3459 @param {Object} payload
3460 @param {String or Number} id
3461 @param {String} requestType
3462 @return {Array} array An array of deserialized objects
3463 */
3464 extractFindMany: function(store, type, payload, id, requestType){
3465 return this.extractArray(store, type, payload, id, requestType);
3466 },
3467 /**
3468 `extractFindHasMany` is a hook into the extract method used when a
3469 call is made to `DS.Store#findHasMany`. By default this method is
3470 alias for [extractArray](#method_extractArray).
3471
3472 @method extractFindHasMany
3473 @param {DS.Store} store
3474 @param {subclass of DS.Model} type
3475 @param {Object} payload
3476 @param {String or Number} id
3477 @param {String} requestType
3478 @return {Array} array An array of deserialized objects
3479 */
3480 extractFindHasMany: function(store, type, payload, id, requestType){
3481 return this.extractArray(store, type, payload, id, requestType);
3482 },
3483
3484 /**
3485 `extractCreateRecord` is a hook into the extract method used when a
3486 call is made to `DS.Store#createRecord`. By default this method is
3487 alias for [extractSave](#method_extractSave).
3488
3489 @method extractCreateRecord
3490 @param {DS.Store} store
3491 @param {subclass of DS.Model} type
3492 @param {Object} payload
3493 @param {String or Number} id
3494 @param {String} requestType
3495 @return {Object} json The deserialized payload
3496 */
3497 extractCreateRecord: function(store, type, payload, id, requestType) {
3498 return this.extractSave(store, type, payload, id, requestType);
3499 },
3500 /**
3501 `extractUpdateRecord` is a hook into the extract method used when
3502 a call is made to `DS.Store#update`. By default this method is alias
3503 for [extractSave](#method_extractSave).
3504
3505 @method extractUpdateRecord
3506 @param {DS.Store} store
3507 @param {subclass of DS.Model} type
3508 @param {Object} payload
3509 @param {String or Number} id
3510 @param {String} requestType
3511 @return {Object} json The deserialized payload
3512 */
3513 extractUpdateRecord: function(store, type, payload, id, requestType) {
3514 return this.extractSave(store, type, payload, id, requestType);
3515 },
3516 /**
3517 `extractDeleteRecord` is a hook into the extract method used when
3518 a call is made to `DS.Store#deleteRecord`. By default this method is
3519 alias for [extractSave](#method_extractSave).
3520
3521 @method extractDeleteRecord
3522 @param {DS.Store} store
3523 @param {subclass of DS.Model} type
3524 @param {Object} payload
3525 @param {String or Number} id
3526 @param {String} requestType
3527 @return {Object} json The deserialized payload
3528 */
3529 extractDeleteRecord: function(store, type, payload, id, requestType) {
3530 return this.extractSave(store, type, payload, id, requestType);
3531 },
3532
3533 /**
3534 `extractFind` is a hook into the extract method used when
3535 a call is made to `DS.Store#find`. By default this method is
3536 alias for [extractSingle](#method_extractSingle).
3537
3538 @method extractFind
3539 @param {DS.Store} store
3540 @param {subclass of DS.Model} type
3541 @param {Object} payload
3542 @param {String or Number} id
3543 @param {String} requestType
3544 @return {Object} json The deserialized payload
3545 */
3546 extractFind: function(store, type, payload, id, requestType) {
3547 return this.extractSingle(store, type, payload, id, requestType);
3548 },
3549 /**
3550 `extractFindBelongsTo` is a hook into the extract method used when
3551 a call is made to `DS.Store#findBelongsTo`. By default this method is
3552 alias for [extractSingle](#method_extractSingle).
3553
3554 @method extractFindBelongsTo
3555 @param {DS.Store} store
3556 @param {subclass of DS.Model} type
3557 @param {Object} payload
3558 @param {String or Number} id
3559 @param {String} requestType
3560 @return {Object} json The deserialized payload
3561 */
3562 extractFindBelongsTo: function(store, type, payload, id, requestType) {
3563 return this.extractSingle(store, type, payload, id, requestType);
3564 },
3565 /**
3566 `extractSave` is a hook into the extract method used when a call
3567 is made to `DS.Model#save`. By default this method is alias
3568 for [extractSingle](#method_extractSingle).
3569
3570 @method extractSave
3571 @param {DS.Store} store
3572 @param {subclass of DS.Model} type
3573 @param {Object} payload
3574 @param {String or Number} id
3575 @param {String} requestType
3576 @return {Object} json The deserialized payload
3577 */
3578 extractSave: function(store, type, payload, id, requestType) {
3579 return this.extractSingle(store, type, payload, id, requestType);
3580 },
3581
3582 /**
3583 `extractSingle` is used to deserialize a single record returned
3584 from the adapter.
3585
3586 Example
3587
3588 ```javascript
3589 App.PostSerializer = DS.JSONSerializer.extend({
3590 extractSingle: function(store, type, payload) {
3591 payload.comments = payload._embedded.comment;
3592 delete payload._embedded;
3593
3594 return this._super(store, type, payload);
3595 },
3596 });
3597 ```
3598
3599 @method extractSingle
3600 @param {DS.Store} store
3601 @param {subclass of DS.Model} type
3602 @param {Object} payload
3603 @param {String or Number} id
3604 @param {String} requestType
3605 @return {Object} json The deserialized payload
3606 */
3607 extractSingle: function(store, type, payload, id, requestType) {
3608 payload = this.normalizePayload(payload);
3609 return this.normalize(type, payload);
3610 },
3611
3612 /**
3613 `extractArray` is used to deserialize an array of records
3614 returned from the adapter.
3615
3616 Example
3617
3618 ```javascript
3619 App.PostSerializer = DS.JSONSerializer.extend({
3620 extractArray: function(store, type, payload) {
3621 return payload.map(function(json) {
3622 return this.extractSingle(store, type, json);
3623 }, this);
3624 }
3625 });
3626 ```
3627
3628 @method extractArray
3629 @param {DS.Store} store
3630 @param {subclass of DS.Model} type
3631 @param {Object} payload
3632 @param {String or Number} id
3633 @param {String} requestType
3634 @return {Array} array An array of deserialized objects
3635 */
3636 extractArray: function(store, type, arrayPayload, id, requestType) {
3637 var normalizedPayload = this.normalizePayload(arrayPayload);
3638 var serializer = this;
3639
3640 return map.call(normalizedPayload, function(singlePayload) {
3641 return serializer.normalize(type, singlePayload);
3642 });
3643 },
3644
3645 /**
3646 `extractMeta` is used to deserialize any meta information in the
3647 adapter payload. By default Ember Data expects meta information to
3648 be located on the `meta` property of the payload object.
3649
3650 Example
3651
3652 ```javascript
3653 App.PostSerializer = DS.JSONSerializer.extend({
3654 extractMeta: function(store, type, payload) {
3655 if (payload && payload._pagination) {
3656 store.metaForType(type, payload._pagination);
3657 delete payload._pagination;
3658 }
3659 }
3660 });
3661 ```
3662
3663 @method extractMeta
3664 @param {DS.Store} store
3665 @param {subclass of DS.Model} type
3666 @param {Object} payload
3667 */
3668 extractMeta: function(store, type, payload) {
3669 if (payload && payload.meta) {
3670 store.metaForType(type, payload.meta);
3671 delete payload.meta;
3672 }
3673 },
3674
3675 /**
3676 `keyForAttribute` can be used to define rules for how to convert an
3677 attribute name in your model to a key in your JSON.
3678
3679 Example
3680
3681 ```javascript
3682 App.ApplicationSerializer = DS.RESTSerializer.extend({
3683 keyForAttribute: function(attr) {
3684 return Ember.String.underscore(attr).toUpperCase();
3685 }
3686 });
3687 ```
3688
3689 @method keyForAttribute
3690 @param {String} key
3691 @return {String} normalized key
3692 */
3693 keyForAttribute: function(key){
3694 return key;
3695 },
3696
3697 /**
3698 `keyForRelationship` can be used to define a custom key when
3699 serializing relationship properties. By default `JSONSerializer`
3700 does not provide an implementation of this method.
3701
3702 Example
3703
3704 ```javascript
3705 App.PostSerializer = DS.JSONSerializer.extend({
3706 keyForRelationship: function(key, relationship) {
3707 return 'rel_' + Ember.String.underscore(key);
3708 }
3709 });
3710 ```
3711
3712 @method keyForRelationship
3713 @param {String} key
3714 @param {String} relationship type
3715 @return {String} normalized key
3716 */
3717
3718 keyForRelationship: function(key, type){
3719 return key;
3720 },
3721
3722 // HELPERS
3723
3724 /**
3725 @method transformFor
3726 @private
3727 @param {String} attributeType
3728 @param {Boolean} skipAssertion
3729 @return {DS.Transform} transform
3730 */
3731 transformFor: function(attributeType, skipAssertion) {
3732 var transform = this.container.lookup('transform:' + attributeType);
3733 Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform);
3734 return transform;
3735 }
3736 });
3737 });
3738define("ember-data/serializers/rest_serializer",
3739 ["ember-data/serializers/json_serializer","ember-inflector/system/string","exports"],
3740 function(__dependency1__, __dependency2__, __exports__) {
3741 "use strict";
3742 /**
3743 @module ember-data
3744 */
3745
3746 var JSONSerializer = __dependency1__["default"];
3747 var get = Ember.get;
3748 var forEach = Ember.ArrayPolyfills.forEach;
3749 var map = Ember.ArrayPolyfills.map;
3750 var camelize = Ember.String.camelize;
3751
3752 var singularize = __dependency2__.singularize;
3753
3754 function coerceId(id) {
3755 return id == null ? null : id + '';
3756 }
3757
3758 /**
3759 Normally, applications will use the `RESTSerializer` by implementing
3760 the `normalize` method and individual normalizations under
3761 `normalizeHash`.
3762
3763 This allows you to do whatever kind of munging you need, and is
3764 especially useful if your server is inconsistent and you need to
3765 do munging differently for many different kinds of responses.
3766
3767 See the `normalize` documentation for more information.
3768
3769 ## Across the Board Normalization
3770
3771 There are also a number of hooks that you might find useful to define
3772 across-the-board rules for your payload. These rules will be useful
3773 if your server is consistent, or if you're building an adapter for
3774 an infrastructure service, like Parse, and want to encode service
3775 conventions.
3776
3777 For example, if all of your keys are underscored and all-caps, but
3778 otherwise consistent with the names you use in your models, you
3779 can implement across-the-board rules for how to convert an attribute
3780 name in your model to a key in your JSON.
3781
3782 ```js
3783 App.ApplicationSerializer = DS.RESTSerializer.extend({
3784 keyForAttribute: function(attr) {
3785 return Ember.String.underscore(attr).toUpperCase();
3786 }
3787 });
3788 ```
3789
3790 You can also implement `keyForRelationship`, which takes the name
3791 of the relationship as the first parameter, and the kind of
3792 relationship (`hasMany` or `belongsTo`) as the second parameter.
3793
3794 @class RESTSerializer
3795 @namespace DS
3796 @extends DS.JSONSerializer
3797 */
3798 var RESTSerializer = JSONSerializer.extend({
3799 /**
3800 If you want to do normalizations specific to some part of the payload, you
3801 can specify those under `normalizeHash`.
3802
3803 For example, given the following json where the the `IDs` under
3804 `"comments"` are provided as `_id` instead of `id`.
3805
3806 ```javascript
3807 {
3808 "post": {
3809 "id": 1,
3810 "title": "Rails is omakase",
3811 "comments": [ 1, 2 ]
3812 },
3813 "comments": [{
3814 "_id": 1,
3815 "body": "FIRST"
3816 }, {
3817 "_id": 2,
3818 "body": "Rails is unagi"
3819 }]
3820 }
3821 ```
3822
3823 You use `normalizeHash` to normalize just the comments:
3824
3825 ```javascript
3826 App.PostSerializer = DS.RESTSerializer.extend({
3827 normalizeHash: {
3828 comments: function(hash) {
3829 hash.id = hash._id;
3830 delete hash._id;
3831 return hash;
3832 }
3833 }
3834 });
3835 ```
3836
3837 The key under `normalizeHash` is usually just the original key
3838 that was in the original payload. However, key names will be
3839 impacted by any modifications done in the `normalizePayload`
3840 method. The `DS.RESTSerializer`'s default implementation makes no
3841 changes to the payload keys.
3842
3843 @property normalizeHash
3844 @type {Object}
3845 @default undefined
3846 */
3847
3848 /**
3849 Normalizes a part of the JSON payload returned by
3850 the server. You should override this method, munge the hash
3851 and call super if you have generic normalization to do.
3852
3853 It takes the type of the record that is being normalized
3854 (as a DS.Model class), the property where the hash was
3855 originally found, and the hash to normalize.
3856
3857 For example, if you have a payload that looks like this:
3858
3859 ```js
3860 {
3861 "post": {
3862 "id": 1,
3863 "title": "Rails is omakase",
3864 "comments": [ 1, 2 ]
3865 },
3866 "comments": [{
3867 "id": 1,
3868 "body": "FIRST"
3869 }, {
3870 "id": 2,
3871 "body": "Rails is unagi"
3872 }]
3873 }
3874 ```
3875
3876 The `normalize` method will be called three times:
3877
3878 * With `App.Post`, `"posts"` and `{ id: 1, title: "Rails is omakase", ... }`
3879 * With `App.Comment`, `"comments"` and `{ id: 1, body: "FIRST" }`
3880 * With `App.Comment`, `"comments"` and `{ id: 2, body: "Rails is unagi" }`
3881
3882 You can use this method, for example, to normalize underscored keys to camelized
3883 or other general-purpose normalizations.
3884
3885 If you want to do normalizations specific to some part of the payload, you
3886 can specify those under `normalizeHash`.
3887
3888 For example, if the `IDs` under `"comments"` are provided as `_id` instead of
3889 `id`, you can specify how to normalize just the comments:
3890
3891 ```js
3892 App.PostSerializer = DS.RESTSerializer.extend({
3893 normalizeHash: {
3894 comments: function(hash) {
3895 hash.id = hash._id;
3896 delete hash._id;
3897 return hash;
3898 }
3899 }
3900 });
3901 ```
3902
3903 The key under `normalizeHash` is just the original key that was in the original
3904 payload.
3905
3906 @method normalize
3907 @param {subclass of DS.Model} type
3908 @param {Object} hash
3909 @param {String} prop
3910 @return {Object}
3911 */
3912 normalize: function(type, hash, prop) {
3913 this.normalizeId(hash);
3914 this.normalizeAttributes(type, hash);
3915 this.normalizeRelationships(type, hash);
3916
3917 this.normalizeUsingDeclaredMapping(type, hash);
3918
3919 if (this.normalizeHash && this.normalizeHash[prop]) {
3920 this.normalizeHash[prop](hash);
3921 }
3922
3923 this.applyTransforms(type, hash);
3924 return hash;
3925 },
3926
3927
3928 /**
3929 Called when the server has returned a payload representing
3930 a single record, such as in response to a `find` or `save`.
3931
3932 It is your opportunity to clean up the server's response into the normalized
3933 form expected by Ember Data.
3934
3935 If you want, you can just restructure the top-level of your payload, and
3936 do more fine-grained normalization in the `normalize` method.
3937
3938 For example, if you have a payload like this in response to a request for
3939 post 1:
3940
3941 ```js
3942 {
3943 "id": 1,
3944 "title": "Rails is omakase",
3945
3946 "_embedded": {
3947 "comment": [{
3948 "_id": 1,
3949 "comment_title": "FIRST"
3950 }, {
3951 "_id": 2,
3952 "comment_title": "Rails is unagi"
3953 }]
3954 }
3955 }
3956 ```
3957
3958 You could implement a serializer that looks like this to get your payload
3959 into shape:
3960
3961 ```js
3962 App.PostSerializer = DS.RESTSerializer.extend({
3963 // First, restructure the top-level so it's organized by type
3964 extractSingle: function(store, type, payload, id) {
3965 var comments = payload._embedded.comment;
3966 delete payload._embedded;
3967
3968 payload = { comments: comments, post: payload };
3969 return this._super(store, type, payload, id);
3970 },
3971
3972 normalizeHash: {
3973 // Next, normalize individual comments, which (after `extract`)
3974 // are now located under `comments`
3975 comments: function(hash) {
3976 hash.id = hash._id;
3977 hash.title = hash.comment_title;
3978 delete hash._id;
3979 delete hash.comment_title;
3980 return hash;
3981 }
3982 }
3983 })
3984 ```
3985
3986 When you call super from your own implementation of `extractSingle`, the
3987 built-in implementation will find the primary record in your normalized
3988 payload and push the remaining records into the store.
3989
3990 The primary record is the single hash found under `post` or the first
3991 element of the `posts` array.
3992
3993 The primary record has special meaning when the record is being created
3994 for the first time or updated (`createRecord` or `updateRecord`). In
3995 particular, it will update the properties of the record that was saved.
3996
3997 @method extractSingle
3998 @param {DS.Store} store
3999 @param {subclass of DS.Model} primaryType
4000 @param {Object} payload
4001 @param {String} recordId
4002 @return {Object} the primary response to the original request
4003 */
4004 extractSingle: function(store, primaryType, rawPayload, recordId) {
4005 var payload = this.normalizePayload(rawPayload);
4006 var primaryTypeName = primaryType.typeKey;
4007 var primaryRecord;
4008
4009 for (var prop in payload) {
4010 var typeName = this.typeForRoot(prop);
4011 if (!store.modelFactoryFor(typeName)){
4012 Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false);
4013 continue;
4014 }
4015 var type = store.modelFor(typeName);
4016 var isPrimary = type.typeKey === primaryTypeName;
4017 var value = payload[prop];
4018
4019 // legacy support for singular resources
4020 if (isPrimary && Ember.typeOf(value) !== "array" ) {
4021 primaryRecord = this.normalize(primaryType, value, prop);
4022 continue;
4023 }
4024
4025 /*jshint loopfunc:true*/
4026 forEach.call(value, function(hash) {
4027 var typeName = this.typeForRoot(prop);
4028 var type = store.modelFor(typeName);
4029 var typeSerializer = store.serializerFor(type);
4030
4031 hash = typeSerializer.normalize(type, hash, prop);
4032
4033 var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord;
4034 var isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId;
4035
4036 // find the primary record.
4037 //
4038 // It's either:
4039 // * the record with the same ID as the original request
4040 // * in the case of a newly created record that didn't have an ID, the first
4041 // record in the Array
4042 if (isFirstCreatedRecord || isUpdatedRecord) {
4043 primaryRecord = hash;
4044 } else {
4045 store.push(typeName, hash);
4046 }
4047 }, this);
4048 }
4049
4050 return primaryRecord;
4051 },
4052
4053 /**
4054 Called when the server has returned a payload representing
4055 multiple records, such as in response to a `findAll` or `findQuery`.
4056
4057 It is your opportunity to clean up the server's response into the normalized
4058 form expected by Ember Data.
4059
4060 If you want, you can just restructure the top-level of your payload, and
4061 do more fine-grained normalization in the `normalize` method.
4062
4063 For example, if you have a payload like this in response to a request for
4064 all posts:
4065
4066 ```js
4067 {
4068 "_embedded": {
4069 "post": [{
4070 "id": 1,
4071 "title": "Rails is omakase"
4072 }, {
4073 "id": 2,
4074 "title": "The Parley Letter"
4075 }],
4076 "comment": [{
4077 "_id": 1,
4078 "comment_title": "Rails is unagi"
4079 "post_id": 1
4080 }, {
4081 "_id": 2,
4082 "comment_title": "Don't tread on me",
4083 "post_id": 2
4084 }]
4085 }
4086 }
4087 ```
4088
4089 You could implement a serializer that looks like this to get your payload
4090 into shape:
4091
4092 ```js
4093 App.PostSerializer = DS.RESTSerializer.extend({
4094 // First, restructure the top-level so it's organized by type
4095 // and the comments are listed under a post's `comments` key.
4096 extractArray: function(store, type, payload) {
4097 var posts = payload._embedded.post;
4098 var comments = [];
4099 var postCache = {};
4100
4101 posts.forEach(function(post) {
4102 post.comments = [];
4103 postCache[post.id] = post;
4104 });
4105
4106 payload._embedded.comment.forEach(function(comment) {
4107 comments.push(comment);
4108 postCache[comment.post_id].comments.push(comment);
4109 delete comment.post_id;
4110 });
4111
4112 payload = { comments: comments, posts: payload };
4113
4114 return this._super(store, type, payload);
4115 },
4116
4117 normalizeHash: {
4118 // Next, normalize individual comments, which (after `extract`)
4119 // are now located under `comments`
4120 comments: function(hash) {
4121 hash.id = hash._id;
4122 hash.title = hash.comment_title;
4123 delete hash._id;
4124 delete hash.comment_title;
4125 return hash;
4126 }
4127 }
4128 })
4129 ```
4130
4131 When you call super from your own implementation of `extractArray`, the
4132 built-in implementation will find the primary array in your normalized
4133 payload and push the remaining records into the store.
4134
4135 The primary array is the array found under `posts`.
4136
4137 The primary record has special meaning when responding to `findQuery`
4138 or `findHasMany`. In particular, the primary array will become the
4139 list of records in the record array that kicked off the request.
4140
4141 If your primary array contains secondary (embedded) records of the same type,
4142 you cannot place these into the primary array `posts`. Instead, place the
4143 secondary items into an underscore prefixed property `_posts`, which will
4144 push these items into the store and will not affect the resulting query.
4145
4146 @method extractArray
4147 @param {DS.Store} store
4148 @param {subclass of DS.Model} primaryType
4149 @param {Object} payload
4150 @return {Array} The primary array that was returned in response
4151 to the original query.
4152 */
4153 extractArray: function(store, primaryType, rawPayload) {
4154 var payload = this.normalizePayload(rawPayload);
4155 var primaryTypeName = primaryType.typeKey;
4156 var primaryArray;
4157
4158 for (var prop in payload) {
4159 var typeKey = prop;
4160 var forcedSecondary = false;
4161
4162 if (prop.charAt(0) === '_') {
4163 forcedSecondary = true;
4164 typeKey = prop.substr(1);
4165 }
4166
4167 var typeName = this.typeForRoot(typeKey);
4168 if (!store.modelFactoryFor(typeName)) {
4169 Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false);
4170 continue;
4171 }
4172 var type = store.modelFor(typeName);
4173 var typeSerializer = store.serializerFor(type);
4174 var isPrimary = (!forcedSecondary && (type.typeKey === primaryTypeName));
4175
4176 /*jshint loopfunc:true*/
4177 var normalizedArray = map.call(payload[prop], function(hash) {
4178 return typeSerializer.normalize(type, hash, prop);
4179 }, this);
4180
4181 if (isPrimary) {
4182 primaryArray = normalizedArray;
4183 } else {
4184 store.pushMany(typeName, normalizedArray);
4185 }
4186 }
4187
4188 return primaryArray;
4189 },
4190
4191 /**
4192 This method allows you to push a payload containing top-level
4193 collections of records organized per type.
4194
4195 ```js
4196 {
4197 "posts": [{
4198 "id": "1",
4199 "title": "Rails is omakase",
4200 "author", "1",
4201 "comments": [ "1" ]
4202 }],
4203 "comments": [{
4204 "id": "1",
4205 "body": "FIRST"
4206 }],
4207 "users": [{
4208 "id": "1",
4209 "name": "@d2h"
4210 }]
4211 }
4212 ```
4213
4214 It will first normalize the payload, so you can use this to push
4215 in data streaming in from your server structured the same way
4216 that fetches and saves are structured.
4217
4218 @method pushPayload
4219 @param {DS.Store} store
4220 @param {Object} payload
4221 */
4222 pushPayload: function(store, rawPayload) {
4223 var payload = this.normalizePayload(rawPayload);
4224
4225 for (var prop in payload) {
4226 var typeName = this.typeForRoot(prop);
4227 if (!store.modelFactoryFor(typeName, prop)){
4228 Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false);
4229 continue;
4230 }
4231 var type = store.modelFor(typeName);
4232 var typeSerializer = store.serializerFor(type);
4233
4234 /*jshint loopfunc:true*/
4235 var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) {
4236 return typeSerializer.normalize(type, hash, prop);
4237 }, this);
4238
4239 store.pushMany(typeName, normalizedArray);
4240 }
4241 },
4242
4243 /**
4244 This method is used to convert each JSON root key in the payload
4245 into a typeKey that it can use to look up the appropriate model for
4246 that part of the payload. By default the typeKey for a model is its
4247 name in camelCase, so if your JSON root key is 'fast-car' you would
4248 use typeForRoot to convert it to 'fastCar' so that Ember Data finds
4249 the `FastCar` model.
4250
4251 If you diverge from this norm you should also consider changes to
4252 store._normalizeTypeKey as well.
4253
4254 For example, your server may return prefixed root keys like so:
4255
4256 ```js
4257 {
4258 "response-fast-car": {
4259 "id": "1",
4260 "name": "corvette"
4261 }
4262 }
4263 ```
4264
4265 In order for Ember Data to know that the model corresponding to
4266 the 'response-fast-car' hash is `FastCar` (typeKey: 'fastCar'),
4267 you can override typeForRoot to convert 'response-fast-car' to
4268 'fastCar' like so:
4269
4270 ```js
4271 App.ApplicationSerializer = DS.RESTSerializer.extend({
4272 typeForRoot: function(root) {
4273 // 'response-fast-car' should become 'fast-car'
4274 var subRoot = root.substring(9);
4275
4276 // _super normalizes 'fast-car' to 'fastCar'
4277 return this._super(subRoot);
4278 }
4279 });
4280 ```
4281
4282 @method typeForRoot
4283 @param {String} key
4284 @return {String} the model's typeKey
4285 */
4286 typeForRoot: function(key) {
4287 return camelize(singularize(key));
4288 },
4289
4290 // SERIALIZE
4291
4292 /**
4293 Called when a record is saved in order to convert the
4294 record into JSON.
4295
4296 By default, it creates a JSON object with a key for
4297 each attribute and belongsTo relationship.
4298
4299 For example, consider this model:
4300
4301 ```js
4302 App.Comment = DS.Model.extend({
4303 title: DS.attr(),
4304 body: DS.attr(),
4305
4306 author: DS.belongsTo('user')
4307 });
4308 ```
4309
4310 The default serialization would create a JSON object like:
4311
4312 ```js
4313 {
4314 "title": "Rails is unagi",
4315 "body": "Rails? Omakase? O_O",
4316 "author": 12
4317 }
4318 ```
4319
4320 By default, attributes are passed through as-is, unless
4321 you specified an attribute type (`DS.attr('date')`). If
4322 you specify a transform, the JavaScript value will be
4323 serialized when inserted into the JSON hash.
4324
4325 By default, belongs-to relationships are converted into
4326 IDs when inserted into the JSON hash.
4327
4328 ## IDs
4329
4330 `serialize` takes an options hash with a single option:
4331 `includeId`. If this option is `true`, `serialize` will,
4332 by default include the ID in the JSON object it builds.
4333
4334 The adapter passes in `includeId: true` when serializing
4335 a record for `createRecord`, but not for `updateRecord`.
4336
4337 ## Customization
4338
4339 Your server may expect a different JSON format than the
4340 built-in serialization format.
4341
4342 In that case, you can implement `serialize` yourself and
4343 return a JSON hash of your choosing.
4344
4345 ```js
4346 App.PostSerializer = DS.RESTSerializer.extend({
4347 serialize: function(post, options) {
4348 var json = {
4349 POST_TTL: post.get('title'),
4350 POST_BDY: post.get('body'),
4351 POST_CMS: post.get('comments').mapBy('id')
4352 }
4353
4354 if (options.includeId) {
4355 json.POST_ID_ = post.get('id');
4356 }
4357
4358 return json;
4359 }
4360 });
4361 ```
4362
4363 ## Customizing an App-Wide Serializer
4364
4365 If you want to define a serializer for your entire
4366 application, you'll probably want to use `eachAttribute`
4367 and `eachRelationship` on the record.
4368
4369 ```js
4370 App.ApplicationSerializer = DS.RESTSerializer.extend({
4371 serialize: function(record, options) {
4372 var json = {};
4373
4374 record.eachAttribute(function(name) {
4375 json[serverAttributeName(name)] = record.get(name);
4376 })
4377
4378 record.eachRelationship(function(name, relationship) {
4379 if (relationship.kind === 'hasMany') {
4380 json[serverHasManyName(name)] = record.get(name).mapBy('id');
4381 }
4382 });
4383
4384 if (options.includeId) {
4385 json.ID_ = record.get('id');
4386 }
4387
4388 return json;
4389 }
4390 });
4391
4392 function serverAttributeName(attribute) {
4393 return attribute.underscore().toUpperCase();
4394 }
4395
4396 function serverHasManyName(name) {
4397 return serverAttributeName(name.singularize()) + "_IDS";
4398 }
4399 ```
4400
4401 This serializer will generate JSON that looks like this:
4402
4403 ```js
4404 {
4405 "TITLE": "Rails is omakase",
4406 "BODY": "Yep. Omakase.",
4407 "COMMENT_IDS": [ 1, 2, 3 ]
4408 }
4409 ```
4410
4411 ## Tweaking the Default JSON
4412
4413 If you just want to do some small tweaks on the default JSON,
4414 you can call super first and make the tweaks on the returned
4415 JSON.
4416
4417 ```js
4418 App.PostSerializer = DS.RESTSerializer.extend({
4419 serialize: function(record, options) {
4420 var json = this._super(record, options);
4421
4422 json.subject = json.title;
4423 delete json.title;
4424
4425 return json;
4426 }
4427 });
4428 ```
4429
4430 @method serialize
4431 @param {subclass of DS.Model} record
4432 @param {Object} options
4433 @return {Object} json
4434 */
4435 serialize: function(record, options) {
4436 return this._super.apply(this, arguments);
4437 },
4438
4439 /**
4440 You can use this method to customize the root keys serialized into the JSON.
4441 By default the REST Serializer sends the typeKey of a model, which is a camelized
4442 version of the name.
4443
4444 For example, your server may expect underscored root objects.
4445
4446 ```js
4447 App.ApplicationSerializer = DS.RESTSerializer.extend({
4448 serializeIntoHash: function(data, type, record, options) {
4449 var root = Ember.String.decamelize(type.typeKey);
4450 data[root] = this.serialize(record, options);
4451 }
4452 });
4453 ```
4454
4455 @method serializeIntoHash
4456 @param {Object} hash
4457 @param {subclass of DS.Model} type
4458 @param {DS.Model} record
4459 @param {Object} options
4460 */
4461 serializeIntoHash: function(hash, type, record, options) {
4462 hash[type.typeKey] = this.serialize(record, options);
4463 },
4464
4465 /**
4466 You can use this method to customize how polymorphic objects are serialized.
4467 By default the JSON Serializer creates the key by appending `Type` to
4468 the attribute and value from the model's camelcased model name.
4469
4470 @method serializePolymorphicType
4471 @param {DS.Model} record
4472 @param {Object} json
4473 @param {Object} relationship
4474 */
4475 serializePolymorphicType: function(record, json, relationship) {
4476 var key = relationship.key;
4477 var belongsTo = get(record, key);
4478 key = this.keyForAttribute ? this.keyForAttribute(key) : key;
4479 if (Ember.isNone(belongsTo)) {
4480 json[key + "Type"] = null;
4481 } else {
4482 json[key + "Type"] = Ember.String.camelize(belongsTo.constructor.typeKey);
4483 }
4484 }
4485 });
4486
4487 Ember.runInDebug(function(){
4488 RESTSerializer.reopen({
4489 warnMessageNoModelForKey: function(prop, typeKey){
4490 return 'Encountered "' + prop + '" in payload, but no model was found for model name "' + typeKey + '" (resolved model name using ' + this.constructor.toString() + '.typeForRoot("' + prop + '"))';
4491 }
4492 });
4493 });
4494
4495 __exports__["default"] = RESTSerializer;
4496 });
4497define("ember-data/setup-container",
4498 ["ember-data/initializers/store","ember-data/initializers/transforms","ember-data/initializers/store_injections","ember-data/initializers/data_adapter","activemodel-adapter/setup-container","exports"],
4499 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
4500 "use strict";
4501 var initializeStore = __dependency1__["default"];
4502 var initializeTransforms = __dependency2__["default"];
4503 var initializeStoreInjections = __dependency3__["default"];
4504 var initializeDataAdapter = __dependency4__["default"];
4505 var setupActiveModelContainer = __dependency5__["default"];
4506
4507 __exports__["default"] = function setupContainer(container, application){
4508 // application is not a required argument. This ensures
4509 // testing setups can setup a container without booting an
4510 // entire ember application.
4511
4512 initializeDataAdapter(container, application);
4513 initializeTransforms(container, application);
4514 initializeStoreInjections(container, application);
4515 initializeStore(container, application);
4516 setupActiveModelContainer(container, application);
4517 };
4518 });
4519define("ember-data/system/adapter",
4520 ["exports"],
4521 function(__exports__) {
4522 "use strict";
4523 /**
4524 @module ember-data
4525 */
4526
4527 var get = Ember.get;
4528
4529 var errorProps = [
4530 'description',
4531 'fileName',
4532 'lineNumber',
4533 'message',
4534 'name',
4535 'number',
4536 'stack'
4537 ];
4538
4539 /**
4540 A `DS.InvalidError` is used by an adapter to signal the external API
4541 was unable to process a request because the content was not
4542 semantically correct or meaningful per the API. Usually this means a
4543 record failed some form of server side validation. When a promise
4544 from an adapter is rejected with a `DS.InvalidError` the record will
4545 transition to the `invalid` state and the errors will be set to the
4546 `errors` property on the record.
4547
4548 Example
4549
4550 ```javascript
4551 App.ApplicationAdapter = DS.RESTAdapter.extend({
4552 ajaxError: function(jqXHR) {
4553 var error = this._super(jqXHR);
4554
4555 if (jqXHR && jqXHR.status === 422) {
4556 var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
4557 return new DS.InvalidError(jsonErrors);
4558 } else {
4559 return error;
4560 }
4561 }
4562 });
4563 ```
4564
4565 The `DS.InvalidError` must be constructed with a single object whose
4566 keys are the invalid model properties, and whose values are the
4567 corresponding error messages. For example:
4568
4569 ```javascript
4570 return new DS.InvalidError({
4571 length: 'Must be less than 15',
4572 name: 'Must not be blank
4573 });
4574 ```
4575
4576 @class InvalidError
4577 @namespace DS
4578 */
4579 function InvalidError(errors) {
4580 var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors));
4581 this.errors = errors;
4582
4583 for (var i=0, l=errorProps.length; i<l; i++) {
4584 this[errorProps[i]] = tmp[errorProps[i]];
4585 }
4586 }
4587
4588 InvalidError.prototype = Ember.create(Error.prototype);
4589
4590 /**
4591 An adapter is an object that receives requests from a store and
4592 translates them into the appropriate action to take against your
4593 persistence layer. The persistence layer is usually an HTTP API, but
4594 may be anything, such as the browser's local storage. Typically the
4595 adapter is not invoked directly instead its functionality is accessed
4596 through the `store`.
4597
4598 ### Creating an Adapter
4599
4600 Create a new subclass of `DS.Adapter`, then assign
4601 it to the `ApplicationAdapter` property of the application.
4602
4603 ```javascript
4604 var MyAdapter = DS.Adapter.extend({
4605 // ...your code here
4606 });
4607
4608 App.ApplicationAdapter = MyAdapter;
4609 ```
4610
4611 Model-specific adapters can be created by assigning your adapter
4612 class to the `ModelName` + `Adapter` property of the application.
4613
4614 ```javascript
4615 var MyPostAdapter = DS.Adapter.extend({
4616 // ...Post-specific adapter code goes here
4617 });
4618
4619 App.PostAdapter = MyPostAdapter;
4620 ```
4621
4622 `DS.Adapter` is an abstract base class that you should override in your
4623 application to customize it for your backend. The minimum set of methods
4624 that you should implement is:
4625
4626 * `find()`
4627 * `createRecord()`
4628 * `updateRecord()`
4629 * `deleteRecord()`
4630 * `findAll()`
4631 * `findQuery()`
4632
4633 To improve the network performance of your application, you can optimize
4634 your adapter by overriding these lower-level methods:
4635
4636 * `findMany()`
4637
4638
4639 For an example implementation, see `DS.RESTAdapter`, the
4640 included REST adapter.
4641
4642 @class Adapter
4643 @namespace DS
4644 @extends Ember.Object
4645 */
4646
4647 var Adapter = Ember.Object.extend({
4648
4649 /**
4650 If you would like your adapter to use a custom serializer you can
4651 set the `defaultSerializer` property to be the name of the custom
4652 serializer.
4653
4654 Note the `defaultSerializer` serializer has a lower priority than
4655 a model specific serializer (i.e. `PostSerializer`) or the
4656 `application` serializer.
4657
4658 ```javascript
4659 var DjangoAdapter = DS.Adapter.extend({
4660 defaultSerializer: 'django'
4661 });
4662 ```
4663
4664 @property defaultSerializer
4665 @type {String}
4666 */
4667
4668 /**
4669 The `find()` method is invoked when the store is asked for a record that
4670 has not previously been loaded. In response to `find()` being called, you
4671 should query your persistence layer for a record with the given ID. Once
4672 found, you can asynchronously call the store's `push()` method to push
4673 the record into the store.
4674
4675 Here is an example `find` implementation:
4676
4677 ```javascript
4678 App.ApplicationAdapter = DS.Adapter.extend({
4679 find: function(store, type, id) {
4680 var url = [type.typeKey, id].join('/');
4681
4682 return new Ember.RSVP.Promise(function(resolve, reject) {
4683 jQuery.getJSON(url).then(function(data) {
4684 Ember.run(null, resolve, data);
4685 }, function(jqXHR) {
4686 jqXHR.then = null; // tame jQuery's ill mannered promises
4687 Ember.run(null, reject, jqXHR);
4688 });
4689 });
4690 }
4691 });
4692 ```
4693
4694 @method find
4695 @param {DS.Store} store
4696 @param {subclass of DS.Model} type
4697 @param {String} id
4698 @return {Promise} promise
4699 */
4700 find: Ember.required(Function),
4701
4702 /**
4703 The `findAll()` method is called when you call `find` on the store
4704 without an ID (i.e. `store.find('post')`).
4705
4706 Example
4707
4708 ```javascript
4709 App.ApplicationAdapter = DS.Adapter.extend({
4710 findAll: function(store, type, sinceToken) {
4711 var url = type;
4712 var query = { since: sinceToken };
4713 return new Ember.RSVP.Promise(function(resolve, reject) {
4714 jQuery.getJSON(url, query).then(function(data) {
4715 Ember.run(null, resolve, data);
4716 }, function(jqXHR) {
4717 jqXHR.then = null; // tame jQuery's ill mannered promises
4718 Ember.run(null, reject, jqXHR);
4719 });
4720 });
4721 }
4722 });
4723 ```
4724
4725 @private
4726 @method findAll
4727 @param {DS.Store} store
4728 @param {subclass of DS.Model} type
4729 @param {String} sinceToken
4730 @return {Promise} promise
4731 */
4732 findAll: null,
4733
4734 /**
4735 This method is called when you call `find` on the store with a
4736 query object as the second parameter (i.e. `store.find('person', {
4737 page: 1 })`).
4738
4739 Example
4740
4741 ```javascript
4742 App.ApplicationAdapter = DS.Adapter.extend({
4743 findQuery: function(store, type, query) {
4744 var url = type;
4745 return new Ember.RSVP.Promise(function(resolve, reject) {
4746 jQuery.getJSON(url, query).then(function(data) {
4747 Ember.run(null, resolve, data);
4748 }, function(jqXHR) {
4749 jqXHR.then = null; // tame jQuery's ill mannered promises
4750 Ember.run(null, reject, jqXHR);
4751 });
4752 });
4753 }
4754 });
4755 ```
4756
4757 @private
4758 @method findQuery
4759 @param {DS.Store} store
4760 @param {subclass of DS.Model} type
4761 @param {Object} query
4762 @param {DS.AdapterPopulatedRecordArray} recordArray
4763 @return {Promise} promise
4764 */
4765 findQuery: null,
4766
4767 /**
4768 If the globally unique IDs for your records should be generated on the client,
4769 implement the `generateIdForRecord()` method. This method will be invoked
4770 each time you create a new record, and the value returned from it will be
4771 assigned to the record's `primaryKey`.
4772
4773 Most traditional REST-like HTTP APIs will not use this method. Instead, the ID
4774 of the record will be set by the server, and your adapter will update the store
4775 with the new ID when it calls `didCreateRecord()`. Only implement this method if
4776 you intend to generate record IDs on the client-side.
4777
4778 The `generateIdForRecord()` method will be invoked with the requesting store as
4779 the first parameter and the newly created record as the second parameter:
4780
4781 ```javascript
4782 generateIdForRecord: function(store, record) {
4783 var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision();
4784 return uuid;
4785 }
4786 ```
4787
4788 @method generateIdForRecord
4789 @param {DS.Store} store
4790 @param {DS.Model} record
4791 @return {String|Number} id
4792 */
4793 generateIdForRecord: null,
4794
4795 /**
4796 Proxies to the serializer's `serialize` method.
4797
4798 Example
4799
4800 ```javascript
4801 App.ApplicationAdapter = DS.Adapter.extend({
4802 createRecord: function(store, type, record) {
4803 var data = this.serialize(record, { includeId: true });
4804 var url = type;
4805
4806 // ...
4807 }
4808 });
4809 ```
4810
4811 @method serialize
4812 @param {DS.Model} record
4813 @param {Object} options
4814 @return {Object} serialized record
4815 */
4816 serialize: function(record, options) {
4817 return get(record, 'store').serializerFor(record.constructor.typeKey).serialize(record, options);
4818 },
4819
4820 /**
4821 Implement this method in a subclass to handle the creation of
4822 new records.
4823
4824 Serializes the record and send it to the server.
4825
4826 Example
4827
4828 ```javascript
4829 App.ApplicationAdapter = DS.Adapter.extend({
4830 createRecord: function(store, type, record) {
4831 var data = this.serialize(record, { includeId: true });
4832 var url = type;
4833
4834 return new Ember.RSVP.Promise(function(resolve, reject) {
4835 jQuery.ajax({
4836 type: 'POST',
4837 url: url,
4838 dataType: 'json',
4839 data: data
4840 }).then(function(data) {
4841 Ember.run(null, resolve, data);
4842 }, function(jqXHR) {
4843 jqXHR.then = null; // tame jQuery's ill mannered promises
4844 Ember.run(null, reject, jqXHR);
4845 });
4846 });
4847 }
4848 });
4849 ```
4850
4851 @method createRecord
4852 @param {DS.Store} store
4853 @param {subclass of DS.Model} type the DS.Model class of the record
4854 @param {DS.Model} record
4855 @return {Promise} promise
4856 */
4857 createRecord: Ember.required(Function),
4858
4859 /**
4860 Implement this method in a subclass to handle the updating of
4861 a record.
4862
4863 Serializes the record update and send it to the server.
4864
4865 Example
4866
4867 ```javascript
4868 App.ApplicationAdapter = DS.Adapter.extend({
4869 updateRecord: function(store, type, record) {
4870 var data = this.serialize(record, { includeId: true });
4871 var id = record.get('id');
4872 var url = [type, id].join('/');
4873
4874 return new Ember.RSVP.Promise(function(resolve, reject) {
4875 jQuery.ajax({
4876 type: 'PUT',
4877 url: url,
4878 dataType: 'json',
4879 data: data
4880 }).then(function(data) {
4881 Ember.run(null, resolve, data);
4882 }, function(jqXHR) {
4883 jqXHR.then = null; // tame jQuery's ill mannered promises
4884 Ember.run(null, reject, jqXHR);
4885 });
4886 });
4887 }
4888 });
4889 ```
4890
4891 @method updateRecord
4892 @param {DS.Store} store
4893 @param {subclass of DS.Model} type the DS.Model class of the record
4894 @param {DS.Model} record
4895 @return {Promise} promise
4896 */
4897 updateRecord: Ember.required(Function),
4898
4899 /**
4900 Implement this method in a subclass to handle the deletion of
4901 a record.
4902
4903 Sends a delete request for the record to the server.
4904
4905 Example
4906
4907 ```javascript
4908 App.ApplicationAdapter = DS.Adapter.extend({
4909 deleteRecord: function(store, type, record) {
4910 var data = this.serialize(record, { includeId: true });
4911 var id = record.get('id');
4912 var url = [type, id].join('/');
4913
4914 return new Ember.RSVP.Promise(function(resolve, reject) {
4915 jQuery.ajax({
4916 type: 'DELETE',
4917 url: url,
4918 dataType: 'json',
4919 data: data
4920 }).then(function(data) {
4921 Ember.run(null, resolve, data);
4922 }, function(jqXHR) {
4923 jqXHR.then = null; // tame jQuery's ill mannered promises
4924 Ember.run(null, reject, jqXHR);
4925 });
4926 });
4927 }
4928 });
4929 ```
4930
4931 @method deleteRecord
4932 @param {DS.Store} store
4933 @param {subclass of DS.Model} type the DS.Model class of the record
4934 @param {DS.Model} record
4935 @return {Promise} promise
4936 */
4937 deleteRecord: Ember.required(Function),
4938
4939 /**
4940 By default the store will try to coalesce all `fetchRecord` calls within the same runloop
4941 into as few requests as possible by calling groupRecordsForFindMany and passing it into a findMany call.
4942 You can opt out of this behaviour by either not implementing the findMany hook or by setting
4943 coalesceFindRequests to false
4944
4945 @property coalesceFindRequests
4946 @type {boolean}
4947 */
4948 coalesceFindRequests: true,
4949
4950 /**
4951 Find multiple records at once if coalesceFindRequests is true
4952
4953 @method findMany
4954 @param {DS.Store} store
4955 @param {subclass of DS.Model} type the DS.Model class of the records
4956 @param {Array} ids
4957 @param {Array} records
4958 @return {Promise} promise
4959 */
4960
4961 /**
4962 Organize records into groups, each of which is to be passed to separate
4963 calls to `findMany`.
4964
4965 For example, if your api has nested URLs that depend on the parent, you will
4966 want to group records by their parent.
4967
4968 The default implementation returns the records as a single group.
4969
4970 @method groupRecordsForFindMany
4971 @param {Array} records
4972 @return {Array} an array of arrays of records, each of which is to be
4973 loaded separately by `findMany`.
4974 */
4975 groupRecordsForFindMany: function (store, records) {
4976 return [records];
4977 }
4978 });
4979
4980 __exports__.InvalidError = InvalidError;
4981 __exports__.Adapter = Adapter;
4982 __exports__["default"] = Adapter;
4983 });
4984define("ember-data/system/container_proxy",
4985 ["exports"],
4986 function(__exports__) {
4987 "use strict";
4988 /**
4989 This is used internally to enable deprecation of container paths and provide
4990 a decent message to the user indicating how to fix the issue.
4991
4992 @class ContainerProxy
4993 @namespace DS
4994 @private
4995 */
4996 function ContainerProxy(container){
4997 this.container = container;
4998 }
4999
5000 ContainerProxy.prototype.aliasedFactory = function(path, preLookup) {
5001 var _this = this;
5002
5003 return {create: function(){
5004 if (preLookup) { preLookup(); }
5005
5006 return _this.container.lookup(path);
5007 }};
5008 };
5009
5010 ContainerProxy.prototype.registerAlias = function(source, dest, preLookup) {
5011 var factory = this.aliasedFactory(dest, preLookup);
5012
5013 return this.container.register(source, factory);
5014 };
5015
5016 ContainerProxy.prototype.registerDeprecation = function(deprecated, valid) {
5017 var preLookupCallback = function(){
5018 Ember.deprecate("You tried to look up '" + deprecated + "', " +
5019 "but this has been deprecated in favor of '" + valid + "'.", false);
5020 };
5021
5022 return this.registerAlias(deprecated, valid, preLookupCallback);
5023 };
5024
5025 ContainerProxy.prototype.registerDeprecations = function(proxyPairs) {
5026 var i, proxyPair, deprecated, valid;
5027
5028 for (i = proxyPairs.length; i > 0; i--) {
5029 proxyPair = proxyPairs[i - 1];
5030 deprecated = proxyPair['deprecated'];
5031 valid = proxyPair['valid'];
5032
5033 this.registerDeprecation(deprecated, valid);
5034 }
5035 };
5036
5037 __exports__["default"] = ContainerProxy;
5038 });
5039define("ember-data/system/debug",
5040 ["ember-data/system/debug/debug_info","ember-data/system/debug/debug_adapter","exports"],
5041 function(__dependency1__, __dependency2__, __exports__) {
5042 "use strict";
5043 /**
5044 @module ember-data
5045 */
5046
5047 var DebugAdapter = __dependency2__["default"];
5048
5049 __exports__["default"] = DebugAdapter;
5050 });
5051define("ember-data/system/debug/debug_adapter",
5052 ["ember-data/system/model","exports"],
5053 function(__dependency1__, __exports__) {
5054 "use strict";
5055 /**
5056 @module ember-data
5057 */
5058 var Model = __dependency1__.Model;
5059 var get = Ember.get;
5060 var capitalize = Ember.String.capitalize;
5061 var underscore = Ember.String.underscore;
5062
5063 /**
5064 Extend `Ember.DataAdapter` with ED specific code.
5065
5066 @class DebugAdapter
5067 @namespace DS
5068 @extends Ember.DataAdapter
5069 @private
5070 */
5071 __exports__["default"] = Ember.DataAdapter.extend({
5072 getFilters: function() {
5073 return [
5074 { name: 'isNew', desc: 'New' },
5075 { name: 'isModified', desc: 'Modified' },
5076 { name: 'isClean', desc: 'Clean' }
5077 ];
5078 },
5079
5080 detect: function(klass) {
5081 return klass !== Model && Model.detect(klass);
5082 },
5083
5084 columnsForType: function(type) {
5085 var columns = [{
5086 name: 'id',
5087 desc: 'Id'
5088 }];
5089 var count = 0;
5090 var self = this;
5091 get(type, 'attributes').forEach(function(meta, name) {
5092 if (count++ > self.attributeLimit) { return false; }
5093 var desc = capitalize(underscore(name).replace('_', ' '));
5094 columns.push({ name: name, desc: desc });
5095 });
5096 return columns;
5097 },
5098
5099 getRecords: function(type) {
5100 return this.get('store').all(type);
5101 },
5102
5103 getRecordColumnValues: function(record) {
5104 var self = this, count = 0;
5105 var columnValues = { id: get(record, 'id') };
5106
5107 record.eachAttribute(function(key) {
5108 if (count++ > self.attributeLimit) {
5109 return false;
5110 }
5111 var value = get(record, key);
5112 columnValues[key] = value;
5113 });
5114 return columnValues;
5115 },
5116
5117 getRecordKeywords: function(record) {
5118 var keywords = [];
5119 var keys = Ember.A(['id']);
5120 record.eachAttribute(function(key) {
5121 keys.push(key);
5122 });
5123 keys.forEach(function(key) {
5124 keywords.push(get(record, key));
5125 });
5126 return keywords;
5127 },
5128
5129 getRecordFilterValues: function(record) {
5130 return {
5131 isNew: record.get('isNew'),
5132 isModified: record.get('isDirty') && !record.get('isNew'),
5133 isClean: !record.get('isDirty')
5134 };
5135 },
5136
5137 getRecordColor: function(record) {
5138 var color = 'black';
5139 if (record.get('isNew')) {
5140 color = 'green';
5141 } else if (record.get('isDirty')) {
5142 color = 'blue';
5143 }
5144 return color;
5145 },
5146
5147 observeRecord: function(record, recordUpdated) {
5148 var releaseMethods = Ember.A(), self = this;
5149 var keysToObserve = Ember.A(['id', 'isNew', 'isDirty']);
5150
5151 record.eachAttribute(function(key) {
5152 keysToObserve.push(key);
5153 });
5154
5155 keysToObserve.forEach(function(key) {
5156 var handler = function() {
5157 recordUpdated(self.wrapRecord(record));
5158 };
5159 Ember.addObserver(record, key, handler);
5160 releaseMethods.push(function() {
5161 Ember.removeObserver(record, key, handler);
5162 });
5163 });
5164
5165 var release = function() {
5166 releaseMethods.forEach(function(fn) { fn(); } );
5167 };
5168
5169 return release;
5170 }
5171
5172 });
5173 });
5174define("ember-data/system/debug/debug_info",
5175 ["ember-data/system/model","exports"],
5176 function(__dependency1__, __exports__) {
5177 "use strict";
5178 var Model = __dependency1__.Model;
5179
5180 Model.reopen({
5181
5182 /**
5183 Provides info about the model for debugging purposes
5184 by grouping the properties into more semantic groups.
5185
5186 Meant to be used by debugging tools such as the Chrome Ember Extension.
5187
5188 - Groups all attributes in "Attributes" group.
5189 - Groups all belongsTo relationships in "Belongs To" group.
5190 - Groups all hasMany relationships in "Has Many" group.
5191 - Groups all flags in "Flags" group.
5192 - Flags relationship CPs as expensive properties.
5193
5194 @method _debugInfo
5195 @for DS.Model
5196 @private
5197 */
5198 _debugInfo: function() {
5199 var attributes = ['id'],
5200 relationships = { belongsTo: [], hasMany: [] },
5201 expensiveProperties = [];
5202
5203 this.eachAttribute(function(name, meta) {
5204 attributes.push(name);
5205 }, this);
5206
5207 this.eachRelationship(function(name, relationship) {
5208 relationships[relationship.kind].push(name);
5209 expensiveProperties.push(name);
5210 });
5211
5212 var groups = [
5213 {
5214 name: 'Attributes',
5215 properties: attributes,
5216 expand: true
5217 },
5218 {
5219 name: 'Belongs To',
5220 properties: relationships.belongsTo,
5221 expand: true
5222 },
5223 {
5224 name: 'Has Many',
5225 properties: relationships.hasMany,
5226 expand: true
5227 },
5228 {
5229 name: 'Flags',
5230 properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid']
5231 }
5232 ];
5233
5234 return {
5235 propertyInfo: {
5236 // include all other mixins / properties (not just the grouped ones)
5237 includeOtherProperties: true,
5238 groups: groups,
5239 // don't pre-calculate unless cached
5240 expensiveProperties: expensiveProperties
5241 }
5242 };
5243 }
5244 });
5245
5246 __exports__["default"] = Model;
5247 });
5248define("ember-data/system/map",
5249 ["exports"],
5250 function(__exports__) {
5251 "use strict";
5252 /**
5253 * Polyfill Ember.Map behavior for Ember <= 1.7
5254 * This can probably be removed before 1.0 final
5255 */
5256 var mapForEach, deleteFn;
5257
5258 function OrderedSet(){
5259 Ember.OrderedSet.apply(this, arguments);
5260 }
5261
5262 function Map() {
5263 Ember.Map.apply(this, arguments);
5264 }
5265
5266 function MapWithDefault(){
5267 Ember.MapWithDefault.apply(this, arguments);
5268 }
5269
5270 var testMap = Ember.Map.create();
5271 testMap.set('key', 'value');
5272
5273 var usesOldBehavior = false;
5274
5275 testMap.forEach(function(value, key){
5276 usesOldBehavior = value === 'key' && key === 'value';
5277 });
5278
5279 Map.prototype = Object.create(Ember.Map.prototype);
5280 MapWithDefault.prototype = Object.create(Ember.MapWithDefault.prototype);
5281 OrderedSet.prototype = Object.create(Ember.OrderedSet.prototype);
5282
5283 OrderedSet.create = function(){
5284 return new OrderedSet();
5285 };
5286
5287 /**
5288 * returns a function that calls the original
5289 * callback function in the correct order.
5290 * if we are in pre-Ember.1.8 land, Map/MapWithDefault
5291 * forEach calls with key, value, in that order.
5292 * >= 1.8 forEach is called with the order value, key as per
5293 * the ES6 spec.
5294 */
5295 function translate(valueKeyOrderedCallback){
5296 return function(key, value){
5297 valueKeyOrderedCallback.call(this, value, key);
5298 };
5299 }
5300
5301 // old, non ES6 compliant behavior
5302 if (usesOldBehavior){
5303 mapForEach = function(callback, thisArg){
5304 this.__super$forEach(translate(callback), thisArg);
5305 };
5306
5307 /* alias to remove */
5308 deleteFn = function(thing){
5309 this.remove(thing);
5310 };
5311
5312 Map.prototype.__super$forEach = Ember.Map.prototype.forEach;
5313 Map.prototype.forEach = mapForEach;
5314 Map.prototype["delete"] = deleteFn;
5315
5316 MapWithDefault.prototype.forEach = mapForEach;
5317 MapWithDefault.prototype.__super$forEach = Ember.MapWithDefault.prototype.forEach;
5318 MapWithDefault.prototype["delete"] = deleteFn;
5319
5320 OrderedSet.prototype["delete"] = deleteFn;
5321 }
5322
5323 MapWithDefault.constructor = MapWithDefault;
5324 Map.constructor = Map;
5325
5326 MapWithDefault.create = function(options){
5327 if (options) {
5328 return new MapWithDefault(options);
5329 } else {
5330 return new Map();
5331 }
5332 };
5333
5334 Map.create = function(){
5335 return new this.constructor();
5336 };
5337
5338 __exports__["default"] = Map;
5339 __exports__.Map = Map;
5340 __exports__.MapWithDefault = MapWithDefault;
5341 __exports__.OrderedSet = OrderedSet;
5342 });
5343define("ember-data/system/model",
5344 ["ember-data/system/model/model","ember-data/system/model/attributes","ember-data/system/model/states","ember-data/system/model/errors","exports"],
5345 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
5346 "use strict";
5347 /**
5348 @module ember-data
5349 */
5350
5351 var Model = __dependency1__["default"];
5352 var attr = __dependency2__["default"];
5353 var RootState = __dependency3__["default"];
5354 var Errors = __dependency4__["default"];
5355
5356 __exports__.Model = Model;
5357 __exports__.RootState = RootState;
5358 __exports__.attr = attr;
5359 __exports__.Errors = Errors;
5360 });
5361define("ember-data/system/model/attributes",
5362 ["ember-data/system/model/model","ember-data/system/map","exports"],
5363 function(__dependency1__, __dependency2__, __exports__) {
5364 "use strict";
5365 var Model = __dependency1__["default"];
5366 var Map = __dependency2__.Map;
5367
5368 /**
5369 @module ember-data
5370 */
5371
5372 var get = Ember.get;
5373
5374 /**
5375 @class Model
5376 @namespace DS
5377 */
5378 Model.reopenClass({
5379 /**
5380 A map whose keys are the attributes of the model (properties
5381 described by DS.attr) and whose values are the meta object for the
5382 property.
5383
5384 Example
5385
5386 ```javascript
5387
5388 App.Person = DS.Model.extend({
5389 firstName: attr('string'),
5390 lastName: attr('string'),
5391 birthday: attr('date')
5392 });
5393
5394 var attributes = Ember.get(App.Person, 'attributes')
5395
5396 attributes.forEach(function(name, meta) {
5397 console.log(name, meta);
5398 });
5399
5400 // prints:
5401 // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
5402 // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
5403 // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
5404 ```
5405
5406 @property attributes
5407 @static
5408 @type {Ember.Map}
5409 @readOnly
5410 */
5411 attributes: Ember.computed(function() {
5412 var map = Map.create();
5413
5414 this.eachComputedProperty(function(name, meta) {
5415 if (meta.isAttribute) {
5416 Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.toString(), name !== 'id');
5417
5418 meta.name = name;
5419 map.set(name, meta);
5420 }
5421 });
5422
5423 return map;
5424 }).readOnly(),
5425
5426 /**
5427 A map whose keys are the attributes of the model (properties
5428 described by DS.attr) and whose values are type of transformation
5429 applied to each attribute. This map does not include any
5430 attributes that do not have an transformation type.
5431
5432 Example
5433
5434 ```javascript
5435 App.Person = DS.Model.extend({
5436 firstName: attr(),
5437 lastName: attr('string'),
5438 birthday: attr('date')
5439 });
5440
5441 var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')
5442
5443 transformedAttributes.forEach(function(field, type) {
5444 console.log(field, type);
5445 });
5446
5447 // prints:
5448 // lastName string
5449 // birthday date
5450 ```
5451
5452 @property transformedAttributes
5453 @static
5454 @type {Ember.Map}
5455 @readOnly
5456 */
5457 transformedAttributes: Ember.computed(function() {
5458 var map = Map.create();
5459
5460 this.eachAttribute(function(key, meta) {
5461 if (meta.type) {
5462 map.set(key, meta.type);
5463 }
5464 });
5465
5466 return map;
5467 }).readOnly(),
5468
5469 /**
5470 Iterates through the attributes of the model, calling the passed function on each
5471 attribute.
5472
5473 The callback method you provide should have the following signature (all
5474 parameters are optional):
5475
5476 ```javascript
5477 function(name, meta);
5478 ```
5479
5480 - `name` the name of the current property in the iteration
5481 - `meta` the meta object for the attribute property in the iteration
5482
5483 Note that in addition to a callback, you can also pass an optional target
5484 object that will be set as `this` on the context.
5485
5486 Example
5487
5488 ```javascript
5489 App.Person = DS.Model.extend({
5490 firstName: attr('string'),
5491 lastName: attr('string'),
5492 birthday: attr('date')
5493 });
5494
5495 App.Person.eachAttribute(function(name, meta) {
5496 console.log(name, meta);
5497 });
5498
5499 // prints:
5500 // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
5501 // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
5502 // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
5503 ```
5504
5505 @method eachAttribute
5506 @param {Function} callback The callback to execute
5507 @param {Object} [target] The target object to use
5508 @static
5509 */
5510 eachAttribute: function(callback, binding) {
5511 get(this, 'attributes').forEach(function(meta, name) {
5512 callback.call(binding, name, meta);
5513 }, binding);
5514 },
5515
5516 /**
5517 Iterates through the transformedAttributes of the model, calling
5518 the passed function on each attribute. Note the callback will not be
5519 called for any attributes that do not have an transformation type.
5520
5521 The callback method you provide should have the following signature (all
5522 parameters are optional):
5523
5524 ```javascript
5525 function(name, type);
5526 ```
5527
5528 - `name` the name of the current property in the iteration
5529 - `type` a string containing the name of the type of transformed
5530 applied to the attribute
5531
5532 Note that in addition to a callback, you can also pass an optional target
5533 object that will be set as `this` on the context.
5534
5535 Example
5536
5537 ```javascript
5538 App.Person = DS.Model.extend({
5539 firstName: attr(),
5540 lastName: attr('string'),
5541 birthday: attr('date')
5542 });
5543
5544 App.Person.eachTransformedAttribute(function(name, type) {
5545 console.log(name, type);
5546 });
5547
5548 // prints:
5549 // lastName string
5550 // birthday date
5551 ```
5552
5553 @method eachTransformedAttribute
5554 @param {Function} callback The callback to execute
5555 @param {Object} [target] The target object to use
5556 @static
5557 */
5558 eachTransformedAttribute: function(callback, binding) {
5559 get(this, 'transformedAttributes').forEach(function(type, name) {
5560 callback.call(binding, name, type);
5561 });
5562 }
5563 });
5564
5565
5566 Model.reopen({
5567 eachAttribute: function(callback, binding) {
5568 this.constructor.eachAttribute(callback, binding);
5569 }
5570 });
5571
5572 function getDefaultValue(record, options, key) {
5573 if (typeof options.defaultValue === "function") {
5574 return options.defaultValue.apply(null, arguments);
5575 } else {
5576 return options.defaultValue;
5577 }
5578 }
5579
5580 function hasValue(record, key) {
5581 return record._attributes.hasOwnProperty(key) ||
5582 record._inFlightAttributes.hasOwnProperty(key) ||
5583 record._data.hasOwnProperty(key);
5584 }
5585
5586 function getValue(record, key) {
5587 if (record._attributes.hasOwnProperty(key)) {
5588 return record._attributes[key];
5589 } else if (record._inFlightAttributes.hasOwnProperty(key)) {
5590 return record._inFlightAttributes[key];
5591 } else {
5592 return record._data[key];
5593 }
5594 }
5595
5596 /**
5597 `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html).
5598 By default, attributes are passed through as-is, however you can specify an
5599 optional type to have the value automatically transformed.
5600 Ember Data ships with four basic transform types: `string`, `number`,
5601 `boolean` and `date`. You can define your own transforms by subclassing
5602 [DS.Transform](/api/data/classes/DS.Transform.html).
5603
5604 Note that you cannot use `attr` to define an attribute of `id`.
5605
5606 `DS.attr` takes an optional hash as a second parameter, currently
5607 supported options are:
5608
5609 - `defaultValue`: Pass a string or a function to be called to set the attribute
5610 to a default value if none is supplied.
5611
5612 Example
5613
5614 ```javascript
5615 var attr = DS.attr;
5616
5617 App.User = DS.Model.extend({
5618 username: attr('string'),
5619 email: attr('string'),
5620 verified: attr('boolean', {defaultValue: false})
5621 });
5622 ```
5623
5624 @namespace
5625 @method attr
5626 @for DS
5627 @param {String} type the attribute type
5628 @param {Object} options a hash of options
5629 @return {Attribute}
5630 */
5631
5632 __exports__["default"] = function attr(type, options) {
5633 options = options || {};
5634
5635 var meta = {
5636 type: type,
5637 isAttribute: true,
5638 options: options
5639 };
5640
5641 return Ember.computed('data', function(key, value) {
5642 if (arguments.length > 1) {
5643 Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id');
5644 var oldValue = getValue(this, key);
5645
5646 if (value !== oldValue) {
5647 // Add the new value to the changed attributes hash; it will get deleted by
5648 // the 'didSetProperty' handler if it is no different from the original value
5649 this._attributes[key] = value;
5650
5651 this.send('didSetProperty', {
5652 name: key,
5653 oldValue: oldValue,
5654 originalValue: this._data[key],
5655 value: value
5656 });
5657 }
5658
5659 return value;
5660 } else if (hasValue(this, key)) {
5661 return getValue(this, key);
5662 } else {
5663 return getDefaultValue(this, options, key);
5664 }
5665
5666 // `data` is never set directly. However, it may be
5667 // invalidated from the state manager's setData
5668 // event.
5669 }).meta(meta);
5670 };
5671 });
5672define("ember-data/system/model/errors",
5673 ["ember-data/system/map","exports"],
5674 function(__dependency1__, __exports__) {
5675 "use strict";
5676 var get = Ember.get;
5677 var isEmpty = Ember.isEmpty;
5678 var map = Ember.EnumerableUtils.map;
5679
5680 var MapWithDefault = __dependency1__.MapWithDefault;
5681
5682 /**
5683 @module ember-data
5684 */
5685
5686 /**
5687 Holds validation errors for a given record organized by attribute names.
5688
5689 Every DS.Model has an `errors` property that is an instance of
5690 `DS.Errors`. This can be used to display validation error
5691 messages returned from the server when a `record.save()` rejects.
5692 This works automatically with `DS.ActiveModelAdapter`, but you
5693 can implement [ajaxError](api/data/classes/DS.RESTAdapter.html#method_ajaxError)
5694 in other adapters as well.
5695
5696 For Example, if you had an `User` model that looked like this:
5697
5698 ```javascript
5699 App.User = DS.Model.extend({
5700 username: attr('string'),
5701 email: attr('string')
5702 });
5703 ```
5704 And you attempted to save a record that did not validate on the backend.
5705
5706 ```javascript
5707 var user = store.createRecord('user', {
5708 username: 'tomster',
5709 email: 'invalidEmail'
5710 });
5711 user.save();
5712 ```
5713
5714 Your backend data store might return a response that looks like
5715 this. This response will be used to populate the error object.
5716
5717 ```javascript
5718 {
5719 "errors": {
5720 "username": ["This username is already taken!"],
5721 "email": ["Doesn't look like a valid email."]
5722 }
5723 }
5724 ```
5725
5726 Errors can be displayed to the user by accessing their property name
5727 or using the `messages` property to get an array of all errors.
5728
5729 ```handlebars
5730 {{#each errors.messages}}
5731 <div class="error">
5732 {{message}}
5733 </div>
5734 {{/each}}
5735
5736 <label>Username: {{input value=username}} </label>
5737 {{#each errors.username}}
5738 <div class="error">
5739 {{message}}
5740 </div>
5741 {{/each}}
5742
5743 <label>Email: {{input value=email}} </label>
5744 {{#each errors.email}}
5745 <div class="error">
5746 {{message}}
5747 </div>
5748 {{/each}}
5749 ```
5750
5751 @class Errors
5752 @namespace DS
5753 @extends Ember.Object
5754 @uses Ember.Enumerable
5755 @uses Ember.Evented
5756 */
5757 __exports__["default"] = Ember.Object.extend(Ember.Enumerable, Ember.Evented, {
5758 /**
5759 Register with target handler
5760
5761 @method registerHandlers
5762 @param {Object} target
5763 @param {Function} becameInvalid
5764 @param {Function} becameValid
5765 */
5766 registerHandlers: function(target, becameInvalid, becameValid) {
5767 this.on('becameInvalid', target, becameInvalid);
5768 this.on('becameValid', target, becameValid);
5769 },
5770
5771 /**
5772 @property errorsByAttributeName
5773 @type {Ember.MapWithDefault}
5774 @private
5775 */
5776 errorsByAttributeName: Ember.reduceComputed("content", {
5777 initialValue: function() {
5778 return MapWithDefault.create({
5779 defaultValue: function() {
5780 return Ember.A();
5781 }
5782 });
5783 },
5784
5785 addedItem: function(errors, error) {
5786 errors.get(error.attribute).pushObject(error);
5787
5788 return errors;
5789 },
5790
5791 removedItem: function(errors, error) {
5792 errors.get(error.attribute).removeObject(error);
5793
5794 return errors;
5795 }
5796 }),
5797
5798 /**
5799 Returns errors for a given attribute
5800
5801 ```javascript
5802 var user = store.createRecord('user', {
5803 username: 'tomster',
5804 email: 'invalidEmail'
5805 });
5806 user.save().catch(function(){
5807 user.get('errors').errorsFor('email'); // ["Doesn't look like a valid email."]
5808 });
5809 ```
5810
5811 @method errorsFor
5812 @param {String} attribute
5813 @return {Array}
5814 */
5815 errorsFor: function(attribute) {
5816 return get(this, 'errorsByAttributeName').get(attribute);
5817 },
5818
5819 /**
5820 An array containing all of the error messages for this
5821 record. This is useful for displaying all errors to the user.
5822
5823 ```handlebars
5824 {{#each errors.messages}}
5825 <div class="error">
5826 {{message}}
5827 </div>
5828 {{/each}}
5829 ```
5830
5831 @property messages
5832 @type {Array}
5833 */
5834 messages: Ember.computed.mapBy('content', 'message'),
5835
5836 /**
5837 @property content
5838 @type {Array}
5839 @private
5840 */
5841 content: Ember.computed(function() {
5842 return Ember.A();
5843 }),
5844
5845 /**
5846 @method unknownProperty
5847 @private
5848 */
5849 unknownProperty: function(attribute) {
5850 var errors = this.errorsFor(attribute);
5851 if (isEmpty(errors)) { return null; }
5852 return errors;
5853 },
5854
5855 /**
5856 @method nextObject
5857 @private
5858 */
5859 nextObject: function(index, previousObject, context) {
5860 return get(this, 'content').objectAt(index);
5861 },
5862
5863 /**
5864 Total number of errors.
5865
5866 @property length
5867 @type {Number}
5868 @readOnly
5869 */
5870 length: Ember.computed.oneWay('content.length').readOnly(),
5871
5872 /**
5873 @property isEmpty
5874 @type {Boolean}
5875 @readOnly
5876 */
5877 isEmpty: Ember.computed.not('length').readOnly(),
5878
5879 /**
5880 Adds error messages to a given attribute and sends
5881 `becameInvalid` event to the record.
5882
5883 Example:
5884
5885 ```javascript
5886 if (!user.get('username') {
5887 user.get('errors').add('username', 'This field is required');
5888 }
5889 ```
5890
5891 @method add
5892 @param {String} attribute
5893 @param {Array|String} messages
5894 */
5895 add: function(attribute, messages) {
5896 var wasEmpty = get(this, 'isEmpty');
5897
5898 messages = this._findOrCreateMessages(attribute, messages);
5899 get(this, 'content').addObjects(messages);
5900
5901 this.notifyPropertyChange(attribute);
5902 this.enumerableContentDidChange();
5903
5904 if (wasEmpty && !get(this, 'isEmpty')) {
5905 this.trigger('becameInvalid');
5906 }
5907 },
5908
5909 /**
5910 @method _findOrCreateMessages
5911 @private
5912 */
5913 _findOrCreateMessages: function(attribute, messages) {
5914 var errors = this.errorsFor(attribute);
5915
5916 return map(Ember.makeArray(messages), function(message) {
5917 return errors.findBy('message', message) || {
5918 attribute: attribute,
5919 message: message
5920 };
5921 });
5922 },
5923
5924 /**
5925 Removes all error messages from the given attribute and sends
5926 `becameValid` event to the record if there no more errors left.
5927
5928 Example:
5929
5930 ```javascript
5931 App.User = DS.Model.extend({
5932 email: DS.attr('string'),
5933 twoFactorAuth: DS.attr('boolean'),
5934 phone: DS.attr('string')
5935 });
5936
5937 App.UserEditRoute = Ember.Route.extend({
5938 actions: {
5939 save: function(user) {
5940 if (!user.get('twoFactorAuth')) {
5941 user.get('errors').remove('phone');
5942 }
5943 user.save();
5944 }
5945 }
5946 });
5947 ```
5948
5949 @method remove
5950 @param {String} attribute
5951 */
5952 remove: function(attribute) {
5953 if (get(this, 'isEmpty')) { return; }
5954
5955 var content = get(this, 'content').rejectBy('attribute', attribute);
5956 get(this, 'content').setObjects(content);
5957
5958 this.notifyPropertyChange(attribute);
5959 this.enumerableContentDidChange();
5960
5961 if (get(this, 'isEmpty')) {
5962 this.trigger('becameValid');
5963 }
5964 },
5965
5966 /**
5967 Removes all error messages and sends `becameValid` event
5968 to the record.
5969
5970 Example:
5971
5972 ```javascript
5973 App.UserEditRoute = Ember.Route.extend({
5974 actions: {
5975 retrySave: function(user) {
5976 user.get('errors').clear();
5977 user.save();
5978 }
5979 }
5980 });
5981 ```
5982
5983 @method clear
5984 */
5985 clear: function() {
5986 if (get(this, 'isEmpty')) { return; }
5987
5988 get(this, 'content').clear();
5989 this.enumerableContentDidChange();
5990
5991 this.trigger('becameValid');
5992 },
5993
5994 /**
5995 Checks if there is error messages for the given attribute.
5996
5997 ```javascript
5998 App.UserEditRoute = Ember.Route.extend({
5999 actions: {
6000 save: function(user) {
6001 if (user.get('errors').has('email')) {
6002 return alert('Please update your email before attempting to save.');
6003 }
6004 user.save();
6005 }
6006 }
6007 });
6008 ```
6009
6010 @method has
6011 @param {String} attribute
6012 @return {Boolean} true if there some errors on given attribute
6013 */
6014 has: function(attribute) {
6015 return !isEmpty(this.errorsFor(attribute));
6016 }
6017 });
6018 });
6019define("ember-data/system/model/model",
6020 ["ember-data/system/model/states","ember-data/system/model/errors","ember-data/system/promise_proxies","ember-data/system/relationships/relationship","exports"],
6021 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
6022 "use strict";
6023 var RootState = __dependency1__["default"];
6024 var Errors = __dependency2__["default"];
6025 var PromiseObject = __dependency3__.PromiseObject;
6026 var createRelationshipFor = __dependency4__.createRelationshipFor;
6027
6028 /**
6029 @module ember-data
6030 */
6031
6032 var get = Ember.get;
6033 var set = Ember.set;
6034 var merge = Ember.merge;
6035 var Promise = Ember.RSVP.Promise;
6036 var forEach = Ember.ArrayPolyfills.forEach;
6037 var map = Ember.ArrayPolyfills.map;
6038
6039 var JSONSerializer;
6040 var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) {
6041 return get(get(this, 'currentState'), key);
6042 }).readOnly();
6043
6044 var _extractPivotNameCache = Object.create(null);
6045 var _splitOnDotCache = Object.create(null);
6046
6047 function splitOnDot(name) {
6048 return _splitOnDotCache[name] || (
6049 _splitOnDotCache[name] = name.split('.')
6050 );
6051 }
6052
6053 function extractPivotName(name) {
6054 return _extractPivotNameCache[name] || (
6055 _extractPivotNameCache[name] = splitOnDot(name)[0]
6056 );
6057 }
6058
6059 /**
6060
6061 The model class that all Ember Data records descend from.
6062
6063 @class Model
6064 @namespace DS
6065 @extends Ember.Object
6066 @uses Ember.Evented
6067 */
6068 var Model = Ember.Object.extend(Ember.Evented, {
6069 _recordArrays: undefined,
6070 _relationships: undefined,
6071 _loadingRecordArrays: undefined,
6072 /**
6073 If this property is `true` the record is in the `empty`
6074 state. Empty is the first state all records enter after they have
6075 been created. Most records created by the store will quickly
6076 transition to the `loading` state if data needs to be fetched from
6077 the server or the `created` state if the record is created on the
6078 client. A record can also enter the empty state if the adapter is
6079 unable to locate the record.
6080
6081 @property isEmpty
6082 @type {Boolean}
6083 @readOnly
6084 */
6085 isEmpty: retrieveFromCurrentState,
6086 /**
6087 If this property is `true` the record is in the `loading` state. A
6088 record enters this state when the store asks the adapter for its
6089 data. It remains in this state until the adapter provides the
6090 requested data.
6091
6092 @property isLoading
6093 @type {Boolean}
6094 @readOnly
6095 */
6096 isLoading: retrieveFromCurrentState,
6097 /**
6098 If this property is `true` the record is in the `loaded` state. A
6099 record enters this state when its data is populated. Most of a
6100 record's lifecycle is spent inside substates of the `loaded`
6101 state.
6102
6103 Example
6104
6105 ```javascript
6106 var record = store.createRecord('model');
6107 record.get('isLoaded'); // true
6108
6109 store.find('model', 1).then(function(model) {
6110 model.get('isLoaded'); // true
6111 });
6112 ```
6113
6114 @property isLoaded
6115 @type {Boolean}
6116 @readOnly
6117 */
6118 isLoaded: retrieveFromCurrentState,
6119 /**
6120 If this property is `true` the record is in the `dirty` state. The
6121 record has local changes that have not yet been saved by the
6122 adapter. This includes records that have been created (but not yet
6123 saved) or deleted.
6124
6125 Example
6126
6127 ```javascript
6128 var record = store.createRecord('model');
6129 record.get('isDirty'); // true
6130
6131 store.find('model', 1).then(function(model) {
6132 model.get('isDirty'); // false
6133 model.set('foo', 'some value');
6134 model.get('isDirty'); // true
6135 });
6136 ```
6137
6138 @property isDirty
6139 @type {Boolean}
6140 @readOnly
6141 */
6142 isDirty: retrieveFromCurrentState,
6143 /**
6144 If this property is `true` the record is in the `saving` state. A
6145 record enters the saving state when `save` is called, but the
6146 adapter has not yet acknowledged that the changes have been
6147 persisted to the backend.
6148
6149 Example
6150
6151 ```javascript
6152 var record = store.createRecord('model');
6153 record.get('isSaving'); // false
6154 var promise = record.save();
6155 record.get('isSaving'); // true
6156 promise.then(function() {
6157 record.get('isSaving'); // false
6158 });
6159 ```
6160
6161 @property isSaving
6162 @type {Boolean}
6163 @readOnly
6164 */
6165 isSaving: retrieveFromCurrentState,
6166 /**
6167 If this property is `true` the record is in the `deleted` state
6168 and has been marked for deletion. When `isDeleted` is true and
6169 `isDirty` is true, the record is deleted locally but the deletion
6170 was not yet persisted. When `isSaving` is true, the change is
6171 in-flight. When both `isDirty` and `isSaving` are false, the
6172 change has persisted.
6173
6174 Example
6175
6176 ```javascript
6177 var record = store.createRecord('model');
6178 record.get('isDeleted'); // false
6179 record.deleteRecord();
6180
6181 // Locally deleted
6182 record.get('isDeleted'); // true
6183 record.get('isDirty'); // true
6184 record.get('isSaving'); // false
6185
6186 // Persisting the deletion
6187 var promise = record.save();
6188 record.get('isDeleted'); // true
6189 record.get('isSaving'); // true
6190
6191 // Deletion Persisted
6192 promise.then(function() {
6193 record.get('isDeleted'); // true
6194 record.get('isSaving'); // false
6195 record.get('isDirty'); // false
6196 });
6197 ```
6198
6199 @property isDeleted
6200 @type {Boolean}
6201 @readOnly
6202 */
6203 isDeleted: retrieveFromCurrentState,
6204 /**
6205 If this property is `true` the record is in the `new` state. A
6206 record will be in the `new` state when it has been created on the
6207 client and the adapter has not yet report that it was successfully
6208 saved.
6209
6210 Example
6211
6212 ```javascript
6213 var record = store.createRecord('model');
6214 record.get('isNew'); // true
6215
6216 record.save().then(function(model) {
6217 model.get('isNew'); // false
6218 });
6219 ```
6220
6221 @property isNew
6222 @type {Boolean}
6223 @readOnly
6224 */
6225 isNew: retrieveFromCurrentState,
6226 /**
6227 If this property is `true` the record is in the `valid` state.
6228
6229 A record will be in the `valid` state when the adapter did not report any
6230 server-side validation failures.
6231
6232 @property isValid
6233 @type {Boolean}
6234 @readOnly
6235 */
6236 isValid: retrieveFromCurrentState,
6237 /**
6238 If the record is in the dirty state this property will report what
6239 kind of change has caused it to move into the dirty
6240 state. Possible values are:
6241
6242 - `created` The record has been created by the client and not yet saved to the adapter.
6243 - `updated` The record has been updated by the client and not yet saved to the adapter.
6244 - `deleted` The record has been deleted by the client and not yet saved to the adapter.
6245
6246 Example
6247
6248 ```javascript
6249 var record = store.createRecord('model');
6250 record.get('dirtyType'); // 'created'
6251 ```
6252
6253 @property dirtyType
6254 @type {String}
6255 @readOnly
6256 */
6257 dirtyType: retrieveFromCurrentState,
6258
6259 /**
6260 If `true` the adapter reported that it was unable to save local
6261 changes to the backend for any reason other than a server-side
6262 validation error.
6263
6264 Example
6265
6266 ```javascript
6267 record.get('isError'); // false
6268 record.set('foo', 'valid value');
6269 record.save().then(null, function() {
6270 record.get('isError'); // true
6271 });
6272 ```
6273
6274 @property isError
6275 @type {Boolean}
6276 @readOnly
6277 */
6278 isError: false,
6279 /**
6280 If `true` the store is attempting to reload the record form the adapter.
6281
6282 Example
6283
6284 ```javascript
6285 record.get('isReloading'); // false
6286 record.reload();
6287 record.get('isReloading'); // true
6288 ```
6289
6290 @property isReloading
6291 @type {Boolean}
6292 @readOnly
6293 */
6294 isReloading: false,
6295
6296 /**
6297 The `clientId` property is a transient numerical identifier
6298 generated at runtime by the data store. It is important
6299 primarily because newly created objects may not yet have an
6300 externally generated id.
6301
6302 @property clientId
6303 @private
6304 @type {Number|String}
6305 */
6306 clientId: null,
6307 /**
6308 All ember models have an id property. This is an identifier
6309 managed by an external source. These are always coerced to be
6310 strings before being used internally. Note when declaring the
6311 attributes for a model it is an error to declare an id
6312 attribute.
6313
6314 ```javascript
6315 var record = store.createRecord('model');
6316 record.get('id'); // null
6317
6318 store.find('model', 1).then(function(model) {
6319 model.get('id'); // '1'
6320 });
6321 ```
6322
6323 @property id
6324 @type {String}
6325 */
6326 id: null,
6327
6328 /**
6329 @property currentState
6330 @private
6331 @type {Object}
6332 */
6333 currentState: RootState.empty,
6334
6335 /**
6336 When the record is in the `invalid` state this object will contain
6337 any errors returned by the adapter. When present the errors hash
6338 typically contains keys corresponding to the invalid property names
6339 and values which are an array of error messages.
6340
6341 ```javascript
6342 record.get('errors.length'); // 0
6343 record.set('foo', 'invalid value');
6344 record.save().then(null, function() {
6345 record.get('errors').get('foo'); // ['foo should be a number.']
6346 });
6347 ```
6348
6349 @property errors
6350 @type {DS.Errors}
6351 */
6352 errors: Ember.computed(function() {
6353 var errors = Errors.create();
6354
6355 errors.registerHandlers(this, function() {
6356 this.send('becameInvalid');
6357 }, function() {
6358 this.send('becameValid');
6359 });
6360
6361 return errors;
6362 }).readOnly(),
6363
6364 /**
6365 Create a JSON representation of the record, using the serialization
6366 strategy of the store's adapter.
6367
6368 `serialize` takes an optional hash as a parameter, currently
6369 supported options are:
6370
6371 - `includeId`: `true` if the record's ID should be included in the
6372 JSON representation.
6373
6374 @method serialize
6375 @param {Object} options
6376 @return {Object} an object whose values are primitive JSON values only
6377 */
6378 serialize: function(options) {
6379 var store = get(this, 'store');
6380 return store.serialize(this, options);
6381 },
6382
6383 /**
6384 Use [DS.JSONSerializer](DS.JSONSerializer.html) to
6385 get the JSON representation of a record.
6386
6387 `toJSON` takes an optional hash as a parameter, currently
6388 supported options are:
6389
6390 - `includeId`: `true` if the record's ID should be included in the
6391 JSON representation.
6392
6393 @method toJSON
6394 @param {Object} options
6395 @return {Object} A JSON representation of the object.
6396 */
6397 toJSON: function(options) {
6398 if (!JSONSerializer) { JSONSerializer = requireModule("ember-data/serializers/json_serializer")["default"]; }
6399 // container is for lazy transform lookups
6400 var serializer = JSONSerializer.create({ container: this.container });
6401 return serializer.serialize(this, options);
6402 },
6403
6404 /**
6405 Fired when the record is loaded from the server.
6406
6407 @event didLoad
6408 */
6409 didLoad: Ember.K,
6410
6411 /**
6412 Fired when the record is updated.
6413
6414 @event didUpdate
6415 */
6416 didUpdate: Ember.K,
6417
6418 /**
6419 Fired when the record is created.
6420
6421 @event didCreate
6422 */
6423 didCreate: Ember.K,
6424
6425 /**
6426 Fired when the record is deleted.
6427
6428 @event didDelete
6429 */
6430 didDelete: Ember.K,
6431
6432 /**
6433 Fired when the record becomes invalid.
6434
6435 @event becameInvalid
6436 */
6437 becameInvalid: Ember.K,
6438
6439 /**
6440 Fired when the record enters the error state.
6441
6442 @event becameError
6443 */
6444 becameError: Ember.K,
6445
6446 /**
6447 @property data
6448 @private
6449 @type {Object}
6450 */
6451 data: Ember.computed(function() {
6452 this._data = this._data || {};
6453 return this._data;
6454 }).readOnly(),
6455
6456 _data: null,
6457
6458 init: function() {
6459 this._super();
6460 this._setup();
6461 },
6462
6463 _setup: function() {
6464 this._changesToSync = {};
6465 this._deferredTriggers = [];
6466 this._data = {};
6467 this._attributes = {};
6468 this._inFlightAttributes = {};
6469 this._relationships = {};
6470 /*
6471 implicit relationships are relationship which have not been declared but the inverse side exists on
6472 another record somewhere
6473 For example if there was
6474 ```
6475 App.Comment = DS.Model.extend({
6476 name: DS.attr()
6477 })
6478 ```
6479 but there is also
6480 ```
6481 App.Post = DS.Model.extend({
6482 name: DS.attr(),
6483 comments: DS.hasMany('comment')
6484 })
6485 ```
6486
6487 would have a implicit post relationship in order to be do things like remove ourselves from the post
6488 when we are deleted
6489 */
6490 this._implicitRelationships = Object.create(null);
6491 var model = this;
6492 //TODO Move into a getter for better perf
6493 this.constructor.eachRelationship(function(key, descriptor) {
6494 model._relationships[key] = createRelationshipFor(model, descriptor, model.store);
6495 });
6496
6497 },
6498
6499 /**
6500 @method send
6501 @private
6502 @param {String} name
6503 @param {Object} context
6504 */
6505 send: function(name, context) {
6506 var currentState = get(this, 'currentState');
6507
6508 if (!currentState[name]) {
6509 this._unhandledEvent(currentState, name, context);
6510 }
6511
6512 return currentState[name](this, context);
6513 },
6514
6515 /**
6516 @method transitionTo
6517 @private
6518 @param {String} name
6519 */
6520 transitionTo: function(name) {
6521 // POSSIBLE TODO: Remove this code and replace with
6522 // always having direct references to state objects
6523
6524 var pivotName = extractPivotName(name);
6525 var currentState = get(this, 'currentState');
6526 var state = currentState;
6527
6528 do {
6529 if (state.exit) { state.exit(this); }
6530 state = state.parentState;
6531 } while (!state.hasOwnProperty(pivotName));
6532
6533 var path = splitOnDot(name);
6534 var setups = [], enters = [], i, l;
6535
6536 for (i=0, l=path.length; i<l; i++) {
6537 state = state[path[i]];
6538
6539 if (state.enter) { enters.push(state); }
6540 if (state.setup) { setups.push(state); }
6541 }
6542
6543 for (i=0, l=enters.length; i<l; i++) {
6544 enters[i].enter(this);
6545 }
6546
6547 set(this, 'currentState', state);
6548
6549 for (i=0, l=setups.length; i<l; i++) {
6550 setups[i].setup(this);
6551 }
6552
6553 this.updateRecordArraysLater();
6554 },
6555
6556 _unhandledEvent: function(state, name, context) {
6557 var errorMessage = "Attempted to handle event `" + name + "` ";
6558 errorMessage += "on " + String(this) + " while in state ";
6559 errorMessage += state.stateName + ". ";
6560
6561 if (context !== undefined) {
6562 errorMessage += "Called with " + Ember.inspect(context) + ".";
6563 }
6564
6565 throw new Ember.Error(errorMessage);
6566 },
6567
6568 withTransaction: function(fn) {
6569 var transaction = get(this, 'transaction');
6570 if (transaction) { fn(transaction); }
6571 },
6572
6573 /**
6574 @method loadingData
6575 @private
6576 @param {Promise} promise
6577 */
6578 loadingData: function(promise) {
6579 this.send('loadingData', promise);
6580 },
6581
6582 /**
6583 @method loadedData
6584 @private
6585 */
6586 loadedData: function() {
6587 this.send('loadedData');
6588 },
6589
6590 /**
6591 @method notFound
6592 @private
6593 */
6594 notFound: function() {
6595 this.send('notFound');
6596 },
6597
6598 /**
6599 @method pushedData
6600 @private
6601 */
6602 pushedData: function() {
6603 this.send('pushedData');
6604 },
6605
6606 /**
6607 Marks the record as deleted but does not save it. You must call
6608 `save` afterwards if you want to persist it. You might use this
6609 method if you want to allow the user to still `rollback()` a
6610 delete after it was made.
6611
6612 Example
6613
6614 ```javascript
6615 App.ModelDeleteRoute = Ember.Route.extend({
6616 actions: {
6617 softDelete: function() {
6618 this.controller.get('model').deleteRecord();
6619 },
6620 confirm: function() {
6621 this.controller.get('model').save();
6622 },
6623 undo: function() {
6624 this.controller.get('model').rollback();
6625 }
6626 }
6627 });
6628 ```
6629
6630 @method deleteRecord
6631 */
6632 deleteRecord: function() {
6633 this.send('deleteRecord');
6634 },
6635
6636 /**
6637 Same as `deleteRecord`, but saves the record immediately.
6638
6639 Example
6640
6641 ```javascript
6642 App.ModelDeleteRoute = Ember.Route.extend({
6643 actions: {
6644 delete: function() {
6645 var controller = this.controller;
6646 controller.get('model').destroyRecord().then(function() {
6647 controller.transitionToRoute('model.index');
6648 });
6649 }
6650 }
6651 });
6652 ```
6653
6654 @method destroyRecord
6655 @return {Promise} a promise that will be resolved when the adapter returns
6656 successfully or rejected if the adapter returns with an error.
6657 */
6658 destroyRecord: function() {
6659 this.deleteRecord();
6660 return this.save();
6661 },
6662
6663 /**
6664 @method unloadRecord
6665 @private
6666 */
6667 unloadRecord: function() {
6668 if (this.isDestroyed) { return; }
6669
6670 this.send('unloadRecord');
6671 },
6672
6673 /**
6674 @method clearRelationships
6675 @private
6676 */
6677 clearRelationships: function() {
6678 this.eachRelationship(function(name, relationship) {
6679 var rel = this._relationships[name];
6680 if (rel){
6681 //TODO(Igor) figure out whether we want to clear or disconnect
6682 rel.clear();
6683 rel.destroy();
6684 }
6685 }, this);
6686 },
6687
6688 disconnectRelationships: function() {
6689 this.eachRelationship(function(name, relationship) {
6690 this._relationships[name].disconnect();
6691 }, this);
6692 var model = this;
6693 forEach.call(Ember.keys(this._implicitRelationships), function(key) {
6694 model._implicitRelationships[key].disconnect();
6695 });
6696 },
6697
6698 reconnectRelationships: function() {
6699 this.eachRelationship(function(name, relationship) {
6700 this._relationships[name].reconnect();
6701 }, this);
6702 var model = this;
6703 forEach.call(Ember.keys(this._implicitRelationships), function(key) {
6704 model._implicitRelationships[key].reconnect();
6705 });
6706 },
6707
6708
6709 /**
6710 @method updateRecordArrays
6711 @private
6712 */
6713 updateRecordArrays: function() {
6714 this._updatingRecordArraysLater = false;
6715 get(this, 'store').dataWasUpdated(this.constructor, this);
6716 },
6717
6718 /**
6719 When a find request is triggered on the store, the user can optionally pass in
6720 attributes and relationships to be preloaded. These are meant to behave as if they
6721 came back from the server, except the user obtained them out of band and is informing
6722 the store of their existence. The most common use case is for supporting client side
6723 nested URLs, such as `/posts/1/comments/2` so the user can do
6724 `store.find('comment', 2, {post:1})` without having to fetch the post.
6725
6726 Preloaded data can be attributes and relationships passed in either as IDs or as actual
6727 models.
6728
6729 @method _preloadData
6730 @private
6731 @param {Object} preload
6732 */
6733 _preloadData: function(preload) {
6734 var record = this;
6735 //TODO(Igor) consider the polymorphic case
6736 forEach.call(Ember.keys(preload), function(key) {
6737 var preloadValue = get(preload, key);
6738 var relationshipMeta = record.constructor.metaForProperty(key);
6739 if (relationshipMeta.isRelationship) {
6740 record._preloadRelationship(key, preloadValue);
6741 } else {
6742 get(record, '_data')[key] = preloadValue;
6743 }
6744 });
6745 },
6746
6747 _preloadRelationship: function(key, preloadValue) {
6748 var relationshipMeta = this.constructor.metaForProperty(key);
6749 var type = relationshipMeta.type;
6750 if (relationshipMeta.kind === 'hasMany'){
6751 this._preloadHasMany(key, preloadValue, type);
6752 } else {
6753 this._preloadBelongsTo(key, preloadValue, type);
6754 }
6755 },
6756
6757 _preloadHasMany: function(key, preloadValue, type) {
6758 Ember.assert("You need to pass in an array to set a hasMany property on a record", Ember.isArray(preloadValue));
6759 var record = this;
6760
6761 var recordsToSet = map.call(preloadValue, function(recordToPush) {
6762 return record._convertStringOrNumberIntoRecord(recordToPush, type);
6763 });
6764 //We use the pathway of setting the hasMany as if it came from the adapter
6765 //because the user told us that they know this relationships exists already
6766 this._relationships[key].updateRecordsFromAdapter(recordsToSet);
6767 },
6768
6769 _preloadBelongsTo: function(key, preloadValue, type){
6770 var recordToSet = this._convertStringOrNumberIntoRecord(preloadValue, type);
6771
6772 //We use the pathway of setting the hasMany as if it came from the adapter
6773 //because the user told us that they know this relationships exists already
6774 this._relationships[key].setRecord(recordToSet);
6775 },
6776
6777 _convertStringOrNumberIntoRecord: function(value, type) {
6778 if (Ember.typeOf(value) === 'string' || Ember.typeOf(value) === 'number'){
6779 return this.store.recordForId(type, value);
6780 }
6781 return value;
6782 },
6783
6784 /**
6785 Returns an object, whose keys are changed properties, and value is
6786 an [oldProp, newProp] array.
6787
6788 Example
6789
6790 ```javascript
6791 App.Mascot = DS.Model.extend({
6792 name: attr('string')
6793 });
6794
6795 var person = store.createRecord('person');
6796 person.changedAttributes(); // {}
6797 person.set('name', 'Tomster');
6798 person.changedAttributes(); // {name: [undefined, 'Tomster']}
6799 ```
6800
6801 @method changedAttributes
6802 @return {Object} an object, whose keys are changed properties,
6803 and value is an [oldProp, newProp] array.
6804 */
6805 changedAttributes: function() {
6806 var oldData = get(this, '_data');
6807 var newData = get(this, '_attributes');
6808 var diffData = {};
6809 var prop;
6810
6811 for (prop in newData) {
6812 diffData[prop] = [oldData[prop], newData[prop]];
6813 }
6814
6815 return diffData;
6816 },
6817
6818 /**
6819 @method adapterWillCommit
6820 @private
6821 */
6822 adapterWillCommit: function() {
6823 this.send('willCommit');
6824 },
6825
6826 /**
6827 If the adapter did not return a hash in response to a commit,
6828 merge the changed attributes and relationships into the existing
6829 saved data.
6830
6831 @method adapterDidCommit
6832 */
6833 adapterDidCommit: function(data) {
6834 set(this, 'isError', false);
6835
6836 if (data) {
6837 this._data = data;
6838 } else {
6839 Ember.mixin(this._data, this._inFlightAttributes);
6840 }
6841
6842 this._inFlightAttributes = {};
6843
6844 this.send('didCommit');
6845 this.updateRecordArraysLater();
6846
6847 if (!data) { return; }
6848
6849 this.notifyPropertyChange('data');
6850 },
6851
6852 /**
6853 @method adapterDidDirty
6854 @private
6855 */
6856 adapterDidDirty: function() {
6857 this.send('becomeDirty');
6858 this.updateRecordArraysLater();
6859 },
6860
6861
6862 /**
6863 @method updateRecordArraysLater
6864 @private
6865 */
6866 updateRecordArraysLater: function() {
6867 // quick hack (something like this could be pushed into run.once
6868 if (this._updatingRecordArraysLater) { return; }
6869 this._updatingRecordArraysLater = true;
6870
6871 Ember.run.schedule('actions', this, this.updateRecordArrays);
6872 },
6873
6874 /**
6875 @method setupData
6876 @private
6877 @param {Object} data
6878 @param {Boolean} partial the data should be merged into
6879 the existing data, not replace it.
6880 */
6881 setupData: function(data, partial) {
6882 if (partial) {
6883 Ember.merge(this._data, data);
6884 } else {
6885 this._data = data;
6886 }
6887
6888 if (data) { this.pushedData(); }
6889
6890 this.notifyPropertyChange('data');
6891 },
6892
6893 materializeId: function(id) {
6894 set(this, 'id', id);
6895 },
6896
6897 materializeAttributes: function(attributes) {
6898 Ember.assert("Must pass a hash of attributes to materializeAttributes", !!attributes);
6899 merge(this._data, attributes);
6900 },
6901
6902 materializeAttribute: function(name, value) {
6903 this._data[name] = value;
6904 },
6905
6906 /**
6907 If the model `isDirty` this function will discard any unsaved
6908 changes
6909
6910 Example
6911
6912 ```javascript
6913 record.get('name'); // 'Untitled Document'
6914 record.set('name', 'Doc 1');
6915 record.get('name'); // 'Doc 1'
6916 record.rollback();
6917 record.get('name'); // 'Untitled Document'
6918 ```
6919
6920 @method rollback
6921 */
6922 rollback: function() {
6923 this._attributes = {};
6924
6925 if (get(this, 'isError')) {
6926 this._inFlightAttributes = {};
6927 set(this, 'isError', false);
6928 }
6929
6930 //Eventually rollback will always work for relationships
6931 //For now we support it only out of deleted state, because we
6932 //have an explicit way of knowing when the server acked the relationship change
6933 if (get(this, 'isDeleted')) {
6934 this.reconnectRelationships();
6935 }
6936
6937 if (!get(this, 'isValid')) {
6938 this._inFlightAttributes = {};
6939 }
6940
6941 this.send('rolledBack');
6942
6943 this.notifyPropertyChange('data');
6944 },
6945
6946 toStringExtension: function() {
6947 return get(this, 'id');
6948 },
6949
6950 /**
6951 Save the record and persist any changes to the record to an
6952 external source via the adapter.
6953
6954 Example
6955
6956 ```javascript
6957 record.set('name', 'Tomster');
6958 record.save().then(function(){
6959 // Success callback
6960 }, function() {
6961 // Error callback
6962 });
6963 ```
6964 @method save
6965 @return {Promise} a promise that will be resolved when the adapter returns
6966 successfully or rejected if the adapter returns with an error.
6967 */
6968 save: function() {
6969 var promiseLabel = "DS: Model#save " + this;
6970 var resolver = Ember.RSVP.defer(promiseLabel);
6971
6972 this.get('store').scheduleSave(this, resolver);
6973 this._inFlightAttributes = this._attributes;
6974 this._attributes = {};
6975
6976 return PromiseObject.create({
6977 promise: resolver.promise
6978 });
6979 },
6980
6981 /**
6982 Reload the record from the adapter.
6983
6984 This will only work if the record has already finished loading
6985 and has not yet been modified (`isLoaded` but not `isDirty`,
6986 or `isSaving`).
6987
6988 Example
6989
6990 ```javascript
6991 App.ModelViewRoute = Ember.Route.extend({
6992 actions: {
6993 reload: function() {
6994 this.controller.get('model').reload();
6995 }
6996 }
6997 });
6998 ```
6999
7000 @method reload
7001 @return {Promise} a promise that will be resolved with the record when the
7002 adapter returns successfully or rejected if the adapter returns
7003 with an error.
7004 */
7005 reload: function() {
7006 set(this, 'isReloading', true);
7007
7008 var record = this;
7009 var promiseLabel = "DS: Model#reload of " + this;
7010 var promise = new Promise(function(resolve){
7011 record.send('reloadRecord', resolve);
7012 }, promiseLabel).then(function() {
7013 record.set('isReloading', false);
7014 record.set('isError', false);
7015 return record;
7016 }, function(reason) {
7017 record.set('isError', true);
7018 throw reason;
7019 }, "DS: Model#reload complete, update flags")['finally'](function () {
7020 record.updateRecordArrays();
7021 });
7022
7023 return PromiseObject.create({
7024 promise: promise
7025 });
7026 },
7027
7028 // FOR USE DURING COMMIT PROCESS
7029
7030 adapterDidUpdateAttribute: function(attributeName, value) {
7031
7032 // If a value is passed in, update the internal attributes and clear
7033 // the attribute cache so it picks up the new value. Otherwise,
7034 // collapse the current value into the internal attributes because
7035 // the adapter has acknowledged it.
7036 if (value !== undefined) {
7037 this._data[attributeName] = value;
7038 this.notifyPropertyChange(attributeName);
7039 } else {
7040 this._data[attributeName] = this._inFlightAttributes[attributeName];
7041 }
7042
7043 this.updateRecordArraysLater();
7044 },
7045
7046 /**
7047 @method adapterDidInvalidate
7048 @private
7049 */
7050 adapterDidInvalidate: function(errors) {
7051 var recordErrors = get(this, 'errors');
7052 function addError(name) {
7053 if (errors[name]) {
7054 recordErrors.add(name, errors[name]);
7055 }
7056 }
7057
7058 this.eachAttribute(addError);
7059 this.eachRelationship(addError);
7060 },
7061
7062 /**
7063 @method adapterDidError
7064 @private
7065 */
7066 adapterDidError: function() {
7067 this.send('becameError');
7068 set(this, 'isError', true);
7069 },
7070
7071 /**
7072 Override the default event firing from Ember.Evented to
7073 also call methods with the given name.
7074
7075 @method trigger
7076 @private
7077 @param {String} name
7078 */
7079 trigger: function() {
7080 var length = arguments.length;
7081 var args = new Array(length - 1);
7082 var name = arguments[0];
7083
7084 for (var i = 1; i < length; i++ ){
7085 args[i - 1] = arguments[i];
7086 }
7087
7088 Ember.tryInvoke(this, name, args);
7089 this._super.apply(this, arguments);
7090 },
7091
7092 triggerLater: function() {
7093 var length = arguments.length;
7094 var args = new Array(length);
7095
7096 for (var i = 0; i < length; i++ ){
7097 args[i] = arguments[i];
7098 }
7099
7100 if (this._deferredTriggers.push(args) !== 1) {
7101 return;
7102 }
7103 Ember.run.schedule('actions', this, '_triggerDeferredTriggers');
7104 },
7105
7106 _triggerDeferredTriggers: function() {
7107 for (var i=0, l= this._deferredTriggers.length; i<l; i++) {
7108 this.trigger.apply(this, this._deferredTriggers[i]);
7109 }
7110
7111 this._deferredTriggers.length = 0;
7112 },
7113
7114 willDestroy: function() {
7115 this._super();
7116 this.clearRelationships();
7117 },
7118
7119 // This is a temporary solution until we refactor DS.Model to not
7120 // rely on the data property.
7121 willMergeMixin: function(props) {
7122 Ember.assert('`data` is a reserved property name on DS.Model objects. Please choose a different property name for ' + this.constructor.toString(), !props.data);
7123 }
7124 });
7125
7126 Model.reopenClass({
7127 /**
7128 Alias DS.Model's `create` method to `_create`. This allows us to create DS.Model
7129 instances from within the store, but if end users accidentally call `create()`
7130 (instead of `createRecord()`), we can raise an error.
7131
7132 @method _create
7133 @private
7134 @static
7135 */
7136 _create: Model.create,
7137
7138 /**
7139 Override the class' `create()` method to raise an error. This
7140 prevents end users from inadvertently calling `create()` instead
7141 of `createRecord()`. The store is still able to create instances
7142 by calling the `_create()` method. To create an instance of a
7143 `DS.Model` use [store.createRecord](DS.Store.html#method_createRecord).
7144
7145 @method create
7146 @private
7147 @static
7148 */
7149 create: function() {
7150 throw new Ember.Error("You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.");
7151 }
7152 });
7153
7154 __exports__["default"] = Model;
7155 });
7156define("ember-data/system/model/states",
7157 ["exports"],
7158 function(__exports__) {
7159 "use strict";
7160 /**
7161 @module ember-data
7162 */
7163
7164 var get = Ember.get;
7165 var set = Ember.set;
7166 /*
7167 This file encapsulates the various states that a record can transition
7168 through during its lifecycle.
7169 */
7170 /**
7171 ### State
7172
7173 Each record has a `currentState` property that explicitly tracks what
7174 state a record is in at any given time. For instance, if a record is
7175 newly created and has not yet been sent to the adapter to be saved,
7176 it would be in the `root.loaded.created.uncommitted` state. If a
7177 record has had local modifications made to it that are in the
7178 process of being saved, the record would be in the
7179 `root.loaded.updated.inFlight` state. (This state paths will be
7180 explained in more detail below.)
7181
7182 Events are sent by the record or its store to the record's
7183 `currentState` property. How the state reacts to these events is
7184 dependent on which state it is in. In some states, certain events
7185 will be invalid and will cause an exception to be raised.
7186
7187 States are hierarchical and every state is a substate of the
7188 `RootState`. For example, a record can be in the
7189 `root.deleted.uncommitted` state, then transition into the
7190 `root.deleted.inFlight` state. If a child state does not implement
7191 an event handler, the state manager will attempt to invoke the event
7192 on all parent states until the root state is reached. The state
7193 hierarchy of a record is described in terms of a path string. You
7194 can determine a record's current state by getting the state's
7195 `stateName` property:
7196
7197 ```javascript
7198 record.get('currentState.stateName');
7199 //=> "root.created.uncommitted"
7200 ```
7201
7202 The hierarchy of valid states that ship with ember data looks like
7203 this:
7204
7205 ```text
7206 * root
7207 * deleted
7208 * saved
7209 * uncommitted
7210 * inFlight
7211 * empty
7212 * loaded
7213 * created
7214 * uncommitted
7215 * inFlight
7216 * saved
7217 * updated
7218 * uncommitted
7219 * inFlight
7220 * loading
7221 ```
7222
7223 The `DS.Model` states are themselves stateless. What that means is
7224 that, the hierarchical states that each of *those* points to is a
7225 shared data structure. For performance reasons, instead of each
7226 record getting its own copy of the hierarchy of states, each record
7227 points to this global, immutable shared instance. How does a state
7228 know which record it should be acting on? We pass the record
7229 instance into the state's event handlers as the first argument.
7230
7231 The record passed as the first parameter is where you should stash
7232 state about the record if needed; you should never store data on the state
7233 object itself.
7234
7235 ### Events and Flags
7236
7237 A state may implement zero or more events and flags.
7238
7239 #### Events
7240
7241 Events are named functions that are invoked when sent to a record. The
7242 record will first look for a method with the given name on the
7243 current state. If no method is found, it will search the current
7244 state's parent, and then its grandparent, and so on until reaching
7245 the top of the hierarchy. If the root is reached without an event
7246 handler being found, an exception will be raised. This can be very
7247 helpful when debugging new features.
7248
7249 Here's an example implementation of a state with a `myEvent` event handler:
7250
7251 ```javascript
7252 aState: DS.State.create({
7253 myEvent: function(manager, param) {
7254 console.log("Received myEvent with", param);
7255 }
7256 })
7257 ```
7258
7259 To trigger this event:
7260
7261 ```javascript
7262 record.send('myEvent', 'foo');
7263 //=> "Received myEvent with foo"
7264 ```
7265
7266 Note that an optional parameter can be sent to a record's `send()` method,
7267 which will be passed as the second parameter to the event handler.
7268
7269 Events should transition to a different state if appropriate. This can be
7270 done by calling the record's `transitionTo()` method with a path to the
7271 desired state. The state manager will attempt to resolve the state path
7272 relative to the current state. If no state is found at that path, it will
7273 attempt to resolve it relative to the current state's parent, and then its
7274 parent, and so on until the root is reached. For example, imagine a hierarchy
7275 like this:
7276
7277 * created
7278 * uncommitted <-- currentState
7279 * inFlight
7280 * updated
7281 * inFlight
7282
7283 If we are currently in the `uncommitted` state, calling
7284 `transitionTo('inFlight')` would transition to the `created.inFlight` state,
7285 while calling `transitionTo('updated.inFlight')` would transition to
7286 the `updated.inFlight` state.
7287
7288 Remember that *only events* should ever cause a state transition. You should
7289 never call `transitionTo()` from outside a state's event handler. If you are
7290 tempted to do so, create a new event and send that to the state manager.
7291
7292 #### Flags
7293
7294 Flags are Boolean values that can be used to introspect a record's current
7295 state in a more user-friendly way than examining its state path. For example,
7296 instead of doing this:
7297
7298 ```javascript
7299 var statePath = record.get('stateManager.currentPath');
7300 if (statePath === 'created.inFlight') {
7301 doSomething();
7302 }
7303 ```
7304
7305 You can say:
7306
7307 ```javascript
7308 if (record.get('isNew') && record.get('isSaving')) {
7309 doSomething();
7310 }
7311 ```
7312
7313 If your state does not set a value for a given flag, the value will
7314 be inherited from its parent (or the first place in the state hierarchy
7315 where it is defined).
7316
7317 The current set of flags are defined below. If you want to add a new flag,
7318 in addition to the area below, you will also need to declare it in the
7319 `DS.Model` class.
7320
7321
7322 * [isEmpty](DS.Model.html#property_isEmpty)
7323 * [isLoading](DS.Model.html#property_isLoading)
7324 * [isLoaded](DS.Model.html#property_isLoaded)
7325 * [isDirty](DS.Model.html#property_isDirty)
7326 * [isSaving](DS.Model.html#property_isSaving)
7327 * [isDeleted](DS.Model.html#property_isDeleted)
7328 * [isNew](DS.Model.html#property_isNew)
7329 * [isValid](DS.Model.html#property_isValid)
7330
7331 @namespace DS
7332 @class RootState
7333 */
7334
7335 function didSetProperty(record, context) {
7336 if (context.value === context.originalValue) {
7337 delete record._attributes[context.name];
7338 record.send('propertyWasReset', context.name);
7339 } else if (context.value !== context.oldValue) {
7340 record.send('becomeDirty');
7341 }
7342
7343 record.updateRecordArraysLater();
7344 }
7345
7346 // Implementation notes:
7347 //
7348 // Each state has a boolean value for all of the following flags:
7349 //
7350 // * isLoaded: The record has a populated `data` property. When a
7351 // record is loaded via `store.find`, `isLoaded` is false
7352 // until the adapter sets it. When a record is created locally,
7353 // its `isLoaded` property is always true.
7354 // * isDirty: The record has local changes that have not yet been
7355 // saved by the adapter. This includes records that have been
7356 // created (but not yet saved) or deleted.
7357 // * isSaving: The record has been committed, but
7358 // the adapter has not yet acknowledged that the changes have
7359 // been persisted to the backend.
7360 // * isDeleted: The record was marked for deletion. When `isDeleted`
7361 // is true and `isDirty` is true, the record is deleted locally
7362 // but the deletion was not yet persisted. When `isSaving` is
7363 // true, the change is in-flight. When both `isDirty` and
7364 // `isSaving` are false, the change has persisted.
7365 // * isError: The adapter reported that it was unable to save
7366 // local changes to the backend. This may also result in the
7367 // record having its `isValid` property become false if the
7368 // adapter reported that server-side validations failed.
7369 // * isNew: The record was created on the client and the adapter
7370 // did not yet report that it was successfully saved.
7371 // * isValid: The adapter did not report any server-side validation
7372 // failures.
7373
7374 // The dirty state is a abstract state whose functionality is
7375 // shared between the `created` and `updated` states.
7376 //
7377 // The deleted state shares the `isDirty` flag with the
7378 // subclasses of `DirtyState`, but with a very different
7379 // implementation.
7380 //
7381 // Dirty states have three child states:
7382 //
7383 // `uncommitted`: the store has not yet handed off the record
7384 // to be saved.
7385 // `inFlight`: the store has handed off the record to be saved,
7386 // but the adapter has not yet acknowledged success.
7387 // `invalid`: the record has invalid information and cannot be
7388 // send to the adapter yet.
7389 var DirtyState = {
7390 initialState: 'uncommitted',
7391
7392 // FLAGS
7393 isDirty: true,
7394
7395 // SUBSTATES
7396
7397 // When a record first becomes dirty, it is `uncommitted`.
7398 // This means that there are local pending changes, but they
7399 // have not yet begun to be saved, and are not invalid.
7400 uncommitted: {
7401 // EVENTS
7402 didSetProperty: didSetProperty,
7403
7404 //TODO(Igor) reloading now triggers a
7405 //loadingData event, though it seems fine?
7406 loadingData: Ember.K,
7407
7408 propertyWasReset: function(record, name) {
7409 var length = Ember.keys(record._attributes);
7410 var stillDirty = length > 0;
7411
7412 if (!stillDirty) { record.send('rolledBack'); }
7413 },
7414
7415 pushedData: Ember.K,
7416
7417 becomeDirty: Ember.K,
7418
7419 willCommit: function(record) {
7420 record.transitionTo('inFlight');
7421 },
7422
7423 reloadRecord: function(record, resolve) {
7424 resolve(get(record, 'store').reloadRecord(record));
7425 },
7426
7427 rolledBack: function(record) {
7428 record.transitionTo('loaded.saved');
7429 },
7430
7431 becameInvalid: function(record) {
7432 record.transitionTo('invalid');
7433 },
7434
7435 rollback: function(record) {
7436 record.rollback();
7437 }
7438 },
7439
7440 // Once a record has been handed off to the adapter to be
7441 // saved, it is in the 'in flight' state. Changes to the
7442 // record cannot be made during this window.
7443 inFlight: {
7444 // FLAGS
7445 isSaving: true,
7446
7447 // EVENTS
7448 didSetProperty: didSetProperty,
7449 becomeDirty: Ember.K,
7450 pushedData: Ember.K,
7451
7452 unloadRecord: function(record) {
7453 Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + " `", false);
7454 },
7455
7456 // TODO: More robust semantics around save-while-in-flight
7457 willCommit: Ember.K,
7458
7459 didCommit: function(record) {
7460 var dirtyType = get(this, 'dirtyType');
7461
7462 record.transitionTo('saved');
7463 record.send('invokeLifecycleCallbacks', dirtyType);
7464 },
7465
7466 becameInvalid: function(record) {
7467 record.transitionTo('invalid');
7468 record.send('invokeLifecycleCallbacks');
7469 },
7470
7471 becameError: function(record) {
7472 record.transitionTo('uncommitted');
7473 record.triggerLater('becameError', record);
7474 }
7475 },
7476
7477 // A record is in the `invalid` if the adapter has indicated
7478 // the the record failed server-side invalidations.
7479 invalid: {
7480 // FLAGS
7481 isValid: false,
7482
7483 // EVENTS
7484 deleteRecord: function(record) {
7485 record.transitionTo('deleted.uncommitted');
7486 record.disconnectRelationships();
7487 },
7488
7489 didSetProperty: function(record, context) {
7490 get(record, 'errors').remove(context.name);
7491
7492 didSetProperty(record, context);
7493 },
7494
7495 becomeDirty: Ember.K,
7496
7497 willCommit: function(record) {
7498 get(record, 'errors').clear();
7499 record.transitionTo('inFlight');
7500 },
7501
7502 rolledBack: function(record) {
7503 get(record, 'errors').clear();
7504 },
7505
7506 becameValid: function(record) {
7507 record.transitionTo('uncommitted');
7508 },
7509
7510 invokeLifecycleCallbacks: function(record) {
7511 record.triggerLater('becameInvalid', record);
7512 },
7513
7514 exit: function(record) {
7515 record._inFlightAttributes = {};
7516 }
7517 }
7518 };
7519
7520 // The created and updated states are created outside the state
7521 // chart so we can reopen their substates and add mixins as
7522 // necessary.
7523
7524 function deepClone(object) {
7525 var clone = {}, value;
7526
7527 for (var prop in object) {
7528 value = object[prop];
7529 if (value && typeof value === 'object') {
7530 clone[prop] = deepClone(value);
7531 } else {
7532 clone[prop] = value;
7533 }
7534 }
7535
7536 return clone;
7537 }
7538
7539 function mixin(original, hash) {
7540 for (var prop in hash) {
7541 original[prop] = hash[prop];
7542 }
7543
7544 return original;
7545 }
7546
7547 function dirtyState(options) {
7548 var newState = deepClone(DirtyState);
7549 return mixin(newState, options);
7550 }
7551
7552 var createdState = dirtyState({
7553 dirtyType: 'created',
7554 // FLAGS
7555 isNew: true
7556 });
7557
7558 createdState.uncommitted.rolledBack = function(record) {
7559 record.transitionTo('deleted.saved');
7560 };
7561
7562 var updatedState = dirtyState({
7563 dirtyType: 'updated'
7564 });
7565
7566 createdState.uncommitted.deleteRecord = function(record) {
7567 record.disconnectRelationships();
7568 record.transitionTo('deleted.saved');
7569 };
7570
7571 createdState.uncommitted.rollback = function(record) {
7572 DirtyState.uncommitted.rollback.apply(this, arguments);
7573 record.transitionTo('deleted.saved');
7574 };
7575
7576 createdState.uncommitted.propertyWasReset = Ember.K;
7577
7578 function assertAgainstUnloadRecord(record) {
7579 Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", false);
7580 }
7581
7582 updatedState.inFlight.unloadRecord = assertAgainstUnloadRecord;
7583
7584 updatedState.uncommitted.deleteRecord = function(record) {
7585 record.transitionTo('deleted.uncommitted');
7586 record.disconnectRelationships();
7587 };
7588
7589 var RootState = {
7590 // FLAGS
7591 isEmpty: false,
7592 isLoading: false,
7593 isLoaded: false,
7594 isDirty: false,
7595 isSaving: false,
7596 isDeleted: false,
7597 isNew: false,
7598 isValid: true,
7599
7600 // DEFAULT EVENTS
7601
7602 // Trying to roll back if you're not in the dirty state
7603 // doesn't change your state. For example, if you're in the
7604 // in-flight state, rolling back the record doesn't move
7605 // you out of the in-flight state.
7606 rolledBack: Ember.K,
7607 unloadRecord: function(record) {
7608 // clear relationships before moving to deleted state
7609 // otherwise it fails
7610 record.clearRelationships();
7611 record.transitionTo('deleted.saved');
7612 },
7613
7614
7615 propertyWasReset: Ember.K,
7616
7617 // SUBSTATES
7618
7619 // A record begins its lifecycle in the `empty` state.
7620 // If its data will come from the adapter, it will
7621 // transition into the `loading` state. Otherwise, if
7622 // the record is being created on the client, it will
7623 // transition into the `created` state.
7624 empty: {
7625 isEmpty: true,
7626
7627 // EVENTS
7628 loadingData: function(record, promise) {
7629 record._loadingPromise = promise;
7630 record.transitionTo('loading');
7631 },
7632
7633 loadedData: function(record) {
7634 record.transitionTo('loaded.created.uncommitted');
7635 record.notifyPropertyChange('data');
7636 },
7637
7638 pushedData: function(record) {
7639 record.transitionTo('loaded.saved');
7640 record.triggerLater('didLoad');
7641 }
7642 },
7643
7644 // A record enters this state when the store asks
7645 // the adapter for its data. It remains in this state
7646 // until the adapter provides the requested data.
7647 //
7648 // Usually, this process is asynchronous, using an
7649 // XHR to retrieve the data.
7650 loading: {
7651 // FLAGS
7652 isLoading: true,
7653
7654 exit: function(record) {
7655 record._loadingPromise = null;
7656 },
7657
7658 // EVENTS
7659 pushedData: function(record) {
7660 record.transitionTo('loaded.saved');
7661 record.triggerLater('didLoad');
7662 set(record, 'isError', false);
7663 },
7664
7665 becameError: function(record) {
7666 record.triggerLater('becameError', record);
7667 },
7668
7669 notFound: function(record) {
7670 record.transitionTo('empty');
7671 }
7672 },
7673
7674 // A record enters this state when its data is populated.
7675 // Most of a record's lifecycle is spent inside substates
7676 // of the `loaded` state.
7677 loaded: {
7678 initialState: 'saved',
7679
7680 // FLAGS
7681 isLoaded: true,
7682
7683 //TODO(Igor) Reloading now triggers a loadingData event,
7684 //but it should be ok?
7685 loadingData: Ember.K,
7686
7687 // SUBSTATES
7688
7689 // If there are no local changes to a record, it remains
7690 // in the `saved` state.
7691 saved: {
7692 setup: function(record) {
7693 var attrs = record._attributes;
7694 var isDirty = false;
7695
7696 for (var prop in attrs) {
7697 if (attrs.hasOwnProperty(prop)) {
7698 isDirty = true;
7699 break;
7700 }
7701 }
7702
7703 if (isDirty) {
7704 record.adapterDidDirty();
7705 }
7706 },
7707
7708 // EVENTS
7709 didSetProperty: didSetProperty,
7710
7711 pushedData: Ember.K,
7712
7713 becomeDirty: function(record) {
7714 record.transitionTo('updated.uncommitted');
7715 },
7716
7717 willCommit: function(record) {
7718 record.transitionTo('updated.inFlight');
7719 },
7720
7721 reloadRecord: function(record, resolve) {
7722 resolve(get(record, 'store').reloadRecord(record));
7723 },
7724
7725 deleteRecord: function(record) {
7726 record.transitionTo('deleted.uncommitted');
7727 record.disconnectRelationships();
7728 },
7729
7730 unloadRecord: function(record) {
7731 // clear relationships before moving to deleted state
7732 // otherwise it fails
7733 record.clearRelationships();
7734 record.transitionTo('deleted.saved');
7735 },
7736
7737 didCommit: function(record) {
7738 record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType'));
7739 },
7740
7741 // loaded.saved.notFound would be triggered by a failed
7742 // `reload()` on an unchanged record
7743 notFound: Ember.K
7744
7745 },
7746
7747 // A record is in this state after it has been locally
7748 // created but before the adapter has indicated that
7749 // it has been saved.
7750 created: createdState,
7751
7752 // A record is in this state if it has already been
7753 // saved to the server, but there are new local changes
7754 // that have not yet been saved.
7755 updated: updatedState
7756 },
7757
7758 // A record is in this state if it was deleted from the store.
7759 deleted: {
7760 initialState: 'uncommitted',
7761 dirtyType: 'deleted',
7762
7763 // FLAGS
7764 isDeleted: true,
7765 isLoaded: true,
7766 isDirty: true,
7767
7768 // TRANSITIONS
7769 setup: function(record) {
7770 record.updateRecordArrays();
7771 },
7772
7773 // SUBSTATES
7774
7775 // When a record is deleted, it enters the `start`
7776 // state. It will exit this state when the record
7777 // starts to commit.
7778 uncommitted: {
7779
7780 // EVENTS
7781
7782 willCommit: function(record) {
7783 record.transitionTo('inFlight');
7784 },
7785
7786 rollback: function(record) {
7787 record.rollback();
7788 },
7789
7790 becomeDirty: Ember.K,
7791 deleteRecord: Ember.K,
7792
7793 rolledBack: function(record) {
7794 record.transitionTo('loaded.saved');
7795 }
7796 },
7797
7798 // After a record starts committing, but
7799 // before the adapter indicates that the deletion
7800 // has saved to the server, a record is in the
7801 // `inFlight` substate of `deleted`.
7802 inFlight: {
7803 // FLAGS
7804 isSaving: true,
7805
7806 // EVENTS
7807
7808 unloadRecord: assertAgainstUnloadRecord,
7809
7810 // TODO: More robust semantics around save-while-in-flight
7811 willCommit: Ember.K,
7812 didCommit: function(record) {
7813 record.transitionTo('saved');
7814
7815 record.send('invokeLifecycleCallbacks');
7816 },
7817
7818 becameError: function(record) {
7819 record.transitionTo('uncommitted');
7820 record.triggerLater('becameError', record);
7821 }
7822 },
7823
7824 // Once the adapter indicates that the deletion has
7825 // been saved, the record enters the `saved` substate
7826 // of `deleted`.
7827 saved: {
7828 // FLAGS
7829 isDirty: false,
7830
7831 setup: function(record) {
7832 var store = get(record, 'store');
7833 store.dematerializeRecord(record);
7834 },
7835
7836 invokeLifecycleCallbacks: function(record) {
7837 record.triggerLater('didDelete', record);
7838 record.triggerLater('didCommit', record);
7839 },
7840
7841 willCommit: Ember.K,
7842
7843 didCommit: Ember.K
7844 }
7845 },
7846
7847 invokeLifecycleCallbacks: function(record, dirtyType) {
7848 if (dirtyType === 'created') {
7849 record.triggerLater('didCreate', record);
7850 } else {
7851 record.triggerLater('didUpdate', record);
7852 }
7853
7854 record.triggerLater('didCommit', record);
7855 }
7856 };
7857
7858 function wireState(object, parent, name) {
7859 /*jshint proto:true*/
7860 // TODO: Use Object.create and copy instead
7861 object = mixin(parent ? Ember.create(parent) : {}, object);
7862 object.parentState = parent;
7863 object.stateName = name;
7864
7865 for (var prop in object) {
7866 if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; }
7867 if (typeof object[prop] === 'object') {
7868 object[prop] = wireState(object[prop], object, name + "." + prop);
7869 }
7870 }
7871
7872 return object;
7873 }
7874
7875 RootState = wireState(RootState, null, "root");
7876
7877 __exports__["default"] = RootState;
7878 });
7879define("ember-data/system/promise_proxies",
7880 ["exports"],
7881 function(__exports__) {
7882 "use strict";
7883 var Promise = Ember.RSVP.Promise;
7884 var get = Ember.get;
7885
7886 /**
7887 A `PromiseArray` is an object that acts like both an `Ember.Array`
7888 and a promise. When the promise is resolved the resulting value
7889 will be set to the `PromiseArray`'s `content` property. This makes
7890 it easy to create data bindings with the `PromiseArray` that will be
7891 updated when the promise resolves.
7892
7893 For more information see the [Ember.PromiseProxyMixin
7894 documentation](/api/classes/Ember.PromiseProxyMixin.html).
7895
7896 Example
7897
7898 ```javascript
7899 var promiseArray = DS.PromiseArray.create({
7900 promise: $.getJSON('/some/remote/data.json')
7901 });
7902
7903 promiseArray.get('length'); // 0
7904
7905 promiseArray.then(function() {
7906 promiseArray.get('length'); // 100
7907 });
7908 ```
7909
7910 @class PromiseArray
7911 @namespace DS
7912 @extends Ember.ArrayProxy
7913 @uses Ember.PromiseProxyMixin
7914 */
7915 var PromiseArray = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin);
7916
7917 /**
7918 A `PromiseObject` is an object that acts like both an `Ember.Object`
7919 and a promise. When the promise is resolved, then the resulting value
7920 will be set to the `PromiseObject`'s `content` property. This makes
7921 it easy to create data bindings with the `PromiseObject` that will
7922 be updated when the promise resolves.
7923
7924 For more information see the [Ember.PromiseProxyMixin
7925 documentation](/api/classes/Ember.PromiseProxyMixin.html).
7926
7927 Example
7928
7929 ```javascript
7930 var promiseObject = DS.PromiseObject.create({
7931 promise: $.getJSON('/some/remote/data.json')
7932 });
7933
7934 promiseObject.get('name'); // null
7935
7936 promiseObject.then(function() {
7937 promiseObject.get('name'); // 'Tomster'
7938 });
7939 ```
7940
7941 @class PromiseObject
7942 @namespace DS
7943 @extends Ember.ObjectProxy
7944 @uses Ember.PromiseProxyMixin
7945 */
7946 var PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin);
7947
7948 var promiseObject = function(promise, label) {
7949 return PromiseObject.create({
7950 promise: Promise.resolve(promise, label)
7951 });
7952 };
7953
7954 var promiseArray = function(promise, label) {
7955 return PromiseArray.create({
7956 promise: Promise.resolve(promise, label)
7957 });
7958 };
7959
7960 /**
7961 A PromiseManyArray is a PromiseArray that also proxies certain method calls
7962 to the underlying manyArray.
7963 Right now we proxy:
7964 `reload()`
7965 */
7966
7967 var PromiseManyArray = PromiseArray.extend({
7968 reload: function() {
7969 //I don't think this should ever happen right now, but worth guarding if we refactor the async relationships
7970 Ember.assert('You are trying to reload an async manyArray before it has been created', get(this, 'content'));
7971 return get(this, 'content').reload();
7972 }
7973 });
7974
7975 var promiseManyArray = function(promise, label) {
7976 return PromiseManyArray.create({
7977 promise: Promise.resolve(promise, label)
7978 });
7979 };
7980
7981
7982 __exports__.PromiseArray = PromiseArray;
7983 __exports__.PromiseObject = PromiseObject;
7984 __exports__.PromiseManyArray = PromiseManyArray;
7985 __exports__.promiseArray = promiseArray;
7986 __exports__.promiseObject = promiseObject;
7987 __exports__.promiseManyArray = promiseManyArray;
7988 });
7989define("ember-data/system/record_array_manager",
7990 ["ember-data/system/record_arrays","ember-data/system/map","exports"],
7991 function(__dependency1__, __dependency2__, __exports__) {
7992 "use strict";
7993 /**
7994 @module ember-data
7995 */
7996
7997 var RecordArray = __dependency1__.RecordArray;
7998 var FilteredRecordArray = __dependency1__.FilteredRecordArray;
7999 var AdapterPopulatedRecordArray = __dependency1__.AdapterPopulatedRecordArray;
8000 var ManyArray = __dependency1__.ManyArray;
8001 var MapWithDefault = __dependency2__.MapWithDefault;
8002 var OrderedSet = __dependency2__.OrderedSet;
8003 var get = Ember.get;
8004 var forEach = Ember.EnumerableUtils.forEach;
8005
8006 /**
8007 @class RecordArrayManager
8008 @namespace DS
8009 @private
8010 @extends Ember.Object
8011 */
8012 __exports__["default"] = Ember.Object.extend({
8013 init: function() {
8014 this.filteredRecordArrays = MapWithDefault.create({
8015 defaultValue: function() { return []; }
8016 });
8017
8018 this.changedRecords = [];
8019 this._adapterPopulatedRecordArrays = [];
8020 },
8021
8022 recordDidChange: function(record) {
8023 if (this.changedRecords.push(record) !== 1) { return; }
8024
8025 Ember.run.schedule('actions', this, this.updateRecordArrays);
8026 },
8027
8028 recordArraysForRecord: function(record) {
8029 record._recordArrays = record._recordArrays || OrderedSet.create();
8030 return record._recordArrays;
8031 },
8032
8033 /**
8034 This method is invoked whenever data is loaded into the store by the
8035 adapter or updated by the adapter, or when a record has changed.
8036
8037 It updates all record arrays that a record belongs to.
8038
8039 To avoid thrashing, it only runs at most once per run loop.
8040
8041 @method updateRecordArrays
8042 @param {Class} type
8043 @param {Number|String} clientId
8044 */
8045 updateRecordArrays: function() {
8046 forEach(this.changedRecords, function(record) {
8047 if (get(record, 'isDeleted')) {
8048 this._recordWasDeleted(record);
8049 } else {
8050 this._recordWasChanged(record);
8051 }
8052 }, this);
8053
8054 this.changedRecords.length = 0;
8055 },
8056
8057 _recordWasDeleted: function (record) {
8058 var recordArrays = record._recordArrays;
8059
8060 if (!recordArrays) { return; }
8061
8062 recordArrays.forEach(function(array){
8063 array.removeRecord(record);
8064 });
8065
8066 record._recordArrays = null;
8067 },
8068
8069 _recordWasChanged: function (record) {
8070 var type = record.constructor;
8071 var recordArrays = this.filteredRecordArrays.get(type);
8072 var filter;
8073
8074 forEach(recordArrays, function(array) {
8075 filter = get(array, 'filterFunction');
8076 this.updateRecordArray(array, filter, type, record);
8077 }, this);
8078
8079 // loop through all manyArrays containing an unloaded copy of this
8080 // clientId and notify them that the record was loaded.
8081 var manyArrays = record._loadingRecordArrays;
8082
8083 if (manyArrays) {
8084 for (var i=0, l=manyArrays.length; i<l; i++) {
8085 manyArrays[i].loadedRecord();
8086 }
8087
8088 record._loadingRecordArrays = [];
8089 }
8090 },
8091
8092 /**
8093 Update an individual filter.
8094
8095 @method updateRecordArray
8096 @param {DS.FilteredRecordArray} array
8097 @param {Function} filter
8098 @param {Class} type
8099 @param {Number|String} clientId
8100 */
8101 updateRecordArray: function(array, filter, type, record) {
8102 var shouldBeInArray;
8103
8104 if (!filter) {
8105 shouldBeInArray = true;
8106 } else {
8107 shouldBeInArray = filter(record);
8108 }
8109
8110 var recordArrays = this.recordArraysForRecord(record);
8111
8112 if (shouldBeInArray) {
8113 if (!recordArrays.has(array)) {
8114 array.pushRecord(record);
8115 recordArrays.add(array);
8116 }
8117 } else if (!shouldBeInArray) {
8118 recordArrays["delete"](array);
8119 array.removeRecord(record);
8120 }
8121 },
8122
8123 /**
8124 This method is invoked if the `filterFunction` property is
8125 changed on a `DS.FilteredRecordArray`.
8126
8127 It essentially re-runs the filter from scratch. This same
8128 method is invoked when the filter is created in th first place.
8129
8130 @method updateFilter
8131 @param {Array} array
8132 @param {String} type
8133 @param {Function} filter
8134 */
8135 updateFilter: function(array, type, filter) {
8136 var typeMap = this.store.typeMapFor(type);
8137 var records = typeMap.records, record;
8138
8139 for (var i=0, l=records.length; i<l; i++) {
8140 record = records[i];
8141
8142 if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) {
8143 this.updateRecordArray(array, filter, type, record);
8144 }
8145 }
8146 },
8147
8148 /**
8149 Create a `DS.ManyArray` for a type and list of record references, and index
8150 the `ManyArray` under each reference. This allows us to efficiently remove
8151 records from `ManyArray`s when they are deleted.
8152
8153 @method createManyArray
8154 @param {Class} type
8155 @param {Array} references
8156 @return {DS.ManyArray}
8157 */
8158 createManyArray: function(type, records) {
8159 var manyArray = ManyArray.create({
8160 type: type,
8161 content: records,
8162 store: this.store
8163 });
8164
8165 forEach(records, function(record) {
8166 var arrays = this.recordArraysForRecord(record);
8167 arrays.add(manyArray);
8168 }, this);
8169
8170 return manyArray;
8171 },
8172
8173 /**
8174 Create a `DS.RecordArray` for a type and register it for updates.
8175
8176 @method createRecordArray
8177 @param {Class} type
8178 @return {DS.RecordArray}
8179 */
8180 createRecordArray: function(type) {
8181 var array = RecordArray.create({
8182 type: type,
8183 content: Ember.A(),
8184 store: this.store,
8185 isLoaded: true
8186 });
8187
8188 this.registerFilteredRecordArray(array, type);
8189
8190 return array;
8191 },
8192
8193 /**
8194 Create a `DS.FilteredRecordArray` for a type and register it for updates.
8195
8196 @method createFilteredRecordArray
8197 @param {Class} type
8198 @param {Function} filter
8199 @param {Object} query (optional
8200 @return {DS.FilteredRecordArray}
8201 */
8202 createFilteredRecordArray: function(type, filter, query) {
8203 var array = FilteredRecordArray.create({
8204 query: query,
8205 type: type,
8206 content: Ember.A(),
8207 store: this.store,
8208 manager: this,
8209 filterFunction: filter
8210 });
8211
8212 this.registerFilteredRecordArray(array, type, filter);
8213
8214 return array;
8215 },
8216
8217 /**
8218 Create a `DS.AdapterPopulatedRecordArray` for a type with given query.
8219
8220 @method createAdapterPopulatedRecordArray
8221 @param {Class} type
8222 @param {Object} query
8223 @return {DS.AdapterPopulatedRecordArray}
8224 */
8225 createAdapterPopulatedRecordArray: function(type, query) {
8226 var array = AdapterPopulatedRecordArray.create({
8227 type: type,
8228 query: query,
8229 content: Ember.A(),
8230 store: this.store,
8231 manager: this
8232 });
8233
8234 this._adapterPopulatedRecordArrays.push(array);
8235
8236 return array;
8237 },
8238
8239 /**
8240 Register a RecordArray for a given type to be backed by
8241 a filter function. This will cause the array to update
8242 automatically when records of that type change attribute
8243 values or states.
8244
8245 @method registerFilteredRecordArray
8246 @param {DS.RecordArray} array
8247 @param {Class} type
8248 @param {Function} filter
8249 */
8250 registerFilteredRecordArray: function(array, type, filter) {
8251 var recordArrays = this.filteredRecordArrays.get(type);
8252 recordArrays.push(array);
8253
8254 this.updateFilter(array, type, filter);
8255 },
8256
8257 // Internally, we maintain a map of all unloaded IDs requested by
8258 // a ManyArray. As the adapter loads data into the store, the
8259 // store notifies any interested ManyArrays. When the ManyArray's
8260 // total number of loading records drops to zero, it becomes
8261 // `isLoaded` and fires a `didLoad` event.
8262 registerWaitingRecordArray: function(record, array) {
8263 var loadingRecordArrays = record._loadingRecordArrays || [];
8264 loadingRecordArrays.push(array);
8265 record._loadingRecordArrays = loadingRecordArrays;
8266 },
8267
8268 willDestroy: function(){
8269 this._super();
8270
8271 forEach(flatten(values(this.filteredRecordArrays.values)), destroy);
8272 forEach(this._adapterPopulatedRecordArrays, destroy);
8273 }
8274 });
8275
8276 function values(obj) {
8277 var result = [];
8278 var keys = Ember.keys(obj);
8279
8280 for (var i = 0; i < keys.length; i++) {
8281 result.push(obj[keys[i]]);
8282 }
8283
8284 return result;
8285 }
8286
8287 function destroy(entry) {
8288 entry.destroy();
8289 }
8290
8291 function flatten(list) {
8292 var length = list.length;
8293 var result = Ember.A();
8294
8295 for (var i = 0; i < length; i++) {
8296 result = result.concat(list[i]);
8297 }
8298
8299 return result;
8300 }
8301 });
8302define("ember-data/system/record_arrays",
8303 ["ember-data/system/record_arrays/record_array","ember-data/system/record_arrays/filtered_record_array","ember-data/system/record_arrays/adapter_populated_record_array","ember-data/system/record_arrays/many_array","exports"],
8304 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
8305 "use strict";
8306 /**
8307 @module ember-data
8308 */
8309
8310 var RecordArray = __dependency1__["default"];
8311 var FilteredRecordArray = __dependency2__["default"];
8312 var AdapterPopulatedRecordArray = __dependency3__["default"];
8313 var ManyArray = __dependency4__["default"];
8314
8315 __exports__.RecordArray = RecordArray;
8316 __exports__.FilteredRecordArray = FilteredRecordArray;
8317 __exports__.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray;
8318 __exports__.ManyArray = ManyArray;
8319 });
8320define("ember-data/system/record_arrays/adapter_populated_record_array",
8321 ["ember-data/system/record_arrays/record_array","exports"],
8322 function(__dependency1__, __exports__) {
8323 "use strict";
8324 var RecordArray = __dependency1__["default"];
8325 /**
8326 @module ember-data
8327 */
8328
8329 var get = Ember.get;
8330
8331 function cloneNull(source) {
8332 var clone = Object.create(null);
8333 for (var key in source) {
8334 clone[key] = source[key];
8335 }
8336 return clone;
8337 }
8338
8339 /**
8340 Represents an ordered list of records whose order and membership is
8341 determined by the adapter. For example, a query sent to the adapter
8342 may trigger a search on the server, whose results would be loaded
8343 into an instance of the `AdapterPopulatedRecordArray`.
8344
8345 @class AdapterPopulatedRecordArray
8346 @namespace DS
8347 @extends DS.RecordArray
8348 */
8349 __exports__["default"] = RecordArray.extend({
8350 query: null,
8351
8352 replace: function() {
8353 var type = get(this, 'type').toString();
8354 throw new Error("The result of a server query (on " + type + ") is immutable.");
8355 },
8356
8357 /**
8358 @method load
8359 @private
8360 @param {Array} data
8361 */
8362 load: function(data) {
8363 var store = get(this, 'store');
8364 var type = get(this, 'type');
8365 var records = store.pushMany(type, data);
8366 var meta = store.metadataFor(type);
8367
8368 this.setProperties({
8369 content: Ember.A(records),
8370 isLoaded: true,
8371 meta: cloneNull(meta)
8372 });
8373
8374 records.forEach(function(record) {
8375 this.manager.recordArraysForRecord(record).add(this);
8376 }, this);
8377
8378 // TODO: should triggering didLoad event be the last action of the runLoop?
8379 Ember.run.once(this, 'trigger', 'didLoad');
8380 }
8381 });
8382 });
8383define("ember-data/system/record_arrays/filtered_record_array",
8384 ["ember-data/system/record_arrays/record_array","exports"],
8385 function(__dependency1__, __exports__) {
8386 "use strict";
8387 var RecordArray = __dependency1__["default"];
8388
8389 /**
8390 @module ember-data
8391 */
8392
8393 var get = Ember.get;
8394
8395 /**
8396 Represents a list of records whose membership is determined by the
8397 store. As records are created, loaded, or modified, the store
8398 evaluates them to determine if they should be part of the record
8399 array.
8400
8401 @class FilteredRecordArray
8402 @namespace DS
8403 @extends DS.RecordArray
8404 */
8405 __exports__["default"] = RecordArray.extend({
8406 /**
8407 The filterFunction is a function used to test records from the store to
8408 determine if they should be part of the record array.
8409
8410 Example
8411
8412 ```javascript
8413 var allPeople = store.all('person');
8414 allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"]
8415
8416 var people = store.filter('person', function(person) {
8417 if (person.get('name').match(/Katz$/)) { return true; }
8418 });
8419 people.mapBy('name'); // ["Yehuda Katz"]
8420
8421 var notKatzFilter = function(person) {
8422 return !person.get('name').match(/Katz$/);
8423 };
8424 people.set('filterFunction', notKatzFilter);
8425 people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"]
8426 ```
8427
8428 @method filterFunction
8429 @param {DS.Model} record
8430 @return {Boolean} `true` if the record should be in the array
8431 */
8432 filterFunction: null,
8433 isLoaded: true,
8434
8435 replace: function() {
8436 var type = get(this, 'type').toString();
8437 throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
8438 },
8439
8440 /**
8441 @method updateFilter
8442 @private
8443 */
8444 _updateFilter: function() {
8445 var manager = get(this, 'manager');
8446 manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
8447 },
8448
8449 updateFilter: Ember.observer(function() {
8450 Ember.run.once(this, this._updateFilter);
8451 }, 'filterFunction')
8452 });
8453 });
8454define("ember-data/system/record_arrays/many_array",
8455 ["ember-data/system/record_arrays/record_array","exports"],
8456 function(__dependency1__, __exports__) {
8457 "use strict";
8458 var RecordArray = __dependency1__["default"];
8459
8460 /**
8461 @module ember-data
8462 */
8463
8464 var get = Ember.get, set = Ember.set;
8465
8466 /**
8467 A `ManyArray` is a `RecordArray` that represents the contents of a has-many
8468 relationship.
8469
8470 The `ManyArray` is instantiated lazily the first time the relationship is
8471 requested.
8472
8473 ### Inverses
8474
8475 Often, the relationships in Ember Data applications will have
8476 an inverse. For example, imagine the following models are
8477 defined:
8478
8479 ```javascript
8480 App.Post = DS.Model.extend({
8481 comments: DS.hasMany('comment')
8482 });
8483
8484 App.Comment = DS.Model.extend({
8485 post: DS.belongsTo('post')
8486 });
8487 ```
8488
8489 If you created a new instance of `App.Post` and added
8490 a `App.Comment` record to its `comments` has-many
8491 relationship, you would expect the comment's `post`
8492 property to be set to the post that contained
8493 the has-many.
8494
8495 We call the record to which a relationship belongs the
8496 relationship's _owner_.
8497
8498 @class ManyArray
8499 @namespace DS
8500 @extends DS.RecordArray
8501 */
8502 __exports__["default"] = RecordArray.extend({
8503 init: function() {
8504 this._super.apply(this, arguments);
8505 },
8506
8507 /**
8508 `true` if the relationship is polymorphic, `false` otherwise.
8509
8510 @property {Boolean} isPolymorphic
8511 @private
8512 */
8513 isPolymorphic: false,
8514
8515 // LOADING STATE
8516
8517 isLoaded: false,
8518
8519 /**
8520 The relationship which manages this array.
8521
8522 @property {DS.Model} owner
8523 @private
8524 */
8525
8526 relationship: null,
8527
8528
8529 /**
8530 Used for async `hasMany` arrays
8531 to keep track of when they will resolve.
8532
8533 @property {Ember.RSVP.Promise} promise
8534 @private
8535 */
8536 promise: null,
8537
8538 /**
8539 @method loadingRecordsCount
8540 @param {Number} count
8541 @private
8542 */
8543 loadingRecordsCount: function(count) {
8544 this.loadingRecordsCount = count;
8545 },
8546
8547 /**
8548 @method loadedRecord
8549 @private
8550 */
8551 loadedRecord: function() {
8552 this.loadingRecordsCount--;
8553 if (this.loadingRecordsCount === 0) {
8554 set(this, 'isLoaded', true);
8555 this.trigger('didLoad');
8556 }
8557 },
8558
8559 replaceContent: function(idx, amt, objects){
8560 var records;
8561 if (amt > 0){
8562 records = get(this, 'content').slice(idx, idx+amt);
8563 this.get('relationship').removeRecords(records);
8564 }
8565 if (objects){
8566 this.get('relationship').addRecords(objects, idx);
8567 }
8568 },
8569 /**
8570 @method reload
8571 @public
8572 */
8573 reload: function() {
8574 return this.relationship.reload();
8575 },
8576
8577 /**
8578 Create a child record within the owner
8579
8580 @method createRecord
8581 @private
8582 @param {Object} hash
8583 @return {DS.Model} record
8584 */
8585 createRecord: function(hash) {
8586 var store = get(this, 'store');
8587 var type = get(this, 'type');
8588 var record;
8589
8590 Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic'));
8591
8592 record = store.createRecord.call(store, type, hash);
8593 this.pushObject(record);
8594
8595 return record;
8596 }
8597 });
8598 });
8599define("ember-data/system/record_arrays/record_array",
8600 ["ember-data/system/promise_proxies","exports"],
8601 function(__dependency1__, __exports__) {
8602 "use strict";
8603 /**
8604 @module ember-data
8605 */
8606
8607 var PromiseArray = __dependency1__.PromiseArray;
8608 var get = Ember.get;
8609
8610 /**
8611 A record array is an array that contains records of a certain type. The record
8612 array materializes records as needed when they are retrieved for the first
8613 time. You should not create record arrays yourself. Instead, an instance of
8614 `DS.RecordArray` or its subclasses will be returned by your application's store
8615 in response to queries.
8616
8617 @class RecordArray
8618 @namespace DS
8619 @extends Ember.ArrayProxy
8620 @uses Ember.Evented
8621 */
8622
8623 __exports__["default"] = Ember.ArrayProxy.extend(Ember.Evented, {
8624 /**
8625 The model type contained by this record array.
8626
8627 @property type
8628 @type DS.Model
8629 */
8630 type: null,
8631
8632 /**
8633 The array of client ids backing the record array. When a
8634 record is requested from the record array, the record
8635 for the client id at the same index is materialized, if
8636 necessary, by the store.
8637
8638 @property content
8639 @private
8640 @type Ember.Array
8641 */
8642 content: null,
8643
8644 /**
8645 The flag to signal a `RecordArray` is currently loading data.
8646
8647 Example
8648
8649 ```javascript
8650 var people = store.all('person');
8651 people.get('isLoaded'); // true
8652 ```
8653
8654 @property isLoaded
8655 @type Boolean
8656 */
8657 isLoaded: false,
8658 /**
8659 The flag to signal a `RecordArray` is currently loading data.
8660
8661 Example
8662
8663 ```javascript
8664 var people = store.all('person');
8665 people.get('isUpdating'); // false
8666 people.update();
8667 people.get('isUpdating'); // true
8668 ```
8669
8670 @property isUpdating
8671 @type Boolean
8672 */
8673 isUpdating: false,
8674
8675 /**
8676 The store that created this record array.
8677
8678 @property store
8679 @private
8680 @type DS.Store
8681 */
8682 store: null,
8683
8684 /**
8685 Retrieves an object from the content by index.
8686
8687 @method objectAtContent
8688 @private
8689 @param {Number} index
8690 @return {DS.Model} record
8691 */
8692 objectAtContent: function(index) {
8693 var content = get(this, 'content');
8694
8695 return content.objectAt(index);
8696 },
8697
8698 /**
8699 Used to get the latest version of all of the records in this array
8700 from the adapter.
8701
8702 Example
8703
8704 ```javascript
8705 var people = store.all('person');
8706 people.get('isUpdating'); // false
8707 people.update();
8708 people.get('isUpdating'); // true
8709 ```
8710
8711 @method update
8712 */
8713 update: function() {
8714 if (get(this, 'isUpdating')) { return; }
8715
8716 var store = get(this, 'store');
8717 var type = get(this, 'type');
8718
8719 return store.fetchAll(type, this);
8720 },
8721
8722 /**
8723 Adds a record to the `RecordArray` without duplicates
8724
8725 @method addRecord
8726 @private
8727 @param {DS.Model} record
8728 @param {DS.Model} an optional index to insert at
8729 */
8730 addRecord: function(record, idx) {
8731 var content = get(this, 'content');
8732 if (idx === undefined) {
8733 content.addObject(record);
8734 } else {
8735 if (!content.contains(record)) {
8736 content.insertAt(idx, record);
8737 }
8738 }
8739 },
8740
8741 /**
8742 Adds a record to the `RecordArray`, but allows duplicates
8743
8744 @method pushRecord
8745 @private
8746 @param {DS.Model} record
8747 */
8748 pushRecord: function(record) {
8749 get(this, 'content').pushObject(record);
8750 },
8751
8752
8753 /**
8754 Removes a record to the `RecordArray`.
8755
8756 @method removeRecord
8757 @private
8758 @param {DS.Model} record
8759 */
8760 removeRecord: function(record) {
8761 get(this, 'content').removeObject(record);
8762 },
8763
8764 /**
8765 Saves all of the records in the `RecordArray`.
8766
8767 Example
8768
8769 ```javascript
8770 var messages = store.all('message');
8771 messages.forEach(function(message) {
8772 message.set('hasBeenSeen', true);
8773 });
8774 messages.save();
8775 ```
8776
8777 @method save
8778 @return {DS.PromiseArray} promise
8779 */
8780 save: function() {
8781 var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
8782 var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) {
8783 return Ember.A(array);
8784 }, null, "DS: RecordArray#save apply Ember.NativeArray");
8785
8786 return PromiseArray.create({ promise: promise });
8787 },
8788
8789 _dissociateFromOwnRecords: function() {
8790 var array = this;
8791
8792 this.forEach(function(record){
8793 var recordArrays = record._recordArrays;
8794
8795 if (recordArrays) {
8796 recordArrays["delete"](array);
8797 }
8798 });
8799 },
8800
8801 willDestroy: function(){
8802 this._dissociateFromOwnRecords();
8803 this._super();
8804 }
8805 });
8806 });
8807define("ember-data/system/relationship-meta",
8808 ["ember-inflector/system","exports"],
8809 function(__dependency1__, __exports__) {
8810 "use strict";
8811 var singularize = __dependency1__.singularize;
8812
8813 function typeForRelationshipMeta(store, meta) {
8814 var typeKey, type;
8815
8816 typeKey = meta.type || meta.key;
8817 if (typeof typeKey === 'string') {
8818 if (meta.kind === 'hasMany') {
8819 typeKey = singularize(typeKey);
8820 }
8821 type = store.modelFor(typeKey);
8822 } else {
8823 type = meta.type;
8824 }
8825
8826 return type;
8827 }
8828
8829 __exports__.typeForRelationshipMeta = typeForRelationshipMeta;function relationshipFromMeta(store, meta) {
8830 return {
8831 key: meta.key,
8832 kind: meta.kind,
8833 type: typeForRelationshipMeta(store, meta),
8834 options: meta.options,
8835 parentType: meta.parentType,
8836 isRelationship: true
8837 };
8838 }
8839
8840 __exports__.relationshipFromMeta = relationshipFromMeta;
8841 });
8842define("ember-data/system/relationships",
8843 ["./relationships/belongs_to","./relationships/has_many","ember-data/system/relationships/ext","exports"],
8844 function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
8845 "use strict";
8846 /**
8847 @module ember-data
8848 */
8849
8850 var belongsTo = __dependency1__["default"];
8851 var hasMany = __dependency2__["default"];
8852
8853
8854 __exports__.belongsTo = belongsTo;
8855 __exports__.hasMany = hasMany;
8856 });
8857define("ember-data/system/relationships/belongs_to",
8858 ["ember-data/system/model","exports"],
8859 function(__dependency1__, __exports__) {
8860 "use strict";
8861 var Model = __dependency1__.Model;
8862
8863
8864 /**
8865 `DS.belongsTo` is used to define One-To-One and One-To-Many
8866 relationships on a [DS.Model](/api/data/classes/DS.Model.html).
8867
8868
8869 `DS.belongsTo` takes an optional hash as a second parameter, currently
8870 supported options are:
8871
8872 - `async`: A boolean value used to explicitly declare this to be an async relationship.
8873 - `inverse`: A string used to identify the inverse property on a
8874 related model in a One-To-Many relationship. See [Explicit Inverses](#toc_explicit-inverses)
8875
8876 #### One-To-One
8877 To declare a one-to-one relationship between two models, use
8878 `DS.belongsTo`:
8879
8880 ```javascript
8881 App.User = DS.Model.extend({
8882 profile: DS.belongsTo('profile')
8883 });
8884
8885 App.Profile = DS.Model.extend({
8886 user: DS.belongsTo('user')
8887 });
8888 ```
8889
8890 #### One-To-Many
8891 To declare a one-to-many relationship between two models, use
8892 `DS.belongsTo` in combination with `DS.hasMany`, like this:
8893
8894 ```javascript
8895 App.Post = DS.Model.extend({
8896 comments: DS.hasMany('comment')
8897 });
8898
8899 App.Comment = DS.Model.extend({
8900 post: DS.belongsTo('post')
8901 });
8902 ```
8903
8904 @namespace
8905 @method belongsTo
8906 @for DS
8907 @param {String or DS.Model} type the model type of the relationship
8908 @param {Object} options a hash of options
8909 @return {Ember.computed} relationship
8910 */
8911 function belongsTo(type, options) {
8912 if (typeof type === 'object') {
8913 options = type;
8914 type = undefined;
8915 } else {
8916 Ember.assert("The first argument to DS.belongsTo must be a string representing a model type key, e.g. use DS.belongsTo('person') to define a relation to the App.Person model", !!type && (typeof type === 'string' || Model.detect(type)));
8917 }
8918
8919 options = options || {};
8920
8921 var meta = {
8922 type: type,
8923 isRelationship: true,
8924 options: options,
8925 kind: 'belongsTo',
8926 key: null
8927 };
8928
8929 return Ember.computed(function(key, value) {
8930 if (arguments.length>1) {
8931 if ( value === undefined ) {
8932 value = null;
8933 }
8934 if (value && value.then) {
8935 this._relationships[key].setRecordPromise(value);
8936 } else {
8937 this._relationships[key].setRecord(value);
8938 }
8939 }
8940
8941 return this._relationships[key].getRecord();
8942 }).meta(meta);
8943 }
8944
8945 /**
8946 These observers observe all `belongsTo` relationships on the record. See
8947 `relationships/ext` to see how these observers get their dependencies.
8948
8949 @class Model
8950 @namespace DS
8951 */
8952 Model.reopen({
8953 notifyBelongsToAdded: function(key, relationship) {
8954 this.notifyPropertyChange(key);
8955 },
8956
8957 notifyBelongsToRemoved: function(key) {
8958 this.notifyPropertyChange(key);
8959 }
8960 });
8961
8962 __exports__["default"] = belongsTo;
8963 });
8964define("ember-data/system/relationships/ext",
8965 ["ember-data/system/relationship-meta","ember-data/system/model","ember-data/system/map"],
8966 function(__dependency1__, __dependency2__, __dependency3__) {
8967 "use strict";
8968 var typeForRelationshipMeta = __dependency1__.typeForRelationshipMeta;
8969 var relationshipFromMeta = __dependency1__.relationshipFromMeta;
8970 var Model = __dependency2__.Model;
8971 var Map = __dependency3__.Map;
8972 var MapWithDefault = __dependency3__.MapWithDefault;
8973
8974 var get = Ember.get;
8975 var filter = Ember.ArrayPolyfills.filter;
8976
8977 /**
8978 @module ember-data
8979 */
8980
8981 /*
8982 This file defines several extensions to the base `DS.Model` class that
8983 add support for one-to-many relationships.
8984 */
8985
8986 /**
8987 @class Model
8988 @namespace DS
8989 */
8990 Model.reopen({
8991
8992 /**
8993 This Ember.js hook allows an object to be notified when a property
8994 is defined.
8995
8996 In this case, we use it to be notified when an Ember Data user defines a
8997 belongs-to relationship. In that case, we need to set up observers for
8998 each one, allowing us to track relationship changes and automatically
8999 reflect changes in the inverse has-many array.
9000
9001 This hook passes the class being set up, as well as the key and value
9002 being defined. So, for example, when the user does this:
9003
9004 ```javascript
9005 DS.Model.extend({
9006 parent: DS.belongsTo('user')
9007 });
9008 ```
9009
9010 This hook would be called with "parent" as the key and the computed
9011 property returned by `DS.belongsTo` as the value.
9012
9013 @method didDefineProperty
9014 @param {Object} proto
9015 @param {String} key
9016 @param {Ember.ComputedProperty} value
9017 */
9018 didDefineProperty: function(proto, key, value) {
9019 // Check if the value being set is a computed property.
9020 if (value instanceof Ember.ComputedProperty) {
9021
9022 // If it is, get the metadata for the relationship. This is
9023 // populated by the `DS.belongsTo` helper when it is creating
9024 // the computed property.
9025 var meta = value.meta();
9026
9027 meta.parentType = proto.constructor;
9028 }
9029 }
9030 });
9031
9032 /*
9033 These DS.Model extensions add class methods that provide relationship
9034 introspection abilities about relationships.
9035
9036 A note about the computed properties contained here:
9037
9038 **These properties are effectively sealed once called for the first time.**
9039 To avoid repeatedly doing expensive iteration over a model's fields, these
9040 values are computed once and then cached for the remainder of the runtime of
9041 your application.
9042
9043 If your application needs to modify a class after its initial definition
9044 (for example, using `reopen()` to add additional attributes), make sure you
9045 do it before using your model with the store, which uses these properties
9046 extensively.
9047 */
9048
9049 Model.reopenClass({
9050
9051 /**
9052 For a given relationship name, returns the model type of the relationship.
9053
9054 For example, if you define a model like this:
9055
9056 ```javascript
9057 App.Post = DS.Model.extend({
9058 comments: DS.hasMany('comment')
9059 });
9060 ```
9061
9062 Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
9063
9064 @method typeForRelationship
9065 @static
9066 @param {String} name the name of the relationship
9067 @return {subclass of DS.Model} the type of the relationship, or undefined
9068 */
9069 typeForRelationship: function(name) {
9070 var relationship = get(this, 'relationshipsByName').get(name);
9071 return relationship && relationship.type;
9072 },
9073
9074 inverseMap: Ember.computed(function() {
9075 return Object.create(null);
9076 }),
9077
9078 /*
9079 Find the relationship which is the inverse of the one asked for.
9080
9081 For example, if you define models like this:
9082
9083 ```javascript
9084 App.Post = DS.Model.extend({
9085 comments: DS.hasMany('message')
9086 });
9087
9088 App.Message = DS.Model.extend({
9089 owner: DS.belongsTo('post')
9090 });
9091 ```
9092
9093 App.Post.inverseFor('comments') -> {type: App.Message, name:'owner', kind:'belongsTo'}
9094 App.Message.inverseFor('owner') -> {type: App.Post, name:'comments', kind:'hasMany'}
9095
9096 @method inverseFor
9097 @static
9098 @param {String} name the name of the relationship
9099 @return {Object} the inverse relationship, or null
9100 */
9101 inverseFor: function(name) {
9102 var inverseMap = get(this, 'inverseMap');
9103 if (inverseMap[name]) {
9104 return inverseMap[name];
9105 } else {
9106 var inverse = this._findInverseFor(name);
9107 inverseMap[name] = inverse;
9108 return inverse;
9109 }
9110 },
9111
9112 //Calculate the inverse, ignoring the cache
9113 _findInverseFor: function(name) {
9114
9115 var inverseType = this.typeForRelationship(name);
9116 if (!inverseType) {
9117 return null;
9118 }
9119
9120 //If inverse is manually specified to be null, like `comments: DS.hasMany('message', {inverse: null})`
9121 var options = this.metaForProperty(name).options;
9122 if (options.inverse === null) { return null; }
9123
9124 var inverseName, inverseKind, inverse;
9125
9126 //If inverse is specified manually, return the inverse
9127 if (options.inverse) {
9128 inverseName = options.inverse;
9129 inverse = Ember.get(inverseType, 'relationshipsByName').get(inverseName);
9130
9131 Ember.assert("We found no inverse relationships by the name of '" + inverseName + "' on the '" + inverseType.typeKey +
9132 "' model. This is most likely due to a missing attribute on your model definition.", !Ember.isNone(inverse));
9133
9134 inverseKind = inverse.kind;
9135 } else {
9136 //No inverse was specified manually, we need to use a heuristic to guess one
9137 var possibleRelationships = findPossibleInverses(this, inverseType);
9138
9139 if (possibleRelationships.length === 0) { return null; }
9140
9141 var filteredRelationships = filter.call(possibleRelationships, function(possibleRelationship) {
9142 var optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options;
9143 return name === optionsForRelationship.inverse;
9144 });
9145
9146 Ember.assert("You defined the '" + name + "' relationship on " + this + ", but you defined the inverse relationships of type " +
9147 inverseType.toString() + " multiple times. Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses",
9148 filteredRelationships.length < 2);
9149
9150 if (filteredRelationships.length === 1 ) {
9151 possibleRelationships = filteredRelationships;
9152 }
9153
9154 Ember.assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " +
9155 this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses",
9156 possibleRelationships.length === 1);
9157
9158 inverseName = possibleRelationships[0].name;
9159 inverseKind = possibleRelationships[0].kind;
9160 }
9161
9162 function findPossibleInverses(type, inverseType, relationshipsSoFar) {
9163 var possibleRelationships = relationshipsSoFar || [];
9164
9165 var relationshipMap = get(inverseType, 'relationships');
9166 if (!relationshipMap) { return; }
9167
9168 var relationships = relationshipMap.get(type);
9169
9170 relationships = filter.call(relationships, function(relationship) {
9171 var optionsForRelationship = inverseType.metaForProperty(relationship.name).options;
9172
9173 if (!optionsForRelationship.inverse){
9174 return true;
9175 }
9176
9177 return name === optionsForRelationship.inverse;
9178 });
9179
9180 if (relationships) {
9181 possibleRelationships.push.apply(possibleRelationships, relationships);
9182 }
9183
9184 //Recurse to support polymorphism
9185 if (type.superclass) {
9186 findPossibleInverses(type.superclass, inverseType, possibleRelationships);
9187 }
9188
9189 return possibleRelationships;
9190 }
9191
9192 return {
9193 type: inverseType,
9194 name: inverseName,
9195 kind: inverseKind
9196 };
9197 },
9198
9199 /**
9200 The model's relationships as a map, keyed on the type of the
9201 relationship. The value of each entry is an array containing a descriptor
9202 for each relationship with that type, describing the name of the relationship
9203 as well as the type.
9204
9205 For example, given the following model definition:
9206
9207 ```javascript
9208 App.Blog = DS.Model.extend({
9209 users: DS.hasMany('user'),
9210 owner: DS.belongsTo('user'),
9211 posts: DS.hasMany('post')
9212 });
9213 ```
9214
9215 This computed property would return a map describing these
9216 relationships, like this:
9217
9218 ```javascript
9219 var relationships = Ember.get(App.Blog, 'relationships');
9220 relationships.get(App.User);
9221 //=> [ { name: 'users', kind: 'hasMany' },
9222 // { name: 'owner', kind: 'belongsTo' } ]
9223 relationships.get(App.Post);
9224 //=> [ { name: 'posts', kind: 'hasMany' } ]
9225 ```
9226
9227 @property relationships
9228 @static
9229 @type Ember.Map
9230 @readOnly
9231 */
9232 relationships: Ember.computed(function() {
9233 var map = new MapWithDefault({
9234 defaultValue: function() { return []; }
9235 });
9236
9237 // Loop through each computed property on the class
9238 this.eachComputedProperty(function(name, meta) {
9239 // If the computed property is a relationship, add
9240 // it to the map.
9241 if (meta.isRelationship) {
9242 meta.key = name;
9243 var relationshipsForType = map.get(typeForRelationshipMeta(this.store, meta));
9244
9245 relationshipsForType.push({
9246 name: name,
9247 kind: meta.kind
9248 });
9249 }
9250 });
9251
9252 return map;
9253 }).cacheable(false).readOnly(),
9254
9255 /**
9256 A hash containing lists of the model's relationships, grouped
9257 by the relationship kind. For example, given a model with this
9258 definition:
9259
9260 ```javascript
9261 App.Blog = DS.Model.extend({
9262 users: DS.hasMany('user'),
9263 owner: DS.belongsTo('user'),
9264
9265 posts: DS.hasMany('post')
9266 });
9267 ```
9268
9269 This property would contain the following:
9270
9271 ```javascript
9272 var relationshipNames = Ember.get(App.Blog, 'relationshipNames');
9273 relationshipNames.hasMany;
9274 //=> ['users', 'posts']
9275 relationshipNames.belongsTo;
9276 //=> ['owner']
9277 ```
9278
9279 @property relationshipNames
9280 @static
9281 @type Object
9282 @readOnly
9283 */
9284 relationshipNames: Ember.computed(function() {
9285 var names = {
9286 hasMany: [],
9287 belongsTo: []
9288 };
9289
9290 this.eachComputedProperty(function(name, meta) {
9291 if (meta.isRelationship) {
9292 names[meta.kind].push(name);
9293 }
9294 });
9295
9296 return names;
9297 }),
9298
9299 /**
9300 An array of types directly related to a model. Each type will be
9301 included once, regardless of the number of relationships it has with
9302 the model.
9303
9304 For example, given a model with this definition:
9305
9306 ```javascript
9307 App.Blog = DS.Model.extend({
9308 users: DS.hasMany('user'),
9309 owner: DS.belongsTo('user'),
9310
9311 posts: DS.hasMany('post')
9312 });
9313 ```
9314
9315 This property would contain the following:
9316
9317 ```javascript
9318 var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
9319 //=> [ App.User, App.Post ]
9320 ```
9321
9322 @property relatedTypes
9323 @static
9324 @type Ember.Array
9325 @readOnly
9326 */
9327 relatedTypes: Ember.computed(function() {
9328 var type;
9329 var types = Ember.A();
9330
9331 // Loop through each computed property on the class,
9332 // and create an array of the unique types involved
9333 // in relationships
9334 this.eachComputedProperty(function(name, meta) {
9335 if (meta.isRelationship) {
9336 meta.key = name;
9337 type = typeForRelationshipMeta(this.store, meta);
9338
9339 Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type);
9340
9341 if (!types.contains(type)) {
9342 Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type);
9343 types.push(type);
9344 }
9345 }
9346 });
9347
9348 return types;
9349 }).cacheable(false).readOnly(),
9350
9351 /**
9352 A map whose keys are the relationships of a model and whose values are
9353 relationship descriptors.
9354
9355 For example, given a model with this
9356 definition:
9357
9358 ```javascript
9359 App.Blog = DS.Model.extend({
9360 users: DS.hasMany('user'),
9361 owner: DS.belongsTo('user'),
9362
9363 posts: DS.hasMany('post')
9364 });
9365 ```
9366
9367 This property would contain the following:
9368
9369 ```javascript
9370 var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName');
9371 relationshipsByName.get('users');
9372 //=> { key: 'users', kind: 'hasMany', type: App.User }
9373 relationshipsByName.get('owner');
9374 //=> { key: 'owner', kind: 'belongsTo', type: App.User }
9375 ```
9376
9377 @property relationshipsByName
9378 @static
9379 @type Ember.Map
9380 @readOnly
9381 */
9382 relationshipsByName: Ember.computed(function() {
9383 var map = Map.create();
9384
9385 this.eachComputedProperty(function(name, meta) {
9386 if (meta.isRelationship) {
9387 meta.key = name;
9388 var relationship = relationshipFromMeta(this.store, meta);
9389 relationship.type = typeForRelationshipMeta(this.store, meta);
9390 map.set(name, relationship);
9391 }
9392 });
9393
9394 return map;
9395 }).cacheable(false).readOnly(),
9396
9397 /**
9398 A map whose keys are the fields of the model and whose values are strings
9399 describing the kind of the field. A model's fields are the union of all of its
9400 attributes and relationships.
9401
9402 For example:
9403
9404 ```javascript
9405
9406 App.Blog = DS.Model.extend({
9407 users: DS.hasMany('user'),
9408 owner: DS.belongsTo('user'),
9409
9410 posts: DS.hasMany('post'),
9411
9412 title: DS.attr('string')
9413 });
9414
9415 var fields = Ember.get(App.Blog, 'fields');
9416 fields.forEach(function(field, kind) {
9417 console.log(field, kind);
9418 });
9419
9420 // prints:
9421 // users, hasMany
9422 // owner, belongsTo
9423 // posts, hasMany
9424 // title, attribute
9425 ```
9426
9427 @property fields
9428 @static
9429 @type Ember.Map
9430 @readOnly
9431 */
9432 fields: Ember.computed(function() {
9433 var map = Map.create();
9434
9435 this.eachComputedProperty(function(name, meta) {
9436 if (meta.isRelationship) {
9437 map.set(name, meta.kind);
9438 } else if (meta.isAttribute) {
9439 map.set(name, 'attribute');
9440 }
9441 });
9442
9443 return map;
9444 }).readOnly(),
9445
9446 /**
9447 Given a callback, iterates over each of the relationships in the model,
9448 invoking the callback with the name of each relationship and its relationship
9449 descriptor.
9450
9451 @method eachRelationship
9452 @static
9453 @param {Function} callback the callback to invoke
9454 @param {any} binding the value to which the callback's `this` should be bound
9455 */
9456 eachRelationship: function(callback, binding) {
9457 get(this, 'relationshipsByName').forEach(function(relationship, name) {
9458 callback.call(binding, name, relationship);
9459 });
9460 },
9461
9462 /**
9463 Given a callback, iterates over each of the types related to a model,
9464 invoking the callback with the related type's class. Each type will be
9465 returned just once, regardless of how many different relationships it has
9466 with a model.
9467
9468 @method eachRelatedType
9469 @static
9470 @param {Function} callback the callback to invoke
9471 @param {any} binding the value to which the callback's `this` should be bound
9472 */
9473 eachRelatedType: function(callback, binding) {
9474 get(this, 'relatedTypes').forEach(function(type) {
9475 callback.call(binding, type);
9476 });
9477 },
9478
9479 determineRelationshipType: function(knownSide) {
9480 var knownKey = knownSide.key;
9481 var knownKind = knownSide.kind;
9482 var inverse = this.inverseFor(knownKey);
9483 var key, otherKind;
9484
9485 if (!inverse) {
9486 return knownKind === 'belongsTo' ? 'oneToNone' : 'manyToNone';
9487 }
9488
9489 key = inverse.name;
9490 otherKind = inverse.kind;
9491
9492 if (otherKind === 'belongsTo') {
9493 return knownKind === 'belongsTo' ? 'oneToOne' : 'manyToOne';
9494 } else {
9495 return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany';
9496 }
9497 }
9498
9499 });
9500
9501 Model.reopen({
9502 /**
9503 Given a callback, iterates over each of the relationships in the model,
9504 invoking the callback with the name of each relationship and its relationship
9505 descriptor.
9506
9507 @method eachRelationship
9508 @param {Function} callback the callback to invoke
9509 @param {any} binding the value to which the callback's `this` should be bound
9510 */
9511 eachRelationship: function(callback, binding) {
9512 this.constructor.eachRelationship(callback, binding);
9513 },
9514
9515 relationshipFor: function(name) {
9516 return get(this.constructor, 'relationshipsByName').get(name);
9517 },
9518
9519 inverseFor: function(key) {
9520 return this.constructor.inverseFor(key);
9521 }
9522
9523 });
9524 });
9525define("ember-data/system/relationships/has_many",
9526 ["ember-data/system/model","exports"],
9527 function(__dependency1__, __exports__) {
9528 "use strict";
9529 /**
9530 @module ember-data
9531 */
9532
9533 var Model = __dependency1__.Model;
9534
9535 /**
9536 `DS.hasMany` is used to define One-To-Many and Many-To-Many
9537 relationships on a [DS.Model](/api/data/classes/DS.Model.html).
9538
9539 `DS.hasMany` takes an optional hash as a second parameter, currently
9540 supported options are:
9541
9542 - `async`: A boolean value used to explicitly declare this to be an async relationship.
9543 - `inverse`: A string used to identify the inverse property on a related model.
9544
9545 #### One-To-Many
9546 To declare a one-to-many relationship between two models, use
9547 `DS.belongsTo` in combination with `DS.hasMany`, like this:
9548
9549 ```javascript
9550 App.Post = DS.Model.extend({
9551 comments: DS.hasMany('comment')
9552 });
9553
9554 App.Comment = DS.Model.extend({
9555 post: DS.belongsTo('post')
9556 });
9557 ```
9558
9559 #### Many-To-Many
9560 To declare a many-to-many relationship between two models, use
9561 `DS.hasMany`:
9562
9563 ```javascript
9564 App.Post = DS.Model.extend({
9565 tags: DS.hasMany('tag')
9566 });
9567
9568 App.Tag = DS.Model.extend({
9569 posts: DS.hasMany('post')
9570 });
9571 ```
9572
9573 #### Explicit Inverses
9574
9575 Ember Data will do its best to discover which relationships map to
9576 one another. In the one-to-many code above, for example, Ember Data
9577 can figure out that changing the `comments` relationship should update
9578 the `post` relationship on the inverse because post is the only
9579 relationship to that model.
9580
9581 However, sometimes you may have multiple `belongsTo`/`hasManys` for the
9582 same type. You can specify which property on the related model is
9583 the inverse using `DS.hasMany`'s `inverse` option:
9584
9585 ```javascript
9586 var belongsTo = DS.belongsTo,
9587 hasMany = DS.hasMany;
9588
9589 App.Comment = DS.Model.extend({
9590 onePost: belongsTo('post'),
9591 twoPost: belongsTo('post'),
9592 redPost: belongsTo('post'),
9593 bluePost: belongsTo('post')
9594 });
9595
9596 App.Post = DS.Model.extend({
9597 comments: hasMany('comment', {
9598 inverse: 'redPost'
9599 })
9600 });
9601 ```
9602
9603 You can also specify an inverse on a `belongsTo`, which works how
9604 you'd expect.
9605
9606 @namespace
9607 @method hasMany
9608 @for DS
9609 @param {String or DS.Model} type the model type of the relationship
9610 @param {Object} options a hash of options
9611 @return {Ember.computed} relationship
9612 */
9613 function hasMany(type, options) {
9614 if (typeof type === 'object') {
9615 options = type;
9616 type = undefined;
9617 }
9618
9619 options = options || {};
9620
9621 // Metadata about relationships is stored on the meta of
9622 // the relationship. This is used for introspection and
9623 // serialization. Note that `key` is populated lazily
9624 // the first time the CP is called.
9625 var meta = {
9626 type: type,
9627 isRelationship: true,
9628 options: options,
9629 kind: 'hasMany',
9630 key: null
9631 };
9632
9633 return Ember.computed(function(key) {
9634 var relationship = this._relationships[key];
9635 return relationship.getRecords();
9636 }).meta(meta).readOnly();
9637 }
9638
9639 Model.reopen({
9640 notifyHasManyAdded: function(key, record, idx) {
9641 var relationship = this._relationships[key];
9642 var manyArray = relationship.manyArray;
9643 manyArray.addRecord(record, idx);
9644 //We need to notifyPropertyChange in the adding case because we need to make sure
9645 //we fetch the newly added record in case it is unloaded
9646 //TODO(Igor): Consider whether we could do this only if the record state is unloaded
9647 this.notifyPropertyChange(key);
9648 },
9649
9650 notifyHasManyRemoved: function(key, record) {
9651 var relationship = this._relationships[key];
9652 var manyArray = relationship.manyArray;
9653 manyArray.removeRecord(record);
9654 }
9655 });
9656
9657
9658 __exports__["default"] = hasMany;
9659 });
9660define("ember-data/system/relationships/relationship",
9661 ["ember-data/system/promise_proxies","ember-data/system/map","exports"],
9662 function(__dependency1__, __dependency2__, __exports__) {
9663 "use strict";
9664 var PromiseManyArray = __dependency1__.PromiseManyArray;
9665 var PromiseObject = __dependency1__.PromiseObject;
9666 var OrderedSet = __dependency2__.OrderedSet;
9667
9668 var Relationship = function(store, record, inverseKey, relationshipMeta) {
9669 this.members = new OrderedSet();
9670 this.store = store;
9671 this.key = relationshipMeta.key;
9672 this.inverseKey = inverseKey;
9673 this.record = record;
9674 this.key = relationshipMeta.key;
9675 this.isAsync = relationshipMeta.options.async;
9676 this.relationshipMeta = relationshipMeta;
9677 //This probably breaks for polymorphic relationship in complex scenarios, due to
9678 //multiple possible typeKeys
9679 this.inverseKeyForImplicit = this.store.modelFor(this.record.constructor).typeKey + this.key;
9680 //Cached promise when fetching the relationship from a link
9681 this.linkPromise = null;
9682 };
9683
9684 Relationship.prototype = {
9685 constructor: Relationship,
9686
9687 destroy: Ember.K,
9688
9689 clear: function() {
9690 this.members.forEach(function(member) {
9691 this.removeRecord(member);
9692 }, this);
9693 },
9694
9695 disconnect: function(){
9696 this.members.forEach(function(member) {
9697 this.removeRecordFromInverse(member);
9698 }, this);
9699 },
9700
9701 reconnect: function(){
9702 this.members.forEach(function(member) {
9703 this.addRecordToInverse(member);
9704 }, this);
9705 },
9706
9707 removeRecords: function(records){
9708 var that = this;
9709 records.forEach(function(record){
9710 that.removeRecord(record);
9711 });
9712 },
9713
9714 addRecords: function(records, idx){
9715 var that = this;
9716 records.forEach(function(record){
9717 that.addRecord(record, idx);
9718 if (idx !== undefined) {
9719 idx++;
9720 }
9721 });
9722 },
9723
9724 addRecord: function(record, idx) {
9725 if (!this.members.has(record)) {
9726 this.members.add(record);
9727 this.notifyRecordRelationshipAdded(record, idx);
9728 if (this.inverseKey) {
9729 record._relationships[this.inverseKey].addRecord(this.record);
9730 } else {
9731 if (!record._implicitRelationships[this.inverseKeyForImplicit]) {
9732 record._implicitRelationships[this.inverseKeyForImplicit] = new Relationship(this.store, record, this.key, {options:{}});
9733 }
9734 record._implicitRelationships[this.inverseKeyForImplicit].addRecord(this.record);
9735 }
9736 this.record.updateRecordArrays();
9737 }
9738 },
9739
9740 removeRecord: function(record) {
9741 if (this.members.has(record)) {
9742 this.removeRecordFromOwn(record);
9743 if (this.inverseKey) {
9744 this.removeRecordFromInverse(record);
9745 } else {
9746 if (record._implicitRelationships[this.inverseKeyForImplicit]) {
9747 record._implicitRelationships[this.inverseKeyForImplicit].removeRecord(this.record);
9748 }
9749 }
9750 }
9751 },
9752
9753 addRecordToInverse: function(record) {
9754 if (this.inverseKey) {
9755 record._relationships[this.inverseKey].addRecord(this.record);
9756 }
9757 },
9758
9759 removeRecordFromInverse: function(record) {
9760 var inverseRelationship = record._relationships[this.inverseKey];
9761 //Need to check for existence, as the record might unloading at the moment
9762 if (inverseRelationship) {
9763 inverseRelationship.removeRecordFromOwn(this.record);
9764 }
9765 },
9766
9767 removeRecordFromOwn: function(record) {
9768 this.members["delete"](record);
9769 this.notifyRecordRelationshipRemoved(record);
9770 this.record.updateRecordArrays();
9771 },
9772
9773 updateLink: function(link) {
9774 if (link !== this.link) {
9775 this.link = link;
9776 this.linkPromise = null;
9777 this.record.notifyPropertyChange(this.key);
9778 }
9779 },
9780
9781 findLink: function() {
9782 if (this.linkPromise) {
9783 return this.linkPromise;
9784 } else {
9785 var promise = this.fetchLink();
9786 this.linkPromise = promise;
9787 return promise.then(function(result) {
9788 return result;
9789 });
9790 }
9791 },
9792
9793 updateRecordsFromAdapter: function(records) {
9794 //TODO Once we have adapter support, we need to handle updated and canonical changes
9795 this.computeChanges(records);
9796 },
9797
9798 notifyRecordRelationshipAdded: Ember.K,
9799 notifyRecordRelationshipRemoved: Ember.K
9800 };
9801
9802 var ManyRelationship = function(store, record, inverseKey, relationshipMeta) {
9803 this._super$constructor(store, record, inverseKey, relationshipMeta);
9804 this.belongsToType = relationshipMeta.type;
9805 this.manyArray = store.recordArrayManager.createManyArray(this.belongsToType, Ember.A());
9806 this.manyArray.relationship = this;
9807 this.isPolymorphic = relationshipMeta.options.polymorphic;
9808 this.manyArray.isPolymorphic = this.isPolymorphic;
9809 };
9810
9811 ManyRelationship.prototype = Object.create(Relationship.prototype);
9812 ManyRelationship.prototype.constructor = ManyRelationship;
9813 ManyRelationship.prototype._super$constructor = Relationship;
9814
9815 ManyRelationship.prototype.destroy = function() {
9816 this.manyArray.destroy();
9817 };
9818
9819 ManyRelationship.prototype.notifyRecordRelationshipAdded = function(record, idx) {
9820 Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.belongsToType.typeKey + "' allowed)", !this.belongsToType || record instanceof this.belongsToType);
9821 this.record.notifyHasManyAdded(this.key, record, idx);
9822 };
9823
9824 ManyRelationship.prototype.notifyRecordRelationshipRemoved = function(record) {
9825 this.record.notifyHasManyRemoved(this.key, record);
9826 };
9827
9828 ManyRelationship.prototype.reload = function() {
9829 var self = this;
9830 if (this.link) {
9831 return this.fetchLink();
9832 } else {
9833 return this.store.scheduleFetchMany(this.manyArray.toArray()).then(function() {
9834 //Goes away after the manyArray refactor
9835 self.manyArray.set('isLoaded', true);
9836 return self.manyArray;
9837 });
9838 }
9839 };
9840
9841 ManyRelationship.prototype.computeChanges = function(records) {
9842 var members = this.members;
9843 var recordsToRemove = [];
9844 var length;
9845 var record;
9846 var i;
9847
9848 records = setForArray(records);
9849
9850 members.forEach(function(member) {
9851 if (records.has(member)) return;
9852
9853 recordsToRemove.push(member);
9854 });
9855 this.removeRecords(recordsToRemove);
9856
9857 var hasManyArray = this.manyArray;
9858
9859 // Using records.toArray() since currently using
9860 // removeRecord can modify length, messing stuff up
9861 // forEach since it directly looks at "length" each
9862 // iteration
9863 records = records.toArray();
9864 length = records.length;
9865 for (i = 0; i < length; i++){
9866 record = records[i];
9867 //Need to preserve the order of incoming records
9868 if (hasManyArray.objectAt(i) === record ) {
9869 continue;
9870 }
9871 this.removeRecord(record);
9872 this.addRecord(record, i);
9873 }
9874 };
9875
9876 ManyRelationship.prototype.fetchLink = function() {
9877 var self = this;
9878 return this.store.findHasMany(this.record, this.link, this.relationshipMeta).then(function(records){
9879 self.updateRecordsFromAdapter(records);
9880 return self.manyArray;
9881 });
9882 };
9883
9884 ManyRelationship.prototype.findRecords = function() {
9885 var manyArray = this.manyArray;
9886 return this.store.findMany(manyArray.toArray()).then(function(){
9887 //Goes away after the manyArray refactor
9888 manyArray.set('isLoaded', true);
9889 return manyArray;
9890 });
9891 };
9892
9893 ManyRelationship.prototype.getRecords = function() {
9894 if (this.isAsync) {
9895 var self = this;
9896 var promise;
9897 if (this.link) {
9898 promise = this.findLink().then(function() {
9899 return self.findRecords();
9900 });
9901 } else {
9902 promise = this.findRecords();
9903 }
9904 return PromiseManyArray.create({
9905 content: this.manyArray,
9906 promise: promise
9907 });
9908 } else {
9909 Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.typeKey + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", this.manyArray.isEvery('isEmpty', false));
9910
9911 this.manyArray.set('isLoaded', true);
9912 return this.manyArray;
9913 }
9914 };
9915
9916 var BelongsToRelationship = function(store, record, inverseKey, relationshipMeta) {
9917 this._super$constructor(store, record, inverseKey, relationshipMeta);
9918 this.record = record;
9919 this.key = relationshipMeta.key;
9920 this.inverseRecord = null;
9921 };
9922
9923 BelongsToRelationship.prototype = Object.create(Relationship.prototype);
9924 BelongsToRelationship.prototype.constructor = BelongsToRelationship;
9925 BelongsToRelationship.prototype._super$constructor = Relationship;
9926
9927 BelongsToRelationship.prototype.setRecord = function(newRecord) {
9928 if (newRecord) {
9929 this.addRecord(newRecord);
9930 } else if (this.inverseRecord) {
9931 this.removeRecord(this.inverseRecord);
9932 }
9933 };
9934
9935 BelongsToRelationship.prototype._super$addRecord = Relationship.prototype.addRecord;
9936 BelongsToRelationship.prototype.addRecord = function(newRecord) {
9937 if (this.members.has(newRecord)){ return;}
9938 var type = this.relationshipMeta.type;
9939 Ember.assert("You can only add a '" + type.typeKey + "' record to this relationship", newRecord instanceof type);
9940
9941 if (this.inverseRecord) {
9942 this.removeRecord(this.inverseRecord);
9943 }
9944
9945 this.inverseRecord = newRecord;
9946 this._super$addRecord(newRecord);
9947 };
9948
9949 BelongsToRelationship.prototype.setRecordPromise = function(newPromise) {
9950 var content = newPromise.get && newPromise.get('content');
9951 Ember.assert("You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.", content !== undefined);
9952 this.setRecord(content);
9953 };
9954
9955 BelongsToRelationship.prototype.notifyRecordRelationshipAdded = function(newRecord) {
9956 this.record.notifyBelongsToAdded(this.key, this);
9957 };
9958
9959 BelongsToRelationship.prototype.notifyRecordRelationshipRemoved = function(record) {
9960 this.record.notifyBelongsToRemoved(this.key, this);
9961 };
9962
9963 BelongsToRelationship.prototype._super$removeRecordFromOwn = Relationship.prototype.removeRecordFromOwn;
9964 BelongsToRelationship.prototype.removeRecordFromOwn = function(record) {
9965 if (!this.members.has(record)){ return;}
9966 this._super$removeRecordFromOwn(record);
9967 this.inverseRecord = null;
9968 };
9969
9970 BelongsToRelationship.prototype.findRecord = function() {
9971 if (this.inverseRecord) {
9972 return this.store._findByRecord(this.inverseRecord);
9973 } else {
9974 return Ember.RSVP.Promise.resolve(null);
9975 }
9976 };
9977
9978 BelongsToRelationship.prototype.fetchLink = function() {
9979 var self = this;
9980 return this.store.findBelongsTo(this.record, this.link, this.relationshipMeta).then(function(record){
9981 self.addRecord(record);
9982 return record;
9983 });
9984 };
9985
9986 BelongsToRelationship.prototype.getRecord = function() {
9987 if (this.isAsync) {
9988 var promise;
9989 if (this.link){
9990 var self = this;
9991 promise = this.findLink().then(function() {
9992 return self.findRecord();
9993 });
9994 } else {
9995 promise = this.findRecord();
9996 }
9997
9998 return PromiseObject.create({
9999 promise: promise,
10000 content: this.inverseRecord
10001 });
10002 } else {
10003 Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.typeKey + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", this.inverseRecord === null || !this.inverseRecord.get('isEmpty'));
10004 return this.inverseRecord;
10005 }
10006 };
10007
10008 function setForArray(array) {
10009 var set = new OrderedSet();
10010
10011 if (array) {
10012 for (var i=0, l=array.length; i<l; i++) {
10013 set.add(array[i]);
10014 }
10015 }
10016
10017 return set;
10018 }
10019
10020 var createRelationshipFor = function(record, relationshipMeta, store){
10021 var inverseKey;
10022 var inverse = record.constructor.inverseFor(relationshipMeta.key);
10023
10024 if (inverse) {
10025 inverseKey = inverse.name;
10026 }
10027
10028 if (relationshipMeta.kind === 'hasMany'){
10029 return new ManyRelationship(store, record, inverseKey, relationshipMeta);
10030 }
10031 else {
10032 return new BelongsToRelationship(store, record, inverseKey, relationshipMeta);
10033 }
10034 };
10035
10036
10037 __exports__.Relationship = Relationship;
10038 __exports__.ManyRelationship = ManyRelationship;
10039 __exports__.BelongsToRelationship = BelongsToRelationship;
10040 __exports__.createRelationshipFor = createRelationshipFor;
10041 });
10042define("ember-data/system/store",
10043 ["ember-data/system/adapter","ember-inflector/system/string","ember-data/system/map","ember-data/system/promise_proxies","exports"],
10044 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
10045 "use strict";
10046 /*globals Ember*/
10047 /*jshint eqnull:true*/
10048
10049 /**
10050 @module ember-data
10051 */
10052
10053 var InvalidError = __dependency1__.InvalidError;
10054 var Adapter = __dependency1__.Adapter;
10055 var singularize = __dependency2__.singularize;
10056 var Map = __dependency3__.Map;
10057
10058 var promiseArray = __dependency4__.promiseArray;
10059 var promiseObject = __dependency4__.promiseObject;
10060
10061
10062 var get = Ember.get;
10063 var set = Ember.set;
10064 var once = Ember.run.once;
10065 var isNone = Ember.isNone;
10066 var forEach = Ember.EnumerableUtils.forEach;
10067 var indexOf = Ember.EnumerableUtils.indexOf;
10068 var map = Ember.EnumerableUtils.map;
10069 var Promise = Ember.RSVP.Promise;
10070 var copy = Ember.copy;
10071 var Store, RecordArrayManager, Model;
10072
10073 var camelize = Ember.String.camelize;
10074
10075 // Implementors Note:
10076 //
10077 // The variables in this file are consistently named according to the following
10078 // scheme:
10079 //
10080 // * +id+ means an identifier managed by an external source, provided inside
10081 // the data provided by that source. These are always coerced to be strings
10082 // before being used internally.
10083 // * +clientId+ means a transient numerical identifier generated at runtime by
10084 // the data store. It is important primarily because newly created objects may
10085 // not yet have an externally generated id.
10086 // * +reference+ means a record reference object, which holds metadata about a
10087 // record, even if it has not yet been fully materialized.
10088 // * +type+ means a subclass of DS.Model.
10089
10090 // Used by the store to normalize IDs entering the store. Despite the fact
10091 // that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`),
10092 // it is important that internally we use strings, since IDs may be serialized
10093 // and lose type information. For example, Ember's router may put a record's
10094 // ID into the URL, and if we later try to deserialize that URL and find the
10095 // corresponding record, we will not know if it is a string or a number.
10096 function coerceId(id) {
10097 return id == null ? null : id+'';
10098 }
10099
10100 /**
10101 The store contains all of the data for records loaded from the server.
10102 It is also responsible for creating instances of `DS.Model` that wrap
10103 the individual data for a record, so that they can be bound to in your
10104 Handlebars templates.
10105
10106 Define your application's store like this:
10107
10108 ```javascript
10109 MyApp.Store = DS.Store.extend();
10110 ```
10111
10112 Most Ember.js applications will only have a single `DS.Store` that is
10113 automatically created by their `Ember.Application`.
10114
10115 You can retrieve models from the store in several ways. To retrieve a record
10116 for a specific id, use `DS.Store`'s `find()` method:
10117
10118 ```javascript
10119 store.find('person', 123).then(function (person) {
10120 });
10121 ```
10122
10123 If your application has multiple `DS.Store` instances (an unusual case), you can
10124 specify which store should be used:
10125
10126 ```javascript
10127 store.find('person', 123).then(function (person) {
10128 });
10129 ```
10130
10131 By default, the store will talk to your backend using a standard
10132 REST mechanism. You can customize how the store talks to your
10133 backend by specifying a custom adapter:
10134
10135 ```javascript
10136 MyApp.ApplicationAdapter = MyApp.CustomAdapter
10137 ```
10138
10139 You can learn more about writing a custom adapter by reading the `DS.Adapter`
10140 documentation.
10141
10142 ### Store createRecord() vs. push() vs. pushPayload() vs. update()
10143
10144 The store provides multiple ways to create new record objects. They have
10145 some subtle differences in their use which are detailed below:
10146
10147 [createRecord](#method_createRecord) is used for creating new
10148 records on the client side. This will return a new record in the
10149 `created.uncommitted` state. In order to persist this record to the
10150 backend you will need to call `record.save()`.
10151
10152 [push](#method_push) is used to notify Ember Data's store of new or
10153 updated records that exist in the backend. This will return a record
10154 in the `loaded.saved` state. The primary use-case for `store#push` is
10155 to notify Ember Data about record updates that happen
10156 outside of the normal adapter methods (for example
10157 [SSE](http://dev.w3.org/html5/eventsource/) or [Web
10158 Sockets](http://www.w3.org/TR/2009/WD-websockets-20091222/)).
10159
10160 [pushPayload](#method_pushPayload) is a convenience wrapper for
10161 `store#push` that will deserialize payloads if the
10162 Serializer implements a `pushPayload` method.
10163
10164 [update](#method_update) works like `push`, except it can handle
10165 partial attributes without overwriting the existing record
10166 properties.
10167
10168 Note: When creating a new record using any of the above methods
10169 Ember Data will update `DS.RecordArray`s such as those returned by
10170 `store#all()`, `store#findAll()` or `store#filter()`. This means any
10171 data bindings or computed properties that depend on the RecordArray
10172 will automatically be synced to include the new or updated record
10173 values.
10174
10175 @class Store
10176 @namespace DS
10177 @extends Ember.Object
10178 */
10179 Store = Ember.Object.extend({
10180
10181 /**
10182 @method init
10183 @private
10184 */
10185 init: function() {
10186 // internal bookkeeping; not observable
10187 if (!RecordArrayManager) { RecordArrayManager = requireModule("ember-data/system/record_array_manager")["default"]; }
10188 this.typeMaps = {};
10189 this.recordArrayManager = RecordArrayManager.create({
10190 store: this
10191 });
10192 this._pendingSave = [];
10193 //Used to keep track of all the find requests that need to be coalesced
10194 this._pendingFetch = Map.create();
10195 },
10196
10197 /**
10198 The adapter to use to communicate to a backend server or other persistence layer.
10199
10200 This can be specified as an instance, class, or string.
10201
10202 If you want to specify `App.CustomAdapter` as a string, do:
10203
10204 ```js
10205 adapter: 'custom'
10206 ```
10207
10208 @property adapter
10209 @default DS.RESTAdapter
10210 @type {DS.Adapter|String}
10211 */
10212 adapter: '-rest',
10213
10214 /**
10215 Returns a JSON representation of the record using a custom
10216 type-specific serializer, if one exists.
10217
10218 The available options are:
10219
10220 * `includeId`: `true` if the record's ID should be included in
10221 the JSON representation
10222
10223 @method serialize
10224 @private
10225 @param {DS.Model} record the record to serialize
10226 @param {Object} options an options hash
10227 */
10228 serialize: function(record, options) {
10229 return this.serializerFor(record.constructor.typeKey).serialize(record, options);
10230 },
10231
10232 /**
10233 This property returns the adapter, after resolving a possible
10234 string key.
10235
10236 If the supplied `adapter` was a class, or a String property
10237 path resolved to a class, this property will instantiate the
10238 class.
10239
10240 This property is cacheable, so the same instance of a specified
10241 adapter class should be used for the lifetime of the store.
10242
10243 @property defaultAdapter
10244 @private
10245 @return DS.Adapter
10246 */
10247 defaultAdapter: Ember.computed('adapter', function() {
10248 var adapter = get(this, 'adapter');
10249
10250 Ember.assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name or a factory', !(adapter instanceof Adapter));
10251
10252 if (typeof adapter === 'string') {
10253 adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:-rest');
10254 }
10255
10256 if (DS.Adapter.detect(adapter)) {
10257 adapter = adapter.create({
10258 container: this.container
10259 });
10260 }
10261
10262 return adapter;
10263 }),
10264
10265 // .....................
10266 // . CREATE NEW RECORD .
10267 // .....................
10268
10269 /**
10270 Create a new record in the current store. The properties passed
10271 to this method are set on the newly created record.
10272
10273 To create a new instance of `App.Post`:
10274
10275 ```js
10276 store.createRecord('post', {
10277 title: "Rails is omakase"
10278 });
10279 ```
10280
10281 @method createRecord
10282 @param {String} type
10283 @param {Object} properties a hash of properties to set on the
10284 newly created record.
10285 @return {DS.Model} record
10286 */
10287 createRecord: function(typeName, inputProperties) {
10288 var type = this.modelFor(typeName);
10289 var properties = copy(inputProperties) || {};
10290
10291 // If the passed properties do not include a primary key,
10292 // give the adapter an opportunity to generate one. Typically,
10293 // client-side ID generators will use something like uuid.js
10294 // to avoid conflicts.
10295
10296 if (isNone(properties.id)) {
10297 properties.id = this._generateId(type);
10298 }
10299
10300 // Coerce ID to a string
10301 properties.id = coerceId(properties.id);
10302
10303 var record = this.buildRecord(type, properties.id);
10304
10305 // Move the record out of its initial `empty` state into
10306 // the `loaded` state.
10307 record.loadedData();
10308
10309 // Set the properties specified on the record.
10310 record.setProperties(properties);
10311
10312 return record;
10313 },
10314
10315 /**
10316 If possible, this method asks the adapter to generate an ID for
10317 a newly created record.
10318
10319 @method _generateId
10320 @private
10321 @param {String} type
10322 @return {String} if the adapter can generate one, an ID
10323 */
10324 _generateId: function(type) {
10325 var adapter = this.adapterFor(type);
10326
10327 if (adapter && adapter.generateIdForRecord) {
10328 return adapter.generateIdForRecord(this);
10329 }
10330
10331 return null;
10332 },
10333
10334 // .................
10335 // . DELETE RECORD .
10336 // .................
10337
10338 /**
10339 For symmetry, a record can be deleted via the store.
10340
10341 Example
10342
10343 ```javascript
10344 var post = store.createRecord('post', {
10345 title: "Rails is omakase"
10346 });
10347
10348 store.deleteRecord(post);
10349 ```
10350
10351 @method deleteRecord
10352 @param {DS.Model} record
10353 */
10354 deleteRecord: function(record) {
10355 record.deleteRecord();
10356 },
10357
10358 /**
10359 For symmetry, a record can be unloaded via the store. Only
10360 non-dirty records can be unloaded.
10361
10362 Example
10363
10364 ```javascript
10365 store.find('post', 1).then(function(post) {
10366 store.unloadRecord(post);
10367 });
10368 ```
10369
10370 @method unloadRecord
10371 @param {DS.Model} record
10372 */
10373 unloadRecord: function(record) {
10374 record.unloadRecord();
10375 },
10376
10377 // ................
10378 // . FIND RECORDS .
10379 // ................
10380
10381 /**
10382 This is the main entry point into finding records. The first parameter to
10383 this method is the model's name as a string.
10384
10385 ---
10386
10387 To find a record by ID, pass the `id` as the second parameter:
10388
10389 ```javascript
10390 store.find('person', 1);
10391 ```
10392
10393 The `find` method will always return a **promise** that will be resolved
10394 with the record. If the record was already in the store, the promise will
10395 be resolved immediately. Otherwise, the store will ask the adapter's `find`
10396 method to find the necessary data.
10397
10398 The `find` method will always resolve its promise with the same object for
10399 a given type and `id`.
10400
10401 ---
10402
10403 You can optionally `preload` specific attributes and relationships that you know of
10404 by passing them as the third argument to find.
10405
10406 For example, if your Ember route looks like `/posts/1/comments/2` and your API route
10407 for the comment also looks like `/posts/1/comments/2` if you want to fetch the comment
10408 without fetching the post you can pass in the post to the `find` call:
10409
10410 ```javascript
10411 store.find('comment', 2, {post: 1});
10412 ```
10413
10414 If you have access to the post model you can also pass the model itself:
10415
10416 ```javascript
10417 store.find('post', 1).then(function (myPostModel) {
10418 store.find('comment', 2, {post: myPostModel});
10419 });
10420 ```
10421
10422 This way, your adapter's `find` or `buildURL` method will be able to look up the
10423 relationship on the record and construct the nested URL without having to first
10424 fetch the post.
10425
10426 ---
10427
10428 To find all records for a type, call `find` with no additional parameters:
10429
10430 ```javascript
10431 store.find('person');
10432 ```
10433
10434 This will ask the adapter's `findAll` method to find the records for the
10435 given type, and return a promise that will be resolved once the server
10436 returns the values.
10437
10438 ---
10439
10440 To find a record by a query, call `find` with a hash as the second
10441 parameter:
10442
10443 ```javascript
10444 store.find('person', { page: 1 });
10445 ```
10446
10447 This will ask the adapter's `findQuery` method to find the records for
10448 the query, and return a promise that will be resolved once the server
10449 responds.
10450
10451 @method find
10452 @param {String or subclass of DS.Model} type
10453 @param {Object|String|Integer|null} id
10454 @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models
10455 @return {Promise} promise
10456 */
10457 find: function(type, id, preload) {
10458 Ember.assert("You need to pass a type to the store's find method", arguments.length >= 1);
10459 Ember.assert("You may not pass `" + id + "` as id to the store's find method", arguments.length === 1 || !Ember.isNone(id));
10460
10461 if (arguments.length === 1) {
10462 return this.findAll(type);
10463 }
10464
10465 // We are passed a query instead of an id.
10466 if (Ember.typeOf(id) === 'object') {
10467 return this.findQuery(type, id);
10468 }
10469
10470 return this.findById(type, coerceId(id), preload);
10471 },
10472
10473 /**
10474 This method returns a record for a given type and id combination.
10475
10476 @method findById
10477 @private
10478 @param {String or subclass of DS.Model} type
10479 @param {String|Integer} id
10480 @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models
10481 @return {Promise} promise
10482 */
10483 findById: function(typeName, id, preload) {
10484
10485 var type = this.modelFor(typeName);
10486 var record = this.recordForId(type, id);
10487
10488 return this._findByRecord(record, preload);
10489 },
10490
10491 _findByRecord: function(record, preload) {
10492 var fetchedRecord;
10493
10494 if (preload) {
10495 record._preloadData(preload);
10496 }
10497
10498 if (get(record, 'isEmpty')) {
10499 fetchedRecord = this.scheduleFetch(record);
10500 //TODO double check about reloading
10501 } else if (get(record, 'isLoading')){
10502 fetchedRecord = record._loadingPromise;
10503 }
10504
10505 return promiseObject(fetchedRecord || record, "DS: Store#findByRecord " + record.typeKey + " with id: " + get(record, 'id'));
10506 },
10507
10508 /**
10509 This method makes a series of requests to the adapter's `find` method
10510 and returns a promise that resolves once they are all loaded.
10511
10512 @private
10513 @method findByIds
10514 @param {String} type
10515 @param {Array} ids
10516 @return {Promise} promise
10517 */
10518 findByIds: function(type, ids) {
10519 var store = this;
10520
10521 return promiseArray(Ember.RSVP.all(map(ids, function(id) {
10522 return store.findById(type, id);
10523 })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete"));
10524 },
10525
10526 /**
10527 This method is called by `findById` if it discovers that a particular
10528 type/id pair hasn't been loaded yet to kick off a request to the
10529 adapter.
10530
10531 @method fetchRecord
10532 @private
10533 @param {DS.Model} record
10534 @return {Promise} promise
10535 */
10536 fetchRecord: function(record) {
10537 var type = record.constructor;
10538 var id = get(record, 'id');
10539 var adapter = this.adapterFor(type);
10540
10541 Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter);
10542 Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find);
10543
10544 var promise = _find(adapter, this, type, id, record);
10545 return promise;
10546 },
10547
10548 scheduleFetchMany: function(records) {
10549 return Ember.RSVP.all(map(records, this.scheduleFetch, this));
10550 },
10551
10552 scheduleFetch: function(record) {
10553 var type = record.constructor;
10554 if (isNone(record)) { return null; }
10555 if (record._loadingPromise) { return record._loadingPromise; }
10556
10557 var resolver = Ember.RSVP.defer('Fetching ' + type + 'with id: ' + record.get('id'));
10558 var recordResolverPair = {
10559 record: record,
10560 resolver: resolver
10561 };
10562 var promise = resolver.promise;
10563
10564 record.loadingData(promise);
10565
10566 if (!this._pendingFetch.get(type)){
10567 this._pendingFetch.set(type, [recordResolverPair]);
10568 } else {
10569 this._pendingFetch.get(type).push(recordResolverPair);
10570 }
10571 Ember.run.scheduleOnce('afterRender', this, this.flushAllPendingFetches);
10572
10573 return promise;
10574 },
10575
10576 flushAllPendingFetches: function(){
10577 if (this.isDestroyed || this.isDestroying) {
10578 return;
10579 }
10580
10581 this._pendingFetch.forEach(this._flushPendingFetchForType, this);
10582 this._pendingFetch = Map.create();
10583 },
10584
10585 _flushPendingFetchForType: function (recordResolverPairs, type) {
10586 var store = this;
10587 var adapter = store.adapterFor(type);
10588 var shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests;
10589 var records = Ember.A(recordResolverPairs).mapBy('record');
10590
10591 function _fetchRecord(recordResolverPair) {
10592 recordResolverPair.resolver.resolve(store.fetchRecord(recordResolverPair.record));
10593 }
10594
10595 function resolveFoundRecords(records) {
10596 forEach(records, function(record){
10597 var pair = Ember.A(recordResolverPairs).findBy('record', record);
10598 if (pair){
10599 var resolver = pair.resolver;
10600 resolver.resolve(record);
10601 }
10602 });
10603 }
10604
10605 function makeMissingRecordsRejector(requestedRecords) {
10606 return function rejectMissingRecords(resolvedRecords) {
10607 var missingRecords = requestedRecords.without(resolvedRecords);
10608 rejectRecords(missingRecords);
10609 };
10610 }
10611
10612 function makeRecordsRejector(records) {
10613 return function (error) {
10614 rejectRecords(records, error);
10615 };
10616 }
10617
10618 function rejectRecords(records, error) {
10619 forEach(records, function(record){
10620 var pair = Ember.A(recordResolverPairs).findBy('record', record);
10621 if (pair){
10622 var resolver = pair.resolver;
10623 resolver.reject(error);
10624 }
10625 });
10626 }
10627
10628 if (recordResolverPairs.length === 1) {
10629 _fetchRecord(recordResolverPairs[0]);
10630 } else if (shouldCoalesce) {
10631 var groups = adapter.groupRecordsForFindMany(this, records);
10632 forEach(groups, function (groupOfRecords) {
10633 var requestedRecords = Ember.A(groupOfRecords);
10634 var ids = requestedRecords.mapBy('id');
10635 if (ids.length > 1) {
10636 _findMany(adapter, store, type, ids, requestedRecords).
10637 then(resolveFoundRecords).
10638 then(makeMissingRecordsRejector(requestedRecords)).
10639 then(null, makeRecordsRejector(requestedRecords));
10640 } else if (ids.length === 1) {
10641 var pair = Ember.A(recordResolverPairs).findBy('record', groupOfRecords[0]);
10642 _fetchRecord(pair);
10643 } else {
10644 Ember.assert("You cannot return an empty array from adapter's method groupRecordsForFindMany", false);
10645 }
10646 });
10647 } else {
10648 forEach(recordResolverPairs, _fetchRecord);
10649 }
10650 },
10651
10652 /**
10653 Get a record by a given type and ID without triggering a fetch.
10654
10655 This method will synchronously return the record if it is available in the store,
10656 otherwise it will return `null`. A record is available if it has been fetched earlier, or
10657 pushed manually into the store.
10658
10659 _Note: This is an synchronous method and does not return a promise._
10660
10661 ```js
10662 var post = store.getById('post', 1);
10663
10664 post.get('id'); // 1
10665 ```
10666
10667 @method getById
10668 @param {String or subclass of DS.Model} type
10669 @param {String|Integer} id
10670 @return {DS.Model|null} record
10671 */
10672 getById: function(type, id) {
10673 if (this.hasRecordForId(type, id)) {
10674 return this.recordForId(type, id);
10675 } else {
10676 return null;
10677 }
10678 },
10679
10680 /**
10681 This method is called by the record's `reload` method.
10682
10683 This method calls the adapter's `find` method, which returns a promise. When
10684 **that** promise resolves, `reloadRecord` will resolve the promise returned
10685 by the record's `reload`.
10686
10687 @method reloadRecord
10688 @private
10689 @param {DS.Model} record
10690 @return {Promise} promise
10691 */
10692 reloadRecord: function(record) {
10693 var type = record.constructor;
10694 var adapter = this.adapterFor(type);
10695 var id = get(record, 'id');
10696
10697 Ember.assert("You cannot reload a record without an ID", id);
10698 Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter);
10699 Ember.assert("You tried to reload a record but your adapter does not implement `find`", adapter.find);
10700
10701 return this.scheduleFetch(record);
10702 },
10703
10704 /**
10705 Returns true if a record for a given type and ID is already loaded.
10706
10707 @method hasRecordForId
10708 @param {String or subclass of DS.Model} type
10709 @param {String|Integer} id
10710 @return {Boolean}
10711 */
10712 hasRecordForId: function(typeName, inputId) {
10713 var type = this.modelFor(typeName);
10714 var id = coerceId(inputId);
10715 return !!this.typeMapFor(type).idToRecord[id];
10716 },
10717
10718 /**
10719 Returns id record for a given type and ID. If one isn't already loaded,
10720 it builds a new record and leaves it in the `empty` state.
10721
10722 @method recordForId
10723 @private
10724 @param {String or subclass of DS.Model} type
10725 @param {String|Integer} id
10726 @return {DS.Model} record
10727 */
10728 recordForId: function(typeName, inputId) {
10729 var type = this.modelFor(typeName);
10730 var id = coerceId(inputId);
10731 var idToRecord = this.typeMapFor(type).idToRecord;
10732 var record = idToRecord[id];
10733
10734 if (!record || !idToRecord[id]) {
10735 record = this.buildRecord(type, id);
10736 }
10737
10738 return record;
10739 },
10740
10741 /**
10742 @method findMany
10743 @private
10744 @param {DS.Model} owner
10745 @param {Array} records
10746 @param {String or subclass of DS.Model} type
10747 @param {Resolver} resolver
10748 @return {DS.ManyArray} records
10749 */
10750 findMany: function(records) {
10751 var store = this;
10752 return Promise.all( map(records, function(record) {
10753 return store._findByRecord(record);
10754 }));
10755 },
10756
10757
10758 /**
10759 If a relationship was originally populated by the adapter as a link
10760 (as opposed to a list of IDs), this method is called when the
10761 relationship is fetched.
10762
10763 The link (which is usually a URL) is passed through unchanged, so the
10764 adapter can make whatever request it wants.
10765
10766 The usual use-case is for the server to register a URL as a link, and
10767 then use that URL in the future to make a request for the relationship.
10768
10769 @method findHasMany
10770 @private
10771 @param {DS.Model} owner
10772 @param {any} link
10773 @param {String or subclass of DS.Model} type
10774 @return {Promise} promise
10775 */
10776 findHasMany: function(owner, link, type) {
10777 var adapter = this.adapterFor(owner.constructor);
10778
10779 Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter);
10780 Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", adapter.findHasMany);
10781
10782 return _findHasMany(adapter, this, owner, link, type);
10783 },
10784
10785 /**
10786 @method findBelongsTo
10787 @private
10788 @param {DS.Model} owner
10789 @param {any} link
10790 @param {Relationship} relationship
10791 @return {Promise} promise
10792 */
10793 findBelongsTo: function(owner, link, relationship) {
10794 var adapter = this.adapterFor(owner.constructor);
10795
10796 Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter);
10797 Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", adapter.findBelongsTo);
10798
10799 return _findBelongsTo(adapter, this, owner, link, relationship);
10800 },
10801
10802 /**
10803 This method delegates a query to the adapter. This is the one place where
10804 adapter-level semantics are exposed to the application.
10805
10806 Exposing queries this way seems preferable to creating an abstract query
10807 language for all server-side queries, and then require all adapters to
10808 implement them.
10809
10810 This method returns a promise, which is resolved with a `RecordArray`
10811 once the server returns.
10812
10813 @method findQuery
10814 @private
10815 @param {String or subclass of DS.Model} type
10816 @param {any} query an opaque query to be used by the adapter
10817 @return {Promise} promise
10818 */
10819 findQuery: function(typeName, query) {
10820 var type = this.modelFor(typeName);
10821 var array = this.recordArrayManager
10822 .createAdapterPopulatedRecordArray(type, query);
10823
10824 var adapter = this.adapterFor(type);
10825
10826 Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter);
10827 Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", adapter.findQuery);
10828
10829 return promiseArray(_findQuery(adapter, this, type, query, array));
10830 },
10831
10832 /**
10833 This method returns an array of all records adapter can find.
10834 It triggers the adapter's `findAll` method to give it an opportunity to populate
10835 the array with records of that type.
10836
10837 @method findAll
10838 @private
10839 @param {String or subclass of DS.Model} type
10840 @return {DS.AdapterPopulatedRecordArray}
10841 */
10842 findAll: function(typeName) {
10843 var type = this.modelFor(typeName);
10844
10845 return this.fetchAll(type, this.all(type));
10846 },
10847
10848 /**
10849 @method fetchAll
10850 @private
10851 @param {DS.Model} type
10852 @param {DS.RecordArray} array
10853 @return {Promise} promise
10854 */
10855 fetchAll: function(type, array) {
10856 var adapter = this.adapterFor(type);
10857 var sinceToken = this.typeMapFor(type).metadata.since;
10858
10859 set(array, 'isUpdating', true);
10860
10861 Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter);
10862 Ember.assert("You tried to load all records but your adapter does not implement `findAll`", adapter.findAll);
10863
10864 return promiseArray(_findAll(adapter, this, type, sinceToken));
10865 },
10866
10867 /**
10868 @method didUpdateAll
10869 @param {DS.Model} type
10870 */
10871 didUpdateAll: function(type) {
10872 var findAllCache = this.typeMapFor(type).findAllCache;
10873 set(findAllCache, 'isUpdating', false);
10874 },
10875
10876 /**
10877 This method returns a filtered array that contains all of the known records
10878 for a given type.
10879
10880 Note that because it's just a filter, it will have any locally
10881 created records of the type.
10882
10883 Also note that multiple calls to `all` for a given type will always
10884 return the same RecordArray.
10885
10886 Example
10887
10888 ```javascript
10889 var localPosts = store.all('post');
10890 ```
10891
10892 @method all
10893 @param {String or subclass of DS.Model} type
10894 @return {DS.RecordArray}
10895 */
10896 all: function(typeName) {
10897 var type = this.modelFor(typeName);
10898 var typeMap = this.typeMapFor(type);
10899 var findAllCache = typeMap.findAllCache;
10900
10901 if (findAllCache) { return findAllCache; }
10902
10903 var array = this.recordArrayManager.createRecordArray(type);
10904
10905 typeMap.findAllCache = array;
10906 return array;
10907 },
10908
10909
10910 /**
10911 This method unloads all of the known records for a given type.
10912
10913 ```javascript
10914 store.unloadAll('post');
10915 ```
10916
10917 @method unloadAll
10918 @param {String or subclass of DS.Model} type
10919 */
10920 unloadAll: function(type) {
10921 var modelType = this.modelFor(type);
10922 var typeMap = this.typeMapFor(modelType);
10923 var records = typeMap.records.slice();
10924 var record;
10925
10926 for (var i = 0; i < records.length; i++) {
10927 record = records[i];
10928 record.unloadRecord();
10929 record.destroy(); // maybe within unloadRecord
10930 }
10931
10932 typeMap.findAllCache = null;
10933 },
10934
10935 /**
10936 Takes a type and filter function, and returns a live RecordArray that
10937 remains up to date as new records are loaded into the store or created
10938 locally.
10939
10940 The callback function takes a materialized record, and returns true
10941 if the record should be included in the filter and false if it should
10942 not.
10943
10944 The filter function is called once on all records for the type when
10945 it is created, and then once on each newly loaded or created record.
10946
10947 If any of a record's properties change, or if it changes state, the
10948 filter function will be invoked again to determine whether it should
10949 still be in the array.
10950
10951 Optionally you can pass a query which will be triggered at first. The
10952 results returned by the server could then appear in the filter if they
10953 match the filter function.
10954
10955 Example
10956
10957 ```javascript
10958 store.filter('post', {unread: true}, function(post) {
10959 return post.get('unread');
10960 }).then(function(unreadPosts) {
10961 unreadPosts.get('length'); // 5
10962 var unreadPost = unreadPosts.objectAt(0);
10963 unreadPost.set('unread', false);
10964 unreadPosts.get('length'); // 4
10965 });
10966 ```
10967
10968 @method filter
10969 @param {String or subclass of DS.Model} type
10970 @param {Object} query optional query
10971 @param {Function} filter
10972 @return {DS.PromiseArray}
10973 */
10974 filter: function(type, query, filter) {
10975 var promise;
10976 var length = arguments.length;
10977 var array;
10978 var hasQuery = length === 3;
10979
10980 // allow an optional server query
10981 if (hasQuery) {
10982 promise = this.findQuery(type, query);
10983 } else if (arguments.length === 2) {
10984 filter = query;
10985 }
10986
10987 type = this.modelFor(type);
10988
10989 if (hasQuery) {
10990 array = this.recordArrayManager.createFilteredRecordArray(type, filter, query);
10991 } else {
10992 array = this.recordArrayManager.createFilteredRecordArray(type, filter);
10993 }
10994
10995 promise = promise || Promise.cast(array);
10996
10997
10998 return promiseArray(promise.then(function() {
10999 return array;
11000 }, null, "DS: Store#filter of " + type));
11001 },
11002
11003 /**
11004 This method returns if a certain record is already loaded
11005 in the store. Use this function to know beforehand if a find()
11006 will result in a request or that it will be a cache hit.
11007
11008 Example
11009
11010 ```javascript
11011 store.recordIsLoaded('post', 1); // false
11012 store.find('post', 1).then(function() {
11013 store.recordIsLoaded('post', 1); // true
11014 });
11015 ```
11016
11017 @method recordIsLoaded
11018 @param {String or subclass of DS.Model} type
11019 @param {string} id
11020 @return {boolean}
11021 */
11022 recordIsLoaded: function(type, id) {
11023 if (!this.hasRecordForId(type, id)) { return false; }
11024 return !get(this.recordForId(type, id), 'isEmpty');
11025 },
11026
11027 /**
11028 This method returns the metadata for a specific type.
11029
11030 @method metadataFor
11031 @param {String or subclass of DS.Model} type
11032 @return {object}
11033 */
11034 metadataFor: function(type) {
11035 type = this.modelFor(type);
11036 return this.typeMapFor(type).metadata;
11037 },
11038
11039 // ............
11040 // . UPDATING .
11041 // ............
11042
11043 /**
11044 If the adapter updates attributes or acknowledges creation
11045 or deletion, the record will notify the store to update its
11046 membership in any filters.
11047 To avoid thrashing, this method is invoked only once per
11048
11049 run loop per record.
11050
11051 @method dataWasUpdated
11052 @private
11053 @param {Class} type
11054 @param {DS.Model} record
11055 */
11056 dataWasUpdated: function(type, record) {
11057 this.recordArrayManager.recordDidChange(record);
11058 },
11059
11060 // ..............
11061 // . PERSISTING .
11062 // ..............
11063
11064 /**
11065 This method is called by `record.save`, and gets passed a
11066 resolver for the promise that `record.save` returns.
11067
11068 It schedules saving to happen at the end of the run loop.
11069
11070 @method scheduleSave
11071 @private
11072 @param {DS.Model} record
11073 @param {Resolver} resolver
11074 */
11075 scheduleSave: function(record, resolver) {
11076 record.adapterWillCommit();
11077 this._pendingSave.push([record, resolver]);
11078 once(this, 'flushPendingSave');
11079 },
11080
11081 /**
11082 This method is called at the end of the run loop, and
11083 flushes any records passed into `scheduleSave`
11084
11085 @method flushPendingSave
11086 @private
11087 */
11088 flushPendingSave: function() {
11089 var pending = this._pendingSave.slice();
11090 this._pendingSave = [];
11091
11092 forEach(pending, function(tuple) {
11093 var record = tuple[0], resolver = tuple[1];
11094 var adapter = this.adapterFor(record.constructor);
11095 var operation;
11096
11097 if (get(record, 'currentState.stateName') === 'root.deleted.saved') {
11098 return resolver.resolve(record);
11099 } else if (get(record, 'isNew')) {
11100 operation = 'createRecord';
11101 } else if (get(record, 'isDeleted')) {
11102 operation = 'deleteRecord';
11103 } else {
11104 operation = 'updateRecord';
11105 }
11106
11107 resolver.resolve(_commit(adapter, this, operation, record));
11108 }, this);
11109 },
11110
11111 /**
11112 This method is called once the promise returned by an
11113 adapter's `createRecord`, `updateRecord` or `deleteRecord`
11114 is resolved.
11115
11116 If the data provides a server-generated ID, it will
11117 update the record and the store's indexes.
11118
11119 @method didSaveRecord
11120 @private
11121 @param {DS.Model} record the in-flight record
11122 @param {Object} data optional data (see above)
11123 */
11124 didSaveRecord: function(record, data) {
11125 if (data) {
11126 // normalize relationship IDs into records
11127 data = normalizeRelationships(this, record.constructor, data, record);
11128 setupRelationships(this, record, data);
11129
11130 this.updateId(record, data);
11131 }
11132
11133 record.adapterDidCommit(data);
11134 },
11135
11136 /**
11137 This method is called once the promise returned by an
11138 adapter's `createRecord`, `updateRecord` or `deleteRecord`
11139 is rejected with a `DS.InvalidError`.
11140
11141 @method recordWasInvalid
11142 @private
11143 @param {DS.Model} record
11144 @param {Object} errors
11145 */
11146 recordWasInvalid: function(record, errors) {
11147 record.adapterDidInvalidate(errors);
11148 },
11149
11150 /**
11151 This method is called once the promise returned by an
11152 adapter's `createRecord`, `updateRecord` or `deleteRecord`
11153 is rejected (with anything other than a `DS.InvalidError`).
11154
11155 @method recordWasError
11156 @private
11157 @param {DS.Model} record
11158 */
11159 recordWasError: function(record) {
11160 record.adapterDidError();
11161 },
11162
11163 /**
11164 When an adapter's `createRecord`, `updateRecord` or `deleteRecord`
11165 resolves with data, this method extracts the ID from the supplied
11166 data.
11167
11168 @method updateId
11169 @private
11170 @param {DS.Model} record
11171 @param {Object} data
11172 */
11173 updateId: function(record, data) {
11174 var oldId = get(record, 'id');
11175 var id = coerceId(data.id);
11176
11177 Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId);
11178
11179 this.typeMapFor(record.constructor).idToRecord[id] = record;
11180
11181 set(record, 'id', id);
11182 },
11183
11184 /**
11185 Returns a map of IDs to client IDs for a given type.
11186
11187 @method typeMapFor
11188 @private
11189 @param {subclass of DS.Model} type
11190 @return {Object} typeMap
11191 */
11192 typeMapFor: function(type) {
11193 var typeMaps = get(this, 'typeMaps');
11194 var guid = Ember.guidFor(type);
11195 var typeMap;
11196
11197 typeMap = typeMaps[guid];
11198
11199 if (typeMap) { return typeMap; }
11200
11201 typeMap = {
11202 idToRecord: Object.create(null),
11203 records: [],
11204 metadata: Object.create(null),
11205 type: type
11206 };
11207
11208 typeMaps[guid] = typeMap;
11209
11210 return typeMap;
11211 },
11212
11213 // ................
11214 // . LOADING DATA .
11215 // ................
11216
11217 /**
11218 This internal method is used by `push`.
11219
11220 @method _load
11221 @private
11222 @param {String or subclass of DS.Model} type
11223 @param {Object} data
11224 @param {Boolean} partial the data should be merged into
11225 the existing data, not replace it.
11226 */
11227 _load: function(type, data, partial) {
11228 var id = coerceId(data.id);
11229 var record = this.recordForId(type, id);
11230
11231 record.setupData(data, partial);
11232 this.recordArrayManager.recordDidChange(record);
11233
11234 return record;
11235 },
11236
11237 /**
11238 Returns a model class for a particular key. Used by
11239 methods that take a type key (like `find`, `createRecord`,
11240 etc.)
11241
11242 @method modelFor
11243 @param {String or subclass of DS.Model} key
11244 @return {subclass of DS.Model}
11245 */
11246 modelFor: function(key) {
11247 var factory;
11248
11249 if (typeof key === 'string') {
11250 factory = this.modelFactoryFor(key);
11251 if (!factory) {
11252 throw new Ember.Error("No model was found for '" + key + "'");
11253 }
11254 factory.typeKey = factory.typeKey || this._normalizeTypeKey(key);
11255 } else {
11256 // A factory already supplied. Ensure it has a normalized key.
11257 factory = key;
11258 if (factory.typeKey) {
11259 factory.typeKey = this._normalizeTypeKey(factory.typeKey);
11260 }
11261 }
11262
11263 factory.store = this;
11264 return factory;
11265 },
11266
11267 modelFactoryFor: function(key){
11268 return this.container.lookupFactory('model:' + key);
11269 },
11270
11271 /**
11272 Push some data for a given type into the store.
11273
11274 This method expects normalized data:
11275
11276 * The ID is a key named `id` (an ID is mandatory)
11277 * The names of attributes are the ones you used in
11278 your model's `DS.attr`s.
11279 * Your relationships must be:
11280 * represented as IDs or Arrays of IDs
11281 * represented as model instances
11282 * represented as URLs, under the `links` key
11283
11284 For this model:
11285
11286 ```js
11287 App.Person = DS.Model.extend({
11288 firstName: DS.attr(),
11289 lastName: DS.attr(),
11290
11291 children: DS.hasMany('person')
11292 });
11293 ```
11294
11295 To represent the children as IDs:
11296
11297 ```js
11298 {
11299 id: 1,
11300 firstName: "Tom",
11301 lastName: "Dale",
11302 children: [1, 2, 3]
11303 }
11304 ```
11305
11306 To represent the children relationship as a URL:
11307
11308 ```js
11309 {
11310 id: 1,
11311 firstName: "Tom",
11312 lastName: "Dale",
11313 links: {
11314 children: "/people/1/children"
11315 }
11316 }
11317 ```
11318
11319 If you're streaming data or implementing an adapter,
11320 make sure that you have converted the incoming data
11321 into this form.
11322
11323 This method can be used both to push in brand new
11324 records, as well as to update existing records.
11325
11326 @method push
11327 @param {String or subclass of DS.Model} type
11328 @param {Object} data
11329 @return {DS.Model} the record that was created or
11330 updated.
11331 */
11332 push: function(typeName, data, _partial) {
11333 // _partial is an internal param used by `update`.
11334 // If passed, it means that the data should be
11335 // merged into the existing data, not replace it.
11336 Ember.assert("Expected an object as `data` in a call to push for " + typeName + " , but was " + data, Ember.typeOf(data) === 'object');
11337 Ember.assert("You must include an `id` for " + typeName + " in an object passed to `push`", data.id != null);
11338
11339 var type = this.modelFor(typeName);
11340
11341 // If the payload contains relationships that are specified as
11342 // IDs, normalizeRelationships will convert them into DS.Model instances
11343 // (possibly unloaded) before we push the payload into the
11344 // store.
11345
11346 data = normalizeRelationships(this, type, data);
11347
11348 // Actually load the record into the store.
11349
11350 this._load(type, data, _partial);
11351
11352 var record = this.recordForId(type, data.id);
11353
11354 // Now that the pushed record as well as any related records
11355 // are in the store, create the data structures used to track
11356 // relationships.
11357 setupRelationships(this, record, data);
11358
11359 return record;
11360 },
11361
11362 /**
11363 Push some raw data into the store.
11364
11365 This method can be used both to push in brand new
11366 records, as well as to update existing records. You
11367 can push in more than one type of object at once.
11368 All objects should be in the format expected by the
11369 serializer.
11370
11371 ```js
11372 App.ApplicationSerializer = DS.ActiveModelSerializer;
11373
11374 var pushData = {
11375 posts: [
11376 {id: 1, post_title: "Great post", comment_ids: [2]}
11377 ],
11378 comments: [
11379 {id: 2, comment_body: "Insightful comment"}
11380 ]
11381 }
11382
11383 store.pushPayload(pushData);
11384 ```
11385
11386 By default, the data will be deserialized using a default
11387 serializer (the application serializer if it exists).
11388
11389 Alternatively, `pushPayload` will accept a model type which
11390 will determine which serializer will process the payload.
11391 However, the serializer itself (processing this data via
11392 `normalizePayload`) will not know which model it is
11393 deserializing.
11394
11395 ```js
11396 App.ApplicationSerializer = DS.ActiveModelSerializer;
11397 App.PostSerializer = DS.JSONSerializer;
11398 store.pushPayload('comment', pushData); // Will use the ApplicationSerializer
11399 store.pushPayload('post', pushData); // Will use the PostSerializer
11400 ```
11401
11402 @method pushPayload
11403 @param {String} type Optionally, a model used to determine which serializer will be used
11404 @param {Object} payload
11405 */
11406 pushPayload: function (type, inputPayload) {
11407 var serializer;
11408 var payload;
11409 if (!inputPayload) {
11410 payload = type;
11411 serializer = defaultSerializer(this.container);
11412 Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", serializer.pushPayload);
11413 } else {
11414 payload = inputPayload;
11415 serializer = this.serializerFor(type);
11416 }
11417 serializer.pushPayload(this, payload);
11418 },
11419
11420 /**
11421 `normalize` converts a json payload into the normalized form that
11422 [push](#method_push) expects.
11423
11424 Example
11425
11426 ```js
11427 socket.on('message', function(message) {
11428 var modelName = message.model;
11429 var data = message.data;
11430 store.push(modelName, store.normalize(modelName, data));
11431 });
11432 ```
11433
11434 @method normalize
11435 @param {String} type The name of the model type for this payload
11436 @param {Object} payload
11437 @return {Object} The normalized payload
11438 */
11439 normalize: function (type, payload) {
11440 var serializer = this.serializerFor(type);
11441 var model = this.modelFor(type);
11442 return serializer.normalize(model, payload);
11443 },
11444
11445 /**
11446 Update existing records in the store. Unlike [push](#method_push),
11447 update will merge the new data properties with the existing
11448 properties. This makes it safe to use with a subset of record
11449 attributes. This method expects normalized data.
11450
11451 `update` is useful if your app broadcasts partial updates to
11452 records.
11453
11454 ```js
11455 App.Person = DS.Model.extend({
11456 firstName: DS.attr('string'),
11457 lastName: DS.attr('string')
11458 });
11459
11460 store.get('person', 1).then(function(tom) {
11461 tom.get('firstName'); // Tom
11462 tom.get('lastName'); // Dale
11463
11464 var updateEvent = {id: 1, firstName: "TomHuda"};
11465 store.update('person', updateEvent);
11466
11467 tom.get('firstName'); // TomHuda
11468 tom.get('lastName'); // Dale
11469 });
11470 ```
11471
11472 @method update
11473 @param {String} type
11474 @param {Object} data
11475 @return {DS.Model} the record that was updated.
11476 */
11477 update: function(type, data) {
11478 Ember.assert("You must include an `id` for " + type + " in a hash passed to `update`", data.id != null);
11479
11480 return this.push(type, data, true);
11481 },
11482
11483 /**
11484 If you have an Array of normalized data to push,
11485 you can call `pushMany` with the Array, and it will
11486 call `push` repeatedly for you.
11487
11488 @method pushMany
11489 @param {String or subclass of DS.Model} type
11490 @param {Array} datas
11491 @return {Array}
11492 */
11493 pushMany: function(type, datas) {
11494 var length = datas.length;
11495 var result = new Array(length);
11496
11497 for (var i = 0; i < length; i++) {
11498 result[i] = this.push(type, datas[i]);
11499 }
11500
11501 return result;
11502 },
11503
11504 /**
11505 If you have some metadata to set for a type
11506 you can call `metaForType`.
11507
11508 @method metaForType
11509 @param {String or subclass of DS.Model} type
11510 @param {Object} metadata
11511 */
11512 metaForType: function(typeName, metadata) {
11513 var type = this.modelFor(typeName);
11514
11515 Ember.merge(this.typeMapFor(type).metadata, metadata);
11516 },
11517
11518 /**
11519 Build a brand new record for a given type, ID, and
11520 initial data.
11521
11522 @method buildRecord
11523 @private
11524 @param {subclass of DS.Model} type
11525 @param {String} id
11526 @param {Object} data
11527 @return {DS.Model} record
11528 */
11529 buildRecord: function(type, id, data) {
11530 var typeMap = this.typeMapFor(type);
11531 var idToRecord = typeMap.idToRecord;
11532
11533 Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]);
11534 Ember.assert("`" + Ember.inspect(type)+ "` does not appear to be an ember-data model", (typeof type._create === 'function') );
11535
11536 // lookupFactory should really return an object that creates
11537 // instances with the injections applied
11538 var record = type._create({
11539 id: id,
11540 store: this,
11541 container: this.container
11542 });
11543
11544 if (data) {
11545 record.setupData(data);
11546 }
11547
11548 // if we're creating an item, this process will be done
11549 // later, once the object has been persisted.
11550 if (id) {
11551 idToRecord[id] = record;
11552 }
11553
11554 typeMap.records.push(record);
11555
11556 return record;
11557 },
11558
11559 // ...............
11560 // . DESTRUCTION .
11561 // ...............
11562
11563 /**
11564 When a record is destroyed, this un-indexes it and
11565 removes it from any record arrays so it can be GCed.
11566
11567 @method dematerializeRecord
11568 @private
11569 @param {DS.Model} record
11570 */
11571 dematerializeRecord: function(record) {
11572 var type = record.constructor;
11573 var typeMap = this.typeMapFor(type);
11574 var id = get(record, 'id');
11575
11576 record.updateRecordArrays();
11577
11578 if (id) {
11579 delete typeMap.idToRecord[id];
11580 }
11581
11582 var loc = indexOf(typeMap.records, record);
11583 typeMap.records.splice(loc, 1);
11584 },
11585
11586 // ......................
11587 // . PER-TYPE ADAPTERS
11588 // ......................
11589
11590 /**
11591 Returns the adapter for a given type.
11592
11593 @method adapterFor
11594 @private
11595 @param {subclass of DS.Model} type
11596 @return DS.Adapter
11597 */
11598 adapterFor: function(type) {
11599 var container = this.container, adapter;
11600
11601 if (container) {
11602 adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application');
11603 }
11604
11605 return adapter || get(this, 'defaultAdapter');
11606 },
11607
11608 // ..............................
11609 // . RECORD CHANGE NOTIFICATION .
11610 // ..............................
11611
11612 /**
11613 Returns an instance of the serializer for a given type. For
11614 example, `serializerFor('person')` will return an instance of
11615 `App.PersonSerializer`.
11616
11617 If no `App.PersonSerializer` is found, this method will look
11618 for an `App.ApplicationSerializer` (the default serializer for
11619 your entire application).
11620
11621 If no `App.ApplicationSerializer` is found, it will fall back
11622 to an instance of `DS.JSONSerializer`.
11623
11624 @method serializerFor
11625 @private
11626 @param {String} type the record to serialize
11627 @return {DS.Serializer}
11628 */
11629 serializerFor: function(type) {
11630 type = this.modelFor(type);
11631 var adapter = this.adapterFor(type);
11632
11633 return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer);
11634 },
11635
11636 willDestroy: function() {
11637 var typeMaps = this.typeMaps;
11638 var keys = Ember.keys(typeMaps);
11639
11640 var types = map(keys, byType);
11641
11642 this.recordArrayManager.destroy();
11643
11644 forEach(types, this.unloadAll, this);
11645
11646 function byType(entry) {
11647 return typeMaps[entry]['type'];
11648 }
11649
11650 },
11651
11652 /**
11653 All typeKeys are camelCase internally. Changing this function may
11654 require changes to other normalization hooks (such as typeForRoot).
11655
11656 @method _normalizeTypeKey
11657 @private
11658 @param {String} type
11659 @return {String} if the adapter can generate one, an ID
11660 */
11661 _normalizeTypeKey: function(key) {
11662 return camelize(singularize(key));
11663 }
11664 });
11665
11666
11667 function normalizeRelationships(store, type, data, record) {
11668 type.eachRelationship(function(key, relationship) {
11669 var kind = relationship.kind;
11670 var value = data[key];
11671 if (kind === 'belongsTo') {
11672 deserializeRecordId(store, data, key, relationship, value);
11673 } else if (kind === 'hasMany') {
11674 deserializeRecordIds(store, data, key, relationship, value);
11675 }
11676 });
11677
11678 return data;
11679 }
11680
11681 function deserializeRecordId(store, data, key, relationship, id) {
11682 if (!Model) { Model = requireModule("ember-data/system/model")["Model"]; }
11683 if (isNone(id) || id instanceof Model) {
11684 return;
11685 }
11686
11687 var type;
11688
11689 if (typeof id === 'number' || typeof id === 'string') {
11690 type = typeFor(relationship, key, data);
11691 data[key] = store.recordForId(type, id);
11692 } else if (typeof id === 'object') {
11693 // polymorphic
11694 data[key] = store.recordForId(id.type, id.id);
11695 }
11696 }
11697
11698 function typeFor(relationship, key, data) {
11699 if (relationship.options.polymorphic) {
11700 return data[key + "Type"];
11701 } else {
11702 return relationship.type;
11703 }
11704 }
11705
11706 function deserializeRecordIds(store, data, key, relationship, ids) {
11707 if (!Ember.isArray(ids)) {
11708 return;
11709 }
11710 for (var i=0, l=ids.length; i<l; i++) {
11711 deserializeRecordId(store, ids, i, relationship, ids[i]);
11712 }
11713 }
11714
11715 // Delegation to the adapter and promise management
11716
11717
11718 function serializerFor(container, type, defaultSerializer) {
11719 return container.lookup('serializer:'+type) ||
11720 container.lookup('serializer:application') ||
11721 container.lookup('serializer:' + defaultSerializer) ||
11722 container.lookup('serializer:-default');
11723 }
11724
11725 function defaultSerializer(container) {
11726 return container.lookup('serializer:application') ||
11727 container.lookup('serializer:-default');
11728 }
11729
11730 function serializerForAdapter(adapter, type) {
11731 var serializer = adapter.serializer;
11732 var defaultSerializer = adapter.defaultSerializer;
11733 var container = adapter.container;
11734
11735 if (container && serializer === undefined) {
11736 serializer = serializerFor(container, type.typeKey, defaultSerializer);
11737 }
11738
11739 if (serializer === null || serializer === undefined) {
11740 serializer = {
11741 extract: function(store, type, payload) { return payload; }
11742 };
11743 }
11744
11745 return serializer;
11746 }
11747
11748 function _objectIsAlive(object) {
11749 return !(get(object, "isDestroyed") || get(object, "isDestroying"));
11750 }
11751
11752 function _guard(promise, test) {
11753 var guarded = promise['finally'](function() {
11754 if (!test()) {
11755 guarded._subscribers.length = 0;
11756 }
11757 });
11758
11759 return guarded;
11760 }
11761
11762 function _bind(fn) {
11763 var args = Array.prototype.slice.call(arguments, 1);
11764
11765 return function() {
11766 return fn.apply(undefined, args);
11767 };
11768 }
11769
11770 function _find(adapter, store, type, id, record) {
11771 var promise = adapter.find(store, type, id, record);
11772 var serializer = serializerForAdapter(adapter, type);
11773 var label = "DS: Handle Adapter#find of " + type + " with id: " + id;
11774
11775 promise = Promise.cast(promise, label);
11776 promise = _guard(promise, _bind(_objectIsAlive, store));
11777
11778 return promise.then(function(adapterPayload) {
11779 Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", adapterPayload);
11780 var payload = serializer.extract(store, type, adapterPayload, id, 'find');
11781
11782 return store.push(type, payload);
11783 }, function(error) {
11784 var record = store.getById(type, id);
11785 if (record) {
11786 record.notFound();
11787 }
11788 throw error;
11789 }, "DS: Extract payload of '" + type + "'");
11790 }
11791
11792
11793 function _findMany(adapter, store, type, ids, records) {
11794 var promise = adapter.findMany(store, type, ids, records);
11795 var serializer = serializerForAdapter(adapter, type);
11796 var label = "DS: Handle Adapter#findMany of " + type;
11797
11798 if (promise === undefined) {
11799 throw new Error('adapter.findMany returned undefined, this was very likely a mistake');
11800 }
11801
11802 promise = Promise.cast(promise, label);
11803 promise = _guard(promise, _bind(_objectIsAlive, store));
11804
11805 return promise.then(function(adapterPayload) {
11806 var payload = serializer.extract(store, type, adapterPayload, null, 'findMany');
11807
11808 Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
11809
11810 return store.pushMany(type, payload);
11811 }, null, "DS: Extract payload of " + type);
11812 }
11813
11814 function _findHasMany(adapter, store, record, link, relationship) {
11815 var promise = adapter.findHasMany(store, record, link, relationship);
11816 var serializer = serializerForAdapter(adapter, relationship.type);
11817 var label = "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type;
11818
11819 promise = Promise.cast(promise, label);
11820 promise = _guard(promise, _bind(_objectIsAlive, store));
11821 promise = _guard(promise, _bind(_objectIsAlive, record));
11822
11823 return promise.then(function(adapterPayload) {
11824 var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findHasMany');
11825
11826 Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
11827
11828 var records = store.pushMany(relationship.type, payload);
11829 return records;
11830 }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type);
11831 }
11832
11833 function _findBelongsTo(adapter, store, record, link, relationship) {
11834 var promise = adapter.findBelongsTo(store, record, link, relationship);
11835 var serializer = serializerForAdapter(adapter, relationship.type);
11836 var label = "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type;
11837
11838 promise = Promise.cast(promise, label);
11839 promise = _guard(promise, _bind(_objectIsAlive, store));
11840 promise = _guard(promise, _bind(_objectIsAlive, record));
11841
11842 return promise.then(function(adapterPayload) {
11843 var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findBelongsTo');
11844 var record = store.push(relationship.type, payload);
11845 return record;
11846 }, null, "DS: Extract payload of " + record + " : " + relationship.type);
11847 }
11848
11849 function _findAll(adapter, store, type, sinceToken) {
11850 var promise = adapter.findAll(store, type, sinceToken);
11851 var serializer = serializerForAdapter(adapter, type);
11852 var label = "DS: Handle Adapter#findAll of " + type;
11853
11854 promise = Promise.cast(promise, label);
11855 promise = _guard(promise, _bind(_objectIsAlive, store));
11856
11857 return promise.then(function(adapterPayload) {
11858 var payload = serializer.extract(store, type, adapterPayload, null, 'findAll');
11859
11860 Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
11861
11862 store.pushMany(type, payload);
11863 store.didUpdateAll(type);
11864 return store.all(type);
11865 }, null, "DS: Extract payload of findAll " + type);
11866 }
11867
11868 function _findQuery(adapter, store, type, query, recordArray) {
11869 var promise = adapter.findQuery(store, type, query, recordArray);
11870 var serializer = serializerForAdapter(adapter, type);
11871 var label = "DS: Handle Adapter#findQuery of " + type;
11872
11873 promise = Promise.cast(promise, label);
11874 promise = _guard(promise, _bind(_objectIsAlive, store));
11875
11876 return promise.then(function(adapterPayload) {
11877 var payload = serializer.extract(store, type, adapterPayload, null, 'findQuery');
11878
11879 Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
11880
11881 recordArray.load(payload);
11882 return recordArray;
11883 }, null, "DS: Extract payload of findQuery " + type);
11884 }
11885
11886 function _commit(adapter, store, operation, record) {
11887 var type = record.constructor;
11888 var promise = adapter[operation](store, type, record);
11889 var serializer = serializerForAdapter(adapter, type);
11890 var label = "DS: Extract and notify about " + operation + " completion of " + record;
11891
11892 Ember.assert("Your adapter's '" + operation + "' method must return a value, but it returned `undefined", promise !==undefined);
11893
11894 promise = Promise.cast(promise, label);
11895 promise = _guard(promise, _bind(_objectIsAlive, store));
11896 promise = _guard(promise, _bind(_objectIsAlive, record));
11897
11898 return promise.then(function(adapterPayload) {
11899 var payload;
11900
11901 if (adapterPayload) {
11902 payload = serializer.extract(store, type, adapterPayload, get(record, 'id'), operation);
11903 } else {
11904 payload = adapterPayload;
11905 }
11906
11907 store.didSaveRecord(record, payload);
11908 return record;
11909 }, function(reason) {
11910 if (reason instanceof InvalidError) {
11911 store.recordWasInvalid(record, reason.errors);
11912 } else {
11913 store.recordWasError(record, reason);
11914 }
11915
11916 throw reason;
11917 }, label);
11918 }
11919
11920 function setupRelationships(store, record, data) {
11921 var type = record.constructor;
11922
11923 type.eachRelationship(function(key, descriptor) {
11924 var kind = descriptor.kind;
11925 var value = data[key];
11926 var relationship = record._relationships[key];
11927
11928 if (data.links && data.links[key]) {
11929 relationship.updateLink(data.links[key]);
11930 }
11931
11932 if (kind === 'belongsTo') {
11933 if (value === undefined) {
11934 return;
11935 }
11936 relationship.setRecord(value);
11937 } else if (kind === 'hasMany' && value) {
11938 relationship.updateRecordsFromAdapter(value);
11939 }
11940 });
11941 }
11942
11943 __exports__.Store = Store;
11944 __exports__["default"] = Store;
11945 });
11946define("ember-data/transforms",
11947 ["ember-data/transforms/base","ember-data/transforms/number","ember-data/transforms/date","ember-data/transforms/string","ember-data/transforms/boolean","exports"],
11948 function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
11949 "use strict";
11950 var Transform = __dependency1__["default"];
11951 var NumberTransform = __dependency2__["default"];
11952 var DateTransform = __dependency3__["default"];
11953 var StringTransform = __dependency4__["default"];
11954 var BooleanTransform = __dependency5__["default"];
11955
11956 __exports__.Transform = Transform;
11957 __exports__.NumberTransform = NumberTransform;
11958 __exports__.DateTransform = DateTransform;
11959 __exports__.StringTransform = StringTransform;
11960 __exports__.BooleanTransform = BooleanTransform;
11961 });
11962define("ember-data/transforms/base",
11963 ["exports"],
11964 function(__exports__) {
11965 "use strict";
11966 /**
11967 The `DS.Transform` class is used to serialize and deserialize model
11968 attributes when they are saved or loaded from an
11969 adapter. Subclassing `DS.Transform` is useful for creating custom
11970 attributes. All subclasses of `DS.Transform` must implement a
11971 `serialize` and a `deserialize` method.
11972
11973 Example
11974
11975 ```javascript
11976 // Converts centigrade in the JSON to fahrenheit in the app
11977 App.TemperatureTransform = DS.Transform.extend({
11978 deserialize: function(serialized) {
11979 return (serialized * 1.8) + 32;
11980 },
11981 serialize: function(deserialized) {
11982 return (deserialized - 32) / 1.8;
11983 }
11984 });
11985 ```
11986
11987 Usage
11988
11989 ```javascript
11990 var attr = DS.attr;
11991 App.Requirement = DS.Model.extend({
11992 name: attr('string'),
11993 temperature: attr('temperature')
11994 });
11995 ```
11996
11997 @class Transform
11998 @namespace DS
11999 */
12000 __exports__["default"] = Ember.Object.extend({
12001 /**
12002 When given a deserialized value from a record attribute this
12003 method must return the serialized value.
12004
12005 Example
12006
12007 ```javascript
12008 serialize: function(deserialized) {
12009 return Ember.isEmpty(deserialized) ? null : Number(deserialized);
12010 }
12011 ```
12012
12013 @method serialize
12014 @param {mixed} deserialized The deserialized value
12015 @return {mixed} The serialized value
12016 */
12017 serialize: Ember.required(),
12018
12019 /**
12020 When given a serialize value from a JSON object this method must
12021 return the deserialized value for the record attribute.
12022
12023 Example
12024
12025 ```javascript
12026 deserialize: function(serialized) {
12027 return empty(serialized) ? null : Number(serialized);
12028 }
12029 ```
12030
12031 @method deserialize
12032 @param {mixed} serialized The serialized value
12033 @return {mixed} The deserialized value
12034 */
12035 deserialize: Ember.required()
12036 });
12037 });
12038define("ember-data/transforms/boolean",
12039 ["ember-data/transforms/base","exports"],
12040 function(__dependency1__, __exports__) {
12041 "use strict";
12042 var Transform = __dependency1__["default"];
12043
12044 /**
12045 The `DS.BooleanTransform` class is used to serialize and deserialize
12046 boolean attributes on Ember Data record objects. This transform is
12047 used when `boolean` is passed as the type parameter to the
12048 [DS.attr](../../data#method_attr) function.
12049
12050 Usage
12051
12052 ```javascript
12053 var attr = DS.attr;
12054 App.User = DS.Model.extend({
12055 isAdmin: attr('boolean'),
12056 name: attr('string'),
12057 email: attr('string')
12058 });
12059 ```
12060
12061 @class BooleanTransform
12062 @extends DS.Transform
12063 @namespace DS
12064 */
12065 __exports__["default"] = Transform.extend({
12066 deserialize: function(serialized) {
12067 var type = typeof serialized;
12068
12069 if (type === "boolean") {
12070 return serialized;
12071 } else if (type === "string") {
12072 return serialized.match(/^true$|^t$|^1$/i) !== null;
12073 } else if (type === "number") {
12074 return serialized === 1;
12075 } else {
12076 return false;
12077 }
12078 },
12079
12080 serialize: function(deserialized) {
12081 return Boolean(deserialized);
12082 }
12083 });
12084 });
12085define("ember-data/transforms/date",
12086 ["ember-data/transforms/base","exports"],
12087 function(__dependency1__, __exports__) {
12088 "use strict";
12089 /**
12090 The `DS.DateTransform` class is used to serialize and deserialize
12091 date attributes on Ember Data record objects. This transform is used
12092 when `date` is passed as the type parameter to the
12093 [DS.attr](../../data#method_attr) function.
12094
12095 ```javascript
12096 var attr = DS.attr;
12097 App.Score = DS.Model.extend({
12098 value: attr('number'),
12099 player: DS.belongsTo('player'),
12100 date: attr('date')
12101 });
12102 ```
12103
12104 @class DateTransform
12105 @extends DS.Transform
12106 @namespace DS
12107 */
12108 var Transform = __dependency1__["default"];
12109
12110 // Date.prototype.toISOString shim
12111 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
12112 var toISOString = Date.prototype.toISOString || function() {
12113 function pad(number) {
12114 if ( number < 10 ) {
12115 return '0' + number;
12116 }
12117 return number;
12118 }
12119
12120 return this.getUTCFullYear() +
12121 '-' + pad( this.getUTCMonth() + 1 ) +
12122 '-' + pad( this.getUTCDate() ) +
12123 'T' + pad( this.getUTCHours() ) +
12124 ':' + pad( this.getUTCMinutes() ) +
12125 ':' + pad( this.getUTCSeconds() ) +
12126 '.' + (this.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) +
12127 'Z';
12128 };
12129
12130 if (Ember.SHIM_ES5) {
12131 if (!Date.prototype.toISOString) {
12132 Date.prototype.toISOString = toISOString;
12133 }
12134 }
12135
12136 __exports__["default"] = Transform.extend({
12137
12138 deserialize: function(serialized) {
12139 var type = typeof serialized;
12140
12141 if (type === "string") {
12142 return new Date(Ember.Date.parse(serialized));
12143 } else if (type === "number") {
12144 return new Date(serialized);
12145 } else if (serialized === null || serialized === undefined) {
12146 // if the value is not present in the data,
12147 // return undefined, not null.
12148 return serialized;
12149 } else {
12150 return null;
12151 }
12152 },
12153
12154 serialize: function(date) {
12155 if (date instanceof Date) {
12156 return toISOString.call(date);
12157 } else {
12158 return null;
12159 }
12160 }
12161 });
12162 });
12163define("ember-data/transforms/number",
12164 ["ember-data/transforms/base","exports"],
12165 function(__dependency1__, __exports__) {
12166 "use strict";
12167 var Transform = __dependency1__["default"];
12168
12169 var empty = Ember.isEmpty;
12170
12171 /**
12172 The `DS.NumberTransform` class is used to serialize and deserialize
12173 numeric attributes on Ember Data record objects. This transform is
12174 used when `number` is passed as the type parameter to the
12175 [DS.attr](../../data#method_attr) function.
12176
12177 Usage
12178
12179 ```javascript
12180 var attr = DS.attr;
12181 App.Score = DS.Model.extend({
12182 value: attr('number'),
12183 player: DS.belongsTo('player'),
12184 date: attr('date')
12185 });
12186 ```
12187
12188 @class NumberTransform
12189 @extends DS.Transform
12190 @namespace DS
12191 */
12192 __exports__["default"] = Transform.extend({
12193 deserialize: function(serialized) {
12194 return empty(serialized) ? null : Number(serialized);
12195 },
12196
12197 serialize: function(deserialized) {
12198 return empty(deserialized) ? null : Number(deserialized);
12199 }
12200 });
12201 });
12202define("ember-data/transforms/string",
12203 ["ember-data/transforms/base","exports"],
12204 function(__dependency1__, __exports__) {
12205 "use strict";
12206 var Transform = __dependency1__["default"];
12207 var none = Ember.isNone;
12208
12209 /**
12210 The `DS.StringTransform` class is used to serialize and deserialize
12211 string attributes on Ember Data record objects. This transform is
12212 used when `string` is passed as the type parameter to the
12213 [DS.attr](../../data#method_attr) function.
12214
12215 Usage
12216
12217 ```javascript
12218 var attr = DS.attr;
12219 App.User = DS.Model.extend({
12220 isAdmin: attr('boolean'),
12221 name: attr('string'),
12222 email: attr('string')
12223 });
12224 ```
12225
12226 @class StringTransform
12227 @extends DS.Transform
12228 @namespace DS
12229 */
12230 __exports__["default"] = Transform.extend({
12231 deserialize: function(serialized) {
12232 return none(serialized) ? null : String(serialized);
12233 },
12234 serialize: function(deserialized) {
12235 return none(deserialized) ? null : String(deserialized);
12236 }
12237 });
12238 });
12239define("ember-inflector",
12240 ["./system","./helpers","./ext/string","exports"],
12241 function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
12242 "use strict";
12243 var Inflector = __dependency1__.Inflector;
12244 var defaultRules = __dependency1__.defaultRules;
12245 var pluralize = __dependency1__.pluralize;
12246 var singularize = __dependency1__.singularize;
12247
12248 Inflector.defaultRules = defaultRules;
12249 Ember.Inflector = Inflector;
12250
12251 Ember.String.pluralize = pluralize;
12252 Ember.String.singularize = singularize;
12253
12254
12255 __exports__["default"] = Inflector;
12256
12257 __exports__.pluralize = pluralize;
12258 __exports__.singularize = singularize;
12259 });
12260define("ember-inflector/ext/string",
12261 ["../system/string"],
12262 function(__dependency1__) {
12263 "use strict";
12264 var pluralize = __dependency1__.pluralize;
12265 var singularize = __dependency1__.singularize;
12266
12267 if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
12268 /**
12269 See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
12270
12271 @method pluralize
12272 @for String
12273 */
12274 String.prototype.pluralize = function() {
12275 return pluralize(this);
12276 };
12277
12278 /**
12279 See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
12280
12281 @method singularize
12282 @for String
12283 */
12284 String.prototype.singularize = function() {
12285 return singularize(this);
12286 };
12287 }
12288 });
12289define("ember-inflector/helpers",
12290 ["./system/string"],
12291 function(__dependency1__) {
12292 "use strict";
12293 var singularize = __dependency1__.singularize;
12294 var pluralize = __dependency1__.pluralize;
12295
12296 /**
12297 *
12298 * If you have Ember Inflector (such as if Ember Data is present),
12299 * singularize a word. For example, turn "oxen" into "ox".
12300 *
12301 * Example:
12302 *
12303 * {{singularize myProperty}}
12304 * {{singularize "oxen"}}
12305 *
12306 * @for Ember.Handlebars.helpers
12307 * @method singularize
12308 * @param {String|Property} word word to singularize
12309 */
12310 Ember.Handlebars.helper('singularize', singularize);
12311
12312 /**
12313 *
12314 * If you have Ember Inflector (such as if Ember Data is present),
12315 * pluralize a word. For example, turn "ox" into "oxen".
12316 *
12317 * Example:
12318 *
12319 * {{pluralize myProperty}}
12320 * {{pluralize "oxen"}}
12321 *
12322 * @for Ember.Handlebars.helpers
12323 * @method pluralize
12324 * @param {String|Property} word word to pluralize
12325 */
12326 Ember.Handlebars.helper('pluralize', pluralize);
12327 });
12328define("ember-inflector/system",
12329 ["./system/inflector","./system/string","./system/inflections","exports"],
12330 function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
12331 "use strict";
12332 var Inflector = __dependency1__["default"];
12333
12334 var pluralize = __dependency2__.pluralize;
12335 var singularize = __dependency2__.singularize;
12336
12337 var defaultRules = __dependency3__["default"];
12338
12339
12340 Inflector.inflector = new Inflector(defaultRules);
12341
12342 __exports__.Inflector = Inflector;
12343 __exports__.singularize = singularize;
12344 __exports__.pluralize = pluralize;
12345 __exports__.defaultRules = defaultRules;
12346 });
12347define("ember-inflector/system/inflections",
12348 ["exports"],
12349 function(__exports__) {
12350 "use strict";
12351 __exports__["default"] = {
12352 plurals: [
12353 [/$/, 's'],
12354 [/s$/i, 's'],
12355 [/^(ax|test)is$/i, '$1es'],
12356 [/(octop|vir)us$/i, '$1i'],
12357 [/(octop|vir)i$/i, '$1i'],
12358 [/(alias|status)$/i, '$1es'],
12359 [/(bu)s$/i, '$1ses'],
12360 [/(buffal|tomat)o$/i, '$1oes'],
12361 [/([ti])um$/i, '$1a'],
12362 [/([ti])a$/i, '$1a'],
12363 [/sis$/i, 'ses'],
12364 [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
12365 [/(hive)$/i, '$1s'],
12366 [/([^aeiouy]|qu)y$/i, '$1ies'],
12367 [/(x|ch|ss|sh)$/i, '$1es'],
12368 [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
12369 [/^(m|l)ouse$/i, '$1ice'],
12370 [/^(m|l)ice$/i, '$1ice'],
12371 [/^(ox)$/i, '$1en'],
12372 [/^(oxen)$/i, '$1'],
12373 [/(quiz)$/i, '$1zes']
12374 ],
12375
12376 singular: [
12377 [/s$/i, ''],
12378 [/(ss)$/i, '$1'],
12379 [/(n)ews$/i, '$1ews'],
12380 [/([ti])a$/i, '$1um'],
12381 [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
12382 [/(^analy)(sis|ses)$/i, '$1sis'],
12383 [/([^f])ves$/i, '$1fe'],
12384 [/(hive)s$/i, '$1'],
12385 [/(tive)s$/i, '$1'],
12386 [/([lr])ves$/i, '$1f'],
12387 [/([^aeiouy]|qu)ies$/i, '$1y'],
12388 [/(s)eries$/i, '$1eries'],
12389 [/(m)ovies$/i, '$1ovie'],
12390 [/(x|ch|ss|sh)es$/i, '$1'],
12391 [/^(m|l)ice$/i, '$1ouse'],
12392 [/(bus)(es)?$/i, '$1'],
12393 [/(o)es$/i, '$1'],
12394 [/(shoe)s$/i, '$1'],
12395 [/(cris|test)(is|es)$/i, '$1is'],
12396 [/^(a)x[ie]s$/i, '$1xis'],
12397 [/(octop|vir)(us|i)$/i, '$1us'],
12398 [/(alias|status)(es)?$/i, '$1'],
12399 [/^(ox)en/i, '$1'],
12400 [/(vert|ind)ices$/i, '$1ex'],
12401 [/(matr)ices$/i, '$1ix'],
12402 [/(quiz)zes$/i, '$1'],
12403 [/(database)s$/i, '$1']
12404 ],
12405
12406 irregularPairs: [
12407 ['person', 'people'],
12408 ['man', 'men'],
12409 ['child', 'children'],
12410 ['sex', 'sexes'],
12411 ['move', 'moves'],
12412 ['cow', 'kine'],
12413 ['zombie', 'zombies']
12414 ],
12415
12416 uncountable: [
12417 'equipment',
12418 'information',
12419 'rice',
12420 'money',
12421 'species',
12422 'series',
12423 'fish',
12424 'sheep',
12425 'jeans',
12426 'police'
12427 ]
12428 };
12429 });
12430define("ember-inflector/system/inflector",
12431 ["exports"],
12432 function(__exports__) {
12433 "use strict";
12434 var BLANK_REGEX = /^\s*$/;
12435 var LAST_WORD_DASHED_REGEX = /(\w+[_-])([a-z\d]+$)/;
12436 var LAST_WORD_CAMELIZED_REGEX = /(\w+)([A-Z][a-z\d]*$)/;
12437 var CAMELIZED_REGEX = /[A-Z][a-z\d]*$/;
12438
12439 function loadUncountable(rules, uncountable) {
12440 for (var i = 0, length = uncountable.length; i < length; i++) {
12441 rules.uncountable[uncountable[i].toLowerCase()] = true;
12442 }
12443 }
12444
12445 function loadIrregular(rules, irregularPairs) {
12446 var pair;
12447
12448 for (var i = 0, length = irregularPairs.length; i < length; i++) {
12449 pair = irregularPairs[i];
12450
12451 //pluralizing
12452 rules.irregular[pair[0].toLowerCase()] = pair[1];
12453 rules.irregular[pair[1].toLowerCase()] = pair[1];
12454
12455 //singularizing
12456 rules.irregularInverse[pair[1].toLowerCase()] = pair[0];
12457 rules.irregularInverse[pair[0].toLowerCase()] = pair[0];
12458 }
12459 }
12460
12461 /**
12462 Inflector.Ember provides a mechanism for supplying inflection rules for your
12463 application. Ember includes a default set of inflection rules, and provides an
12464 API for providing additional rules.
12465
12466 Examples:
12467
12468 Creating an inflector with no rules.
12469
12470 ```js
12471 var inflector = new Ember.Inflector();
12472 ```
12473
12474 Creating an inflector with the default ember ruleset.
12475
12476 ```js
12477 var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
12478
12479 inflector.pluralize('cow'); //=> 'kine'
12480 inflector.singularize('kine'); //=> 'cow'
12481 ```
12482
12483 Creating an inflector and adding rules later.
12484
12485 ```javascript
12486 var inflector = Ember.Inflector.inflector;
12487
12488 inflector.pluralize('advice'); // => 'advices'
12489 inflector.uncountable('advice');
12490 inflector.pluralize('advice'); // => 'advice'
12491
12492 inflector.pluralize('formula'); // => 'formulas'
12493 inflector.irregular('formula', 'formulae');
12494 inflector.pluralize('formula'); // => 'formulae'
12495
12496 // you would not need to add these as they are the default rules
12497 inflector.plural(/$/, 's');
12498 inflector.singular(/s$/i, '');
12499 ```
12500
12501 Creating an inflector with a nondefault ruleset.
12502
12503 ```javascript
12504 var rules = {
12505 plurals: [ /$/, 's' ],
12506 singular: [ /\s$/, '' ],
12507 irregularPairs: [
12508 [ 'cow', 'kine' ]
12509 ],
12510 uncountable: [ 'fish' ]
12511 };
12512
12513 var inflector = new Ember.Inflector(rules);
12514 ```
12515
12516 @class Inflector
12517 @namespace Ember
12518 */
12519 function Inflector(ruleSet) {
12520 ruleSet = ruleSet || {};
12521 ruleSet.uncountable = ruleSet.uncountable || makeDictionary();
12522 ruleSet.irregularPairs = ruleSet.irregularPairs || makeDictionary();
12523
12524 var rules = this.rules = {
12525 plurals: ruleSet.plurals || [],
12526 singular: ruleSet.singular || [],
12527 irregular: makeDictionary(),
12528 irregularInverse: makeDictionary(),
12529 uncountable: makeDictionary()
12530 };
12531
12532 loadUncountable(rules, ruleSet.uncountable);
12533 loadIrregular(rules, ruleSet.irregularPairs);
12534
12535 this.enableCache();
12536 }
12537
12538 if (!Object.create && !Object.create(null).hasOwnProperty) {
12539 throw new Error("This browser does not support Object.create(null), please polyfil with es5-sham: http://git.io/yBU2rg");
12540 }
12541
12542 function makeDictionary() {
12543 var cache = Object.create(null);
12544 cache['_dict'] = null;
12545 delete cache['_dict'];
12546 return cache;
12547 }
12548
12549 Inflector.prototype = {
12550 /**
12551 @public
12552
12553 As inflections can be costly, and commonly the same subset of words are repeatedly
12554 inflected an optional cache is provided.
12555
12556 @method enableCache
12557 */
12558 enableCache: function() {
12559 this.purgeCache();
12560
12561 this.singularize = function(word) {
12562 this._cacheUsed = true;
12563 return this._sCache[word] || (this._sCache[word] = this._singularize(word));
12564 };
12565
12566 this.pluralize = function(word) {
12567 this._cacheUsed = true;
12568 return this._pCache[word] || (this._pCache[word] = this._pluralize(word));
12569 };
12570 },
12571
12572 /**
12573 @public
12574
12575 @method purgedCache
12576 */
12577 purgeCache: function() {
12578 this._cacheUsed = false;
12579 this._sCache = makeDictionary();
12580 this._pCache = makeDictionary();
12581 },
12582
12583 /**
12584 @public
12585 disable caching
12586
12587 @method disableCache;
12588 */
12589 disableCache: function() {
12590 this._sCache = null;
12591 this._pCache = null;
12592 this.singularize = function(word) {
12593 return this._singularize(word);
12594 };
12595
12596 this.pluralize = function(word) {
12597 return this._pluralize(word);
12598 };
12599 },
12600
12601 /**
12602 @method plural
12603 @param {RegExp} regex
12604 @param {String} string
12605 */
12606 plural: function(regex, string) {
12607 if (this._cacheUsed) { this.purgeCache(); }
12608 this.rules.plurals.push([regex, string.toLowerCase()]);
12609 },
12610
12611 /**
12612 @method singular
12613 @param {RegExp} regex
12614 @param {String} string
12615 */
12616 singular: function(regex, string) {
12617 if (this._cacheUsed) { this.purgeCache(); }
12618 this.rules.singular.push([regex, string.toLowerCase()]);
12619 },
12620
12621 /**
12622 @method uncountable
12623 @param {String} regex
12624 */
12625 uncountable: function(string) {
12626 if (this._cacheUsed) { this.purgeCache(); }
12627 loadUncountable(this.rules, [string.toLowerCase()]);
12628 },
12629
12630 /**
12631 @method irregular
12632 @param {String} singular
12633 @param {String} plural
12634 */
12635 irregular: function (singular, plural) {
12636 if (this._cacheUsed) { this.purgeCache(); }
12637 loadIrregular(this.rules, [[singular, plural]]);
12638 },
12639
12640 /**
12641 @method pluralize
12642 @param {String} word
12643 */
12644 pluralize: function(word) {
12645 return this._pluralize(word);
12646 },
12647
12648 _pluralize: function(word) {
12649 return this.inflect(word, this.rules.plurals, this.rules.irregular);
12650 },
12651 /**
12652 @method singularize
12653 @param {String} word
12654 */
12655 singularize: function(word) {
12656 return this._singularize(word);
12657 },
12658
12659 _singularize: function(word) {
12660 return this.inflect(word, this.rules.singular, this.rules.irregularInverse);
12661 },
12662
12663 /**
12664 @protected
12665
12666 @method inflect
12667 @param {String} word
12668 @param {Object} typeRules
12669 @param {Object} irregular
12670 */
12671 inflect: function(word, typeRules, irregular) {
12672 var inflection, substitution, result, lowercase, wordSplit,
12673 firstPhrase, lastWord, isBlank, isCamelized, isUncountable,
12674 isIrregular, isIrregularInverse, rule;
12675
12676 isBlank = BLANK_REGEX.test(word);
12677 isCamelized = CAMELIZED_REGEX.test(word);
12678 firstPhrase = "";
12679
12680 if (isBlank) {
12681 return word;
12682 }
12683
12684 lowercase = word.toLowerCase();
12685 wordSplit = LAST_WORD_DASHED_REGEX.exec(word) || LAST_WORD_CAMELIZED_REGEX.exec(word);
12686 if (wordSplit){
12687 firstPhrase = wordSplit[1];
12688 lastWord = wordSplit[2].toLowerCase();
12689 }
12690
12691 isUncountable = this.rules.uncountable[lowercase] || this.rules.uncountable[lastWord];
12692
12693 if (isUncountable) {
12694 return word;
12695 }
12696
12697 isIrregular = irregular && (irregular[lowercase] || irregular[lastWord]);
12698
12699 if (isIrregular) {
12700 if (irregular[lowercase]){
12701 return isIrregular;
12702 }
12703 else {
12704 isIrregular = (isCamelized) ? isIrregular.capitalize() : isIrregular;
12705 return firstPhrase + isIrregular;
12706 }
12707 }
12708
12709 for (var i = typeRules.length, min = 0; i > min; i--) {
12710 inflection = typeRules[i-1];
12711 rule = inflection[0];
12712
12713 if (rule.test(word)) {
12714 break;
12715 }
12716 }
12717
12718 inflection = inflection || [];
12719
12720 rule = inflection[0];
12721 substitution = inflection[1];
12722
12723 result = word.replace(rule, substitution);
12724
12725 return result;
12726 }
12727 };
12728
12729 __exports__["default"] = Inflector;
12730 });
12731define("ember-inflector/system/string",
12732 ["./inflector","exports"],
12733 function(__dependency1__, __exports__) {
12734 "use strict";
12735 var Inflector = __dependency1__["default"];
12736
12737 function pluralize(word) {
12738 return Inflector.inflector.pluralize(word);
12739 }
12740
12741 function singularize(word) {
12742 return Inflector.inflector.singularize(word);
12743 }
12744
12745 __exports__.pluralize = pluralize;
12746 __exports__.singularize = singularize;
12747 });
12748 global.DS = requireModule('ember-data')['default'];
12749 })(this);