ember-data-source 1.0.0.beta.3 → 1.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,18 @@
1
- // ==========================================================================
2
- // Project: Ember Data
3
- // Copyright: Copyright 2011-2013 Tilde Inc. and contributors.
4
- // Portions Copyright 2011 LivingSocial Inc.
5
- // License: Licensed under MIT license (see license.js)
6
- // ==========================================================================
1
+ /*!
2
+ * @overview Ember Data
3
+ * @copyright Copyright 2011-2013 Tilde Inc. and contributors.
4
+ * Portions Copyright 2011 LivingSocial Inc.
5
+ * @license Licensed under MIT license (see license.js)
6
+ */
7
7
 
8
8
 
9
+ // Version: 1.0.0-beta.4
9
10
 
10
- // Version: v1.0.0-beta.3-48-gb47afef
11
- // Last commit: b47afef (2013-10-08 19:07:59 -0700)
12
-
13
-
14
- // Copyright: Copyright 2011-2013 Tilde Inc. and contributors.
15
- // Portions Copyright 2011 LivingSocial Inc.
16
- !function(){var e,t;!function(){var r={},n={};e=function(e,t,n){r[e]={deps:t,callback:n}},t=function(e){if(n[e])return n[e];n[e]={};var i,a,o,s,c;if(i=r[e],!i)throw new Error("Module '"+e+"' not found.");a=i.deps,o=i.callback,s=[];for(var u=0,d=a.length;d>u;u++)"exports"===a[u]?s.push(c={}):s.push(t(a[u]));var l=o.apply(this,s);return n[e]=c||l}}(),function(){"undefined"==typeof DS&&(DS=Ember.Namespace.create({VERSION:"1.0.0-beta.2"}),"undefined"!=typeof window&&(window.DS=DS),Ember.libraries&&Ember.libraries.registerCoreLibrary("Ember Data",DS.VERSION))}(),function(){function e(e){return function(){return this[e].apply(this,arguments)}}var t=Ember.get,r=(Ember.set,Ember.isNone);DS.JSONSerializer=Ember.Object.extend({primaryKey:"id",applyTransforms:function(e,t){return e.eachTransformedAttribute(function(e,r){var n=this.transformFor(r);t[e]=n.deserialize(t[e])},this),t},normalize:function(e,t){return t?(this.applyTransforms(e,t),t):t},serialize:function(e,r){var n={};if(r&&r.includeId){var i=t(e,"id");i&&(n[t(this,"primaryKey")]=t(e,"id"))}return e.eachAttribute(function(t,r){this.serializeAttribute(e,n,t,r)},this),e.eachRelationship(function(t,r){"belongsTo"===r.kind?this.serializeBelongsTo(e,n,r):"hasMany"===r.kind&&this.serializeHasMany(e,n,r)},this),n},serializeAttribute:function(e,r,n,i){var a=t(this,"attrs"),o=t(e,n),s=i.type;if(s){var c=this.transformFor(s);o=c.serialize(o)}n=a&&a[n]||(this.keyForAttribute?this.keyForAttribute(n):n),r[n]=o},serializeBelongsTo:function(e,n,i){var a=i.key,o=t(e,a);a=this.keyForRelationship?this.keyForRelationship(a,"belongsTo"):a,n[a]=r(o)?o:t(o,"id"),i.options.polymorphic&&this.serializePolymorphicType(e,n,i)},serializeHasMany:function(e,r,n){var i=n.key,a=DS.RelationshipChange.determineRelationshipType(e.constructor,n);("manyToNone"===a||"manyToMany"===a)&&(r[i]=t(e,i).mapBy("id"))},serializePolymorphicType:Ember.K,extract:function(e,t,r,n,i){this.extractMeta(e,t,r);var a="extract"+i.charAt(0).toUpperCase()+i.substr(1);return this[a](e,t,r,n,i)},extractFindAll:e("extractArray"),extractFindQuery:e("extractArray"),extractFindMany:e("extractArray"),extractFindHasMany:e("extractArray"),extractCreateRecord:e("extractSave"),extractUpdateRecord:e("extractSave"),extractDeleteRecord:e("extractSave"),extractFind:e("extractSingle"),extractFindBelongsTo:e("extractSingle"),extractSave:e("extractSingle"),extractSingle:function(e,t,r){return this.normalize(t,r)},extractArray:function(e,t,r){return this.normalize(t,r)},extractMeta:function(e,t,r){r&&r.meta&&(e.metaForType(t,r.meta),delete r.meta)},transformFor:function(e){var t=this.container.lookup("transform:"+e);return t}})}(),function(){var e=Ember.get,t=Ember.String.capitalize,r=Ember.String.underscore,n=window.DS;n.DebugAdapter=Ember.DataAdapter.extend({getFilters:function(){return[{name:"isNew",desc:"New"},{name:"isModified",desc:"Modified"},{name:"isClean",desc:"Clean"}]},detect:function(e){return e!==n.Model&&n.Model.detect(e)},columnsForType:function(n){var i=[{name:"id",desc:"Id"}],a=0,o=this;return e(n,"attributes").forEach(function(e){if(a++>o.attributeLimit)return!1;var n=t(r(e).replace("_"," "));i.push({name:e,desc:n})}),i},getRecords:function(e){return this.get("store").all(e)},getRecordColumnValues:function(t){var r=this,n=0,i={id:e(t,"id")};return t.eachAttribute(function(a){if(n++>r.attributeLimit)return!1;var o=e(t,a);i[a]=o}),i},getRecordKeywords:function(t){var r=[],n=Ember.A(["id"]);return t.eachAttribute(function(e){n.push(e)}),n.forEach(function(n){r.push(e(t,n))}),r},getRecordFilterValues:function(e){return{isNew:e.get("isNew"),isModified:e.get("isDirty")&&!e.get("isNew"),isClean:!e.get("isDirty")}},getRecordColor:function(e){var t="black";return e.get("isNew")?t="green":e.get("isDirty")&&(t="blue"),t},observeRecord:function(e,t){var r=Ember.A(),n=this,i=Ember.A(["id","isNew","isDirty"]);e.eachAttribute(function(e){i.push(e)}),i.forEach(function(i){var a=function(){t(n.wrapRecord(e))};Ember.addObserver(e,i,a),r.push(function(){Ember.removeObserver(e,i,a)})});var a=function(){r.forEach(function(e){e()})};return a}})}(),function(){DS.Transform=Ember.Object.extend({serialize:Ember.required(),deserialize:Ember.required()})}(),function(){DS.BooleanTransform=DS.Transform.extend({deserialize:function(e){var t=typeof e;return"boolean"===t?e:"string"===t?null!==e.match(/^true$|^t$|^1$/i):"number"===t?1===e:!1},serialize:function(e){return Boolean(e)}})}(),function(){DS.DateTransform=DS.Transform.extend({deserialize:function(e){var t=typeof e;return"string"===t?new Date(Ember.Date.parse(e)):"number"===t?new Date(e):null===e||void 0===e?e:null},serialize:function(e){if(e instanceof Date){var t=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],r=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],n=function(e){return 10>e?"0"+e:""+e},i=e.getUTCFullYear(),a=e.getUTCMonth(),o=e.getUTCDate(),s=e.getUTCDay(),c=e.getUTCHours(),u=e.getUTCMinutes(),d=e.getUTCSeconds(),l=t[s],h=n(o),f=r[a];return l+", "+h+" "+f+" "+i+" "+n(c)+":"+n(u)+":"+n(d)+" GMT"}return null}})}(),function(){var e=Ember.isEmpty;DS.NumberTransform=DS.Transform.extend({deserialize:function(t){return e(t)?null:Number(t)},serialize:function(t){return e(t)?null:Number(t)}})}(),function(){var e=Ember.isNone;DS.StringTransform=DS.Transform.extend({deserialize:function(t){return e(t)?null:String(t)},serialize:function(t){return e(t)?null:String(t)}})}(),function(){Ember.set,Ember.onLoad("Ember.Application",function(e){e.initializer({name:"store",initialize:function(e,t){t.register("store:main",t.Store||DS.Store),t.register("serializer:_default",DS.JSONSerializer),t.register("serializer:_rest",DS.RESTSerializer),t.register("adapter:_rest",DS.RESTAdapter),e.lookup("store:main")}}),e.initializer({name:"transforms",initialize:function(e,t){t.register("transform:boolean",DS.BooleanTransform),t.register("transform:date",DS.DateTransform),t.register("transform:number",DS.NumberTransform),t.register("transform:string",DS.StringTransform)}}),e.initializer({name:"dataAdapter",initialize:function(e,t){t.register("dataAdapter:main",DS.DebugAdapter)}}),e.initializer({name:"injectStore",initialize:function(e,t){t.inject("controller","store","store:main"),t.inject("route","store","store:main"),t.inject("serializer","store","store:main"),t.inject("dataAdapter","store","store:main")}})})}(),function(){Ember.Date=Ember.Date||{};var e=Date.parse,t=[1,4,5,6,7,10,11];Ember.Date.parse=function(r){var n,i,a=0;if(i=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(r)){for(var o,s=0;o=t[s];++s)i[o]=+i[o]||0;i[2]=(+i[2]||1)-1,i[3]=+i[3]||1,"Z"!==i[8]&&void 0!==i[9]&&(a=60*i[10]+i[11],"+"===i[9]&&(a=0-a)),n=Date.UTC(i[1],i[2],i[3],i[4],i[5]+a,i[6],i[7])}else n=e?e(r):0/0;return n},(Ember.EXTEND_PROTOTYPES===!0||Ember.EXTEND_PROTOTYPES.Date)&&(Date.parse=Ember.Date.parse)}(),function(){var e=Ember.get;Ember.set,DS.RecordArray=Ember.ArrayProxy.extend(Ember.Evented,{type:null,content:null,isLoaded:!1,isUpdating:!1,store:null,objectAtContent:function(t){var r=e(this,"content");return r.objectAt(t)},update:function(){if(!e(this,"isUpdating")){var t=e(this,"store"),r=e(this,"type");t.fetchAll(r,this)}},addRecord:function(t){e(this,"content").addObject(t)},removeRecord:function(t){e(this,"content").removeObject(t)},save:function(){var e=Ember.RSVP.all(this.invoke("save")).then(function(e){return Ember.A(e)});return DS.PromiseArray.create({promise:e})}})}(),function(){var e=Ember.get;DS.FilteredRecordArray=DS.RecordArray.extend({filterFunction:null,isLoaded:!0,replace:function(){var t=e(this,"type").toString();throw new Error("The result of a client-side filter (on "+t+") is immutable.")},updateFilter:Ember.observer(function(){var t=e(this,"manager");t.updateFilter(this,e(this,"type"),e(this,"filterFunction"))},"filterFunction")})}(),function(){var e=Ember.get;Ember.set,DS.AdapterPopulatedRecordArray=DS.RecordArray.extend({query:null,replace:function(){var t=e(this,"type").toString();throw new Error("The result of a server query (on "+t+") is immutable.")},load:function(t){var r=e(this,"store"),n=e(this,"type"),i=r.pushMany(n,t),a=r.metadataFor(n);this.setProperties({content:Ember.A(i),isLoaded:!0,meta:a}),Ember.run.once(this,"trigger","didLoad")}})}(),function(){var e=Ember.get,t=Ember.set,r=Ember.EnumerableUtils.map;DS.ManyArray=DS.RecordArray.extend({init:function(){this._super.apply(this,arguments),this._changesToSync=Ember.OrderedSet.create()},owner:null,isPolymorphic:!1,isLoaded:!1,loadingRecordsCount:function(e){this.loadingRecordsCount=e},loadedRecord:function(){this.loadingRecordsCount--,0===this.loadingRecordsCount&&(t(this,"isLoaded",!0),this.trigger("didLoad"))},fetch:function(){var t=e(this,"content"),r=e(this,"store"),n=e(this,"owner"),i=Ember.RSVP.defer(),a=t.filterProperty("isEmpty",!0);r.fetchMany(a,n,i)},replaceContent:function(e,t,n){n=r(n,function(e){return e},this),this._super(e,t,n)},arrangedContentDidChange:function(){Ember.run.once(this,"fetch")},arrayContentWillChange:function(t,r){var n=e(this,"owner"),i=e(this,"name");if(!n._suspendedRelationships)for(var a=t;t+r>a;a++){var o=e(this,"content").objectAt(a),s=DS.RelationshipChange.createChange(n,o,e(this,"store"),{parentType:n.constructor,changeType:"remove",kind:"hasMany",key:i});this._changesToSync.add(s)}return this._super.apply(this,arguments)},arrayContentDidChange:function(t,r,n){this._super.apply(this,arguments);var i=e(this,"owner"),a=e(this,"name"),o=e(this,"store");if(!i._suspendedRelationships){for(var s=t;t+n>s;s++){var c=e(this,"content").objectAt(s),u=DS.RelationshipChange.createChange(i,c,o,{parentType:i.constructor,changeType:"add",kind:"hasMany",key:a});u.hasManyName=a,this._changesToSync.add(u)}this._changesToSync.forEach(function(e){e.sync()}),this._changesToSync.clear()}},createRecord:function(t){var r,n=e(this,"owner"),i=e(n,"store"),a=e(this,"type");return r=i.createRecord.call(i,a,t),this.pushObject(r),r}})}(),function(){function e(e){var t=Ember.meta(e,!0),r="DS.Mappable",n=t[r];return n||(t[r]={}),t.hasOwnProperty(r)||(t[r]=Ember.create(t[r])),t[r]}Ember.get;var t=Ember.ArrayPolyfills.forEach,r=function(e){return e},n=function(e){return e},i=function(e,t){return t};DS._Mappable=Ember.Mixin.create({createInstanceMapFor:function(t){var r=e(this);if(r.values=r.values||{},r.values[t])return r.values[t];for(var n=r.values[t]=new Ember.Map,i=this.constructor;i&&i!==DS.Store;)this._copyMap(t,i,n),i=i.superclass;return r.values[t]=n,n},_copyMap:function(a,o,s){function c(e,t){var a=(o.transformMapKey||n)(e,t),c=(o.transformMapValue||i)(e,t),u=s.get(a),d=c;u&&(d=(this.constructor.resolveMapConflict||r)(u,d)),s.set(a,d)}var u=e(o),d=u[a];d&&t.call(d,c,this)}}),DS._Mappable.generateMapFunctionFor=function(t,r){return function(n,i){var a=e(this),o=a[t]||Ember.MapWithDefault.create({defaultValue:function(){return{}}});r.call(this,n,i,o),a[t]=o}}}(),function(){function e(e,r,a,o){return r.eachRelationship(function(r,s){if(a.links&&a.links[r])return o&&s.options.async&&(o._relationships[r]=null),void 0;var c=s.kind,u=a[r];null!=u&&("belongsTo"===c?t(e,a,r,s,u):"hasMany"===c&&(n(e,a,r,s,u),i(o,r,u)))}),a}function t(e,t,n,i,a){if(!(v(a)||a instanceof DS.Model)){var o;"number"==typeof a||"string"==typeof a?(o=r(i,n,t),t[n]=e.recordForId(o,a)):"object"==typeof a&&(t[n]=e.recordForId(a.type,a.id))}}function r(e,t,r){return e.options.polymorphic?r[t+"Type"]:e.type}function n(e,r,n,i,a){for(var o=0,s=a.length;s>o;o++)t(e,a,o,i,a[o])}function i(e,t,r){e&&r.pushObjects(e.get(t).filterBy("isNew"))}function a(e){return DS.PromiseObject.create({promise:e})}function o(e){return DS.PromiseArray.create({promise:e})}function s(e,t,r){return e.lookup("serializer:"+t)||e.lookup("serializer:application")||e.lookup("serializer:"+r)||e.lookup("serializer:_default")}function c(e,t){var r=e.serializer,n=e.defaultSerializer,i=e.container;return i&&void 0===r&&(r=s(i,t.typeKey,n)),(null===r||void 0===r)&&(r={extract:function(e,t,r){return r}}),r}function u(e,t,r,n,i){var a=e.find(t,r,n),o=c(e,r);return D(a).then(function(e){return e=o.extract(t,r,e,n,"find"),t.push(r,e)},function(e){var i=t.getById(r,n);throw i.notFound(),e}).then(i.resolve,i.reject)}function d(e,t,r,n,i,a){var o=e.findMany(t,r,n,i),s=c(e,r);return D(o).then(function(e){e=s.extract(t,r,e,null,"findMany"),t.pushMany(r,e)}).then(a.resolve,a.reject)}function l(e,t,r,n,i,a){var o=e.findHasMany(t,r,n,i),s=c(e,i.type);return D(o).then(function(e){e=s.extract(t,i.type,e,null,"findHasMany");var n=t.pushMany(i.type,e);r.updateHasMany(i.key,n)}).then(a.resolve,a.reject)}function h(e,t,r,n,i,a){var o=e.findBelongsTo(t,r,n,i),s=c(e,i.type);return D(o).then(function(e){e=s.extract(t,i.type,e,null,"findBelongsTo");var r=t.push(i.type,e);return r.updateBelongsTo(i.key,r),r}).then(a.resolve,a.reject)}function f(e,t,r,n,i){var a=e.findAll(t,r,n),o=c(e,r);return D(a).then(function(e){return e=o.extract(t,r,e,null,"findAll"),t.pushMany(r,e),t.didUpdateAll(r),t.all(r)}).then(i.resolve,i.reject)}function p(e,t,r,n,i,a){var o=e.findQuery(t,r,n,i),s=c(e,r);return D(o).then(function(e){return e=s.extract(t,r,e,null,"findAll"),i.load(e),i}).then(a.resolve,a.reject)}function m(e,t,r,n,i){var a=n.constructor,o=e[r](t,a,n),s=c(e,a);return o.then(function(e){return e&&(e=s.extract(t,a,e,y(n,"id"),r)),t.didSaveRecord(n,e),n},function(e){throw e instanceof DS.InvalidError?t.recordWasInvalid(n,e.errors):t.recordWasError(n,e),e}).then(i.resolve,i.reject)}var y=Ember.get,g=Ember.set,b=Ember.run.once,v=Ember.isNone,R=Ember.EnumerableUtils.forEach,E=Ember.EnumerableUtils.indexOf,S=Ember.EnumerableUtils.map,D=Ember.RSVP.resolve,T=function(e){return null==e?null:e+""};DS.Store=Ember.Object.extend(DS._Mappable,{init:function(){this.typeMaps={},this.recordArrayManager=DS.RecordArrayManager.create({store:this}),this._relationshipChanges={},this._pendingSave=[]},adapter:"_rest",serialize:function(e,t){return this.serializerFor(e.constructor.typeKey).serialize(e,t)},defaultAdapter:Ember.computed(function(){var e=y(this,"adapter");return"string"==typeof e&&(e=this.container.lookup("adapter:"+e)||this.container.lookup("adapter:application")||this.container.lookup("adapter:_rest")),DS.Adapter.detect(e)&&(e=e.create({container:this.container})),e}).property("adapter"),createRecord:function(e,t){e=this.modelFor(e),t=t||{},v(t.id)&&(t.id=this._generateId(e)),t.id=T(t.id);var r=this.buildRecord(e,t.id);return r.loadedData(),r.setProperties(t),r},_generateId:function(e){var t=this.adapterFor(e);return t&&t.generateIdForRecord?t.generateIdForRecord(this):null},deleteRecord:function(e){e.deleteRecord()},unloadRecord:function(e){e.unloadRecord()},find:function(e,t){return void 0===t?this.findAll(e):"object"===Ember.typeOf(t)?this.findQuery(e,t):this.findById(e,T(t))},findById:function(e,t){e=this.modelFor(e);var r=this.recordForId(e,t),n=this.fetchRecord(r)||D(r);return a(n)},findByIds:function(e,t){var r=this;return o(Ember.RSVP.all(S(t,function(t){return r.findById(e,t)})).then(function(e){return Ember.A(e)}))},fetchRecord:function(e){if(v(e))return null;if(e._loadingPromise)return e._loadingPromise;if(!y(e,"isEmpty"))return null;var t=e.constructor,r=y(e,"id"),n=Ember.RSVP.defer();e.loadingData(n.promise);var i=this.adapterFor(t);return u(i,this,t,r,n),n.promise},getById:function(e,t){return e=this.modelFor(e),this.hasRecordForId(e,t)?this.recordForId(e,t):null},reloadRecord:function(e,t){var r=e.constructor,n=this.adapterFor(r),i=y(e,"id");return u(n,this,r,i,t)},fetchMany:function(e,t,r){if(e.length){var n=Ember.MapWithDefault.create({defaultValue:function(){return Ember.A()}});R(e,function(e){n.get(e.constructor).push(e)}),R(n,function(e,n){var i=n.mapProperty("id"),a=this.adapterFor(e);d(a,this,e,i,t,r)},this)}},hasRecordForId:function(e,t){return t=T(t),!!this.typeMapFor(e).idToRecord[t]},recordForId:function(e,t){e=this.modelFor(e),t=T(t);var r=this.typeMapFor(e).idToRecord[t];return r||(r=this.buildRecord(e,t)),r},findMany:function(e,t,r,n){r=this.modelFor(r),t=Ember.A(t);var i=t.filterProperty("isEmpty",!0),a=this.recordArrayManager.createManyArray(r,t);return R(i,function(e){e.loadingData()}),a.loadingRecordsCount=i.length,i.length?(R(i,function(e){this.recordArrayManager.registerWaitingRecordArray(e,a)},this),this.fetchMany(i,e,n)):(n&&n.resolve(),a.set("isLoaded",!0),Ember.run.once(a,"trigger","didLoad")),a},findHasMany:function(e,t,r,n){var i=this.adapterFor(e.constructor),a=this.recordArrayManager.createManyArray(r.type,Ember.A([]));return l(i,this,e,t,r,n),a},findBelongsTo:function(e,t,r,n){var i=this.adapterFor(e.constructor);h(i,this,e,t,r,n)},findQuery:function(e,t){e=this.modelFor(e);var r=DS.AdapterPopulatedRecordArray.create({type:e,query:t,content:Ember.A(),store:this}),n=this.adapterFor(e),i=Ember.RSVP.defer();return p(n,this,e,t,r,i),o(i.promise)},findAll:function(e){return e=this.modelFor(e),this.fetchAll(e,this.all(e))},fetchAll:function(e,t){var r=this.adapterFor(e),n=this.typeMapFor(e).metadata.since,i=Ember.RSVP.defer();return g(t,"isUpdating",!0),f(r,this,e,n,i),o(i.promise)},didUpdateAll:function(e){var t=this.typeMapFor(e).findAllCache;g(t,"isUpdating",!1)},all:function(e){e=this.modelFor(e);var t=this.typeMapFor(e),r=t.findAllCache;if(r)return r;var n=DS.RecordArray.create({type:e,content:Ember.A(),store:this,isLoaded:!0});return this.recordArrayManager.registerFilteredRecordArray(n,e),t.findAllCache=n,n},unloadAll:function(e){e=this.modelFor(e);for(var t,r=this.typeMapFor(e),n=r.records;t=n.pop();)t.unloadRecord()},filter:function(e,t,r){var n;3===arguments.length?n=this.findQuery(e,t):2===arguments.length&&(r=t),e=this.modelFor(e);var i=DS.FilteredRecordArray.create({type:e,content:Ember.A(),store:this,manager:this.recordArrayManager,filterFunction:r});return this.recordArrayManager.registerFilteredRecordArray(i,e,r),n?n.then(function(){return i}):i},recordIsLoaded:function(e,t){return this.hasRecordForId(e,t)?!y(this.recordForId(e,t),"isEmpty"):!1},metadataFor:function(e){return e=this.modelFor(e),this.typeMapFor(e).metadata},dataWasUpdated:function(e,t){y(t,"isDeleted")||y(t,"isLoaded")&&this.recordArrayManager.recordDidChange(t)},scheduleSave:function(e,t){e.adapterWillCommit(),this._pendingSave.push([e,t]),b(this,"flushPendingSave")},flushPendingSave:function(){var e=this._pendingSave.slice();this._pendingSave=[],R(e,function(e){var t,r=e[0],n=e[1],i=this.adapterFor(r.constructor);t=y(r,"isNew")?"createRecord":y(r,"isDeleted")?"deleteRecord":"updateRecord",m(i,this,t,r,n)},this)},didSaveRecord:function(t,r){r&&(r=e(this,t.constructor,r,t),this.updateId(t,r)),t.adapterDidCommit(r)},recordWasInvalid:function(e,t){e.adapterDidInvalidate(t)},recordWasError:function(e){e.adapterDidError()},updateId:function(e,t){var r=(y(e,"id"),T(t.id));this.typeMapFor(e.constructor).idToRecord[r]=e,g(e,"id",r)},typeMapFor:function(e){var t,r=y(this,"typeMaps"),n=Ember.guidFor(e);return(t=r[n])?t:(t={idToRecord:{},records:[],metadata:{}},r[n]=t,t)},_load:function(e,t,r){var n=T(t.id),i=this.recordForId(e,n);return i.setupData(t,r),this.recordArrayManager.recordDidChange(i),i},modelFor:function(e){var t;return"string"==typeof e?(t=this.container.lookupFactory("model:"+e),t.typeKey=e):t=e,t.store=this,t},push:function(t,r,n){return t=this.modelFor(t),r=e(this,t,r),this._load(t,r,n),this.recordForId(t,r.id)},pushPayload:function(e,t){var r=this.serializerFor(e);r.pushPayload(this,t)},update:function(e,t){return this.push(e,t,!0)},pushMany:function(e,t){return S(t,function(t){return this.push(e,t)},this)},metaForType:function(e,t){e=this.modelFor(e),Ember.merge(this.typeMapFor(e).metadata,t)},buildRecord:function(e,t,r){var n=this.typeMapFor(e),i=n.idToRecord,a=e._create({id:t,store:this,container:this.container});return r&&a.setupData(r),t&&(i[t]=a),n.records.push(a),a},dematerializeRecord:function(e){var t=e.constructor,r=this.typeMapFor(t),n=y(e,"id");e.updateRecordArrays(),n&&delete r.idToRecord[n];var i=E(r.records,e);r.records.splice(i,1)},addRelationshipChangeFor:function(e,t,r,n,i){var a=e.clientId,o=r?r:r,s=t+n,c=this._relationshipChanges;a in c||(c[a]={}),o in c[a]||(c[a][o]={}),s in c[a][o]||(c[a][o][s]={}),c[a][o][s][i.changeType]=i},removeRelationshipChangeFor:function(e,t,r,n,i){var a=e.clientId,o=r?r.clientId:r,s=this._relationshipChanges,c=t+n;a in s&&o in s[a]&&c in s[a][o]&&delete s[a][o][c][i]},relationshipChangePairsFor:function(e){var t=[];if(!e)return t;var r=this._relationshipChanges[e.clientId];for(var n in r)if(r.hasOwnProperty(n))for(var i in r[n])r[n].hasOwnProperty(i)&&t.push(r[n][i]);return t},adapterFor:function(e){var t,r=this.container;return r&&(t=r.lookup("adapter:"+e.typeKey)||r.lookup("adapter:application")),t||y(this,"defaultAdapter")},serializerFor:function(e){e=this.modelFor(e);var t=this.adapterFor(e);return s(this.container,e.typeKey,t&&t.defaultSerializer)}}),DS.PromiseArray=Ember.ArrayProxy.extend(Ember.PromiseProxyMixin),DS.PromiseObject=Ember.ObjectProxy.extend(Ember.PromiseProxyMixin)}(),function(){function e(t){var r,n={};for(var i in t)r=t[i],n[i]=r&&"object"==typeof r?e(r):r;return n}function t(e,t){for(var r in t)e[r]=t[r];return e}function r(r){var n=e(c);return t(n,r)}function n(e,r,i){e=t(r?Ember.create(r):{},e),e.parentState=r,e.stateName=i;for(var a in e)e.hasOwnProperty(a)&&"parentState"!==a&&"stateName"!==a&&"object"==typeof e[a]&&(e[a]=n(e[a],e,i+"."+a));return e}var i=Ember.get,a=Ember.set,o=function(e){var t,r,n,i=Ember.keys(e);for(t=0,r=i.length;r>t;t++)if(n=i[t],e.hasOwnProperty(n)&&e[n])return!0;return!1},s=function(e,t){t.value===t.originalValue?(delete e._attributes[t.name],e.send("propertyWasReset",t.name)):t.value!==t.oldValue&&e.send("becomeDirty"),e.updateRecordArraysLater()},c={initialState:"uncommitted",isDirty:!0,uncommitted:{didSetProperty:s,propertyWasReset:function(e){var t=!1;for(var r in e._attributes){t=!0;break}t||e.send("rolledBack")},pushedData:Ember.K,becomeDirty:Ember.K,willCommit:function(e){e.transitionTo("inFlight")},reloadRecord:function(e,t){i(e,"store").reloadRecord(e,t)},rolledBack:function(e){e.transitionTo("loaded.saved")},becameInvalid:function(e){e.transitionTo("invalid")},rollback:function(e){e.rollback()}},inFlight:{isSaving:!0,didSetProperty:s,becomeDirty:Ember.K,pushedData:Ember.K,willCommit:Ember.K,didCommit:function(e){var t=i(this,"dirtyType");e.transitionTo("saved"),e.send("invokeLifecycleCallbacks",t)},becameInvalid:function(e,t){a(e,"errors",t),e.transitionTo("invalid"),e.send("invokeLifecycleCallbacks")},becameError:function(e){e.transitionTo("uncommitted"),e.triggerLater("becameError",e)}},invalid:{isValid:!1,deleteRecord:function(e){e.transitionTo("deleted.uncommitted"),e.clearRelationships()},didSetProperty:function(e,t){var r=i(e,"errors"),n=t.name;a(r,n,null),o(r)||e.send("becameValid"),s(e,t)},becomeDirty:Ember.K,rollback:function(e){e.send("becameValid"),e.send("rollback")},becameValid:function(e){e.transitionTo("uncommitted")},invokeLifecycleCallbacks:function(e){e.triggerLater("becameInvalid",e)}}},u=r({dirtyType:"created",isNew:!0});u.uncommitted.rolledBack=function(e){e.transitionTo("deleted.saved")};var d=r({dirtyType:"updated"});u.uncommitted.deleteRecord=function(e){e.clearRelationships(),e.transitionTo("deleted.saved")},u.uncommitted.rollback=function(e){c.uncommitted.rollback.apply(this,arguments),e.transitionTo("deleted.saved")},d.uncommitted.deleteRecord=function(e){e.transitionTo("deleted.uncommitted"),e.clearRelationships()};var l={isEmpty:!1,isLoading:!1,isLoaded:!1,isDirty:!1,isSaving:!1,isDeleted:!1,isNew:!1,isValid:!0,rolledBack:Ember.K,propertyWasReset:Ember.K,empty:{isEmpty:!0,loadingData:function(e,t){e._loadingPromise=t,e.transitionTo("loading")},loadedData:function(e){e.transitionTo("loaded.created.uncommitted"),e.suspendRelationshipObservers(function(){e.notifyPropertyChange("data")})},pushedData:function(e){e.transitionTo("loaded.saved"),e.triggerLater("didLoad")}},loading:{isLoading:!0,exit:function(e){e._loadingPromise=null},pushedData:function(e){e.transitionTo("loaded.saved"),e.triggerLater("didLoad"),a(e,"isError",!1)},becameError:function(e){e.triggerLater("becameError",e)},notFound:function(e){e.transitionTo("empty")}},loaded:{initialState:"saved",isLoaded:!0,saved:{setup:function(e){var t=e._attributes,r=!1;for(var n in t)if(t.hasOwnProperty(n)){r=!0;break}r&&e.adapterDidDirty()},didSetProperty:s,pushedData:Ember.K,becomeDirty:function(e){e.transitionTo("updated.uncommitted")},willCommit:function(e){e.transitionTo("updated.inFlight")},reloadRecord:function(e,t){i(e,"store").reloadRecord(e,t)},deleteRecord:function(e){e.transitionTo("deleted.uncommitted"),e.clearRelationships()},unloadRecord:function(e){e.clearRelationships(),e.transitionTo("deleted.saved")},didCommit:function(e){e.send("invokeLifecycleCallbacks",i(e,"lastDirtyType"))}},created:u,updated:d},deleted:{initialState:"uncommitted",dirtyType:"deleted",isDeleted:!0,isLoaded:!0,isDirty:!0,setup:function(e){var t=i(e,"store");t.recordArrayManager.remove(e)},uncommitted:{willCommit:function(e){e.transitionTo("inFlight")},rollback:function(e){e.rollback()},becomeDirty:Ember.K,deleteRecord:Ember.K,rolledBack:function(e){e.transitionTo("loaded.saved")}},inFlight:{isSaving:!0,willCommit:Ember.K,didCommit:function(e){e.transitionTo("saved"),e.send("invokeLifecycleCallbacks")},becameError:function(e){e.transitionTo("uncommitted"),e.triggerLater("becameError",e)}},saved:{isDirty:!1,setup:function(e){var t=i(e,"store");t.dematerializeRecord(e)},invokeLifecycleCallbacks:function(e){e.triggerLater("didDelete",e),e.triggerLater("didCommit",e)}}},invokeLifecycleCallbacks:function(e,t){"created"===t?e.triggerLater("didCreate",e):e.triggerLater("didUpdate",e),e.triggerLater("didCommit",e)}};l=n(l,null,"root"),DS.RootState=l}(),function(){var e=Ember.get,t=Ember.set,r=Ember.merge,n=Ember.run.once,i=Ember.computed(function(t){return e(e(this,"currentState"),t)}).property("currentState").readOnly();DS.Model=Ember.Object.extend(Ember.Evented,{isEmpty:i,isLoading:i,isLoaded:i,isDirty:i,isSaving:i,isDeleted:i,isNew:i,isValid:i,dirtyType:i,isError:!1,isReloading:!1,clientId:null,id:null,transaction:null,currentState:null,errors:null,serialize:function(t){var r=e(this,"store");return r.serialize(this,t)},toJSON:function(e){var t=DS.JSONSerializer.create({container:this.container});return t.serialize(this,e)},didLoad:Ember.K,didReload:Ember.K,didUpdate:Ember.K,didCreate:Ember.K,didDelete:Ember.K,becameInvalid:Ember.K,becameError:Ember.K,data:Ember.computed(function(){return this._data=this._data||{},this._data}).property(),_data:null,init:function(){t(this,"currentState",DS.RootState.empty),this._super(),this._setup()},_setup:function(){this._changesToSync={},this._deferredTriggers=[],this._data={},this._attributes={},this._inFlightAttributes={},this._relationships={}},send:function(t,r){var n=e(this,"currentState");return n[t]||this._unhandledEvent(n,t,r),n[t](this,r)},transitionTo:function(r){var n=r.split(".",1),i=e(this,"currentState"),a=i;do a.exit&&a.exit(this),a=a.parentState;while(!a.hasOwnProperty(n));var o,s,c=r.split("."),u=[],d=[];for(o=0,s=c.length;s>o;o++)a=a[c[o]],a.enter&&d.push(a),a.setup&&u.push(a);for(o=0,s=d.length;s>o;o++)d[o].enter(this);for(t(this,"currentState",a),o=0,s=u.length;s>o;o++)u[o].setup(this);this.updateRecordArraysLater()},_unhandledEvent:function(e,t,r){var n="Attempted to handle event `"+t+"` ";throw n+="on "+String(this)+" while in state ",n+=e.stateName+". ",void 0!==r&&(n+="Called with "+Ember.inspect(r)+"."),new Ember.Error(n)},withTransaction:function(t){var r=e(this,"transaction");r&&t(r)},loadingData:function(e){this.send("loadingData",e)},loadedData:function(){this.send("loadedData")},notFound:function(){this.send("notFound")},pushedData:function(){this.send("pushedData")},deleteRecord:function(){this.send("deleteRecord")},destroyRecord:function(){return this.deleteRecord(),this.save()},unloadRecord:function(){this.send("unloadRecord")},clearRelationships:function(){this.eachRelationship(function(e,r){if("belongsTo"===r.kind)t(this,e,null);else if("hasMany"===r.kind){var n=this._relationships[r.name];n&&n.clear()}},this)},updateRecordArrays:function(){var t=e(this,"store");t&&t.dataWasUpdated(this.constructor,this)},changedAttributes:function(){var t,r=e(this,"_data"),n=e(this,"_attributes"),i={};for(t in n)i[t]=[r[t],n[t]];return i},adapterWillCommit:function(){this.send("willCommit")},adapterDidCommit:function(e){t(this,"isError",!1),e?this._data=e:Ember.mixin(this._data,this._inFlightAttributes),this._inFlightAttributes={},this.send("didCommit"),this.updateRecordArraysLater(),e&&this.suspendRelationshipObservers(function(){this.notifyPropertyChange("data")})},adapterDidDirty:function(){this.send("becomeDirty"),this.updateRecordArraysLater()},dataDidChange:Ember.observer(function(){this.reloadHasManys()},"data"),reloadHasManys:function(){var t=e(this.constructor,"relationshipsByName");this.updateRecordArraysLater(),t.forEach(function(e,t){this._data.links&&this._data.links[e]||"hasMany"===t.kind&&this.hasManyDidChange(t.key)},this)},hasManyDidChange:function(e){var r=this._relationships[e];if(r){var n=this._data[e]||[];t(r,"content",Ember.A(n)),t(r,"isLoaded",!0),r.trigger("didLoad")}},updateRecordArraysLater:function(){Ember.run.once(this,this.updateRecordArrays)},setupData:function(e,t){t?Ember.merge(this._data,e):this._data=e;var r=this._relationships;this.eachRelationship(function(t,n){e.links&&e.links[t]||n.options.async&&(r[t]=null)}),e&&this.pushedData(),this.suspendRelationshipObservers(function(){this.notifyPropertyChange("data")})},materializeId:function(e){t(this,"id",e)},materializeAttributes:function(e){r(this._data,e)},materializeAttribute:function(e,t){this._data[e]=t},updateHasMany:function(e,t){this._data[e]=t,this.hasManyDidChange(e)},updateBelongsTo:function(e,t){this._data[e]=t},rollback:function(){this._attributes={},e(this,"isError")&&(this._inFlightAttributes={},t(this,"isError",!1)),this.send("rolledBack"),this.suspendRelationshipObservers(function(){this.notifyPropertyChange("data")})},toStringExtension:function(){return e(this,"id")},suspendRelationshipObservers:function(t,r){var n=e(this.constructor,"relationshipNames").belongsTo,i=this;try{this._suspendedRelationships=!0,Ember._suspendObservers(i,n,null,"belongsToDidChange",function(){Ember._suspendBeforeObservers(i,n,null,"belongsToWillChange",function(){t.call(r||i)})})}finally{this._suspendedRelationships=!1}},save:function(){var e=Ember.RSVP.defer();return this.get("store").scheduleSave(this,e),this._inFlightAttributes=this._attributes,this._attributes={},DS.PromiseObject.create({promise:e.promise})},reload:function(){t(this,"isReloading",!0);var e=Ember.RSVP.defer(),r=this;return e.promise=e.promise.then(function(){return r.set("isReloading",!1),r.set("isError",!1),r},function(e){throw r.set("isError",!0),e}),this.send("reloadRecord",e),DS.PromiseObject.create({promise:e.promise})},adapterDidUpdateAttribute:function(e,t){void 0!==t?(this._data[e]=t,this.notifyPropertyChange(e)):this._data[e]=this._inFlightAttributes[e],this.updateRecordArraysLater()},adapterDidInvalidate:function(e){this.send("becameInvalid",e)},adapterDidError:function(){this.send("becameError"),t(this,"isError",!0)},trigger:function(e){Ember.tryInvoke(this,e,[].slice.call(arguments,1)),this._super.apply(this,arguments)},triggerLater:function(){this._deferredTriggers.push(arguments),n(this,"_triggerDeferredTriggers")},_triggerDeferredTriggers:function(){for(var e=0,t=this._deferredTriggers.length;t>e;e++)this.trigger.apply(this,this._deferredTriggers[e]);this._deferredTriggers=[]}}),DS.Model.reopenClass({_create:DS.Model.create,create:function(){throw new Ember.Error("You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.")}})}(),function(){function e(e,t){return"function"==typeof t.defaultValue?t.defaultValue():t.defaultValue}function t(e,t){return e._attributes.hasOwnProperty(t)||e._inFlightAttributes.hasOwnProperty(t)||e._data.hasOwnProperty(t)}function r(e,t){return e._attributes.hasOwnProperty(t)?e._attributes[t]:e._inFlightAttributes.hasOwnProperty(t)?e._inFlightAttributes[t]:e._data[t]}var n=Ember.get;DS.Model.reopenClass({attributes:Ember.computed(function(){var e=Ember.Map.create();return this.eachComputedProperty(function(t,r){r.isAttribute&&(r.name=t,e.set(t,r))}),e}),transformedAttributes:Ember.computed(function(){var e=Ember.Map.create();return this.eachAttribute(function(t,r){r.type&&e.set(t,r.type)}),e}),eachAttribute:function(e,t){n(this,"attributes").forEach(function(r,n){e.call(t,r,n)},t)},eachTransformedAttribute:function(e,t){n(this,"transformedAttributes").forEach(function(r,n){e.call(t,r,n)
17
- })}}),DS.Model.reopen({eachAttribute:function(e,t){this.constructor.eachAttribute(e,t)}}),DS.attr=function(n,i){i=i||{};var a={type:n,isAttribute:!0,options:i};return Ember.computed(function(n,a){if(arguments.length>1){var o=this._attributes[n]||this._inFlightAttributes[n]||this._data[n];return this.send("didSetProperty",{name:n,oldValue:o,originalValue:this._data[n],value:a}),this._attributes[n]=a,a}return t(this,n)?r(this,n):e(this,i,n)}).property("data").meta(a)}}(),function(){var e=DS.AttributeChange=function(e){this.record=e.record,this.store=e.store,this.name=e.name,this.value=e.value,this.oldValue=e.oldValue};e.createChange=function(t){return new e(t)},e.prototype={sync:function(){this.value!==this.oldValue&&(this.record.send("becomeDirty"),this.record.updateRecordArraysLater()),this.destroy()},destroy:function(){delete this.record._changesToSync[this.name]}}}(),function(){function e(e){return"object"==typeof e&&(!e.then||"function"!=typeof e.then)}var t=Ember.get,r=Ember.set,n=Ember.EnumerableUtils.forEach;DS.RelationshipChange=function(e){this.parentRecord=e.parentRecord,this.childRecord=e.childRecord,this.firstRecord=e.firstRecord,this.firstRecordKind=e.firstRecordKind,this.firstRecordName=e.firstRecordName,this.secondRecord=e.secondRecord,this.secondRecordKind=e.secondRecordKind,this.secondRecordName=e.secondRecordName,this.changeType=e.changeType,this.store=e.store,this.committed={}},DS.RelationshipChangeAdd=function(e){DS.RelationshipChange.call(this,e)},DS.RelationshipChangeRemove=function(e){DS.RelationshipChange.call(this,e)},DS.RelationshipChange.create=function(e){return new DS.RelationshipChange(e)},DS.RelationshipChangeAdd.create=function(e){return new DS.RelationshipChangeAdd(e)},DS.RelationshipChangeRemove.create=function(e){return new DS.RelationshipChangeRemove(e)},DS.OneToManyChange={},DS.OneToNoneChange={},DS.ManyToNoneChange={},DS.OneToOneChange={},DS.ManyToManyChange={},DS.RelationshipChange._createChange=function(e){return"add"===e.changeType?DS.RelationshipChangeAdd.create(e):"remove"===e.changeType?DS.RelationshipChangeRemove.create(e):void 0},DS.RelationshipChange.determineRelationshipType=function(e,t){var r,n,i=t.key,a=t.kind,o=e.inverseFor(i);return o&&(r=o.name,n=o.kind),o?"belongsTo"===n?"belongsTo"===a?"oneToOne":"manyToOne":"belongsTo"===a?"oneToMany":"manyToMany":"belongsTo"===a?"oneToNone":"manyToNone"},DS.RelationshipChange.createChange=function(e,t,r,n){var i,a=e.constructor;return i=DS.RelationshipChange.determineRelationshipType(a,n),"oneToMany"===i?DS.OneToManyChange.createChange(e,t,r,n):"manyToOne"===i?DS.OneToManyChange.createChange(t,e,r,n):"oneToNone"===i?DS.OneToNoneChange.createChange(e,t,r,n):"manyToNone"===i?DS.ManyToNoneChange.createChange(e,t,r,n):"oneToOne"===i?DS.OneToOneChange.createChange(e,t,r,n):"manyToMany"===i?DS.ManyToManyChange.createChange(e,t,r,n):void 0},DS.OneToNoneChange.createChange=function(e,t,r,n){var i=n.key,a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,store:r,changeType:n.changeType,firstRecordName:i,firstRecordKind:"belongsTo"});return r.addRelationshipChangeFor(e,i,t,null,a),a},DS.ManyToNoneChange.createChange=function(e,t,r,n){var i=n.key,a=DS.RelationshipChange._createChange({parentRecord:e,childRecord:t,secondRecord:e,store:r,changeType:n.changeType,secondRecordName:n.key,secondRecordKind:"hasMany"});return r.addRelationshipChangeFor(e,i,t,null,a),a},DS.ManyToManyChange.createChange=function(e,t,r,n){var i=n.key,a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,secondRecord:t,firstRecordKind:"hasMany",secondRecordKind:"hasMany",store:r,changeType:n.changeType,firstRecordName:i});return r.addRelationshipChangeFor(e,i,t,null,a),a},DS.OneToOneChange.createChange=function(e,t,r,n){var i;n.parentType?i=n.parentType.inverseFor(n.key).name:n.key&&(i=n.key);var a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,secondRecord:t,firstRecordKind:"belongsTo",secondRecordKind:"belongsTo",store:r,changeType:n.changeType,firstRecordName:i});return r.addRelationshipChangeFor(e,i,t,null,a),a},DS.OneToOneChange.maintainInvariant=function(e,r,n,i){if("add"===e.changeType&&r.recordIsMaterialized(n)){var a=t(n,i);if(a){var o=DS.OneToOneChange.createChange(n,a,r,{parentType:e.parentType,hasManyName:e.hasManyName,changeType:"remove",key:e.key});r.addRelationshipChangeFor(n,i,e.parentRecord,null,o),o.sync()}}},DS.OneToManyChange.createChange=function(e,t,r,n){var i;n.parentType?(i=n.parentType.inverseFor(n.key).name,DS.OneToManyChange.maintainInvariant(n,r,e,i)):n.key&&(i=n.key);var a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,secondRecord:t,firstRecordKind:"belongsTo",secondRecordKind:"hasMany",store:r,changeType:n.changeType,firstRecordName:i});return r.addRelationshipChangeFor(e,i,t,a.getSecondRecordName(),a),a},DS.OneToManyChange.maintainInvariant=function(e,r,n,i){if("add"===e.changeType&&n){var a=t(n,i);if(a){var o=DS.OneToManyChange.createChange(n,a,r,{parentType:e.parentType,hasManyName:e.hasManyName,changeType:"remove",key:e.key});r.addRelationshipChangeFor(n,i,e.parentRecord,o.getSecondRecordName(),o),o.sync()}}},DS.RelationshipChange.prototype={getSecondRecordName:function(){var e,t=this.secondRecordName;if(!t){if(e=this.secondRecord,!e)return;var r=this.firstRecord.constructor,n=r.inverseFor(this.firstRecordName);this.secondRecordName=n.name}return this.secondRecordName},getFirstRecordName:function(){var e=this.firstRecordName;return e},destroy:function(){var e=this.childRecord,t=this.getFirstRecordName(),r=this.getSecondRecordName(),n=this.store;n.removeRelationshipChangeFor(e,t,this.parentRecord,r,this.changeType)},getSecondRecord:function(){return this.secondRecord},getFirstRecord:function(){return this.firstRecord},coalesce:function(){var e=this.store.relationshipChangePairsFor(this.firstRecord);n(e,function(e){var t=e.add,r=e.remove;t&&r&&(t.destroy(),r.destroy())})}},DS.RelationshipChangeAdd.prototype=Ember.create(DS.RelationshipChange.create({})),DS.RelationshipChangeRemove.prototype=Ember.create(DS.RelationshipChange.create({})),DS.RelationshipChangeAdd.prototype.changeType="add",DS.RelationshipChangeAdd.prototype.sync=function(){var n=this.getSecondRecordName(),i=this.getFirstRecordName(),a=this.getFirstRecord(),o=this.getSecondRecord();o instanceof DS.Model&&a instanceof DS.Model&&("belongsTo"===this.secondRecordKind?o.suspendRelationshipObservers(function(){r(o,n,a)}):"hasMany"===this.secondRecordKind&&o.suspendRelationshipObservers(function(){var r=t(o,n);e(r)&&r.addObject(a)})),a instanceof DS.Model&&o instanceof DS.Model&&t(a,i)!==o&&("belongsTo"===this.firstRecordKind?a.suspendRelationshipObservers(function(){r(a,i,o)}):"hasMany"===this.firstRecordKind&&a.suspendRelationshipObservers(function(){var r=t(a,i);e(r)&&r.addObject(o)})),this.coalesce()},DS.RelationshipChangeRemove.prototype.changeType="remove",DS.RelationshipChangeRemove.prototype.sync=function(){var n=this.getSecondRecordName(),i=this.getFirstRecordName(),a=this.getFirstRecord(),o=this.getSecondRecord();o instanceof DS.Model&&a instanceof DS.Model&&("belongsTo"===this.secondRecordKind?o.suspendRelationshipObservers(function(){r(o,n,null)}):"hasMany"===this.secondRecordKind&&o.suspendRelationshipObservers(function(){var r=t(o,n);e(r)&&r.removeObject(a)})),a instanceof DS.Model&&t(a,i)&&("belongsTo"===this.firstRecordKind?a.suspendRelationshipObservers(function(){r(a,i,null)}):"hasMany"===this.firstRecordKind&&a.suspendRelationshipObservers(function(){var r=t(a,i);e(r)&&r.removeObject(o)})),this.coalesce()}}(),function(){function e(e,n,i){return Ember.computed(function(e,n){var a=t(this,"data"),o=t(this,"store");if(2===arguments.length)return void 0===n?null:DS.PromiseObject.create({promise:Ember.RSVP.resolve(n)});var s=a.links&&a.links[e],c=a[e];if(r(c)){if(s){var u=Ember.RSVP.defer();return o.findBelongsTo(this,s,i,u),DS.PromiseObject.create({promise:u.promise})}return null}var d=o.fetchRecord(c)||Ember.RSVP.resolve(c);return DS.PromiseObject.create({promise:d})}).property("data").meta(i)}var t=Ember.get,r=(Ember.set,Ember.isNone);DS.belongsTo=function(n,i){"object"==typeof n&&(i=n,n=void 0),i=i||{};var a={type:n,isRelationship:!0,options:i,kind:"belongsTo"};return i.async?e(n,i,a):Ember.computed(function(e,i){var a,o,s=t(this,"data"),c=t(this,"store");return o="string"==typeof n?c.modelFor(n):n,2===arguments.length?void 0===i?null:i:(a=s[e],r(a)?null:(c.fetchRecord(a),a))}).property("data").meta(a)},DS.Model.reopen({belongsToWillChange:Ember.beforeObserver(function(e,r){if(t(e,"isLoaded")){var n=t(e,r);if(n){var i=t(e,"store"),a=DS.RelationshipChange.createChange(e,n,i,{key:r,kind:"belongsTo",changeType:"remove"});a.sync(),this._changesToSync[r]=a}}}),belongsToDidChange:Ember.immediateObserver(function(e,r){if(t(e,"isLoaded")){var n=t(e,r);if(n){var i=t(e,"store"),a=DS.RelationshipChange.createChange(e,n,i,{key:r,kind:"belongsTo",changeType:"add"});a.sync()}}delete this._changesToSync[r]})})}(),function(){function e(e,r,n){return Ember.computed(function(e){if(this._relationships[e])return this._relationships[e];var i=Ember.RSVP.defer(),a=t(this,e,r,function(t,r){var a=r.links&&r.links[e];return a?t.findHasMany(this,a,n,i):t.findMany(this,r[e],n.type,i)}),o=i.promise.then(function(){return a});return DS.PromiseArray.create({promise:o})}).property("data").meta(n)}function t(e,t,r,a){var o=e._relationships;if(o[t])return o[t];var s=n(e,"data"),c=n(e,"store"),u=o[t]=a.call(e,c,s);return i(u,{owner:e,name:t,isPolymorphic:r.polymorphic})}function r(r,n){n=n||{};var i={type:r,isRelationship:!0,options:n,kind:"hasMany"};return n.async?e(r,n,i):Ember.computed(function(e){return t(this,e,n,function(t,r){return r[e],t.findMany(this,r[e],i.type)})}).property("data").meta(i)}var n=Ember.get,i=(Ember.set,Ember.setProperties);DS.hasMany=function(e,t){return"object"==typeof e&&(t=e,e=void 0),r(e,t)}}(),function(){var e=Ember.get;Ember.set,DS.Model.reopen({didDefineProperty:function(e,t,r){if(r instanceof Ember.Descriptor){var n=r.meta();n.isRelationship&&"belongsTo"===n.kind&&(Ember.addObserver(e,t,null,"belongsToDidChange"),Ember.addBeforeObserver(e,t,null,"belongsToWillChange")),n.parentType=e.constructor}}}),DS.Model.reopenClass({typeForRelationship:function(t){var r=e(this,"relationshipsByName").get(t);return r&&r.type},inverseFor:function(t){function r(t,n,i){i=i||[];var a=e(n,"relationships");if(a){var o=a.get(t);return o&&i.push.apply(i,a.get(t)),t.superclass&&r(t.superclass,n,i),i}}var n=this.typeForRelationship(t);if(!n)return null;var i=this.metaForProperty(t).options;if(null===i.inverse)return null;var a,o;if(i.inverse)a=i.inverse,o=Ember.get(n,"relationshipsByName").get(a).kind;else{var s=r(this,n);if(0===s.length)return null;a=s[0].name,o=s[0].kind}return{type:n,name:a,kind:o}},relationships:Ember.computed(function(){var e=new Ember.MapWithDefault({defaultValue:function(){return[]}});return this.eachComputedProperty(function(t,r){if(r.isRelationship){"string"==typeof r.type&&(r.type=this.store.modelFor(r.type));var n=e.get(r.type);n.push({name:t,kind:r.kind})}}),e}),relationshipNames:Ember.computed(function(){var e={hasMany:[],belongsTo:[]};return this.eachComputedProperty(function(t,r){r.isRelationship&&e[r.kind].push(t)}),e}),relatedTypes:Ember.computed(function(){var t,r=Ember.A();return this.eachComputedProperty(function(n,i){i.isRelationship&&(t=i.type,"string"==typeof t&&(t=e(this,t,!1)||this.store.modelFor(t)),r.contains(t)||r.push(t))}),r}),relationshipsByName:Ember.computed(function(){var e,t=Ember.Map.create();return this.eachComputedProperty(function(r,n){n.isRelationship&&(n.key=r,e=n.type,e||"hasMany"!==n.kind?e||(e=r):e=Ember.String.singularize(r),"string"==typeof e&&(n.type=this.store.modelFor(e)),t.set(r,n))}),t}),fields:Ember.computed(function(){var e=Ember.Map.create();return this.eachComputedProperty(function(t,r){r.isRelationship?e.set(t,r.kind):r.isAttribute&&e.set(t,"attribute")}),e}),eachRelationship:function(t,r){e(this,"relationshipsByName").forEach(function(e,n){t.call(r,e,n)})},eachRelatedType:function(t,r){e(this,"relatedTypes").forEach(function(e){t.call(r,e)})}}),DS.Model.reopen({eachRelationship:function(e,t){this.constructor.eachRelationship(e,t)}})}(),function(){var e=Ember.get;Ember.set;var t=Ember.run.once,r=Ember.EnumerableUtils.forEach;DS.RecordArrayManager=Ember.Object.extend({init:function(){this.filteredRecordArrays=Ember.MapWithDefault.create({defaultValue:function(){return[]}}),this.changedRecords=[]},recordDidChange:function(e){this.changedRecords.push(e),t(this,this.updateRecordArrays)},recordArraysForRecord:function(e){return e._recordArrays=e._recordArrays||Ember.OrderedSet.create(),e._recordArrays},updateRecordArrays:function(){r(this.changedRecords,function(t){var n,i=t.constructor,a=this.filteredRecordArrays.get(i);r(a,function(r){n=e(r,"filterFunction"),this.updateRecordArray(r,n,i,t)},this);var o=t._loadingRecordArrays;if(o){for(var s=0,c=o.length;c>s;s++)o[s].loadedRecord();t._loadingRecordArrays=[]}},this),this.changedRecords=[]},updateRecordArray:function(e,t,r,n){var i;i=t?t(n):!0;var a=this.recordArraysForRecord(n);i?(a.add(e),e.addRecord(n)):i||(a.remove(e),e.removeRecord(n))},remove:function(e){var t=e._recordArrays;t&&r(t,function(t){t.removeRecord(e)})},updateFilter:function(t,r,n){for(var i,a=this.store.typeMapFor(r),o=a.records,s=0,c=o.length;c>s;s++)i=o[s],e(i,"isDeleted")||e(i,"isEmpty")||this.updateRecordArray(t,n,r,i)},createManyArray:function(e,t){var n=DS.ManyArray.create({type:e,content:t,store:this.store});return r(t,function(e){var t=this.recordArraysForRecord(e);t.add(n)},this),n},registerFilteredRecordArray:function(e,t,r){var n=this.filteredRecordArrays.get(t);n.push(e),this.updateFilter(e,t,r)},registerWaitingRecordArray:function(e,t){var r=e._loadingRecordArrays||[];r.push(t),e._loadingRecordArrays=r}})}(),function(){var e=Ember.get;Ember.set;var t=Ember.ArrayPolyfills.map,r=["description","fileName","lineNumber","message","name","number","stack"];DS.InvalidError=function(e){var t=Error.prototype.constructor.call(this,"The backend rejected the commit because it was invalid: "+Ember.inspect(e));this.errors=e;for(var n=0,i=r.length;i>n;n++)this[r[n]]=t[r[n]]},DS.InvalidError.prototype=Ember.create(Error.prototype),DS.Adapter=Ember.Object.extend(DS._Mappable,{find:Ember.required(Function),findAll:null,findQuery:null,generateIdForRecord:null,serialize:function(t,r){return e(t,"store").serializerFor(t.constructor.typeKey).serialize(t,r)},createRecord:Ember.required(Function),updateRecord:Ember.required(Function),deleteRecord:Ember.required(Function),findMany:function(e,r,n){var i=t.call(n,function(t){return this.find(e,r,t)},this);return Ember.RSVP.all(i)}})}(),function(){var e=Ember.get,t=Ember.String.fmt,r=Ember.EnumerableUtils.indexOf,n=0;DS.FixtureAdapter=DS.Adapter.extend({serializer:null,simulateRemoteResponse:!0,latency:50,fixturesForType:function(e){if(e.FIXTURES){var r=Ember.A(e.FIXTURES);return r.map(function(e){var r=typeof e.id;if("number"!==r&&"string"!==r)throw new Error(t("the id property must be defined as a number or string for fixture %@",[e]));return e.id=e.id+"",e})}return null},queryFixtures:function(){},updateFixtures:function(e,t){e.FIXTURES||(e.FIXTURES=[]);var r=e.FIXTURES;this.deleteLoadedFixture(e,t),r.push(t)},mockJSON:function(e,t,r){return e.serializerFor(t).serialize(r,{includeId:!0})},generateIdForRecord:function(){return"fixture-"+n++},find:function(e,t,r){var n,i=this.fixturesForType(t);return i&&(n=Ember.A(i).findProperty("id",r)),n?this.simulateRemoteCall(function(){return n},this):void 0},findMany:function(e,t,n){var i=this.fixturesForType(t);return i&&(i=i.filter(function(e){return-1!==r(n,e.id)})),i?this.simulateRemoteCall(function(){return i},this):void 0},findAll:function(e,t){var r=this.fixturesForType(t);return this.simulateRemoteCall(function(){return r},this)},findQuery:function(e,t,r){var n=this.fixturesForType(t);return n=this.queryFixtures(n,r,t),n?this.simulateRemoteCall(function(){return n},this):void 0},createRecord:function(e,t,r){var n=this.mockJSON(e,t,r);return this.updateFixtures(t,n),this.simulateRemoteCall(function(){return n},this)},updateRecord:function(e,t,r){var n=this.mockJSON(e,t,r);return this.updateFixtures(t,n),this.simulateRemoteCall(function(){return n},this)},deleteRecord:function(e,t,r){var n=this.mockJSON(e,t,r);return this.deleteLoadedFixture(t,n),this.simulateRemoteCall(function(){return null})},deleteLoadedFixture:function(e,t){var n=this.findExistingFixture(e,t);if(n){var i=r(e.FIXTURES,n);return e.FIXTURES.splice(i,1),!0}},findExistingFixture:function(t,r){var n=this.fixturesForType(t),i=e(r,"id");return this.findFixtureById(n,i)},findFixtureById:function(t,r){return Ember.A(t).find(function(t){return""+e(t,"id")==""+r?!0:!1})},simulateRemoteCall:function(t,r){var n=this;return new Ember.RSVP.Promise(function(i){e(n,"simulateRemoteResponse")?Ember.run.later(function(){i(t.call(r))},e(n,"latency")):Ember.run.once(function(){i(t.call(r))})})}})}(),function(){function e(e){return null==e?null:e+""}var t=Ember.get;Ember.set;var r=Ember.ArrayPolyfills.forEach,n=Ember.ArrayPolyfills.map;DS.RESTSerializer=DS.JSONSerializer.extend({normalize:function(e,t,r){return this.normalizeId(t),this.normalizeUsingDeclaredMapping(e,t),this.normalizeAttributes(e,t),this.normalizeRelationships(e,t),this.normalizeHash&&this.normalizeHash[r]?this.normalizeHash[r](t):this._super(e,t,r)},normalizePayload:function(e,t){return t},normalizeId:function(e){var r=t(this,"primaryKey");"id"!==r&&(e.id=e[r],delete e[r])},normalizeUsingDeclaredMapping:function(e,r){var n,i,a=t(this,"attrs");if(a)for(i in a)n=a[i],r[i]=r[n],delete r[n]},normalizeAttributes:function(e,t){var r;this.keyForAttribute&&e.eachAttribute(function(e){r=this.keyForAttribute(e),e!==r&&(t[e]=t[r],delete t[r])},this)},normalizeRelationships:function(e,t){var r;this.keyForRelationship&&e.eachRelationship(function(e,n){r=this.keyForRelationship(e,n.kind),e!==r&&(t[e]=t[r],delete t[r])},this)},extractSingle:function(t,n,i,a){i=this.normalizePayload(n,i);var o,s=n.typeKey;for(var c in i){var u=this.typeForRoot(c),d=u===s;d&&"array"!==Ember.typeOf(i[c])?o=this.normalize(n,i[c],c):(t.modelFor(u),r.call(i[c],function(r){var n=this.typeForRoot(c),i=t.modelFor(n),s=t.serializerFor(i);r=s.normalize(i,r,c);var u=d&&!a&&!o,l=d&&e(r.id)===a;u||l?o=r:t.push(n,r)},this))}return o},extractArray:function(e,t,r){r=this.normalizePayload(t,r);var i,a=t.typeKey;for(var o in r){var s=o,c=!1;"_"===o.charAt(0)&&(c=!0,s=o.substr(1));var u=this.typeForRoot(s),d=e.modelFor(u),l=e.serializerFor(d),h=!c&&u===a,f=n.call(r[o],function(e){return l.normalize(d,e,o)},this);h?i=f:e.pushMany(u,f)}return i},pushPayload:function(e,t){t=this.normalizePayload(null,t);for(var r in t){var i=this.typeForRoot(r),a=e.modelFor(i),o=n.call(t[r],function(e){return this.normalize(a,e,r)},this);e.pushMany(i,o)}},typeForRoot:function(e){return Ember.String.singularize(e)},serialize:function(){return this._super.apply(this,arguments)},serializeIntoHash:function(e,t,r,n){e[t.typeKey]=this.serialize(r,n)},serializePolymorphicType:function(e,r,n){var i=n.key,a=t(e,i);i=this.keyForAttribute?this.keyForAttribute(i):i,r[i+"Type"]=a.constructor.typeKey}})}(),function(){var e=Ember.get;Ember.set;var t=Ember.ArrayPolyfills.forEach;DS.RESTAdapter=DS.Adapter.extend({defaultSerializer:"_rest",find:function(e,t,r){return this.ajax(this.buildURL(t.typeKey,r),"GET")},findAll:function(e,t,r){var n;return r&&(n={since:r}),this.ajax(this.buildURL(t.typeKey),"GET",{data:n})},findQuery:function(e,t,r){return this.ajax(this.buildURL(t.typeKey),"GET",{data:r})},findMany:function(e,t,r){return this.ajax(this.buildURL(t.typeKey),"GET",{data:{ids:r}})},findHasMany:function(t,r,n){var i=e(this,"host"),a=e(r,"id"),o=r.constructor.typeKey;return i&&"/"===n.charAt(0)&&"/"!==n.charAt(1)&&(n=i+n),this.ajax(this.urlPrefix(n,this.buildURL(o,a)),"GET")},findBelongsTo:function(t,r,n){var i=e(r,"id"),a=r.constructor.typeKey;return this.ajax(this.urlPrefix(n,this.buildURL(a,i)),"GET")},createRecord:function(e,t,r){var n={},i=e.serializerFor(t.typeKey);return i.serializeIntoHash(n,t,r,{includeId:!0}),this.ajax(this.buildURL(t.typeKey),"POST",{data:n})},updateRecord:function(t,r,n){var i={},a=t.serializerFor(r.typeKey);a.serializeIntoHash(i,r,n);var o=e(n,"id");return this.ajax(this.buildURL(r.typeKey,o),"PUT",{data:i})},deleteRecord:function(t,r,n){var i=e(n,"id");return this.ajax(this.buildURL(r.typeKey,i),"DELETE")},buildURL:function(t,r){var n=[],i=e(this,"host"),a=this.urlPrefix();return t&&n.push(this.pathForType(t)),r&&n.push(r),a&&n.unshift(a),n=n.join("/"),!i&&n&&(n="/"+n),n},urlPrefix:function(t,r){var n=e(this,"host"),i=e(this,"namespace"),a=[];return t?"/"===t.charAt(0)?n&&(t=t.slice(1),a.push(n)):/^http(s)?:\/\//.test(t)||a.push(r):(n&&a.push(n),i&&a.push(i)),t&&a.push(t),a.join("/")},pathForType:function(e){return Ember.String.pluralize(e)},ajaxError:function(e){return e&&(e.then=null),e},ajax:function(e,t,r){var n=this;return new Ember.RSVP.Promise(function(i,a){r=n.ajaxOptions(e,t,r),r.success=function(e){Ember.run(null,i,e)},r.error=function(e){Ember.run(null,a,n.ajaxError(e))},Ember.$.ajax(r)})},ajaxOptions:function(e,r,n){if(n=n||{},n.url=e,n.type=r,n.dataType="json",n.context=this,n.data&&"GET"!==r&&(n.contentType="application/json; charset=utf-8",n.data=JSON.stringify(n.data)),void 0!==this.headers){var i=this.headers;n.beforeSend=function(e){t.call(Ember.keys(i),function(t){e.setRequestHeader(t,i[t])})}}return n}})}(),function(){DS.Model.reopen({_debugInfo:function(){var e=["id"],t={belongsTo:[],hasMany:[]},r=[];this.eachAttribute(function(t){e.push(t)},this),this.eachRelationship(function(e,n){t[n.kind].push(e),r.push(e)});var n=[{name:"Attributes",properties:e,expand:!0},{name:"Belongs To",properties:t.belongsTo,expand:!0},{name:"Has Many",properties:t.hasMany,expand:!0},{name:"Flags",properties:["isLoaded","isDirty","isSaving","isDeleted","isError","isNew","isValid"]}];return{propertyInfo:{includeOtherProperties:!0,groups:n,expensiveProperties:r}}}})}(),function(){Ember.String.pluralize=function(e){return Ember.Inflector.inflector.pluralize(e)},Ember.String.singularize=function(e){return Ember.Inflector.inflector.singularize(e)}}(),function(){function e(e,t){for(var r=0,n=t.length;n>r;r++)e.uncountable[t[r]]=!0}function t(e,t){for(var r,n=0,i=t.length;i>n;n++)r=t[n],e.irregular[r[0]]=r[1],e.irregularInverse[r[1]]=r[0]}function r(r){r=r||{},r.uncountable=r.uncountable||{},r.irregularPairs=r.irregularPairs||{};var n=this.rules={plurals:r.plurals||[],singular:r.singular||[],irregular:{},irregularInverse:{},uncountable:{}};e(n,r.uncountable),t(n,r.irregularPairs)}var n=/^\s*$/;r.prototype={plural:function(e,t){this.rules.plurals.push([e,t])},singular:function(e,t){this.rules.singular.push([e,t])},uncountable:function(t){e(this.rules,[t])},irregular:function(e,r){t(this.rules,[[e,r]])},pluralize:function(e){return this.inflect(e,this.rules.plurals,this.rules.irregular)},singularize:function(e){return this.inflect(e,this.rules.singular,this.rules.irregularInverse)},inflect:function(e,t,r){var i,a,o,s,c,u,d,l;if(c=n.test(e))return e;if(s=e.toLowerCase(),u=this.rules.uncountable[s])return e;if(d=r&&r[s])return d;for(var h=t.length,f=0;h>f&&(i=t[h-1],l=i[0],!l.test(e));h--);return i=i||[],l=i[0],a=i[1],o=e.replace(l,a)}},Ember.Inflector=r}(),function(){Ember.Inflector.defaultRules={plurals:[[/$/,"s"],[/s$/i,"s"],[/^(ax|test)is$/i,"$1es"],[/(octop|vir)us$/i,"$1i"],[/(octop|vir)i$/i,"$1i"],[/(alias|status)$/i,"$1es"],[/(bu)s$/i,"$1ses"],[/(buffal|tomat)o$/i,"$1oes"],[/([ti])um$/i,"$1a"],[/([ti])a$/i,"$1a"],[/sis$/i,"ses"],[/(?:([^f])fe|([lr])f)$/i,"$1$2ves"],[/(hive)$/i,"$1s"],[/([^aeiouy]|qu)y$/i,"$1ies"],[/(x|ch|ss|sh)$/i,"$1es"],[/(matr|vert|ind)(?:ix|ex)$/i,"$1ices"],[/^(m|l)ouse$/i,"$1ice"],[/^(m|l)ice$/i,"$1ice"],[/^(ox)$/i,"$1en"],[/^(oxen)$/i,"$1"],[/(quiz)$/i,"$1zes"]],singular:[[/s$/i,""],[/(ss)$/i,"$1"],[/(n)ews$/i,"$1ews"],[/([ti])a$/i,"$1um"],[/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i,"$1sis"],[/(^analy)(sis|ses)$/i,"$1sis"],[/([^f])ves$/i,"$1fe"],[/(hive)s$/i,"$1"],[/(tive)s$/i,"$1"],[/([lr])ves$/i,"$1f"],[/([^aeiouy]|qu)ies$/i,"$1y"],[/(s)eries$/i,"$1eries"],[/(m)ovies$/i,"$1ovie"],[/(x|ch|ss|sh)es$/i,"$1"],[/^(m|l)ice$/i,"$1ouse"],[/(bus)(es)?$/i,"$1"],[/(o)es$/i,"$1"],[/(shoe)s$/i,"$1"],[/(cris|test)(is|es)$/i,"$1is"],[/^(a)x[ie]s$/i,"$1xis"],[/(octop|vir)(us|i)$/i,"$1us"],[/(alias|status)(es)?$/i,"$1"],[/^(ox)en/i,"$1"],[/(vert|ind)ices$/i,"$1ex"],[/(matr)ices$/i,"$1ix"],[/(quiz)zes$/i,"$1"],[/(database)s$/i,"$1"]],irregularPairs:[["person","people"],["man","men"],["child","children"],["sex","sexes"],["move","moves"],["cow","kine"],["zombie","zombies"]],uncountable:["equipment","information","rice","money","species","series","fish","sheep","jeans","police"]}}(),function(){(Ember.EXTEND_PROTOTYPES===!0||Ember.EXTEND_PROTOTYPES.String)&&(String.prototype.pluralize=function(){return Ember.String.pluralize(this)},String.prototype.singularize=function(){return Ember.String.singularize(this)})}(),function(){Ember.Inflector.inflector=new Ember.Inflector(Ember.Inflector.defaultRules)}(),function(){function e(n,i,a,o,s){var c=t(i,"attrs");c&&a.eachRelationship(function(i,a){var u,d,l,h,f=c[i],p=n.serializerFor(a.type.typeKey),m=t(p,"primaryKey");if("hasMany"===a.kind&&f&&("always"===f.embedded||"load"===f.embedded)){if(d="_"+Ember.String.pluralize(a.type.typeKey),u=this.keyForRelationship(i,a.kind),l=this.keyForAttribute(i),h=[],!o[l])return;s[d]=s[d]||[],r(o[l],function(t){var r=n.modelFor(a.type.typeKey);e(n,p,r,t,s),h.push(t[m]),s[d].push(t)}),o[u]=h,delete o[l]}},i)}var t=Ember.get,r=Ember.EnumerableUtils.forEach;DS.ActiveModelSerializer=DS.RESTSerializer.extend({keyForAttribute:function(e){return Ember.String.decamelize(e)},keyForRelationship:function(e,t){return e=Ember.String.decamelize(e),"belongsTo"===t?e+"_id":"hasMany"===t?Ember.String.singularize(e)+"_ids":e},serializeHasMany:function(e,r,n){var i=n.key,a=t(this,"attrs"),o=a&&a[i]&&"always"===a[i].embedded;o&&(r[this.keyForAttribute(i)]=t(e,i).map(function(e){var r=e.serialize(),n=t(this,"primaryKey");return r[n]=t(e,n),r},this))},serializeIntoHash:function(e,t,r,n){var i=Ember.String.decamelize(t.typeKey);e[i]=this.serialize(r,n)},serializePolymorphicType:function(e,r,n){var i=n.key,a=t(e,i);i=this.keyForAttribute(i),r[i+"_type"]=Ember.String.capitalize(a.constructor.typeKey)},typeForRoot:function(e){var t=Ember.String.camelize(e);return Ember.String.singularize(t)},normalizeRelationships:function(e,t){var n,i;this.keyForRelationship&&e.eachRelationship(function(e,a){if(a.options.polymorphic){if(n=this.keyForAttribute(e),i=t[n],i&&i.type)i.type=this.typeForRoot(i.type);else if(i&&"hasMany"===a.kind){var o=this;r(i,function(e){e.type=o.typeForRoot(e.type)})}}else n=this.keyForRelationship(e,a.kind),i=t[n];t[e]=i,e!==n&&delete t[n]},this)},extractSingle:function(t,r,n,i,a){var o=this.keyForAttribute(r.typeKey),s=n[o];return e(t,this,r,s,n),this._super(t,r,n,i,a)},extractArray:function(t,n,i){var a=this.keyForAttribute(n.typeKey),o=i[Ember.String.pluralize(a)];return r(o,function(r){e(t,this,n,r,i)},this),this._super(t,n,i)}})}(),function(){var e=Ember.EnumerableUtils.forEach;DS.ActiveModelAdapter=DS.RESTAdapter.extend({defaultSerializer:"_ams",pathForType:function(e){var t=Ember.String.decamelize(e);return Ember.String.pluralize(t)},ajaxError:function(t){var r=this._super(t);if(t&&422===t.status){var n=Ember.$.parseJSON(t.responseText).errors,i={};return e(Ember.keys(n),function(e){i[Ember.String.camelize(e)]=n[e]}),new DS.InvalidError(i)}return r}})}(),function(){Ember.onLoad("Ember.Application",function(e){e.initializer({name:"activeModelAdapter",initialize:function(e,t){t.register("serializer:_ams",DS.ActiveModelSerializer),t.register("adapter:_ams",DS.ActiveModelAdapter)}})})}()}(),"undefined"==typeof location||"localhost"!==location.hostname&&"127.0.0.1"!==location.hostname||Ember.Logger.warn("You are running a production build of Ember on localhost and won't receive detailed error messages. If you want full error messages please use the non-minified build provided on the Ember website.");
11
+ /*!
12
+ * @overview Ember Data
13
+ * @copyright Copyright 2011-2013 Tilde Inc. and contributors.
14
+ * Portions Copyright 2011 LivingSocial Inc.
15
+ * @license Licensed under MIT license (see license.js)
16
+ */
17
+ !function(){var e,t;!function(){var r={},i={};e=function(e,t,i){r[e]={deps:t,callback:i}},t=function(e){if(i[e])return i[e];i[e]={};var n,a,o,s,c;if(n=r[e],!n)throw new Error("Module '"+e+"' not found.");a=n.deps,o=n.callback,s=[];for(var d=0,u=a.length;u>d;d++)"exports"===a[d]?s.push(c={}):s.push(t(a[d]));var l=o.apply(this,s);return i[e]=c||l}}(),function(){var e;"undefined"==typeof e&&(e=Ember.Namespace.create({VERSION:"1.0.0-beta.4"}),"undefined"!=typeof window&&(window.DS=e),Ember.libraries&&Ember.libraries.registerCoreLibrary("Ember Data",e.VERSION))}(),function(){function e(e){return function(){return this[e].apply(this,arguments)}}var t=Ember.get,r=(Ember.set,Ember.isNone);DS.JSONSerializer=Ember.Object.extend({primaryKey:"id",applyTransforms:function(e,t){return e.eachTransformedAttribute(function(e,r){var i=this.transformFor(r);t[e]=i.deserialize(t[e])},this),t},normalize:function(e,t){return t?(this.applyTransforms(e,t),t):t},serialize:function(e,r){var i={};if(r&&r.includeId){var n=t(e,"id");n&&(i[t(this,"primaryKey")]=t(e,"id"))}return e.eachAttribute(function(t,r){this.serializeAttribute(e,i,t,r)},this),e.eachRelationship(function(t,r){"belongsTo"===r.kind?this.serializeBelongsTo(e,i,r):"hasMany"===r.kind&&this.serializeHasMany(e,i,r)},this),i},serializeAttribute:function(e,r,i,n){var a=t(this,"attrs"),o=t(e,i),s=n.type;if(s){var c=this.transformFor(s);o=c.serialize(o)}i=a&&a[i]||(this.keyForAttribute?this.keyForAttribute(i):i),r[i]=o},serializeBelongsTo:function(e,i,n){var a=n.key,o=t(e,a);a=this.keyForRelationship?this.keyForRelationship(a,"belongsTo"):a,i[a]=r(o)?o:t(o,"id"),n.options.polymorphic&&this.serializePolymorphicType(e,i,n)},serializeHasMany:function(e,r,i){var n=i.key,a=DS.RelationshipChange.determineRelationshipType(e.constructor,i);("manyToNone"===a||"manyToMany"===a)&&(r[n]=t(e,n).mapBy("id"))},serializePolymorphicType:Ember.K,extract:function(e,t,r,i,n){this.extractMeta(e,t,r);var a="extract"+n.charAt(0).toUpperCase()+n.substr(1);return this[a](e,t,r,i,n)},extractFindAll:e("extractArray"),extractFindQuery:e("extractArray"),extractFindMany:e("extractArray"),extractFindHasMany:e("extractArray"),extractCreateRecord:e("extractSave"),extractUpdateRecord:e("extractSave"),extractDeleteRecord:e("extractSave"),extractFind:e("extractSingle"),extractFindBelongsTo:e("extractSingle"),extractSave:e("extractSingle"),extractSingle:function(e,t,r){return this.normalize(t,r)},extractArray:function(e,t,r){return this.normalize(t,r)},extractMeta:function(e,t,r){r&&r.meta&&(e.metaForType(t,r.meta),delete r.meta)},transformFor:function(e){var t=this.container.lookup("transform:"+e);return t}})}(),function(){var e=Ember.get,t=Ember.String.capitalize,r=Ember.String.underscore,i=window.DS;i.DebugAdapter=Ember.DataAdapter.extend({getFilters:function(){return[{name:"isNew",desc:"New"},{name:"isModified",desc:"Modified"},{name:"isClean",desc:"Clean"}]},detect:function(e){return e!==i.Model&&i.Model.detect(e)},columnsForType:function(i){var n=[{name:"id",desc:"Id"}],a=0,o=this;return e(i,"attributes").forEach(function(e){if(a++>o.attributeLimit)return!1;var i=t(r(e).replace("_"," "));n.push({name:e,desc:i})}),n},getRecords:function(e){return this.get("store").all(e)},getRecordColumnValues:function(t){var r=this,i=0,n={id:e(t,"id")};return t.eachAttribute(function(a){if(i++>r.attributeLimit)return!1;var o=e(t,a);n[a]=o}),n},getRecordKeywords:function(t){var r=[],i=Ember.A(["id"]);return t.eachAttribute(function(e){i.push(e)}),i.forEach(function(i){r.push(e(t,i))}),r},getRecordFilterValues:function(e){return{isNew:e.get("isNew"),isModified:e.get("isDirty")&&!e.get("isNew"),isClean:!e.get("isDirty")}},getRecordColor:function(e){var t="black";return e.get("isNew")?t="green":e.get("isDirty")&&(t="blue"),t},observeRecord:function(e,t){var r=Ember.A(),i=this,n=Ember.A(["id","isNew","isDirty"]);e.eachAttribute(function(e){n.push(e)}),n.forEach(function(n){var a=function(){t(i.wrapRecord(e))};Ember.addObserver(e,n,a),r.push(function(){Ember.removeObserver(e,n,a)})});var a=function(){r.forEach(function(e){e()})};return a}})}(),function(){DS.Transform=Ember.Object.extend({serialize:Ember.required(),deserialize:Ember.required()})}(),function(){DS.BooleanTransform=DS.Transform.extend({deserialize:function(e){var t=typeof e;return"boolean"===t?e:"string"===t?null!==e.match(/^true$|^t$|^1$/i):"number"===t?1===e:!1},serialize:function(e){return Boolean(e)}})}(),function(){DS.DateTransform=DS.Transform.extend({deserialize:function(e){var t=typeof e;return"string"===t?new Date(Ember.Date.parse(e)):"number"===t?new Date(e):null===e||void 0===e?e:null},serialize:function(e){if(e instanceof Date){var t=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],r=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],i=function(e){return 10>e?"0"+e:""+e},n=e.getUTCFullYear(),a=e.getUTCMonth(),o=e.getUTCDate(),s=e.getUTCDay(),c=e.getUTCHours(),d=e.getUTCMinutes(),u=e.getUTCSeconds(),l=t[s],h=i(o),f=r[a];return l+", "+h+" "+f+" "+n+" "+i(c)+":"+i(d)+":"+i(u)+" GMT"}return null}})}(),function(){var e=Ember.isEmpty;DS.NumberTransform=DS.Transform.extend({deserialize:function(t){return e(t)?null:Number(t)},serialize:function(t){return e(t)?null:Number(t)}})}(),function(){var e=Ember.isNone;DS.StringTransform=DS.Transform.extend({deserialize:function(t){return e(t)?null:String(t)},serialize:function(t){return e(t)?null:String(t)}})}(),function(){Ember.set;Ember.onLoad("Ember.Application",function(e){e.initializer({name:"store",initialize:function(e,t){t.register("store:main",t.Store||DS.Store),t.register("serializer:_default",DS.JSONSerializer),t.register("serializer:_rest",DS.RESTSerializer),t.register("adapter:_rest",DS.RESTAdapter),e.lookup("store:main")}}),e.initializer({name:"transforms",before:"store",initialize:function(e,t){t.register("transform:boolean",DS.BooleanTransform),t.register("transform:date",DS.DateTransform),t.register("transform:number",DS.NumberTransform),t.register("transform:string",DS.StringTransform)}}),e.initializer({name:"dataAdapter",before:"store",initialize:function(e,t){t.register("dataAdapter:main",DS.DebugAdapter)}}),e.initializer({name:"injectStore",before:"store",initialize:function(e,t){t.inject("controller","store","store:main"),t.inject("route","store","store:main"),t.inject("serializer","store","store:main"),t.inject("dataAdapter","store","store:main")}})})}(),function(){Ember.Date=Ember.Date||{};var e=Date.parse,t=[1,4,5,6,7,10,11];Ember.Date.parse=function(r){var i,n,a=0;if(n=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(r)){for(var o,s=0;o=t[s];++s)n[o]=+n[o]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,"Z"!==n[8]&&void 0!==n[9]&&(a=60*n[10]+n[11],"+"===n[9]&&(a=0-a)),i=Date.UTC(n[1],n[2],n[3],n[4],n[5]+a,n[6],n[7])}else i=e?e(r):0/0;return i},(Ember.EXTEND_PROTOTYPES===!0||Ember.EXTEND_PROTOTYPES.Date)&&(Date.parse=Ember.Date.parse)}(),function(){{var e=Ember.get;Ember.set}DS.RecordArray=Ember.ArrayProxy.extend(Ember.Evented,{type:null,content:null,isLoaded:!1,isUpdating:!1,store:null,objectAtContent:function(t){var r=e(this,"content");return r.objectAt(t)},update:function(){if(!e(this,"isUpdating")){var t=e(this,"store"),r=e(this,"type");t.fetchAll(r,this)}},addRecord:function(t){e(this,"content").addObject(t)},removeRecord:function(t){e(this,"content").removeObject(t)},save:function(){var t="DS: RecordArray#save "+e(this,"type"),r=Ember.RSVP.all(this.invoke("save"),t).then(function(e){return Ember.A(e)},null,"DS: RecordArray#save apply Ember.NativeArray");return DS.PromiseArray.create({promise:r})}})}(),function(){var e=Ember.get;DS.FilteredRecordArray=DS.RecordArray.extend({filterFunction:null,isLoaded:!0,replace:function(){var t=e(this,"type").toString();throw new Error("The result of a client-side filter (on "+t+") is immutable.")},updateFilter:Ember.observer(function(){var t=e(this,"manager");t.updateFilter(this,e(this,"type"),e(this,"filterFunction"))},"filterFunction")})}(),function(){{var e=Ember.get;Ember.set}DS.AdapterPopulatedRecordArray=DS.RecordArray.extend({query:null,replace:function(){var t=e(this,"type").toString();throw new Error("The result of a server query (on "+t+") is immutable.")},load:function(t){var r=e(this,"store"),i=e(this,"type"),n=r.pushMany(i,t),a=r.metadataFor(i);this.setProperties({content:Ember.A(n),isLoaded:!0,meta:a}),Ember.run.once(this,"trigger","didLoad")}})}(),function(){var e=Ember.get,t=Ember.set,r=Ember.EnumerableUtils.map;DS.ManyArray=DS.RecordArray.extend({init:function(){this._super.apply(this,arguments),this._changesToSync=Ember.OrderedSet.create()},name:null,owner:null,isPolymorphic:!1,isLoaded:!1,promise:null,loadingRecordsCount:function(e){this.loadingRecordsCount=e},loadedRecord:function(){this.loadingRecordsCount--,0===this.loadingRecordsCount&&(t(this,"isLoaded",!0),this.trigger("didLoad"))},fetch:function(){var t=e(this,"content"),r=e(this,"store"),i=e(this,"owner"),n=Ember.RSVP.defer("DS: ManyArray#fetch "+e(this,"type")),a=t.filterProperty("isEmpty",!0);r.fetchMany(a,i,n)},replaceContent:function(e,t,i){i=r(i,function(e){return e},this),this._super(e,t,i)},arrangedContentDidChange:function(){Ember.run.once(this,"fetch")},arrayContentWillChange:function(t,r){var i=e(this,"owner"),n=e(this,"name");if(!i._suspendedRelationships)for(var a=t;t+r>a;a++){var o=e(this,"content").objectAt(a),s=DS.RelationshipChange.createChange(i,o,e(this,"store"),{parentType:i.constructor,changeType:"remove",kind:"hasMany",key:n});this._changesToSync.add(s)}return this._super.apply(this,arguments)},arrayContentDidChange:function(t,r,i){this._super.apply(this,arguments);var n=e(this,"owner"),a=e(this,"name"),o=e(this,"store");if(!n._suspendedRelationships){for(var s=t;t+i>s;s++){var c=e(this,"content").objectAt(s),d=DS.RelationshipChange.createChange(n,c,o,{parentType:n.constructor,changeType:"add",kind:"hasMany",key:a});d.hasManyName=a,this._changesToSync.add(d)}this._changesToSync.forEach(function(e){e.sync()}),this._changesToSync.clear()}},createRecord:function(t){var r,i=e(this,"owner"),n=e(i,"store"),a=e(this,"type");return r=n.createRecord.call(n,a,t),this.pushObject(r),r}})}(),function(){function e(e,r,a,o){return r.eachRelationship(function(r,s){if(a.links&&a.links[r])return o&&s.options.async&&(o._relationships[r]=null),void 0;var c=s.kind,d=a[r];null!=d&&("belongsTo"===c?t(e,a,r,s,d):"hasMany"===c&&(i(e,a,r,s,d),n(o,r,d)))}),a}function t(e,t,i,n,a){if(!(R(a)||a instanceof DS.Model)){var o;"number"==typeof a||"string"==typeof a?(o=r(n,i,t),t[i]=e.recordForId(o,a)):"object"==typeof a&&(t[i]=e.recordForId(a.type,a.id))}}function r(e,t,r){return e.options.polymorphic?r[t+"Type"]:e.type}function i(e,r,i,n,a){for(var o=0,s=a.length;s>o;o++)t(e,a,o,n,a[o])}function n(e,t,r){e&&r.pushObjects(e.get(t).filterBy("isNew"))}function a(e){return DS.PromiseObject.create({promise:e})}function o(e){return DS.PromiseArray.create({promise:e})}function s(e,t,r){return e.lookup("serializer:"+t)||e.lookup("serializer:application")||e.lookup("serializer:"+r)||e.lookup("serializer:_default")}function c(e){return e.lookup("serializer:application")||e.lookup("serializer:_default")}function d(e,t){var r=e.serializer,i=e.defaultSerializer,n=e.container;return n&&void 0===r&&(r=s(n,t.typeKey,i)),(null===r||void 0===r)&&(r={extract:function(e,t,r){return r}}),r}function u(e,t,r,i){var n=e.find(t,r,i),a=d(e,r);return T(n,"DS: Handle Adapter#find of "+r+" with id: "+i).then(function(e){return e=a.extract(t,r,e,i,"find"),t.push(r,e)},function(e){var n=t.getById(r,i);throw n.notFound(),e},"DS: Extract payload of '"+r+"'")}function l(e,t,r,i,n){var a=e.findMany(t,r,i,n),o=d(e,r);return T(a,"DS: Handle Adapter#findMany of "+r).then(function(e){e=o.extract(t,r,e,null,"findMany"),t.pushMany(r,e)},null,"DS: Extract payload of "+r)}function h(e,t,r,i,n){var a=e.findHasMany(t,r,i,n),o=d(e,n.type);return T(a,"DS: Handle Adapter#findHasMany of "+r+" : "+n.type).then(function(e){e=o.extract(t,n.type,e,null,"findHasMany");var i=t.pushMany(n.type,e);r.updateHasMany(n.key,i)},null,"DS: Extract payload of "+r+" : hasMany "+n.type)}function f(e,t,r,i,n){var a=e.findBelongsTo(t,r,i,n),o=d(e,n.type);return T(a,"DS: Handle Adapter#findBelongsTo of "+r+" : "+n.type).then(function(e){e=o.extract(t,n.type,e,null,"findBelongsTo");var r=t.push(n.type,e);return r.updateBelongsTo(n.key,r),r},null,"DS: Extract payload of "+r+" : "+n.type)}function p(e,t,r,i){var n=e.findAll(t,r,i),a=d(e,r);return T(n,"DS: Handle Adapter#findAll of "+r).then(function(e){return e=a.extract(t,r,e,null,"findAll"),t.pushMany(r,e),t.didUpdateAll(r),t.all(r)},null,"DS: Extract payload of findAll "+r)}function m(e,t,r,i,n){var a=e.findQuery(t,r,i,n),o=d(e,r);return T(a,"DS: Handle Adapter#findQuery of "+r).then(function(e){return e=o.extract(t,r,e,null,"findAll"),n.load(e),n},null,"DS: Extract payload of findQuery "+r)}function y(e,t,r,i){var n=i.constructor,a=e[r](t,n,i),o=d(e,n);return a.then(function(e){return e&&(e=o.extract(t,n,e,g(i,"id"),r)),t.didSaveRecord(i,e),i},function(e){throw e instanceof DS.InvalidError?t.recordWasInvalid(i,e.errors):t.recordWasError(i,e),e},"DS: Extract and notify about "+r+" completion of "+i)}var g=Ember.get,b=Ember.set,v=Ember.run.once,R=Ember.isNone,E=Ember.EnumerableUtils.forEach,S=Ember.EnumerableUtils.indexOf,D=Ember.EnumerableUtils.map,T=Ember.RSVP.resolve,A=Ember.copy,F=function(e){return null==e?null:e+""};DS.Store=Ember.Object.extend({init:function(){this.typeMaps={},this.recordArrayManager=DS.RecordArrayManager.create({store:this}),this._relationshipChanges={},this._pendingSave=[]},adapter:"_rest",serialize:function(e,t){return this.serializerFor(e.constructor.typeKey).serialize(e,t)},defaultAdapter:Ember.computed("adapter",function(){var e=g(this,"adapter");return"string"==typeof e&&(e=this.container.lookup("adapter:"+e)||this.container.lookup("adapter:application")||this.container.lookup("adapter:_rest")),DS.Adapter.detect(e)&&(e=e.create({container:this.container})),e}),createRecord:function(e,t){e=this.modelFor(e),t=A(t)||{},R(t.id)&&(t.id=this._generateId(e)),t.id=F(t.id);var r=this.buildRecord(e,t.id);return r.loadedData(),r.setProperties(t),r},_generateId:function(e){var t=this.adapterFor(e);return t&&t.generateIdForRecord?t.generateIdForRecord(this):null},deleteRecord:function(e){e.deleteRecord()},unloadRecord:function(e){e.unloadRecord()},find:function(e,t){return void 0===t?this.findAll(e):"object"===Ember.typeOf(t)?this.findQuery(e,t):this.findById(e,F(t))},findById:function(e,t){e=this.modelFor(e);var r=this.recordForId(e,t),i=this.fetchRecord(r)||T(r,"DS: Store#findById "+e+" with id: "+t);return a(i)},findByIds:function(e,t){var r=this;return o(Ember.RSVP.all(D(t,function(t){return r.findById(e,t)})).then(Ember.A,null,"DS: Store#findByIds of "+e+" complete"))},fetchRecord:function(e){if(R(e))return null;if(e._loadingPromise)return e._loadingPromise;if(!g(e,"isEmpty"))return null;var t=e.constructor,r=g(e,"id"),i=Ember.RSVP.defer("DS: Store#fetchRecord "+e);e.loadingData(i.promise);var n=this.adapterFor(t);return i.resolve(u(n,this,t,r)),i.promise},getById:function(e,t){return this.hasRecordForId(e,t)?this.recordForId(e,t):null},reloadRecord:function(e){var t=e.constructor,r=this.adapterFor(t),i=g(e,"id");return u(r,this,t,i)},fetchMany:function(e,t,r){if(e.length){var i=Ember.MapWithDefault.create({defaultValue:function(){return Ember.A()}});E(e,function(e){i.get(e.constructor).push(e)}),E(i,function(e,i){var n=i.mapProperty("id"),a=this.adapterFor(e);r.resolve(l(a,this,e,n,t))},this)}},hasRecordForId:function(e,t){return t=F(t),e=this.modelFor(e),!!this.typeMapFor(e).idToRecord[t]},recordForId:function(e,t){e=this.modelFor(e),t=F(t);var r=this.typeMapFor(e).idToRecord[t];return r||(r=this.buildRecord(e,t)),r},findMany:function(e,t,r,i){r=this.modelFor(r),t=Ember.A(t);var n=t.filterProperty("isEmpty",!0),a=this.recordArrayManager.createManyArray(r,t);return E(n,function(e){e.loadingData()}),a.loadingRecordsCount=n.length,n.length?(E(n,function(e){this.recordArrayManager.registerWaitingRecordArray(e,a)},this),this.fetchMany(n,e,i)):(i&&i.resolve(),a.set("isLoaded",!0),Ember.run.once(a,"trigger","didLoad")),a},findHasMany:function(e,t,r,i){var n=this.adapterFor(e.constructor),a=this.recordArrayManager.createManyArray(r.type,Ember.A([]));return i.resolve(h(n,this,e,t,r)),a},findBelongsTo:function(e,t,r,i){var n=this.adapterFor(e.constructor);i.resolve(f(n,this,e,t,r))},findQuery:function(e,t){e=this.modelFor(e);var r=DS.AdapterPopulatedRecordArray.create({type:e,query:t,content:Ember.A(),store:this}),i=this.adapterFor(e),n="DS: Store#findQuery "+e,a=Ember.RSVP.defer(n);return a.resolve(m(i,this,e,t,r)),o(a.promise)},findAll:function(e){return e=this.modelFor(e),this.fetchAll(e,this.all(e))},fetchAll:function(e,t){var r=this.adapterFor(e),i=this.typeMapFor(e).metadata.since,n=Ember.RSVP.defer("DS: Store#findAll "+e);return b(t,"isUpdating",!0),n.resolve(p(r,this,e,i)),o(n.promise)},didUpdateAll:function(e){var t=this.typeMapFor(e).findAllCache;b(t,"isUpdating",!1)},all:function(e){e=this.modelFor(e);var t=this.typeMapFor(e),r=t.findAllCache;if(r)return r;var i=DS.RecordArray.create({type:e,content:Ember.A(),store:this,isLoaded:!0});return this.recordArrayManager.registerFilteredRecordArray(i,e),t.findAllCache=i,i},unloadAll:function(e){e=this.modelFor(e);for(var t,r=this.typeMapFor(e),i=r.records;t=i.pop();)t.unloadRecord();r.findAllCache=null},filter:function(e,t,r){var i;3===arguments.length?i=this.findQuery(e,t):2===arguments.length&&(r=t),e=this.modelFor(e);var n=DS.FilteredRecordArray.create({type:e,content:Ember.A(),store:this,manager:this.recordArrayManager,filterFunction:r});return this.recordArrayManager.registerFilteredRecordArray(n,e,r),i?i.then(function(){return n},null,"DS: Store#filter of "+e):n},recordIsLoaded:function(e,t){return this.hasRecordForId(e,t)?!g(this.recordForId(e,t),"isEmpty"):!1},metadataFor:function(e){return e=this.modelFor(e),this.typeMapFor(e).metadata},dataWasUpdated:function(e,t){this.recordArrayManager.recordDidChange(t)},scheduleSave:function(e,t){e.adapterWillCommit(),this._pendingSave.push([e,t]),v(this,"flushPendingSave")},flushPendingSave:function(){var e=this._pendingSave.slice();this._pendingSave=[],E(e,function(e){var t,r=e[0],i=e[1],n=this.adapterFor(r.constructor);t=g(r,"isNew")?"createRecord":g(r,"isDeleted")?"deleteRecord":"updateRecord",i.resolve(y(n,this,t,r))},this)},didSaveRecord:function(t,r){r&&(r=e(this,t.constructor,r,t),this.updateId(t,r)),t.adapterDidCommit(r)},recordWasInvalid:function(e,t){e.adapterDidInvalidate(t)},recordWasError:function(e){e.adapterDidError()},updateId:function(e,t){var r=(g(e,"id"),F(t.id));this.typeMapFor(e.constructor).idToRecord[r]=e,b(e,"id",r)},typeMapFor:function(e){var t,r=g(this,"typeMaps"),i=Ember.guidFor(e);return(t=r[i])?t:(t={idToRecord:{},records:[],metadata:{}},r[i]=t,t)},_load:function(e,t,r){var i=F(t.id),n=this.recordForId(e,i);return n.setupData(t,r),this.recordArrayManager.recordDidChange(n),n},modelFor:function(e){var t;if("string"==typeof e){if(t=this.container.lookupFactory("model:"+e),!t)throw new Ember.Error("No model was found for '"+e+"'");t.typeKey=e}else t=e;return t.store=this,t},push:function(t,r,i){return t=this.modelFor(t),r=e(this,t,r),this._load(t,r,i),this.recordForId(t,r.id)},pushPayload:function(e,t){var r;t?r=this.serializerFor(e):(t=e,r=c(this.container)),r.pushPayload(this,t)},update:function(e,t){return this.push(e,t,!0)},pushMany:function(e,t){return D(t,function(t){return this.push(e,t)},this)},metaForType:function(e,t){e=this.modelFor(e),Ember.merge(this.typeMapFor(e).metadata,t)},buildRecord:function(e,t,r){var i=this.typeMapFor(e),n=i.idToRecord,a=e._create({id:t,store:this,container:this.container});return r&&a.setupData(r),t&&(n[t]=a),i.records.push(a),a},dematerializeRecord:function(e){var t=e.constructor,r=this.typeMapFor(t),i=g(e,"id");e.updateRecordArrays(),i&&delete r.idToRecord[i];var n=S(r.records,e);r.records.splice(n,1)},addRelationshipChangeFor:function(e,t,r,i,n){var a=e.clientId,o=r?r:r,s=t+i,c=this._relationshipChanges;a in c||(c[a]={}),o in c[a]||(c[a][o]={}),s in c[a][o]||(c[a][o][s]={}),c[a][o][s][n.changeType]=n},removeRelationshipChangeFor:function(e,t,r,i,n){var a=e.clientId,o=r?r.clientId:r,s=this._relationshipChanges,c=t+i;a in s&&o in s[a]&&c in s[a][o]&&delete s[a][o][c][n]},relationshipChangePairsFor:function(e){var t=[];if(!e)return t;var r=this._relationshipChanges[e.clientId];for(var i in r)if(r.hasOwnProperty(i))for(var n in r[i])r[i].hasOwnProperty(n)&&t.push(r[i][n]);return t},adapterFor:function(e){var t,r=this.container;return r&&(t=r.lookup("adapter:"+e.typeKey)||r.lookup("adapter:application")),t||g(this,"defaultAdapter")},serializerFor:function(e){e=this.modelFor(e);var t=this.adapterFor(e);return s(this.container,e.typeKey,t&&t.defaultSerializer)}}),DS.PromiseArray=Ember.ArrayProxy.extend(Ember.PromiseProxyMixin),DS.PromiseObject=Ember.ObjectProxy.extend(Ember.PromiseProxyMixin)}(),function(){function e(t){var r,i={};for(var n in t)r=t[n],i[n]=r&&"object"==typeof r?e(r):r;return i}function t(e,t){for(var r in t)e[r]=t[r];return e}function r(r){var i=e(c);return t(i,r)}function i(e,r,n){e=t(r?Ember.create(r):{},e),e.parentState=r,e.stateName=n;for(var a in e)e.hasOwnProperty(a)&&"parentState"!==a&&"stateName"!==a&&"object"==typeof e[a]&&(e[a]=i(e[a],e,n+"."+a));return e}var n=Ember.get,a=Ember.set,o=function(e){var t,r,i,n=Ember.keys(e);for(t=0,r=n.length;r>t;t++)if(i=n[t],e.hasOwnProperty(i)&&e[i])return!0;return!1},s=function(e,t){t.value===t.originalValue?(delete e._attributes[t.name],e.send("propertyWasReset",t.name)):t.value!==t.oldValue&&e.send("becomeDirty"),e.updateRecordArraysLater()},c={initialState:"uncommitted",isDirty:!0,uncommitted:{didSetProperty:s,propertyWasReset:function(e){var t=!1;for(var r in e._attributes){t=!0;break}t||e.send("rolledBack")},pushedData:Ember.K,becomeDirty:Ember.K,willCommit:function(e){e.transitionTo("inFlight")},reloadRecord:function(e,t){t(n(e,"store").reloadRecord(e))},rolledBack:function(e){e.transitionTo("loaded.saved")},becameInvalid:function(e){e.transitionTo("invalid")},rollback:function(e){e.rollback()}},inFlight:{isSaving:!0,didSetProperty:s,becomeDirty:Ember.K,pushedData:Ember.K,willCommit:Ember.K,didCommit:function(e){var t=n(this,"dirtyType");e.transitionTo("saved"),e.send("invokeLifecycleCallbacks",t)},becameInvalid:function(e,t){a(e,"errors",t),e.transitionTo("invalid"),e.send("invokeLifecycleCallbacks")},becameError:function(e){e.transitionTo("uncommitted"),e.triggerLater("becameError",e)}},invalid:{isValid:!1,deleteRecord:function(e){e.transitionTo("deleted.uncommitted"),e.clearRelationships()},didSetProperty:function(e,t){var r=n(e,"errors"),i=t.name;a(r,i,null),o(r)||e.send("becameValid"),s(e,t)},becomeDirty:Ember.K,rollback:function(e){e.send("becameValid"),e.send("rollback")},becameValid:function(e){e.transitionTo("uncommitted")},invokeLifecycleCallbacks:function(e){e.triggerLater("becameInvalid",e)}}},d=r({dirtyType:"created",isNew:!0});d.uncommitted.rolledBack=function(e){e.transitionTo("deleted.saved")};var u=r({dirtyType:"updated"});d.uncommitted.deleteRecord=function(e){e.clearRelationships(),e.transitionTo("deleted.saved")},d.uncommitted.rollback=function(e){c.uncommitted.rollback.apply(this,arguments),e.transitionTo("deleted.saved")},u.uncommitted.deleteRecord=function(e){e.transitionTo("deleted.uncommitted"),e.clearRelationships()};var l={isEmpty:!1,isLoading:!1,isLoaded:!1,isDirty:!1,isSaving:!1,isDeleted:!1,isNew:!1,isValid:!0,rolledBack:Ember.K,propertyWasReset:Ember.K,empty:{isEmpty:!0,loadingData:function(e,t){e._loadingPromise=t,e.transitionTo("loading")},loadedData:function(e){e.transitionTo("loaded.created.uncommitted"),e.suspendRelationshipObservers(function(){e.notifyPropertyChange("data")})},pushedData:function(e){e.transitionTo("loaded.saved"),e.triggerLater("didLoad")}},loading:{isLoading:!0,exit:function(e){e._loadingPromise=null},pushedData:function(e){e.transitionTo("loaded.saved"),e.triggerLater("didLoad"),a(e,"isError",!1)},becameError:function(e){e.triggerLater("becameError",e)},notFound:function(e){e.transitionTo("empty")}},loaded:{initialState:"saved",isLoaded:!0,saved:{setup:function(e){var t=e._attributes,r=!1;for(var i in t)if(t.hasOwnProperty(i)){r=!0;break}r&&e.adapterDidDirty()},didSetProperty:s,pushedData:Ember.K,becomeDirty:function(e){e.transitionTo("updated.uncommitted")},willCommit:function(e){e.transitionTo("updated.inFlight")},reloadRecord:function(e,t){t(n(e,"store").reloadRecord(e))},deleteRecord:function(e){e.transitionTo("deleted.uncommitted"),e.clearRelationships()},unloadRecord:function(e){e.clearRelationships(),e.transitionTo("deleted.saved")},didCommit:function(e){e.send("invokeLifecycleCallbacks",n(e,"lastDirtyType"))},notFound:Ember.K},created:d,updated:u},deleted:{initialState:"uncommitted",dirtyType:"deleted",isDeleted:!0,isLoaded:!0,isDirty:!0,setup:function(e){e.updateRecordArrays()},uncommitted:{willCommit:function(e){e.transitionTo("inFlight")},rollback:function(e){e.rollback()},becomeDirty:Ember.K,deleteRecord:Ember.K,rolledBack:function(e){e.transitionTo("loaded.saved")}},inFlight:{isSaving:!0,willCommit:Ember.K,didCommit:function(e){e.transitionTo("saved"),e.send("invokeLifecycleCallbacks")},becameError:function(e){e.transitionTo("uncommitted"),e.triggerLater("becameError",e)}},saved:{isDirty:!1,setup:function(e){var t=n(e,"store");t.dematerializeRecord(e)},invokeLifecycleCallbacks:function(e){e.triggerLater("didDelete",e),e.triggerLater("didCommit",e)}}},invokeLifecycleCallbacks:function(e,t){"created"===t?e.triggerLater("didCreate",e):e.triggerLater("didUpdate",e),e.triggerLater("didCommit",e)}};l=i(l,null,"root"),DS.RootState=l}(),function(){var e=Ember.get,t=Ember.set,r=Ember.merge,i=Ember.run.once,n=Ember.computed("currentState",function(t){return e(e(this,"currentState"),t)}).readOnly();DS.Model=Ember.Object.extend(Ember.Evented,{isEmpty:n,isLoading:n,isLoaded:n,isDirty:n,isSaving:n,isDeleted:n,isNew:n,isValid:n,dirtyType:n,isError:!1,isReloading:!1,clientId:null,id:null,transaction:null,currentState:null,errors:null,serialize:function(t){var r=e(this,"store");return r.serialize(this,t)},toJSON:function(e){var t=DS.JSONSerializer.create({container:this.container});return t.serialize(this,e)},didLoad:Ember.K,didUpdate:Ember.K,didCreate:Ember.K,didDelete:Ember.K,becameInvalid:Ember.K,becameError:Ember.K,data:Ember.computed(function(){return this._data=this._data||{},this._data}).property(),_data:null,init:function(){t(this,"currentState",DS.RootState.empty),this._super(),this._setup()},_setup:function(){this._changesToSync={},this._deferredTriggers=[],this._data={},this._attributes={},this._inFlightAttributes={},this._relationships={}},send:function(t,r){var i=e(this,"currentState");return i[t]||this._unhandledEvent(i,t,r),i[t](this,r)},transitionTo:function(r){var i=r.split(".",1),n=e(this,"currentState"),a=n;do a.exit&&a.exit(this),a=a.parentState;while(!a.hasOwnProperty(i));var o,s,c=r.split("."),d=[],u=[];for(o=0,s=c.length;s>o;o++)a=a[c[o]],a.enter&&u.push(a),a.setup&&d.push(a);for(o=0,s=u.length;s>o;o++)u[o].enter(this);for(t(this,"currentState",a),o=0,s=d.length;s>o;o++)d[o].setup(this);this.updateRecordArraysLater()},_unhandledEvent:function(e,t,r){var i="Attempted to handle event `"+t+"` ";throw i+="on "+String(this)+" while in state ",i+=e.stateName+". ",void 0!==r&&(i+="Called with "+Ember.inspect(r)+"."),new Ember.Error(i)},withTransaction:function(t){var r=e(this,"transaction");r&&t(r)},loadingData:function(e){this.send("loadingData",e)},loadedData:function(){this.send("loadedData")},notFound:function(){this.send("notFound")},pushedData:function(){this.send("pushedData")},deleteRecord:function(){this.send("deleteRecord")},destroyRecord:function(){return this.deleteRecord(),this.save()},unloadRecord:function(){this.send("unloadRecord")},clearRelationships:function(){this.eachRelationship(function(e,r){if("belongsTo"===r.kind)t(this,e,null);else if("hasMany"===r.kind){var i=this._relationships[r.name];i&&i.clear()}},this)},updateRecordArrays:function(){e(this,"store").dataWasUpdated(this.constructor,this)},changedAttributes:function(){var t,r=e(this,"_data"),i=e(this,"_attributes"),n={};for(t in i)n[t]=[r[t],i[t]];return n},adapterWillCommit:function(){this.send("willCommit")},adapterDidCommit:function(e){t(this,"isError",!1),e?this._data=e:Ember.mixin(this._data,this._inFlightAttributes),this._inFlightAttributes={},this.send("didCommit"),this.updateRecordArraysLater(),e&&this.suspendRelationshipObservers(function(){this.notifyPropertyChange("data")})},adapterDidDirty:function(){this.send("becomeDirty"),this.updateRecordArraysLater()},dataDidChange:Ember.observer(function(){this.reloadHasManys()},"data"),reloadHasManys:function(){var t=e(this.constructor,"relationshipsByName");this.updateRecordArraysLater(),t.forEach(function(e,t){this._data.links&&this._data.links[e]||"hasMany"===t.kind&&this.hasManyDidChange(t.key)},this)},hasManyDidChange:function(e){var r=this._relationships[e];if(r){var i=this._data[e]||[];t(r,"content",Ember.A(i)),t(r,"isLoaded",!0),r.trigger("didLoad")}},updateRecordArraysLater:function(){Ember.run.once(this,this.updateRecordArrays)},setupData:function(e,t){t?Ember.merge(this._data,e):this._data=e;var r=this._relationships;this.eachRelationship(function(t,i){e.links&&e.links[t]||i.options.async&&(r[t]=null)}),e&&this.pushedData(),this.suspendRelationshipObservers(function(){this.notifyPropertyChange("data")})},materializeId:function(e){t(this,"id",e)},materializeAttributes:function(e){r(this._data,e)},materializeAttribute:function(e,t){this._data[e]=t},updateHasMany:function(e,t){this._data[e]=t,this.hasManyDidChange(e)},updateBelongsTo:function(e,t){this._data[e]=t},rollback:function(){this._attributes={},e(this,"isError")&&(this._inFlightAttributes={},t(this,"isError",!1)),e(this,"isValid")||(this._inFlightAttributes={},this.send("becameValid")),this.send("rolledBack"),this.suspendRelationshipObservers(function(){this.notifyPropertyChange("data")})},toStringExtension:function(){return e(this,"id")},suspendRelationshipObservers:function(t,r){var i=e(this.constructor,"relationshipNames").belongsTo,n=this;try{this._suspendedRelationships=!0,Ember._suspendObservers(n,i,null,"belongsToDidChange",function(){Ember._suspendBeforeObservers(n,i,null,"belongsToWillChange",function(){t.call(r||n)})})}finally{this._suspendedRelationships=!1}},save:function(){var e="DS: Model#save "+this,t=Ember.RSVP.defer(e);return this.get("store").scheduleSave(this,t),this._inFlightAttributes=this._attributes,this._attributes={},DS.PromiseObject.create({promise:t.promise})},reload:function(){t(this,"isReloading",!0);var e=this,r="DS: Model#reload of "+this,i=new Ember.RSVP.Promise(function(t){e.send("reloadRecord",t)},r).then(function(){return e.set("isReloading",!1),e.set("isError",!1),e},function(t){throw e.set("isError",!0),t},"DS: Model#reload complete, update flags");return DS.PromiseObject.create({promise:i})},adapterDidUpdateAttribute:function(e,t){void 0!==t?(this._data[e]=t,this.notifyPropertyChange(e)):this._data[e]=this._inFlightAttributes[e],this.updateRecordArraysLater()},adapterDidInvalidate:function(e){this.send("becameInvalid",e)},adapterDidError:function(){this.send("becameError"),t(this,"isError",!0)},trigger:function(e){Ember.tryInvoke(this,e,[].slice.call(arguments,1)),this._super.apply(this,arguments)},triggerLater:function(){this._deferredTriggers.push(arguments),i(this,"_triggerDeferredTriggers")},_triggerDeferredTriggers:function(){for(var e=0,t=this._deferredTriggers.length;t>e;e++)this.trigger.apply(this,this._deferredTriggers[e]);this._deferredTriggers=[]}}),DS.Model.reopenClass({_create:DS.Model.create,create:function(){throw new Ember.Error("You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.")}})}(),function(){function e(e,t){return"function"==typeof t.defaultValue?t.defaultValue():t.defaultValue}function t(e,t){return e._attributes.hasOwnProperty(t)||e._inFlightAttributes.hasOwnProperty(t)||e._data.hasOwnProperty(t)}function r(e,t){return e._attributes.hasOwnProperty(t)?e._attributes[t]:e._inFlightAttributes.hasOwnProperty(t)?e._inFlightAttributes[t]:e._data[t]}var i=Ember.get;DS.Model.reopenClass({attributes:Ember.computed(function(){var e=Ember.Map.create();return this.eachComputedProperty(function(t,r){r.isAttribute&&(r.name=t,e.set(t,r))}),e}),transformedAttributes:Ember.computed(function(){var e=Ember.Map.create();return this.eachAttribute(function(t,r){r.type&&e.set(t,r.type)}),e}),eachAttribute:function(e,t){i(this,"attributes").forEach(function(r,i){e.call(t,r,i)
18
+ },t)},eachTransformedAttribute:function(e,t){i(this,"transformedAttributes").forEach(function(r,i){e.call(t,r,i)})}}),DS.Model.reopen({eachAttribute:function(e,t){this.constructor.eachAttribute(e,t)}}),DS.attr=function(i,n){n=n||{};var a={type:i,isAttribute:!0,options:n};return Ember.computed(function(i,a){if(arguments.length>1){var o=this._attributes[i]||this._inFlightAttributes[i]||this._data[i];return this.send("didSetProperty",{name:i,oldValue:o,originalValue:this._data[i],value:a}),this._attributes[i]=a,a}return t(this,i)?r(this,i):e(this,n,i)}).property("data").meta(a)}}(),function(){var e=DS.AttributeChange=function(e){this.record=e.record,this.store=e.store,this.name=e.name,this.value=e.value,this.oldValue=e.oldValue};e.createChange=function(t){return new e(t)},e.prototype={sync:function(){this.value!==this.oldValue&&(this.record.send("becomeDirty"),this.record.updateRecordArraysLater()),this.destroy()},destroy:function(){delete this.record._changesToSync[this.name]}}}(),function(){function e(e){return"object"==typeof e&&(!e.then||"function"!=typeof e.then)}var t=Ember.get,r=Ember.set,i=Ember.EnumerableUtils.forEach;DS.RelationshipChange=function(e){this.parentRecord=e.parentRecord,this.childRecord=e.childRecord,this.firstRecord=e.firstRecord,this.firstRecordKind=e.firstRecordKind,this.firstRecordName=e.firstRecordName,this.secondRecord=e.secondRecord,this.secondRecordKind=e.secondRecordKind,this.secondRecordName=e.secondRecordName,this.changeType=e.changeType,this.store=e.store,this.committed={}},DS.RelationshipChangeAdd=function(e){DS.RelationshipChange.call(this,e)},DS.RelationshipChangeRemove=function(e){DS.RelationshipChange.call(this,e)},DS.RelationshipChange.create=function(e){return new DS.RelationshipChange(e)},DS.RelationshipChangeAdd.create=function(e){return new DS.RelationshipChangeAdd(e)},DS.RelationshipChangeRemove.create=function(e){return new DS.RelationshipChangeRemove(e)},DS.OneToManyChange={},DS.OneToNoneChange={},DS.ManyToNoneChange={},DS.OneToOneChange={},DS.ManyToManyChange={},DS.RelationshipChange._createChange=function(e){return"add"===e.changeType?DS.RelationshipChangeAdd.create(e):"remove"===e.changeType?DS.RelationshipChangeRemove.create(e):void 0},DS.RelationshipChange.determineRelationshipType=function(e,t){var r,i,n=t.key,a=t.kind,o=e.inverseFor(n);return o&&(r=o.name,i=o.kind),o?"belongsTo"===i?"belongsTo"===a?"oneToOne":"manyToOne":"belongsTo"===a?"oneToMany":"manyToMany":"belongsTo"===a?"oneToNone":"manyToNone"},DS.RelationshipChange.createChange=function(e,t,r,i){var n,a=e.constructor;return n=DS.RelationshipChange.determineRelationshipType(a,i),"oneToMany"===n?DS.OneToManyChange.createChange(e,t,r,i):"manyToOne"===n?DS.OneToManyChange.createChange(t,e,r,i):"oneToNone"===n?DS.OneToNoneChange.createChange(e,t,r,i):"manyToNone"===n?DS.ManyToNoneChange.createChange(e,t,r,i):"oneToOne"===n?DS.OneToOneChange.createChange(e,t,r,i):"manyToMany"===n?DS.ManyToManyChange.createChange(e,t,r,i):void 0},DS.OneToNoneChange.createChange=function(e,t,r,i){var n=i.key,a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,store:r,changeType:i.changeType,firstRecordName:n,firstRecordKind:"belongsTo"});return r.addRelationshipChangeFor(e,n,t,null,a),a},DS.ManyToNoneChange.createChange=function(e,t,r,i){var n=i.key,a=DS.RelationshipChange._createChange({parentRecord:e,childRecord:t,secondRecord:e,store:r,changeType:i.changeType,secondRecordName:i.key,secondRecordKind:"hasMany"});return r.addRelationshipChangeFor(e,n,t,null,a),a},DS.ManyToManyChange.createChange=function(e,t,r,i){var n=i.key,a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,secondRecord:t,firstRecordKind:"hasMany",secondRecordKind:"hasMany",store:r,changeType:i.changeType,firstRecordName:n});return r.addRelationshipChangeFor(e,n,t,null,a),a},DS.OneToOneChange.createChange=function(e,t,r,i){var n;i.parentType?n=i.parentType.inverseFor(i.key).name:i.key&&(n=i.key);var a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,secondRecord:t,firstRecordKind:"belongsTo",secondRecordKind:"belongsTo",store:r,changeType:i.changeType,firstRecordName:n});return r.addRelationshipChangeFor(e,n,t,null,a),a},DS.OneToOneChange.maintainInvariant=function(e,r,i,n){if("add"===e.changeType&&r.recordIsMaterialized(i)){var a=t(i,n);if(a){var o=DS.OneToOneChange.createChange(i,a,r,{parentType:e.parentType,hasManyName:e.hasManyName,changeType:"remove",key:e.key});r.addRelationshipChangeFor(i,n,e.parentRecord,null,o),o.sync()}}},DS.OneToManyChange.createChange=function(e,t,r,i){var n;i.parentType?(n=i.parentType.inverseFor(i.key).name,DS.OneToManyChange.maintainInvariant(i,r,e,n)):i.key&&(n=i.key);var a=DS.RelationshipChange._createChange({parentRecord:t,childRecord:e,firstRecord:e,secondRecord:t,firstRecordKind:"belongsTo",secondRecordKind:"hasMany",store:r,changeType:i.changeType,firstRecordName:n});return r.addRelationshipChangeFor(e,n,t,a.getSecondRecordName(),a),a},DS.OneToManyChange.maintainInvariant=function(e,r,i,n){if("add"===e.changeType&&i){var a=t(i,n);if(a){var o=DS.OneToManyChange.createChange(i,a,r,{parentType:e.parentType,hasManyName:e.hasManyName,changeType:"remove",key:e.key});r.addRelationshipChangeFor(i,n,e.parentRecord,o.getSecondRecordName(),o),o.sync()}}},DS.RelationshipChange.prototype={getSecondRecordName:function(){var e,t=this.secondRecordName;if(!t){if(e=this.secondRecord,!e)return;var r=this.firstRecord.constructor,i=r.inverseFor(this.firstRecordName);this.secondRecordName=i.name}return this.secondRecordName},getFirstRecordName:function(){var e=this.firstRecordName;return e},destroy:function(){var e=this.childRecord,t=this.getFirstRecordName(),r=this.getSecondRecordName(),i=this.store;i.removeRelationshipChangeFor(e,t,this.parentRecord,r,this.changeType)},getSecondRecord:function(){return this.secondRecord},getFirstRecord:function(){return this.firstRecord},coalesce:function(){var e=this.store.relationshipChangePairsFor(this.firstRecord);i(e,function(e){var t=e.add,r=e.remove;t&&r&&(t.destroy(),r.destroy())})}},DS.RelationshipChangeAdd.prototype=Ember.create(DS.RelationshipChange.create({})),DS.RelationshipChangeRemove.prototype=Ember.create(DS.RelationshipChange.create({})),DS.RelationshipChangeAdd.prototype.changeType="add",DS.RelationshipChangeAdd.prototype.sync=function(){var i=this.getSecondRecordName(),n=this.getFirstRecordName(),a=this.getFirstRecord(),o=this.getSecondRecord();o instanceof DS.Model&&a instanceof DS.Model&&("belongsTo"===this.secondRecordKind?o.suspendRelationshipObservers(function(){r(o,i,a)}):"hasMany"===this.secondRecordKind&&o.suspendRelationshipObservers(function(){var r=t(o,i);e(r)&&r.addObject(a)})),a instanceof DS.Model&&o instanceof DS.Model&&t(a,n)!==o&&("belongsTo"===this.firstRecordKind?a.suspendRelationshipObservers(function(){r(a,n,o)}):"hasMany"===this.firstRecordKind&&a.suspendRelationshipObservers(function(){var r=t(a,n);e(r)&&r.addObject(o)})),this.coalesce()},DS.RelationshipChangeRemove.prototype.changeType="remove",DS.RelationshipChangeRemove.prototype.sync=function(){var i=this.getSecondRecordName(),n=this.getFirstRecordName(),a=this.getFirstRecord(),o=this.getSecondRecord();o instanceof DS.Model&&a instanceof DS.Model&&("belongsTo"===this.secondRecordKind?o.suspendRelationshipObservers(function(){r(o,i,null)}):"hasMany"===this.secondRecordKind&&o.suspendRelationshipObservers(function(){var r=t(o,i);e(r)&&r.removeObject(a)})),a instanceof DS.Model&&t(a,n)&&("belongsTo"===this.firstRecordKind?a.suspendRelationshipObservers(function(){r(a,n,null)}):"hasMany"===this.firstRecordKind&&a.suspendRelationshipObservers(function(){var r=t(a,n);e(r)&&r.removeObject(o)})),this.coalesce()}}(),function(){function e(e,i,n){return Ember.computed(function(e,i){var a=t(this,"data"),o=t(this,"store"),s="DS: Async belongsTo "+this+" : "+e;if(2===arguments.length)return void 0===i?null:DS.PromiseObject.create({promise:Ember.RSVP.resolve(i,s)});var c=a.links&&a.links[e],d=a[e];if(r(d)){if(c){var u=Ember.RSVP.defer("DS: Async belongsTo (link) "+this+" : "+e);return o.findBelongsTo(this,c,n,u),DS.PromiseObject.create({promise:u.promise})}return null}var l=o.fetchRecord(d)||Ember.RSVP.resolve(d,s);return DS.PromiseObject.create({promise:l})}).property("data").meta(n)}var t=Ember.get,r=(Ember.set,Ember.isNone);DS.belongsTo=function(i,n){"object"==typeof i&&(n=i,i=void 0),n=n||{};var a={type:i,isRelationship:!0,options:n,kind:"belongsTo"};return n.async?e(i,n,a):Ember.computed(function(e,n){var a,o,s=t(this,"data"),c=t(this,"store");return o="string"==typeof i?c.modelFor(i):i,2===arguments.length?void 0===n?null:n:(a=s[e],r(a)?null:(c.fetchRecord(a),a))}).property("data").meta(a)},DS.Model.reopen({belongsToWillChange:Ember.beforeObserver(function(e,r){if(t(e,"isLoaded")){var i=t(e,r);if(i){var n=t(e,"store"),a=DS.RelationshipChange.createChange(e,i,n,{key:r,kind:"belongsTo",changeType:"remove"});a.sync(),this._changesToSync[r]=a}}}),belongsToDidChange:Ember.immediateObserver(function(e,r){if(t(e,"isLoaded")){var i=t(e,r);if(i){var n=t(e,"store"),a=DS.RelationshipChange.createChange(e,i,n,{key:r,kind:"belongsTo",changeType:"add"});a.sync()}}delete this._changesToSync[r]})})}(),function(){function e(e,r,i){return Ember.computed(function(e){var a=this._relationships[e],o="DS: Async hasMany "+this+" : "+e;if(!a){var s=Ember.RSVP.defer(o);a=t(this,e,r,function(t,r){var a,o=r.links&&r.links[e];return a=o?t.findHasMany(this,o,i,s):t.findMany(this,r[e],i.type,s),n(a,"promise",s.promise),a})}var c=a.get("promise").then(function(){return a},null,"DS: Async hasMany records received");return DS.PromiseArray.create({promise:c})}).property("data").meta(i)}function t(e,t,r,n){var o=e._relationships;if(o[t])return o[t];var s=i(e,"data"),c=i(e,"store"),d=o[t]=n.call(e,c,s);return a(d,{owner:e,name:t,isPolymorphic:r.polymorphic})}function r(r,i){i=i||{};var n={type:r,isRelationship:!0,options:i,kind:"hasMany"};return i.async?e(r,i,n):Ember.computed(function(e){return t(this,e,i,function(t,r){r[e];return t.findMany(this,r[e],n.type)})}).property("data").meta(n)}var i=Ember.get,n=Ember.set,a=Ember.setProperties;DS.hasMany=function(e,t){return"object"==typeof e&&(t=e,e=void 0),r(e,t)}}(),function(){{var e=Ember.get;Ember.set}DS.Model.reopen({didDefineProperty:function(e,t,r){if(r instanceof Ember.Descriptor){var i=r.meta();i.isRelationship&&"belongsTo"===i.kind&&(Ember.addObserver(e,t,null,"belongsToDidChange"),Ember.addBeforeObserver(e,t,null,"belongsToWillChange")),i.parentType=e.constructor}}}),DS.Model.reopenClass({typeForRelationship:function(t){var r=e(this,"relationshipsByName").get(t);return r&&r.type},inverseFor:function(t){function r(t,i,n){n=n||[];var a=e(i,"relationships");if(a){var o=a.get(t);return o&&n.push.apply(n,a.get(t)),t.superclass&&r(t.superclass,i,n),n}}var i=this.typeForRelationship(t);if(!i)return null;var n=this.metaForProperty(t).options;if(null===n.inverse)return null;var a,o;if(n.inverse)a=n.inverse,o=Ember.get(i,"relationshipsByName").get(a).kind;else{var s=r(this,i);if(0===s.length)return null;a=s[0].name,o=s[0].kind}return{type:i,name:a,kind:o}},relationships:Ember.computed(function(){var e=new Ember.MapWithDefault({defaultValue:function(){return[]}});return this.eachComputedProperty(function(t,r){if(r.isRelationship){"string"==typeof r.type&&(r.type=this.store.modelFor(r.type));var i=e.get(r.type);i.push({name:t,kind:r.kind})}}),e}),relationshipNames:Ember.computed(function(){var e={hasMany:[],belongsTo:[]};return this.eachComputedProperty(function(t,r){r.isRelationship&&e[r.kind].push(t)}),e}),relatedTypes:Ember.computed(function(){var t,r=Ember.A();return this.eachComputedProperty(function(i,n){n.isRelationship&&(t=n.type,"string"==typeof t&&(t=e(this,t,!1)||this.store.modelFor(t)),r.contains(t)||r.push(t))}),r}),relationshipsByName:Ember.computed(function(){var e,t=Ember.Map.create();return this.eachComputedProperty(function(r,i){i.isRelationship&&(i.key=r,e=i.type,e||"hasMany"!==i.kind?e||(e=r):e=Ember.String.singularize(r),"string"==typeof e&&(i.type=this.store.modelFor(e)),t.set(r,i))}),t}),fields:Ember.computed(function(){var e=Ember.Map.create();return this.eachComputedProperty(function(t,r){r.isRelationship?e.set(t,r.kind):r.isAttribute&&e.set(t,"attribute")}),e}),eachRelationship:function(t,r){e(this,"relationshipsByName").forEach(function(e,i){t.call(r,e,i)})},eachRelatedType:function(t,r){e(this,"relatedTypes").forEach(function(e){t.call(r,e)})}}),DS.Model.reopen({eachRelationship:function(e,t){this.constructor.eachRelationship(e,t)}})}(),function(){var e=Ember.get,t=(Ember.set,Ember.run.once),r=Ember.EnumerableUtils.forEach;DS.RecordArrayManager=Ember.Object.extend({init:function(){this.filteredRecordArrays=Ember.MapWithDefault.create({defaultValue:function(){return[]}}),this.changedRecords=[]},recordDidChange:function(e){this.changedRecords.push(e),t(this,this.updateRecordArrays)},recordArraysForRecord:function(e){return e._recordArrays=e._recordArrays||Ember.OrderedSet.create(),e._recordArrays},updateRecordArrays:function(){r(this.changedRecords,function(t){e(t,"isDeleted")?this._recordWasDeleted(t):this._recordWasChanged(t)},this),this.changedRecords=[]},_recordWasDeleted:function(e){var t=e._recordArrays;t&&r(t,function(t){t.removeRecord(e)})},_recordWasChanged:function(t){var i,n=t.constructor,a=this.filteredRecordArrays.get(n);r(a,function(r){i=e(r,"filterFunction"),this.updateRecordArray(r,i,n,t)},this);var o=t._loadingRecordArrays;if(o){for(var s=0,c=o.length;c>s;s++)o[s].loadedRecord();t._loadingRecordArrays=[]}},updateRecordArray:function(e,t,r,i){var n;n=t?t(i):!0;var a=this.recordArraysForRecord(i);n?(a.add(e),e.addRecord(i)):n||(a.remove(e),e.removeRecord(i))},updateFilter:function(t,r,i){for(var n,a=this.store.typeMapFor(r),o=a.records,s=0,c=o.length;c>s;s++)n=o[s],e(n,"isDeleted")||e(n,"isEmpty")||this.updateRecordArray(t,i,r,n)},createManyArray:function(e,t){var i=DS.ManyArray.create({type:e,content:t,store:this.store});return r(t,function(e){var t=this.recordArraysForRecord(e);t.add(i)},this),i},registerFilteredRecordArray:function(e,t,r){var i=this.filteredRecordArrays.get(t);i.push(e),this.updateFilter(e,t,r)},registerWaitingRecordArray:function(e,t){var r=e._loadingRecordArrays||[];r.push(t),e._loadingRecordArrays=r}})}(),function(){var e=Ember.get,t=(Ember.set,Ember.ArrayPolyfills.map),r=["description","fileName","lineNumber","message","name","number","stack"];DS.InvalidError=function(e){var t=Error.prototype.constructor.call(this,"The backend rejected the commit because it was invalid: "+Ember.inspect(e));this.errors=e;for(var i=0,n=r.length;n>i;i++)this[r[i]]=t[r[i]]},DS.InvalidError.prototype=Ember.create(Error.prototype),DS.Adapter=Ember.Object.extend({find:Ember.required(Function),findAll:null,findQuery:null,generateIdForRecord:null,serialize:function(t,r){return e(t,"store").serializerFor(t.constructor.typeKey).serialize(t,r)},createRecord:Ember.required(Function),updateRecord:Ember.required(Function),deleteRecord:Ember.required(Function),findMany:function(e,r,i){var n=t.call(i,function(t){return this.find(e,r,t)},this);return Ember.RSVP.all(n)}})}(),function(){var e=Ember.get,t=Ember.String.fmt,r=Ember.EnumerableUtils.indexOf,i=0;DS.FixtureAdapter=DS.Adapter.extend({serializer:null,simulateRemoteResponse:!0,latency:50,fixturesForType:function(e){if(e.FIXTURES){var r=Ember.A(e.FIXTURES);return r.map(function(e){var r=typeof e.id;if("number"!==r&&"string"!==r)throw new Error(t("the id property must be defined as a number or string for fixture %@",[e]));return e.id=e.id+"",e})}return null},queryFixtures:function(){},updateFixtures:function(e,t){e.FIXTURES||(e.FIXTURES=[]);var r=e.FIXTURES;this.deleteLoadedFixture(e,t),r.push(t)},mockJSON:function(e,t,r){return e.serializerFor(t).serialize(r,{includeId:!0})},generateIdForRecord:function(){return"fixture-"+i++},find:function(e,t,r){var i,n=this.fixturesForType(t);return n&&(i=Ember.A(n).findProperty("id",r)),i?this.simulateRemoteCall(function(){return i},this):void 0},findMany:function(e,t,i){var n=this.fixturesForType(t);return n&&(n=n.filter(function(e){return-1!==r(i,e.id)})),n?this.simulateRemoteCall(function(){return n},this):void 0},findAll:function(e,t){var r=this.fixturesForType(t);return this.simulateRemoteCall(function(){return r},this)},findQuery:function(e,t,r){var i=this.fixturesForType(t);return i=this.queryFixtures(i,r,t),i?this.simulateRemoteCall(function(){return i},this):void 0},createRecord:function(e,t,r){var i=this.mockJSON(e,t,r);return this.updateFixtures(t,i),this.simulateRemoteCall(function(){return i},this)},updateRecord:function(e,t,r){var i=this.mockJSON(e,t,r);return this.updateFixtures(t,i),this.simulateRemoteCall(function(){return i},this)},deleteRecord:function(e,t,r){var i=this.mockJSON(e,t,r);return this.deleteLoadedFixture(t,i),this.simulateRemoteCall(function(){return null})},deleteLoadedFixture:function(e,t){var i=this.findExistingFixture(e,t);if(i){var n=r(e.FIXTURES,i);return e.FIXTURES.splice(n,1),!0}},findExistingFixture:function(t,r){var i=this.fixturesForType(t),n=e(r,"id");return this.findFixtureById(i,n)},findFixtureById:function(t,r){return Ember.A(t).find(function(t){return""+e(t,"id")==""+r?!0:!1})},simulateRemoteCall:function(t,r){var i=this;return new Ember.RSVP.Promise(function(n){e(i,"simulateRemoteResponse")?Ember.run.later(function(){n(t.call(r))},e(i,"latency")):Ember.run.schedule("actions",null,function(){n(t.call(r))})},"DS: FixtureAdapter#simulateRemoteCall")}})}(),function(){function e(e){return null==e?null:e+""}var t=Ember.get,r=(Ember.set,Ember.ArrayPolyfills.forEach),i=Ember.ArrayPolyfills.map;DS.RESTSerializer=DS.JSONSerializer.extend({normalize:function(e,t,r){return this.normalizeId(t),this.normalizeUsingDeclaredMapping(e,t),this.normalizeAttributes(e,t),this.normalizeRelationships(e,t),this.normalizeHash&&this.normalizeHash[r]&&this.normalizeHash[r](t),this._super(e,t,r)},normalizePayload:function(e,t){return t},normalizeId:function(e){var r=t(this,"primaryKey");"id"!==r&&(e.id=e[r],delete e[r])},normalizeUsingDeclaredMapping:function(e,r){var i,n,a=t(this,"attrs");if(a)for(n in a)i=a[n],r[n]=r[i],delete r[i]},normalizeAttributes:function(e,t){var r;this.keyForAttribute&&e.eachAttribute(function(e){r=this.keyForAttribute(e),e!==r&&(t[e]=t[r],delete t[r])},this)},normalizeRelationships:function(e,t){var r;this.keyForRelationship&&e.eachRelationship(function(e,i){r=this.keyForRelationship(e,i.kind),e!==r&&(t[e]=t[r],delete t[r])},this)},extractSingle:function(t,i,n,a){n=this.normalizePayload(i,n);var o,s=i.typeKey;for(var c in n){var d=this.typeForRoot(c),u=d===s;if(u&&"array"!==Ember.typeOf(n[c]))o=this.normalize(i,n[c],c);else{{t.modelFor(d)}r.call(n[c],function(r){var i=this.typeForRoot(c),n=t.modelFor(i),s=t.serializerFor(n);r=s.normalize(n,r,c);var d=u&&!a&&!o,l=u&&e(r.id)===a;d||l?o=r:t.push(i,r)},this)}}return o},extractArray:function(e,t,r){r=this.normalizePayload(t,r);var n,a=t.typeKey;for(var o in r){var s=o,c=!1;"_"===o.charAt(0)&&(c=!0,s=o.substr(1));var d=this.typeForRoot(s),u=e.modelFor(d),l=e.serializerFor(u),h=!c&&d===a,f=i.call(r[o],function(e){return l.normalize(u,e,o)},this);h?n=f:e.pushMany(d,f)}return n},pushPayload:function(e,t){t=this.normalizePayload(null,t);for(var r in t){var n=this.typeForRoot(r),a=e.modelFor(n),o=i.call(t[r],function(e){return this.normalize(a,e,r)},this);e.pushMany(n,o)}},typeForRoot:function(e){return Ember.String.singularize(e)},serialize:function(){return this._super.apply(this,arguments)},serializeIntoHash:function(e,t,r,i){e[t.typeKey]=this.serialize(r,i)},serializePolymorphicType:function(e,r,i){var n=i.key,a=t(e,n);n=this.keyForAttribute?this.keyForAttribute(n):n,r[n+"Type"]=a.constructor.typeKey}})}(),function(){var e=Ember.get,t=(Ember.set,Ember.ArrayPolyfills.forEach);DS.RESTAdapter=DS.Adapter.extend({defaultSerializer:"_rest",find:function(e,t,r){return this.ajax(this.buildURL(t.typeKey,r),"GET")},findAll:function(e,t,r){var i;return r&&(i={since:r}),this.ajax(this.buildURL(t.typeKey),"GET",{data:i})},findQuery:function(e,t,r){return this.ajax(this.buildURL(t.typeKey),"GET",{data:r})},findMany:function(e,t,r){return this.ajax(this.buildURL(t.typeKey),"GET",{data:{ids:r}})},findHasMany:function(t,r,i){var n=e(this,"host"),a=e(r,"id"),o=r.constructor.typeKey;return n&&"/"===i.charAt(0)&&"/"!==i.charAt(1)&&(i=n+i),this.ajax(this.urlPrefix(i,this.buildURL(o,a)),"GET")},findBelongsTo:function(t,r,i){var n=e(r,"id"),a=r.constructor.typeKey;return this.ajax(this.urlPrefix(i,this.buildURL(a,n)),"GET")},createRecord:function(e,t,r){var i={},n=e.serializerFor(t.typeKey);return n.serializeIntoHash(i,t,r,{includeId:!0}),this.ajax(this.buildURL(t.typeKey),"POST",{data:i})},updateRecord:function(t,r,i){var n={},a=t.serializerFor(r.typeKey);a.serializeIntoHash(n,r,i);var o=e(i,"id");return this.ajax(this.buildURL(r.typeKey,o),"PUT",{data:n})},deleteRecord:function(t,r,i){var n=e(i,"id");return this.ajax(this.buildURL(r.typeKey,n),"DELETE")},buildURL:function(t,r){var i=[],n=e(this,"host"),a=this.urlPrefix();return t&&i.push(this.pathForType(t)),r&&i.push(r),a&&i.unshift(a),i=i.join("/"),!n&&i&&(i="/"+i),i},urlPrefix:function(t,r){var i=e(this,"host"),n=e(this,"namespace"),a=[];return t?"/"===t.charAt(0)?i&&(t=t.slice(1),a.push(i)):/^http(s)?:\/\//.test(t)||a.push(r):(i&&a.push(i),n&&a.push(n)),t&&a.push(t),a.join("/")},pathForType:function(e){return Ember.String.pluralize(e)},ajaxError:function(e){return e&&(e.then=null),e},ajax:function(e,t,r){var i=this;return new Ember.RSVP.Promise(function(n,a){r=i.ajaxOptions(e,t,r),r.success=function(e){Ember.run(null,n,e)},r.error=function(e){Ember.run(null,a,i.ajaxError(e))},Ember.$.ajax(r)},"DS: RestAdapter#ajax "+t+" to "+e)},ajaxOptions:function(e,r,i){if(i=i||{},i.url=e,i.type=r,i.dataType="json",i.context=this,i.data&&"GET"!==r&&(i.contentType="application/json; charset=utf-8",i.data=JSON.stringify(i.data)),void 0!==this.headers){var n=this.headers;i.beforeSend=function(e){t.call(Ember.keys(n),function(t){e.setRequestHeader(t,n[t])})}}return i}})}(),function(){DS.Model.reopen({_debugInfo:function(){var e=["id"],t={belongsTo:[],hasMany:[]},r=[];this.eachAttribute(function(t){e.push(t)},this),this.eachRelationship(function(e,i){t[i.kind].push(e),r.push(e)});var i=[{name:"Attributes",properties:e,expand:!0},{name:"Belongs To",properties:t.belongsTo,expand:!0},{name:"Has Many",properties:t.hasMany,expand:!0},{name:"Flags",properties:["isLoaded","isDirty","isSaving","isDeleted","isError","isNew","isValid"]}];return{propertyInfo:{includeOtherProperties:!0,groups:i,expensiveProperties:r}}}})}(),function(){Ember.String.pluralize=function(e){return Ember.Inflector.inflector.pluralize(e)},Ember.String.singularize=function(e){return Ember.Inflector.inflector.singularize(e)}}(),function(){function e(e,t){for(var r=0,i=t.length;i>r;r++)e.uncountable[t[r].toLowerCase()]=!0}function t(e,t){for(var r,i=0,n=t.length;n>i;i++)r=t[i],e.irregular[r[0].toLowerCase()]=r[1],e.irregularInverse[r[1].toLowerCase()]=r[0]}function r(r){r=r||{},r.uncountable=r.uncountable||{},r.irregularPairs=r.irregularPairs||{};var i=this.rules={plurals:r.plurals||[],singular:r.singular||[],irregular:{},irregularInverse:{},uncountable:{}};e(i,r.uncountable),t(i,r.irregularPairs)}var i=/^\s*$/;r.prototype={plural:function(e,t){this.rules.plurals.push([e,t.toLowerCase()])},singular:function(e,t){this.rules.singular.push([e,t.toLowerCase()])},uncountable:function(t){e(this.rules,[t.toLowerCase()])},irregular:function(e,r){t(this.rules,[[e,r]])},pluralize:function(e){return this.inflect(e,this.rules.plurals,this.rules.irregular)},singularize:function(e){return this.inflect(e,this.rules.singular,this.rules.irregularInverse)},inflect:function(e,t,r){var n,a,o,s,c,d,u,l;if(c=i.test(e))return e;if(s=e.toLowerCase(),d=this.rules.uncountable[s])return e;if(u=r&&r[s])return u;for(var h=t.length,f=0;h>f&&(n=t[h-1],l=n[0],!l.test(e));h--);return n=n||[],l=n[0],a=n[1],o=e.replace(l,a)}},Ember.Inflector=r}(),function(){Ember.Inflector.defaultRules={plurals:[[/$/,"s"],[/s$/i,"s"],[/^(ax|test)is$/i,"$1es"],[/(octop|vir)us$/i,"$1i"],[/(octop|vir)i$/i,"$1i"],[/(alias|status)$/i,"$1es"],[/(bu)s$/i,"$1ses"],[/(buffal|tomat)o$/i,"$1oes"],[/([ti])um$/i,"$1a"],[/([ti])a$/i,"$1a"],[/sis$/i,"ses"],[/(?:([^f])fe|([lr])f)$/i,"$1$2ves"],[/(hive)$/i,"$1s"],[/([^aeiouy]|qu)y$/i,"$1ies"],[/(x|ch|ss|sh)$/i,"$1es"],[/(matr|vert|ind)(?:ix|ex)$/i,"$1ices"],[/^(m|l)ouse$/i,"$1ice"],[/^(m|l)ice$/i,"$1ice"],[/^(ox)$/i,"$1en"],[/^(oxen)$/i,"$1"],[/(quiz)$/i,"$1zes"]],singular:[[/s$/i,""],[/(ss)$/i,"$1"],[/(n)ews$/i,"$1ews"],[/([ti])a$/i,"$1um"],[/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i,"$1sis"],[/(^analy)(sis|ses)$/i,"$1sis"],[/([^f])ves$/i,"$1fe"],[/(hive)s$/i,"$1"],[/(tive)s$/i,"$1"],[/([lr])ves$/i,"$1f"],[/([^aeiouy]|qu)ies$/i,"$1y"],[/(s)eries$/i,"$1eries"],[/(m)ovies$/i,"$1ovie"],[/(x|ch|ss|sh)es$/i,"$1"],[/^(m|l)ice$/i,"$1ouse"],[/(bus)(es)?$/i,"$1"],[/(o)es$/i,"$1"],[/(shoe)s$/i,"$1"],[/(cris|test)(is|es)$/i,"$1is"],[/^(a)x[ie]s$/i,"$1xis"],[/(octop|vir)(us|i)$/i,"$1us"],[/(alias|status)(es)?$/i,"$1"],[/^(ox)en/i,"$1"],[/(vert|ind)ices$/i,"$1ex"],[/(matr)ices$/i,"$1ix"],[/(quiz)zes$/i,"$1"],[/(database)s$/i,"$1"]],irregularPairs:[["person","people"],["man","men"],["child","children"],["sex","sexes"],["move","moves"],["cow","kine"],["zombie","zombies"]],uncountable:["equipment","information","rice","money","species","series","fish","sheep","jeans","police"]}}(),function(){(Ember.EXTEND_PROTOTYPES===!0||Ember.EXTEND_PROTOTYPES.String)&&(String.prototype.pluralize=function(){return Ember.String.pluralize(this)},String.prototype.singularize=function(){return Ember.String.singularize(this)})}(),function(){Ember.Inflector.inflector=new Ember.Inflector(Ember.Inflector.defaultRules)}(),function(){var e=Ember.get,t=Ember.EnumerableUtils.forEach;DS.ActiveModelSerializer=DS.RESTSerializer.extend({keyForAttribute:function(e){return Ember.String.decamelize(e)},keyForRelationship:function(e,t){return e=Ember.String.decamelize(e),"belongsTo"===t?e+"_id":"hasMany"===t?Ember.String.singularize(e)+"_ids":e},serializeHasMany:Ember.K,serializeIntoHash:function(e,t,r,i){var n=Ember.String.decamelize(t.typeKey);e[n]=this.serialize(r,i)},serializePolymorphicType:function(t,r,i){var n=i.key,a=e(t,n);n=this.keyForAttribute(n),r[n+"_type"]=Ember.String.capitalize(a.constructor.typeKey)},typeForRoot:function(e){var t=Ember.String.camelize(e);return Ember.String.singularize(t)},normalize:function(e,t,r){return this.normalizeLinks(t),this._super(e,t,r)},normalizeLinks:function(e){if(e.links){var t=e.links;for(var r in t){var i=Ember.String.camelize(r);i!==r&&(t[i]=t[r],delete t[r])}}},normalizeRelationships:function(e,r){var i,n;this.keyForRelationship&&e.eachRelationship(function(e,a){if(a.options.polymorphic){if(i=this.keyForAttribute(e),n=r[i],n&&n.type)n.type=this.typeForRoot(n.type);else if(n&&"hasMany"===a.kind){var o=this;t(n,function(e){e.type=o.typeForRoot(e.type)})}}else i=this.keyForRelationship(e,a.kind),n=r[i];r[e]=n,e!==i&&delete r[i]},this)}})}(),function(){function e(i,n,a,o,s){var c=t(n,"attrs");c&&a.eachRelationship(function(n,a){var d,u,l,h,f=c[n],p=i.serializerFor(a.type.typeKey),m=t(p,"primaryKey");if("hasMany"===a.kind&&f&&("always"===f.embedded||"load"===f.embedded)){if(u="_"+Ember.String.pluralize(a.type.typeKey),d=this.keyForRelationship(n,a.kind),l=this.keyForAttribute(n),h=[],!o[l])return;s[u]=s[u]||[],r(o[l],function(t){var r=i.modelFor(a.type.typeKey);e(i,p,r,t,s),h.push(t[m]),s[u].push(t)}),o[d]=h,delete o[l]}},n)}var t=Ember.get,r=Ember.EnumerableUtils.forEach;DS.EmbeddedRecordsMixin=Ember.Mixin.create({serializeHasMany:function(e,r,i){var n=i.key,a=t(this,"attrs"),o=a&&a[n]&&"always"===a[n].embedded;o&&(r[this.keyForAttribute(n)]=t(e,n).map(function(e){var r=e.serialize(),i=t(this,"primaryKey");return r[i]=t(e,i),r},this))},extractSingle:function(t,r,i,n,a){var o=this.keyForAttribute(r.typeKey),s=i[o];return e(t,this,r,s,i),this._super(t,r,i,n,a)},extractArray:function(t,i,n){var a=this.keyForAttribute(i.typeKey),o=n[Ember.String.pluralize(a)];return r(o,function(r){e(t,this,i,r,n)},this),this._super(t,i,n)}})}(),function(){var e=Ember.EnumerableUtils.forEach;DS.ActiveModelAdapter=DS.RESTAdapter.extend({defaultSerializer:"_ams",pathForType:function(e){var t=Ember.String.decamelize(e);return Ember.String.pluralize(t)},ajaxError:function(t){var r=this._super(t);if(t&&422===t.status){var i=Ember.$.parseJSON(t.responseText).errors,n={};return e(Ember.keys(i),function(e){n[Ember.String.camelize(e)]=i[e]}),new DS.InvalidError(n)}return r}})}(),function(){Ember.onLoad("Ember.Application",function(e){e.initializer({name:"activeModelAdapter",initialize:function(e,t){t.register("serializer:_ams",DS.ActiveModelSerializer),t.register("adapter:_ams",DS.ActiveModelAdapter)}})})}()}(),"undefined"==typeof location||"localhost"!==location.hostname&&"127.0.0.1"!==location.hostname||Ember.Logger.warn("You are running a production build of Ember on localhost and won't receive detailed error messages. If you want full error messages please use the non-minified build provided on the Ember website.");
@@ -1,15 +1,12 @@
1
- // ==========================================================================
2
- // Project: Ember Data
3
- // Copyright: Copyright 2011-2013 Tilde Inc. and contributors.
4
- // Portions Copyright 2011 LivingSocial Inc.
5
- // License: Licensed under MIT license (see license.js)
6
- // ==========================================================================
1
+ /*!
2
+ * @overview Ember Data
3
+ * @copyright Copyright 2011-2013 Tilde Inc. and contributors.
4
+ * Portions Copyright 2011 LivingSocial Inc.
5
+ * @license Licensed under MIT license (see license.js)
6
+ */
7
7
 
8
8
 
9
-
10
- // Version: v1.0.0-beta.3-48-gb47afef
11
- // Last commit: b47afef (2013-10-08 19:07:59 -0700)
12
-
9
+ // Version: 1.0.0-beta.4
13
10
 
14
11
  (function() {
15
12
  var define, requireModule;
@@ -61,10 +58,10 @@ var define, requireModule;
61
58
  @class DS
62
59
  @static
63
60
  */
64
-
61
+ var DS;
65
62
  if ('undefined' === typeof DS) {
66
63
  DS = Ember.Namespace.create({
67
- VERSION: '1.0.0-beta.2'
64
+ VERSION: '1.0.0-beta.4'
68
65
  });
69
66
 
70
67
  if ('undefined' !== typeof window) {
@@ -75,6 +72,7 @@ if ('undefined' === typeof DS) {
75
72
  Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
76
73
  }
77
74
  }
75
+
78
76
  })();
79
77
 
80
78
 
@@ -90,9 +88,56 @@ function aliasMethod(methodName) {
90
88
  };
91
89
  }
92
90
 
91
+ /**
92
+ In Ember Data a Serializer is used to serialize and deserialize
93
+ records when they are transfered in and out of an external source.
94
+ This process involves normalizing property names, transforming
95
+ attribute values and serializeing relationships.
96
+
97
+ For maximum performance Ember Data recomends you use the
98
+ [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses.
99
+
100
+ `JSONSerializer` is useful for simpler or legacy backends that may
101
+ not support the http://jsonapi.org/ spec.
102
+
103
+ @class JSONSerializer
104
+ @namespace DS
105
+ */
93
106
  DS.JSONSerializer = Ember.Object.extend({
107
+ /**
108
+ The primaryKey is used when serializing and deserializing
109
+ data. Ember Data always uses the `id` propery to store the id of
110
+ the record. The external source may not always follow this
111
+ convention. In these cases it is usesful to override the
112
+ primaryKey property to match the primaryKey of your external
113
+ store.
114
+
115
+ Example
116
+
117
+ ```javascript
118
+ App.ApplicationSerializer = DS.JSONSerializer.extend({
119
+ primaryKey: '_id'
120
+ });
121
+ ```
122
+
123
+ @property primaryKey
124
+ @type {String}
125
+ */
94
126
  primaryKey: 'id',
95
127
 
128
+ /**
129
+ Given a subclass of `DS.Model` and a JSON object this method will
130
+ iterate through each attribute of the `DS.Model` and invoke the
131
+ `DS.Transform#deserialize` method on the matching property of the
132
+ JSON object. This method is typically called after the
133
+ serializer's `normalize` method.
134
+
135
+ @method applyTransforms
136
+ @private
137
+ @param {subclass of DS.Model} type
138
+ @param {Object} data The data to transform
139
+ @return {Object} data The transformed data object
140
+ */
96
141
  applyTransforms: function(type, data) {
97
142
  type.eachTransformedAttribute(function(key, type) {
98
143
  var transform = this.transformFor(type);
@@ -102,6 +147,39 @@ DS.JSONSerializer = Ember.Object.extend({
102
147
  return data;
103
148
  },
104
149
 
150
+ /**
151
+ Normalizes a part of the JSON payload returned by
152
+ the server. You should override this method, munge the hash
153
+ and call super if you have generic normalization to do.
154
+
155
+ It takes the type of the record that is being normalized
156
+ (as a DS.Model class), the property where the hash was
157
+ originally found, and the hash to normalize.
158
+
159
+ You can use this method, for example, to normalize underscored keys to camelized
160
+ or other general-purpose normalizations.
161
+
162
+ Example
163
+
164
+ ```javascript
165
+ App.ApplicationSerializer = DS.JSONSerializer.extend({
166
+ normalize: function(type, hash) {
167
+ var normalizedHash = {};
168
+ var fields = Ember.get(type, 'fields');
169
+ fields.forEach(function(field) {
170
+ var normalizedProp = Ember.String.camelize(field);
171
+ normalizedHash[normalizedProp] = hash[field];
172
+ });
173
+ return this._super.apply(this, arguments);
174
+ }
175
+ });
176
+ ```
177
+
178
+ @method normalize
179
+ @param {subclass of DS.Model} type
180
+ @param {Object} hash
181
+ @return {Object}
182
+ */
105
183
  normalize: function(type, hash) {
106
184
  if (!hash) { return hash; }
107
185
 
@@ -110,7 +188,149 @@ DS.JSONSerializer = Ember.Object.extend({
110
188
  },
111
189
 
112
190
  // SERIALIZE
191
+ /**
192
+ Called when a record is saved in order to convert the
193
+ record into JSON.
194
+
195
+ By default, it creates a JSON object with a key for
196
+ each attribute and belongsTo relationship.
197
+
198
+ For example, consider this model:
199
+
200
+ ```javascript
201
+ App.Comment = DS.Model.extend({
202
+ title: DS.attr(),
203
+ body: DS.attr(),
204
+
205
+ author: DS.belongsTo('user')
206
+ });
207
+ ```
208
+
209
+ The default serialization would create a JSON object like:
210
+
211
+ ```javascript
212
+ {
213
+ "title": "Rails is unagi",
214
+ "body": "Rails? Omakase? O_O",
215
+ "author": 12
216
+ }
217
+ ```
218
+
219
+ By default, attributes are passed through as-is, unless
220
+ you specified an attribute type (`DS.attr('date')`). If
221
+ you specify a transform, the JavaScript value will be
222
+ serialized when inserted into the JSON hash.
223
+
224
+ By default, belongs-to relationships are converted into
225
+ IDs when inserted into the JSON hash.
226
+
227
+ ## IDs
228
+
229
+ `serialize` takes an options hash with a single option:
230
+ `includeId`. If this option is `true`, `serialize` will,
231
+ by default include the ID in the JSON object it builds.
232
+
233
+ The adapter passes in `includeId: true` when serializing
234
+ a record for `createRecord`, but not for `updateRecord`.
235
+
236
+ ## Customization
237
+
238
+ Your server may expect a different JSON format than the
239
+ built-in serialization format.
240
+
241
+ In that case, you can implement `serialize` yourself and
242
+ return a JSON hash of your choosing.
243
+
244
+ ```javascript
245
+ App.PostSerializer = DS.JSONSerializer.extend({
246
+ serialize: function(post, options) {
247
+ var json = {
248
+ POST_TTL: post.get('title'),
249
+ POST_BDY: post.get('body'),
250
+ POST_CMS: post.get('comments').mapProperty('id')
251
+ }
252
+
253
+ if (options.includeId) {
254
+ json.POST_ID_ = post.get('id');
255
+ }
256
+
257
+ return json;
258
+ }
259
+ });
260
+ ```
261
+
262
+ ## Customizing an App-Wide Serializer
263
+
264
+ If you want to define a serializer for your entire
265
+ application, you'll probably want to use `eachAttribute`
266
+ and `eachRelationship` on the record.
267
+
268
+ ```javascript
269
+ App.ApplicationSerializer = DS.JSONSerializer.extend({
270
+ serialize: function(record, options) {
271
+ var json = {};
272
+
273
+ record.eachAttribute(function(name) {
274
+ json[serverAttributeName(name)] = record.get(name);
275
+ })
276
+
277
+ record.eachRelationship(function(name, relationship) {
278
+ if (relationship.kind === 'hasMany') {
279
+ json[serverHasManyName(name)] = record.get(name).mapBy('id');
280
+ }
281
+ });
282
+
283
+ if (options.includeId) {
284
+ json.ID_ = record.get('id');
285
+ }
286
+
287
+ return json;
288
+ }
289
+ });
290
+
291
+ function serverAttributeName(attribute) {
292
+ return attribute.underscore().toUpperCase();
293
+ }
294
+
295
+ function serverHasManyName(name) {
296
+ return serverAttributeName(name.singularize()) + "_IDS";
297
+ }
298
+ ```
299
+
300
+ This serializer will generate JSON that looks like this:
113
301
 
302
+ ```javascript
303
+ {
304
+ "TITLE": "Rails is omakase",
305
+ "BODY": "Yep. Omakase.",
306
+ "COMMENT_IDS": [ 1, 2, 3 ]
307
+ }
308
+ ```
309
+
310
+ ## Tweaking the Default JSON
311
+
312
+ If you just want to do some small tweaks on the default JSON,
313
+ you can call super first and make the tweaks on the returned
314
+ JSON.
315
+
316
+ ```javascript
317
+ App.PostSerializer = DS.JSONSerializer.extend({
318
+ serialize: function(record, options) {
319
+ var json = this._super.apply(this, arguments);
320
+
321
+ json.subject = json.title;
322
+ delete json.title;
323
+
324
+ return json;
325
+ }
326
+ });
327
+ ```
328
+
329
+ @method serialize
330
+ @param {subclass of DS.Model} record
331
+ @param {Object} options
332
+ @return {Object} json
333
+ */
114
334
  serialize: function(record, options) {
115
335
  var json = {};
116
336
 
@@ -137,6 +357,29 @@ DS.JSONSerializer = Ember.Object.extend({
137
357
  return json;
138
358
  },
139
359
 
360
+ /**
361
+ `serializeAttribute` can be used to customize how `DS.attr`
362
+ properties are serialized
363
+
364
+ For example if you wanted to ensure all you attributes were always
365
+ serialized as properties on an `attributes` object you could
366
+ write:
367
+
368
+ ```javascript
369
+ App.ApplicationSerializer = DS.JSONSerializer.extend({
370
+ serializeAttribute: function(record, json, key, attributes) {
371
+ json.attributes = json.attributes || {};
372
+ this._super(record, json.attributes, key, attributes);
373
+ }
374
+ });
375
+ ```
376
+
377
+ @method serializeAttribute
378
+ @param {DS.Model} record
379
+ @param {Object} json
380
+ @param {String} key
381
+ @param {Object} attribute
382
+ */
140
383
  serializeAttribute: function(record, json, key, attribute) {
141
384
  var attrs = get(this, 'attrs');
142
385
  var value = get(record, key), type = attribute.type;
@@ -153,6 +396,31 @@ DS.JSONSerializer = Ember.Object.extend({
153
396
  json[key] = value;
154
397
  },
155
398
 
399
+ /**
400
+ `serializeBelongsTo` can be used to customize how `DS.belongsTo`
401
+ properties are serialized.
402
+
403
+ Example
404
+
405
+ ```javascript
406
+ App.PostSerializer = DS.JSONSerializer.extend({
407
+ serializeBelongsTo: function(record, json, relationship) {
408
+ var key = relationship.key;
409
+
410
+ var belongsTo = get(record, key);
411
+
412
+ key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
413
+
414
+ json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON();
415
+ }
416
+ });
417
+ ```
418
+
419
+ @method serializeBelongsTo
420
+ @param {DS.Model} record
421
+ @param {Object} json
422
+ @param {Object} relationship
423
+ */
156
424
  serializeBelongsTo: function(record, json, relationship) {
157
425
  var key = relationship.key;
158
426
 
@@ -171,6 +439,30 @@ DS.JSONSerializer = Ember.Object.extend({
171
439
  }
172
440
  },
173
441
 
442
+ /**
443
+ `serializeHasMany` can be used to customize how `DS.hasMany`
444
+ properties are serialized.
445
+
446
+ Example
447
+
448
+ ```javascript
449
+ App.PostSerializer = DS.JSONSerializer.extend({
450
+ serializeHasMany: function(record, json, relationship) {
451
+ var key = relationship.key;
452
+ if (key === 'comments') {
453
+ return;
454
+ } else {
455
+ this._super.apply(this, arguments);
456
+ }
457
+ }
458
+ });
459
+ ```
460
+
461
+ @method serializeHasMany
462
+ @param {DS.Model} record
463
+ @param {Object} json
464
+ @param {Object} relationship
465
+ */
174
466
  serializeHasMany: function(record, json, relationship) {
175
467
  var key = relationship.key;
176
468
 
@@ -183,12 +475,65 @@ DS.JSONSerializer = Ember.Object.extend({
183
475
  },
184
476
 
185
477
  /**
186
- You can use this method to customize how polymorphic objects are serialized.
478
+ You can use this method to customize how polymorphic objects are
479
+ serialized. Objects are considered to be polymorphic if
480
+ `{polymorphic: true}` is pass as the second argument to the
481
+ `DS.belongsTo` function.
482
+
483
+ Example
484
+
485
+ ```javascript
486
+ App.CommentSerializer = DS.JSONSerializer.extend({
487
+ serializePolymorphicType: function(record, json, relationship) {
488
+ var key = relationship.key,
489
+ belongsTo = get(record, key);
490
+ key = this.keyForAttribute ? this.keyForAttribute(key) : key;
491
+ json[key + "_type"] = belongsTo.constructor.typeKey;
492
+ }
493
+ });
494
+ ```
495
+
496
+ @method serializePolymorphicType
497
+ @param {DS.Model} record
498
+ @param {Object} json
499
+ @param {Object} relationship
187
500
  */
188
501
  serializePolymorphicType: Ember.K,
189
502
 
190
503
  // EXTRACT
191
504
 
505
+ /**
506
+ The `extract` method is used to deserialize payload data from the
507
+ server. By default the `JSONSerializer` does not push the records
508
+ into the store. However records that subclass `JSONSerializer`
509
+ such as the `RESTSerializer` may push records into the store as
510
+ part of the extract call.
511
+
512
+ This method deletegates to a more specific extract method based on
513
+ the `requestType`.
514
+
515
+ Example
516
+
517
+ ```javascript
518
+ var get = Ember.get;
519
+ socket.on('message', function(message) {
520
+ var modelName = message.model;
521
+ var data = message.data;
522
+ var type = store.modelFor(modelName);
523
+ var serializer = store.serializerFor(type.typeKey);
524
+ var record = serializer.extract(store, type, data, get(data, 'id'), 'single');
525
+ store.push(modelName, record);
526
+ });
527
+ ```
528
+
529
+ @method extract
530
+ @param {DS.Store} store
531
+ @param {subclass of DS.Model} type
532
+ @param {Object} payload
533
+ @param {String or Number} id
534
+ @param {String} requestType
535
+ @return {Object} json The deserialized payload
536
+ */
192
537
  extract: function(store, type, payload, id, requestType) {
193
538
  this.extractMeta(store, type, payload);
194
539
 
@@ -196,27 +541,205 @@ DS.JSONSerializer = Ember.Object.extend({
196
541
  return this[specificExtract](store, type, payload, id, requestType);
197
542
  },
198
543
 
544
+ /**
545
+ `extractFindAll` is a hook into the extract method used when a
546
+ call is made to `DS.Store#findAll`. By default this method is an
547
+ alias for [extractArray](#method_extractArray).
548
+
549
+ @method extractFindAll
550
+ @param {DS.Store} store
551
+ @param {subclass of DS.Model} type
552
+ @param {Object} payload
553
+ @return {Array} array An array of deserialized objects
554
+ */
199
555
  extractFindAll: aliasMethod('extractArray'),
556
+ /**
557
+ `extractFindQuery` is a hook into the extract method used when a
558
+ call is made to `DS.Store#findQuery`. By default this method is an
559
+ alias for [extractArray](#method_extractArray).
560
+
561
+ @method extractFindQuery
562
+ @param {DS.Store} store
563
+ @param {subclass of DS.Model} type
564
+ @param {Object} payload
565
+ @return {Array} array An array of deserialized objects
566
+ */
200
567
  extractFindQuery: aliasMethod('extractArray'),
568
+ /**
569
+ `extractFindMany` is a hook into the extract method used when a
570
+ call is made to `DS.Store#findMany`. By default this method is
571
+ alias for [extractArray](#method_extractArray).
572
+
573
+ @method extractFindMany
574
+ @param {DS.Store} store
575
+ @param {subclass of DS.Model} type
576
+ @param {Object} payload
577
+ @return {Array} array An array of deserialized objects
578
+ */
201
579
  extractFindMany: aliasMethod('extractArray'),
580
+ /**
581
+ `extractFindHasMany` is a hook into the extract method used when a
582
+ call is made to `DS.Store#findHasMany`. By default this method is
583
+ alias for [extractArray](#method_extractArray).
584
+
585
+ @method extractFindHasMany
586
+ @param {DS.Store} store
587
+ @param {subclass of DS.Model} type
588
+ @param {Object} payload
589
+ @return {Array} array An array of deserialized objects
590
+ */
202
591
  extractFindHasMany: aliasMethod('extractArray'),
203
592
 
593
+ /**
594
+ `extractCreateRecord` is a hook into the extract method used when a
595
+ call is made to `DS.Store#createRecord`. By default this method is
596
+ alias for [extractSave](#method_extractSave).
597
+
598
+ @method extractCreateRecord
599
+ @param {DS.Store} store
600
+ @param {subclass of DS.Model} type
601
+ @param {Object} payload
602
+ @return {Object} json The deserialized payload
603
+ */
204
604
  extractCreateRecord: aliasMethod('extractSave'),
605
+ /**
606
+ `extractUpdateRecord` is a hook into the extract method used when
607
+ a call is made to `DS.Store#update`. By default this method is alias
608
+ for [extractSave](#method_extractSave).
609
+
610
+ @method extractUpdateRecord
611
+ @param {DS.Store} store
612
+ @param {subclass of DS.Model} type
613
+ @param {Object} payload
614
+ @return {Object} json The deserialized payload
615
+ */
205
616
  extractUpdateRecord: aliasMethod('extractSave'),
617
+ /**
618
+ `extractDeleteRecord` is a hook into the extract method used when
619
+ a call is made to `DS.Store#deleteRecord`. By default this method is
620
+ alias for [extractSave](#method_extractSave).
621
+
622
+ @method extractDeleteRecord
623
+ @param {DS.Store} store
624
+ @param {subclass of DS.Model} type
625
+ @param {Object} payload
626
+ @return {Object} json The deserialized payload
627
+ */
206
628
  extractDeleteRecord: aliasMethod('extractSave'),
207
629
 
630
+ /**
631
+ `extractFind` is a hook into the extract method used when
632
+ a call is made to `DS.Store#find`. By default this method is
633
+ alias for [extractSingle](#method_extractSingle).
634
+
635
+ @method extractFind
636
+ @param {DS.Store} store
637
+ @param {subclass of DS.Model} type
638
+ @param {Object} payload
639
+ @return {Object} json The deserialized payload
640
+ */
208
641
  extractFind: aliasMethod('extractSingle'),
642
+ /**
643
+ `extractFindBelongsTo` is a hook into the extract method used when
644
+ a call is made to `DS.Store#findBelongsTo`. By default this method is
645
+ alias for [extractSingle](#method_extractSingle).
646
+
647
+ @method extractFindBelongsTo
648
+ @param {DS.Store} store
649
+ @param {subclass of DS.Model} type
650
+ @param {Object} payload
651
+ @return {Object} json The deserialized payload
652
+ */
209
653
  extractFindBelongsTo: aliasMethod('extractSingle'),
654
+ /**
655
+ `extractSave` is a hook into the extract method used when a call
656
+ is made to `DS.Model#save`. By default this method is alias
657
+ for [extractSingle](#method_extractSingle).
658
+
659
+ @method extractSave
660
+ @param {DS.Store} store
661
+ @param {subclass of DS.Model} type
662
+ @param {Object} payload
663
+ @return {Object} json The deserialized payload
664
+ */
210
665
  extractSave: aliasMethod('extractSingle'),
211
666
 
667
+ /**
668
+ `extractSingle` is used to deserialize a single record returned
669
+ from the adapter.
670
+
671
+ Example
672
+
673
+ ```javascript
674
+ App.PostSerializer = DS.JSONSerializer.extend({
675
+ extractSingle: function(store, type, payload) {
676
+ payload.comments = payload._embedded.comment;
677
+ delete payload._embedded;
678
+
679
+ return this._super(store, type, payload);
680
+ },
681
+ });
682
+ ```
683
+
684
+ @method extractSingle
685
+ @param {DS.Store} store
686
+ @param {subclass of DS.Model} type
687
+ @param {Object} payload
688
+ @return {Object} json The deserialized payload
689
+ */
212
690
  extractSingle: function(store, type, payload) {
213
691
  return this.normalize(type, payload);
214
692
  },
215
693
 
694
+ /**
695
+ `extractArray` is used to deserialize an array of records
696
+ returned from the adapter.
697
+
698
+ Example
699
+
700
+ ```javascript
701
+ App.PostSerializer = DS.JSONSerializer.extend({
702
+ extractArray: function(store, type, payload) {
703
+ return payload.map(function(json) {
704
+ return this.extractSingle(json);
705
+ }, this);
706
+ }
707
+ });
708
+ ```
709
+
710
+ @method extractArray
711
+ @param {DS.Store} store
712
+ @param {subclass of DS.Model} type
713
+ @param {Object} payload
714
+ @return {Array} array An array of deserialized objects
715
+ */
216
716
  extractArray: function(store, type, payload) {
217
717
  return this.normalize(type, payload);
218
718
  },
219
719
 
720
+ /**
721
+ `extractMeta` is used to deserialize any meta information in the
722
+ adapter payload. By default Ember Data expects meta information to
723
+ be located on the `meta` property of the payload object.
724
+
725
+ Example
726
+
727
+ ```javascript
728
+ App.PostSerializer = DS.JSONSerializer.extend({
729
+ extractMeta: function(store, type, payload) {
730
+ if (payload && payload._pagination) {
731
+ store.metaForType(type, payload._pagination);
732
+ delete payload._pagination;
733
+ }
734
+ }
735
+ });
736
+ ```
737
+
738
+ @method extractMeta
739
+ @param {DS.Store} store
740
+ @param {subclass of DS.Model} type
741
+ @param {Object} payload
742
+ */
220
743
  extractMeta: function(store, type, payload) {
221
744
  if (payload && payload.meta) {
222
745
  store.metaForType(type, payload.meta);
@@ -224,8 +747,36 @@ DS.JSONSerializer = Ember.Object.extend({
224
747
  }
225
748
  },
226
749
 
750
+ /**
751
+ `keyForRelationship` can be used to define a custom key when
752
+ serializeing relationship properties. By default `JSONSerializer`
753
+ does not provide an implementation of this method.
754
+
755
+ Example
756
+
757
+ ```javascript
758
+ App.PostSerializer = DS.JSONSerializer.extend({
759
+ keyForRelationship: function(key, relationship) {
760
+ return 'rel_' + Ember.String.underscore(key);
761
+ }
762
+ });
763
+ ```
764
+
765
+ @method keyForRelationship
766
+ @param {String} key
767
+ @param {String} relationship type
768
+ @return {String} normalized key
769
+ */
770
+
227
771
  // HELPERS
228
772
 
773
+ /**
774
+ @method transformFor
775
+ @private
776
+ @param {String} attributeType
777
+ @param {Boolean} skipAssertion
778
+ @return {DS.Transform} transform
779
+ */
229
780
  transformFor: function(attributeType, skipAssertion) {
230
781
  var transform = this.container.lookup('transform:' + attributeType);
231
782
 
@@ -353,19 +904,105 @@ DS.DebugAdapter = Ember.DataAdapter.extend({
353
904
 
354
905
 
355
906
  (function() {
907
+ /**
908
+ The `DS.Transform` class is used to serialize and deserialize model
909
+ attributes when they are saved or loaded from an
910
+ adapter. Subclassing `DS.Transform` is useful for creating custom
911
+ attributes. All subclasses of `DS.Transform` must implement a
912
+ `serialize` and a `deserialize` method.
913
+
914
+ Example
915
+
916
+ ```javascript
917
+ App.RawTransform = DS.Transform.extend({
918
+ deserialize: function(serialized) {
919
+ return serialized;
920
+ },
921
+ serialize: function(deserialized) {
922
+ return deserialized;
923
+ }
924
+ });
925
+ ```
926
+
927
+ Usage
928
+
929
+ ```javascript
930
+ var attr = DS.attr;
931
+ App.Requirement = DS.Model.extend({
932
+ name: attr('string'),
933
+ optionsArray: attr('raw')
934
+ });
935
+ ```
936
+
937
+ @class Transform
938
+ @namespace DS
939
+ */
356
940
  DS.Transform = Ember.Object.extend({
941
+ /**
942
+ When given a deserialized value from a record attribute this
943
+ method must return the serialized value.
357
944
 
945
+ Example
946
+
947
+ ```javascript
948
+ serialize: function(deserialized) {
949
+ return Ember.isEmpty(deserialized) ? null : Number(deserialized);
950
+ }
951
+ ```
952
+
953
+ @method serialize
954
+ @param deserialized The deserialized value
955
+ @return The serialized value
956
+ */
358
957
  serialize: Ember.required(),
359
-
958
+
959
+ /**
960
+ When given a serialize value from a JSON object this method must
961
+ return the deserialized value for the record attribute.
962
+
963
+ Example
964
+
965
+ ```javascript
966
+ deserialize: function(serialized) {
967
+ return empty(serialized) ? null : Number(serialized);
968
+ }
969
+ ```
970
+
971
+ @method deserialized
972
+ @param serialized The serialized value
973
+ @return The deserialized value
974
+ */
360
975
  deserialize: Ember.required()
361
976
 
362
977
  });
978
+
363
979
  })();
364
980
 
365
981
 
366
982
 
367
983
  (function() {
368
984
 
985
+ /**
986
+ The `DS.BooleanTransform` class is used to serialize and deserialize
987
+ boolean attributes on Ember Data record objects. This transform is
988
+ used when `boolean` is passed as the type parameter to the
989
+ [DS.attr](../../data#method_attr) function.
990
+
991
+ Usage
992
+
993
+ ```javascript
994
+ var attr = DS.attr;
995
+ App.User = DS.Model.extend({
996
+ isAdmin: attr('boolean'),
997
+ name: attr('string'),
998
+ email: attr('string')
999
+ });
1000
+ ```
1001
+
1002
+ @class BooleanTransform
1003
+ @extends DS.Transform
1004
+ @namespace DS
1005
+ */
369
1006
  DS.BooleanTransform = DS.Transform.extend({
370
1007
  deserialize: function(serialized) {
371
1008
  var type = typeof serialized;
@@ -391,6 +1028,25 @@ DS.BooleanTransform = DS.Transform.extend({
391
1028
 
392
1029
 
393
1030
  (function() {
1031
+ /**
1032
+ The `DS.DateTransform` class is used to serialize and deserialize
1033
+ date attributes on Ember Data record objects. This transform is used
1034
+ when `date` is passed as the type parameter to the
1035
+ [DS.attr](../../data#method_attr) function.
1036
+
1037
+ ```javascript
1038
+ var attr = DS.attr;
1039
+ App.Score = DS.Model.extend({
1040
+ value: attr('number'),
1041
+ player: DS.belongsTo('player'),
1042
+ date: attr('date')
1043
+ });
1044
+ ```
1045
+
1046
+ @class DateTransform
1047
+ @extends DS.Transform
1048
+ @namespace DS
1049
+ */
394
1050
  DS.DateTransform = DS.Transform.extend({
395
1051
 
396
1052
  deserialize: function(serialized) {
@@ -446,7 +1102,27 @@ DS.DateTransform = DS.Transform.extend({
446
1102
 
447
1103
  (function() {
448
1104
  var empty = Ember.isEmpty;
1105
+ /**
1106
+ The `DS.NumberTransform` class is used to serialize and deserialize
1107
+ numeric attributes on Ember Data record objects. This transform is
1108
+ used when `number` is passed as the type parameter to the
1109
+ [DS.attr](../../data#method_attr) function.
1110
+
1111
+ Usage
1112
+
1113
+ ```javascript
1114
+ var attr = DS.attr;
1115
+ App.Score = DS.Model.extend({
1116
+ value: attr('number'),
1117
+ player: DS.belongsTo('player'),
1118
+ date: attr('date')
1119
+ });
1120
+ ```
449
1121
 
1122
+ @class NumberTransform
1123
+ @extends DS.Transform
1124
+ @namespace DS
1125
+ */
450
1126
  DS.NumberTransform = DS.Transform.extend({
451
1127
 
452
1128
  deserialize: function(serialized) {
@@ -457,6 +1133,7 @@ DS.NumberTransform = DS.Transform.extend({
457
1133
  return empty(deserialized) ? null : Number(deserialized);
458
1134
  }
459
1135
  });
1136
+
460
1137
  })();
461
1138
 
462
1139
 
@@ -464,6 +1141,27 @@ DS.NumberTransform = DS.Transform.extend({
464
1141
  (function() {
465
1142
  var none = Ember.isNone;
466
1143
 
1144
+ /**
1145
+ The `DS.StringTransform` class is used to serialize and deserialize
1146
+ string attributes on Ember Data record objects. This transform is
1147
+ used when `string` is passed as the type parameter to the
1148
+ [DS.attr](../../data#method_attr) function.
1149
+
1150
+ Usage
1151
+
1152
+ ```javascript
1153
+ var attr = DS.attr;
1154
+ App.User = DS.Model.extend({
1155
+ isAdmin: attr('boolean'),
1156
+ name: attr('string'),
1157
+ email: attr('string')
1158
+ });
1159
+ ```
1160
+
1161
+ @class StringTransform
1162
+ @extends DS.Transform
1163
+ @namespace DS
1164
+ */
467
1165
  DS.StringTransform = DS.Transform.extend({
468
1166
 
469
1167
  deserialize: function(serialized) {
@@ -541,6 +1239,7 @@ Ember.onLoad('Ember.Application', function(Application) {
541
1239
 
542
1240
  Application.initializer({
543
1241
  name: "transforms",
1242
+ before: "store",
544
1243
 
545
1244
  initialize: function(container, application) {
546
1245
  application.register('transform:boolean', DS.BooleanTransform);
@@ -552,6 +1251,7 @@ Ember.onLoad('Ember.Application', function(Application) {
552
1251
 
553
1252
  Application.initializer({
554
1253
  name: "dataAdapter",
1254
+ before: "store",
555
1255
 
556
1256
  initialize: function(container, application) {
557
1257
  application.register('dataAdapter:main', DS.DebugAdapter);
@@ -560,6 +1260,7 @@ Ember.onLoad('Ember.Application', function(Application) {
560
1260
 
561
1261
  Application.initializer({
562
1262
  name: "injectStore",
1263
+ before: "store",
563
1264
 
564
1265
  initialize: function(container, application) {
565
1266
  application.inject('controller', 'store', 'store:main');
@@ -712,9 +1413,10 @@ DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
712
1413
  },
713
1414
 
714
1415
  save: function() {
715
- var promise = Ember.RSVP.all(this.invoke("save")).then(function(array) {
1416
+ var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
1417
+ var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) {
716
1418
  return Ember.A(array);
717
- });
1419
+ }, null, "DS: RecordArray#save apply Ember.NativeArray");
718
1420
 
719
1421
  return DS.PromiseArray.create({ promise: promise });
720
1422
  }
@@ -844,6 +1546,14 @@ DS.ManyArray = DS.RecordArray.extend({
844
1546
  this._changesToSync = Ember.OrderedSet.create();
845
1547
  },
846
1548
 
1549
+ /**
1550
+ The property name of the relationship
1551
+
1552
+ @property {String}
1553
+ @private
1554
+ */
1555
+ name: null,
1556
+
847
1557
  /**
848
1558
  The record to which this relationship belongs.
849
1559
 
@@ -864,6 +1574,15 @@ DS.ManyArray = DS.RecordArray.extend({
864
1574
 
865
1575
  isLoaded: false,
866
1576
 
1577
+ /**
1578
+ Used for async `hasMany` arrays
1579
+ to keep track of when they will resolve.
1580
+
1581
+ @property {Ember.RSVP.Promise}
1582
+ @private
1583
+ */
1584
+ promise: null,
1585
+
867
1586
  loadingRecordsCount: function(count) {
868
1587
  this.loadingRecordsCount = count;
869
1588
  },
@@ -880,7 +1599,7 @@ DS.ManyArray = DS.RecordArray.extend({
880
1599
  var records = get(this, 'content'),
881
1600
  store = get(this, 'store'),
882
1601
  owner = get(this, 'owner'),
883
- resolver = Ember.RSVP.defer();
1602
+ resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type'));
884
1603
 
885
1604
  var unloadedRecords = records.filterProperty('isEmpty', true);
886
1605
  store.fetchMany(unloadedRecords, owner, resolver);
@@ -1000,160 +1719,6 @@ DS.ManyArray = DS.RecordArray.extend({
1000
1719
 
1001
1720
 
1002
1721
 
1003
- (function() {
1004
- /**
1005
- @module ember-data
1006
- */
1007
-
1008
- var get = Ember.get;
1009
- var forEach = Ember.ArrayPolyfills.forEach;
1010
-
1011
- var resolveMapConflict = function(oldValue, newValue) {
1012
- return oldValue;
1013
- };
1014
-
1015
- var transformMapKey = function(key, value) {
1016
- return key;
1017
- };
1018
-
1019
- var transformMapValue = function(key, value) {
1020
- return value;
1021
- };
1022
-
1023
- /**
1024
- The Mappable mixin is designed for classes that would like to
1025
- behave as a map for configuration purposes.
1026
-
1027
- For example, the DS.Adapter class can behave like a map, with
1028
- more semantic API, via the `map` API:
1029
-
1030
- DS.Adapter.map('App.Person', { firstName: { key: 'FIRST' } });
1031
-
1032
- Class configuration via a map-like API has a few common requirements
1033
- that differentiate it from the standard Ember.Map implementation.
1034
-
1035
- First, values often are provided as strings that should be normalized
1036
- into classes the first time the configuration options are used.
1037
-
1038
- Second, the values configured on parent classes should also be taken
1039
- into account.
1040
-
1041
- Finally, setting the value of a key sometimes should merge with the
1042
- previous value, rather than replacing it.
1043
-
1044
- This mixin provides a instance method, `createInstanceMapFor`, that
1045
- will reify all of the configuration options set on an instance's
1046
- constructor and provide it for the instance to use.
1047
-
1048
- Classes can implement certain hooks that allow them to customize
1049
- the requirements listed above:
1050
-
1051
- * `resolveMapConflict` - called when a value is set for an existing
1052
- value
1053
- * `transformMapKey` - allows a key name (for example, a global path
1054
- to a class) to be normalized
1055
- * `transformMapValue` - allows a value (for example, a class that
1056
- should be instantiated) to be normalized
1057
-
1058
- Classes that implement this mixin should also implement a class
1059
- method built using the `generateMapFunctionFor` method:
1060
-
1061
- DS.Adapter.reopenClass({
1062
- map: DS.Mappable.generateMapFunctionFor('attributes', function(key, newValue, map) {
1063
- var existingValue = map.get(key);
1064
-
1065
- for (var prop in newValue) {
1066
- if (!newValue.hasOwnProperty(prop)) { continue; }
1067
- existingValue[prop] = newValue[prop];
1068
- }
1069
- })
1070
- });
1071
-
1072
- The function passed to `generateMapFunctionFor` is invoked every time a
1073
- new value is added to the map.
1074
-
1075
- @class _Mappable
1076
- @private
1077
- @namespace DS
1078
- **/
1079
- DS._Mappable = Ember.Mixin.create({
1080
- createInstanceMapFor: function(mapName) {
1081
- var instanceMeta = getMappableMeta(this);
1082
-
1083
- instanceMeta.values = instanceMeta.values || {};
1084
-
1085
- if (instanceMeta.values[mapName]) { return instanceMeta.values[mapName]; }
1086
-
1087
- var instanceMap = instanceMeta.values[mapName] = new Ember.Map();
1088
-
1089
- var klass = this.constructor;
1090
-
1091
- while (klass && klass !== DS.Store) {
1092
- this._copyMap(mapName, klass, instanceMap);
1093
- klass = klass.superclass;
1094
- }
1095
-
1096
- instanceMeta.values[mapName] = instanceMap;
1097
- return instanceMap;
1098
- },
1099
-
1100
- _copyMap: function(mapName, klass, instanceMap) {
1101
- var classMeta = getMappableMeta(klass);
1102
-
1103
- var classMap = classMeta[mapName];
1104
- if (classMap) {
1105
- forEach.call(classMap, eachMap, this);
1106
- }
1107
-
1108
- function eachMap(key, value) {
1109
- var transformedKey = (klass.transformMapKey || transformMapKey)(key, value);
1110
- var transformedValue = (klass.transformMapValue || transformMapValue)(key, value);
1111
-
1112
- var oldValue = instanceMap.get(transformedKey);
1113
- var newValue = transformedValue;
1114
-
1115
- if (oldValue) {
1116
- newValue = (this.constructor.resolveMapConflict || resolveMapConflict)(oldValue, newValue);
1117
- }
1118
-
1119
- instanceMap.set(transformedKey, newValue);
1120
- }
1121
- }
1122
-
1123
- });
1124
-
1125
- DS._Mappable.generateMapFunctionFor = function(mapName, transform) {
1126
- return function(key, value) {
1127
- var meta = getMappableMeta(this);
1128
-
1129
- var map = meta[mapName] || Ember.MapWithDefault.create({
1130
- defaultValue: function() { return {}; }
1131
- });
1132
-
1133
- transform.call(this, key, value, map);
1134
-
1135
- meta[mapName] = map;
1136
- };
1137
- };
1138
-
1139
- function getMappableMeta(obj) {
1140
- var meta = Ember.meta(obj, true),
1141
- keyName = 'DS.Mappable',
1142
- value = meta[keyName];
1143
-
1144
- if (!value) { meta[keyName] = {}; }
1145
-
1146
- if (!meta.hasOwnProperty(keyName)) {
1147
- meta[keyName] = Ember.create(meta[keyName]);
1148
- }
1149
-
1150
- return meta[keyName];
1151
- }
1152
-
1153
- })();
1154
-
1155
-
1156
-
1157
1722
  (function() {
1158
1723
  /*globals Ember*/
1159
1724
  /*jshint eqnull:true*/
@@ -1168,6 +1733,7 @@ var forEach = Ember.EnumerableUtils.forEach;
1168
1733
  var indexOf = Ember.EnumerableUtils.indexOf;
1169
1734
  var map = Ember.EnumerableUtils.map;
1170
1735
  var resolve = Ember.RSVP.resolve;
1736
+ var copy = Ember.copy;
1171
1737
 
1172
1738
  // Implementors Note:
1173
1739
  //
@@ -1202,7 +1768,9 @@ var coerceId = function(id) {
1202
1768
 
1203
1769
  Define your application's store like this:
1204
1770
 
1205
- MyApp.Store = DS.Store.extend();
1771
+ ```javascript
1772
+ MyApp.Store = DS.Store.extend();
1773
+ ```
1206
1774
 
1207
1775
  Most Ember.js applications will only have a single `DS.Store` that is
1208
1776
  automatically created by their `Ember.Application`.
@@ -1210,12 +1778,16 @@ var coerceId = function(id) {
1210
1778
  You can retrieve models from the store in several ways. To retrieve a record
1211
1779
  for a specific id, use `DS.Model`'s `find()` method:
1212
1780
 
1213
- var person = App.Person.find(123);
1781
+ ```javascript
1782
+ var person = App.Person.find(123);
1783
+ ```
1214
1784
 
1215
1785
  If your application has multiple `DS.Store` instances (an unusual case), you can
1216
1786
  specify which store should be used:
1217
1787
 
1218
- var person = store.find(App.Person, 123);
1788
+ ```javascript
1789
+ var person = store.find(App.Person, 123);
1790
+ ```
1219
1791
 
1220
1792
  In general, you should retrieve models using the methods on `DS.Model`; you should
1221
1793
  rarely need to interact with the store directly.
@@ -1223,9 +1795,11 @@ var coerceId = function(id) {
1223
1795
  By default, the store will talk to your backend using a standard REST mechanism.
1224
1796
  You can customize how the store talks to your backend by specifying a custom adapter:
1225
1797
 
1226
- MyApp.store = DS.Store.create({
1227
- adapter: 'MyApp.CustomAdapter'
1228
- });
1798
+ ```javascript
1799
+ MyApp.store = DS.Store.create({
1800
+ adapter: 'MyApp.CustomAdapter'
1801
+ });
1802
+ ```
1229
1803
 
1230
1804
  You can learn more about writing a custom adapter by reading the `DS.Adapter`
1231
1805
  documentation.
@@ -1233,9 +1807,8 @@ var coerceId = function(id) {
1233
1807
  @class Store
1234
1808
  @namespace DS
1235
1809
  @extends Ember.Object
1236
- @uses DS._Mappable
1237
1810
  */
1238
- DS.Store = Ember.Object.extend(DS._Mappable, {
1811
+ DS.Store = Ember.Object.extend({
1239
1812
 
1240
1813
  /**
1241
1814
  @method init
@@ -1301,7 +1874,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1301
1874
  @private
1302
1875
  @returns DS.Adapter
1303
1876
  */
1304
- defaultAdapter: Ember.computed(function() {
1877
+ defaultAdapter: Ember.computed('adapter', function() {
1305
1878
  var adapter = get(this, 'adapter');
1306
1879
 
1307
1880
 
@@ -1314,7 +1887,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1314
1887
  }
1315
1888
 
1316
1889
  return adapter;
1317
- }).property('adapter'),
1890
+ }),
1318
1891
 
1319
1892
  // .....................
1320
1893
  // . CREATE NEW RECORD .
@@ -1341,7 +1914,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1341
1914
  createRecord: function(type, properties) {
1342
1915
  type = this.modelFor(type);
1343
1916
 
1344
- properties = properties || {};
1917
+ properties = copy(properties) || {};
1345
1918
 
1346
1919
  // If the passed properties do not include a primary key,
1347
1920
  // give the adapter an opportunity to generate one. Typically,
@@ -1421,7 +1994,9 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1421
1994
 
1422
1995
  To find a record by ID, pass the `id` as the second parameter:
1423
1996
 
1424
- store.find('person', 1);
1997
+ ```javascript
1998
+ store.find('person', 1);
1999
+ ```
1425
2000
 
1426
2001
  The `find` method will always return a **promise** that will be resolved
1427
2002
  with the record. If the record was already in the store, the promise will
@@ -1435,7 +2010,9 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1435
2010
 
1436
2011
  To find all records for a type, call `find` with no additional parameters:
1437
2012
 
1438
- store.find('person');
2013
+ ```javascript
2014
+ store.find('person');
2015
+ ```
1439
2016
 
1440
2017
  This will ask the adapter's `findAll` method to find the records for the
1441
2018
  given type, and return a promise that will be resolved once the server
@@ -1446,7 +2023,9 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1446
2023
  To find a record by a query, call `find` with a hash as the second
1447
2024
  parameter:
1448
2025
 
1449
- store.find(App.Person, { page: 1 });
2026
+ ```javascript
2027
+ store.find(App.Person, { page: 1 });
2028
+ ```
1450
2029
 
1451
2030
  This will ask the adapter's `findQuery` method to find the records for
1452
2031
  the query, and return a promise that will be resolved once the server
@@ -1482,7 +2061,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1482
2061
 
1483
2062
  var record = this.recordForId(type, id);
1484
2063
 
1485
- var promise = this.fetchRecord(record) || resolve(record);
2064
+ var promise = this.fetchRecord(record) || resolve(record, "DS: Store#findById " + type + " with id: " + id);
1486
2065
  return promiseObject(promise);
1487
2066
  },
1488
2067
 
@@ -1490,6 +2069,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1490
2069
  This method makes a series of requests to the adapter's `find` method
1491
2070
  and returns a promise that resolves once they are all loaded.
1492
2071
 
2072
+ @private
1493
2073
  @method findByIds
1494
2074
  @param {String} type
1495
2075
  @param {Array} ids
@@ -1497,12 +2077,10 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1497
2077
  */
1498
2078
  findByIds: function(type, ids) {
1499
2079
  var store = this;
1500
-
2080
+ var promiseLabel = "DS: Store#findByIds " + type;
1501
2081
  return promiseArray(Ember.RSVP.all(map(ids, function(id) {
1502
2082
  return store.findById(type, id);
1503
- })).then(function(array) {
1504
- return Ember.A(array);
1505
- }));
2083
+ })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete"));
1506
2084
  },
1507
2085
 
1508
2086
  /**
@@ -1522,7 +2100,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1522
2100
 
1523
2101
  var type = record.constructor,
1524
2102
  id = get(record, 'id'),
1525
- resolver = Ember.RSVP.defer();
2103
+ resolver = Ember.RSVP.defer("DS: Store#fetchRecord " + record );
1526
2104
 
1527
2105
  record.loadingData(resolver.promise);
1528
2106
 
@@ -1530,7 +2108,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1530
2108
 
1531
2109
 
1532
2110
 
1533
- _find(adapter, this, type, id, resolver);
2111
+ resolver.resolve(_find(adapter, this, type, id));
1534
2112
 
1535
2113
  return resolver.promise;
1536
2114
  },
@@ -1550,8 +2128,6 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1550
2128
  @param id
1551
2129
  */
1552
2130
  getById: function(type, id) {
1553
- type = this.modelFor(type);
1554
-
1555
2131
  if (this.hasRecordForId(type, id)) {
1556
2132
  return this.recordForId(type, id);
1557
2133
  } else {
@@ -1572,7 +2148,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1572
2148
  @param {DS.Model} record
1573
2149
  @param {Resolver} resolver
1574
2150
  */
1575
- reloadRecord: function(record, resolver) {
2151
+ reloadRecord: function(record) {
1576
2152
  var type = record.constructor,
1577
2153
  adapter = this.adapterFor(type),
1578
2154
  id = get(record, 'id');
@@ -1580,7 +2156,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1580
2156
 
1581
2157
 
1582
2158
 
1583
- return _find(adapter, this, type, id, resolver);
2159
+ return _find(adapter, this, type, id);
1584
2160
  },
1585
2161
 
1586
2162
  /**
@@ -1618,7 +2194,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1618
2194
 
1619
2195
 
1620
2196
 
1621
- _findMany(adapter, this, type, ids, owner, resolver);
2197
+ resolver.resolve(_findMany(adapter, this, type, ids, owner));
1622
2198
  }, this);
1623
2199
  },
1624
2200
 
@@ -1632,7 +2208,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1632
2208
  */
1633
2209
  hasRecordForId: function(type, id) {
1634
2210
  id = coerceId(id);
1635
-
2211
+ type = this.modelFor(type);
1636
2212
  return !!this.typeMapFor(type).idToRecord[id];
1637
2213
  },
1638
2214
 
@@ -1722,7 +2298,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1722
2298
 
1723
2299
 
1724
2300
  var records = this.recordArrayManager.createManyArray(relationship.type, Ember.A([]));
1725
- _findHasMany(adapter, this, owner, link, relationship, resolver);
2301
+ resolver.resolve(_findHasMany(adapter, this, owner, link, relationship));
1726
2302
  return records;
1727
2303
  },
1728
2304
 
@@ -1731,7 +2307,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1731
2307
 
1732
2308
 
1733
2309
 
1734
- _findBelongsTo(adapter, this, owner, link, relationship, resolver);
2310
+ resolver.resolve(_findBelongsTo(adapter, this, owner, link, relationship));
1735
2311
  },
1736
2312
 
1737
2313
  /**
@@ -1762,11 +2338,12 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1762
2338
  });
1763
2339
 
1764
2340
  var adapter = this.adapterFor(type),
1765
- resolver = Ember.RSVP.defer();
2341
+ promiseLabel = "DS: Store#findQuery " + type,
2342
+ resolver = Ember.RSVP.defer(promiseLabel);
1766
2343
 
1767
2344
 
1768
2345
 
1769
- _findQuery(adapter, this, type, query, array, resolver);
2346
+ resolver.resolve(_findQuery(adapter, this, type, query, array));
1770
2347
 
1771
2348
  return promiseArray(resolver.promise);
1772
2349
  },
@@ -1797,13 +2374,13 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1797
2374
  fetchAll: function(type, array) {
1798
2375
  var adapter = this.adapterFor(type),
1799
2376
  sinceToken = this.typeMapFor(type).metadata.since,
1800
- resolver = Ember.RSVP.defer();
2377
+ resolver = Ember.RSVP.defer("DS: Store#findAll " + type);
1801
2378
 
1802
2379
  set(array, 'isUpdating', true);
1803
2380
 
1804
2381
 
1805
2382
 
1806
- _findAll(adapter, this, type, sinceToken, resolver);
2383
+ resolver.resolve(_findAll(adapter, this, type, sinceToken));
1807
2384
 
1808
2385
  return promiseArray(resolver.promise);
1809
2386
  },
@@ -1868,6 +2445,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1868
2445
  while(record = records.pop()) {
1869
2446
  record.unloadRecord();
1870
2447
  }
2448
+
2449
+ typeMap.findAllCache = null;
1871
2450
  },
1872
2451
 
1873
2452
  /**
@@ -1886,8 +2465,13 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1886
2465
  filter function will be invoked again to determine whether it should
1887
2466
  still be in the array.
1888
2467
 
2468
+ Optionally you can pass a query which will be triggered at first. The
2469
+ results returned by the server could then appear in the filter if they
2470
+ match the filter function.
2471
+
1889
2472
  @method filter
1890
2473
  @param {Class} type
2474
+ @param {Object} query optional query
1891
2475
  @param {Function} filter
1892
2476
  @return {DS.FilteredRecordArray}
1893
2477
  */
@@ -1914,7 +2498,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1914
2498
  this.recordArrayManager.registerFilteredRecordArray(array, type, filter);
1915
2499
 
1916
2500
  if (promise) {
1917
- return promise.then(function() { return array; });
2501
+ return promise.then(function() { return array; }, null, "DS: Store#filter of " + type);
1918
2502
  } else {
1919
2503
  return array;
1920
2504
  }
@@ -1926,7 +2510,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1926
2510
  will result in a request or that it will be a cache hit.
1927
2511
 
1928
2512
  @method recordIsLoaded
1929
- @param {Class} type
2513
+ @param type
1930
2514
  @param {string} id
1931
2515
  @return {boolean}
1932
2516
  */
@@ -1955,8 +2539,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1955
2539
  If the adapter updates attributes or acknowledges creation
1956
2540
  or deletion, the record will notify the store to update its
1957
2541
  membership in any filters.
1958
-
1959
2542
  To avoid thrashing, this method is invoked only once per
2543
+
1960
2544
  run loop per record.
1961
2545
 
1962
2546
  @method dataWasUpdated
@@ -1966,20 +2550,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1966
2550
  @param {DS.Model} record
1967
2551
  */
1968
2552
  dataWasUpdated: function(type, record) {
1969
- // Because data updates are invoked at the end of the run loop,
1970
- // it is possible that a record might be deleted after its data
1971
- // has been modified and this method was scheduled to be called.
1972
- //
1973
- // If that's the case, the record would have already been removed
1974
- // from all record arrays; calling updateRecordArrays would just
1975
- // add it back. If the record is deleted, just bail. It shouldn't
1976
- // give us any more trouble after this.
1977
-
1978
- if (get(record, 'isDeleted')) { return; }
1979
-
1980
- if (get(record, 'isLoaded')) {
1981
- this.recordArrayManager.recordDidChange(record);
1982
- }
2553
+ this.recordArrayManager.recordDidChange(record);
1983
2554
  },
1984
2555
 
1985
2556
  // ..............
@@ -2027,7 +2598,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2027
2598
  operation = 'updateRecord';
2028
2599
  }
2029
2600
 
2030
- _commit(adapter, this, operation, record, resolver);
2601
+ resolver.resolve(_commit(adapter, this, operation, record));
2031
2602
  }, this);
2032
2603
  },
2033
2604
 
@@ -2167,7 +2738,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2167
2738
 
2168
2739
  if (typeof key === 'string') {
2169
2740
  factory = this.container.lookupFactory('model:' + key);
2170
-
2741
+ if (!factory) { throw new Ember.Error("No model was found for '" + key + "'"); }
2171
2742
  factory.typeKey = key;
2172
2743
  } else {
2173
2744
  // A factory already supplied.
@@ -2283,13 +2854,20 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2283
2854
  store.pushPayload('post', pushData);
2284
2855
  ```
2285
2856
 
2286
- @method push
2857
+ @method pushPayload
2287
2858
  @param {String} type
2288
2859
  @param {Object} payload
2289
2860
  */
2290
2861
 
2291
2862
  pushPayload: function (type, payload) {
2292
- var serializer = this.serializerFor(type);
2863
+ var serializer;
2864
+ if (!payload) {
2865
+ payload = type;
2866
+ serializer = defaultSerializer(this.container);
2867
+
2868
+ } else {
2869
+ serializer = this.serializerFor(type);
2870
+ }
2293
2871
  serializer.pushPayload(this, payload);
2294
2872
  },
2295
2873
 
@@ -2583,6 +3161,11 @@ function serializerFor(container, type, defaultSerializer) {
2583
3161
  container.lookup('serializer:_default');
2584
3162
  }
2585
3163
 
3164
+ function defaultSerializer(container) {
3165
+ return container.lookup('serializer:application') ||
3166
+ container.lookup('serializer:_default');
3167
+ }
3168
+
2586
3169
  function serializerForAdapter(adapter, type) {
2587
3170
  var serializer = adapter.serializer,
2588
3171
  defaultSerializer = adapter.defaultSerializer,
@@ -2601,11 +3184,11 @@ function serializerForAdapter(adapter, type) {
2601
3184
  return serializer;
2602
3185
  }
2603
3186
 
2604
- function _find(adapter, store, type, id, resolver) {
3187
+ function _find(adapter, store, type, id) {
2605
3188
  var promise = adapter.find(store, type, id),
2606
3189
  serializer = serializerForAdapter(adapter, type);
2607
3190
 
2608
- return resolve(promise).then(function(payload) {
3191
+ return resolve(promise, "DS: Handle Adapter#find of " + type + " with id: " + id).then(function(payload) {
2609
3192
 
2610
3193
  payload = serializer.extract(store, type, payload, id, 'find');
2611
3194
 
@@ -2614,75 +3197,75 @@ function _find(adapter, store, type, id, resolver) {
2614
3197
  var record = store.getById(type, id);
2615
3198
  record.notFound();
2616
3199
  throw error;
2617
- }).then(resolver.resolve, resolver.reject);
3200
+ }, "DS: Extract payload of '" + type + "'");
2618
3201
  }
2619
3202
 
2620
- function _findMany(adapter, store, type, ids, owner, resolver) {
3203
+ function _findMany(adapter, store, type, ids, owner) {
2621
3204
  var promise = adapter.findMany(store, type, ids, owner),
2622
3205
  serializer = serializerForAdapter(adapter, type);
2623
3206
 
2624
- return resolve(promise).then(function(payload) {
3207
+ return resolve(promise, "DS: Handle Adapter#findMany of " + type).then(function(payload) {
2625
3208
  payload = serializer.extract(store, type, payload, null, 'findMany');
2626
3209
 
2627
3210
 
2628
3211
  store.pushMany(type, payload);
2629
- }).then(resolver.resolve, resolver.reject);
3212
+ }, null, "DS: Extract payload of " + type);
2630
3213
  }
2631
3214
 
2632
- function _findHasMany(adapter, store, record, link, relationship, resolver) {
3215
+ function _findHasMany(adapter, store, record, link, relationship) {
2633
3216
  var promise = adapter.findHasMany(store, record, link, relationship),
2634
3217
  serializer = serializerForAdapter(adapter, relationship.type);
2635
3218
 
2636
- return resolve(promise).then(function(payload) {
3219
+ return resolve(promise, "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type).then(function(payload) {
2637
3220
  payload = serializer.extract(store, relationship.type, payload, null, 'findHasMany');
2638
3221
 
2639
3222
 
2640
3223
  var records = store.pushMany(relationship.type, payload);
2641
3224
  record.updateHasMany(relationship.key, records);
2642
- }).then(resolver.resolve, resolver.reject);
3225
+ }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type);
2643
3226
  }
2644
3227
 
2645
- function _findBelongsTo(adapter, store, record, link, relationship, resolver) {
3228
+ function _findBelongsTo(adapter, store, record, link, relationship) {
2646
3229
  var promise = adapter.findBelongsTo(store, record, link, relationship),
2647
3230
  serializer = serializerForAdapter(adapter, relationship.type);
2648
3231
 
2649
- return resolve(promise).then(function(payload) {
3232
+ return resolve(promise, "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type).then(function(payload) {
2650
3233
  payload = serializer.extract(store, relationship.type, payload, null, 'findBelongsTo');
2651
3234
 
2652
3235
  var record = store.push(relationship.type, payload);
2653
3236
  record.updateBelongsTo(relationship.key, record);
2654
3237
  return record;
2655
- }).then(resolver.resolve, resolver.reject);
3238
+ }, null, "DS: Extract payload of " + record + " : " + relationship.type);
2656
3239
  }
2657
3240
 
2658
- function _findAll(adapter, store, type, sinceToken, resolver) {
3241
+ function _findAll(adapter, store, type, sinceToken) {
2659
3242
  var promise = adapter.findAll(store, type, sinceToken),
2660
3243
  serializer = serializerForAdapter(adapter, type);
2661
3244
 
2662
- return resolve(promise).then(function(payload) {
3245
+ return resolve(promise, "DS: Handle Adapter#findAll of " + type).then(function(payload) {
2663
3246
  payload = serializer.extract(store, type, payload, null, 'findAll');
2664
3247
 
2665
3248
 
2666
3249
  store.pushMany(type, payload);
2667
3250
  store.didUpdateAll(type);
2668
3251
  return store.all(type);
2669
- }).then(resolver.resolve, resolver.reject);
3252
+ }, null, "DS: Extract payload of findAll " + type);
2670
3253
  }
2671
3254
 
2672
- function _findQuery(adapter, store, type, query, recordArray, resolver) {
3255
+ function _findQuery(adapter, store, type, query, recordArray) {
2673
3256
  var promise = adapter.findQuery(store, type, query, recordArray),
2674
3257
  serializer = serializerForAdapter(adapter, type);
2675
3258
 
2676
- return resolve(promise).then(function(payload) {
3259
+ return resolve(promise, "DS: Handle Adapter#findQuery of " + type).then(function(payload) {
2677
3260
  payload = serializer.extract(store, type, payload, null, 'findAll');
2678
3261
 
2679
3262
 
2680
3263
  recordArray.load(payload);
2681
3264
  return recordArray;
2682
- }).then(resolver.resolve, resolver.reject);
3265
+ }, null, "DS: Extract payload of findQuery " + type);
2683
3266
  }
2684
3267
 
2685
- function _commit(adapter, store, operation, record, resolver) {
3268
+ function _commit(adapter, store, operation, record) {
2686
3269
  var type = record.constructor,
2687
3270
  promise = adapter[operation](store, type, record),
2688
3271
  serializer = serializerForAdapter(adapter, type);
@@ -2700,7 +3283,7 @@ function _commit(adapter, store, operation, record, resolver) {
2700
3283
  }
2701
3284
 
2702
3285
  throw reason;
2703
- }).then(resolver.resolve, resolver.reject);
3286
+ }, "DS: Extract and notify about " + operation + " completion of " + record);
2704
3287
  }
2705
3288
 
2706
3289
  })();
@@ -2713,95 +3296,111 @@ function _commit(adapter, store, operation, record, resolver) {
2713
3296
  */
2714
3297
 
2715
3298
  var get = Ember.get, set = Ember.set;
2716
-
2717
3299
  /*
2718
- WARNING: Much of these docs are inaccurate as of bf8497.
2719
-
2720
3300
  This file encapsulates the various states that a record can transition
2721
3301
  through during its lifecycle.
3302
+ */
3303
+ /**
3304
+ ### State
3305
+
3306
+ Each record has a `currentState` property that explicitly tracks what
3307
+ state a record is in at any given time. For instance, if a record is
3308
+ newly created and has not yet been sent to the adapter to be saved,
3309
+ it would be in the `root.loaded.created.uncommitted` state. If a
3310
+ record has had local modifications made to it that are in the
3311
+ process of being saved, the record would be in the
3312
+ `root.loaded.updated.inFlight` state. (These state paths will be
3313
+ explained in more detail below.)
3314
+
3315
+ Events are sent by the record or its store to the record's
3316
+ `currentState` property. How the state reacts to these events is
3317
+ dependent on which state it is in. In some states, certain events
3318
+ will be invalid and will cause an exception to be raised.
3319
+
3320
+ States are hierarchical and every state is a substate of the
3321
+ `RootState`. For example, a record can be in the
3322
+ `root.deleted.uncommitted` state, then transition into the
3323
+ `root.deleted.inFlight` state. If a child state does not implement
3324
+ an event handler, the state manager will attempt to invoke the event
3325
+ on all parent states until the root state is reached. The state
3326
+ hierarchy of a record is described in terms of a path string. You
3327
+ can determine a record's current state by getting the state's
3328
+ `stateName` property:
2722
3329
 
2723
- ### State Manager
2724
-
2725
- A record's state manager explicitly tracks what state a record is in
2726
- at any given time. For instance, if a record is newly created and has
2727
- not yet been sent to the adapter to be saved, it would be in the
2728
- `created.uncommitted` state. If a record has had local modifications
2729
- made to it that are in the process of being saved, the record would be
2730
- in the `updated.inFlight` state. (These state paths will be explained
2731
- in more detail below.)
2732
-
2733
- Events are sent by the record or its store to the record's state manager.
2734
- How the state manager reacts to these events is dependent on which state
2735
- it is in. In some states, certain events will be invalid and will cause
2736
- an exception to be raised.
2737
-
2738
- States are hierarchical. For example, a record can be in the
2739
- `deleted.start` state, then transition into the `deleted.inFlight` state.
2740
- If a child state does not implement an event handler, the state manager
2741
- will attempt to invoke the event on all parent states until the root state is
2742
- reached. The state hierarchy of a record is described in terms of a path
2743
- string. You can determine a record's current state by getting its manager's
2744
- current state path:
2745
-
2746
- record.get('stateManager.currentPath');
2747
- //=> "created.uncommitted"
2748
-
2749
- The `DS.Model` states are themselves stateless. What we mean is that,
2750
- though each instance of a record also has a unique instance of a
2751
- `DS.StateManager`, the hierarchical states that each of *those* points
2752
- to is a shared data structure. For performance reasons, instead of each
2753
- record getting its own copy of the hierarchy of states, each state
2754
- manager points to this global, immutable shared instance. How does a
2755
- state know which record it should be acting on? We pass a reference to
2756
- the current state manager as the first parameter to every method invoked
2757
- on a state.
2758
-
2759
- The state manager passed as the first parameter is where you should stash
2760
- state about the record if needed; you should never store data on the state
2761
- object itself. If you need access to the record being acted on, you can
2762
- retrieve the state manager's `record` property. For example, if you had
2763
- an event handler `myEvent`:
3330
+ ```javascript
3331
+ record.get('currentState.stateName');
3332
+ //=> "root.created.uncommitted"
3333
+ ```
3334
+
3335
+ The hierarchy of valid states that ship with ember data looks like
3336
+ this:
3337
+
3338
+ ```text
3339
+ * root
3340
+ * deleted
3341
+ * saved
3342
+ * uncommitted
3343
+ * inFlight
3344
+ * empty
3345
+ * loaded
3346
+ * created
3347
+ * uncommitted
3348
+ * inFlight
3349
+ * saved
3350
+ * updated
3351
+ * uncommitted
3352
+ * inFlight
3353
+ * loading
3354
+ ```
2764
3355
 
2765
- myEvent: function(manager) {
2766
- var record = manager.get('record');
2767
- record.doSomething();
2768
- }
3356
+ The `DS.Model` states are themselves stateless. What we mean is
3357
+ that, the hierarchical states that each of *those* points to is a
3358
+ shared data structure. For performance reasons, instead of each
3359
+ record getting its own copy of the hierarchy of states, each record
3360
+ points to this global, immutable shared instance. How does a state
3361
+ know which record it should be acting on? We pass the record
3362
+ instance into the state's event handlers as the first argument.
2769
3363
 
2770
- For more information about state managers in general, see the Ember.js
2771
- documentation on `Ember.StateManager`.
3364
+ The record passed as the first parameter is where you should stash
3365
+ state about the record if needed; you should never store data on the state
3366
+ object itself.
2772
3367
 
2773
- ### Events, Flags, and Transitions
3368
+ ### Events and Flags
2774
3369
 
2775
- A state may implement zero or more events, flags, or transitions.
3370
+ A state may implement zero or more events and flags.
2776
3371
 
2777
3372
  #### Events
2778
3373
 
2779
3374
  Events are named functions that are invoked when sent to a record. The
2780
- state manager will first look for a method with the given name on the
2781
- current state. If no method is found, it will search the current state's
2782
- parent, and then its grandparent, and so on until reaching the top of
2783
- the hierarchy. If the root is reached without an event handler being found,
2784
- an exception will be raised. This can be very helpful when debugging new
2785
- features.
3375
+ record will first look for a method with the given name on the
3376
+ current state. If no method is found, it will search the current
3377
+ state's parent, and then its grandparent, and so on until reaching
3378
+ the top of the hierarchy. If the root is reached without an event
3379
+ handler being found, an exception will be raised. This can be very
3380
+ helpful when debugging new features.
2786
3381
 
2787
3382
  Here's an example implementation of a state with a `myEvent` event handler:
2788
3383
 
2789
- aState: DS.State.create({
2790
- myEvent: function(manager, param) {
2791
- console.log("Received myEvent with "+param);
2792
- }
2793
- })
3384
+ ```javascript
3385
+ aState: DS.State.create({
3386
+ myEvent: function(manager, param) {
3387
+ console.log("Received myEvent with", param);
3388
+ }
3389
+ })
3390
+ ```
2794
3391
 
2795
3392
  To trigger this event:
2796
3393
 
2797
- record.send('myEvent', 'foo');
2798
- //=> "Received myEvent with foo"
3394
+ ```javascript
3395
+ record.send('myEvent', 'foo');
3396
+ //=> "Received myEvent with foo"
3397
+ ```
2799
3398
 
2800
3399
  Note that an optional parameter can be sent to a record's `send()` method,
2801
3400
  which will be passed as the second parameter to the event handler.
2802
3401
 
2803
3402
  Events should transition to a different state if appropriate. This can be
2804
- done by calling the state manager's `transitionTo()` method with a path to the
3403
+ done by calling the record's `transitionTo()` method with a path to the
2805
3404
  desired state. The state manager will attempt to resolve the state path
2806
3405
  relative to the current state. If no state is found at that path, it will
2807
3406
  attempt to resolve it relative to the current state's parent, and then its
@@ -2809,12 +3408,12 @@ var get = Ember.get, set = Ember.set;
2809
3408
  like this:
2810
3409
 
2811
3410
  * created
2812
- * start <-- currentState
3411
+ * uncommitted <-- currentState
2813
3412
  * inFlight
2814
3413
  * updated
2815
3414
  * inFlight
2816
3415
 
2817
- If we are currently in the `start` state, calling
3416
+ If we are currently in the `uncommitted` state, calling
2818
3417
  `transitionTo('inFlight')` would transition to the `created.inFlight` state,
2819
3418
  while calling `transitionTo('updated.inFlight')` would transition to
2820
3419
  the `updated.inFlight` state.
@@ -2829,16 +3428,20 @@ var get = Ember.get, set = Ember.set;
2829
3428
  state in a more user-friendly way than examining its state path. For example,
2830
3429
  instead of doing this:
2831
3430
 
2832
- var statePath = record.get('stateManager.currentPath');
2833
- if (statePath === 'created.inFlight') {
2834
- doSomething();
2835
- }
3431
+ ```javascript
3432
+ var statePath = record.get('stateManager.currentPath');
3433
+ if (statePath === 'created.inFlight') {
3434
+ doSomething();
3435
+ }
3436
+ ```
2836
3437
 
2837
3438
  You can say:
2838
3439
 
2839
- if (record.get('isNew') && record.get('isSaving')) {
2840
- doSomething();
2841
- }
3440
+ ```javascript
3441
+ if (record.get('isNew') && record.get('isSaving')) {
3442
+ doSomething();
3443
+ }
3444
+ ```
2842
3445
 
2843
3446
  If your state does not set a value for a given flag, the value will
2844
3447
  be inherited from its parent (or the first place in the state hierarchy
@@ -2848,23 +3451,18 @@ var get = Ember.get, set = Ember.set;
2848
3451
  in addition to the area below, you will also need to declare it in the
2849
3452
  `DS.Model` class.
2850
3453
 
2851
- #### Transitions
2852
-
2853
- Transitions are like event handlers but are called automatically upon
2854
- entering or exiting a state. To implement a transition, just call a method
2855
- either `enter` or `exit`:
2856
3454
 
2857
- myState: DS.State.create({
2858
- // Gets called automatically when entering
2859
- // this state.
2860
- enter: function(manager) {
2861
- console.log("Entered myState");
2862
- }
2863
- })
3455
+ * [isEmpty](DS.Model.html#property_isEmpty)
3456
+ * [isLoading](DS.Model.html#property_isLoading)
3457
+ * [isLoaded](DS.Model.html#property_isLoaded)
3458
+ * [isDirty](DS.Model.html#property_isDirty)
3459
+ * [isSaving](DS.Model.html#property_isSaving)
3460
+ * [isDeleted](DS.Model.html#property_isDeleted)
3461
+ * [isNew](DS.Model.html#property_isNew)
3462
+ * [isValid](DS.Model.html#property_isValid)
2864
3463
 
2865
- Note that enter and exit events are called once per transition. If the
2866
- current state changes, but changes to another child state of the parent,
2867
- the transition event on the parent will not be triggered.
3464
+ @namespace DS
3465
+ @class RootState
2868
3466
  */
2869
3467
 
2870
3468
  var hasDefinedProperties = function(object) {
@@ -2967,8 +3565,8 @@ var DirtyState = {
2967
3565
  record.transitionTo('inFlight');
2968
3566
  },
2969
3567
 
2970
- reloadRecord: function(record, resolver) {
2971
- get(record, 'store').reloadRecord(record, resolver);
3568
+ reloadRecord: function(record, resolve) {
3569
+ resolve(get(record, 'store').reloadRecord(record));
2972
3570
  },
2973
3571
 
2974
3572
  rolledBack: function(record) {
@@ -3248,8 +3846,8 @@ var RootState = {
3248
3846
  record.transitionTo('updated.inFlight');
3249
3847
  },
3250
3848
 
3251
- reloadRecord: function(record, resolver) {
3252
- get(record, 'store').reloadRecord(record, resolver);
3849
+ reloadRecord: function(record, resolve) {
3850
+ resolve(get(record, 'store').reloadRecord(record));
3253
3851
  },
3254
3852
 
3255
3853
  deleteRecord: function(record) {
@@ -3266,7 +3864,11 @@ var RootState = {
3266
3864
 
3267
3865
  didCommit: function(record) {
3268
3866
  record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType'));
3269
- }
3867
+ },
3868
+
3869
+ // loaded.saved.notFound would be triggered by a failed
3870
+ // `reload()` on an unchanged record
3871
+ notFound: Ember.K
3270
3872
 
3271
3873
  },
3272
3874
 
@@ -3293,9 +3895,7 @@ var RootState = {
3293
3895
 
3294
3896
  // TRANSITIONS
3295
3897
  setup: function(record) {
3296
- var store = get(record, 'store');
3297
-
3298
- store.recordArrayManager.remove(record);
3898
+ record.updateRecordArrays();
3299
3899
  },
3300
3900
 
3301
3901
  // SUBSTATES
@@ -3410,9 +4010,9 @@ DS.RootState = RootState;
3410
4010
  var get = Ember.get, set = Ember.set,
3411
4011
  merge = Ember.merge, once = Ember.run.once;
3412
4012
 
3413
- var retrieveFromCurrentState = Ember.computed(function(key, value) {
4013
+ var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) {
3414
4014
  return get(get(this, 'currentState'), key);
3415
- }).property('currentState').readOnly();
4015
+ }).readOnly();
3416
4016
 
3417
4017
  /**
3418
4018
 
@@ -3424,35 +4024,284 @@ var retrieveFromCurrentState = Ember.computed(function(key, value) {
3424
4024
  @uses Ember.Evented
3425
4025
  */
3426
4026
  DS.Model = Ember.Object.extend(Ember.Evented, {
4027
+ /**
4028
+ If this property is `true` the record is in the `empty`
4029
+ state. Empty is the first state all records enter after they have
4030
+ been created. Most records created by the store will quickly
4031
+ transition to the `loading` state if data needs to be fetched from
4032
+ the server or the `created` state if the record is created on the
4033
+ client. A record can also enter the empty state if the adapter is
4034
+ unable to locate the record.
4035
+
4036
+ @property isEmpty
4037
+ @type {Boolean}
4038
+ @readOnly
4039
+ */
3427
4040
  isEmpty: retrieveFromCurrentState,
4041
+ /**
4042
+ If this property is `true` the record is in the `loading` state. A
4043
+ record enters this state when the store askes the adapter for its
4044
+ data. It remains in this state until the adapter provides the
4045
+ requested data.
4046
+
4047
+ @property isLoading
4048
+ @type {Boolean}
4049
+ @readOnly
4050
+ */
3428
4051
  isLoading: retrieveFromCurrentState,
4052
+ /**
4053
+ If this property is `true` the record is in the `loaded` state. A
4054
+ record enters this state when its data is populated. Most of a
4055
+ record's lifecycle is spent inside substates of the `loaded`
4056
+ state.
4057
+
4058
+ Example
4059
+
4060
+ ```javascript
4061
+ var record = store.createRecord(App.Model);
4062
+ record.get('isLoaded'); // true
4063
+
4064
+ store.find('model', 1).then(function(model) {
4065
+ model.get('isLoaded'); // true
4066
+ });
4067
+ ```
4068
+
4069
+ @property isLoaded
4070
+ @type {Boolean}
4071
+ @readOnly
4072
+ */
3429
4073
  isLoaded: retrieveFromCurrentState,
4074
+ /**
4075
+ If this property is `true` the record is in the `dirty` state. The
4076
+ record has local changes that have not yet been saved by the
4077
+ adapter. This includes records that have been created (but not yet
4078
+ saved) or deleted.
4079
+
4080
+ Example
4081
+
4082
+ ```javascript
4083
+ var record = store.createRecord(App.Model);
4084
+ record.get('isDirty'); // true
4085
+
4086
+ store.find('model', 1).then(function(model) {
4087
+ model.get('isDirty'); // false
4088
+ model.set('foo', 'some value');
4089
+ model.set('isDirty'); // true
4090
+ });
4091
+ ```
4092
+
4093
+ @property isDirty
4094
+ @type {Boolean}
4095
+ @readOnly
4096
+ */
3430
4097
  isDirty: retrieveFromCurrentState,
4098
+ /**
4099
+ If this property is `true` the record is in the `saving` state. A
4100
+ record enters the saving state when `save` is called, but the
4101
+ adapter has not yet acknowledged that the changes have been
4102
+ persisted to the backend.
4103
+
4104
+ Example
4105
+
4106
+ ```javascript
4107
+ var record = store.createRecord(App.Model);
4108
+ record.get('isSaving'); // false
4109
+ var promise = record.save();
4110
+ record.get('isSaving'); // true
4111
+ promise.then(function() {
4112
+ record.get('isSaving'); // false
4113
+ });
4114
+ ```
4115
+
4116
+ @property isSaving
4117
+ @type {Boolean}
4118
+ @readOnly
4119
+ */
3431
4120
  isSaving: retrieveFromCurrentState,
4121
+ /**
4122
+ If this property is `true` the record is in the `deleted` state
4123
+ and has been marked for deletion. When `isDeleted` is true and
4124
+ `isDirty` is true, the record is deleted locally but the deletion
4125
+ was not yet persisted. When `isSaving` is true, the change is
4126
+ in-flight. When both `isDirty` and `isSaving` are false, the
4127
+ change has persisted.
4128
+
4129
+ Example
4130
+
4131
+ ```javascript
4132
+ var record = store.createRecord(App.Model);
4133
+ record.get('isDeleted'); // false
4134
+ record.deleteRecord();
4135
+ record.get('isDeleted'); // true
4136
+ ```
4137
+
4138
+ @property isDeleted
4139
+ @type {Boolean}
4140
+ @readOnly
4141
+ */
3432
4142
  isDeleted: retrieveFromCurrentState,
4143
+ /**
4144
+ If this property is `true` the record is in the `new` state. A
4145
+ record will be in the `new` state when it has been created on the
4146
+ client and the adapter has not yet report that it was successfully
4147
+ saved.
4148
+
4149
+ Example
4150
+
4151
+ ```javascript
4152
+ var record = store.createRecord(App.Model);
4153
+ record.get('isNew'); // true
4154
+
4155
+ store.find('model', 1).then(function(model) {
4156
+ model.get('isNew'); // false
4157
+ });
4158
+ ```
4159
+
4160
+ @property isNew
4161
+ @type {Boolean}
4162
+ @readOnly
4163
+ */
3433
4164
  isNew: retrieveFromCurrentState,
4165
+ /**
4166
+ If this property is `true` the record is in the `valid` state. A
4167
+ record will be in the `valid` state when no client-side
4168
+ validations have failed and the adapter did not report any
4169
+ server-side validation failures.
4170
+
4171
+ @property isValid
4172
+ @type {Boolean}
4173
+ @readOnly
4174
+ */
3434
4175
  isValid: retrieveFromCurrentState,
4176
+ /**
4177
+ If the record is in the dirty state this property will report what
4178
+ kind of change has caused it to move into the dirty
4179
+ state. Possible values are:
4180
+
4181
+ - `created` The record has been created by the client and not yet saved to the adapter.
4182
+ - `updated` The record has been updated by the client and not yet saved to the adapter.
4183
+ - `deleted` The record has been deleted by the client and not yet saved to the adapter.
4184
+
4185
+ Example
4186
+
4187
+ ```javascript
4188
+ var record = store.createRecord(App.Model);
4189
+ record.get('dirtyType'); // 'created'
4190
+ ```
4191
+
4192
+ @property dirtyType
4193
+ @type {String}
4194
+ @readOnly
4195
+ */
3435
4196
  dirtyType: retrieveFromCurrentState,
3436
4197
 
4198
+ /**
4199
+ If `true` the adapter reported that it was unable to save local
4200
+ changes to the backend. This may also result in the record having
4201
+ its `isValid` property become false if the adapter reported that
4202
+ server-side validations failed.
4203
+
4204
+ Example
4205
+
4206
+ ```javascript
4207
+ record.get('isError'); // false
4208
+ record.set('foo', 'invalid value');
4209
+ record.save().then(null, function() {
4210
+ record.get('isError'); // true
4211
+ });
4212
+ ```
4213
+
4214
+ @property isError
4215
+ @type {Boolean}
4216
+ @readOnly
4217
+ */
3437
4218
  isError: false,
4219
+ /**
4220
+ If `true` the store is attempting to reload the record form the adapter.
4221
+
4222
+ Example
4223
+
4224
+ ```javascript
4225
+ record.get('isReloading'); // false
4226
+ record.reload();
4227
+ record.get('isReloading'); // true
4228
+ ```
4229
+
4230
+ @property isReloading
4231
+ @type {Boolean}
4232
+ @readOnly
4233
+ */
3438
4234
  isReloading: false,
3439
4235
 
4236
+ /**
4237
+ The `clientId` property is a transient numerical identifier
4238
+ generated at runtime by the data store. It is important
4239
+ primarily because newly created objects may not yet have an
4240
+ externally generated id.
4241
+
4242
+ @property clientId
4243
+ @private
4244
+ @type {Number|String}
4245
+ */
3440
4246
  clientId: null,
4247
+ /**
4248
+ All ember models have an id property. This is an identifier
4249
+ managed by an external source. These are always coerced to be
4250
+ strings before being used internally. Note when declaring the
4251
+ attributes for a model it is an error to declare an id
4252
+ attribute.
4253
+
4254
+ ```javascript
4255
+ var record = store.createRecord(App.Model);
4256
+ record.get('id'); // null
4257
+
4258
+ store.find('model', 1).then(function(model) {
4259
+ model.get('id'); // '1'
4260
+ });
4261
+ ```
4262
+
4263
+ @property id
4264
+ @type {String}
4265
+ */
3441
4266
  id: null,
3442
4267
  transaction: null,
4268
+ /**
4269
+ @property currentState
4270
+ @private
4271
+ @type {Object}
4272
+ */
3443
4273
  currentState: null,
4274
+ /**
4275
+ When the record is in the `invalid` state this object will contain
4276
+ any errors returned by the adapter. When present the errors hash
4277
+ typically contains keys coresponding to the invalid property names
4278
+ and values which are an array of error messages.
4279
+
4280
+ ```javascript
4281
+ record.get('errors'); // null
4282
+ record.set('foo', 'invalid value');
4283
+ record.save().then(null, function() {
4284
+ record.get('errors'); // {foo: ['foo should be a number.']}
4285
+ });
4286
+ ```
4287
+
4288
+ @property errors
4289
+ @type {Object}
4290
+ */
3444
4291
  errors: null,
3445
4292
 
3446
4293
  /**
3447
4294
  Create a JSON representation of the record, using the serialization
3448
4295
  strategy of the store's adapter.
3449
4296
 
3450
- @method serialize
3451
- @param {Object} options Available options:
4297
+ `serialize` takes an optional hash as a parameter, currently
4298
+ supported options are:
3452
4299
 
3453
- * `includeId`: `true` if the record's ID should be included in the
4300
+ - `includeId`: `true` if the record's ID should be included in the
3454
4301
  JSON representation.
3455
4302
 
4303
+ @method serialize
4304
+ @param {Object} options
3456
4305
  @returns {Object} an object whose values are primitive JSON values only
3457
4306
  */
3458
4307
  serialize: function(options) {
@@ -3461,15 +4310,17 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3461
4310
  },
3462
4311
 
3463
4312
  /**
3464
- Use {{#crossLink "DS.JSONSerializer"}}DS.JSONSerializer{{/crossLink}} to
4313
+ Use [DS.JSONSerializer](DS.JSONSerializer.html) to
3465
4314
  get the JSON representation of a record.
3466
4315
 
3467
- @method toJSON
3468
- @param {Object} options Available options:
4316
+ `toJSON` takes an optional hash as a parameter, currently
4317
+ supported options are:
3469
4318
 
3470
- * `includeId`: `true` if the record's ID should be included in the
4319
+ - `includeId`: `true` if the record's ID should be included in the
3471
4320
  JSON representation.
3472
4321
 
4322
+ @method toJSON
4323
+ @param {Object} options
3473
4324
  @returns {Object} A JSON representation of the object.
3474
4325
  */
3475
4326
  toJSON: function(options) {
@@ -3485,13 +4336,6 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3485
4336
  */
3486
4337
  didLoad: Ember.K,
3487
4338
 
3488
- /**
3489
- Fired when the record is reloaded from the server.
3490
-
3491
- @event didReload
3492
- */
3493
- didReload: Ember.K,
3494
-
3495
4339
  /**
3496
4340
  Fired when the record is updated.
3497
4341
 
@@ -3527,6 +4371,11 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3527
4371
  */
3528
4372
  becameError: Ember.K,
3529
4373
 
4374
+ /**
4375
+ @property data
4376
+ @private
4377
+ @type {Object}
4378
+ */
3530
4379
  data: Ember.computed(function() {
3531
4380
  this._data = this._data || {};
3532
4381
  return this._data;
@@ -3549,6 +4398,12 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3549
4398
  this._relationships = {};
3550
4399
  },
3551
4400
 
4401
+ /**
4402
+ @method send
4403
+ @private
4404
+ @param {String} name
4405
+ @param {Object} context
4406
+ */
3552
4407
  send: function(name, context) {
3553
4408
  var currentState = get(this, 'currentState');
3554
4409
 
@@ -3559,6 +4414,11 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3559
4414
  return currentState[name](this, context);
3560
4415
  },
3561
4416
 
4417
+ /**
4418
+ @method transitionTo
4419
+ @private
4420
+ @param {String} name
4421
+ */
3562
4422
  transitionTo: function(name) {
3563
4423
  // POSSIBLE TODO: Remove this code and replace with
3564
4424
  // always having direct references to state objects
@@ -3613,18 +4473,35 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3613
4473
  if (transaction) { fn(transaction); }
3614
4474
  },
3615
4475
 
4476
+ /**
4477
+ @method loadingData
4478
+ @private
4479
+ @param {Promise} promise
4480
+ */
3616
4481
  loadingData: function(promise) {
3617
4482
  this.send('loadingData', promise);
3618
4483
  },
3619
4484
 
4485
+ /**
4486
+ @method loadedData
4487
+ @private
4488
+ */
3620
4489
  loadedData: function() {
3621
4490
  this.send('loadedData');
3622
4491
  },
3623
4492
 
4493
+ /**
4494
+ @method notFound
4495
+ @private
4496
+ */
3624
4497
  notFound: function() {
3625
4498
  this.send('notFound');
3626
4499
  },
3627
4500
 
4501
+ /**
4502
+ @method pushedData
4503
+ @private
4504
+ */
3628
4505
  pushedData: function() {
3629
4506
  this.send('pushedData');
3630
4507
  },
@@ -3635,6 +4512,24 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3635
4512
  method if you want to allow the user to still `rollback()` a
3636
4513
  delete after it was made.
3637
4514
 
4515
+ Example
4516
+
4517
+ ```javascript
4518
+ App.ModelDeleteRoute = Ember.Route.extend({
4519
+ actions: {
4520
+ softDelete: function() {
4521
+ this.get('model').deleteRecord();
4522
+ },
4523
+ confirm: function() {
4524
+ this.get('model').save();
4525
+ },
4526
+ undo: function() {
4527
+ this.get('model').rollback();
4528
+ }
4529
+ }
4530
+ });
4531
+ ```
4532
+
3638
4533
  @method deleteRecord
3639
4534
  */
3640
4535
  deleteRecord: function() {
@@ -3644,20 +4539,44 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3644
4539
  /**
3645
4540
  Same as `deleteRecord`, but saves the record immediately.
3646
4541
 
4542
+ Example
4543
+
4544
+ ```javascript
4545
+ App.ModelDeleteRoute = Ember.Route.extend({
4546
+ actions: {
4547
+ delete: function() {
4548
+ var controller = this.controller;
4549
+ this.get('model').destroyRecord().then(function() {
4550
+ controller.transitionToRoute('model.index');
4551
+ });
4552
+ }
4553
+ }
4554
+ });
4555
+ ```
4556
+
3647
4557
  @method destroyRecord
3648
- @returns Promise
4558
+ @return {Promise} a promise that will be resolved when the adapter returns
4559
+ successfully or rejected if the adapter returns with an error.
3649
4560
  */
3650
4561
  destroyRecord: function() {
3651
4562
  this.deleteRecord();
3652
4563
  return this.save();
3653
4564
  },
3654
4565
 
4566
+ /**
4567
+ @method unloadRecord
4568
+ @private
4569
+ */
3655
4570
  unloadRecord: function() {
3656
4571
 
3657
4572
 
3658
4573
  this.send('unloadRecord');
3659
4574
  },
3660
4575
 
4576
+ /**
4577
+ @method clearRelationships
4578
+ @private
4579
+ */
3661
4580
  clearRelationships: function() {
3662
4581
  this.eachRelationship(function(name, relationship) {
3663
4582
  if (relationship.kind === 'belongsTo') {
@@ -3669,19 +4588,33 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3669
4588
  }, this);
3670
4589
  },
3671
4590
 
4591
+ /**
4592
+ @method updateRecordArrays
4593
+ @private
4594
+ */
3672
4595
  updateRecordArrays: function() {
3673
- var store = get(this, 'store');
3674
- if (store) {
3675
- store.dataWasUpdated(this.constructor, this);
3676
- }
4596
+ get(this, 'store').dataWasUpdated(this.constructor, this);
3677
4597
  },
3678
4598
 
3679
4599
  /**
3680
- Gets the diff for the current model.
4600
+ Returns an object, whose keys are changed properties, and value is
4601
+ an [oldProp, newProp] array.
3681
4602
 
3682
- @method changedAttributes
4603
+ Example
4604
+
4605
+ ```javascript
4606
+ App.Mascot = DS.Model.extend({
4607
+ name: attr('string')
4608
+ });
3683
4609
 
3684
- @returns {Object} an object, whose keys are changed properties,
4610
+ var person = store.createRecord('person');
4611
+ person.changedAttributes(); // {}
4612
+ person.set('name', 'Tomster');
4613
+ person.changedAttributes(); // {name: [undefined, 'Tomster']}
4614
+ ```
4615
+
4616
+ @method changedAttributes
4617
+ @return {Object} an object, whose keys are changed properties,
3685
4618
  and value is an [oldProp, newProp] array.
3686
4619
  */
3687
4620
  changedAttributes: function() {
@@ -3697,6 +4630,10 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3697
4630
  return diffData;
3698
4631
  },
3699
4632
 
4633
+ /**
4634
+ @method adapterWillCommit
4635
+ @private
4636
+ */
3700
4637
  adapterWillCommit: function() {
3701
4638
  this.send('willCommit');
3702
4639
  },
@@ -3729,6 +4666,10 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3729
4666
  });
3730
4667
  },
3731
4668
 
4669
+ /**
4670
+ @method adapterDidDirty
4671
+ @private
4672
+ */
3732
4673
  adapterDidDirty: function() {
3733
4674
  this.send('becomeDirty');
3734
4675
  this.updateRecordArraysLater();
@@ -3761,10 +4702,21 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3761
4702
  }
3762
4703
  },
3763
4704
 
4705
+ /**
4706
+ @method updateRecordArraysLater
4707
+ @private
4708
+ */
3764
4709
  updateRecordArraysLater: function() {
3765
4710
  Ember.run.once(this, this.updateRecordArrays);
3766
4711
  },
3767
4712
 
4713
+ /**
4714
+ @method setupData
4715
+ @private
4716
+ @param {Object} data
4717
+ @param {Boolean} partial the data should be merged into
4718
+ the existing data, not replace it.
4719
+ */
3768
4720
  setupData: function(data, partial) {
3769
4721
  if (partial) {
3770
4722
  Ember.merge(this._data, data);
@@ -3799,15 +4751,43 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3799
4751
  this._data[name] = value;
3800
4752
  },
3801
4753
 
4754
+ /**
4755
+ @method updateHasMany
4756
+ @private
4757
+ @param {String} name
4758
+ @param {Array} records
4759
+ */
3802
4760
  updateHasMany: function(name, records) {
3803
4761
  this._data[name] = records;
3804
4762
  this.hasManyDidChange(name);
3805
4763
  },
3806
4764
 
4765
+ /**
4766
+ @method updateBelongsTo
4767
+ @private
4768
+ @param {String} name
4769
+ @param {DS.Model} record
4770
+ */
3807
4771
  updateBelongsTo: function(name, record) {
3808
4772
  this._data[name] = record;
3809
4773
  },
3810
4774
 
4775
+ /**
4776
+ If the model `isDirty` this function will which discard any unsaved
4777
+ changes
4778
+
4779
+ Example
4780
+
4781
+ ```javascript
4782
+ record.get('name'); // 'Untitled Document'
4783
+ record.set('name', 'Doc 1');
4784
+ record.get('name'); // 'Doc 1'
4785
+ record.rollback();
4786
+ record.get('name'); // 'Untitled Document'
4787
+ ```
4788
+
4789
+ @method rollback
4790
+ */
3811
4791
  rollback: function() {
3812
4792
  this._attributes = {};
3813
4793
 
@@ -3815,7 +4795,12 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3815
4795
  this._inFlightAttributes = {};
3816
4796
  set(this, 'isError', false);
3817
4797
  }
3818
-
4798
+
4799
+ if (!get(this, 'isValid')) {
4800
+ this._inFlightAttributes = {};
4801
+ this.send('becameValid');
4802
+ }
4803
+
3819
4804
  this.send('rolledBack');
3820
4805
 
3821
4806
  this.suspendRelationshipObservers(function() {
@@ -3861,12 +4846,26 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3861
4846
  },
3862
4847
 
3863
4848
  /**
3864
- Save the record.
4849
+ Save the record and persist any changes to the record to an
4850
+ extenal source via the adapter.
4851
+
4852
+ Example
3865
4853
 
4854
+ ```javascript
4855
+ record.set('name', 'Tomster');
4856
+ record.save().then(function(){
4857
+ // Success callback
4858
+ }, function() {
4859
+ // Error callback
4860
+ });
4861
+ ```
3866
4862
  @method save
4863
+ @return {Promise} a promise that will be resolved when the adapter returns
4864
+ successfully or rejected if the adapter returns with an error.
3867
4865
  */
3868
4866
  save: function() {
3869
- var resolver = Ember.RSVP.defer();
4867
+ var promiseLabel = "DS: Model#save " + this;
4868
+ var resolver = Ember.RSVP.defer(promiseLabel);
3870
4869
 
3871
4870
  this.get('store').scheduleSave(this, resolver);
3872
4871
  this._inFlightAttributes = this._attributes;
@@ -3882,25 +4881,41 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3882
4881
  and has not yet been modified (`isLoaded` but not `isDirty`,
3883
4882
  or `isSaving`).
3884
4883
 
4884
+ Example
4885
+
4886
+ ```javascript
4887
+ App.ModelViewRoute = Ember.Route.extend({
4888
+ actions: {
4889
+ reload: function() {
4890
+ this.get('model').reload();
4891
+ }
4892
+ }
4893
+ });
4894
+ ```
4895
+
3885
4896
  @method reload
4897
+ @return {Promise} a promise that will be resolved with the record when the
4898
+ adapter returns successfully or rejected if the adapter returns
4899
+ with an error.
3886
4900
  */
3887
4901
  reload: function() {
3888
4902
  set(this, 'isReloading', true);
3889
4903
 
3890
- var resolver = Ember.RSVP.defer(), record = this;
4904
+ var record = this;
3891
4905
 
3892
- resolver.promise = resolver.promise.then(function() {
4906
+ var promiseLabel = "DS: Model#reload of " + this;
4907
+ var promise = new Ember.RSVP.Promise(function(resolve){
4908
+ record.send('reloadRecord', resolve);
4909
+ }, promiseLabel).then(function() {
3893
4910
  record.set('isReloading', false);
3894
4911
  record.set('isError', false);
3895
4912
  return record;
3896
4913
  }, function(reason) {
3897
4914
  record.set('isError', true);
3898
4915
  throw reason;
3899
- });
3900
-
3901
- this.send('reloadRecord', resolver);
4916
+ }, "DS: Model#reload complete, update flags");
3902
4917
 
3903
- return DS.PromiseObject.create({ promise: resolver.promise });
4918
+ return DS.PromiseObject.create({ promise: promise });
3904
4919
  },
3905
4920
 
3906
4921
  // FOR USE DURING COMMIT PROCESS
@@ -3921,10 +4936,18 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3921
4936
  this.updateRecordArraysLater();
3922
4937
  },
3923
4938
 
4939
+ /**
4940
+ @method adapterDidInvalidate
4941
+ @private
4942
+ */
3924
4943
  adapterDidInvalidate: function(errors) {
3925
4944
  this.send('becameInvalid', errors);
3926
4945
  },
3927
4946
 
4947
+ /**
4948
+ @method adapterDidError
4949
+ @private
4950
+ */
3928
4951
  adapterDidError: function() {
3929
4952
  this.send('becameError');
3930
4953
  set(this, 'isError', true);
@@ -3971,9 +4994,11 @@ DS.Model.reopenClass({
3971
4994
  _create: DS.Model.create,
3972
4995
 
3973
4996
  /**
3974
- Override the class' `create()` method to raise an error. This prevents end users
3975
- from inadvertently calling `create()` instead of `createRecord()`. The store is
3976
- still able to create instances by calling the `_create()` method.
4997
+ Override the class' `create()` method to raise an error. This
4998
+ prevents end users from inadvertently calling `create()` instead
4999
+ of `createRecord()`. The store is still able to create instances
5000
+ by calling the `_create()` method. To create an instance of a
5001
+ `DS.Model` use [store.createRecord](DS.Store.html#method_createRecord).
3977
5002
 
3978
5003
  @method create
3979
5004
  @private
@@ -4000,6 +5025,38 @@ var get = Ember.get;
4000
5025
  @namespace DS
4001
5026
  */
4002
5027
  DS.Model.reopenClass({
5028
+ /**
5029
+ A map whose keys are the attributes of the model (properties
5030
+ described by DS.attr) and whose values are the meta object for the
5031
+ property.
5032
+
5033
+ Example
5034
+
5035
+ ```javascript
5036
+
5037
+ App.Person = DS.Model.extend({
5038
+ firstName: attr('string'),
5039
+ lastName: attr('string'),
5040
+ birthday: attr('date')
5041
+ });
5042
+
5043
+ var attributes = Ember.get(App.Person, 'attributes')
5044
+
5045
+ attributes.forEach(function(name, meta) {
5046
+ console.log(name, meta);
5047
+ });
5048
+
5049
+ // prints:
5050
+ // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
5051
+ // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
5052
+ // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
5053
+ ```
5054
+
5055
+ @property attributes
5056
+ @static
5057
+ @type {Ember.Map}
5058
+ @readOnly
5059
+ */
4003
5060
  attributes: Ember.computed(function() {
4004
5061
  var map = Ember.Map.create();
4005
5062
 
@@ -4015,6 +5072,37 @@ DS.Model.reopenClass({
4015
5072
  return map;
4016
5073
  }),
4017
5074
 
5075
+ /**
5076
+ A map whose keys are the attributes of the model (properties
5077
+ described by DS.attr) and whose values are type of transformation
5078
+ applied to each attribute. This map does not include any
5079
+ attributes that do not have an transformation type.
5080
+
5081
+ Example
5082
+
5083
+ ```javascript
5084
+ App.Person = DS.Model.extend({
5085
+ firstName: attr(),
5086
+ lastName: attr('string'),
5087
+ birthday: attr('date')
5088
+ });
5089
+
5090
+ var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')
5091
+
5092
+ transformedAttributes.forEach(function(field, type) {
5093
+ console.log(field, type);
5094
+ });
5095
+
5096
+ // prints:
5097
+ // lastName string
5098
+ // birthday date
5099
+ ```
5100
+
5101
+ @property transformedAttributes
5102
+ @static
5103
+ @type {Ember.Map}
5104
+ @readOnly
5105
+ */
4018
5106
  transformedAttributes: Ember.computed(function() {
4019
5107
  var map = Ember.Map.create();
4020
5108
 
@@ -4027,12 +5115,95 @@ DS.Model.reopenClass({
4027
5115
  return map;
4028
5116
  }),
4029
5117
 
5118
+ /**
5119
+ Iterates through the attributes of the model, calling the passed function on each
5120
+ attribute.
5121
+
5122
+ The callback method you provide should have the following signature (all
5123
+ parameters are optional):
5124
+
5125
+ ```javascript
5126
+ function(name, meta);
5127
+ ```
5128
+
5129
+ - `name` the name of the current property in the iteration
5130
+ - `meta` the meta object for the attribute property in the iteration
5131
+
5132
+ Note that in addition to a callback, you can also pass an optional target
5133
+ object that will be set as `this` on the context.
5134
+
5135
+ Example
5136
+
5137
+ ```javascript
5138
+ App.Person = DS.Model.extend({
5139
+ firstName: attr('string'),
5140
+ lastName: attr('string'),
5141
+ birthday: attr('date')
5142
+ });
5143
+
5144
+ App.Person.eachAttribute(function(name, meta) {
5145
+ console.log(name, meta);
5146
+ });
5147
+
5148
+ // prints:
5149
+ // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
5150
+ // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
5151
+ // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
5152
+ ```
5153
+
5154
+ @method eachAttribute
5155
+ @param {Function} callback The callback to execute
5156
+ @param {Object} [target] The target object to use
5157
+ @static
5158
+ */
4030
5159
  eachAttribute: function(callback, binding) {
4031
5160
  get(this, 'attributes').forEach(function(name, meta) {
4032
5161
  callback.call(binding, name, meta);
4033
5162
  }, binding);
4034
5163
  },
4035
5164
 
5165
+ /**
5166
+ Iterates through the transformedAttributes of the model, calling
5167
+ the passed function on each attribute. Note the callback will not be
5168
+ called for any attributes that do not have an transformation type.
5169
+
5170
+ The callback method you provide should have the following signature (all
5171
+ parameters are optional):
5172
+
5173
+ ```javascript
5174
+ function(name, type);
5175
+ ```
5176
+
5177
+ - `name` the name of the current property in the iteration
5178
+ - `type` a tring contrining the name of the type of transformed
5179
+ applied to the attribute
5180
+
5181
+ Note that in addition to a callback, you can also pass an optional target
5182
+ object that will be set as `this` on the context.
5183
+
5184
+ Example
5185
+
5186
+ ```javascript
5187
+ App.Person = DS.Model.extend({
5188
+ firstName: attr(),
5189
+ lastName: attr('string'),
5190
+ birthday: attr('date')
5191
+ });
5192
+
5193
+ App.Person.eachTransformedAttribute(function(name, type) {
5194
+ console.log(name, type);
5195
+ });
5196
+
5197
+ // prints:
5198
+ // lastName string
5199
+ // birthday date
5200
+ ```
5201
+
5202
+ @method eachTransformedAttribute
5203
+ @param {Function} callback The callback to execute
5204
+ @param {Object} [target] The target object to use
5205
+ @static
5206
+ */
4036
5207
  eachTransformedAttribute: function(callback, binding) {
4037
5208
  get(this, 'transformedAttributes').forEach(function(name, type) {
4038
5209
  callback.call(binding, name, type);
@@ -4072,21 +5243,37 @@ function getValue(record, key) {
4072
5243
  }
4073
5244
 
4074
5245
  /**
4075
- `DS.attr` defines an attribute on a DS.Model.
5246
+ `DS.attr` defines an attribute on a [DS.Model](DS.Model.html).
4076
5247
  By default, attributes are passed through as-is, however you can specify an
4077
5248
  optional type to have the value automatically transformed.
4078
- Ember Data ships with four basic transform types:
4079
- 'string', 'number', 'boolean' and 'date'.
4080
- You can define your own transforms by subclassing DS.Transform.
5249
+ Ember Data ships with four basic transform types: `string`, `number`,
5250
+ `boolean` and `date`. You can define your own transforms by subclassing
5251
+ [DS.Transform](DS.Transform.html).
4081
5252
 
4082
- DS.attr takes an optional hash as a second parameter, currently
5253
+ `DS.attr` takes an optional hash as a second parameter, currently
4083
5254
  supported options are:
4084
- 'defaultValue': Pass a string or a function to be called to set the attribute
5255
+
5256
+ - `defaultValue`: Pass a string or a function to be called to set the attribute
4085
5257
  to a default value if none is supplied.
4086
5258
 
5259
+ Example
5260
+
5261
+ ```javascript
5262
+ var attr = DS.attr;
5263
+
5264
+ App.User = DS.Model.extend({
5265
+ username: attr('string'),
5266
+ email: attr('string'),
5267
+ verified: attr('boolean', {defaultValue: false})
5268
+ });
5269
+ ```
5270
+
5271
+ @namespace
4087
5272
  @method attr
5273
+ @for DS
4088
5274
  @param {String} type the attribute type
4089
5275
  @param {Object} options a hash of options
5276
+ @return {Attribute}
4090
5277
  */
4091
5278
 
4092
5279
  DS.attr = function(type, options) {
@@ -4102,7 +5289,14 @@ DS.attr = function(type, options) {
4102
5289
  if (arguments.length > 1) {
4103
5290
 
4104
5291
  var oldValue = this._attributes[key] || this._inFlightAttributes[key] || this._data[key];
4105
- this.send('didSetProperty', { name: key, oldValue: oldValue, originalValue: this._data[key], value: value });
5292
+
5293
+ this.send('didSetProperty', {
5294
+ name: key,
5295
+ oldValue: oldValue,
5296
+ originalValue: this._data[key],
5297
+ value: value
5298
+ });
5299
+
4106
5300
  this._attributes[key] = value;
4107
5301
  return value;
4108
5302
  } else if (hasValue(this, key)) {
@@ -4655,21 +5849,22 @@ var get = Ember.get, set = Ember.set,
4655
5849
  function asyncBelongsTo(type, options, meta) {
4656
5850
  return Ember.computed(function(key, value) {
4657
5851
  var data = get(this, 'data'),
4658
- store = get(this, 'store');
5852
+ store = get(this, 'store'),
5853
+ promiseLabel = "DS: Async belongsTo " + this + " : " + key;
4659
5854
 
4660
5855
  if (arguments.length === 2) {
4661
5856
 
4662
- return value === undefined ? null : DS.PromiseObject.create({ promise: Ember.RSVP.resolve(value) });
5857
+ return value === undefined ? null : DS.PromiseObject.create({ promise: Ember.RSVP.resolve(value, promiseLabel) });
4663
5858
  }
4664
5859
 
4665
5860
  var link = data.links && data.links[key],
4666
5861
  belongsTo = data[key];
4667
5862
 
4668
5863
  if(!isNone(belongsTo)) {
4669
- var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo);
4670
- return DS.PromiseObject.create({promise: promise});
5864
+ var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo, promiseLabel);
5865
+ return DS.PromiseObject.create({ promise: promise});
4671
5866
  } else if (link) {
4672
- var resolver = Ember.RSVP.defer();
5867
+ var resolver = Ember.RSVP.defer("DS: Async belongsTo (link) " + this + " : " + key);
4673
5868
  store.findBelongsTo(this, link, meta, resolver);
4674
5869
  return DS.PromiseObject.create({ promise: resolver.promise });
4675
5870
  } else {
@@ -4678,6 +5873,53 @@ function asyncBelongsTo(type, options, meta) {
4678
5873
  }).property('data').meta(meta);
4679
5874
  }
4680
5875
 
5876
+ /**
5877
+ `DS.belongsTo` is used to define One-To-One and One-To-Many
5878
+ relationships on a [DS.Model](DS.Model.html).
5879
+
5880
+
5881
+ `DS.belongsTo` takes an optional hash as a second parameter, currently
5882
+ supported options are:
5883
+
5884
+ - `async`: A boolean value used to explicitly declare this to be an async relationship.
5885
+ - `inverse`: A string used to identify the inverse property on a
5886
+ related model in a One-To-Many relationship. See [Explicit Inverses](#toc_explicit-inverses)
5887
+
5888
+ #### One-To-One
5889
+ To declare a one-to-one relationship between two models, use
5890
+ `DS.belongsTo`:
5891
+
5892
+ ```javascript
5893
+ App.User = DS.Model.extend({
5894
+ profile: DS.belongsTo('profile')
5895
+ });
5896
+
5897
+ App.Profile = DS.Model.extend({
5898
+ user: DS.belongsTo('user')
5899
+ });
5900
+ ```
5901
+
5902
+ #### One-To-Many
5903
+ To declare a one-to-many relationship between two models, use
5904
+ `DS.belongsTo` in combination with `DS.hasMany`, like this:
5905
+
5906
+ ```javascript
5907
+ App.Post = DS.Model.extend({
5908
+ comments: DS.hasMany('comment')
5909
+ });
5910
+
5911
+ App.Comment = DS.Model.extend({
5912
+ post: DS.belongsTo('post')
5913
+ });
5914
+ ```
5915
+
5916
+ @namespace
5917
+ @method belongsTo
5918
+ @for DS
5919
+ @param {String or DS.Model} type the model type of the relationship
5920
+ @param {Object} options a hash of options
5921
+ @return {Ember.computed} relationship
5922
+ */
4681
5923
  DS.belongsTo = function(type, options) {
4682
5924
  if (typeof type === 'object') {
4683
5925
  options = type;
@@ -4719,7 +5961,7 @@ DS.belongsTo = function(type, options) {
4719
5961
  }).property('data').meta(meta);
4720
5962
  };
4721
5963
 
4722
- /*
5964
+ /**
4723
5965
  These observers observe all `belongsTo` relationships on the record. See
4724
5966
  `relationships/ext` to see how these observers get their dependencies.
4725
5967
 
@@ -4785,23 +6027,30 @@ var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
4785
6027
 
4786
6028
  function asyncHasMany(type, options, meta) {
4787
6029
  return Ember.computed(function(key, value) {
4788
- if (this._relationships[key]) { return this._relationships[key]; }
4789
-
4790
- var resolver = Ember.RSVP.defer();
4791
-
4792
- var relationship = buildRelationship(this, key, options, function(store, data) {
4793
- var link = data.links && data.links[key];
4794
-
4795
- if (link) {
4796
- return store.findHasMany(this, link, meta, resolver);
4797
- } else {
4798
- return store.findMany(this, data[key], meta.type, resolver);
4799
- }
4800
- });
6030
+ var relationship = this._relationships[key],
6031
+ promiseLabel = "DS: Async hasMany " + this + " : " + key;
6032
+
6033
+ if (!relationship) {
6034
+ var resolver = Ember.RSVP.defer(promiseLabel);
6035
+ relationship = buildRelationship(this, key, options, function(store, data) {
6036
+ var link = data.links && data.links[key];
6037
+ var rel;
6038
+ if (link) {
6039
+ rel = store.findHasMany(this, link, meta, resolver);
6040
+ } else {
6041
+ rel = store.findMany(this, data[key], meta.type, resolver);
6042
+ }
6043
+ // cache the promise so we can use it
6044
+ // when we come back and don't need to rebuild
6045
+ // the relationship.
6046
+ set(rel, 'promise', resolver.promise);
6047
+ return rel;
6048
+ });
6049
+ }
4801
6050
 
4802
- var promise = resolver.promise.then(function() {
6051
+ var promise = relationship.get('promise').then(function() {
4803
6052
  return relationship;
4804
- });
6053
+ }, null, "DS: Async hasMany records received");
4805
6054
 
4806
6055
  return DS.PromiseArray.create({ promise: promise });
4807
6056
  }).property('data').meta(meta);
@@ -4817,29 +6066,107 @@ function buildRelationship(record, key, options, callback) {
4817
6066
 
4818
6067
  var relationship = rels[key] = callback.call(record, store, data);
4819
6068
 
4820
- return setProperties(relationship, {
4821
- owner: record, name: key, isPolymorphic: options.polymorphic
6069
+ return setProperties(relationship, {
6070
+ owner: record, name: key, isPolymorphic: options.polymorphic
6071
+ });
6072
+ }
6073
+
6074
+ function hasRelationship(type, options) {
6075
+ options = options || {};
6076
+
6077
+ var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' };
6078
+
6079
+ if (options.async) {
6080
+ return asyncHasMany(type, options, meta);
6081
+ }
6082
+
6083
+ return Ember.computed(function(key, value) {
6084
+ return buildRelationship(this, key, options, function(store, data) {
6085
+ var records = data[key];
6086
+
6087
+ return store.findMany(this, data[key], meta.type);
6088
+ });
6089
+ }).property('data').meta(meta);
6090
+ }
6091
+
6092
+ /**
6093
+ `DS.hasMany` is used to define One-To-Many and Many-To-Many
6094
+ relationships on a [DS.Model](DS.Model.html).
6095
+
6096
+ `DS.hasMany` takes an optional hash as a second parameter, currently
6097
+ supported options are:
6098
+
6099
+ - `async`: A boolean value used to explicitly declare this to be an async relationship.
6100
+ - `inverse`: A string used to identify the inverse property on a related model.
6101
+
6102
+ #### One-To-Many
6103
+ To declare a one-to-many relationship between two models, use
6104
+ `DS.belongsTo` in combination with `DS.hasMany`, like this:
6105
+
6106
+ ```javascript
6107
+ App.Post = DS.Model.extend({
6108
+ comments: DS.hasMany('comment')
6109
+ });
6110
+
6111
+ App.Comment = DS.Model.extend({
6112
+ post: DS.belongsTo('post')
6113
+ });
6114
+ ```
6115
+
6116
+ #### Many-To-Many
6117
+ To declare a many-to-many relationship between two models, use
6118
+ `DS.hasMany`:
6119
+
6120
+ ```javascript
6121
+ App.Post = DS.Model.extend({
6122
+ tags: DS.hasMany('tag')
6123
+ });
6124
+
6125
+ App.Tag = DS.Model.extend({
6126
+ posts: DS.hasMany('post')
4822
6127
  });
4823
- }
6128
+ ```
4824
6129
 
4825
- function hasRelationship(type, options) {
4826
- options = options || {};
6130
+ #### Explicit Inverses
4827
6131
 
4828
- var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' };
6132
+ Ember Data will do its best to discover which relationships map to
6133
+ one another. In the one-to-many code above, for example, Ember Data
6134
+ can figure out that changing the `comments` relationship should update
6135
+ the `post` relationship on the inverse because post is the only
6136
+ relationship to that model.
4829
6137
 
4830
- if (options.async) {
4831
- return asyncHasMany(type, options, meta);
4832
- }
6138
+ However, sometimes you may have multiple `belongsTo`/`hasManys` for the
6139
+ same type. You can specify which property on the related model is
6140
+ the inverse using `DS.hasMany`'s `inverse` option:
4833
6141
 
4834
- return Ember.computed(function(key, value) {
4835
- return buildRelationship(this, key, options, function(store, data) {
4836
- var records = data[key];
6142
+ ```javascript
6143
+ var belongsTo = DS.belongsTo,
6144
+ hasMany = DS.hasMany;
6145
+
6146
+ App.Comment = DS.Model.extend({
6147
+ onePost: belongsTo('post'),
6148
+ twoPost: belongsTo('post'),
6149
+ redPost: belongsTo('post'),
6150
+ bluePost: belongsTo('post')
6151
+ });
4837
6152
 
4838
- return store.findMany(this, data[key], meta.type);
4839
- });
4840
- }).property('data').meta(meta);
4841
- }
6153
+ App.Post = DS.Model.extend({
6154
+ comments: hasMany('comment', {
6155
+ inverse: 'redPost'
6156
+ })
6157
+ });
6158
+ ```
4842
6159
 
6160
+ You can also specify an inverse on a `belongsTo`, which works how
6161
+ you'd expect.
6162
+
6163
+ @namespace
6164
+ @method hasMany
6165
+ @for DS
6166
+ @param {String or DS.Model} type the model type of the relationship
6167
+ @param {Object} options a hash of options
6168
+ @return {Ember.computed} relationship
6169
+ */
4843
6170
  DS.hasMany = function(type, options) {
4844
6171
  if (typeof type === 'object') {
4845
6172
  options = type;
@@ -4882,9 +6209,11 @@ DS.Model.reopen({
4882
6209
  This hook passes the class being set up, as well as the key and value
4883
6210
  being defined. So, for example, when the user does this:
4884
6211
 
4885
- DS.Model.extend({
4886
- parent: DS.belongsTo('user')
4887
- });
6212
+ ```javascript
6213
+ DS.Model.extend({
6214
+ parent: DS.belongsTo('user')
6215
+ });
6216
+ ```
4888
6217
 
4889
6218
  This hook would be called with "parent" as the key and the computed
4890
6219
  property returned by `DS.belongsTo` as the value.
@@ -4936,9 +6265,11 @@ DS.Model.reopenClass({
4936
6265
 
4937
6266
  For example, if you define a model like this:
4938
6267
 
4939
- App.Post = DS.Model.extend({
4940
- comments: DS.hasMany('comment')
4941
- });
6268
+ ```javascript
6269
+ App.Post = DS.Model.extend({
6270
+ comments: DS.hasMany('comment')
6271
+ });
6272
+ ```
4942
6273
 
4943
6274
  Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
4944
6275
 
@@ -5009,21 +6340,25 @@ DS.Model.reopenClass({
5009
6340
 
5010
6341
  For example, given the following model definition:
5011
6342
 
5012
- App.Blog = DS.Model.extend({
5013
- users: DS.hasMany('user'),
5014
- owner: DS.belongsTo('user'),
5015
- posts: DS.hasMany('post')
5016
- });
6343
+ ```javascript
6344
+ App.Blog = DS.Model.extend({
6345
+ users: DS.hasMany('user'),
6346
+ owner: DS.belongsTo('user'),
6347
+ posts: DS.hasMany('post')
6348
+ });
6349
+ ```
5017
6350
 
5018
6351
  This computed property would return a map describing these
5019
6352
  relationships, like this:
5020
6353
 
5021
- var relationships = Ember.get(App.Blog, 'relationships');
5022
- relationships.get(App.User);
5023
- //=> [ { name: 'users', kind: 'hasMany' },
5024
- // { name: 'owner', kind: 'belongsTo' } ]
5025
- relationships.get(App.Post);
5026
- //=> [ { name: 'posts', kind: 'hasMany' } ]
6354
+ ```javascript
6355
+ var relationships = Ember.get(App.Blog, 'relationships');
6356
+ relationships.get(App.User);
6357
+ //=> [ { name: 'users', kind: 'hasMany' },
6358
+ // { name: 'owner', kind: 'belongsTo' } ]
6359
+ relationships.get(App.Post);
6360
+ //=> [ { name: 'posts', kind: 'hasMany' } ]
6361
+ ```
5027
6362
 
5028
6363
  @property relationships
5029
6364
  @static
@@ -5059,20 +6394,24 @@ DS.Model.reopenClass({
5059
6394
  by the relationship kind. For example, given a model with this
5060
6395
  definition:
5061
6396
 
5062
- App.Blog = DS.Model.extend({
5063
- users: DS.hasMany('user'),
5064
- owner: DS.belongsTo('user'),
6397
+ ```javascript
6398
+ App.Blog = DS.Model.extend({
6399
+ users: DS.hasMany('user'),
6400
+ owner: DS.belongsTo('user'),
5065
6401
 
5066
- posts: DS.hasMany('post')
5067
- });
6402
+ posts: DS.hasMany('post')
6403
+ });
6404
+ ```
5068
6405
 
5069
6406
  This property would contain the following:
5070
6407
 
5071
- var relationshipNames = Ember.get(App.Blog, 'relationshipNames');
5072
- relationshipNames.hasMany;
5073
- //=> ['users', 'posts']
5074
- relationshipNames.belongsTo;
5075
- //=> ['owner']
6408
+ ```javascript
6409
+ var relationshipNames = Ember.get(App.Blog, 'relationshipNames');
6410
+ relationshipNames.hasMany;
6411
+ //=> ['users', 'posts']
6412
+ relationshipNames.belongsTo;
6413
+ //=> ['owner']
6414
+ ```
5076
6415
 
5077
6416
  @property relationshipNames
5078
6417
  @static
@@ -5098,17 +6437,21 @@ DS.Model.reopenClass({
5098
6437
 
5099
6438
  For example, given a model with this definition:
5100
6439
 
5101
- App.Blog = DS.Model.extend({
5102
- users: DS.hasMany('user'),
5103
- owner: DS.belongsTo('user'),
5104
-
5105
- posts: DS.hasMany('post')
5106
- });
6440
+ ```javascript
6441
+ App.Blog = DS.Model.extend({
6442
+ users: DS.hasMany('user'),
6443
+ owner: DS.belongsTo('user'),
6444
+
6445
+ posts: DS.hasMany('post')
6446
+ });
6447
+ ```
5107
6448
 
5108
6449
  This property would contain the following:
5109
6450
 
5110
- var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
5111
- //=> [ App.User, App.Post ]
6451
+ ```javascript
6452
+ var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
6453
+ //=> [ App.User, App.Post ]
6454
+ ```
5112
6455
 
5113
6456
  @property relatedTypes
5114
6457
  @static
@@ -5148,20 +6491,24 @@ DS.Model.reopenClass({
5148
6491
  For example, given a model with this
5149
6492
  definition:
5150
6493
 
5151
- App.Blog = DS.Model.extend({
5152
- users: DS.hasMany('user'),
5153
- owner: DS.belongsTo('user'),
6494
+ ```javascript
6495
+ App.Blog = DS.Model.extend({
6496
+ users: DS.hasMany('user'),
6497
+ owner: DS.belongsTo('user'),
5154
6498
 
5155
- posts: DS.hasMany('post')
5156
- });
6499
+ posts: DS.hasMany('post')
6500
+ });
6501
+ ```
5157
6502
 
5158
6503
  This property would contain the following:
5159
6504
 
5160
- var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName');
5161
- relationshipsByName.get('users');
5162
- //=> { key: 'users', kind: 'hasMany', type: App.User }
5163
- relationshipsByName.get('owner');
5164
- //=> { key: 'owner', kind: 'belongsTo', type: App.User }
6505
+ ```javascript
6506
+ var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName');
6507
+ relationshipsByName.get('users');
6508
+ //=> { key: 'users', kind: 'hasMany', type: App.User }
6509
+ relationshipsByName.get('owner');
6510
+ //=> { key: 'owner', kind: 'belongsTo', type: App.User }
6511
+ ```
5165
6512
 
5166
6513
  @property relationshipsByName
5167
6514
  @static
@@ -5200,25 +6547,28 @@ DS.Model.reopenClass({
5200
6547
 
5201
6548
  For example:
5202
6549
 
5203
- App.Blog = DS.Model.extend({
5204
- users: DS.hasMany('user'),
5205
- owner: DS.belongsTo('user'),
6550
+ ```javascript
5206
6551
 
5207
- posts: DS.hasMany('post'),
6552
+ App.Blog = DS.Model.extend({
6553
+ users: DS.hasMany('user'),
6554
+ owner: DS.belongsTo('user'),
5208
6555
 
5209
- title: DS.attr('string')
5210
- });
6556
+ posts: DS.hasMany('post'),
5211
6557
 
5212
- var fields = Ember.get(App.Blog, 'fields');
5213
- fields.forEach(function(field, kind) {
5214
- console.log(field, kind);
5215
- });
6558
+ title: DS.attr('string')
6559
+ });
6560
+
6561
+ var fields = Ember.get(App.Blog, 'fields');
6562
+ fields.forEach(function(field, kind) {
6563
+ console.log(field, kind);
6564
+ });
5216
6565
 
5217
- // prints:
5218
- // users, hasMany
5219
- // owner, belongsTo
5220
- // posts, hasMany
5221
- // title, attribute
6566
+ // prints:
6567
+ // users, hasMany
6568
+ // owner, belongsTo
6569
+ // posts, hasMany
6570
+ // title, attribute
6571
+ ```
5222
6572
 
5223
6573
  @property fields
5224
6574
  @static
@@ -5336,13 +6686,12 @@ DS.RecordArrayManager = Ember.Object.extend({
5336
6686
  },
5337
6687
 
5338
6688
  /**
5339
- This method is invoked whenever data is loaded into the store
5340
- by the adapter or updated by the adapter, or when an attribute
5341
- changes on a record.
6689
+ This method is invoked whenever data is loaded into the store by the
6690
+ adapter or updated by the adapter, or when a record has changed.
5342
6691
 
5343
- It updates all filters that a record belongs to.
6692
+ It updates all record arrays that a record belongs to.
5344
6693
 
5345
- To avoid thrashing, it only runs once per run loop per record.
6694
+ To avoid thrashing, it only runs at most once per run loop.
5346
6695
 
5347
6696
  @method updateRecordArrays
5348
6697
  @param {Class} type
@@ -5350,29 +6699,47 @@ DS.RecordArrayManager = Ember.Object.extend({
5350
6699
  */
5351
6700
  updateRecordArrays: function() {
5352
6701
  forEach(this.changedRecords, function(record) {
5353
- var type = record.constructor,
5354
- recordArrays = this.filteredRecordArrays.get(type),
5355
- filter;
6702
+ if (get(record, 'isDeleted')) {
6703
+ this._recordWasDeleted(record);
6704
+ } else {
6705
+ this._recordWasChanged(record);
6706
+ }
6707
+ }, this);
5356
6708
 
5357
- forEach(recordArrays, function(array) {
5358
- filter = get(array, 'filterFunction');
5359
- this.updateRecordArray(array, filter, type, record);
5360
- }, this);
6709
+ this.changedRecords = [];
6710
+ },
5361
6711
 
5362
- // loop through all manyArrays containing an unloaded copy of this
5363
- // clientId and notify them that the record was loaded.
5364
- var manyArrays = record._loadingRecordArrays;
6712
+ _recordWasDeleted: function (record) {
6713
+ var recordArrays = record._recordArrays;
5365
6714
 
5366
- if (manyArrays) {
5367
- for (var i=0, l=manyArrays.length; i<l; i++) {
5368
- manyArrays[i].loadedRecord();
5369
- }
6715
+ if (!recordArrays) { return; }
5370
6716
 
5371
- record._loadingRecordArrays = [];
5372
- }
6717
+ forEach(recordArrays, function(array) {
6718
+ array.removeRecord(record);
6719
+ });
6720
+ },
6721
+
6722
+ _recordWasChanged: function (record) {
6723
+ var type = record.constructor,
6724
+ recordArrays = this.filteredRecordArrays.get(type),
6725
+ filter;
6726
+
6727
+ forEach(recordArrays, function(array) {
6728
+ filter = get(array, 'filterFunction');
6729
+ this.updateRecordArray(array, filter, type, record);
5373
6730
  }, this);
5374
6731
 
5375
- this.changedRecords = [];
6732
+ // loop through all manyArrays containing an unloaded copy of this
6733
+ // clientId and notify them that the record was loaded.
6734
+ var manyArrays = record._loadingRecordArrays;
6735
+
6736
+ if (manyArrays) {
6737
+ for (var i=0, l=manyArrays.length; i<l; i++) {
6738
+ manyArrays[i].loadedRecord();
6739
+ }
6740
+
6741
+ record._loadingRecordArrays = [];
6742
+ }
5376
6743
  },
5377
6744
 
5378
6745
  /**
@@ -5404,23 +6771,6 @@ DS.RecordArrayManager = Ember.Object.extend({
5404
6771
  }
5405
6772
  },
5406
6773
 
5407
- /**
5408
- When a record is deleted, it is removed from all its
5409
- record arrays.
5410
-
5411
- @method remove
5412
- @param {DS.Model} record
5413
- */
5414
- remove: function(record) {
5415
- var recordArrays = record._recordArrays;
5416
-
5417
- if (!recordArrays) { return; }
5418
-
5419
- forEach(recordArrays, function(array) {
5420
- array.removeRecord(record);
5421
- });
5422
- },
5423
-
5424
6774
  /**
5425
6775
  This method is invoked if the `filterFunction` property is
5426
6776
  changed on a `DS.FilteredRecordArray`.
@@ -5535,15 +6885,19 @@ DS.InvalidError.prototype = Ember.create(Error.prototype);
5535
6885
 
5536
6886
  First, create a new subclass of `DS.Adapter`:
5537
6887
 
5538
- App.MyAdapter = DS.Adapter.extend({
5539
- // ...your code here
5540
- });
6888
+ ```javascript
6889
+ App.MyAdapter = DS.Adapter.extend({
6890
+ // ...your code here
6891
+ });
6892
+ ```
5541
6893
 
5542
6894
  To tell your store which adapter to use, set its `adapter` property:
5543
6895
 
5544
- App.store = DS.Store.create({
5545
- adapter: App.MyAdapter.create()
5546
- });
6896
+ ```javascript
6897
+ App.store = DS.Store.create({
6898
+ adapter: App.MyAdapter.create()
6899
+ });
6900
+ ```
5547
6901
 
5548
6902
  `DS.Adapter` is an abstract base class that you should override in your
5549
6903
  application to customize it for your backend. The minimum set of methods
@@ -5569,10 +6923,9 @@ DS.InvalidError.prototype = Ember.create(Error.prototype);
5569
6923
  @class Adapter
5570
6924
  @namespace DS
5571
6925
  @extends Ember.Object
5572
- @uses DS._Mappable
5573
6926
  */
5574
6927
 
5575
- DS.Adapter = Ember.Object.extend(DS._Mappable, {
6928
+ DS.Adapter = Ember.Object.extend({
5576
6929
 
5577
6930
  /**
5578
6931
  The `find()` method is invoked when the store is asked for a record that
@@ -5583,25 +6936,27 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
5583
6936
 
5584
6937
  Here is an example `find` implementation:
5585
6938
 
5586
- find: function(store, type, id) {
5587
- var url = type.url;
5588
- url = url.fmt(id);
6939
+ ```javascript
6940
+ find: function(store, type, id) {
6941
+ var url = type.url;
6942
+ url = url.fmt(id);
5589
6943
 
5590
- jQuery.getJSON(url, function(data) {
5591
- // data is a hash of key/value pairs. If your server returns a
5592
- // root, simply do something like:
5593
- // store.push(type, id, data.person)
5594
- store.push(type, id, data);
5595
- });
5596
- }
6944
+ jQuery.getJSON(url, function(data) {
6945
+ // data is a hash of key/value pairs. If your server returns a
6946
+ // root, simply do something like:
6947
+ // store.push(type, id, data.person)
6948
+ store.push(type, id, data);
6949
+ });
6950
+ }
6951
+ ```
5597
6952
 
5598
6953
  @method find
5599
6954
  */
5600
6955
  find: Ember.required(Function),
5601
6956
 
5602
6957
  /**
5603
- Optional
5604
6958
 
6959
+ @private
5605
6960
  @method findAll
5606
6961
  @param store
5607
6962
  @param type
@@ -5610,8 +6965,8 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
5610
6965
  findAll: null,
5611
6966
 
5612
6967
  /**
5613
- Optional
5614
6968
 
6969
+ @private
5615
6970
  @method findQuery
5616
6971
  @param store
5617
6972
  @param type
@@ -5634,10 +6989,12 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
5634
6989
  The `generateIdForRecord()` method will be invoked with the requesting store as
5635
6990
  the first parameter and the newly created record as the second parameter:
5636
6991
 
5637
- generateIdForRecord: function(store, record) {
5638
- var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision();
5639
- return uuid;
5640
- }
6992
+ ```javascript
6993
+ generateIdForRecord: function(store, record) {
6994
+ var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision();
6995
+ return uuid;
6996
+ }
6997
+ ```
5641
6998
 
5642
6999
  @method generateIdForRecord
5643
7000
  @param {DS.Store} store
@@ -5870,6 +7227,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
5870
7227
  },
5871
7228
 
5872
7229
  /**
7230
+ @private
5873
7231
  @method findAll
5874
7232
  @param store
5875
7233
  @param type
@@ -5884,6 +7242,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
5884
7242
  },
5885
7243
 
5886
7244
  /**
7245
+ @private
5887
7246
  @method findQuery
5888
7247
  @param store
5889
7248
  @param type
@@ -6014,11 +7373,11 @@ DS.FixtureAdapter = DS.Adapter.extend({
6014
7373
  }, get(adapter, 'latency'));
6015
7374
  } else {
6016
7375
  // Asynchronous, but at the of the runloop with zero latency
6017
- Ember.run.once(function() {
7376
+ Ember.run.schedule('actions', null, function() {
6018
7377
  resolve(callback.call(context));
6019
7378
  });
6020
7379
  }
6021
- });
7380
+ }, "DS: FixtureAdapter#simulateRemoteCall");
6022
7381
  }
6023
7382
  });
6024
7383
 
@@ -6140,9 +7499,9 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6140
7499
 
6141
7500
  @method normalize
6142
7501
  @param {subclass of DS.Model} type
6143
- @param {String} prop
6144
7502
  @param {Object} hash
6145
- @returns Object
7503
+ @param {String} prop
7504
+ @returns {Object}
6146
7505
  */
6147
7506
  normalize: function(type, hash, prop) {
6148
7507
  this.normalizeId(hash);
@@ -6151,7 +7510,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6151
7510
  this.normalizeRelationships(type, hash);
6152
7511
 
6153
7512
  if (this.normalizeHash && this.normalizeHash[prop]) {
6154
- return this.normalizeHash[prop](hash);
7513
+ this.normalizeHash[prop](hash);
6155
7514
  }
6156
7515
 
6157
7516
  return this._super(type, hash, prop);
@@ -6176,7 +7535,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6176
7535
  @method normalizePayload
6177
7536
  @param {subclass of DS.Model} type
6178
7537
  @param {Object} hash
6179
- @returns Object the normalized payload
7538
+ @returns {Object} the normalized payload
6180
7539
  */
6181
7540
  normalizePayload: function(type, payload) {
6182
7541
  return payload;
@@ -6323,7 +7682,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6323
7682
  @param {Object} payload
6324
7683
  @param {String} id
6325
7684
  @param {'find'|'createRecord'|'updateRecord'|'deleteRecord'} requestType
6326
- @returns Object the primary response to the original request
7685
+ @returns {Object} the primary response to the original request
6327
7686
  */
6328
7687
  extractSingle: function(store, primaryType, payload, recordId, requestType) {
6329
7688
  payload = this.normalizePayload(primaryType, payload);
@@ -6469,7 +7828,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6469
7828
  @param {subclass of DS.Model} type
6470
7829
  @param {Object} payload
6471
7830
  @param {'findAll'|'findMany'|'findHasMany'|'findQuery'} requestType
6472
- @returns {Array<Object>} The primary array that was returned in response
7831
+ @returns {Array} The primary array that was returned in response
6473
7832
  to the original query.
6474
7833
  */
6475
7834
  extractArray: function(store, primaryType, payload) {
@@ -6521,7 +7880,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6521
7880
  }],
6522
7881
  "comments": [{
6523
7882
  "id": "1",
6524
- "body": "FIRST
7883
+ "body": "FIRST"
6525
7884
  }],
6526
7885
  "users": [{
6527
7886
  "id": "1",
@@ -6572,7 +7931,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6572
7931
 
6573
7932
  @method typeForRoot
6574
7933
  @param {String} root
6575
- @returns String the model's typeKey
7934
+ @returns {String} the model's typeKey
6576
7935
  */
6577
7936
  typeForRoot: function(root) {
6578
7937
  return Ember.String.singularize(root);
@@ -6758,7 +8117,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6758
8117
  @method serializePolymorphicType
6759
8118
  @param {DS.Model} record
6760
8119
  @param {Object} json
6761
- @param relationship
8120
+ @param {Object} relationship
6762
8121
  */
6763
8122
  serializePolymorphicType: function(record, json, relationship) {
6764
8123
  var key = relationship.key,
@@ -6810,7 +8169,7 @@ var forEach = Ember.ArrayPolyfills.forEach;
6810
8169
 
6811
8170
  ### Conventional Names
6812
8171
 
6813
- Attribute names in your JSON payload should be the camelcased versions of
8172
+ Attribute names in your JSON payload should be the camelCased versions of
6814
8173
  the attributes in your Ember.js models.
6815
8174
 
6816
8175
  For example, if you have a `Person` model:
@@ -6861,14 +8220,14 @@ var forEach = Ember.ArrayPolyfills.forEach;
6861
8220
 
6862
8221
  ### Headers customization
6863
8222
 
6864
- Some APIs require HTTP headers, eg to provide an API key. An array of
8223
+ Some APIs require HTTP headers, e.g. to provide an API key. An array of
6865
8224
  headers can be added to the adapter which are passed with every request:
6866
8225
 
6867
8226
  ```js
6868
8227
  DS.RESTAdapter.reopen({
6869
8228
  headers: {
6870
8229
  "API_KEY": "secret key",
6871
- "ANOTHER_HEADER": "asdsada"
8230
+ "ANOTHER_HEADER": "Some header value"
6872
8231
  }
6873
8232
  });
6874
8233
  ```
@@ -6885,9 +8244,11 @@ DS.RESTAdapter = DS.Adapter.extend({
6885
8244
  Called by the store in order to fetch the JSON for a given
6886
8245
  type and ID.
6887
8246
 
6888
- It makes an Ajax request to a URL computed by `buildURL`, and returns a
8247
+ The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a
6889
8248
  promise for the resulting payload.
6890
8249
 
8250
+ This method performs an HTTP `GET` request with the id provided as part of the querystring.
8251
+
6891
8252
  @method find
6892
8253
  @see RESTAdapter/buildURL
6893
8254
  @see RESTAdapter/ajax
@@ -6904,9 +8265,10 @@ DS.RESTAdapter = DS.Adapter.extend({
6904
8265
  Called by the store in order to fetch a JSON array for all
6905
8266
  of the records for a given type.
6906
8267
 
6907
- It makes an Ajax request to a URL computed by `buildURL`, and returns a
8268
+ The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
6908
8269
  promise for the resulting payload.
6909
8270
 
8271
+ @private
6910
8272
  @method findAll
6911
8273
  @see RESTAdapter/buildURL
6912
8274
  @see RESTAdapter/ajax
@@ -6929,12 +8291,13 @@ DS.RESTAdapter = DS.Adapter.extend({
6929
8291
  Called by the store in order to fetch a JSON array for
6930
8292
  the records that match a particular query.
6931
8293
 
6932
- The query is a simple JavaScript object that will be passed directly
6933
- to the server as parameters.
6934
-
6935
- It makes an Ajax request to a URL computed by `buildURL`, and returns a
8294
+ The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
6936
8295
  promise for the resulting payload.
6937
8296
 
8297
+ The `query` argument is a simple JavaScript object that will be passed directly
8298
+ to the server as parameters.
8299
+
8300
+ @private
6938
8301
  @method findQuery
6939
8302
  @see RESTAdapter/buildURL
6940
8303
  @see RESTAdapter/ajax
@@ -6968,11 +8331,11 @@ DS.RESTAdapter = DS.Adapter.extend({
6968
8331
  ids[]=1&ids[]=2&ids[]=3
6969
8332
  ```
6970
8333
 
6971
- Many servers, such as Rails and PHP, will automatically convert this
8334
+ Many servers, such as Rails and PHP, will automatically convert this URL-encoded array
6972
8335
  into an Array for you on the server-side. If you want to encode the
6973
8336
  IDs, differently, just override this (one-line) method.
6974
8337
 
6975
- It makes an Ajax request to a URL computed by `buildURL`, and returns a
8338
+ The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
6976
8339
  promise for the resulting payload.
6977
8340
 
6978
8341
  @method findMany
@@ -7006,7 +8369,7 @@ DS.RESTAdapter = DS.Adapter.extend({
7006
8369
 
7007
8370
  This method will be called with the parent record and `/posts/1/comments`.
7008
8371
 
7009
- It will make an Ajax request to the originally specified URL.
8372
+ The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL.
7010
8373
  If the URL is host-relative (starting with a single slash), the
7011
8374
  request will use the host specified on the adapter (if any).
7012
8375
 
@@ -7049,7 +8412,7 @@ DS.RESTAdapter = DS.Adapter.extend({
7049
8412
 
7050
8413
  This method will be called with the parent record and `/people/1/group`.
7051
8414
 
7052
- It will make an Ajax request to the originally specified URL.
8415
+ The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL.
7053
8416
 
7054
8417
  @method findBelongsTo
7055
8418
  @see RESTAdapter/buildURL
@@ -7068,9 +8431,10 @@ DS.RESTAdapter = DS.Adapter.extend({
7068
8431
 
7069
8432
  /**
7070
8433
  Called by the store when a newly created record is
7071
- `save`d.
8434
+ saved via the `save` method on a model record instance.
7072
8435
 
7073
- It serializes the record, and `POST`s it to a URL generated by `buildURL`.
8436
+ The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request
8437
+ to a URL computed by `buildURL`.
7074
8438
 
7075
8439
  See `serialize` for information on how to customize the serialized form
7076
8440
  of a record.
@@ -7094,9 +8458,11 @@ DS.RESTAdapter = DS.Adapter.extend({
7094
8458
  },
7095
8459
 
7096
8460
  /**
7097
- Called by the store when an existing record is `save`d.
8461
+ Called by the store when an existing record is saved
8462
+ via the `save` method on a model record instance.
7098
8463
 
7099
- It serializes the record, and `POST`s it to a URL generated by `buildURL`.
8464
+ The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request
8465
+ to a URL computed by `buildURL`.
7100
8466
 
7101
8467
  See `serialize` for information on how to customize the serialized form
7102
8468
  of a record.
@@ -7122,9 +8488,9 @@ DS.RESTAdapter = DS.Adapter.extend({
7122
8488
  },
7123
8489
 
7124
8490
  /**
7125
- Called by the store when an deleted record is `save`d.
8491
+ Called by the store when a record is deleted.
7126
8492
 
7127
- It serializes the record, and `POST`s it to a URL generated by `buildURL`.
8493
+ The `deleteRecord` method makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`.
7128
8494
 
7129
8495
  @method deleteRecord
7130
8496
  @see RESTAdapter/buildURL
@@ -7230,9 +8596,9 @@ DS.RESTAdapter = DS.Adapter.extend({
7230
8596
  /**
7231
8597
  Takes an ajax response, and returns a relavant error.
7232
8598
 
7233
- By default, it has the following behavior:
8599
+ By default, the `ajaxError` method has the following behavior:
7234
8600
 
7235
- * It simply returns the ajax response.
8601
+ * It simply returns the ajax response (jqXHR).
7236
8602
 
7237
8603
  @method ajaxError
7238
8604
  @param jqXHR
@@ -7253,7 +8619,7 @@ DS.RESTAdapter = DS.Adapter.extend({
7253
8619
  or `extractArray` (depending on whether the original query was for one record or
7254
8620
  many records).
7255
8621
 
7256
- By default, it has the following behavior:
8622
+ By default, `ajax` method has the following behavior:
7257
8623
 
7258
8624
  * It sets the response `dataType` to `"json"`
7259
8625
  * If the HTTP method is not `"GET"`, it sets the `Content-Type` to be
@@ -7283,7 +8649,7 @@ DS.RESTAdapter = DS.Adapter.extend({
7283
8649
  };
7284
8650
 
7285
8651
  Ember.$.ajax(hash);
7286
- });
8652
+ }, "DS: RestAdapter#ajax " + type + " to " + url);
7287
8653
  },
7288
8654
 
7289
8655
  ajaxOptions: function(url, type, hash) {
@@ -7435,7 +8801,7 @@ var BLANK_REGEX = /^\s*$/;
7435
8801
 
7436
8802
  function loadUncountable(rules, uncountable) {
7437
8803
  for (var i = 0, length = uncountable.length; i < length; i++) {
7438
- rules.uncountable[uncountable[i]] = true;
8804
+ rules.uncountable[uncountable[i].toLowerCase()] = true;
7439
8805
  }
7440
8806
  }
7441
8807
 
@@ -7445,8 +8811,8 @@ function loadIrregular(rules, irregularPairs) {
7445
8811
  for (var i = 0, length = irregularPairs.length; i < length; i++) {
7446
8812
  pair = irregularPairs[i];
7447
8813
 
7448
- rules.irregular[pair[0]] = pair[1];
7449
- rules.irregularInverse[pair[1]] = pair[0];
8814
+ rules.irregular[pair[0].toLowerCase()] = pair[1];
8815
+ rules.irregularInverse[pair[1].toLowerCase()] = pair[0];
7450
8816
  }
7451
8817
  }
7452
8818
 
@@ -7511,7 +8877,7 @@ function loadIrregular(rules, irregularPairs) {
7511
8877
  function Inflector(ruleSet) {
7512
8878
  ruleSet = ruleSet || {};
7513
8879
  ruleSet.uncountable = ruleSet.uncountable || {};
7514
- ruleSet.irregularPairs= ruleSet.irregularPairs|| {};
8880
+ ruleSet.irregularPairs = ruleSet.irregularPairs || {};
7515
8881
 
7516
8882
  var rules = this.rules = {
7517
8883
  plurals: ruleSet.plurals || [],
@@ -7532,7 +8898,7 @@ Inflector.prototype = {
7532
8898
  @param {String} string
7533
8899
  */
7534
8900
  plural: function(regex, string) {
7535
- this.rules.plurals.push([regex, string]);
8901
+ this.rules.plurals.push([regex, string.toLowerCase()]);
7536
8902
  },
7537
8903
 
7538
8904
  /**
@@ -7541,7 +8907,7 @@ Inflector.prototype = {
7541
8907
  @param {String} string
7542
8908
  */
7543
8909
  singular: function(regex, string) {
7544
- this.rules.singular.push([regex, string]);
8910
+ this.rules.singular.push([regex, string.toLowerCase()]);
7545
8911
  },
7546
8912
 
7547
8913
  /**
@@ -7549,7 +8915,7 @@ Inflector.prototype = {
7549
8915
  @param {String} regex
7550
8916
  */
7551
8917
  uncountable: function(string) {
7552
- loadUncountable(this.rules, [string]);
8918
+ loadUncountable(this.rules, [string.toLowerCase()]);
7553
8919
  },
7554
8920
 
7555
8921
  /**
@@ -7720,27 +9086,27 @@ Ember.Inflector.defaultRules = {
7720
9086
 
7721
9087
 
7722
9088
  (function() {
7723
- if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
7724
- /**
7725
- See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
7726
-
7727
- @method pluralize
7728
- @for String
7729
- */
7730
- String.prototype.pluralize = function() {
7731
- return Ember.String.pluralize(this);
7732
- };
7733
-
7734
- /**
7735
- See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
7736
-
7737
- @method singularize
7738
- @for String
7739
- */
7740
- String.prototype.singularize = function() {
7741
- return Ember.String.singularize(this);
7742
- };
7743
- }
9089
+ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
9090
+ /**
9091
+ See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
9092
+
9093
+ @method pluralize
9094
+ @for String
9095
+ */
9096
+ String.prototype.pluralize = function() {
9097
+ return Ember.String.pluralize(this);
9098
+ };
9099
+
9100
+ /**
9101
+ See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
9102
+
9103
+ @method singularize
9104
+ @for String
9105
+ */
9106
+ String.prototype.singularize = function() {
9107
+ return Ember.String.singularize(this);
9108
+ };
9109
+ }
7744
9110
 
7745
9111
  })();
7746
9112
 
@@ -7800,26 +9166,9 @@ DS.ActiveModelSerializer = DS.RESTSerializer.extend({
7800
9166
  },
7801
9167
 
7802
9168
  /**
7803
- Serialize has-may relationship when it is configured as embedded objects.
7804
-
7805
- @method serializeHasMany
9169
+ Does not serialize hasMany relationships by default.
7806
9170
  */
7807
- serializeHasMany: function(record, json, relationship) {
7808
- var key = relationship.key,
7809
- attrs = get(this, 'attrs'),
7810
- embed = attrs && attrs[key] && attrs[key].embedded === 'always';
7811
-
7812
- if (embed) {
7813
- json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
7814
- var data = relation.serialize(),
7815
- primaryKey = get(this, 'primaryKey');
7816
-
7817
- data[primaryKey] = get(relation, primaryKey);
7818
-
7819
- return data;
7820
- }, this);
7821
- }
7822
- },
9171
+ serializeHasMany: Ember.K,
7823
9172
 
7824
9173
  /**
7825
9174
  Underscores the JSON root keys when serializing.
@@ -7864,6 +9213,68 @@ DS.ActiveModelSerializer = DS.RESTSerializer.extend({
7864
9213
  return Ember.String.singularize(camelized);
7865
9214
  },
7866
9215
 
9216
+ /**
9217
+ Add extra step to `DS.RESTSerializer.normalize` so links are
9218
+ normalized.
9219
+
9220
+ If your payload looks like this
9221
+
9222
+ ```js
9223
+ {
9224
+ "post": {
9225
+ "id": 1,
9226
+ "title": "Rails is omakase",
9227
+ "links": { "flagged_comments": "api/comments/flagged" }
9228
+ }
9229
+ }
9230
+ ```
9231
+ The normalized version would look like this
9232
+
9233
+ ```js
9234
+ {
9235
+ "post": {
9236
+ "id": 1,
9237
+ "title": "Rails is omakase",
9238
+ "links": { "flaggedComments": "api/comments/flagged" }
9239
+ }
9240
+ }
9241
+ ```
9242
+
9243
+ @method normalize
9244
+ @param {subclass of DS.Model} type
9245
+ @param {Object} hash
9246
+ @param {String} prop
9247
+ @returns Object
9248
+ */
9249
+
9250
+ normalize: function(type, hash, prop) {
9251
+ this.normalizeLinks(hash);
9252
+
9253
+ return this._super(type, hash, prop);
9254
+ },
9255
+
9256
+ /**
9257
+ Convert `snake_cased` links to `camelCase`
9258
+
9259
+ @method normalizeLinks
9260
+ @param {Object} hash
9261
+ */
9262
+
9263
+ normalizeLinks: function(data){
9264
+ if (data.links) {
9265
+ var links = data.links;
9266
+
9267
+ for (var link in links) {
9268
+ var camelizedLink = Ember.String.camelize(link);
9269
+
9270
+ if (camelizedLink !== link) {
9271
+ links[camelizedLink] = links[link];
9272
+ delete links[link];
9273
+ }
9274
+ }
9275
+ }
9276
+ },
9277
+
7867
9278
  /**
7868
9279
  Normalize the polymorphic type from the JSON.
7869
9280
 
@@ -7914,8 +9325,66 @@ DS.ActiveModelSerializer = DS.RESTSerializer.extend({
7914
9325
  }
7915
9326
  }, this);
7916
9327
  }
9328
+ }
9329
+ });
9330
+
9331
+ })();
9332
+
9333
+
9334
+
9335
+ (function() {
9336
+ var get = Ember.get;
9337
+ var forEach = Ember.EnumerableUtils.forEach;
9338
+
9339
+ /**
9340
+ The EmbeddedRecordsMixin allows you to add embedded record support to your
9341
+ serializers.
9342
+ To set up embedded records, you include the mixin into the serializer and then
9343
+ define your embedded relations.
9344
+
9345
+ ```js
9346
+ App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
9347
+ attrs: {
9348
+ comments: {embedded: 'always'}
9349
+ }
9350
+ })
9351
+ ```
9352
+
9353
+ Currently only `{embedded: 'always'}` records are supported.
9354
+
9355
+ @class EmbeddedRecordsMixin
9356
+ @namespace DS
9357
+ */
9358
+ DS.EmbeddedRecordsMixin = Ember.Mixin.create({
9359
+
9360
+ /**
9361
+ Serialize has-may relationship when it is configured as embedded objects.
9362
+
9363
+ @method serializeHasMany
9364
+ */
9365
+ serializeHasMany: function(record, json, relationship) {
9366
+ var key = relationship.key,
9367
+ attrs = get(this, 'attrs'),
9368
+ embed = attrs && attrs[key] && attrs[key].embedded === 'always';
9369
+
9370
+ if (embed) {
9371
+ json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
9372
+ var data = relation.serialize(),
9373
+ primaryKey = get(this, 'primaryKey');
9374
+
9375
+ data[primaryKey] = get(relation, primaryKey);
9376
+
9377
+ return data;
9378
+ }, this);
9379
+ }
7917
9380
  },
7918
9381
 
9382
+ /**
9383
+ Extract embedded objects out of the payload for a single object
9384
+ and add them as sideloaded objects instead.
9385
+
9386
+ @method extractSingle
9387
+ */
7919
9388
  extractSingle: function(store, primaryType, payload, recordId, requestType) {
7920
9389
  var root = this.keyForAttribute(primaryType.typeKey),
7921
9390
  partial = payload[root];
@@ -7925,6 +9394,12 @@ DS.ActiveModelSerializer = DS.RESTSerializer.extend({
7925
9394
  return this._super(store, primaryType, payload, recordId, requestType);
7926
9395
  },
7927
9396
 
9397
+ /**
9398
+ Extract embedded objects out of a standard payload
9399
+ and add them as sideloaded objects instead.
9400
+
9401
+ @method extractArray
9402
+ */
7928
9403
  extractArray: function(store, type, payload) {
7929
9404
  var root = this.keyForAttribute(type.typeKey),
7930
9405
  partials = payload[Ember.String.pluralize(root)];
@@ -7980,7 +9455,6 @@ function updatePayloadWithEmbedded(store, serializer, type, partial, payload) {
7980
9455
  }
7981
9456
  }, serializer);
7982
9457
  }
7983
-
7984
9458
  })();
7985
9459
 
7986
9460
 
@@ -7999,6 +9473,10 @@ var forEach = Ember.EnumerableUtils.forEach;
7999
9473
  [active_model_serializers](http://github.com/rails-api/active_model_serializers)
8000
9474
  Ruby gem.
8001
9475
 
9476
+ This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
9477
+ decamelization and pluralization methods to normalize the serialized JSON into a
9478
+ format that is compatible with a conventional Rails backend and Ember Data.
9479
+
8002
9480
  ## JSON Structure
8003
9481
 
8004
9482
  The ActiveModelAdapter expects the JSON returned from your server to follow
@@ -8040,8 +9518,8 @@ var forEach = Ember.EnumerableUtils.forEach;
8040
9518
  DS.ActiveModelAdapter = DS.RESTAdapter.extend({
8041
9519
  defaultSerializer: '_ams',
8042
9520
  /**
8043
- The ActiveModelAdapter overrides the `pathForType` method
8044
- to build underscored URLs.
9521
+ The ActiveModelAdapter overrides the `pathForType` method to build
9522
+ underscored URLs by decamelizing and pluralizing the object type name.
8045
9523
 
8046
9524
  ```js
8047
9525
  this.pathForType("famousPerson");
@@ -8062,6 +9540,13 @@ DS.ActiveModelAdapter = DS.RESTAdapter.extend({
8062
9540
  to return a DS.InvalidError for all 422 Unprocessable Entity
8063
9541
  responses.
8064
9542
 
9543
+ A 422 HTTP response from the server generally implies that the request
9544
+ was well formed but the API was unable to process it because the
9545
+ content was not semantically correct or meaningful per the API.
9546
+
9547
+ For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
9548
+ https://tools.ietf.org/html/rfc4918#section-11.2
9549
+
8065
9550
  @method ajaxError
8066
9551
  @param jqXHR
8067
9552
  @returns error