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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/dist/ember-data-spade.js +49 -0
- data/dist/ember-data-tests.js +75 -0
- data/dist/ember-data.js +2114 -629
- data/dist/ember-data.min.js +15 -14
- data/dist/ember-data.prod.js +2115 -630
- data/lib/ember/data/version.rb +1 -1
- metadata +7 -4
data/dist/ember-data.min.js
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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.");
|
data/dist/ember-data.prod.js
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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.
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1227
|
-
|
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(
|
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
|
-
})
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
-
}
|
3200
|
+
}, "DS: Extract payload of '" + type + "'");
|
2618
3201
|
}
|
2619
3202
|
|
2620
|
-
function _findMany(adapter, store, type, ids, owner
|
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
|
-
}
|
3212
|
+
}, null, "DS: Extract payload of " + type);
|
2630
3213
|
}
|
2631
3214
|
|
2632
|
-
function _findHasMany(adapter, store, record, link, relationship
|
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
|
-
}
|
3225
|
+
}, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type);
|
2643
3226
|
}
|
2644
3227
|
|
2645
|
-
function _findBelongsTo(adapter, store, record, link, relationship
|
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
|
-
}
|
3238
|
+
}, null, "DS: Extract payload of " + record + " : " + relationship.type);
|
2656
3239
|
}
|
2657
3240
|
|
2658
|
-
function _findAll(adapter, store, type, sinceToken
|
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
|
-
}
|
3252
|
+
}, null, "DS: Extract payload of findAll " + type);
|
2670
3253
|
}
|
2671
3254
|
|
2672
|
-
function _findQuery(adapter, store, type, query, recordArray
|
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
|
-
}
|
3265
|
+
}, null, "DS: Extract payload of findQuery " + type);
|
2683
3266
|
}
|
2684
3267
|
|
2685
|
-
function _commit(adapter, store, operation, record
|
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
|
-
}
|
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
|
-
|
2724
|
-
|
2725
|
-
|
2726
|
-
|
2727
|
-
|
2728
|
-
|
2729
|
-
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2734
|
-
|
2735
|
-
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
2742
|
-
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
2746
|
-
|
2747
|
-
|
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
|
-
|
2766
|
-
|
2767
|
-
|
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
|
-
|
2771
|
-
|
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
|
3368
|
+
### Events and Flags
|
2774
3369
|
|
2775
|
-
A state may implement zero or more events
|
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
|
-
|
2781
|
-
current state. If no method is found, it will search the current
|
2782
|
-
parent, and then its grandparent, and so on until reaching
|
2783
|
-
the hierarchy. If the root is reached without an event
|
2784
|
-
an exception will be raised. This can be very
|
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
|
-
|
2790
|
-
|
2791
|
-
|
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
|
-
|
2798
|
-
|
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
|
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
|
-
*
|
3411
|
+
* uncommitted <-- currentState
|
2813
3412
|
* inFlight
|
2814
3413
|
* updated
|
2815
3414
|
* inFlight
|
2816
3415
|
|
2817
|
-
If we are currently in the `
|
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
|
-
|
2833
|
-
|
2834
|
-
|
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
|
-
|
2840
|
-
|
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
|
-
|
2858
|
-
|
2859
|
-
|
2860
|
-
|
2861
|
-
|
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
|
-
|
2866
|
-
|
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,
|
2971
|
-
get(record, 'store').reloadRecord(record
|
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,
|
3252
|
-
get(record, 'store').reloadRecord(record
|
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
|
-
|
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
|
-
}).
|
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
|
-
|
3451
|
-
|
4297
|
+
`serialize` takes an optional hash as a parameter, currently
|
4298
|
+
supported options are:
|
3452
4299
|
|
3453
|
-
|
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
|
4313
|
+
Use [DS.JSONSerializer](DS.JSONSerializer.html) to
|
3465
4314
|
get the JSON representation of a record.
|
3466
4315
|
|
3467
|
-
|
3468
|
-
|
4316
|
+
`toJSON` takes an optional hash as a parameter, currently
|
4317
|
+
supported options are:
|
3469
4318
|
|
3470
|
-
|
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
|
-
@
|
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
|
-
|
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
|
-
|
4600
|
+
Returns an object, whose keys are changed properties, and value is
|
4601
|
+
an [oldProp, newProp] array.
|
3681
4602
|
|
3682
|
-
|
4603
|
+
Example
|
4604
|
+
|
4605
|
+
```javascript
|
4606
|
+
App.Mascot = DS.Model.extend({
|
4607
|
+
name: attr('string')
|
4608
|
+
});
|
3683
4609
|
|
3684
|
-
|
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
|
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
|
4904
|
+
var record = this;
|
3891
4905
|
|
3892
|
-
|
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:
|
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
|
3975
|
-
from inadvertently calling `create()` instead
|
3976
|
-
still able to create instances
|
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
|
-
|
4080
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
4789
|
-
|
4790
|
-
|
4791
|
-
|
4792
|
-
|
4793
|
-
|
4794
|
-
|
4795
|
-
|
4796
|
-
|
4797
|
-
|
4798
|
-
|
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 =
|
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
|
-
|
4826
|
-
options = options || {};
|
6130
|
+
#### Explicit Inverses
|
4827
6131
|
|
4828
|
-
|
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
|
-
|
4831
|
-
|
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
|
-
|
4835
|
-
|
4836
|
-
|
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
|
-
|
4839
|
-
|
4840
|
-
|
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
|
-
|
4886
|
-
|
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
|
-
|
4940
|
-
|
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
|
-
|
5013
|
-
|
5014
|
-
|
5015
|
-
|
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
|
-
|
5022
|
-
|
5023
|
-
|
5024
|
-
|
5025
|
-
|
5026
|
-
|
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
|
-
|
5063
|
-
|
5064
|
-
|
6397
|
+
```javascript
|
6398
|
+
App.Blog = DS.Model.extend({
|
6399
|
+
users: DS.hasMany('user'),
|
6400
|
+
owner: DS.belongsTo('user'),
|
5065
6401
|
|
5066
|
-
|
5067
|
-
|
6402
|
+
posts: DS.hasMany('post')
|
6403
|
+
});
|
6404
|
+
```
|
5068
6405
|
|
5069
6406
|
This property would contain the following:
|
5070
6407
|
|
5071
|
-
|
5072
|
-
|
5073
|
-
|
5074
|
-
|
5075
|
-
|
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
|
-
|
5102
|
-
|
5103
|
-
|
5104
|
-
|
5105
|
-
|
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
|
-
|
5111
|
-
|
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
|
-
|
5152
|
-
|
5153
|
-
|
6494
|
+
```javascript
|
6495
|
+
App.Blog = DS.Model.extend({
|
6496
|
+
users: DS.hasMany('user'),
|
6497
|
+
owner: DS.belongsTo('user'),
|
5154
6498
|
|
5155
|
-
|
5156
|
-
|
6499
|
+
posts: DS.hasMany('post')
|
6500
|
+
});
|
6501
|
+
```
|
5157
6502
|
|
5158
6503
|
This property would contain the following:
|
5159
6504
|
|
5160
|
-
|
5161
|
-
|
5162
|
-
|
5163
|
-
|
5164
|
-
|
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
|
-
|
5204
|
-
users: DS.hasMany('user'),
|
5205
|
-
owner: DS.belongsTo('user'),
|
6550
|
+
```javascript
|
5206
6551
|
|
5207
|
-
|
6552
|
+
App.Blog = DS.Model.extend({
|
6553
|
+
users: DS.hasMany('user'),
|
6554
|
+
owner: DS.belongsTo('user'),
|
5208
6555
|
|
5209
|
-
|
5210
|
-
});
|
6556
|
+
posts: DS.hasMany('post'),
|
5211
6557
|
|
5212
|
-
|
5213
|
-
|
5214
|
-
|
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
|
-
|
5218
|
-
|
5219
|
-
|
5220
|
-
|
5221
|
-
|
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
|
-
|
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
|
6692
|
+
It updates all record arrays that a record belongs to.
|
5344
6693
|
|
5345
|
-
To avoid thrashing, it only runs once per run loop
|
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
|
-
|
5354
|
-
|
5355
|
-
|
6702
|
+
if (get(record, 'isDeleted')) {
|
6703
|
+
this._recordWasDeleted(record);
|
6704
|
+
} else {
|
6705
|
+
this._recordWasChanged(record);
|
6706
|
+
}
|
6707
|
+
}, this);
|
5356
6708
|
|
5357
|
-
|
5358
|
-
|
5359
|
-
this.updateRecordArray(array, filter, type, record);
|
5360
|
-
}, this);
|
6709
|
+
this.changedRecords = [];
|
6710
|
+
},
|
5361
6711
|
|
5362
|
-
|
5363
|
-
|
5364
|
-
var manyArrays = record._loadingRecordArrays;
|
6712
|
+
_recordWasDeleted: function (record) {
|
6713
|
+
var recordArrays = record._recordArrays;
|
5365
6714
|
|
5366
|
-
|
5367
|
-
for (var i=0, l=manyArrays.length; i<l; i++) {
|
5368
|
-
manyArrays[i].loadedRecord();
|
5369
|
-
}
|
6715
|
+
if (!recordArrays) { return; }
|
5370
6716
|
|
5371
|
-
|
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
|
-
|
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
|
-
|
5539
|
-
|
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
|
-
|
5545
|
-
|
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(
|
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
|
-
|
5587
|
-
|
5588
|
-
|
6939
|
+
```javascript
|
6940
|
+
find: function(store, type, id) {
|
6941
|
+
var url = type.url;
|
6942
|
+
url = url.fmt(id);
|
5589
6943
|
|
5590
|
-
|
5591
|
-
|
5592
|
-
|
5593
|
-
|
5594
|
-
|
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
|
-
|
5638
|
-
|
5639
|
-
|
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.
|
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
|
-
@
|
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
|
-
|
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
|
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
|
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,
|
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": "
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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`
|
8434
|
+
saved via the `save` method on a model record instance.
|
7072
8435
|
|
7073
|
-
|
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
|
8461
|
+
Called by the store when an existing record is saved
|
8462
|
+
via the `save` method on a model record instance.
|
7098
8463
|
|
7099
|
-
|
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
|
8491
|
+
Called by the store when a record is deleted.
|
7126
8492
|
|
7127
|
-
|
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,
|
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,
|
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
|
-
|
7804
|
-
|
7805
|
-
@method serializeHasMany
|
9169
|
+
Does not serialize hasMany relationships by default.
|
7806
9170
|
*/
|
7807
|
-
serializeHasMany:
|
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
|
-
|
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
|