diff options
Diffstat (limited to 'imdb-lookup/js/libs/ember-data-1.0.0-beta.12.js')
-rw-r--r-- | imdb-lookup/js/libs/ember-data-1.0.0-beta.12.js | 12945 |
1 files changed, 0 insertions, 12945 deletions
diff --git a/imdb-lookup/js/libs/ember-data-1.0.0-beta.12.js b/imdb-lookup/js/libs/ember-data-1.0.0-beta.12.js deleted file mode 100644 index a2851b9..0000000 --- a/imdb-lookup/js/libs/ember-data-1.0.0-beta.12.js +++ /dev/null @@ -1,12945 +0,0 @@ -(function(global){ -var enifed, requireModule, eriuqer, requirejs; - -(function() { - - var _isArray; - if (!Array.isArray) { - _isArray = function (x) { - return Object.prototype.toString.call(x) === "[object Array]"; - }; - } else { - _isArray = Array.isArray; - } - - var registry = {}, seen = {}, state = {}; - var FAILED = false; - - enifed = function(name, deps, callback) { - - if (!_isArray(deps)) { - callback = deps; - deps = []; - } - - registry[name] = { - deps: deps, - callback: callback - }; - }; - - function reify(deps, name, seen) { - var length = deps.length; - var reified = new Array(length); - var dep; - var exports; - - for (var i = 0, l = length; i < l; i++) { - dep = deps[i]; - if (dep === 'exports') { - exports = reified[i] = seen; - } else { - reified[i] = eriuqer(resolve(dep, name)); - } - } - - return { - deps: reified, - exports: exports - }; - } - - requirejs = eriuqer = requireModule = function(name) { - if (state[name] !== FAILED && - seen.hasOwnProperty(name)) { - return seen[name]; - } - - if (!registry[name]) { - throw new Error('Could not find module ' + name); - } - - var mod = registry[name]; - var reified; - var module; - var loaded = false; - - seen[name] = { }; // placeholder for run-time cycles - - try { - reified = reify(mod.deps, name, seen[name]); - module = mod.callback.apply(this, reified.deps); - loaded = true; - } finally { - if (!loaded) { - state[name] = FAILED; - } - } - - return reified.exports ? seen[name] : (seen[name] = module); - }; - - function resolve(child, name) { - if (child.charAt(0) !== '.') { return child; } - - var parts = child.split('/'); - var nameParts = name.split('/'); - var parentBase; - - if (nameParts.length === 1) { - parentBase = nameParts; - } else { - parentBase = nameParts.slice(0, -1); - } - - for (var i = 0, l = parts.length; i < l; i++) { - var part = parts[i]; - - if (part === '..') { parentBase.pop(); } - else if (part === '.') { continue; } - else { parentBase.push(part); } - } - - return parentBase.join('/'); - } - - requirejs.entries = requirejs._eak_seen = registry; - requirejs.clear = function(){ - requirejs.entries = requirejs._eak_seen = registry = {}; - seen = state = {}; - }; -})(); - -enifed("activemodel-adapter", - ["activemodel-adapter/system","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var ActiveModelAdapter = __dependency1__.ActiveModelAdapter; - var ActiveModelSerializer = __dependency1__.ActiveModelSerializer; - - __exports__.ActiveModelAdapter = ActiveModelAdapter; - __exports__.ActiveModelSerializer = ActiveModelSerializer; - }); -enifed("activemodel-adapter/setup-container", - ["ember-data/system/container_proxy","activemodel-adapter/system/active_model_serializer","activemodel-adapter/system/active_model_adapter","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var ContainerProxy = __dependency1__["default"]; - var ActiveModelSerializer = __dependency2__["default"]; - var ActiveModelAdapter = __dependency3__["default"]; - - __exports__["default"] = function setupActiveModelAdapter(container, application){ - var proxy = new ContainerProxy(container); - proxy.registerDeprecations([ - { deprecated: 'serializer:_ams', valid: 'serializer:-active-model' }, - { deprecated: 'adapter:_ams', valid: 'adapter:-active-model' } - ]); - - container.register('serializer:-active-model', ActiveModelSerializer); - container.register('adapter:-active-model', ActiveModelAdapter); - }; - }); -enifed("activemodel-adapter/system", - ["activemodel-adapter/system/active_model_adapter","activemodel-adapter/system/active_model_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var ActiveModelAdapter = __dependency1__["default"]; - var ActiveModelSerializer = __dependency2__["default"]; - - __exports__.ActiveModelAdapter = ActiveModelAdapter; - __exports__.ActiveModelSerializer = ActiveModelSerializer; - }); -enifed("activemodel-adapter/system/active_model_adapter", - ["ember-data/adapters","ember-data/system/adapter","ember-inflector","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var RESTAdapter = __dependency1__.RESTAdapter; - var InvalidError = __dependency2__.InvalidError; - var pluralize = __dependency3__.pluralize; - - /** - @module ember-data - */ - - var decamelize = Ember.String.decamelize, - underscore = Ember.String.underscore; - - /** - The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate - with a JSON API that uses an underscored naming convention instead of camelCasing. - It has been designed to work out of the box with the - [active_model_serializers](http://github.com/rails-api/active_model_serializers) - Ruby gem. This Adapter expects specific settings using ActiveModel::Serializers, - `embed :ids, embed_in_root: true` which sideloads the records. - - This adapter extends the DS.RESTAdapter by making consistent use of the camelization, - decamelization and pluralization methods to normalize the serialized JSON into a - format that is compatible with a conventional Rails backend and Ember Data. - - ## JSON Structure - - The ActiveModelAdapter expects the JSON returned from your server to follow - the REST adapter conventions substituting underscored keys for camelcased ones. - - Unlike the DS.RESTAdapter, async relationship keys must be the singular form - of the relationship name, followed by "_id" for DS.belongsTo relationships, - or "_ids" for DS.hasMany relationships. - - ### Conventional Names - - Attribute names in your JSON payload should be the underscored versions of - the attributes in your Ember.js models. - - For example, if you have a `Person` model: - - ```js - App.FamousPerson = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string') - }); - ``` - - The JSON returned should look like this: - - ```js - { - "famous_person": { - "id": 1, - "first_name": "Barack", - "last_name": "Obama", - "occupation": "President" - } - } - ``` - - Let's imagine that `Occupation` is just another model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.belongsTo('occupation') - }); - - App.Occupation = DS.Model.extend({ - name: DS.attr('string'), - salary: DS.attr('number'), - people: DS.hasMany('person') - }); - ``` - - The JSON needed to avoid extra server calls, should look like this: - - ```js - { - "people": [{ - "id": 1, - "first_name": "Barack", - "last_name": "Obama", - "occupation_id": 1 - }], - - "occupations": [{ - "id": 1, - "name": "President", - "salary": 100000, - "person_ids": [1] - }] - } - ``` - - @class ActiveModelAdapter - @constructor - @namespace DS - @extends DS.RESTAdapter - **/ - - var ActiveModelAdapter = RESTAdapter.extend({ - defaultSerializer: '-active-model', - /** - The ActiveModelAdapter overrides the `pathForType` method to build - underscored URLs by decamelizing and pluralizing the object type name. - - ```js - this.pathForType("famousPerson"); - //=> "famous_people" - ``` - - @method pathForType - @param {String} type - @return String - */ - pathForType: function(type) { - var decamelized = decamelize(type); - var underscored = underscore(decamelized); - return pluralize(underscored); - }, - - /** - The ActiveModelAdapter overrides the `ajaxError` method - to return a DS.InvalidError for all 422 Unprocessable Entity - responses. - - A 422 HTTP response from the server generally implies that the request - was well formed but the API was unable to process it because the - content was not semantically correct or meaningful per the API. - - For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918 - https://tools.ietf.org/html/rfc4918#section-11.2 - - @method ajaxError - @param {Object} jqXHR - @return error - */ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); - - if (jqXHR && jqXHR.status === 422) { - return new InvalidError(Ember.$.parseJSON(jqXHR.responseText)); - } else { - return error; - } - } - }); - - __exports__["default"] = ActiveModelAdapter; - }); -enifed("activemodel-adapter/system/active_model_serializer", - ["ember-inflector","ember-data/serializers/rest_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var singularize = __dependency1__.singularize; - var RESTSerializer = __dependency2__["default"]; - /** - @module ember-data - */ - - var get = Ember.get, - forEach = Ember.EnumerableUtils.forEach, - camelize = Ember.String.camelize, - capitalize = Ember.String.capitalize, - decamelize = Ember.String.decamelize, - underscore = Ember.String.underscore; - /** - The ActiveModelSerializer is a subclass of the RESTSerializer designed to integrate - with a JSON API that uses an underscored naming convention instead of camelCasing. - It has been designed to work out of the box with the - [active_model_serializers](http://github.com/rails-api/active_model_serializers) - Ruby gem. This Serializer expects specific settings using ActiveModel::Serializers, - `embed :ids, embed_in_root: true` which sideloads the records. - - This serializer extends the DS.RESTSerializer by making consistent - use of the camelization, decamelization and pluralization methods to - normalize the serialized JSON into a format that is compatible with - a conventional Rails backend and Ember Data. - - ## JSON Structure - - The ActiveModelSerializer expects the JSON returned from your server - to follow the REST adapter conventions substituting underscored keys - for camelcased ones. - - ### Conventional Names - - Attribute names in your JSON payload should be the underscored versions of - the attributes in your Ember.js models. - - For example, if you have a `Person` model: - - ```js - App.FamousPerson = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string') - }); - ``` - - The JSON returned should look like this: - - ```js - { - "famous_person": { - "id": 1, - "first_name": "Barack", - "last_name": "Obama", - "occupation": "President" - } - } - ``` - - Let's imagine that `Occupation` is just another model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.belongsTo('occupation') - }); - - App.Occupation = DS.Model.extend({ - name: DS.attr('string'), - salary: DS.attr('number'), - people: DS.hasMany('person') - }); - ``` - - The JSON needed to avoid extra server calls, should look like this: - - ```js - { - "people": [{ - "id": 1, - "first_name": "Barack", - "last_name": "Obama", - "occupation_id": 1 - }], - - "occupations": [{ - "id": 1, - "name": "President", - "salary": 100000, - "person_ids": [1] - }] - } - ``` - - @class ActiveModelSerializer - @namespace DS - @extends DS.RESTSerializer - */ - var ActiveModelSerializer = RESTSerializer.extend({ - // SERIALIZE - - /** - Converts camelCased attributes to underscored when serializing. - - @method keyForAttribute - @param {String} attribute - @return String - */ - keyForAttribute: function(attr) { - return decamelize(attr); - }, - - /** - Underscores relationship names and appends "_id" or "_ids" when serializing - relationship keys. - - @method keyForRelationship - @param {String} key - @param {String} kind - @return String - */ - keyForRelationship: function(rawKey, kind) { - var key = decamelize(rawKey); - if (kind === "belongsTo") { - return key + "_id"; - } else if (kind === "hasMany") { - return singularize(key) + "_ids"; - } else { - return key; - } - }, - - /* - Does not serialize hasMany relationships by default. - */ - serializeHasMany: Ember.K, - - /** - Underscores the JSON root keys when serializing. - - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type - @param {DS.Model} record - @param {Object} options - */ - serializeIntoHash: function(data, type, record, options) { - var root = underscore(decamelize(type.typeKey)); - data[root] = this.serialize(record, options); - }, - - /** - Serializes a polymorphic type as a fully capitalized model name. - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key; - var belongsTo = get(record, key); - var jsonKey = underscore(key + "_type"); - - if (Ember.isNone(belongsTo)) { - json[jsonKey] = null; - } else { - json[jsonKey] = capitalize(camelize(belongsTo.constructor.typeKey)); - } - }, - - // EXTRACT - - /** - Add extra step to `DS.RESTSerializer.normalize` so links are normalized. - - If your payload looks like: - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "flagged_comments": "api/comments/flagged" } - } - } - ``` - - The normalized version would look like this - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "flaggedComments": "api/comments/flagged" } - } - } - ``` - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @param {String} prop - @return Object - */ - - normalize: function(type, hash, prop) { - this.normalizeLinks(hash); - - return this._super(type, hash, prop); - }, - - /** - Convert `snake_cased` links to `camelCase` - - @method normalizeLinks - @param {Object} data - */ - - normalizeLinks: function(data){ - if (data.links) { - var links = data.links; - - for (var link in links) { - var camelizedLink = camelize(link); - - if (camelizedLink !== link) { - links[camelizedLink] = links[link]; - delete links[link]; - } - } - } - }, - - /** - Normalize the polymorphic type from the JSON. - - Normalize: - ```js - { - id: "1" - minion: { type: "evil_minion", id: "12"} - } - ``` - - To: - ```js - { - id: "1" - minion: { type: "evilMinion", id: "12"} - } - ``` - - @method normalizeRelationships - @private - */ - normalizeRelationships: function(type, hash) { - - if (this.keyForRelationship) { - type.eachRelationship(function(key, relationship) { - var payloadKey, payload; - if (relationship.options.polymorphic) { - payloadKey = this.keyForAttribute(key); - payload = hash[payloadKey]; - if (payload && payload.type) { - payload.type = this.typeForRoot(payload.type); - } else if (payload && relationship.kind === "hasMany") { - var self = this; - forEach(payload, function(single) { - single.type = self.typeForRoot(single.type); - }); - } - } else { - payloadKey = this.keyForRelationship(key, relationship.kind); - if (!hash.hasOwnProperty(payloadKey)) { return; } - payload = hash[payloadKey]; - } - - hash[key] = payload; - - if (key !== payloadKey) { - delete hash[payloadKey]; - } - }, this); - } - } - }); - - __exports__["default"] = ActiveModelSerializer; - }); -enifed("ember-data", - ["ember-data/system/create","ember-data/core","ember-data/ext/date","ember-data/system/promise_proxies","ember-data/system/store","ember-data/system/model","ember-data/system/adapter","ember-data/system/debug","ember-data/system/record_arrays","ember-data/system/record_array_manager","ember-data/adapters","ember-data/serializers/json_serializer","ember-data/serializers/rest_serializer","ember-inflector","ember-data/serializers/embedded_records_mixin","activemodel-adapter","ember-data/transforms","ember-data/system/relationships","ember-data/ember-initializer","ember-data/setup-container","ember-data/system/container_proxy","ember-data/system/relationships/relationship","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __exports__) { - "use strict"; - /** - Ember Data - - @module ember-data - @main ember-data - */ - - // support RSVP 2.x via resolve, but prefer RSVP 3.x's Promise.cast - Ember.RSVP.Promise.cast = Ember.RSVP.Promise.cast || Ember.RSVP.resolve; - - var DS = __dependency2__["default"]; - - var PromiseArray = __dependency4__.PromiseArray; - var PromiseObject = __dependency4__.PromiseObject; - var Store = __dependency5__.Store; - var Model = __dependency6__.Model; - var Errors = __dependency6__.Errors; - var RootState = __dependency6__.RootState; - var attr = __dependency6__.attr; - var InvalidError = __dependency7__.InvalidError; - var Adapter = __dependency7__.Adapter; - var DebugAdapter = __dependency8__["default"]; - var RecordArray = __dependency9__.RecordArray; - var FilteredRecordArray = __dependency9__.FilteredRecordArray; - var AdapterPopulatedRecordArray = __dependency9__.AdapterPopulatedRecordArray; - var ManyArray = __dependency9__.ManyArray; - var RecordArrayManager = __dependency10__["default"]; - var RESTAdapter = __dependency11__.RESTAdapter; - var FixtureAdapter = __dependency11__.FixtureAdapter; - var JSONSerializer = __dependency12__["default"]; - var RESTSerializer = __dependency13__["default"]; - var EmbeddedRecordsMixin = __dependency15__["default"]; - var ActiveModelAdapter = __dependency16__.ActiveModelAdapter; - var ActiveModelSerializer = __dependency16__.ActiveModelSerializer; - - var Transform = __dependency17__.Transform; - var DateTransform = __dependency17__.DateTransform; - var NumberTransform = __dependency17__.NumberTransform; - var StringTransform = __dependency17__.StringTransform; - var BooleanTransform = __dependency17__.BooleanTransform; - - var hasMany = __dependency18__.hasMany; - var belongsTo = __dependency18__.belongsTo; - var setupContainer = __dependency20__["default"]; - - var ContainerProxy = __dependency21__["default"]; - var Relationship = __dependency22__.Relationship; - - DS.Store = Store; - DS.PromiseArray = PromiseArray; - DS.PromiseObject = PromiseObject; - - DS.Model = Model; - DS.RootState = RootState; - DS.attr = attr; - DS.Errors = Errors; - - DS.Adapter = Adapter; - DS.InvalidError = InvalidError; - - DS.DebugAdapter = DebugAdapter; - - DS.RecordArray = RecordArray; - DS.FilteredRecordArray = FilteredRecordArray; - DS.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray; - DS.ManyArray = ManyArray; - - DS.RecordArrayManager = RecordArrayManager; - - DS.RESTAdapter = RESTAdapter; - DS.FixtureAdapter = FixtureAdapter; - - DS.RESTSerializer = RESTSerializer; - DS.JSONSerializer = JSONSerializer; - - DS.Transform = Transform; - DS.DateTransform = DateTransform; - DS.StringTransform = StringTransform; - DS.NumberTransform = NumberTransform; - DS.BooleanTransform = BooleanTransform; - - DS.ActiveModelAdapter = ActiveModelAdapter; - DS.ActiveModelSerializer = ActiveModelSerializer; - DS.EmbeddedRecordsMixin = EmbeddedRecordsMixin; - - DS.belongsTo = belongsTo; - DS.hasMany = hasMany; - - DS.Relationship = Relationship; - - DS.ContainerProxy = ContainerProxy; - - DS._setupContainer = setupContainer; - - Ember.lookup.DS = DS; - - __exports__["default"] = DS; - }); -enifed("ember-data/adapters", - ["ember-data/adapters/fixture_adapter","ember-data/adapters/rest_adapter","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var FixtureAdapter = __dependency1__["default"]; - var RESTAdapter = __dependency2__["default"]; - - __exports__.RESTAdapter = RESTAdapter; - __exports__.FixtureAdapter = FixtureAdapter; - }); -enifed("ember-data/adapters/fixture_adapter", - ["ember-data/system/adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var get = Ember.get; - var fmt = Ember.String.fmt; - var indexOf = Ember.EnumerableUtils.indexOf; - - var counter = 0; - - var Adapter = __dependency1__["default"]; - - /** - `DS.FixtureAdapter` is an adapter that loads records from memory. - It's primarily used for development and testing. You can also use - `DS.FixtureAdapter` while working on the API but is not ready to - integrate yet. It is a fully functioning adapter. All CRUD methods - are implemented. You can also implement query logic that a remote - system would do. It's possible to develop your entire application - with `DS.FixtureAdapter`. - - For information on how to use the `FixtureAdapter` in your - application please see the [FixtureAdapter - guide](/guides/models/the-fixture-adapter/). - - @class FixtureAdapter - @namespace DS - @extends DS.Adapter - */ - __exports__["default"] = Adapter.extend({ - // by default, fixtures are already in normalized form - serializer: null, - - /** - If `simulateRemoteResponse` is `true` the `FixtureAdapter` will - wait a number of milliseconds before resolving promises with the - fixture values. The wait time can be configured via the `latency` - property. - - @property simulateRemoteResponse - @type {Boolean} - @default true - */ - simulateRemoteResponse: true, - - /** - By default the `FixtureAdapter` will simulate a wait of the - `latency` milliseconds before resolving promises with the fixture - values. This behavior can be turned off via the - `simulateRemoteResponse` property. - - @property latency - @type {Number} - @default 50 - */ - latency: 50, - - /** - Implement this method in order to provide data associated with a type - - @method fixturesForType - @param {Subclass of DS.Model} type - @return {Array} - */ - fixturesForType: function(type) { - if (type.FIXTURES) { - var fixtures = Ember.A(type.FIXTURES); - return fixtures.map(function(fixture){ - var fixtureIdType = typeof fixture.id; - if(fixtureIdType !== "number" && fixtureIdType !== "string"){ - throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture])); - } - fixture.id = fixture.id + ''; - return fixture; - }); - } - return null; - }, - - /** - Implement this method in order to query fixtures data - - @method queryFixtures - @param {Array} fixture - @param {Object} query - @param {Subclass of DS.Model} type - @return {Promise|Array} - */ - queryFixtures: function(fixtures, query, type) { - Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.'); - }, - - /** - @method updateFixtures - @param {Subclass of DS.Model} type - @param {Array} fixture - */ - updateFixtures: function(type, fixture) { - if(!type.FIXTURES) { - type.FIXTURES = []; - } - - var fixtures = type.FIXTURES; - - this.deleteLoadedFixture(type, fixture); - - fixtures.push(fixture); - }, - - /** - Implement this method in order to provide json for CRUD methods - - @method mockJSON - @param {Subclass of DS.Model} type - @param {DS.Model} record - */ - mockJSON: function(store, type, record) { - return store.serializerFor(type).serialize(record, { includeId: true }); - }, - - /** - @method generateIdForRecord - @param {DS.Store} store - @param {DS.Model} record - @return {String} id - */ - generateIdForRecord: function(store) { - return "fixture-" + counter++; - }, - - /** - @method find - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} id - @return {Promise} promise - */ - find: function(store, type, id) { - var fixtures = this.fixturesForType(type); - var fixture; - - Ember.assert("Unable to find fixtures for model type "+type.toString() +". If you're defining your fixtures using `Model.FIXTURES = ...`, please change it to `Model.reopenClass({ FIXTURES: ... })`.", fixtures); - - if (fixtures) { - fixture = Ember.A(fixtures).findBy('id', id); - } - - if (fixture) { - return this.simulateRemoteCall(function() { - return fixture; - }, this); - } - }, - - /** - @method findMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Array} ids - @return {Promise} promise - */ - findMany: function(store, type, ids) { - var fixtures = this.fixturesForType(type); - - Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures); - - if (fixtures) { - fixtures = fixtures.filter(function(item) { - return indexOf(ids, item.id) !== -1; - }); - } - - if (fixtures) { - return this.simulateRemoteCall(function() { - return fixtures; - }, this); - } - }, - - /** - @private - @method findAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} sinceToken - @return {Promise} promise - */ - findAll: function(store, type) { - var fixtures = this.fixturesForType(type); - - Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures); - - return this.simulateRemoteCall(function() { - return fixtures; - }, this); - }, - - /** - @private - @method findQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} query - @param {DS.AdapterPopulatedRecordArray} recordArray - @return {Promise} promise - */ - findQuery: function(store, type, query, array) { - var fixtures = this.fixturesForType(type); - - Ember.assert("Unable to find fixtures for model type " + type.toString(), fixtures); - - fixtures = this.queryFixtures(fixtures, query, type); - - if (fixtures) { - return this.simulateRemoteCall(function() { - return fixtures; - }, this); - } - }, - - /** - @method createRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - createRecord: function(store, type, record) { - var fixture = this.mockJSON(store, type, record); - - this.updateFixtures(type, fixture); - - return this.simulateRemoteCall(function() { - return fixture; - }, this); - }, - - /** - @method updateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - updateRecord: function(store, type, record) { - var fixture = this.mockJSON(store, type, record); - - this.updateFixtures(type, fixture); - - return this.simulateRemoteCall(function() { - return fixture; - }, this); - }, - - /** - @method deleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - deleteRecord: function(store, type, record) { - this.deleteLoadedFixture(type, record); - - return this.simulateRemoteCall(function() { - // no payload in a deletion - return null; - }); - }, - - /* - @method deleteLoadedFixture - @private - @param type - @param record - */ - deleteLoadedFixture: function(type, record) { - var existingFixture = this.findExistingFixture(type, record); - - if (existingFixture) { - var index = indexOf(type.FIXTURES, existingFixture); - type.FIXTURES.splice(index, 1); - return true; - } - }, - - /* - @method findExistingFixture - @private - @param type - @param record - */ - findExistingFixture: function(type, record) { - var fixtures = this.fixturesForType(type); - var id = get(record, 'id'); - - return this.findFixtureById(fixtures, id); - }, - - /* - @method findFixtureById - @private - @param fixtures - @param id - */ - findFixtureById: function(fixtures, id) { - return Ember.A(fixtures).find(function(r) { - if (''+get(r, 'id') === ''+id) { - return true; - } else { - return false; - } - }); - }, - - /* - @method simulateRemoteCall - @private - @param callback - @param context - */ - simulateRemoteCall: function(callback, context) { - var adapter = this; - - return new Ember.RSVP.Promise(function(resolve) { - var value = Ember.copy(callback.call(context), true); - if (get(adapter, 'simulateRemoteResponse')) { - // Schedule with setTimeout - Ember.run.later(function() { - resolve(value); - }, get(adapter, 'latency')); - } else { - // Asynchronous, but at the of the runloop with zero latency - Ember.run.schedule('actions', null, function() { - resolve(value); - }); - } - }, "DS: FixtureAdapter#simulateRemoteCall"); - } - }); - }); -enifed("ember-data/adapters/rest_adapter", - ["ember-data/system/adapter","ember-data/system/map","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var Adapter = __dependency1__.Adapter; - var InvalidError = __dependency1__.InvalidError; - var MapWithDefault = __dependency2__.MapWithDefault; - var get = Ember.get; - var forEach = Ember.ArrayPolyfills.forEach; - - /** - The REST adapter allows your store to communicate with an HTTP server by - transmitting JSON via XHR. Most Ember.js apps that consume a JSON API - should use the REST adapter. - - This adapter is designed around the idea that the JSON exchanged with - the server should be conventional. - - ## JSON Structure - - The REST adapter expects the JSON returned from your server to follow - these conventions. - - ### Object Root - - The JSON payload should be an object that contains the record inside a - root property. For example, in response to a `GET` request for - `/posts/1`, the JSON should look like this: - - ```js - { - "post": { - "id": 1, - "title": "I'm Running to Reform the W3C's Tag", - "author": "Yehuda Katz" - } - } - ``` - - Similarly, in response to a `GET` request for `/posts`, the JSON should - look like this: - - ```js - { - "posts": [ - { - "id": 1, - "title": "I'm Running to Reform the W3C's Tag", - "author": "Yehuda Katz" - }, - { - "id": 2, - "title": "Rails is omakase", - "author": "D2H" - } - ] - } - ``` - - ### Conventional Names - - Attribute names in your JSON payload should be the camelCased versions of - the attributes in your Ember.js models. - - For example, if you have a `Person` model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string') - }); - ``` - - The JSON returned should look like this: - - ```js - { - "person": { - "id": 5, - "firstName": "Barack", - "lastName": "Obama", - "occupation": "President" - } - } - ``` - - ## Customization - - ### Endpoint path customization - - Endpoint paths can be prefixed with a `namespace` by setting the namespace - property on the adapter: - - ```js - DS.RESTAdapter.reopen({ - namespace: 'api/1' - }); - ``` - Requests for `App.Person` would now target `/api/1/people/1`. - - ### Host customization - - An adapter can target other hosts by setting the `host` property. - - ```js - DS.RESTAdapter.reopen({ - host: 'https://api.example.com' - }); - ``` - - ### Headers customization - - Some APIs require HTTP headers, e.g. to provide an API key. Arbitrary - headers can be set as key/value pairs on the `RESTAdapter`'s `headers` - object and Ember Data will send them along with each ajax request. - - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: { - "API_KEY": "secret key", - "ANOTHER_HEADER": "Some header value" - } - }); - ``` - - `headers` can also be used as a computed property to support dynamic - headers. In the example below, the `session` object has been - injected into an adapter by Ember's container. - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: function() { - return { - "API_KEY": this.get("session.authToken"), - "ANOTHER_HEADER": "Some header value" - }; - }.property("session.authToken") - }); - ``` - - In some cases, your dynamic headers may require data from some - object outside of Ember's observer system (for example - `document.cookie`). You can use the - [volatile](/api/classes/Ember.ComputedProperty.html#method_volatile) - function to set the property into a non-cached mode causing the headers to - be recomputed with every request. - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: function() { - return { - "API_KEY": Ember.get(document.cookie.match(/apiKey\=([^;]*)/), "1"), - "ANOTHER_HEADER": "Some header value" - }; - }.property().volatile() - }); - ``` - - @class RESTAdapter - @constructor - @namespace DS - @extends DS.Adapter - */ - __exports__["default"] = Adapter.extend({ - defaultSerializer: '-rest', - - /** - By default the RESTAdapter will send each find request coming from a `store.find` - or from accessing a relationship separately to the server. If your server supports passing - ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests - within a single runloop. - - For example, if you have an initial payload of - ```javascript - post: { - id:1, - comments: [1,2] - } - ``` - - By default calling `post.get('comments')` will trigger the following requests(assuming the - comments haven't been loaded before): - - ``` - GET /comments/1 - GET /comments/2 - ``` - - If you set coalesceFindRequests to `true` it will instead trigger the following request: - - ``` - GET /comments?ids[]=1&ids[]=2 - ``` - - Setting coalesceFindRequests to `true` also works for `store.find` requests and `belongsTo` - relationships accessed within the same runloop. If you set `coalesceFindRequests: true` - - ```javascript - store.find('comment', 1); - store.find('comment', 2); - ``` - - will also send a request to: `GET /comments?ids[]=1&ids[]=2` - - Note: Requests coalescing rely on URL building strategy. So if you override `buildUrl` in your app - `groupRecordsForFindMany` more likely should be overriden as well in order for coalescing to work. - - @property coalesceFindRequests - @type {boolean} - */ - coalesceFindRequests: false, - - /** - Endpoint paths can be prefixed with a `namespace` by setting the namespace - property on the adapter: - - ```javascript - DS.RESTAdapter.reopen({ - namespace: 'api/1' - }); - ``` - - Requests for `App.Post` would now target `/api/1/post/`. - - @property namespace - @type {String} - */ - - /** - An adapter can target other hosts by setting the `host` property. - - ```javascript - DS.RESTAdapter.reopen({ - host: 'https://api.example.com' - }); - ``` - - Requests for `App.Post` would now target `https://api.example.com/post/`. - - @property host - @type {String} - */ - - /** - Some APIs require HTTP headers, e.g. to provide an API - key. Arbitrary headers can be set as key/value pairs on the - `RESTAdapter`'s `headers` object and Ember Data will send them - along with each ajax request. For dynamic headers see [headers - customization](/api/data/classes/DS.RESTAdapter.html#toc_headers-customization). - - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: { - "API_KEY": "secret key", - "ANOTHER_HEADER": "Some header value" - } - }); - ``` - - @property headers - @type {Object} - */ - - /** - Called by the store in order to fetch the JSON for a given - type and ID. - - The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - This method performs an HTTP `GET` request with the id provided as part of the query string. - - @method find - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} id - @param {DS.Model} record - @return {Promise} promise - */ - find: function(store, type, id, record) { - return this.ajax(this.buildURL(type.typeKey, id, record), 'GET'); - }, - - /** - Called by the store in order to fetch a JSON array for all - of the records for a given type. - - The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - @private - @method findAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} sinceToken - @return {Promise} promise - */ - findAll: function(store, type, sinceToken) { - var query; - - if (sinceToken) { - query = { since: sinceToken }; - } - - return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); - }, - - /** - Called by the store in order to fetch a JSON array for - the records that match a particular query. - - The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - The `query` argument is a simple JavaScript object that will be passed directly - to the server as parameters. - - @private - @method findQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} query - @return {Promise} promise - */ - findQuery: function(store, type, query) { - return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); - }, - - /** - Called by the store in order to fetch several records together if `coalesceFindRequests` is true - - For example, if the original payload looks like: - - ```js - { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2, 3 ] - } - ``` - - The IDs will be passed as a URL-encoded Array of IDs, in this form: - - ``` - ids[]=1&ids[]=2&ids[]=3 - ``` - - Many servers, such as Rails and PHP, will automatically convert this URL-encoded array - into an Array for you on the server-side. If you want to encode the - IDs, differently, just override this (one-line) method. - - The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - @method findMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Array} ids - @param {Array} records - @return {Promise} promise - */ - findMany: function(store, type, ids, records) { - return this.ajax(this.buildURL(type.typeKey, ids, records), 'GET', { data: { ids: ids } }); - }, - - /** - Called by the store in order to fetch a JSON array for - the unloaded records in a has-many relationship that were originally - specified as a URL (inside of `links`). - - For example, if your original payload looks like this: - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "comments": "/posts/1/comments" } - } - } - ``` - - This method will be called with the parent record and `/posts/1/comments`. - - The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL. - If the URL is host-relative (starting with a single slash), the - request will use the host specified on the adapter (if any). - - @method findHasMany - @param {DS.Store} store - @param {DS.Model} record - @param {String} url - @return {Promise} promise - */ - findHasMany: function(store, record, url, relationship) { - var host = get(this, 'host'); - var id = get(record, 'id'); - var type = record.constructor.typeKey; - - if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') { - url = host + url; - } - - return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); - }, - - /** - Called by the store in order to fetch a JSON array for - the unloaded records in a belongs-to relationship that were originally - specified as a URL (inside of `links`). - - For example, if your original payload looks like this: - - ```js - { - "person": { - "id": 1, - "name": "Tom Dale", - "links": { "group": "/people/1/group" } - } - } - ``` - - This method will be called with the parent record and `/people/1/group`. - - The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL. - - @method findBelongsTo - @param {DS.Store} store - @param {DS.Model} record - @param {String} url - @return {Promise} promise - */ - findBelongsTo: function(store, record, url, relationship) { - var id = get(record, 'id'); - var type = record.constructor.typeKey; - - return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); - }, - - /** - Called by the store when a newly created record is - saved via the `save` method on a model record instance. - - The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request - to a URL computed by `buildURL`. - - See `serialize` for information on how to customize the serialized form - of a record. - - @method createRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - createRecord: function(store, type, record) { - var data = {}; - var serializer = store.serializerFor(type.typeKey); - - serializer.serializeIntoHash(data, type, record, { includeId: true }); - - return this.ajax(this.buildURL(type.typeKey, null, record), "POST", { data: data }); - }, - - /** - Called by the store when an existing record is saved - via the `save` method on a model record instance. - - The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request - to a URL computed by `buildURL`. - - See `serialize` for information on how to customize the serialized form - of a record. - - @method updateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - updateRecord: function(store, type, record) { - var data = {}; - var serializer = store.serializerFor(type.typeKey); - - serializer.serializeIntoHash(data, type, record); - - var id = get(record, 'id'); - - return this.ajax(this.buildURL(type.typeKey, id, record), "PUT", { data: data }); - }, - - /** - Called by the store when a record is deleted. - - The `deleteRecord` method makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`. - - @method deleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - deleteRecord: function(store, type, record) { - var id = get(record, 'id'); - - return this.ajax(this.buildURL(type.typeKey, id, record), "DELETE"); - }, - - /** - Builds a URL for a given type and optional ID. - - By default, it pluralizes the type's name (for example, 'post' - becomes 'posts' and 'person' becomes 'people'). To override the - pluralization see [pathForType](#method_pathForType). - - If an ID is specified, it adds the ID to the path generated - for the type, separated by a `/`. - - @method buildURL - @param {String} type - @param {String} id - @param {DS.Model} record - @return {String} url - */ - buildURL: function(type, id, record) { - var url = [], - host = get(this, 'host'), - prefix = this.urlPrefix(); - - if (type) { url.push(this.pathForType(type)); } - - //We might get passed in an array of ids from findMany - //in which case we don't want to modify the url, as the - //ids will be passed in through a query param - if (id && !Ember.isArray(id)) { url.push(encodeURIComponent(id)); } - - if (prefix) { url.unshift(prefix); } - - url = url.join('/'); - if (!host && url) { url = '/' + url; } - - return url; - }, - - /** - @method urlPrefix - @private - @param {String} path - @param {String} parentUrl - @return {String} urlPrefix - */ - urlPrefix: function(path, parentURL) { - var host = get(this, 'host'); - var namespace = get(this, 'namespace'); - var url = []; - - if (path) { - // Absolute path - if (path.charAt(0) === '/') { - if (host) { - path = path.slice(1); - url.push(host); - } - // Relative path - } else if (!/^http(s)?:\/\//.test(path)) { - url.push(parentURL); - } - } else { - if (host) { url.push(host); } - if (namespace) { url.push(namespace); } - } - - if (path) { - url.push(path); - } - - return url.join('/'); - }, - - _stripIDFromURL: function(store, record) { - var type = record.constructor; - var url = this.buildURL(type.typeKey, record.get('id'), record); - - var expandedURL = url.split('/'); - //Case when the url is of the format ...something/:id - var lastSegment = expandedURL[ expandedURL.length - 1 ]; - var id = record.get('id'); - if (lastSegment === id) { - expandedURL[expandedURL.length - 1] = ""; - } else if(endsWith(lastSegment, '?id=' + id)) { - //Case when the url is of the format ...something?id=:id - expandedURL[expandedURL.length - 1] = lastSegment.substring(0, lastSegment.length - id.length - 1); - } - - return expandedURL.join('/'); - }, - - /** - http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers - */ - maxUrlLength: 2048, - - /** - Organize records into groups, each of which is to be passed to separate - calls to `findMany`. - - This implementation groups together records that have the same base URL but - differing ids. For example `/comments/1` and `/comments/2` will be grouped together - because we know findMany can coalesce them together as `/comments?ids[]=1&ids[]=2` - - It also supports urls where ids are passed as a query param, such as `/comments?id=1` - but not those where there is more than 1 query param such as `/comments?id=2&name=David` - Currently only the query param of `id` is supported. If you need to support others, please - override this or the `_stripIDFromURL` method. - - It does not group records that have differing base urls, such as for example: `/posts/1/comments/2` - and `/posts/2/comments/3` - - @method groupRecordsForFindMany - @param {DS.Store} store - @param {Array} records - @return {Array} an array of arrays of records, each of which is to be - loaded separately by `findMany`. - */ - groupRecordsForFindMany: function (store, records) { - var groups = MapWithDefault.create({defaultValue: function(){return [];}}); - var adapter = this; - var maxUrlLength = this.maxUrlLength; - - forEach.call(records, function(record){ - var baseUrl = adapter._stripIDFromURL(store, record); - groups.get(baseUrl).push(record); - }); - - function splitGroupToFitInUrl(group, maxUrlLength, paramNameLength) { - var baseUrl = adapter._stripIDFromURL(store, group[0]); - var idsSize = 0; - var splitGroups = [[]]; - - forEach.call(group, function(record) { - var additionalLength = encodeURIComponent(record.get('id')).length + paramNameLength; - if (baseUrl.length + idsSize + additionalLength >= maxUrlLength) { - idsSize = 0; - splitGroups.push([]); - } - - idsSize += additionalLength; - - var lastGroupIndex = splitGroups.length - 1; - splitGroups[lastGroupIndex].push(record); - }); - - return splitGroups; - } - - var groupsArray = []; - groups.forEach(function(group, key){ - var paramNameLength = '&ids%5B%5D='.length; - var splitGroups = splitGroupToFitInUrl(group, maxUrlLength, paramNameLength); - - forEach.call(splitGroups, function(splitGroup) { - groupsArray.push(splitGroup); - }); - }); - - return groupsArray; - }, - - /** - Determines the pathname for a given type. - - By default, it pluralizes the type's name (for example, - 'post' becomes 'posts' and 'person' becomes 'people'). - - ### Pathname customization - - For example if you have an object LineItem with an - endpoint of "/line_items/". - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - pathForType: function(type) { - var decamelized = Ember.String.decamelize(type); - return Ember.String.pluralize(decamelized); - } - }); - ``` - - @method pathForType - @param {String} type - @return {String} path - **/ - pathForType: function(type) { - var camelized = Ember.String.camelize(type); - return Ember.String.pluralize(camelized); - }, - - /** - Takes an ajax response, and returns an error payload. - - Returning a `DS.InvalidError` from this method will cause the - record to transition into the `invalid` state and make the - `errors` object available on the record. - - This function should return the entire payload as received from the - server. Error object extraction and normalization of model errors - should be performed by `extractErrors` on the serializer. - - Example - - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); - - if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText); - - return new DS.InvalidError(jsonErrors); - } else { - return error; - } - } - }); - ``` - - Note: As a correctness optimization, the default implementation of - the `ajaxError` method strips out the `then` method from jquery's - ajax response (jqXHR). This is important because the jqXHR's - `then` method fulfills the promise with itself resulting in a - circular "thenable" chain which may cause problems for some - promise libraries. - - @method ajaxError - @param {Object} jqXHR - @param {Object} responseText - @return {Object} jqXHR - */ - ajaxError: function(jqXHR, responseText) { - if (jqXHR && typeof jqXHR === 'object') { - jqXHR.then = null; - } - - return jqXHR; - }, - - /** - Takes an ajax response, and returns the json payload. - - By default this hook just returns the jsonPayload passed to it. - You might want to override it in two cases: - - 1. Your API might return useful results in the request headers. - If you need to access these, you can override this hook to copy them - from jqXHR to the payload object so they can be processed in you serializer. - - - 2. Your API might return errors as successful responses with status code - 200 and an Errors text or object. You can return a DS.InvalidError from - this hook and it will automatically reject the promise and put your record - into the invalid state. - - @method ajaxSuccess - @param {Object} jqXHR - @param {Object} jsonPayload - @return {Object} jsonPayload - */ - - ajaxSuccess: function(jqXHR, jsonPayload) { - return jsonPayload; - }, - - /** - Takes a URL, an HTTP method and a hash of data, and makes an - HTTP request. - - When the server responds with a payload, Ember Data will call into `extractSingle` - or `extractArray` (depending on whether the original query was for one record or - many records). - - By default, `ajax` method has the following behavior: - - * It sets the response `dataType` to `"json"` - * If the HTTP method is not `"GET"`, it sets the `Content-Type` to be - `application/json; charset=utf-8` - * If the HTTP method is not `"GET"`, it stringifies the data passed in. The - data is the serialized record in the case of a save. - * Registers success and failure handlers. - - @method ajax - @private - @param {String} url - @param {String} type The request type GET, POST, PUT, DELETE etc. - @param {Object} hash - @return {Promise} promise - */ - ajax: function(url, type, options) { - var adapter = this; - - return new Ember.RSVP.Promise(function(resolve, reject) { - var hash = adapter.ajaxOptions(url, type, options); - - hash.success = function(json, textStatus, jqXHR) { - json = adapter.ajaxSuccess(jqXHR, json); - if (json instanceof InvalidError) { - Ember.run(null, reject, json); - } else { - Ember.run(null, resolve, json); - } - }; - - hash.error = function(jqXHR, textStatus, errorThrown) { - Ember.run(null, reject, adapter.ajaxError(jqXHR, jqXHR.responseText)); - }; - - Ember.$.ajax(hash); - }, "DS: RESTAdapter#ajax " + type + " to " + url); - }, - - /** - @method ajaxOptions - @private - @param {String} url - @param {String} type The request type GET, POST, PUT, DELETE etc. - @param {Object} hash - @return {Object} hash - */ - ajaxOptions: function(url, type, options) { - var hash = options || {}; - hash.url = url; - hash.type = type; - hash.dataType = 'json'; - hash.context = this; - - if (hash.data && type !== 'GET') { - hash.contentType = 'application/json; charset=utf-8'; - hash.data = JSON.stringify(hash.data); - } - - var headers = get(this, 'headers'); - if (headers !== undefined) { - hash.beforeSend = function (xhr) { - forEach.call(Ember.keys(headers), function(key) { - xhr.setRequestHeader(key, headers[key]); - }); - }; - } - - return hash; - } - }); - - //From http://stackoverflow.com/questions/280634/endswith-in-javascript - function endsWith(string, suffix){ - if (typeof String.prototype.endsWith !== 'function') { - return string.indexOf(suffix, string.length - suffix.length) !== -1; - } else { - return string.endsWith(suffix); - } - } - }); -enifed("ember-data/core", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ - - /** - All Ember Data methods and functions are defined inside of this namespace. - - @class DS - @static - */ - var DS; - if ('undefined' === typeof DS) { - /** - @property VERSION - @type String - @default '1.0.0-beta.12' - @static - */ - DS = Ember.Namespace.create({ - VERSION: '1.0.0-beta.12' - }); - - if (Ember.libraries) { - Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION); - } - } - - __exports__["default"] = DS; - }); -enifed("ember-data/ember-initializer", - ["ember-data/setup-container"], - function(__dependency1__) { - "use strict"; - var setupContainer = __dependency1__["default"]; - - var K = Ember.K; - - /** - @module ember-data - */ - - /* - - This code initializes Ember-Data onto an Ember application. - - If an Ember.js developer defines a subclass of DS.Store on their application, - as `App.ApplicationStore` (or via a module system that resolves to `store:application`) - this code will automatically instantiate it and make it available on the - router. - - Additionally, after an application's controllers have been injected, they will - each have the store made available to them. - - For example, imagine an Ember.js application with the following classes: - - App.ApplicationStore = DS.Store.extend({ - adapter: 'custom' - }); - - App.PostsController = Ember.ArrayController.extend({ - // ... - }); - - When the application is initialized, `App.ApplicationStore` will automatically be - instantiated, and the instance of `App.PostsController` will have its `store` - property set to that instance. - - Note that this code will only be run if the `ember-application` package is - loaded. If Ember Data is being used in an environment other than a - typical application (e.g., node.js where only `ember-runtime` is available), - this code will be ignored. - */ - - Ember.onLoad('Ember.Application', function(Application) { - - Application.initializer({ - name: "ember-data", - initialize: setupContainer - }); - - // Deprecated initializers to satisfy old code that depended on them - - Application.initializer({ - name: "store", - after: "ember-data", - initialize: K - }); - - Application.initializer({ - name: "activeModelAdapter", - before: "store", - initialize: K - }); - - Application.initializer({ - name: "transforms", - before: "store", - initialize: K - }); - - Application.initializer({ - name: "data-adapter", - before: "store", - initialize: K - }); - - Application.initializer({ - name: "injectStore", - before: "store", - initialize: K - }); - }); - }); -enifed("ember-data/ext/date", - [], - function() { - "use strict"; - /** - @module ember-data - */ - - /** - Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601> - - © 2011 Colin Snover <http://zetafleet.com> - - Released under MIT license. - - @class Date - @namespace Ember - @static - */ - Ember.Date = Ember.Date || {}; - - var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; - - /** - @method parse - @param {Date} date - @return {Number} timestamp - */ - Ember.Date.parse = function (date) { - var timestamp, struct, minutesOffset = 0; - - // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string - // before falling back to any implementation-specific date parsing, so that’s what we do, even if native - // implementations could be faster - // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm - if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { - // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC - for (var i = 0, k; (k = numericKeys[i]); ++i) { - struct[k] = +struct[k] || 0; - } - - // allow undefined days and months - struct[2] = (+struct[2] || 1) - 1; - struct[3] = +struct[3] || 1; - - if (struct[8] !== 'Z' && struct[9] !== undefined) { - minutesOffset = struct[10] * 60 + struct[11]; - - if (struct[9] === '+') { - minutesOffset = 0 - minutesOffset; - } - } - - timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); - } - else { - timestamp = origParse ? origParse(date) : NaN; - } - - return timestamp; - }; - - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) { - Date.parse = Ember.Date.parse; - } - }); -enifed("ember-data/initializers/data_adapter", - ["ember-data/system/debug/debug_adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var DebugAdapter = __dependency1__["default"]; - - /** - Configures a container with injections on Ember applications - for the Ember-Data store. Accepts an optional namespace argument. - - @method initializeStoreInjections - @param {Ember.Container} container - */ - __exports__["default"] = function initializeDebugAdapter(container){ - container.register('data-adapter:main', DebugAdapter); - }; - }); -enifed("ember-data/initializers/store", - ["ember-data/serializers","ember-data/adapters","ember-data/system/container_proxy","ember-data/system/store","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var JSONSerializer = __dependency1__.JSONSerializer; - var RESTSerializer = __dependency1__.RESTSerializer; - var RESTAdapter = __dependency2__.RESTAdapter; - var ContainerProxy = __dependency3__["default"]; - var Store = __dependency4__["default"]; - - /** - Configures a container for use with an Ember-Data - store. Accepts an optional namespace argument. - - @method initializeStore - @param {Ember.Container} container - @param {Object} [application] an application namespace - */ - __exports__["default"] = function initializeStore(container, application){ - Ember.deprecate('Specifying a custom Store for Ember Data on your global namespace as `App.Store` ' + - 'has been deprecated. Please use `App.ApplicationStore` instead.', !(application && application.Store)); - - container.register('store:main', container.lookupFactory('store:application') || (application && application.Store) || Store); - - // allow older names to be looked up - - var proxy = new ContainerProxy(container); - proxy.registerDeprecations([ - { deprecated: 'serializer:_default', valid: 'serializer:-default' }, - { deprecated: 'serializer:_rest', valid: 'serializer:-rest' }, - { deprecated: 'adapter:_rest', valid: 'adapter:-rest' } - ]); - - // new go forward paths - container.register('serializer:-default', JSONSerializer); - container.register('serializer:-rest', RESTSerializer); - container.register('adapter:-rest', RESTAdapter); - - // Eagerly generate the store so defaultStore is populated. - // TODO: Do this in a finisher hook - container.lookup('store:main'); - }; - }); -enifed("ember-data/initializers/store_injections", - ["exports"], - function(__exports__) { - "use strict"; - /** - Configures a container with injections on Ember applications - for the Ember-Data store. Accepts an optional namespace argument. - - @method initializeStoreInjections - @param {Ember.Container} container - */ - __exports__["default"] = function initializeStoreInjections(container){ - container.injection('controller', 'store', 'store:main'); - container.injection('route', 'store', 'store:main'); - container.injection('serializer', 'store', 'store:main'); - container.injection('data-adapter', 'store', 'store:main'); - }; - }); -enifed("ember-data/initializers/transforms", - ["ember-data/transforms","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var BooleanTransform = __dependency1__.BooleanTransform; - var DateTransform = __dependency1__.DateTransform; - var StringTransform = __dependency1__.StringTransform; - var NumberTransform = __dependency1__.NumberTransform; - - /** - Configures a container for use with Ember-Data - transforms. - - @method initializeTransforms - @param {Ember.Container} container - */ - __exports__["default"] = function initializeTransforms(container){ - container.register('transform:boolean', BooleanTransform); - container.register('transform:date', DateTransform); - container.register('transform:number', NumberTransform); - container.register('transform:string', StringTransform); - }; - }); -enifed("ember-data/serializers", - ["ember-data/serializers/json_serializer","ember-data/serializers/rest_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var JSONSerializer = __dependency1__["default"]; - var RESTSerializer = __dependency2__["default"]; - - __exports__.JSONSerializer = JSONSerializer; - __exports__.RESTSerializer = RESTSerializer; - }); -enifed("ember-data/serializers/embedded_records_mixin", - ["exports"], - function(__exports__) { - "use strict"; - var get = Ember.get; - var forEach = Ember.EnumerableUtils.forEach; - var camelize = Ember.String.camelize; - - /** - ## Using Embedded Records - - `DS.EmbeddedRecordsMixin` supports serializing embedded records. - - To set up embedded records, include the mixin when extending a serializer - then define and configure embedded (model) relationships. - - Below is an example of a per-type serializer ('post' type). - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - author: { embedded: 'always' }, - comments: { serialize: 'ids' } - } - }); - ``` - Note that this use of `{ embedded: 'always' }` is unrelated to - the `{ embedded: 'always' }` that is defined as an option on `DS.attr` as part of - defining a model while working with the ActiveModelSerializer. Nevertheless, - using `{ embedded: 'always' }` as an option to DS.attr is not a valid way to setup - embedded records. - - The `attrs` option for a resource `{ embedded: 'always' }` is shorthand for: - - ```js - { - serialize: 'records', - deserialize: 'records' - } - ``` - - ### Configuring Attrs - - A resource's `attrs` option may be set to use `ids`, `records` or false for the - `serialize` and `deserialize` settings. - - The `attrs` property can be set on the ApplicationSerializer or a per-type - serializer. - - In the case where embedded JSON is expected while extracting a payload (reading) - the setting is `deserialize: 'records'`, there is no need to use `ids` when - extracting as that is the default behavior without this mixin if you are using - the vanilla EmbeddedRecordsMixin. Likewise, to embed JSON in the payload while - serializing `serialize: 'records'` is the setting to use. There is an option of - not embedding JSON in the serialized payload by using `serialize: 'ids'`. If you - do not want the relationship sent at all, you can use `serialize: false`. - - - ### EmbeddedRecordsMixin defaults - If you do not overwrite `attrs` for a specific relationship, the `EmbeddedRecordsMixin` - will behave in the following way: - - BelongsTo: `{ serialize: 'id', deserialize: 'id' }` - HasMany: `{ serialize: false, deserialize: 'ids' }` - - ### Model Relationships - - Embedded records must have a model defined to be extracted and serialized. Note that - when defining any relationships on your model such as `belongsTo` and `hasMany`, you - should not both specify `async:true` and also indicate through the serializer's - `attrs` attribute that the related model should be embedded. If a model is - declared embedded, then do not use `async:true`. - - To successfully extract and serialize embedded records the model relationships - must be setup correcty See the - [defining relationships](/guides/models/defining-models/#toc_defining-relationships) - section of the **Defining Models** guide page. - - Records without an `id` property are not considered embedded records, model - instances must have an `id` property to be used with Ember Data. - - ### Example JSON payloads, Models and Serializers - - **When customizing a serializer it is important to grok what the customizations - are. Please read the docs for the methods this mixin provides, in case you need - to modify it to fit your specific needs.** - - For example review the docs for each method of this mixin: - * [normalize](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_normalize) - * [serializeBelongsTo](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeBelongsTo) - * [serializeHasMany](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeHasMany) - - @class EmbeddedRecordsMixin - @namespace DS - */ - var EmbeddedRecordsMixin = Ember.Mixin.create({ - - /** - Normalize the record and recursively normalize/extract all the embedded records - while pushing them into the store as they are encountered - - A payload with an attr configured for embedded records needs to be extracted: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "comments": [{ - "id": "1", - "body": "Rails is unagi" - }, { - "id": "2", - "body": "Omakase O_o" - }] - } - } - ``` - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash to be normalized - @param {String} key the hash has been referenced by - @return {Object} the normalized hash - **/ - normalize: function(type, hash, prop) { - var normalizedHash = this._super(type, hash, prop); - return extractEmbeddedRecords(this, this.store, type, normalizedHash); - }, - - keyForRelationship: function(key, type){ - if (this.hasDeserializeRecordsOption(key)) { - return this.keyForAttribute(key); - } else { - return this._super(key, type) || key; - } - }, - - /** - Serialize `belongsTo` relationship when it is configured as an embedded object. - - This example of an author model belongs to a post model: - - ```js - Post = DS.Model.extend({ - title: DS.attr('string'), - body: DS.attr('string'), - author: DS.belongsTo('author') - }); - - Author = DS.Model.extend({ - name: DS.attr('string'), - post: DS.belongsTo('post') - }); - ``` - - Use a custom (type) serializer for the post model to configure embedded author - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - author: {embedded: 'always'} - } - }) - ``` - - A payload with an attribute configured for embedded records can serialize - the records together under the root attribute's payload: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "author": { - "id": "2" - "name": "dhh" - } - } - } - ``` - - @method serializeBelongsTo - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeBelongsTo: function(record, json, relationship) { - var attr = relationship.key; - if (this.noSerializeOptionSpecified(attr)) { - this._super(record, json, relationship); - return; - } - var includeIds = this.hasSerializeIdsOption(attr); - var includeRecords = this.hasSerializeRecordsOption(attr); - var embeddedRecord = record.get(attr); - var key; - if (includeIds) { - key = this.keyForRelationship(attr, relationship.kind); - if (!embeddedRecord) { - json[key] = null; - } else { - json[key] = get(embeddedRecord, 'id'); - } - } else if (includeRecords) { - key = this.keyForAttribute(attr); - if (!embeddedRecord) { - json[key] = null; - } else { - json[key] = embeddedRecord.serialize({includeId: true}); - this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, json[key]); - } - } - }, - - /** - Serialize `hasMany` relationship when it is configured as embedded objects. - - This example of a post model has many comments: - - ```js - Post = DS.Model.extend({ - title: DS.attr('string'), - body: DS.attr('string'), - comments: DS.hasMany('comment') - }); - - Comment = DS.Model.extend({ - body: DS.attr('string'), - post: DS.belongsTo('post') - }); - ``` - - Use a custom (type) serializer for the post model to configure embedded comments - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - comments: {embedded: 'always'} - } - }) - ``` - - A payload with an attribute configured for embedded records can serialize - the records together under the root attribute's payload: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "body": "I want this for my ORM, I want that for my template language..." - "comments": [{ - "id": "1", - "body": "Rails is unagi" - }, { - "id": "2", - "body": "Omakase O_o" - }] - } - } - ``` - - The attrs options object can use more specific instruction for extracting and - serializing. When serializing, an option to embed `ids` or `records` can be set. - When extracting the only option is `records`. - - So `{embedded: 'always'}` is shorthand for: - `{serialize: 'records', deserialize: 'records'}` - - To embed the `ids` for a related object (using a hasMany relationship): - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - comments: {serialize: 'ids', deserialize: 'records'} - } - }) - ``` - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "body": "I want this for my ORM, I want that for my template language..." - "comments": ["1", "2"] - } - } - ``` - - @method serializeHasMany - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeHasMany: function(record, json, relationship) { - var attr = relationship.key; - if (this.noSerializeOptionSpecified(attr)) { - this._super(record, json, relationship); - return; - } - var includeIds = this.hasSerializeIdsOption(attr); - var includeRecords = this.hasSerializeRecordsOption(attr); - var key; - if (includeIds) { - key = this.keyForRelationship(attr, relationship.kind); - json[key] = get(record, attr).mapBy('id'); - } else if (includeRecords) { - key = this.keyForAttribute(attr); - json[key] = get(record, attr).map(function(embeddedRecord) { - var serializedEmbeddedRecord = embeddedRecord.serialize({includeId: true}); - this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, serializedEmbeddedRecord); - return serializedEmbeddedRecord; - }, this); - } - }, - - /** - When serializing an embedded record, modify the property (in the json payload) - that refers to the parent record (foreign key for relationship). - - Serializing a `belongsTo` relationship removes the property that refers to the - parent record - - Serializing a `hasMany` relationship does not remove the property that refers to - the parent record. - - @method removeEmbeddedForeignKey - @param {DS.Model} record - @param {DS.Model} embeddedRecord - @param {Object} relationship - @param {Object} json - */ - removeEmbeddedForeignKey: function (record, embeddedRecord, relationship, json) { - if (relationship.kind === 'hasMany') { - return; - } else if (relationship.kind === 'belongsTo') { - var parentRecord = record.constructor.inverseFor(relationship.key); - if (parentRecord) { - var name = parentRecord.name; - var embeddedSerializer = this.store.serializerFor(embeddedRecord.constructor); - var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind); - if (parentKey) { - delete json[parentKey]; - } - } - } - }, - - // checks config for attrs option to embedded (always) - serialize and deserialize - hasEmbeddedAlwaysOption: function (attr) { - var option = this.attrsOption(attr); - return option && option.embedded === 'always'; - }, - - // checks config for attrs option to serialize ids - hasSerializeRecordsOption: function(attr) { - var alwaysEmbed = this.hasEmbeddedAlwaysOption(attr); - var option = this.attrsOption(attr); - return alwaysEmbed || (option && (option.serialize === 'records')); - }, - - // checks config for attrs option to serialize records - hasSerializeIdsOption: function(attr) { - var option = this.attrsOption(attr); - return option && (option.serialize === 'ids' || option.serialize === 'id'); - }, - - // checks config for attrs option to serialize records - noSerializeOptionSpecified: function(attr) { - var option = this.attrsOption(attr); - return !(option && (option.serialize || option.embedded)); - }, - - // checks config for attrs option to deserialize records - // a defined option object for a resource is treated the same as - // `deserialize: 'records'` - hasDeserializeRecordsOption: function(attr) { - var alwaysEmbed = this.hasEmbeddedAlwaysOption(attr); - var option = this.attrsOption(attr); - return alwaysEmbed || (option && option.deserialize === 'records'); - }, - - attrsOption: function(attr) { - var attrs = this.get('attrs'); - return attrs && (attrs[camelize(attr)] || attrs[attr]); - } - }); - - // chooses a relationship kind to branch which function is used to update payload - // does not change payload if attr is not embedded - function extractEmbeddedRecords(serializer, store, type, partial) { - - type.eachRelationship(function(key, relationship) { - if (serializer.hasDeserializeRecordsOption(key)) { - var embeddedType = store.modelFor(relationship.type.typeKey); - if (relationship.kind === "hasMany") { - if (relationship.options.polymorphic) { - extractEmbeddedHasManyPolymorphic(store, key, partial); - } - else { - extractEmbeddedHasMany(store, key, embeddedType, partial); - } - } - if (relationship.kind === "belongsTo") { - extractEmbeddedBelongsTo(store, key, embeddedType, partial); - } - } - }); - - return partial; - } - - // handles embedding for `hasMany` relationship - function extractEmbeddedHasMany(store, key, embeddedType, hash) { - if (!hash[key]) { - return hash; - } - - var ids = []; - - var embeddedSerializer = store.serializerFor(embeddedType.typeKey); - forEach(hash[key], function(data) { - var embeddedRecord = embeddedSerializer.normalize(embeddedType, data, null); - store.push(embeddedType, embeddedRecord); - ids.push(embeddedRecord.id); - }); - - hash[key] = ids; - return hash; - } - - function extractEmbeddedHasManyPolymorphic(store, key, hash) { - if (!hash[key]) { - return hash; - } - - var ids = []; - - forEach(hash[key], function(data) { - var typeKey = data.type; - var embeddedSerializer = store.serializerFor(typeKey); - var embeddedType = store.modelFor(typeKey); - var primaryKey = get(embeddedSerializer, 'primaryKey'); - - var embeddedRecord = embeddedSerializer.normalize(embeddedType, data, null); - store.push(embeddedType, embeddedRecord); - ids.push({ id: embeddedRecord[primaryKey], type: typeKey }); - }); - - hash[key] = ids; - return hash; - } - - function extractEmbeddedBelongsTo(store, key, embeddedType, hash) { - if (!hash[key]) { - return hash; - } - - var embeddedSerializer = store.serializerFor(embeddedType.typeKey); - var embeddedRecord = embeddedSerializer.normalize(embeddedType, hash[key], null); - store.push(embeddedType, embeddedRecord); - - hash[key] = embeddedRecord.id; - //TODO Need to add a reference to the parent later so relationship works between both `belongsTo` records - return hash; - } - - __exports__["default"] = EmbeddedRecordsMixin; - }); -enifed("ember-data/serializers/json_serializer", - ["exports"], - function(__exports__) { - "use strict"; - var get = Ember.get; - var isNone = Ember.isNone; - var map = Ember.ArrayPolyfills.map; - var merge = Ember.merge; - - /** - In Ember Data a Serializer is used to serialize and deserialize - records when they are transferred in and out of an external source. - This process involves normalizing property names, transforming - attribute values and serializing relationships. - - For maximum performance Ember Data recommends you use the - [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses. - - `JSONSerializer` is useful for simpler or legacy backends that may - not support the http://jsonapi.org/ spec. - - @class JSONSerializer - @namespace DS - */ - __exports__["default"] = Ember.Object.extend({ - /** - The primaryKey is used when serializing and deserializing - data. Ember Data always uses the `id` property to store the id of - the record. The external source may not always follow this - convention. In these cases it is useful to override the - primaryKey property to match the primaryKey of your external - store. - - Example - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - primaryKey: '_id' - }); - ``` - - @property primaryKey - @type {String} - @default 'id' - */ - primaryKey: 'id', - - /** - The `attrs` object can be used to declare a simple mapping between - property names on `DS.Model` records and payload keys in the - serialized JSON object representing the record. An object with the - property `key` can also be used to designate the attribute's key on - the response payload. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string'), - admin: DS.attr('boolean') - }); - - App.PersonSerializer = DS.JSONSerializer.extend({ - attrs: { - admin: 'is_admin', - occupation: {key: 'career'} - } - }); - ``` - - You can also remove attributes by setting the `serialize` key to - false in your mapping object. - - Example - - ```javascript - App.PersonSerializer = DS.JSONSerializer.extend({ - attrs: { - admin: {serialize: false}, - occupation: {key: 'career'} - } - }); - ``` - - When serialized: - - ```javascript - { - "career": "magician" - } - ``` - - Note that the `admin` is now not included in the payload. - - @property attrs - @type {Object} - */ - - /** - Given a subclass of `DS.Model` and a JSON object this method will - iterate through each attribute of the `DS.Model` and invoke the - `DS.Transform#deserialize` method on the matching property of the - JSON object. This method is typically called after the - serializer's `normalize` method. - - @method applyTransforms - @private - @param {subclass of DS.Model} type - @param {Object} data The data to transform - @return {Object} data The transformed data object - */ - applyTransforms: function(type, data) { - type.eachTransformedAttribute(function applyTransform(key, type) { - if (!data.hasOwnProperty(key)) { return; } - - var transform = this.transformFor(type); - data[key] = transform.deserialize(data[key]); - }, this); - - return data; - }, - - /** - Normalizes a part of the JSON payload returned by - the server. You should override this method, munge the hash - and call super if you have generic normalization to do. - - It takes the type of the record that is being normalized - (as a DS.Model class), the property where the hash was - originally found, and the hash to normalize. - - You can use this method, for example, to normalize underscored keys to camelized - or other general-purpose normalizations. - - Example - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - normalize: function(type, hash) { - var fields = Ember.get(type, 'fields'); - fields.forEach(function(field) { - var payloadField = Ember.String.underscore(field); - if (field === payloadField) { return; } - - hash[field] = hash[payloadField]; - delete hash[payloadField]; - }); - return this._super.apply(this, arguments); - } - }); - ``` - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @return {Object} - */ - normalize: function(type, hash) { - if (!hash) { return hash; } - - this.normalizeId(hash); - this.normalizeAttributes(type, hash); - this.normalizeRelationships(type, hash); - - this.normalizeUsingDeclaredMapping(type, hash); - this.applyTransforms(type, hash); - return hash; - }, - - /** - You can use this method to normalize all payloads, regardless of whether they - represent single records or an array. - - For example, you might want to remove some extraneous data from the payload: - - ```js - App.ApplicationSerializer = DS.JSONSerializer.extend({ - normalizePayload: function(payload) { - delete payload.version; - delete payload.status; - return payload; - } - }); - ``` - - @method normalizePayload - @param {Object} payload - @return {Object} the normalized payload - */ - normalizePayload: function(payload) { - return payload; - }, - - /** - @method normalizeAttributes - @private - */ - normalizeAttributes: function(type, hash) { - var payloadKey; - - if (this.keyForAttribute) { - type.eachAttribute(function(key) { - payloadKey = this.keyForAttribute(key); - if (key === payloadKey) { return; } - if (!hash.hasOwnProperty(payloadKey)) { return; } - - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - }, this); - } - }, - - /** - @method normalizeRelationships - @private - */ - normalizeRelationships: function(type, hash) { - var payloadKey; - - if (this.keyForRelationship) { - type.eachRelationship(function(key, relationship) { - payloadKey = this.keyForRelationship(key, relationship.kind); - if (key === payloadKey) { return; } - if (!hash.hasOwnProperty(payloadKey)) { return; } - - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - }, this); - } - }, - - /** - @method normalizeUsingDeclaredMapping - @private - */ - normalizeUsingDeclaredMapping: function(type, hash) { - var attrs = get(this, 'attrs'), payloadKey, key; - - if (attrs) { - for (key in attrs) { - payloadKey = this._getMappedKey(key); - if (!hash.hasOwnProperty(payloadKey)) { continue; } - - if (payloadKey !== key) { - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - } - } - } - }, - - /** - @method normalizeId - @private - */ - normalizeId: function(hash) { - var primaryKey = get(this, 'primaryKey'); - - if (primaryKey === 'id') { return; } - - hash.id = hash[primaryKey]; - delete hash[primaryKey]; - }, - - /** - @method normalizeErrors - @private - */ - normalizeErrors: function(type, hash) { - this.normalizeId(hash); - this.normalizeAttributes(type, hash); - this.normalizeRelationships(type, hash); - }, - - /** - Looks up the property key that was set by the custom `attr` mapping - passed to the serializer. - - @method _getMappedKey - @private - @param {String} key - @return {String} key - */ - _getMappedKey: function(key) { - var attrs = get(this, 'attrs'); - var mappedKey; - if (attrs && attrs[key]) { - mappedKey = attrs[key]; - //We need to account for both the {title: 'post_title'} and - //{title: {key: 'post_title'}} forms - if (mappedKey.key){ - mappedKey = mappedKey.key; - } - if (typeof mappedKey === 'string'){ - key = mappedKey; - } - } - - return key; - }, - - /** - Check attrs.key.serialize property to inform if the `key` - can be serialized - - @method _canSerialize - @private - @param {String} key - @return {boolean} true if the key can be serialized - */ - _canSerialize: function(key) { - var attrs = get(this, 'attrs'); - - return !attrs || !attrs[key] || attrs[key].serialize !== false; - }, - - // SERIALIZE - /** - Called when a record is saved in order to convert the - record into JSON. - - By default, it creates a JSON object with a key for - each attribute and belongsTo relationship. - - For example, consider this model: - - ```javascript - App.Comment = DS.Model.extend({ - title: DS.attr(), - body: DS.attr(), - - author: DS.belongsTo('user') - }); - ``` - - The default serialization would create a JSON object like: - - ```javascript - { - "title": "Rails is unagi", - "body": "Rails? Omakase? O_O", - "author": 12 - } - ``` - - By default, attributes are passed through as-is, unless - you specified an attribute type (`DS.attr('date')`). If - you specify a transform, the JavaScript value will be - serialized when inserted into the JSON hash. - - By default, belongs-to relationships are converted into - IDs when inserted into the JSON hash. - - ## IDs - - `serialize` takes an options hash with a single option: - `includeId`. If this option is `true`, `serialize` will, - by default include the ID in the JSON object it builds. - - The adapter passes in `includeId: true` when serializing - a record for `createRecord`, but not for `updateRecord`. - - ## Customization - - Your server may expect a different JSON format than the - built-in serialization format. - - In that case, you can implement `serialize` yourself and - return a JSON hash of your choosing. - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serialize: function(post, options) { - var json = { - POST_TTL: post.get('title'), - POST_BDY: post.get('body'), - POST_CMS: post.get('comments').mapBy('id') - } - - if (options.includeId) { - json.POST_ID_ = post.get('id'); - } - - return json; - } - }); - ``` - - ## Customizing an App-Wide Serializer - - If you want to define a serializer for your entire - application, you'll probably want to use `eachAttribute` - and `eachRelationship` on the record. - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = {}; - - record.eachAttribute(function(name) { - json[serverAttributeName(name)] = record.get(name); - }) - - record.eachRelationship(function(name, relationship) { - if (relationship.kind === 'hasMany') { - json[serverHasManyName(name)] = record.get(name).mapBy('id'); - } - }); - - if (options.includeId) { - json.ID_ = record.get('id'); - } - - return json; - } - }); - - function serverAttributeName(attribute) { - return attribute.underscore().toUpperCase(); - } - - function serverHasManyName(name) { - return serverAttributeName(name.singularize()) + "_IDS"; - } - ``` - - This serializer will generate JSON that looks like this: - - ```javascript - { - "TITLE": "Rails is omakase", - "BODY": "Yep. Omakase.", - "COMMENT_IDS": [ 1, 2, 3 ] - } - ``` - - ## Tweaking the Default JSON - - If you just want to do some small tweaks on the default JSON, - you can call super first and make the tweaks on the returned - JSON. - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = this._super.apply(this, arguments); - - json.subject = json.title; - delete json.title; - - return json; - } - }); - ``` - - @method serialize - @param {subclass of DS.Model} record - @param {Object} options - @return {Object} json - */ - serialize: function(record, options) { - var json = {}; - - if (options && options.includeId) { - var id = get(record, 'id'); - - if (id) { - json[get(this, 'primaryKey')] = id; - } - } - - record.eachAttribute(function(key, attribute) { - this.serializeAttribute(record, json, key, attribute); - }, this); - - record.eachRelationship(function(key, relationship) { - if (relationship.kind === 'belongsTo') { - this.serializeBelongsTo(record, json, relationship); - } else if (relationship.kind === 'hasMany') { - this.serializeHasMany(record, json, relationship); - } - }, this); - - return json; - }, - - /** - You can use this method to customize how a serialized record is added to the complete - JSON hash to be sent to the server. By default the JSON Serializer does not namespace - the payload and just sends the raw serialized JSON object. - If your server expects namespaced keys, you should consider using the RESTSerializer. - Otherwise you can override this method to customize how the record is added to the hash. - - For example, your server may expect underscored root objects. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serializeIntoHash: function(data, type, record, options) { - var root = Ember.String.decamelize(type.typeKey); - data[root] = this.serialize(record, options); - } - }); - ``` - - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type - @param {DS.Model} record - @param {Object} options - */ - serializeIntoHash: function(hash, type, record, options) { - merge(hash, this.serialize(record, options)); - }, - - /** - `serializeAttribute` can be used to customize how `DS.attr` - properties are serialized - - For example if you wanted to ensure all your attributes were always - serialized as properties on an `attributes` object you could - write: - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - serializeAttribute: function(record, json, key, attributes) { - json.attributes = json.attributes || {}; - this._super(record, json.attributes, key, attributes); - } - }); - ``` - - @method serializeAttribute - @param {DS.Model} record - @param {Object} json - @param {String} key - @param {Object} attribute - */ - serializeAttribute: function(record, json, key, attribute) { - var type = attribute.type; - - if (this._canSerialize(key)) { - var value = get(record, key); - if (type) { - var transform = this.transformFor(type); - value = transform.serialize(value); - } - - // if provided, use the mapping provided by `attrs` in - // the serializer - var payloadKey = this._getMappedKey(key); - - if (payloadKey === key && this.keyForAttribute) { - payloadKey = this.keyForAttribute(key); - } - - json[payloadKey] = value; - } - }, - - /** - `serializeBelongsTo` can be used to customize how `DS.belongsTo` - properties are serialized. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serializeBelongsTo: function(record, json, relationship) { - var key = relationship.key; - - var belongsTo = get(record, key); - - key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; - - json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON(); - } - }); - ``` - - @method serializeBelongsTo - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeBelongsTo: function(record, json, relationship) { - var key = relationship.key; - - if (this._canSerialize(key)) { - var belongsTo = get(record, key); - - // if provided, use the mapping provided by `attrs` in - // the serializer - var payloadKey = this._getMappedKey(key); - if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, "belongsTo"); - } - - //Need to check whether the id is there for new&async records - if (isNone(belongsTo) || isNone(get(belongsTo, 'id'))) { - json[payloadKey] = null; - } else { - json[payloadKey] = get(belongsTo, 'id'); - } - - if (relationship.options.polymorphic) { - this.serializePolymorphicType(record, json, relationship); - } - } - }, - - /** - `serializeHasMany` can be used to customize how `DS.hasMany` - properties are serialized. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key; - if (key === 'comments') { - return; - } else { - this._super.apply(this, arguments); - } - } - }); - ``` - - @method serializeHasMany - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key; - - if (this._canSerialize(key)) { - var payloadKey; - - // if provided, use the mapping provided by `attrs` in - // the serializer - payloadKey = this._getMappedKey(key); - if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, "hasMany"); - } - - var relationshipType = record.constructor.determineRelationshipType(relationship); - - if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') { - json[payloadKey] = get(record, key).mapBy('id'); - // TODO support for polymorphic manyToNone and manyToMany relationships - } - } - }, - - /** - You can use this method to customize how polymorphic objects are - serialized. Objects are considered to be polymorphic if - `{polymorphic: true}` is pass as the second argument to the - `DS.belongsTo` function. - - Example - - ```javascript - App.CommentSerializer = DS.JSONSerializer.extend({ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key, - belongsTo = get(record, key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; - - if (Ember.isNone(belongsTo)) { - json[key + "_type"] = null; - } else { - json[key + "_type"] = belongsTo.constructor.typeKey; - } - } - }); - ``` - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializePolymorphicType: Ember.K, - - // EXTRACT - - /** - The `extract` method is used to deserialize payload data from the - server. By default the `JSONSerializer` does not push the records - into the store. However records that subclass `JSONSerializer` - such as the `RESTSerializer` may push records into the store as - part of the extract call. - - This method delegates to a more specific extract method based on - the `requestType`. - - Example - - ```javascript - var get = Ember.get; - socket.on('message', function(message) { - var modelName = message.model; - var data = message.data; - var type = store.modelFor(modelName); - var serializer = store.serializerFor(type.typeKey); - var record = serializer.extract(store, type, data, get(data, 'id'), 'single'); - store.push(modelName, record); - }); - ``` - - @method extract - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extract: function(store, type, payload, id, requestType) { - this.extractMeta(store, type, payload); - - var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1); - return this[specificExtract](store, type, payload, id, requestType); - }, - - /** - `extractFindAll` is a hook into the extract method used when a - call is made to `DS.Store#findAll`. By default this method is an - alias for [extractArray](#method_extractArray). - - @method extractFindAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Array} array An array of deserialized objects - */ - extractFindAll: function(store, type, payload, id, requestType){ - return this.extractArray(store, type, payload, id, requestType); - }, - /** - `extractFindQuery` is a hook into the extract method used when a - call is made to `DS.Store#findQuery`. By default this method is an - alias for [extractArray](#method_extractArray). - - @method extractFindQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Array} array An array of deserialized objects - */ - extractFindQuery: function(store, type, payload, id, requestType){ - return this.extractArray(store, type, payload, id, requestType); - }, - /** - `extractFindMany` is a hook into the extract method used when a - call is made to `DS.Store#findMany`. By default this method is - alias for [extractArray](#method_extractArray). - - @method extractFindMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Array} array An array of deserialized objects - */ - extractFindMany: function(store, type, payload, id, requestType){ - return this.extractArray(store, type, payload, id, requestType); - }, - /** - `extractFindHasMany` is a hook into the extract method used when a - call is made to `DS.Store#findHasMany`. By default this method is - alias for [extractArray](#method_extractArray). - - @method extractFindHasMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Array} array An array of deserialized objects - */ - extractFindHasMany: function(store, type, payload, id, requestType){ - return this.extractArray(store, type, payload, id, requestType); - }, - - /** - `extractCreateRecord` is a hook into the extract method used when a - call is made to `DS.Store#createRecord`. By default this method is - alias for [extractSave](#method_extractSave). - - @method extractCreateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractCreateRecord: function(store, type, payload, id, requestType) { - return this.extractSave(store, type, payload, id, requestType); - }, - /** - `extractUpdateRecord` is a hook into the extract method used when - a call is made to `DS.Store#update`. By default this method is alias - for [extractSave](#method_extractSave). - - @method extractUpdateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractUpdateRecord: function(store, type, payload, id, requestType) { - return this.extractSave(store, type, payload, id, requestType); - }, - /** - `extractDeleteRecord` is a hook into the extract method used when - a call is made to `DS.Store#deleteRecord`. By default this method is - alias for [extractSave](#method_extractSave). - - @method extractDeleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractDeleteRecord: function(store, type, payload, id, requestType) { - return this.extractSave(store, type, payload, id, requestType); - }, - - /** - `extractFind` is a hook into the extract method used when - a call is made to `DS.Store#find`. By default this method is - alias for [extractSingle](#method_extractSingle). - - @method extractFind - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractFind: function(store, type, payload, id, requestType) { - return this.extractSingle(store, type, payload, id, requestType); - }, - /** - `extractFindBelongsTo` is a hook into the extract method used when - a call is made to `DS.Store#findBelongsTo`. By default this method is - alias for [extractSingle](#method_extractSingle). - - @method extractFindBelongsTo - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractFindBelongsTo: function(store, type, payload, id, requestType) { - return this.extractSingle(store, type, payload, id, requestType); - }, - /** - `extractSave` is a hook into the extract method used when a call - is made to `DS.Model#save`. By default this method is alias - for [extractSingle](#method_extractSingle). - - @method extractSave - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractSave: function(store, type, payload, id, requestType) { - return this.extractSingle(store, type, payload, id, requestType); - }, - - /** - `extractSingle` is used to deserialize a single record returned - from the adapter. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractSingle: function(store, type, payload) { - payload.comments = payload._embedded.comment; - delete payload._embedded; - - return this._super(store, type, payload); - }, - }); - ``` - - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extractSingle: function(store, type, payload, id, requestType) { - payload = this.normalizePayload(payload); - return this.normalize(type, payload); - }, - - /** - `extractArray` is used to deserialize an array of records - returned from the adapter. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractArray: function(store, type, payload) { - return payload.map(function(json) { - return this.extractSingle(store, type, json); - }, this); - } - }); - ``` - - @method extractArray - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Array} array An array of deserialized objects - */ - extractArray: function(store, type, arrayPayload, id, requestType) { - var normalizedPayload = this.normalizePayload(arrayPayload); - var serializer = this; - - return map.call(normalizedPayload, function(singlePayload) { - return serializer.normalize(type, singlePayload); - }); - }, - - /** - `extractMeta` is used to deserialize any meta information in the - adapter payload. By default Ember Data expects meta information to - be located on the `meta` property of the payload object. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractMeta: function(store, type, payload) { - if (payload && payload._pagination) { - store.metaForType(type, payload._pagination); - delete payload._pagination; - } - } - }); - ``` - - @method extractMeta - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - */ - extractMeta: function(store, type, payload) { - if (payload && payload.meta) { - store.metaForType(type, payload.meta); - delete payload.meta; - } - }, - - /** - `extractErrors` is used to extract model errors when a call is made - to `DS.Model#save` which fails with an InvalidError`. By default - Ember Data expects error information to be located on the `errors` - property of the payload object. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractErrors: function(store, type, payload, id) { - if (payload && typeof payload === 'object' && payload._problems) { - payload = payload._problems; - this.normalizeErrors(type, payload); - } - return payload; - } - }); - ``` - - @method extractErrors - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @return {Object} json The deserialized errors - */ - extractErrors: function(store, type, payload, id) { - if (payload && typeof payload === 'object' && payload.errors) { - payload = payload.errors; - this.normalizeErrors(type, payload); - } - return payload; - }, - - /** - `keyForAttribute` can be used to define rules for how to convert an - attribute name in your model to a key in your JSON. - - Example - - ```javascript - App.ApplicationSerializer = DS.RESTSerializer.extend({ - keyForAttribute: function(attr) { - return Ember.String.underscore(attr).toUpperCase(); - } - }); - ``` - - @method keyForAttribute - @param {String} key - @return {String} normalized key - */ - keyForAttribute: function(key){ - return key; - }, - - /** - `keyForRelationship` can be used to define a custom key when - serializing relationship properties. By default `JSONSerializer` - does not provide an implementation of this method. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - keyForRelationship: function(key, relationship) { - return 'rel_' + Ember.String.underscore(key); - } - }); - ``` - - @method keyForRelationship - @param {String} key - @param {String} relationship type - @return {String} normalized key - */ - - keyForRelationship: function(key, type){ - return key; - }, - - // HELPERS - - /** - @method transformFor - @private - @param {String} attributeType - @param {Boolean} skipAssertion - @return {DS.Transform} transform - */ - transformFor: function(attributeType, skipAssertion) { - var transform = this.container.lookup('transform:' + attributeType); - Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform); - return transform; - } - }); - }); -enifed("ember-data/serializers/rest_serializer", - ["ember-data/serializers/json_serializer","ember-inflector/system/string","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var JSONSerializer = __dependency1__["default"]; - var get = Ember.get; - var forEach = Ember.ArrayPolyfills.forEach; - var map = Ember.ArrayPolyfills.map; - var camelize = Ember.String.camelize; - - var singularize = __dependency2__.singularize; - - function coerceId(id) { - return id == null ? null : id + ''; - } - - /** - Normally, applications will use the `RESTSerializer` by implementing - the `normalize` method and individual normalizations under - `normalizeHash`. - - This allows you to do whatever kind of munging you need, and is - especially useful if your server is inconsistent and you need to - do munging differently for many different kinds of responses. - - See the `normalize` documentation for more information. - - ## Across the Board Normalization - - There are also a number of hooks that you might find useful to define - across-the-board rules for your payload. These rules will be useful - if your server is consistent, or if you're building an adapter for - an infrastructure service, like Parse, and want to encode service - conventions. - - For example, if all of your keys are underscored and all-caps, but - otherwise consistent with the names you use in your models, you - can implement across-the-board rules for how to convert an attribute - name in your model to a key in your JSON. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - keyForAttribute: function(attr) { - return Ember.String.underscore(attr).toUpperCase(); - } - }); - ``` - - You can also implement `keyForRelationship`, which takes the name - of the relationship as the first parameter, and the kind of - relationship (`hasMany` or `belongsTo`) as the second parameter. - - @class RESTSerializer - @namespace DS - @extends DS.JSONSerializer - */ - var RESTSerializer = JSONSerializer.extend({ - /** - If you want to do normalizations specific to some part of the payload, you - can specify those under `normalizeHash`. - - For example, given the following json where the the `IDs` under - `"comments"` are provided as `_id` instead of `id`. - - ```javascript - { - "post": { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2 ] - }, - "comments": [{ - "_id": 1, - "body": "FIRST" - }, { - "_id": 2, - "body": "Rails is unagi" - }] - } - ``` - - You use `normalizeHash` to normalize just the comments: - - ```javascript - App.PostSerializer = DS.RESTSerializer.extend({ - normalizeHash: { - comments: function(hash) { - hash.id = hash._id; - delete hash._id; - return hash; - } - } - }); - ``` - - The key under `normalizeHash` is usually just the original key - that was in the original payload. However, key names will be - impacted by any modifications done in the `normalizePayload` - method. The `DS.RESTSerializer`'s default implementation makes no - changes to the payload keys. - - @property normalizeHash - @type {Object} - @default undefined - */ - - /** - Normalizes a part of the JSON payload returned by - the server. You should override this method, munge the hash - and call super if you have generic normalization to do. - - It takes the type of the record that is being normalized - (as a DS.Model class), the property where the hash was - originally found, and the hash to normalize. - - For example, if you have a payload that looks like this: - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2 ] - }, - "comments": [{ - "id": 1, - "body": "FIRST" - }, { - "id": 2, - "body": "Rails is unagi" - }] - } - ``` - - The `normalize` method will be called three times: - - * With `App.Post`, `"posts"` and `{ id: 1, title: "Rails is omakase", ... }` - * With `App.Comment`, `"comments"` and `{ id: 1, body: "FIRST" }` - * With `App.Comment`, `"comments"` and `{ id: 2, body: "Rails is unagi" }` - - You can use this method, for example, to normalize underscored keys to camelized - or other general-purpose normalizations. - - If you want to do normalizations specific to some part of the payload, you - can specify those under `normalizeHash`. - - For example, if the `IDs` under `"comments"` are provided as `_id` instead of - `id`, you can specify how to normalize just the comments: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - normalizeHash: { - comments: function(hash) { - hash.id = hash._id; - delete hash._id; - return hash; - } - } - }); - ``` - - The key under `normalizeHash` is just the original key that was in the original - payload. - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @param {String} prop - @return {Object} - */ - normalize: function(type, hash, prop) { - this.normalizeId(hash); - this.normalizeAttributes(type, hash); - this.normalizeRelationships(type, hash); - - this.normalizeUsingDeclaredMapping(type, hash); - - if (this.normalizeHash && this.normalizeHash[prop]) { - this.normalizeHash[prop](hash); - } - - this.applyTransforms(type, hash); - return hash; - }, - - - /** - Called when the server has returned a payload representing - a single record, such as in response to a `find` or `save`. - - It is your opportunity to clean up the server's response into the normalized - form expected by Ember Data. - - If you want, you can just restructure the top-level of your payload, and - do more fine-grained normalization in the `normalize` method. - - For example, if you have a payload like this in response to a request for - post 1: - - ```js - { - "id": 1, - "title": "Rails is omakase", - - "_embedded": { - "comment": [{ - "_id": 1, - "comment_title": "FIRST" - }, { - "_id": 2, - "comment_title": "Rails is unagi" - }] - } - } - ``` - - You could implement a serializer that looks like this to get your payload - into shape: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - // First, restructure the top-level so it's organized by type - extractSingle: function(store, type, payload, id) { - var comments = payload._embedded.comment; - delete payload._embedded; - - payload = { comments: comments, post: payload }; - return this._super(store, type, payload, id); - }, - - normalizeHash: { - // Next, normalize individual comments, which (after `extract`) - // are now located under `comments` - comments: function(hash) { - hash.id = hash._id; - hash.title = hash.comment_title; - delete hash._id; - delete hash.comment_title; - return hash; - } - } - }) - ``` - - When you call super from your own implementation of `extractSingle`, the - built-in implementation will find the primary record in your normalized - payload and push the remaining records into the store. - - The primary record is the single hash found under `post` or the first - element of the `posts` array. - - The primary record has special meaning when the record is being created - for the first time or updated (`createRecord` or `updateRecord`). In - particular, it will update the properties of the record that was saved. - - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} primaryType - @param {Object} payload - @param {String} recordId - @return {Object} the primary response to the original request - */ - extractSingle: function(store, primaryType, rawPayload, recordId) { - var payload = this.normalizePayload(rawPayload); - var primaryTypeName = primaryType.typeKey; - var primaryRecord; - - for (var prop in payload) { - var typeName = this.typeForRoot(prop); - - if (!store.modelFactoryFor(typeName)){ - Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false); - continue; - } - var type = store.modelFor(typeName); - var isPrimary = type.typeKey === primaryTypeName; - var value = payload[prop]; - - if (value === null) { - continue; - } - - // legacy support for singular resources - if (isPrimary && Ember.typeOf(value) !== "array" ) { - primaryRecord = this.normalize(primaryType, value, prop); - continue; - } - - /*jshint loopfunc:true*/ - forEach.call(value, function(hash) { - var typeName = this.typeForRoot(prop); - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); - - hash = typeSerializer.normalize(type, hash, prop); - - var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord; - var isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId; - - // find the primary record. - // - // It's either: - // * the record with the same ID as the original request - // * in the case of a newly created record that didn't have an ID, the first - // record in the Array - if (isFirstCreatedRecord || isUpdatedRecord) { - primaryRecord = hash; - } else { - store.push(typeName, hash); - } - }, this); - } - - return primaryRecord; - }, - - /** - Called when the server has returned a payload representing - multiple records, such as in response to a `findAll` or `findQuery`. - - It is your opportunity to clean up the server's response into the normalized - form expected by Ember Data. - - If you want, you can just restructure the top-level of your payload, and - do more fine-grained normalization in the `normalize` method. - - For example, if you have a payload like this in response to a request for - all posts: - - ```js - { - "_embedded": { - "post": [{ - "id": 1, - "title": "Rails is omakase" - }, { - "id": 2, - "title": "The Parley Letter" - }], - "comment": [{ - "_id": 1, - "comment_title": "Rails is unagi" - "post_id": 1 - }, { - "_id": 2, - "comment_title": "Don't tread on me", - "post_id": 2 - }] - } - } - ``` - - You could implement a serializer that looks like this to get your payload - into shape: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - // First, restructure the top-level so it's organized by type - // and the comments are listed under a post's `comments` key. - extractArray: function(store, type, payload) { - var posts = payload._embedded.post; - var comments = []; - var postCache = {}; - - posts.forEach(function(post) { - post.comments = []; - postCache[post.id] = post; - }); - - payload._embedded.comment.forEach(function(comment) { - comments.push(comment); - postCache[comment.post_id].comments.push(comment); - delete comment.post_id; - }); - - payload = { comments: comments, posts: payload }; - - return this._super(store, type, payload); - }, - - normalizeHash: { - // Next, normalize individual comments, which (after `extract`) - // are now located under `comments` - comments: function(hash) { - hash.id = hash._id; - hash.title = hash.comment_title; - delete hash._id; - delete hash.comment_title; - return hash; - } - } - }) - ``` - - When you call super from your own implementation of `extractArray`, the - built-in implementation will find the primary array in your normalized - payload and push the remaining records into the store. - - The primary array is the array found under `posts`. - - The primary record has special meaning when responding to `findQuery` - or `findHasMany`. In particular, the primary array will become the - list of records in the record array that kicked off the request. - - If your primary array contains secondary (embedded) records of the same type, - you cannot place these into the primary array `posts`. Instead, place the - secondary items into an underscore prefixed property `_posts`, which will - push these items into the store and will not affect the resulting query. - - @method extractArray - @param {DS.Store} store - @param {subclass of DS.Model} primaryType - @param {Object} payload - @return {Array} The primary array that was returned in response - to the original query. - */ - extractArray: function(store, primaryType, rawPayload) { - var payload = this.normalizePayload(rawPayload); - var primaryTypeName = primaryType.typeKey; - var primaryArray; - - for (var prop in payload) { - var typeKey = prop; - var forcedSecondary = false; - - if (prop.charAt(0) === '_') { - forcedSecondary = true; - typeKey = prop.substr(1); - } - - var typeName = this.typeForRoot(typeKey); - if (!store.modelFactoryFor(typeName)) { - Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false); - continue; - } - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); - var isPrimary = (!forcedSecondary && (type.typeKey === primaryTypeName)); - - /*jshint loopfunc:true*/ - var normalizedArray = map.call(payload[prop], function(hash) { - return typeSerializer.normalize(type, hash, prop); - }, this); - - if (isPrimary) { - primaryArray = normalizedArray; - } else { - store.pushMany(typeName, normalizedArray); - } - } - - return primaryArray; - }, - - /** - This method allows you to push a payload containing top-level - collections of records organized per type. - - ```js - { - "posts": [{ - "id": "1", - "title": "Rails is omakase", - "author", "1", - "comments": [ "1" ] - }], - "comments": [{ - "id": "1", - "body": "FIRST" - }], - "users": [{ - "id": "1", - "name": "@d2h" - }] - } - ``` - - It will first normalize the payload, so you can use this to push - in data streaming in from your server structured the same way - that fetches and saves are structured. - - @method pushPayload - @param {DS.Store} store - @param {Object} payload - */ - pushPayload: function(store, rawPayload) { - var payload = this.normalizePayload(rawPayload); - - for (var prop in payload) { - var typeName = this.typeForRoot(prop); - if (!store.modelFactoryFor(typeName, prop)){ - Ember.warn(this.warnMessageNoModelForKey(prop, typeName), false); - continue; - } - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); - - /*jshint loopfunc:true*/ - var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) { - return typeSerializer.normalize(type, hash, prop); - }, this); - - store.pushMany(typeName, normalizedArray); - } - }, - - /** - This method is used to convert each JSON root key in the payload - into a typeKey that it can use to look up the appropriate model for - that part of the payload. By default the typeKey for a model is its - name in camelCase, so if your JSON root key is 'fast-car' you would - use typeForRoot to convert it to 'fastCar' so that Ember Data finds - the `FastCar` model. - - If you diverge from this norm you should also consider changes to - store._normalizeTypeKey as well. - - For example, your server may return prefixed root keys like so: - - ```js - { - "response-fast-car": { - "id": "1", - "name": "corvette" - } - } - ``` - - In order for Ember Data to know that the model corresponding to - the 'response-fast-car' hash is `FastCar` (typeKey: 'fastCar'), - you can override typeForRoot to convert 'response-fast-car' to - 'fastCar' like so: - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - typeForRoot: function(root) { - // 'response-fast-car' should become 'fast-car' - var subRoot = root.substring(9); - - // _super normalizes 'fast-car' to 'fastCar' - return this._super(subRoot); - } - }); - ``` - - @method typeForRoot - @param {String} key - @return {String} the model's typeKey - */ - typeForRoot: function(key) { - return camelize(singularize(key)); - }, - - // SERIALIZE - - /** - Called when a record is saved in order to convert the - record into JSON. - - By default, it creates a JSON object with a key for - each attribute and belongsTo relationship. - - For example, consider this model: - - ```js - App.Comment = DS.Model.extend({ - title: DS.attr(), - body: DS.attr(), - - author: DS.belongsTo('user') - }); - ``` - - The default serialization would create a JSON object like: - - ```js - { - "title": "Rails is unagi", - "body": "Rails? Omakase? O_O", - "author": 12 - } - ``` - - By default, attributes are passed through as-is, unless - you specified an attribute type (`DS.attr('date')`). If - you specify a transform, the JavaScript value will be - serialized when inserted into the JSON hash. - - By default, belongs-to relationships are converted into - IDs when inserted into the JSON hash. - - ## IDs - - `serialize` takes an options hash with a single option: - `includeId`. If this option is `true`, `serialize` will, - by default include the ID in the JSON object it builds. - - The adapter passes in `includeId: true` when serializing - a record for `createRecord`, but not for `updateRecord`. - - ## Customization - - Your server may expect a different JSON format than the - built-in serialization format. - - In that case, you can implement `serialize` yourself and - return a JSON hash of your choosing. - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - serialize: function(post, options) { - var json = { - POST_TTL: post.get('title'), - POST_BDY: post.get('body'), - POST_CMS: post.get('comments').mapBy('id') - } - - if (options.includeId) { - json.POST_ID_ = post.get('id'); - } - - return json; - } - }); - ``` - - ## Customizing an App-Wide Serializer - - If you want to define a serializer for your entire - application, you'll probably want to use `eachAttribute` - and `eachRelationship` on the record. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serialize: function(record, options) { - var json = {}; - - record.eachAttribute(function(name) { - json[serverAttributeName(name)] = record.get(name); - }) - - record.eachRelationship(function(name, relationship) { - if (relationship.kind === 'hasMany') { - json[serverHasManyName(name)] = record.get(name).mapBy('id'); - } - }); - - if (options.includeId) { - json.ID_ = record.get('id'); - } - - return json; - } - }); - - function serverAttributeName(attribute) { - return attribute.underscore().toUpperCase(); - } - - function serverHasManyName(name) { - return serverAttributeName(name.singularize()) + "_IDS"; - } - ``` - - This serializer will generate JSON that looks like this: - - ```js - { - "TITLE": "Rails is omakase", - "BODY": "Yep. Omakase.", - "COMMENT_IDS": [ 1, 2, 3 ] - } - ``` - - ## Tweaking the Default JSON - - If you just want to do some small tweaks on the default JSON, - you can call super first and make the tweaks on the returned - JSON. - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - serialize: function(record, options) { - var json = this._super(record, options); - - json.subject = json.title; - delete json.title; - - return json; - } - }); - ``` - - @method serialize - @param {subclass of DS.Model} record - @param {Object} options - @return {Object} json - */ - serialize: function(record, options) { - return this._super.apply(this, arguments); - }, - - /** - You can use this method to customize the root keys serialized into the JSON. - By default the REST Serializer sends the typeKey of a model, which is a camelized - version of the name. - - For example, your server may expect underscored root objects. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serializeIntoHash: function(data, type, record, options) { - var root = Ember.String.decamelize(type.typeKey); - data[root] = this.serialize(record, options); - } - }); - ``` - - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type - @param {DS.Model} record - @param {Object} options - */ - serializeIntoHash: function(hash, type, record, options) { - hash[type.typeKey] = this.serialize(record, options); - }, - - /** - You can use this method to customize how polymorphic objects are serialized. - By default the JSON Serializer creates the key by appending `Type` to - the attribute and value from the model's camelcased model name. - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key; - var belongsTo = get(record, key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; - if (Ember.isNone(belongsTo)) { - json[key + "Type"] = null; - } else { - json[key + "Type"] = Ember.String.camelize(belongsTo.constructor.typeKey); - } - } - }); - - Ember.runInDebug(function(){ - RESTSerializer.reopen({ - warnMessageNoModelForKey: function(prop, typeKey){ - return 'Encountered "' + prop + '" in payload, but no model was found for model name "' + typeKey + '" (resolved model name using ' + this.constructor.toString() + '.typeForRoot("' + prop + '"))'; - } - }); - }); - - __exports__["default"] = RESTSerializer; - }); -enifed("ember-data/setup-container", - ["ember-data/initializers/store","ember-data/initializers/transforms","ember-data/initializers/store_injections","ember-data/initializers/data_adapter","activemodel-adapter/setup-container","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var initializeStore = __dependency1__["default"]; - var initializeTransforms = __dependency2__["default"]; - var initializeStoreInjections = __dependency3__["default"]; - var initializeDataAdapter = __dependency4__["default"]; - var setupActiveModelContainer = __dependency5__["default"]; - - __exports__["default"] = function setupContainer(container, application){ - // application is not a required argument. This ensures - // testing setups can setup a container without booting an - // entire ember application. - - initializeDataAdapter(container, application); - initializeTransforms(container, application); - initializeStoreInjections(container, application); - initializeStore(container, application); - setupActiveModelContainer(container, application); - }; - }); -enifed("ember-data/system/adapter", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ - - var get = Ember.get; - - var errorProps = [ - 'description', - 'fileName', - 'lineNumber', - 'message', - 'name', - 'number', - 'stack' - ]; - - /** - A `DS.InvalidError` is used by an adapter to signal the external API - was unable to process a request because the content was not - semantically correct or meaningful per the API. Usually this means a - record failed some form of server side validation. When a promise - from an adapter is rejected with a `DS.InvalidError` the record will - transition to the `invalid` state and the errors will be set to the - `errors` property on the record. - - This function should return the entire payload as received from the - server. Error object extraction and normalization of model errors - should be performed by `extractErrors` on the serializer. - - Example - - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); - - if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText); - return new DS.InvalidError(jsonErrors); - } else { - return error; - } - } - }); - ``` - - The `DS.InvalidError` must be constructed with a single object whose - keys are the invalid model properties, and whose values are the - corresponding error messages. For example: - - ```javascript - return new DS.InvalidError({ - length: 'Must be less than 15', - name: 'Must not be blank' - }); - ``` - - @class InvalidError - @namespace DS - */ - function InvalidError(errors) { - var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors)); - this.errors = errors; - - for (var i=0, l=errorProps.length; i<l; i++) { - this[errorProps[i]] = tmp[errorProps[i]]; - } - } - - InvalidError.prototype = Ember.create(Error.prototype); - - /** - An adapter is an object that receives requests from a store and - translates them into the appropriate action to take against your - persistence layer. The persistence layer is usually an HTTP API, but - may be anything, such as the browser's local storage. Typically the - adapter is not invoked directly instead its functionality is accessed - through the `store`. - - ### Creating an Adapter - - Create a new subclass of `DS.Adapter`, then assign - it to the `ApplicationAdapter` property of the application. - - ```javascript - var MyAdapter = DS.Adapter.extend({ - // ...your code here - }); - - App.ApplicationAdapter = MyAdapter; - ``` - - Model-specific adapters can be created by assigning your adapter - class to the `ModelName` + `Adapter` property of the application. - - ```javascript - var MyPostAdapter = DS.Adapter.extend({ - // ...Post-specific adapter code goes here - }); - - App.PostAdapter = MyPostAdapter; - ``` - - `DS.Adapter` is an abstract base class that you should override in your - application to customize it for your backend. The minimum set of methods - that you should implement is: - - * `find()` - * `createRecord()` - * `updateRecord()` - * `deleteRecord()` - * `findAll()` - * `findQuery()` - - To improve the network performance of your application, you can optimize - your adapter by overriding these lower-level methods: - - * `findMany()` - - - For an example implementation, see `DS.RESTAdapter`, the - included REST adapter. - - @class Adapter - @namespace DS - @extends Ember.Object - */ - - var Adapter = Ember.Object.extend({ - - /** - If you would like your adapter to use a custom serializer you can - set the `defaultSerializer` property to be the name of the custom - serializer. - - Note the `defaultSerializer` serializer has a lower priority than - a model specific serializer (i.e. `PostSerializer`) or the - `application` serializer. - - ```javascript - var DjangoAdapter = DS.Adapter.extend({ - defaultSerializer: 'django' - }); - ``` - - @property defaultSerializer - @type {String} - */ - - /** - The `find()` method is invoked when the store is asked for a record that - has not previously been loaded. In response to `find()` being called, you - should query your persistence layer for a record with the given ID. Once - found, you can asynchronously call the store's `push()` method to push - the record into the store. - - Here is an example `find` implementation: - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - find: function(store, type, id) { - var url = [type.typeKey, id].join('/'); - - return new Ember.RSVP.Promise(function(resolve, reject) { - jQuery.getJSON(url).then(function(data) { - Ember.run(null, resolve, data); - }, function(jqXHR) { - jqXHR.then = null; // tame jQuery's ill mannered promises - Ember.run(null, reject, jqXHR); - }); - }); - } - }); - ``` - - @method find - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} id - @return {Promise} promise - */ - find: Ember.required(Function), - - /** - The `findAll()` method is called when you call `find` on the store - without an ID (i.e. `store.find('post')`). - - Example - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - findAll: function(store, type, sinceToken) { - var url = type; - var query = { since: sinceToken }; - return new Ember.RSVP.Promise(function(resolve, reject) { - jQuery.getJSON(url, query).then(function(data) { - Ember.run(null, resolve, data); - }, function(jqXHR) { - jqXHR.then = null; // tame jQuery's ill mannered promises - Ember.run(null, reject, jqXHR); - }); - }); - } - }); - ``` - - @private - @method findAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} sinceToken - @return {Promise} promise - */ - findAll: null, - - /** - This method is called when you call `find` on the store with a - query object as the second parameter (i.e. `store.find('person', { - page: 1 })`). - - Example - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - findQuery: function(store, type, query) { - var url = type; - return new Ember.RSVP.Promise(function(resolve, reject) { - jQuery.getJSON(url, query).then(function(data) { - Ember.run(null, resolve, data); - }, function(jqXHR) { - jqXHR.then = null; // tame jQuery's ill mannered promises - Ember.run(null, reject, jqXHR); - }); - }); - } - }); - ``` - - @private - @method findQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} query - @param {DS.AdapterPopulatedRecordArray} recordArray - @return {Promise} promise - */ - findQuery: null, - - /** - If the globally unique IDs for your records should be generated on the client, - implement the `generateIdForRecord()` method. This method will be invoked - each time you create a new record, and the value returned from it will be - assigned to the record's `primaryKey`. - - Most traditional REST-like HTTP APIs will not use this method. Instead, the ID - of the record will be set by the server, and your adapter will update the store - with the new ID when it calls `didCreateRecord()`. Only implement this method if - you intend to generate record IDs on the client-side. - - The `generateIdForRecord()` method will be invoked with the requesting store as - the first parameter and the newly created record as the second parameter: - - ```javascript - generateIdForRecord: function(store, record) { - var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision(); - return uuid; - } - ``` - - @method generateIdForRecord - @param {DS.Store} store - @param {DS.Model} record - @return {String|Number} id - */ - generateIdForRecord: null, - - /** - Proxies to the serializer's `serialize` method. - - Example - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - createRecord: function(store, type, record) { - var data = this.serialize(record, { includeId: true }); - var url = type; - - // ... - } - }); - ``` - - @method serialize - @param {DS.Model} record - @param {Object} options - @return {Object} serialized record - */ - serialize: function(record, options) { - return get(record, 'store').serializerFor(record.constructor.typeKey).serialize(record, options); - }, - - /** - Implement this method in a subclass to handle the creation of - new records. - - Serializes the record and send it to the server. - - Example - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - createRecord: function(store, type, record) { - var data = this.serialize(record, { includeId: true }); - var url = type; - - return new Ember.RSVP.Promise(function(resolve, reject) { - jQuery.ajax({ - type: 'POST', - url: url, - dataType: 'json', - data: data - }).then(function(data) { - Ember.run(null, resolve, data); - }, function(jqXHR) { - jqXHR.then = null; // tame jQuery's ill mannered promises - Ember.run(null, reject, jqXHR); - }); - }); - } - }); - ``` - - @method createRecord - @param {DS.Store} store - @param {subclass of DS.Model} type the DS.Model class of the record - @param {DS.Model} record - @return {Promise} promise - */ - createRecord: Ember.required(Function), - - /** - Implement this method in a subclass to handle the updating of - a record. - - Serializes the record update and send it to the server. - - Example - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - updateRecord: function(store, type, record) { - var data = this.serialize(record, { includeId: true }); - var id = record.get('id'); - var url = [type, id].join('/'); - - return new Ember.RSVP.Promise(function(resolve, reject) { - jQuery.ajax({ - type: 'PUT', - url: url, - dataType: 'json', - data: data - }).then(function(data) { - Ember.run(null, resolve, data); - }, function(jqXHR) { - jqXHR.then = null; // tame jQuery's ill mannered promises - Ember.run(null, reject, jqXHR); - }); - }); - } - }); - ``` - - @method updateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type the DS.Model class of the record - @param {DS.Model} record - @return {Promise} promise - */ - updateRecord: Ember.required(Function), - - /** - Implement this method in a subclass to handle the deletion of - a record. - - Sends a delete request for the record to the server. - - Example - - ```javascript - App.ApplicationAdapter = DS.Adapter.extend({ - deleteRecord: function(store, type, record) { - var data = this.serialize(record, { includeId: true }); - var id = record.get('id'); - var url = [type, id].join('/'); - - return new Ember.RSVP.Promise(function(resolve, reject) { - jQuery.ajax({ - type: 'DELETE', - url: url, - dataType: 'json', - data: data - }).then(function(data) { - Ember.run(null, resolve, data); - }, function(jqXHR) { - jqXHR.then = null; // tame jQuery's ill mannered promises - Ember.run(null, reject, jqXHR); - }); - }); - } - }); - ``` - - @method deleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type the DS.Model class of the record - @param {DS.Model} record - @return {Promise} promise - */ - deleteRecord: Ember.required(Function), - - /** - By default the store will try to coalesce all `fetchRecord` calls within the same runloop - into as few requests as possible by calling groupRecordsForFindMany and passing it into a findMany call. - You can opt out of this behaviour by either not implementing the findMany hook or by setting - coalesceFindRequests to false - - @property coalesceFindRequests - @type {boolean} - */ - coalesceFindRequests: true, - - /** - Find multiple records at once if coalesceFindRequests is true - - @method findMany - @param {DS.Store} store - @param {subclass of DS.Model} type the DS.Model class of the records - @param {Array} ids - @param {Array} records - @return {Promise} promise - */ - - /** - Organize records into groups, each of which is to be passed to separate - calls to `findMany`. - - For example, if your api has nested URLs that depend on the parent, you will - want to group records by their parent. - - The default implementation returns the records as a single group. - - @method groupRecordsForFindMany - @param {DS.Store} store - @param {Array} records - @return {Array} an array of arrays of records, each of which is to be - loaded separately by `findMany`. - */ - groupRecordsForFindMany: function (store, records) { - return [records]; - } - }); - - __exports__.InvalidError = InvalidError; - __exports__.Adapter = Adapter; - __exports__["default"] = Adapter; - }); -enifed("ember-data/system/container_proxy", - ["exports"], - function(__exports__) { - "use strict"; - /** - This is used internally to enable deprecation of container paths and provide - a decent message to the user indicating how to fix the issue. - - @class ContainerProxy - @namespace DS - @private - */ - function ContainerProxy(container){ - this.container = container; - } - - ContainerProxy.prototype.aliasedFactory = function(path, preLookup) { - var _this = this; - - return {create: function(){ - if (preLookup) { preLookup(); } - - return _this.container.lookup(path); - }}; - }; - - ContainerProxy.prototype.registerAlias = function(source, dest, preLookup) { - var factory = this.aliasedFactory(dest, preLookup); - - return this.container.register(source, factory); - }; - - ContainerProxy.prototype.registerDeprecation = function(deprecated, valid) { - var preLookupCallback = function(){ - Ember.deprecate("You tried to look up '" + deprecated + "', " + - "but this has been deprecated in favor of '" + valid + "'.", false); - }; - - return this.registerAlias(deprecated, valid, preLookupCallback); - }; - - ContainerProxy.prototype.registerDeprecations = function(proxyPairs) { - var i, proxyPair, deprecated, valid; - - for (i = proxyPairs.length; i > 0; i--) { - proxyPair = proxyPairs[i - 1]; - deprecated = proxyPair['deprecated']; - valid = proxyPair['valid']; - - this.registerDeprecation(deprecated, valid); - } - }; - - __exports__["default"] = ContainerProxy; - }); -enifed("ember-data/system/create", - [], - function() { - "use strict"; - /* - Detect if the user has a correct Object.create shim. - Ember has provided this for a long time but has had an incorrect shim before 1.8 - TODO: Remove for Ember Data 1.0. - */ - var object = Ember.create(null); - if (object.toString !== undefined && Ember.keys(Ember.create({}))[0] === '__proto__'){ - throw new Error("Ember Data requires a correct Object.create shim. You should upgrade to Ember >= 1.8 which provides one for you. If you are using ES5-shim, you should try removing that after upgrading Ember."); - } - }); -enifed("ember-data/system/debug", - ["ember-data/system/debug/debug_info","ember-data/system/debug/debug_adapter","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var DebugAdapter = __dependency2__["default"]; - - __exports__["default"] = DebugAdapter; - }); -enifed("ember-data/system/debug/debug_adapter", - ["ember-data/system/model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - var Model = __dependency1__.Model; - var get = Ember.get; - var capitalize = Ember.String.capitalize; - var underscore = Ember.String.underscore; - - /** - Extend `Ember.DataAdapter` with ED specific code. - - @class DebugAdapter - @namespace DS - @extends Ember.DataAdapter - @private - */ - __exports__["default"] = Ember.DataAdapter.extend({ - getFilters: function() { - return [ - { name: 'isNew', desc: 'New' }, - { name: 'isModified', desc: 'Modified' }, - { name: 'isClean', desc: 'Clean' } - ]; - }, - - detect: function(klass) { - return klass !== Model && Model.detect(klass); - }, - - columnsForType: function(type) { - var columns = [{ - name: 'id', - desc: 'Id' - }]; - var count = 0; - var self = this; - get(type, 'attributes').forEach(function(meta, name) { - if (count++ > self.attributeLimit) { return false; } - var desc = capitalize(underscore(name).replace('_', ' ')); - columns.push({ name: name, desc: desc }); - }); - return columns; - }, - - getRecords: function(type) { - return this.get('store').all(type); - }, - - getRecordColumnValues: function(record) { - var self = this, count = 0; - var columnValues = { id: get(record, 'id') }; - - record.eachAttribute(function(key) { - if (count++ > self.attributeLimit) { - return false; - } - var value = get(record, key); - columnValues[key] = value; - }); - return columnValues; - }, - - getRecordKeywords: function(record) { - var keywords = []; - var keys = Ember.A(['id']); - record.eachAttribute(function(key) { - keys.push(key); - }); - keys.forEach(function(key) { - keywords.push(get(record, key)); - }); - return keywords; - }, - - getRecordFilterValues: function(record) { - return { - isNew: record.get('isNew'), - isModified: record.get('isDirty') && !record.get('isNew'), - isClean: !record.get('isDirty') - }; - }, - - getRecordColor: function(record) { - var color = 'black'; - if (record.get('isNew')) { - color = 'green'; - } else if (record.get('isDirty')) { - color = 'blue'; - } - return color; - }, - - observeRecord: function(record, recordUpdated) { - var releaseMethods = Ember.A(), self = this; - var keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); - - record.eachAttribute(function(key) { - keysToObserve.push(key); - }); - - keysToObserve.forEach(function(key) { - var handler = function() { - recordUpdated(self.wrapRecord(record)); - }; - Ember.addObserver(record, key, handler); - releaseMethods.push(function() { - Ember.removeObserver(record, key, handler); - }); - }); - - var release = function() { - releaseMethods.forEach(function(fn) { fn(); } ); - }; - - return release; - } - - }); - }); -enifed("ember-data/system/debug/debug_info", - ["ember-data/system/model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Model = __dependency1__.Model; - - Model.reopen({ - - /** - Provides info about the model for debugging purposes - by grouping the properties into more semantic groups. - - Meant to be used by debugging tools such as the Chrome Ember Extension. - - - Groups all attributes in "Attributes" group. - - Groups all belongsTo relationships in "Belongs To" group. - - Groups all hasMany relationships in "Has Many" group. - - Groups all flags in "Flags" group. - - Flags relationship CPs as expensive properties. - - @method _debugInfo - @for DS.Model - @private - */ - _debugInfo: function() { - var attributes = ['id'], - relationships = { belongsTo: [], hasMany: [] }, - expensiveProperties = []; - - this.eachAttribute(function(name, meta) { - attributes.push(name); - }, this); - - this.eachRelationship(function(name, relationship) { - relationships[relationship.kind].push(name); - expensiveProperties.push(name); - }); - - var groups = [ - { - name: 'Attributes', - properties: attributes, - expand: true - }, - { - name: 'Belongs To', - properties: relationships.belongsTo, - expand: true - }, - { - name: 'Has Many', - properties: relationships.hasMany, - expand: true - }, - { - name: 'Flags', - properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] - } - ]; - - return { - propertyInfo: { - // include all other mixins / properties (not just the grouped ones) - includeOtherProperties: true, - groups: groups, - // don't pre-calculate unless cached - expensiveProperties: expensiveProperties - } - }; - } - }); - - __exports__["default"] = Model; - }); -enifed("ember-data/system/map", - ["exports"], - function(__exports__) { - "use strict"; - /** - * Polyfill Ember.Map behavior for Ember <= 1.7 - * This can probably be removed before 1.0 final - */ - var mapForEach, deleteFn; - - function OrderedSet(){ - Ember.OrderedSet.apply(this, arguments); - } - - function Map() { - Ember.Map.apply(this, arguments); - } - - function MapWithDefault(){ - Ember.MapWithDefault.apply(this, arguments); - } - - var testMap = Ember.Map.create(); - testMap.set('key', 'value'); - - var usesOldBehavior = false; - - testMap.forEach(function(value, key){ - usesOldBehavior = value === 'key' && key === 'value'; - }); - - Map.prototype = Ember.create(Ember.Map.prototype); - MapWithDefault.prototype = Ember.create(Ember.MapWithDefault.prototype); - OrderedSet.prototype = Ember.create(Ember.OrderedSet.prototype); - - OrderedSet.create = function(){ - return new OrderedSet(); - }; - - /** - * returns a function that calls the original - * callback function in the correct order. - * if we are in pre-Ember.1.8 land, Map/MapWithDefault - * forEach calls with key, value, in that order. - * >= 1.8 forEach is called with the order value, key as per - * the ES6 spec. - */ - function translate(valueKeyOrderedCallback){ - return function(key, value){ - valueKeyOrderedCallback.call(this, value, key); - }; - } - - // old, non ES6 compliant behavior - if (usesOldBehavior){ - mapForEach = function(callback, thisArg){ - this.__super$forEach(translate(callback), thisArg); - }; - - /* alias to remove */ - deleteFn = function(thing){ - this.remove(thing); - }; - - Map.prototype.__super$forEach = Ember.Map.prototype.forEach; - Map.prototype.forEach = mapForEach; - Map.prototype["delete"] = deleteFn; - - MapWithDefault.prototype.forEach = mapForEach; - MapWithDefault.prototype.__super$forEach = Ember.MapWithDefault.prototype.forEach; - MapWithDefault.prototype["delete"] = deleteFn; - - OrderedSet.prototype["delete"] = deleteFn; - } - - MapWithDefault.constructor = MapWithDefault; - Map.constructor = Map; - - MapWithDefault.create = function(options){ - if (options) { - return new MapWithDefault(options); - } else { - return new Map(); - } - }; - - Map.create = function(){ - return new this.constructor(); - }; - - __exports__["default"] = Map; - __exports__.Map = Map; - __exports__.MapWithDefault = MapWithDefault; - __exports__.OrderedSet = OrderedSet; - }); -enifed("ember-data/system/model", - ["ember-data/system/model/model","ember-data/system/model/attributes","ember-data/system/model/states","ember-data/system/model/errors","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var Model = __dependency1__["default"]; - var attr = __dependency2__["default"]; - var RootState = __dependency3__["default"]; - var Errors = __dependency4__["default"]; - - __exports__.Model = Model; - __exports__.RootState = RootState; - __exports__.attr = attr; - __exports__.Errors = Errors; - }); -enifed("ember-data/system/model/attributes", - ["ember-data/system/model/model","ember-data/system/map","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Model = __dependency1__["default"]; - var Map = __dependency2__.Map; - - /** - @module ember-data - */ - - var get = Ember.get; - - /** - @class Model - @namespace DS - */ - Model.reopenClass({ - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are the meta object for the - property. - - Example - - ```javascript - - App.Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); - - var attributes = Ember.get(App.Person, 'attributes') - - attributes.forEach(function(name, meta) { - console.log(name, meta); - }); - - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` - - @property attributes - @static - @type {Ember.Map} - @readOnly - */ - attributes: Ember.computed(function() { - var map = Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isAttribute) { - Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.toString(), name !== 'id'); - - meta.name = name; - map.set(name, meta); - } - }); - - return map; - }).readOnly(), - - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are type of transformation - applied to each attribute. This map does not include any - attributes that do not have an transformation type. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); - - var transformedAttributes = Ember.get(App.Person, 'transformedAttributes') - - transformedAttributes.forEach(function(field, type) { - console.log(field, type); - }); - - // prints: - // lastName string - // birthday date - ``` - - @property transformedAttributes - @static - @type {Ember.Map} - @readOnly - */ - transformedAttributes: Ember.computed(function() { - var map = Map.create(); - - this.eachAttribute(function(key, meta) { - if (meta.type) { - map.set(key, meta.type); - } - }); - - return map; - }).readOnly(), - - /** - Iterates through the attributes of the model, calling the passed function on each - attribute. - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, meta); - ``` - - - `name` the name of the current property in the iteration - - `meta` the meta object for the attribute property in the iteration - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); - - App.Person.eachAttribute(function(name, meta) { - console.log(name, meta); - }); - - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` - - @method eachAttribute - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @static - */ - eachAttribute: function(callback, binding) { - get(this, 'attributes').forEach(function(meta, name) { - callback.call(binding, name, meta); - }, binding); - }, - - /** - Iterates through the transformedAttributes of the model, calling - the passed function on each attribute. Note the callback will not be - called for any attributes that do not have an transformation type. - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, type); - ``` - - - `name` the name of the current property in the iteration - - `type` a string containing the name of the type of transformed - applied to the attribute - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); - - App.Person.eachTransformedAttribute(function(name, type) { - console.log(name, type); - }); - - // prints: - // lastName string - // birthday date - ``` - - @method eachTransformedAttribute - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @static - */ - eachTransformedAttribute: function(callback, binding) { - get(this, 'transformedAttributes').forEach(function(type, name) { - callback.call(binding, name, type); - }); - } - }); - - - Model.reopen({ - eachAttribute: function(callback, binding) { - this.constructor.eachAttribute(callback, binding); - } - }); - - function getDefaultValue(record, options, key) { - if (typeof options.defaultValue === "function") { - return options.defaultValue.apply(null, arguments); - } else { - return options.defaultValue; - } - } - - function hasValue(record, key) { - return record._attributes.hasOwnProperty(key) || - record._inFlightAttributes.hasOwnProperty(key) || - record._data.hasOwnProperty(key); - } - - function getValue(record, key) { - if (record._attributes.hasOwnProperty(key)) { - return record._attributes[key]; - } else if (record._inFlightAttributes.hasOwnProperty(key)) { - return record._inFlightAttributes[key]; - } else { - return record._data[key]; - } - } - - /** - `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html). - By default, attributes are passed through as-is, however you can specify an - optional type to have the value automatically transformed. - Ember Data ships with four basic transform types: `string`, `number`, - `boolean` and `date`. You can define your own transforms by subclassing - [DS.Transform](/api/data/classes/DS.Transform.html). - - Note that you cannot use `attr` to define an attribute of `id`. - - `DS.attr` takes an optional hash as a second parameter, currently - supported options are: - - - `defaultValue`: Pass a string or a function to be called to set the attribute - to a default value if none is supplied. - - Example - - ```javascript - var attr = DS.attr; - - App.User = DS.Model.extend({ - username: attr('string'), - email: attr('string'), - verified: attr('boolean', {defaultValue: false}) - }); - ``` - - @namespace - @method attr - @for DS - @param {String} type the attribute type - @param {Object} options a hash of options - @return {Attribute} - */ - - __exports__["default"] = function attr(type, options) { - options = options || {}; - - var meta = { - type: type, - isAttribute: true, - options: options - }; - - return Ember.computed(function(key, value) { - if (arguments.length > 1) { - Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id'); - var oldValue = getValue(this, key); - - if (value !== oldValue) { - // Add the new value to the changed attributes hash; it will get deleted by - // the 'didSetProperty' handler if it is no different from the original value - this._attributes[key] = value; - - this.send('didSetProperty', { - name: key, - oldValue: oldValue, - originalValue: this._data[key], - value: value - }); - } - - return value; - } else if (hasValue(this, key)) { - return getValue(this, key); - } else { - return getDefaultValue(this, options, key); - } - - // `data` is never set directly. However, it may be - // invalidated from the state manager's setData - // event. - }).meta(meta); - }; - }); -enifed("ember-data/system/model/errors", - ["ember-data/system/map","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var get = Ember.get; - var isEmpty = Ember.isEmpty; - var map = Ember.EnumerableUtils.map; - - var MapWithDefault = __dependency1__.MapWithDefault; - - /** - @module ember-data - */ - - /** - Holds validation errors for a given record organized by attribute names. - - Every DS.Model has an `errors` property that is an instance of - `DS.Errors`. This can be used to display validation error - messages returned from the server when a `record.save()` rejects. - This works automatically with `DS.ActiveModelAdapter`, but you - can implement [ajaxError](/api/data/classes/DS.RESTAdapter.html#method_ajaxError) - in other adapters as well. - - For Example, if you had an `User` model that looked like this: - - ```javascript - App.User = DS.Model.extend({ - username: attr('string'), - email: attr('string') - }); - ``` - And you attempted to save a record that did not validate on the backend. - - ```javascript - var user = store.createRecord('user', { - username: 'tomster', - email: 'invalidEmail' - }); - user.save(); - ``` - - Your backend data store might return a response that looks like - this. This response will be used to populate the error object. - - ```javascript - { - "errors": { - "username": ["This username is already taken!"], - "email": ["Doesn't look like a valid email."] - } - } - ``` - - Errors can be displayed to the user by accessing their property name - or using the `messages` property to get an array of all errors. - - ```handlebars - {{#each errors.messages}} - <div class="error"> - {{message}} - </div> - {{/each}} - - <label>Username: {{input value=username}} </label> - {{#each errors.username}} - <div class="error"> - {{message}} - </div> - {{/each}} - - <label>Email: {{input value=email}} </label> - {{#each errors.email}} - <div class="error"> - {{message}} - </div> - {{/each}} - ``` - - @class Errors - @namespace DS - @extends Ember.Object - @uses Ember.Enumerable - @uses Ember.Evented - */ - __exports__["default"] = Ember.Object.extend(Ember.Enumerable, Ember.Evented, { - /** - Register with target handler - - @method registerHandlers - @param {Object} target - @param {Function} becameInvalid - @param {Function} becameValid - */ - registerHandlers: function(target, becameInvalid, becameValid) { - this.on('becameInvalid', target, becameInvalid); - this.on('becameValid', target, becameValid); - }, - - /** - @property errorsByAttributeName - @type {Ember.MapWithDefault} - @private - */ - errorsByAttributeName: Ember.reduceComputed("content", { - initialValue: function() { - return MapWithDefault.create({ - defaultValue: function() { - return Ember.A(); - } - }); - }, - - addedItem: function(errors, error) { - errors.get(error.attribute).pushObject(error); - - return errors; - }, - - removedItem: function(errors, error) { - errors.get(error.attribute).removeObject(error); - - return errors; - } - }), - - /** - Returns errors for a given attribute - - ```javascript - var user = store.createRecord('user', { - username: 'tomster', - email: 'invalidEmail' - }); - user.save().catch(function(){ - user.get('errors').errorsFor('email'); // ["Doesn't look like a valid email."] - }); - ``` - - @method errorsFor - @param {String} attribute - @return {Array} - */ - errorsFor: function(attribute) { - return get(this, 'errorsByAttributeName').get(attribute); - }, - - /** - An array containing all of the error messages for this - record. This is useful for displaying all errors to the user. - - ```handlebars - {{#each errors.messages}} - <div class="error"> - {{message}} - </div> - {{/each}} - ``` - - @property messages - @type {Array} - */ - messages: Ember.computed.mapBy('content', 'message'), - - /** - @property content - @type {Array} - @private - */ - content: Ember.computed(function() { - return Ember.A(); - }), - - /** - @method unknownProperty - @private - */ - unknownProperty: function(attribute) { - var errors = this.errorsFor(attribute); - if (isEmpty(errors)) { return null; } - return errors; - }, - - /** - @method nextObject - @private - */ - nextObject: function(index, previousObject, context) { - return get(this, 'content').objectAt(index); - }, - - /** - Total number of errors. - - @property length - @type {Number} - @readOnly - */ - length: Ember.computed.oneWay('content.length').readOnly(), - - /** - @property isEmpty - @type {Boolean} - @readOnly - */ - isEmpty: Ember.computed.not('length').readOnly(), - - /** - Adds error messages to a given attribute and sends - `becameInvalid` event to the record. - - Example: - - ```javascript - if (!user.get('username') { - user.get('errors').add('username', 'This field is required'); - } - ``` - - @method add - @param {String} attribute - @param {Array|String} messages - */ - add: function(attribute, messages) { - var wasEmpty = get(this, 'isEmpty'); - - messages = this._findOrCreateMessages(attribute, messages); - get(this, 'content').addObjects(messages); - - this.notifyPropertyChange(attribute); - this.enumerableContentDidChange(); - - if (wasEmpty && !get(this, 'isEmpty')) { - this.trigger('becameInvalid'); - } - }, - - /** - @method _findOrCreateMessages - @private - */ - _findOrCreateMessages: function(attribute, messages) { - var errors = this.errorsFor(attribute); - - return map(Ember.makeArray(messages), function(message) { - return errors.findBy('message', message) || { - attribute: attribute, - message: message - }; - }); - }, - - /** - Removes all error messages from the given attribute and sends - `becameValid` event to the record if there no more errors left. - - Example: - - ```javascript - App.User = DS.Model.extend({ - email: DS.attr('string'), - twoFactorAuth: DS.attr('boolean'), - phone: DS.attr('string') - }); - - App.UserEditRoute = Ember.Route.extend({ - actions: { - save: function(user) { - if (!user.get('twoFactorAuth')) { - user.get('errors').remove('phone'); - } - user.save(); - } - } - }); - ``` - - @method remove - @param {String} attribute - */ - remove: function(attribute) { - if (get(this, 'isEmpty')) { return; } - - var content = get(this, 'content').rejectBy('attribute', attribute); - get(this, 'content').setObjects(content); - - this.notifyPropertyChange(attribute); - this.enumerableContentDidChange(); - - if (get(this, 'isEmpty')) { - this.trigger('becameValid'); - } - }, - - /** - Removes all error messages and sends `becameValid` event - to the record. - - Example: - - ```javascript - App.UserEditRoute = Ember.Route.extend({ - actions: { - retrySave: function(user) { - user.get('errors').clear(); - user.save(); - } - } - }); - ``` - - @method clear - */ - clear: function() { - if (get(this, 'isEmpty')) { return; } - - get(this, 'content').clear(); - this.enumerableContentDidChange(); - - this.trigger('becameValid'); - }, - - /** - Checks if there is error messages for the given attribute. - - ```javascript - App.UserEditRoute = Ember.Route.extend({ - actions: { - save: function(user) { - if (user.get('errors').has('email')) { - return alert('Please update your email before attempting to save.'); - } - user.save(); - } - } - }); - ``` - - @method has - @param {String} attribute - @return {Boolean} true if there some errors on given attribute - */ - has: function(attribute) { - return !isEmpty(this.errorsFor(attribute)); - } - }); - }); -enifed("ember-data/system/model/model", - ["ember-data/system/model/states","ember-data/system/model/errors","ember-data/system/promise_proxies","ember-data/system/relationships/relationship","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var RootState = __dependency1__["default"]; - var Errors = __dependency2__["default"]; - var PromiseObject = __dependency3__.PromiseObject; - var createRelationshipFor = __dependency4__.createRelationshipFor; - - /** - @module ember-data - */ - - var get = Ember.get; - var set = Ember.set; - var merge = Ember.merge; - var Promise = Ember.RSVP.Promise; - var forEach = Ember.ArrayPolyfills.forEach; - var map = Ember.ArrayPolyfills.map; - - var JSONSerializer; - var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) { - return get(get(this, 'currentState'), key); - }).readOnly(); - - var _extractPivotNameCache = Ember.create(null); - var _splitOnDotCache = Ember.create(null); - - function splitOnDot(name) { - return _splitOnDotCache[name] || ( - _splitOnDotCache[name] = name.split('.') - ); - } - - function extractPivotName(name) { - return _extractPivotNameCache[name] || ( - _extractPivotNameCache[name] = splitOnDot(name)[0] - ); - } - - /** - - The model class that all Ember Data records descend from. - - @class Model - @namespace DS - @extends Ember.Object - @uses Ember.Evented - */ - var Model = Ember.Object.extend(Ember.Evented, { - _recordArrays: undefined, - _relationships: undefined, - _loadingRecordArrays: undefined, - /** - If this property is `true` the record is in the `empty` - state. Empty is the first state all records enter after they have - been created. Most records created by the store will quickly - transition to the `loading` state if data needs to be fetched from - the server or the `created` state if the record is created on the - client. A record can also enter the empty state if the adapter is - unable to locate the record. - - @property isEmpty - @type {Boolean} - @readOnly - */ - isEmpty: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `loading` state. A - record enters this state when the store asks the adapter for its - data. It remains in this state until the adapter provides the - requested data. - - @property isLoading - @type {Boolean} - @readOnly - */ - isLoading: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `loaded` state. A - record enters this state when its data is populated. Most of a - record's lifecycle is spent inside substates of the `loaded` - state. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isLoaded'); // true - - store.find('model', 1).then(function(model) { - model.get('isLoaded'); // true - }); - ``` - - @property isLoaded - @type {Boolean} - @readOnly - */ - isLoaded: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `dirty` state. The - record has local changes that have not yet been saved by the - adapter. This includes records that have been created (but not yet - saved) or deleted. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isDirty'); // true - - store.find('model', 1).then(function(model) { - model.get('isDirty'); // false - model.set('foo', 'some value'); - model.get('isDirty'); // true - }); - ``` - - @property isDirty - @type {Boolean} - @readOnly - */ - isDirty: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `saving` state. A - record enters the saving state when `save` is called, but the - adapter has not yet acknowledged that the changes have been - persisted to the backend. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isSaving'); // false - var promise = record.save(); - record.get('isSaving'); // true - promise.then(function() { - record.get('isSaving'); // false - }); - ``` - - @property isSaving - @type {Boolean} - @readOnly - */ - isSaving: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `deleted` state - and has been marked for deletion. When `isDeleted` is true and - `isDirty` is true, the record is deleted locally but the deletion - was not yet persisted. When `isSaving` is true, the change is - in-flight. When both `isDirty` and `isSaving` are false, the - change has persisted. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isDeleted'); // false - record.deleteRecord(); - - // Locally deleted - record.get('isDeleted'); // true - record.get('isDirty'); // true - record.get('isSaving'); // false - - // Persisting the deletion - var promise = record.save(); - record.get('isDeleted'); // true - record.get('isSaving'); // true - - // Deletion Persisted - promise.then(function() { - record.get('isDeleted'); // true - record.get('isSaving'); // false - record.get('isDirty'); // false - }); - ``` - - @property isDeleted - @type {Boolean} - @readOnly - */ - isDeleted: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `new` state. A - record will be in the `new` state when it has been created on the - client and the adapter has not yet report that it was successfully - saved. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isNew'); // true - - record.save().then(function(model) { - model.get('isNew'); // false - }); - ``` - - @property isNew - @type {Boolean} - @readOnly - */ - isNew: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `valid` state. - - A record will be in the `valid` state when the adapter did not report any - server-side validation failures. - - @property isValid - @type {Boolean} - @readOnly - */ - isValid: retrieveFromCurrentState, - /** - If the record is in the dirty state this property will report what - kind of change has caused it to move into the dirty - state. Possible values are: - - - `created` The record has been created by the client and not yet saved to the adapter. - - `updated` The record has been updated by the client and not yet saved to the adapter. - - `deleted` The record has been deleted by the client and not yet saved to the adapter. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('dirtyType'); // 'created' - ``` - - @property dirtyType - @type {String} - @readOnly - */ - dirtyType: retrieveFromCurrentState, - - /** - If `true` the adapter reported that it was unable to save local - changes to the backend for any reason other than a server-side - validation error. - - Example - - ```javascript - record.get('isError'); // false - record.set('foo', 'valid value'); - record.save().then(null, function() { - record.get('isError'); // true - }); - ``` - - @property isError - @type {Boolean} - @readOnly - */ - isError: false, - /** - If `true` the store is attempting to reload the record form the adapter. - - Example - - ```javascript - record.get('isReloading'); // false - record.reload(); - record.get('isReloading'); // true - ``` - - @property isReloading - @type {Boolean} - @readOnly - */ - isReloading: false, - - /** - The `clientId` property is a transient numerical identifier - generated at runtime by the data store. It is important - primarily because newly created objects may not yet have an - externally generated id. - - @property clientId - @private - @type {Number|String} - */ - clientId: null, - /** - All ember models have an id property. This is an identifier - managed by an external source. These are always coerced to be - strings before being used internally. Note when declaring the - attributes for a model it is an error to declare an id - attribute. - - ```javascript - var record = store.createRecord('model'); - record.get('id'); // null - - store.find('model', 1).then(function(model) { - model.get('id'); // '1' - }); - ``` - - @property id - @type {String} - */ - id: null, - - /** - @property currentState - @private - @type {Object} - */ - currentState: RootState.empty, - - /** - When the record is in the `invalid` state this object will contain - any errors returned by the adapter. When present the errors hash - typically contains keys corresponding to the invalid property names - and values which are an array of error messages. - - ```javascript - record.get('errors.length'); // 0 - record.set('foo', 'invalid value'); - record.save().then(null, function() { - record.get('errors').get('foo'); // ['foo should be a number.'] - }); - ``` - - @property errors - @type {DS.Errors} - */ - errors: Ember.computed(function() { - var errors = Errors.create(); - - errors.registerHandlers(this, function() { - this.send('becameInvalid'); - }, function() { - this.send('becameValid'); - }); - - return errors; - }).readOnly(), - - /** - Create a JSON representation of the record, using the serialization - strategy of the store's adapter. - - `serialize` takes an optional hash as a parameter, currently - supported options are: - - - `includeId`: `true` if the record's ID should be included in the - JSON representation. - - @method serialize - @param {Object} options - @return {Object} an object whose values are primitive JSON values only - */ - serialize: function(options) { - var store = get(this, 'store'); - return store.serialize(this, options); - }, - - /** - Use [DS.JSONSerializer](DS.JSONSerializer.html) to - get the JSON representation of a record. - - `toJSON` takes an optional hash as a parameter, currently - supported options are: - - - `includeId`: `true` if the record's ID should be included in the - JSON representation. - - @method toJSON - @param {Object} options - @return {Object} A JSON representation of the object. - */ - toJSON: function(options) { - if (!JSONSerializer) { JSONSerializer = requireModule("ember-data/serializers/json_serializer")["default"]; } - // container is for lazy transform lookups - var serializer = JSONSerializer.create({ container: this.container }); - return serializer.serialize(this, options); - }, - - /** - Fired when the record is loaded from the server. - - @event didLoad - */ - didLoad: Ember.K, - - /** - Fired when the record is updated. - - @event didUpdate - */ - didUpdate: Ember.K, - - /** - Fired when the record is created. - - @event didCreate - */ - didCreate: Ember.K, - - /** - Fired when the record is deleted. - - @event didDelete - */ - didDelete: Ember.K, - - /** - Fired when the record becomes invalid. - - @event becameInvalid - */ - becameInvalid: Ember.K, - - /** - Fired when the record enters the error state. - - @event becameError - */ - becameError: Ember.K, - - /** - @property data - @private - @type {Object} - */ - data: Ember.computed(function() { - this._data = this._data || {}; - return this._data; - }).readOnly(), - - _data: null, - - init: function() { - this._super(); - this._setup(); - }, - - _setup: function() { - this._changesToSync = {}; - this._deferredTriggers = []; - this._data = {}; - this._attributes = {}; - this._inFlightAttributes = {}; - this._relationships = {}; - /* - implicit relationships are relationship which have not been declared but the inverse side exists on - another record somewhere - For example if there was - ``` - App.Comment = DS.Model.extend({ - name: DS.attr() - }) - ``` - but there is also - ``` - App.Post = DS.Model.extend({ - name: DS.attr(), - comments: DS.hasMany('comment') - }) - ``` - - would have a implicit post relationship in order to be do things like remove ourselves from the post - when we are deleted - */ - this._implicitRelationships = Ember.create(null); - var model = this; - //TODO Move into a getter for better perf - this.constructor.eachRelationship(function(key, descriptor) { - model._relationships[key] = createRelationshipFor(model, descriptor, model.store); - }); - - }, - - /** - @method send - @private - @param {String} name - @param {Object} context - */ - send: function(name, context) { - var currentState = get(this, 'currentState'); - - if (!currentState[name]) { - this._unhandledEvent(currentState, name, context); - } - - return currentState[name](this, context); - }, - - /** - @method transitionTo - @private - @param {String} name - */ - transitionTo: function(name) { - // POSSIBLE TODO: Remove this code and replace with - // always having direct references to state objects - - var pivotName = extractPivotName(name); - var currentState = get(this, 'currentState'); - var state = currentState; - - do { - if (state.exit) { state.exit(this); } - state = state.parentState; - } while (!state.hasOwnProperty(pivotName)); - - var path = splitOnDot(name); - var setups = [], enters = [], i, l; - - for (i=0, l=path.length; i<l; i++) { - state = state[path[i]]; - - if (state.enter) { enters.push(state); } - if (state.setup) { setups.push(state); } - } - - for (i=0, l=enters.length; i<l; i++) { - enters[i].enter(this); - } - - set(this, 'currentState', state); - - for (i=0, l=setups.length; i<l; i++) { - setups[i].setup(this); - } - - this.updateRecordArraysLater(); - }, - - _unhandledEvent: function(state, name, context) { - var errorMessage = "Attempted to handle event `" + name + "` "; - errorMessage += "on " + String(this) + " while in state "; - errorMessage += state.stateName + ". "; - - if (context !== undefined) { - errorMessage += "Called with " + Ember.inspect(context) + "."; - } - - throw new Ember.Error(errorMessage); - }, - - withTransaction: function(fn) { - var transaction = get(this, 'transaction'); - if (transaction) { fn(transaction); } - }, - - /** - @method loadingData - @private - @param {Promise} promise - */ - loadingData: function(promise) { - this.send('loadingData', promise); - }, - - /** - @method loadedData - @private - */ - loadedData: function() { - this.send('loadedData'); - }, - - /** - @method notFound - @private - */ - notFound: function() { - this.send('notFound'); - }, - - /** - @method pushedData - @private - */ - pushedData: function() { - this.send('pushedData'); - }, - - /** - Marks the record as deleted but does not save it. You must call - `save` afterwards if you want to persist it. You might use this - method if you want to allow the user to still `rollback()` a - delete after it was made. - - Example - - ```javascript - App.ModelDeleteRoute = Ember.Route.extend({ - actions: { - softDelete: function() { - this.controller.get('model').deleteRecord(); - }, - confirm: function() { - this.controller.get('model').save(); - }, - undo: function() { - this.controller.get('model').rollback(); - } - } - }); - ``` - - @method deleteRecord - */ - deleteRecord: function() { - this.send('deleteRecord'); - }, - - /** - Same as `deleteRecord`, but saves the record immediately. - - Example - - ```javascript - App.ModelDeleteRoute = Ember.Route.extend({ - actions: { - delete: function() { - var controller = this.controller; - controller.get('model').destroyRecord().then(function() { - controller.transitionToRoute('model.index'); - }); - } - } - }); - ``` - - @method destroyRecord - @return {Promise} a promise that will be resolved when the adapter returns - successfully or rejected if the adapter returns with an error. - */ - destroyRecord: function() { - this.deleteRecord(); - return this.save(); - }, - - /** - @method unloadRecord - @private - */ - unloadRecord: function() { - if (this.isDestroyed) { return; } - - this.send('unloadRecord'); - }, - - /** - @method clearRelationships - @private - */ - clearRelationships: function() { - this.eachRelationship(function(name, relationship) { - var rel = this._relationships[name]; - if (rel){ - //TODO(Igor) figure out whether we want to clear or disconnect - rel.clear(); - rel.destroy(); - } - }, this); - }, - - disconnectRelationships: function() { - this.eachRelationship(function(name, relationship) { - this._relationships[name].disconnect(); - }, this); - var model = this; - forEach.call(Ember.keys(this._implicitRelationships), function(key) { - model._implicitRelationships[key].disconnect(); - }); - }, - - reconnectRelationships: function() { - this.eachRelationship(function(name, relationship) { - this._relationships[name].reconnect(); - }, this); - var model = this; - forEach.call(Ember.keys(this._implicitRelationships), function(key) { - model._implicitRelationships[key].reconnect(); - }); - }, - - - /** - @method updateRecordArrays - @private - */ - updateRecordArrays: function() { - this._updatingRecordArraysLater = false; - get(this, 'store').dataWasUpdated(this.constructor, this); - }, - - /** - When a find request is triggered on the store, the user can optionally pass in - attributes and relationships to be preloaded. These are meant to behave as if they - came back from the server, except the user obtained them out of band and is informing - the store of their existence. The most common use case is for supporting client side - nested URLs, such as `/posts/1/comments/2` so the user can do - `store.find('comment', 2, {post:1})` without having to fetch the post. - - Preloaded data can be attributes and relationships passed in either as IDs or as actual - models. - - @method _preloadData - @private - @param {Object} preload - */ - _preloadData: function(preload) { - var record = this; - //TODO(Igor) consider the polymorphic case - forEach.call(Ember.keys(preload), function(key) { - var preloadValue = get(preload, key); - var relationshipMeta = record.constructor.metaForProperty(key); - if (relationshipMeta.isRelationship) { - record._preloadRelationship(key, preloadValue); - } else { - get(record, '_data')[key] = preloadValue; - } - }); - }, - - _preloadRelationship: function(key, preloadValue) { - var relationshipMeta = this.constructor.metaForProperty(key); - var type = relationshipMeta.type; - if (relationshipMeta.kind === 'hasMany'){ - this._preloadHasMany(key, preloadValue, type); - } else { - this._preloadBelongsTo(key, preloadValue, type); - } - }, - - _preloadHasMany: function(key, preloadValue, type) { - Ember.assert("You need to pass in an array to set a hasMany property on a record", Ember.isArray(preloadValue)); - var record = this; - - var recordsToSet = map.call(preloadValue, function(recordToPush) { - return record._convertStringOrNumberIntoRecord(recordToPush, type); - }); - //We use the pathway of setting the hasMany as if it came from the adapter - //because the user told us that they know this relationships exists already - this._relationships[key].updateRecordsFromAdapter(recordsToSet); - }, - - _preloadBelongsTo: function(key, preloadValue, type){ - var recordToSet = this._convertStringOrNumberIntoRecord(preloadValue, type); - - //We use the pathway of setting the hasMany as if it came from the adapter - //because the user told us that they know this relationships exists already - this._relationships[key].setRecord(recordToSet); - }, - - _convertStringOrNumberIntoRecord: function(value, type) { - if (Ember.typeOf(value) === 'string' || Ember.typeOf(value) === 'number'){ - return this.store.recordForId(type, value); - } - return value; - }, - - /** - @method _notifyProperties - @private - */ - _notifyProperties: function(keys) { - Ember.beginPropertyChanges(); - var key; - for (var i = 0, length = keys.length; i < length; i++){ - key = keys[i]; - this.notifyPropertyChange(key); - } - Ember.endPropertyChanges(); - }, - - /** - Returns an object, whose keys are changed properties, and value is - an [oldProp, newProp] array. - - Example - - ```javascript - App.Mascot = DS.Model.extend({ - name: attr('string') - }); - - var person = store.createRecord('person'); - person.changedAttributes(); // {} - person.set('name', 'Tomster'); - person.changedAttributes(); // {name: [undefined, 'Tomster']} - ``` - - @method changedAttributes - @return {Object} an object, whose keys are changed properties, - and value is an [oldProp, newProp] array. - */ - changedAttributes: function() { - var oldData = get(this, '_data'); - var newData = get(this, '_attributes'); - var diffData = {}; - var prop; - - for (prop in newData) { - diffData[prop] = [oldData[prop], newData[prop]]; - } - - return diffData; - }, - - /** - @method adapterWillCommit - @private - */ - adapterWillCommit: function() { - this.send('willCommit'); - }, - - /** - If the adapter did not return a hash in response to a commit, - merge the changed attributes and relationships into the existing - saved data. - - @method adapterDidCommit - */ - adapterDidCommit: function(data) { - set(this, 'isError', false); - - if (data) { - this._data = data; - } else { - Ember.mixin(this._data, this._inFlightAttributes); - } - - this._inFlightAttributes = {}; - - this.send('didCommit'); - this.updateRecordArraysLater(); - - if (!data) { return; } - - this._notifyProperties(Ember.keys(data)); - }, - - /** - @method adapterDidDirty - @private - */ - adapterDidDirty: function() { - this.send('becomeDirty'); - this.updateRecordArraysLater(); - }, - - - /** - @method updateRecordArraysLater - @private - */ - updateRecordArraysLater: function() { - // quick hack (something like this could be pushed into run.once - if (this._updatingRecordArraysLater) { return; } - this._updatingRecordArraysLater = true; - - Ember.run.schedule('actions', this, this.updateRecordArrays); - }, - - /** - @method setupData - @private - @param {Object} data - @param {Boolean} partial the data should be merged into - the existing data, not replace it. - */ - setupData: function(data, partial) { - Ember.assert("Expected an object as `data` in `setupData`", Ember.typeOf(data) === 'object'); - - if (partial) { - Ember.merge(this._data, data); - } else { - this._data = data; - } - - this.pushedData(); - - this._notifyProperties(Ember.keys(data)); - }, - - materializeId: function(id) { - set(this, 'id', id); - }, - - materializeAttributes: function(attributes) { - Ember.assert("Must pass a hash of attributes to materializeAttributes", !!attributes); - merge(this._data, attributes); - }, - - materializeAttribute: function(name, value) { - this._data[name] = value; - }, - - /** - If the model `isDirty` this function will discard any unsaved - changes - - Example - - ```javascript - record.get('name'); // 'Untitled Document' - record.set('name', 'Doc 1'); - record.get('name'); // 'Doc 1' - record.rollback(); - record.get('name'); // 'Untitled Document' - ``` - - @method rollback - */ - rollback: function() { - this._attributes = {}; - - if (get(this, 'isError')) { - this._inFlightAttributes = {}; - set(this, 'isError', false); - } - - //Eventually rollback will always work for relationships - //For now we support it only out of deleted state, because we - //have an explicit way of knowing when the server acked the relationship change - if (get(this, 'isDeleted')) { - this.reconnectRelationships(); - } - - if (get(this, 'isNew')) { - this.clearRelationships(); - } - - if (!get(this, 'isValid')) { - this._inFlightAttributes = {}; - } - - this.send('rolledBack'); - - this._notifyProperties(Ember.keys(this._data)); - - }, - - toStringExtension: function() { - return get(this, 'id'); - }, - - /** - Save the record and persist any changes to the record to an - external source via the adapter. - - Example - - ```javascript - record.set('name', 'Tomster'); - record.save().then(function(){ - // Success callback - }, function() { - // Error callback - }); - ``` - @method save - @return {Promise} a promise that will be resolved when the adapter returns - successfully or rejected if the adapter returns with an error. - */ - save: function() { - var promiseLabel = "DS: Model#save " + this; - var resolver = Ember.RSVP.defer(promiseLabel); - - this.get('store').scheduleSave(this, resolver); - this._inFlightAttributes = this._attributes; - this._attributes = {}; - - return PromiseObject.create({ - promise: resolver.promise - }); - }, - - /** - Reload the record from the adapter. - - This will only work if the record has already finished loading - and has not yet been modified (`isLoaded` but not `isDirty`, - or `isSaving`). - - Example - - ```javascript - App.ModelViewRoute = Ember.Route.extend({ - actions: { - reload: function() { - this.controller.get('model').reload().then(function(model) { - // do something with the reloaded model - }); - } - } - }); - ``` - - @method reload - @return {Promise} a promise that will be resolved with the record when the - adapter returns successfully or rejected if the adapter returns - with an error. - */ - reload: function() { - set(this, 'isReloading', true); - - var record = this; - var promiseLabel = "DS: Model#reload of " + this; - var promise = new Promise(function(resolve){ - record.send('reloadRecord', resolve); - }, promiseLabel).then(function() { - record.set('isReloading', false); - record.set('isError', false); - return record; - }, function(reason) { - record.set('isError', true); - throw reason; - }, "DS: Model#reload complete, update flags")['finally'](function () { - record.updateRecordArrays(); - }); - - return PromiseObject.create({ - promise: promise - }); - }, - - // FOR USE DURING COMMIT PROCESS - - adapterDidUpdateAttribute: function(attributeName, value) { - - // If a value is passed in, update the internal attributes and clear - // the attribute cache so it picks up the new value. Otherwise, - // collapse the current value into the internal attributes because - // the adapter has acknowledged it. - if (value !== undefined) { - this._data[attributeName] = value; - this.notifyPropertyChange(attributeName); - } else { - this._data[attributeName] = this._inFlightAttributes[attributeName]; - } - - this.updateRecordArraysLater(); - }, - - /** - @method adapterDidInvalidate - @private - */ - adapterDidInvalidate: function(errors) { - var recordErrors = get(this, 'errors'); - function addError(name) { - if (errors[name]) { - recordErrors.add(name, errors[name]); - } - } - - this.eachAttribute(addError); - this.eachRelationship(addError); - }, - - /** - @method adapterDidError - @private - */ - adapterDidError: function() { - this.send('becameError'); - set(this, 'isError', true); - }, - - /** - Override the default event firing from Ember.Evented to - also call methods with the given name. - - @method trigger - @private - @param {String} name - */ - trigger: function() { - var length = arguments.length; - var args = new Array(length - 1); - var name = arguments[0]; - - for (var i = 1; i < length; i++ ){ - args[i - 1] = arguments[i]; - } - - Ember.tryInvoke(this, name, args); - this._super.apply(this, arguments); - }, - - triggerLater: function() { - var length = arguments.length; - var args = new Array(length); - - for (var i = 0; i < length; i++ ){ - args[i] = arguments[i]; - } - - if (this._deferredTriggers.push(args) !== 1) { - return; - } - Ember.run.schedule('actions', this, '_triggerDeferredTriggers'); - }, - - _triggerDeferredTriggers: function() { - for (var i=0, l= this._deferredTriggers.length; i<l; i++) { - this.trigger.apply(this, this._deferredTriggers[i]); - } - - this._deferredTriggers.length = 0; - }, - - willDestroy: function() { - this._super(); - this.clearRelationships(); - }, - - // This is a temporary solution until we refactor DS.Model to not - // rely on the data property. - willMergeMixin: function(props) { - Ember.assert('`data` is a reserved property name on DS.Model objects. Please choose a different property name for ' + this.constructor.toString(), !props.data); - } - }); - - Model.reopenClass({ - /** - Alias DS.Model's `create` method to `_create`. This allows us to create DS.Model - instances from within the store, but if end users accidentally call `create()` - (instead of `createRecord()`), we can raise an error. - - @method _create - @private - @static - */ - _create: Model.create, - - /** - Override the class' `create()` method to raise an error. This - prevents end users from inadvertently calling `create()` instead - of `createRecord()`. The store is still able to create instances - by calling the `_create()` method. To create an instance of a - `DS.Model` use [store.createRecord](DS.Store.html#method_createRecord). - - @method create - @private - @static - */ - 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."); - } - }); - - __exports__["default"] = Model; - }); -enifed("ember-data/system/model/states", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ - - var get = Ember.get; - var set = Ember.set; - /* - This file encapsulates the various states that a record can transition - through during its lifecycle. - */ - /** - ### State - - Each record has a `currentState` property that explicitly tracks what - state a record is in at any given time. For instance, if a record is - newly created and has not yet been sent to the adapter to be saved, - it would be in the `root.loaded.created.uncommitted` state. If a - record has had local modifications made to it that are in the - process of being saved, the record would be in the - `root.loaded.updated.inFlight` state. (This state paths will be - explained in more detail below.) - - Events are sent by the record or its store to the record's - `currentState` property. How the state reacts to these events is - dependent on which state it is in. In some states, certain events - will be invalid and will cause an exception to be raised. - - States are hierarchical and every state is a substate of the - `RootState`. For example, a record can be in the - `root.deleted.uncommitted` state, then transition into the - `root.deleted.inFlight` state. If a child state does not implement - an event handler, the state manager will attempt to invoke the event - on all parent states until the root state is reached. The state - hierarchy of a record is described in terms of a path string. You - can determine a record's current state by getting the state's - `stateName` property: - - ```javascript - record.get('currentState.stateName'); - //=> "root.created.uncommitted" - ``` - - The hierarchy of valid states that ship with ember data looks like - this: - - ```text - * root - * deleted - * saved - * uncommitted - * inFlight - * empty - * loaded - * created - * uncommitted - * inFlight - * saved - * updated - * uncommitted - * inFlight - * loading - ``` - - The `DS.Model` states are themselves stateless. What that means is - that, the hierarchical states that each of *those* points to is a - shared data structure. For performance reasons, instead of each - record getting its own copy of the hierarchy of states, each record - points to this global, immutable shared instance. How does a state - know which record it should be acting on? We pass the record - instance into the state's event handlers as the first argument. - - The record passed as the first parameter is where you should stash - state about the record if needed; you should never store data on the state - object itself. - - ### Events and Flags - - A state may implement zero or more events and flags. - - #### Events - - Events are named functions that are invoked when sent to a record. The - record will first look for a method with the given name on the - current state. If no method is found, it will search the current - state's parent, and then its grandparent, and so on until reaching - the top of the hierarchy. If the root is reached without an event - handler being found, an exception will be raised. This can be very - helpful when debugging new features. - - Here's an example implementation of a state with a `myEvent` event handler: - - ```javascript - aState: DS.State.create({ - myEvent: function(manager, param) { - console.log("Received myEvent with", param); - } - }) - ``` - - To trigger this event: - - ```javascript - record.send('myEvent', 'foo'); - //=> "Received myEvent with foo" - ``` - - Note that an optional parameter can be sent to a record's `send()` method, - which will be passed as the second parameter to the event handler. - - Events should transition to a different state if appropriate. This can be - done by calling the record's `transitionTo()` method with a path to the - desired state. The state manager will attempt to resolve the state path - relative to the current state. If no state is found at that path, it will - attempt to resolve it relative to the current state's parent, and then its - parent, and so on until the root is reached. For example, imagine a hierarchy - like this: - - * created - * uncommitted <-- currentState - * inFlight - * updated - * inFlight - - If we are currently in the `uncommitted` state, calling - `transitionTo('inFlight')` would transition to the `created.inFlight` state, - while calling `transitionTo('updated.inFlight')` would transition to - the `updated.inFlight` state. - - Remember that *only events* should ever cause a state transition. You should - never call `transitionTo()` from outside a state's event handler. If you are - tempted to do so, create a new event and send that to the state manager. - - #### Flags - - Flags are Boolean values that can be used to introspect a record's current - state in a more user-friendly way than examining its state path. For example, - instead of doing this: - - ```javascript - var statePath = record.get('stateManager.currentPath'); - if (statePath === 'created.inFlight') { - doSomething(); - } - ``` - - You can say: - - ```javascript - if (record.get('isNew') && record.get('isSaving')) { - doSomething(); - } - ``` - - If your state does not set a value for a given flag, the value will - be inherited from its parent (or the first place in the state hierarchy - where it is defined). - - The current set of flags are defined below. If you want to add a new flag, - in addition to the area below, you will also need to declare it in the - `DS.Model` class. - - - * [isEmpty](DS.Model.html#property_isEmpty) - * [isLoading](DS.Model.html#property_isLoading) - * [isLoaded](DS.Model.html#property_isLoaded) - * [isDirty](DS.Model.html#property_isDirty) - * [isSaving](DS.Model.html#property_isSaving) - * [isDeleted](DS.Model.html#property_isDeleted) - * [isNew](DS.Model.html#property_isNew) - * [isValid](DS.Model.html#property_isValid) - - @namespace DS - @class RootState - */ - - function didSetProperty(record, context) { - if (context.value === context.originalValue) { - delete record._attributes[context.name]; - record.send('propertyWasReset', context.name); - } else if (context.value !== context.oldValue) { - record.send('becomeDirty'); - } - - record.updateRecordArraysLater(); - } - - // Implementation notes: - // - // Each state has a boolean value for all of the following flags: - // - // * isLoaded: The record has a populated `data` property. When a - // record is loaded via `store.find`, `isLoaded` is false - // until the adapter sets it. When a record is created locally, - // its `isLoaded` property is always true. - // * isDirty: The record has local changes that have not yet been - // saved by the adapter. This includes records that have been - // created (but not yet saved) or deleted. - // * isSaving: The record has been committed, but - // the adapter has not yet acknowledged that the changes have - // been persisted to the backend. - // * isDeleted: The record was marked for deletion. When `isDeleted` - // is true and `isDirty` is true, the record is deleted locally - // but the deletion was not yet persisted. When `isSaving` is - // true, the change is in-flight. When both `isDirty` and - // `isSaving` are false, the change has persisted. - // * isError: The adapter reported that it was unable to save - // local changes to the backend. This may also result in the - // record having its `isValid` property become false if the - // adapter reported that server-side validations failed. - // * isNew: The record was created on the client and the adapter - // did not yet report that it was successfully saved. - // * isValid: The adapter did not report any server-side validation - // failures. - - // The dirty state is a abstract state whose functionality is - // shared between the `created` and `updated` states. - // - // The deleted state shares the `isDirty` flag with the - // subclasses of `DirtyState`, but with a very different - // implementation. - // - // Dirty states have three child states: - // - // `uncommitted`: the store has not yet handed off the record - // to be saved. - // `inFlight`: the store has handed off the record to be saved, - // but the adapter has not yet acknowledged success. - // `invalid`: the record has invalid information and cannot be - // send to the adapter yet. - var DirtyState = { - initialState: 'uncommitted', - - // FLAGS - isDirty: true, - - // SUBSTATES - - // When a record first becomes dirty, it is `uncommitted`. - // This means that there are local pending changes, but they - // have not yet begun to be saved, and are not invalid. - uncommitted: { - // EVENTS - didSetProperty: didSetProperty, - - //TODO(Igor) reloading now triggers a - //loadingData event, though it seems fine? - loadingData: Ember.K, - - propertyWasReset: function(record, name) { - var length = Ember.keys(record._attributes); - var stillDirty = length > 0; - - if (!stillDirty) { record.send('rolledBack'); } - }, - - pushedData: Ember.K, - - becomeDirty: Ember.K, - - willCommit: function(record) { - record.transitionTo('inFlight'); - }, - - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); - }, - - rolledBack: function(record) { - record.transitionTo('loaded.saved'); - }, - - becameInvalid: function(record) { - record.transitionTo('invalid'); - }, - - rollback: function(record) { - record.rollback(); - } - }, - - // Once a record has been handed off to the adapter to be - // saved, it is in the 'in flight' state. Changes to the - // record cannot be made during this window. - inFlight: { - // FLAGS - isSaving: true, - - // EVENTS - didSetProperty: didSetProperty, - becomeDirty: Ember.K, - pushedData: Ember.K, - - unloadRecord: function(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + " `", false); - }, - - // TODO: More robust semantics around save-while-in-flight - willCommit: Ember.K, - - didCommit: function(record) { - var dirtyType = get(this, 'dirtyType'); - - record.transitionTo('saved'); - record.send('invokeLifecycleCallbacks', dirtyType); - }, - - becameInvalid: function(record) { - record.transitionTo('invalid'); - record.send('invokeLifecycleCallbacks'); - }, - - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); - } - }, - - // A record is in the `invalid` if the adapter has indicated - // the the record failed server-side invalidations. - invalid: { - // FLAGS - isValid: false, - - // EVENTS - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.disconnectRelationships(); - }, - - didSetProperty: function(record, context) { - get(record, 'errors').remove(context.name); - - didSetProperty(record, context); - }, - - becomeDirty: Ember.K, - - willCommit: function(record) { - get(record, 'errors').clear(); - record.transitionTo('inFlight'); - }, - - rolledBack: function(record) { - get(record, 'errors').clear(); - }, - - becameValid: function(record) { - record.transitionTo('uncommitted'); - }, - - invokeLifecycleCallbacks: function(record) { - record.triggerLater('becameInvalid', record); - }, - - exit: function(record) { - record._inFlightAttributes = {}; - } - } - }; - - // The created and updated states are created outside the state - // chart so we can reopen their substates and add mixins as - // necessary. - - function deepClone(object) { - var clone = {}, value; - - for (var prop in object) { - value = object[prop]; - if (value && typeof value === 'object') { - clone[prop] = deepClone(value); - } else { - clone[prop] = value; - } - } - - return clone; - } - - function mixin(original, hash) { - for (var prop in hash) { - original[prop] = hash[prop]; - } - - return original; - } - - function dirtyState(options) { - var newState = deepClone(DirtyState); - return mixin(newState, options); - } - - var createdState = dirtyState({ - dirtyType: 'created', - // FLAGS - isNew: true - }); - - createdState.uncommitted.rolledBack = function(record) { - record.transitionTo('deleted.saved'); - }; - - var updatedState = dirtyState({ - dirtyType: 'updated' - }); - - createdState.uncommitted.deleteRecord = function(record) { - record.disconnectRelationships(); - record.transitionTo('deleted.saved'); - }; - - createdState.uncommitted.rollback = function(record) { - DirtyState.uncommitted.rollback.apply(this, arguments); - record.transitionTo('deleted.saved'); - }; - - createdState.uncommitted.propertyWasReset = Ember.K; - - function assertAgainstUnloadRecord(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", false); - } - - updatedState.inFlight.unloadRecord = assertAgainstUnloadRecord; - - updatedState.uncommitted.deleteRecord = function(record) { - record.transitionTo('deleted.uncommitted'); - record.disconnectRelationships(); - }; - - var RootState = { - // FLAGS - isEmpty: false, - isLoading: false, - isLoaded: false, - isDirty: false, - isSaving: false, - isDeleted: false, - isNew: false, - isValid: true, - - // DEFAULT EVENTS - - // Trying to roll back if you're not in the dirty state - // doesn't change your state. For example, if you're in the - // in-flight state, rolling back the record doesn't move - // you out of the in-flight state. - rolledBack: Ember.K, - unloadRecord: function(record) { - // clear relationships before moving to deleted state - // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); - }, - - - propertyWasReset: Ember.K, - - // SUBSTATES - - // A record begins its lifecycle in the `empty` state. - // If its data will come from the adapter, it will - // transition into the `loading` state. Otherwise, if - // the record is being created on the client, it will - // transition into the `created` state. - empty: { - isEmpty: true, - - // EVENTS - loadingData: function(record, promise) { - record._loadingPromise = promise; - record.transitionTo('loading'); - }, - - loadedData: function(record) { - record.transitionTo('loaded.created.uncommitted'); - record.notifyPropertyChange('data'); - }, - - pushedData: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('didLoad'); - } - }, - - // A record enters this state when the store asks - // the adapter for its data. It remains in this state - // until the adapter provides the requested data. - // - // Usually, this process is asynchronous, using an - // XHR to retrieve the data. - loading: { - // FLAGS - isLoading: true, - - exit: function(record) { - record._loadingPromise = null; - }, - - // EVENTS - pushedData: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('didLoad'); - set(record, 'isError', false); - }, - - becameError: function(record) { - record.triggerLater('becameError', record); - }, - - notFound: function(record) { - record.transitionTo('empty'); - } - }, - - // A record enters this state when its data is populated. - // Most of a record's lifecycle is spent inside substates - // of the `loaded` state. - loaded: { - initialState: 'saved', - - // FLAGS - isLoaded: true, - - //TODO(Igor) Reloading now triggers a loadingData event, - //but it should be ok? - loadingData: Ember.K, - - // SUBSTATES - - // If there are no local changes to a record, it remains - // in the `saved` state. - saved: { - setup: function(record) { - var attrs = record._attributes; - var isDirty = false; - - for (var prop in attrs) { - if (attrs.hasOwnProperty(prop)) { - isDirty = true; - break; - } - } - - if (isDirty) { - record.adapterDidDirty(); - } - }, - - // EVENTS - didSetProperty: didSetProperty, - - pushedData: Ember.K, - - becomeDirty: function(record) { - record.transitionTo('updated.uncommitted'); - }, - - willCommit: function(record) { - record.transitionTo('updated.inFlight'); - }, - - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); - }, - - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.disconnectRelationships(); - }, - - unloadRecord: function(record) { - // clear relationships before moving to deleted state - // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); - }, - - didCommit: function(record) { - record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType')); - }, - - // loaded.saved.notFound would be triggered by a failed - // `reload()` on an unchanged record - notFound: Ember.K - - }, - - // A record is in this state after it has been locally - // created but before the adapter has indicated that - // it has been saved. - created: createdState, - - // A record is in this state if it has already been - // saved to the server, but there are new local changes - // that have not yet been saved. - updated: updatedState - }, - - // A record is in this state if it was deleted from the store. - deleted: { - initialState: 'uncommitted', - dirtyType: 'deleted', - - // FLAGS - isDeleted: true, - isLoaded: true, - isDirty: true, - - // TRANSITIONS - setup: function(record) { - record.updateRecordArrays(); - }, - - // SUBSTATES - - // When a record is deleted, it enters the `start` - // state. It will exit this state when the record - // starts to commit. - uncommitted: { - - // EVENTS - - willCommit: function(record) { - record.transitionTo('inFlight'); - }, - - rollback: function(record) { - record.rollback(); - }, - - becomeDirty: Ember.K, - deleteRecord: Ember.K, - - rolledBack: function(record) { - record.transitionTo('loaded.saved'); - } - }, - - // After a record starts committing, but - // before the adapter indicates that the deletion - // has saved to the server, a record is in the - // `inFlight` substate of `deleted`. - inFlight: { - // FLAGS - isSaving: true, - - // EVENTS - - unloadRecord: assertAgainstUnloadRecord, - - // TODO: More robust semantics around save-while-in-flight - willCommit: Ember.K, - didCommit: function(record) { - record.transitionTo('saved'); - - record.send('invokeLifecycleCallbacks'); - }, - - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); - } - }, - - // Once the adapter indicates that the deletion has - // been saved, the record enters the `saved` substate - // of `deleted`. - saved: { - // FLAGS - isDirty: false, - - setup: function(record) { - var store = get(record, 'store'); - store.dematerializeRecord(record); - }, - - invokeLifecycleCallbacks: function(record) { - record.triggerLater('didDelete', record); - record.triggerLater('didCommit', record); - }, - - willCommit: Ember.K, - - didCommit: Ember.K - } - }, - - invokeLifecycleCallbacks: function(record, dirtyType) { - if (dirtyType === 'created') { - record.triggerLater('didCreate', record); - } else { - record.triggerLater('didUpdate', record); - } - - record.triggerLater('didCommit', record); - } - }; - - function wireState(object, parent, name) { - /*jshint proto:true*/ - // TODO: Use Object.create and copy instead - object = mixin(parent ? Ember.create(parent) : {}, object); - object.parentState = parent; - object.stateName = name; - - for (var prop in object) { - if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; } - if (typeof object[prop] === 'object') { - object[prop] = wireState(object[prop], object, name + "." + prop); - } - } - - return object; - } - - RootState = wireState(RootState, null, "root"); - - __exports__["default"] = RootState; - }); -enifed("ember-data/system/promise_proxies", - ["exports"], - function(__exports__) { - "use strict"; - var Promise = Ember.RSVP.Promise; - var get = Ember.get; - - /** - A `PromiseArray` is an object that acts like both an `Ember.Array` - and a promise. When the promise is resolved the resulting value - will be set to the `PromiseArray`'s `content` property. This makes - it easy to create data bindings with the `PromiseArray` that will be - updated when the promise resolves. - - For more information see the [Ember.PromiseProxyMixin - documentation](/api/classes/Ember.PromiseProxyMixin.html). - - Example - - ```javascript - var promiseArray = DS.PromiseArray.create({ - promise: $.getJSON('/some/remote/data.json') - }); - - promiseArray.get('length'); // 0 - - promiseArray.then(function() { - promiseArray.get('length'); // 100 - }); - ``` - - @class PromiseArray - @namespace DS - @extends Ember.ArrayProxy - @uses Ember.PromiseProxyMixin - */ - var PromiseArray = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin); - - /** - A `PromiseObject` is an object that acts like both an `Ember.Object` - and a promise. When the promise is resolved, then the resulting value - will be set to the `PromiseObject`'s `content` property. This makes - it easy to create data bindings with the `PromiseObject` that will - be updated when the promise resolves. - - For more information see the [Ember.PromiseProxyMixin - documentation](/api/classes/Ember.PromiseProxyMixin.html). - - Example - - ```javascript - var promiseObject = DS.PromiseObject.create({ - promise: $.getJSON('/some/remote/data.json') - }); - - promiseObject.get('name'); // null - - promiseObject.then(function() { - promiseObject.get('name'); // 'Tomster' - }); - ``` - - @class PromiseObject - @namespace DS - @extends Ember.ObjectProxy - @uses Ember.PromiseProxyMixin - */ - var PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin); - - var promiseObject = function(promise, label) { - return PromiseObject.create({ - promise: Promise.resolve(promise, label) - }); - }; - - var promiseArray = function(promise, label) { - return PromiseArray.create({ - promise: Promise.resolve(promise, label) - }); - }; - - /** - A PromiseManyArray is a PromiseArray that also proxies certain method calls - to the underlying manyArray. - Right now we proxy: - `reload()` - `createRecord()` - `on()` - `one()` - `trigger()` - `off()` - `has()` - */ - - function proxyToContent(method) { - return function() { - var content = get(this, 'content'); - return content[method].apply(content, arguments); - }; - } - - var PromiseManyArray = PromiseArray.extend({ - reload: function() { - //I don't think this should ever happen right now, but worth guarding if we refactor the async relationships - Ember.assert('You are trying to reload an async manyArray before it has been created', get(this, 'content')); - return get(this, 'content').reload(); - }, - - createRecord: proxyToContent('createRecord'), - - on: proxyToContent('on'), - - one: proxyToContent('one'), - - trigger: proxyToContent('trigger'), - - off: proxyToContent('off'), - - has: proxyToContent('has') - }); - - var promiseManyArray = function(promise, label) { - return PromiseManyArray.create({ - promise: Promise.resolve(promise, label) - }); - }; - - - __exports__.PromiseArray = PromiseArray; - __exports__.PromiseObject = PromiseObject; - __exports__.PromiseManyArray = PromiseManyArray; - __exports__.promiseArray = promiseArray; - __exports__.promiseObject = promiseObject; - __exports__.promiseManyArray = promiseManyArray; - }); -enifed("ember-data/system/record_array_manager", - ["ember-data/system/record_arrays","ember-data/system/map","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var RecordArray = __dependency1__.RecordArray; - var FilteredRecordArray = __dependency1__.FilteredRecordArray; - var AdapterPopulatedRecordArray = __dependency1__.AdapterPopulatedRecordArray; - var ManyArray = __dependency1__.ManyArray; - var MapWithDefault = __dependency2__.MapWithDefault; - var OrderedSet = __dependency2__.OrderedSet; - var get = Ember.get; - var forEach = Ember.EnumerableUtils.forEach; - var indexOf = Ember.EnumerableUtils.indexOf; - - /** - @class RecordArrayManager - @namespace DS - @private - @extends Ember.Object - */ - __exports__["default"] = Ember.Object.extend({ - init: function() { - this.filteredRecordArrays = MapWithDefault.create({ - defaultValue: function() { return []; } - }); - - this.changedRecords = []; - this._adapterPopulatedRecordArrays = []; - }, - - recordDidChange: function(record) { - if (this.changedRecords.push(record) !== 1) { return; } - - Ember.run.schedule('actions', this, this.updateRecordArrays); - }, - - recordArraysForRecord: function(record) { - record._recordArrays = record._recordArrays || OrderedSet.create(); - return record._recordArrays; - }, - - /** - This method is invoked whenever data is loaded into the store by the - adapter or updated by the adapter, or when a record has changed. - - It updates all record arrays that a record belongs to. - - To avoid thrashing, it only runs at most once per run loop. - - @method updateRecordArrays - @param {Class} type - @param {Number|String} clientId - */ - updateRecordArrays: function() { - forEach(this.changedRecords, function(record) { - if (get(record, 'isDeleted')) { - this._recordWasDeleted(record); - } else { - this._recordWasChanged(record); - } - }, this); - - this.changedRecords.length = 0; - }, - - _recordWasDeleted: function (record) { - var recordArrays = record._recordArrays; - - if (!recordArrays) { return; } - - recordArrays.forEach(function(array){ - array.removeRecord(record); - }); - - record._recordArrays = null; - }, - - _recordWasChanged: function (record) { - var type = record.constructor; - var recordArrays = this.filteredRecordArrays.get(type); - var filter; - - forEach(recordArrays, function(array) { - filter = get(array, 'filterFunction'); - this.updateRecordArray(array, filter, type, record); - }, this); - - // loop through all manyArrays containing an unloaded copy of this - // clientId and notify them that the record was loaded. - var manyArrays = record._loadingRecordArrays; - - if (manyArrays) { - for (var i=0, l=manyArrays.length; i<l; i++) { - manyArrays[i].loadedRecord(); - } - - record._loadingRecordArrays = []; - } - }, - - /** - Update an individual filter. - - @method updateRecordArray - @param {DS.FilteredRecordArray} array - @param {Function} filter - @param {Class} type - @param {Number|String} clientId - */ - updateRecordArray: function(array, filter, type, record) { - var shouldBeInArray; - - if (!filter) { - shouldBeInArray = true; - } else { - shouldBeInArray = filter(record); - } - - var recordArrays = this.recordArraysForRecord(record); - - if (shouldBeInArray) { - if (!recordArrays.has(array)) { - array.pushRecord(record); - recordArrays.add(array); - } - } else if (!shouldBeInArray) { - recordArrays["delete"](array); - array.removeRecord(record); - } - }, - - /** - This method is invoked if the `filterFunction` property is - changed on a `DS.FilteredRecordArray`. - - It essentially re-runs the filter from scratch. This same - method is invoked when the filter is created in th first place. - - @method updateFilter - @param {Array} array - @param {String} type - @param {Function} filter - */ - updateFilter: function(array, type, filter) { - var typeMap = this.store.typeMapFor(type); - var records = typeMap.records, record; - - for (var i=0, l=records.length; i<l; i++) { - record = records[i]; - - if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) { - this.updateRecordArray(array, filter, type, record); - } - } - }, - - /** - Create a `DS.ManyArray` for a type and list of record references, and index - the `ManyArray` under each reference. This allows us to efficiently remove - records from `ManyArray`s when they are deleted. - - @method createManyArray - @param {Class} type - @param {Array} references - @return {DS.ManyArray} - */ - createManyArray: function(type, records) { - var manyArray = ManyArray.create({ - type: type, - content: records, - store: this.store - }); - - forEach(records, function(record) { - var arrays = this.recordArraysForRecord(record); - arrays.add(manyArray); - }, this); - - return manyArray; - }, - - /** - Create a `DS.RecordArray` for a type and register it for updates. - - @method createRecordArray - @param {Class} type - @return {DS.RecordArray} - */ - createRecordArray: function(type) { - var array = RecordArray.create({ - type: type, - content: Ember.A(), - store: this.store, - isLoaded: true - }); - - this.registerFilteredRecordArray(array, type); - - return array; - }, - - /** - Create a `DS.FilteredRecordArray` for a type and register it for updates. - - @method createFilteredRecordArray - @param {Class} type - @param {Function} filter - @param {Object} query (optional - @return {DS.FilteredRecordArray} - */ - createFilteredRecordArray: function(type, filter, query) { - var array = FilteredRecordArray.create({ - query: query, - type: type, - content: Ember.A(), - store: this.store, - manager: this, - filterFunction: filter - }); - - this.registerFilteredRecordArray(array, type, filter); - - return array; - }, - - /** - Create a `DS.AdapterPopulatedRecordArray` for a type with given query. - - @method createAdapterPopulatedRecordArray - @param {Class} type - @param {Object} query - @return {DS.AdapterPopulatedRecordArray} - */ - createAdapterPopulatedRecordArray: function(type, query) { - var array = AdapterPopulatedRecordArray.create({ - type: type, - query: query, - content: Ember.A(), - store: this.store, - manager: this - }); - - this._adapterPopulatedRecordArrays.push(array); - - return array; - }, - - /** - Register a RecordArray for a given type to be backed by - a filter function. This will cause the array to update - automatically when records of that type change attribute - values or states. - - @method registerFilteredRecordArray - @param {DS.RecordArray} array - @param {Class} type - @param {Function} filter - */ - registerFilteredRecordArray: function(array, type, filter) { - var recordArrays = this.filteredRecordArrays.get(type); - recordArrays.push(array); - - this.updateFilter(array, type, filter); - }, - - /** - Unregister a FilteredRecordArray. - So manager will not update this array. - - @method unregisterFilteredRecordArray - @param {DS.RecordArray} array - */ - unregisterFilteredRecordArray: function(array) { - var recordArrays = this.filteredRecordArrays.get(array.type); - var index = indexOf(recordArrays, array); - recordArrays.splice(index, 1); - }, - - // Internally, we maintain a map of all unloaded IDs requested by - // a ManyArray. As the adapter loads data into the store, the - // store notifies any interested ManyArrays. When the ManyArray's - // total number of loading records drops to zero, it becomes - // `isLoaded` and fires a `didLoad` event. - registerWaitingRecordArray: function(record, array) { - var loadingRecordArrays = record._loadingRecordArrays || []; - loadingRecordArrays.push(array); - record._loadingRecordArrays = loadingRecordArrays; - }, - - willDestroy: function(){ - this._super(); - - forEach(flatten(values(this.filteredRecordArrays.values)), destroy); - forEach(this._adapterPopulatedRecordArrays, destroy); - } - }); - - function values(obj) { - var result = []; - var keys = Ember.keys(obj); - - for (var i = 0; i < keys.length; i++) { - result.push(obj[keys[i]]); - } - - return result; - } - - function destroy(entry) { - entry.destroy(); - } - - function flatten(list) { - var length = list.length; - var result = Ember.A(); - - for (var i = 0; i < length; i++) { - result = result.concat(list[i]); - } - - return result; - } - }); -enifed("ember-data/system/record_arrays", - ["ember-data/system/record_arrays/record_array","ember-data/system/record_arrays/filtered_record_array","ember-data/system/record_arrays/adapter_populated_record_array","ember-data/system/record_arrays/many_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var RecordArray = __dependency1__["default"]; - var FilteredRecordArray = __dependency2__["default"]; - var AdapterPopulatedRecordArray = __dependency3__["default"]; - var ManyArray = __dependency4__["default"]; - - __exports__.RecordArray = RecordArray; - __exports__.FilteredRecordArray = FilteredRecordArray; - __exports__.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray; - __exports__.ManyArray = ManyArray; - }); -enifed("ember-data/system/record_arrays/adapter_populated_record_array", - ["ember-data/system/record_arrays/record_array","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var RecordArray = __dependency1__["default"]; - /** - @module ember-data - */ - - var get = Ember.get; - - function cloneNull(source) { - var clone = Ember.create(null); - for (var key in source) { - clone[key] = source[key]; - } - return clone; - } - - /** - Represents an ordered list of records whose order and membership is - determined by the adapter. For example, a query sent to the adapter - may trigger a search on the server, whose results would be loaded - into an instance of the `AdapterPopulatedRecordArray`. - - @class AdapterPopulatedRecordArray - @namespace DS - @extends DS.RecordArray - */ - __exports__["default"] = RecordArray.extend({ - query: null, - - replace: function() { - var type = get(this, 'type').toString(); - throw new Error("The result of a server query (on " + type + ") is immutable."); - }, - - /** - @method load - @private - @param {Array} data - */ - load: function(data) { - var store = get(this, 'store'); - var type = get(this, 'type'); - var records = store.pushMany(type, data); - var meta = store.metadataFor(type); - - this.setProperties({ - content: Ember.A(records), - isLoaded: true, - meta: cloneNull(meta) - }); - - records.forEach(function(record) { - this.manager.recordArraysForRecord(record).add(this); - }, this); - - // TODO: should triggering didLoad event be the last action of the runLoop? - Ember.run.once(this, 'trigger', 'didLoad'); - } - }); - }); -enifed("ember-data/system/record_arrays/filtered_record_array", - ["ember-data/system/record_arrays/record_array","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var RecordArray = __dependency1__["default"]; - - /** - @module ember-data - */ - - var get = Ember.get; - - /** - Represents a list of records whose membership is determined by the - store. As records are created, loaded, or modified, the store - evaluates them to determine if they should be part of the record - array. - - @class FilteredRecordArray - @namespace DS - @extends DS.RecordArray - */ - __exports__["default"] = RecordArray.extend({ - /** - The filterFunction is a function used to test records from the store to - determine if they should be part of the record array. - - Example - - ```javascript - var allPeople = store.all('person'); - allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"] - - var people = store.filter('person', function(person) { - if (person.get('name').match(/Katz$/)) { return true; } - }); - people.mapBy('name'); // ["Yehuda Katz"] - - var notKatzFilter = function(person) { - return !person.get('name').match(/Katz$/); - }; - people.set('filterFunction', notKatzFilter); - people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"] - ``` - - @method filterFunction - @param {DS.Model} record - @return {Boolean} `true` if the record should be in the array - */ - filterFunction: null, - isLoaded: true, - - replace: function() { - var type = get(this, 'type').toString(); - throw new Error("The result of a client-side filter (on " + type + ") is immutable."); - }, - - /** - @method updateFilter - @private - */ - _updateFilter: function() { - var manager = get(this, 'manager'); - manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction')); - }, - - updateFilter: Ember.observer(function() { - Ember.run.once(this, this._updateFilter); - }, 'filterFunction'), - - /** - @method _unregisterFromManager - @private - */ - _unregisterFromManager: function(){ - this.manager.unregisterFilteredRecordArray(this); - }, - - willDestroy: function(){ - this._unregisterFromManager(); - this._super(); - } - }); - }); -enifed("ember-data/system/record_arrays/many_array", - ["ember-data/system/record_arrays/record_array","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var RecordArray = __dependency1__["default"]; - - /** - @module ember-data - */ - - var get = Ember.get, set = Ember.set; - - /** - A `ManyArray` is a `RecordArray` that represents the contents of a has-many - relationship. - - The `ManyArray` is instantiated lazily the first time the relationship is - requested. - - ### Inverses - - Often, the relationships in Ember Data applications will have - an inverse. For example, imagine the following models are - defined: - - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); - - App.Comment = DS.Model.extend({ - post: DS.belongsTo('post') - }); - ``` - - If you created a new instance of `App.Post` and added - a `App.Comment` record to its `comments` has-many - relationship, you would expect the comment's `post` - property to be set to the post that contained - the has-many. - - We call the record to which a relationship belongs the - relationship's _owner_. - - @class ManyArray - @namespace DS - @extends DS.RecordArray - */ - __exports__["default"] = RecordArray.extend({ - init: function() { - this._super.apply(this, arguments); - }, - - /** - `true` if the relationship is polymorphic, `false` otherwise. - - @property {Boolean} isPolymorphic - @private - */ - isPolymorphic: false, - - /** - The loading state of this array - - @property {Boolean} isLoaded - */ - isLoaded: false, - - /** - The relationship which manages this array. - - @property {ManyRelationship} relationship - @private - */ - relationship: null, - - - /** - Used for async `hasMany` arrays - to keep track of when they will resolve. - - @property {Ember.RSVP.Promise} promise - @private - */ - promise: null, - - /** - @method loadingRecordsCount - @param {Number} count - @private - */ - loadingRecordsCount: function(count) { - this.loadingRecordsCount = count; - }, - - /** - @method loadedRecord - @private - */ - loadedRecord: function() { - this.loadingRecordsCount--; - if (this.loadingRecordsCount === 0) { - set(this, 'isLoaded', true); - this.trigger('didLoad'); - } - }, - - replaceContent: function(idx, amt, objects){ - var records; - if (amt > 0){ - records = get(this, 'content').slice(idx, idx+amt); - this.get('relationship').removeRecords(records); - } - if (objects){ - this.get('relationship').addRecords(objects, idx); - } - }, - /** - @method reload - @public - */ - reload: function() { - return this.relationship.reload(); - }, - - /** - Create a child record within the owner - - @method createRecord - @private - @param {Object} hash - @return {DS.Model} record - */ - createRecord: function(hash) { - var store = get(this, 'store'); - var type = get(this, 'type'); - var record; - - Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic')); - - record = store.createRecord(type, hash); - this.pushObject(record); - - return record; - } - }); - }); -enifed("ember-data/system/record_arrays/record_array", - ["ember-data/system/promise_proxies","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var PromiseArray = __dependency1__.PromiseArray; - var get = Ember.get; - - /** - A record array is an array that contains records of a certain type. The record - array materializes records as needed when they are retrieved for the first - time. You should not create record arrays yourself. Instead, an instance of - `DS.RecordArray` or its subclasses will be returned by your application's store - in response to queries. - - @class RecordArray - @namespace DS - @extends Ember.ArrayProxy - @uses Ember.Evented - */ - - __exports__["default"] = Ember.ArrayProxy.extend(Ember.Evented, { - /** - The model type contained by this record array. - - @property type - @type DS.Model - */ - type: null, - - /** - The array of client ids backing the record array. When a - record is requested from the record array, the record - for the client id at the same index is materialized, if - necessary, by the store. - - @property content - @private - @type Ember.Array - */ - content: null, - - /** - The flag to signal a `RecordArray` is currently loading data. - - Example - - ```javascript - var people = store.all('person'); - people.get('isLoaded'); // true - ``` - - @property isLoaded - @type Boolean - */ - isLoaded: false, - /** - The flag to signal a `RecordArray` is currently loading data. - - Example - - ```javascript - var people = store.all('person'); - people.get('isUpdating'); // false - people.update(); - people.get('isUpdating'); // true - ``` - - @property isUpdating - @type Boolean - */ - isUpdating: false, - - /** - The store that created this record array. - - @property store - @private - @type DS.Store - */ - store: null, - - /** - Retrieves an object from the content by index. - - @method objectAtContent - @private - @param {Number} index - @return {DS.Model} record - */ - objectAtContent: function(index) { - var content = get(this, 'content'); - - return content.objectAt(index); - }, - - /** - Used to get the latest version of all of the records in this array - from the adapter. - - Example - - ```javascript - var people = store.all('person'); - people.get('isUpdating'); // false - people.update(); - people.get('isUpdating'); // true - ``` - - @method update - */ - update: function() { - if (get(this, 'isUpdating')) { return; } - - var store = get(this, 'store'); - var type = get(this, 'type'); - - return store.fetchAll(type, this); - }, - - /** - Adds a record to the `RecordArray` without duplicates - - @method addRecord - @private - @param {DS.Model} record - @param {DS.Model} an optional index to insert at - */ - addRecord: function(record, idx) { - var content = get(this, 'content'); - if (idx === undefined) { - content.addObject(record); - } else { - if (!content.contains(record)) { - content.insertAt(idx, record); - } - } - }, - - /** - Adds a record to the `RecordArray`, but allows duplicates - - @method pushRecord - @private - @param {DS.Model} record - */ - pushRecord: function(record) { - get(this, 'content').pushObject(record); - }, - - - /** - Removes a record to the `RecordArray`. - - @method removeRecord - @private - @param {DS.Model} record - */ - removeRecord: function(record) { - get(this, 'content').removeObject(record); - }, - - /** - Saves all of the records in the `RecordArray`. - - Example - - ```javascript - var messages = store.all('message'); - messages.forEach(function(message) { - message.set('hasBeenSeen', true); - }); - messages.save(); - ``` - - @method save - @return {DS.PromiseArray} promise - */ - save: function() { - var promiseLabel = "DS: RecordArray#save " + get(this, 'type'); - var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) { - return Ember.A(array); - }, null, "DS: RecordArray#save apply Ember.NativeArray"); - - return PromiseArray.create({ promise: promise }); - }, - - _dissociateFromOwnRecords: function() { - var array = this; - - this.forEach(function(record){ - var recordArrays = record._recordArrays; - - if (recordArrays) { - recordArrays["delete"](array); - } - }); - }, - - willDestroy: function(){ - this._dissociateFromOwnRecords(); - this._super(); - } - }); - }); -enifed("ember-data/system/relationship-meta", - ["ember-inflector/system","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var singularize = __dependency1__.singularize; - - function typeForRelationshipMeta(store, meta) { - var typeKey, type; - - typeKey = meta.type || meta.key; - if (typeof typeKey === 'string') { - if (meta.kind === 'hasMany') { - typeKey = singularize(typeKey); - } - type = store.modelFor(typeKey); - } else { - type = meta.type; - } - - return type; - } - - __exports__.typeForRelationshipMeta = typeForRelationshipMeta;function relationshipFromMeta(store, meta) { - return { - key: meta.key, - kind: meta.kind, - type: typeForRelationshipMeta(store, meta), - options: meta.options, - parentType: meta.parentType, - isRelationship: true - }; - } - - __exports__.relationshipFromMeta = relationshipFromMeta; - }); -enifed("ember-data/system/relationships", - ["./relationships/belongs_to","./relationships/has_many","ember-data/system/relationships/ext","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var belongsTo = __dependency1__["default"]; - var hasMany = __dependency2__["default"]; - - - __exports__.belongsTo = belongsTo; - __exports__.hasMany = hasMany; - }); -enifed("ember-data/system/relationships/belongs_to", - ["ember-data/system/model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Model = __dependency1__.Model; - - - /** - `DS.belongsTo` is used to define One-To-One and One-To-Many - relationships on a [DS.Model](/api/data/classes/DS.Model.html). - - - `DS.belongsTo` takes an optional hash as a second parameter, currently - supported options are: - - - `async`: A boolean value used to explicitly declare this to be an async relationship. - - `inverse`: A string used to identify the inverse property on a - related model in a One-To-Many relationship. See [Explicit Inverses](#toc_explicit-inverses) - - #### One-To-One - To declare a one-to-one relationship between two models, use - `DS.belongsTo`: - - ```javascript - App.User = DS.Model.extend({ - profile: DS.belongsTo('profile') - }); - - App.Profile = DS.Model.extend({ - user: DS.belongsTo('user') - }); - ``` - - #### One-To-Many - To declare a one-to-many relationship between two models, use - `DS.belongsTo` in combination with `DS.hasMany`, like this: - - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); - - App.Comment = DS.Model.extend({ - post: DS.belongsTo('post') - }); - ``` - - @namespace - @method belongsTo - @for DS - @param {String or DS.Model} type the model type of the relationship - @param {Object} options a hash of options - @return {Ember.computed} relationship - */ - function belongsTo(type, options) { - if (typeof type === 'object') { - options = type; - type = undefined; - } else { - Ember.assert("The first argument to DS.belongsTo must be a string representing a model type key, e.g. use DS.belongsTo('person') to define a relation to the App.Person model", !!type && (typeof type === 'string' || Model.detect(type))); - } - - options = options || {}; - - var meta = { - type: type, - isRelationship: true, - options: options, - kind: 'belongsTo', - key: null - }; - - return Ember.computed(function(key, value) { - if (arguments.length>1) { - if ( value === undefined ) { - value = null; - } - if (value && value.then) { - this._relationships[key].setRecordPromise(value); - } else { - this._relationships[key].setRecord(value); - } - } - - return this._relationships[key].getRecord(); - }).meta(meta); - } - - /** - These observers observe all `belongsTo` relationships on the record. See - `relationships/ext` to see how these observers get their dependencies. - - @class Model - @namespace DS - */ - Model.reopen({ - notifyBelongsToAdded: function(key, relationship) { - this.notifyPropertyChange(key); - }, - - notifyBelongsToRemoved: function(key) { - this.notifyPropertyChange(key); - } - }); - - __exports__["default"] = belongsTo; - }); -enifed("ember-data/system/relationships/ext", - ["ember-data/system/relationship-meta","ember-data/system/model","ember-data/system/map"], - function(__dependency1__, __dependency2__, __dependency3__) { - "use strict"; - var typeForRelationshipMeta = __dependency1__.typeForRelationshipMeta; - var relationshipFromMeta = __dependency1__.relationshipFromMeta; - var Model = __dependency2__.Model; - var Map = __dependency3__.Map; - var MapWithDefault = __dependency3__.MapWithDefault; - - var get = Ember.get; - var filter = Ember.ArrayPolyfills.filter; - - /** - @module ember-data - */ - - /* - This file defines several extensions to the base `DS.Model` class that - add support for one-to-many relationships. - */ - - /** - @class Model - @namespace DS - */ - Model.reopen({ - - /** - This Ember.js hook allows an object to be notified when a property - is defined. - - In this case, we use it to be notified when an Ember Data user defines a - belongs-to relationship. In that case, we need to set up observers for - each one, allowing us to track relationship changes and automatically - reflect changes in the inverse has-many array. - - This hook passes the class being set up, as well as the key and value - being defined. So, for example, when the user does this: - - ```javascript - DS.Model.extend({ - parent: DS.belongsTo('user') - }); - ``` - - This hook would be called with "parent" as the key and the computed - property returned by `DS.belongsTo` as the value. - - @method didDefineProperty - @param {Object} proto - @param {String} key - @param {Ember.ComputedProperty} value - */ - didDefineProperty: function(proto, key, value) { - // Check if the value being set is a computed property. - if (value instanceof Ember.ComputedProperty) { - - // If it is, get the metadata for the relationship. This is - // populated by the `DS.belongsTo` helper when it is creating - // the computed property. - var meta = value.meta(); - - meta.parentType = proto.constructor; - } - } - }); - - /* - These DS.Model extensions add class methods that provide relationship - introspection abilities about relationships. - - A note about the computed properties contained here: - - **These properties are effectively sealed once called for the first time.** - To avoid repeatedly doing expensive iteration over a model's fields, these - values are computed once and then cached for the remainder of the runtime of - your application. - - If your application needs to modify a class after its initial definition - (for example, using `reopen()` to add additional attributes), make sure you - do it before using your model with the store, which uses these properties - extensively. - */ - - Model.reopenClass({ - - /** - For a given relationship name, returns the model type of the relationship. - - For example, if you define a model like this: - - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); - ``` - - Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`. - - @method typeForRelationship - @static - @param {String} name the name of the relationship - @return {subclass of DS.Model} the type of the relationship, or undefined - */ - typeForRelationship: function(name) { - var relationship = get(this, 'relationshipsByName').get(name); - return relationship && relationship.type; - }, - - inverseMap: Ember.computed(function() { - return Ember.create(null); - }), - - /** - Find the relationship which is the inverse of the one asked for. - - For example, if you define models like this: - - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('message') - }); - - App.Message = DS.Model.extend({ - owner: DS.belongsTo('post') - }); - ``` - - App.Post.inverseFor('comments') -> {type: App.Message, name:'owner', kind:'belongsTo'} - App.Message.inverseFor('owner') -> {type: App.Post, name:'comments', kind:'hasMany'} - - @method inverseFor - @static - @param {String} name the name of the relationship - @return {Object} the inverse relationship, or null - */ - inverseFor: function(name) { - var inverseMap = get(this, 'inverseMap'); - if (inverseMap[name]) { - return inverseMap[name]; - } else { - var inverse = this._findInverseFor(name); - inverseMap[name] = inverse; - return inverse; - } - }, - - //Calculate the inverse, ignoring the cache - _findInverseFor: function(name) { - - var inverseType = this.typeForRelationship(name); - if (!inverseType) { - return null; - } - - //If inverse is manually specified to be null, like `comments: DS.hasMany('message', {inverse: null})` - var options = this.metaForProperty(name).options; - if (options.inverse === null) { return null; } - - var inverseName, inverseKind, inverse; - - //If inverse is specified manually, return the inverse - if (options.inverse) { - inverseName = options.inverse; - inverse = Ember.get(inverseType, 'relationshipsByName').get(inverseName); - - Ember.assert("We found no inverse relationships by the name of '" + inverseName + "' on the '" + inverseType.typeKey + - "' model. This is most likely due to a missing attribute on your model definition.", !Ember.isNone(inverse)); - - inverseKind = inverse.kind; - } else { - //No inverse was specified manually, we need to use a heuristic to guess one - var possibleRelationships = findPossibleInverses(this, inverseType); - - if (possibleRelationships.length === 0) { return null; } - - var filteredRelationships = filter.call(possibleRelationships, function(possibleRelationship) { - var optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options; - return name === optionsForRelationship.inverse; - }); - - Ember.assert("You defined the '" + name + "' relationship on " + this + ", but you defined the inverse relationships of type " + - inverseType.toString() + " multiple times. Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", - filteredRelationships.length < 2); - - if (filteredRelationships.length === 1 ) { - possibleRelationships = filteredRelationships; - } - - Ember.assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + - this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", - possibleRelationships.length === 1); - - inverseName = possibleRelationships[0].name; - inverseKind = possibleRelationships[0].kind; - } - - function findPossibleInverses(type, inverseType, relationshipsSoFar) { - var possibleRelationships = relationshipsSoFar || []; - - var relationshipMap = get(inverseType, 'relationships'); - if (!relationshipMap) { return; } - - var relationships = relationshipMap.get(type); - - relationships = filter.call(relationships, function(relationship) { - var optionsForRelationship = inverseType.metaForProperty(relationship.name).options; - - if (!optionsForRelationship.inverse){ - return true; - } - - return name === optionsForRelationship.inverse; - }); - - if (relationships) { - possibleRelationships.push.apply(possibleRelationships, relationships); - } - - //Recurse to support polymorphism - if (type.superclass) { - findPossibleInverses(type.superclass, inverseType, possibleRelationships); - } - - return possibleRelationships; - } - - return { - type: inverseType, - name: inverseName, - kind: inverseKind - }; - }, - - /** - The model's relationships as a map, keyed on the type of the - relationship. The value of each entry is an array containing a descriptor - for each relationship with that type, describing the name of the relationship - as well as the type. - - For example, given the following model definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - posts: DS.hasMany('post') - }); - ``` - - This computed property would return a map describing these - relationships, like this: - - ```javascript - var relationships = Ember.get(App.Blog, 'relationships'); - relationships.get(App.User); - //=> [ { name: 'users', kind: 'hasMany' }, - // { name: 'owner', kind: 'belongsTo' } ] - relationships.get(App.Post); - //=> [ { name: 'posts', kind: 'hasMany' } ] - ``` - - @property relationships - @static - @type Ember.Map - @readOnly - */ - relationships: Ember.computed(function() { - var map = new MapWithDefault({ - defaultValue: function() { return []; } - }); - - // Loop through each computed property on the class - this.eachComputedProperty(function(name, meta) { - // If the computed property is a relationship, add - // it to the map. - if (meta.isRelationship) { - meta.key = name; - var relationshipsForType = map.get(typeForRelationshipMeta(this.store, meta)); - - relationshipsForType.push({ - name: name, - kind: meta.kind - }); - } - }); - - return map; - }).cacheable(false).readOnly(), - - /** - A hash containing lists of the model's relationships, grouped - by the relationship kind. For example, given a model with this - definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - var relationshipNames = Ember.get(App.Blog, 'relationshipNames'); - relationshipNames.hasMany; - //=> ['users', 'posts'] - relationshipNames.belongsTo; - //=> ['owner'] - ``` - - @property relationshipNames - @static - @type Object - @readOnly - */ - relationshipNames: Ember.computed(function() { - var names = { - hasMany: [], - belongsTo: [] - }; - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - names[meta.kind].push(name); - } - }); - - return names; - }), - - /** - An array of types directly related to a model. Each type will be - included once, regardless of the number of relationships it has with - the model. - - For example, given a model with this definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - var relatedTypes = Ember.get(App.Blog, 'relatedTypes'); - //=> [ App.User, App.Post ] - ``` - - @property relatedTypes - @static - @type Ember.Array - @readOnly - */ - relatedTypes: Ember.computed(function() { - var type; - var types = Ember.A(); - - // Loop through each computed property on the class, - // and create an array of the unique types involved - // in relationships - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - meta.key = name; - type = typeForRelationshipMeta(this.store, meta); - - Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type); - - if (!types.contains(type)) { - Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type); - types.push(type); - } - } - }); - - return types; - }).cacheable(false).readOnly(), - - /** - A map whose keys are the relationships of a model and whose values are - relationship descriptors. - - For example, given a model with this - definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName'); - relationshipsByName.get('users'); - //=> { key: 'users', kind: 'hasMany', type: App.User } - relationshipsByName.get('owner'); - //=> { key: 'owner', kind: 'belongsTo', type: App.User } - ``` - - @property relationshipsByName - @static - @type Ember.Map - @readOnly - */ - relationshipsByName: Ember.computed(function() { - var map = Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - meta.key = name; - var relationship = relationshipFromMeta(this.store, meta); - relationship.type = typeForRelationshipMeta(this.store, meta); - map.set(name, relationship); - } - }); - - return map; - }).cacheable(false).readOnly(), - - /** - A map whose keys are the fields of the model and whose values are strings - describing the kind of the field. A model's fields are the union of all of its - attributes and relationships. - - For example: - - ```javascript - - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post'), - - title: DS.attr('string') - }); - - var fields = Ember.get(App.Blog, 'fields'); - fields.forEach(function(kind, field) { - console.log(field, kind); - }); - - // prints: - // users, hasMany - // owner, belongsTo - // posts, hasMany - // title, attribute - ``` - - @property fields - @static - @type Ember.Map - @readOnly - */ - fields: Ember.computed(function() { - var map = Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - map.set(name, meta.kind); - } else if (meta.isAttribute) { - map.set(name, 'attribute'); - } - }); - - return map; - }).readOnly(), - - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - - @method eachRelationship - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship: function(callback, binding) { - get(this, 'relationshipsByName').forEach(function(relationship, name) { - callback.call(binding, name, relationship); - }); - }, - - /** - Given a callback, iterates over each of the types related to a model, - invoking the callback with the related type's class. Each type will be - returned just once, regardless of how many different relationships it has - with a model. - - @method eachRelatedType - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelatedType: function(callback, binding) { - get(this, 'relatedTypes').forEach(function(type) { - callback.call(binding, type); - }); - }, - - determineRelationshipType: function(knownSide) { - var knownKey = knownSide.key; - var knownKind = knownSide.kind; - var inverse = this.inverseFor(knownKey); - var key, otherKind; - - if (!inverse) { - return knownKind === 'belongsTo' ? 'oneToNone' : 'manyToNone'; - } - - key = inverse.name; - otherKind = inverse.kind; - - if (otherKind === 'belongsTo') { - return knownKind === 'belongsTo' ? 'oneToOne' : 'manyToOne'; - } else { - return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany'; - } - } - - }); - - Model.reopen({ - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - - @method eachRelationship - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship: function(callback, binding) { - this.constructor.eachRelationship(callback, binding); - }, - - relationshipFor: function(name) { - return get(this.constructor, 'relationshipsByName').get(name); - }, - - inverseFor: function(key) { - return this.constructor.inverseFor(key); - } - - }); - }); -enifed("ember-data/system/relationships/has_many", - ["ember-data/system/model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var Model = __dependency1__.Model; - - /** - `DS.hasMany` is used to define One-To-Many and Many-To-Many - relationships on a [DS.Model](/api/data/classes/DS.Model.html). - - `DS.hasMany` takes an optional hash as a second parameter, currently - supported options are: - - - `async`: A boolean value used to explicitly declare this to be an async relationship. - - `inverse`: A string used to identify the inverse property on a related model. - - #### One-To-Many - To declare a one-to-many relationship between two models, use - `DS.belongsTo` in combination with `DS.hasMany`, like this: - - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); - - App.Comment = DS.Model.extend({ - post: DS.belongsTo('post') - }); - ``` - - #### Many-To-Many - To declare a many-to-many relationship between two models, use - `DS.hasMany`: - - ```javascript - App.Post = DS.Model.extend({ - tags: DS.hasMany('tag') - }); - - App.Tag = DS.Model.extend({ - posts: DS.hasMany('post') - }); - ``` - - #### Explicit Inverses - - Ember Data will do its best to discover which relationships map to - one another. In the one-to-many code above, for example, Ember Data - can figure out that changing the `comments` relationship should update - the `post` relationship on the inverse because post is the only - relationship to that model. - - However, sometimes you may have multiple `belongsTo`/`hasManys` for the - same type. You can specify which property on the related model is - the inverse using `DS.hasMany`'s `inverse` option: - - ```javascript - var belongsTo = DS.belongsTo, - hasMany = DS.hasMany; - - App.Comment = DS.Model.extend({ - onePost: belongsTo('post'), - twoPost: belongsTo('post'), - redPost: belongsTo('post'), - bluePost: belongsTo('post') - }); - - App.Post = DS.Model.extend({ - comments: hasMany('comment', { - inverse: 'redPost' - }) - }); - ``` - - You can also specify an inverse on a `belongsTo`, which works how - you'd expect. - - @namespace - @method hasMany - @for DS - @param {String or DS.Model} type the model type of the relationship - @param {Object} options a hash of options - @return {Ember.computed} relationship - */ - function hasMany(type, options) { - if (typeof type === 'object') { - options = type; - type = undefined; - } - - options = options || {}; - - // Metadata about relationships is stored on the meta of - // the relationship. This is used for introspection and - // serialization. Note that `key` is populated lazily - // the first time the CP is called. - var meta = { - type: type, - isRelationship: true, - options: options, - kind: 'hasMany', - key: null - }; - - return Ember.computed(function(key) { - var relationship = this._relationships[key]; - return relationship.getRecords(); - }).meta(meta).readOnly(); - } - - Model.reopen({ - notifyHasManyAdded: function(key, record, idx) { - var relationship = this._relationships[key]; - var manyArray = relationship.manyArray; - manyArray.addRecord(record, idx); - //We need to notifyPropertyChange in the adding case because we need to make sure - //we fetch the newly added record in case it is unloaded - //TODO(Igor): Consider whether we could do this only if the record state is unloaded - this.notifyPropertyChange(key); - }, - - notifyHasManyRemoved: function(key, record) { - var relationship = this._relationships[key]; - var manyArray = relationship.manyArray; - manyArray.removeRecord(record); - } - }); - - - __exports__["default"] = hasMany; - }); -enifed("ember-data/system/relationships/relationship", - ["ember-data/system/promise_proxies","ember-data/system/map","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var PromiseManyArray = __dependency1__.PromiseManyArray; - var PromiseObject = __dependency1__.PromiseObject; - var OrderedSet = __dependency2__.OrderedSet; - - var Relationship = function(store, record, inverseKey, relationshipMeta) { - this.members = new OrderedSet(); - this.store = store; - this.key = relationshipMeta.key; - this.inverseKey = inverseKey; - this.record = record; - this.isAsync = relationshipMeta.options.async; - this.relationshipMeta = relationshipMeta; - //This probably breaks for polymorphic relationship in complex scenarios, due to - //multiple possible typeKeys - this.inverseKeyForImplicit = this.store.modelFor(this.record.constructor).typeKey + this.key; - //Cached promise when fetching the relationship from a link - this.linkPromise = null; - }; - - Relationship.prototype = { - constructor: Relationship, - - destroy: Ember.K, - - clear: function() { - this.members.forEach(function(member) { - this.removeRecord(member); - }, this); - }, - - disconnect: function(){ - this.members.forEach(function(member) { - this.removeRecordFromInverse(member); - }, this); - }, - - reconnect: function(){ - this.members.forEach(function(member) { - this.addRecordToInverse(member); - }, this); - }, - - removeRecords: function(records){ - var length = Ember.get(records, 'length'); - var record; - for (var i = 0; i < length; i++){ - record = records[i]; - this.removeRecord(record); - } - }, - - addRecords: function(records, idx){ - var length = Ember.get(records, 'length'); - var record; - for (var i = 0; i < length; i++){ - record = records[i]; - this.addRecord(record, idx); - if (idx !== undefined) { - idx++; - } - } - }, - - addRecord: function(record, idx) { - if (!this.members.has(record)) { - this.members.add(record); - this.notifyRecordRelationshipAdded(record, idx); - if (this.inverseKey) { - record._relationships[this.inverseKey].addRecord(this.record); - } else { - if (!record._implicitRelationships[this.inverseKeyForImplicit]) { - record._implicitRelationships[this.inverseKeyForImplicit] = new Relationship(this.store, record, this.key, {options:{}}); - } - record._implicitRelationships[this.inverseKeyForImplicit].addRecord(this.record); - } - this.record.updateRecordArrays(); - } - }, - - removeRecord: function(record) { - if (this.members.has(record)) { - this.removeRecordFromOwn(record); - if (this.inverseKey) { - this.removeRecordFromInverse(record); - } else { - if (record._implicitRelationships[this.inverseKeyForImplicit]) { - record._implicitRelationships[this.inverseKeyForImplicit].removeRecord(this.record); - } - } - } - }, - - addRecordToInverse: function(record) { - if (this.inverseKey) { - record._relationships[this.inverseKey].addRecord(this.record); - } - }, - - removeRecordFromInverse: function(record) { - var inverseRelationship = record._relationships[this.inverseKey]; - //Need to check for existence, as the record might unloading at the moment - if (inverseRelationship) { - inverseRelationship.removeRecordFromOwn(this.record); - } - }, - - removeRecordFromOwn: function(record) { - this.members["delete"](record); - this.notifyRecordRelationshipRemoved(record); - this.record.updateRecordArrays(); - }, - - updateLink: function(link) { - Ember.assert("You have pushed a record of type '" + this.record.constructor.typeKey + "' with '" + this.key + "' as a link, but the value of that link is not a string.", typeof link === 'string' || link === null); - if (link !== this.link) { - this.link = link; - this.linkPromise = null; - this.record.notifyPropertyChange(this.key); - } - }, - - findLink: function() { - if (this.linkPromise) { - return this.linkPromise; - } else { - var promise = this.fetchLink(); - this.linkPromise = promise; - return promise.then(function(result) { - return result; - }); - } - }, - - updateRecordsFromAdapter: function(records) { - //TODO Once we have adapter support, we need to handle updated and canonical changes - this.computeChanges(records); - }, - - notifyRecordRelationshipAdded: Ember.K, - notifyRecordRelationshipRemoved: Ember.K - }; - - var ManyRelationship = function(store, record, inverseKey, relationshipMeta) { - this._super$constructor(store, record, inverseKey, relationshipMeta); - this.belongsToType = relationshipMeta.type; - this.manyArray = store.recordArrayManager.createManyArray(this.belongsToType, Ember.A()); - this.manyArray.relationship = this; - this.isPolymorphic = relationshipMeta.options.polymorphic; - this.manyArray.isPolymorphic = this.isPolymorphic; - }; - - ManyRelationship.prototype = Ember.create(Relationship.prototype); - ManyRelationship.prototype.constructor = ManyRelationship; - ManyRelationship.prototype._super$constructor = Relationship; - - ManyRelationship.prototype.destroy = function() { - this.manyArray.destroy(); - }; - - ManyRelationship.prototype.notifyRecordRelationshipAdded = function(record, idx) { - Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.belongsToType.typeKey + "' allowed)", !this.belongsToType || record instanceof this.belongsToType); - this.record.notifyHasManyAdded(this.key, record, idx); - }; - - ManyRelationship.prototype.notifyRecordRelationshipRemoved = function(record) { - this.record.notifyHasManyRemoved(this.key, record); - }; - - ManyRelationship.prototype.reload = function() { - var self = this; - if (this.link) { - return this.fetchLink(); - } else { - return this.store.scheduleFetchMany(this.manyArray.toArray()).then(function() { - //Goes away after the manyArray refactor - self.manyArray.set('isLoaded', true); - return self.manyArray; - }); - } - }; - - ManyRelationship.prototype.computeChanges = function(records) { - var members = this.members; - var recordsToRemove = []; - var length; - var record; - var i; - - records = setForArray(records); - - members.forEach(function(member) { - if (records.has(member)) return; - - recordsToRemove.push(member); - }); - this.removeRecords(recordsToRemove); - - var hasManyArray = this.manyArray; - - // Using records.toArray() since currently using - // removeRecord can modify length, messing stuff up - // forEach since it directly looks at "length" each - // iteration - records = records.toArray(); - length = records.length; - for (i = 0; i < length; i++){ - record = records[i]; - //Need to preserve the order of incoming records - if (hasManyArray.objectAt(i) === record ) { - continue; - } - this.removeRecord(record); - this.addRecord(record, i); - } - }; - - ManyRelationship.prototype.fetchLink = function() { - var self = this; - return this.store.findHasMany(this.record, this.link, this.relationshipMeta).then(function(records){ - self.updateRecordsFromAdapter(records); - return self.manyArray; - }); - }; - - ManyRelationship.prototype.findRecords = function() { - var manyArray = this.manyArray; - return this.store.findMany(manyArray.toArray()).then(function(){ - //Goes away after the manyArray refactor - manyArray.set('isLoaded', true); - return manyArray; - }); - }; - - ManyRelationship.prototype.getRecords = function() { - if (this.isAsync) { - var self = this; - var promise; - if (this.link) { - promise = this.findLink().then(function() { - return self.findRecords(); - }); - } else { - promise = this.findRecords(); - } - return PromiseManyArray.create({ - content: this.manyArray, - promise: promise - }); - } else { - Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.typeKey + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", this.manyArray.isEvery('isEmpty', false)); - - if (!this.manyArray.get('isDestroyed')) { - this.manyArray.set('isLoaded', true); - } - return this.manyArray; - } - }; - - var BelongsToRelationship = function(store, record, inverseKey, relationshipMeta) { - this._super$constructor(store, record, inverseKey, relationshipMeta); - this.record = record; - this.key = relationshipMeta.key; - this.inverseRecord = null; - }; - - BelongsToRelationship.prototype = Ember.create(Relationship.prototype); - BelongsToRelationship.prototype.constructor = BelongsToRelationship; - BelongsToRelationship.prototype._super$constructor = Relationship; - - BelongsToRelationship.prototype.setRecord = function(newRecord) { - if (newRecord) { - this.addRecord(newRecord); - } else if (this.inverseRecord) { - this.removeRecord(this.inverseRecord); - } - }; - - BelongsToRelationship.prototype._super$addRecord = Relationship.prototype.addRecord; - BelongsToRelationship.prototype.addRecord = function(newRecord) { - if (this.members.has(newRecord)){ return;} - var type = this.relationshipMeta.type; - Ember.assert("You can only add a '" + type.typeKey + "' record to this relationship", newRecord instanceof type); - - if (this.inverseRecord) { - this.removeRecord(this.inverseRecord); - } - - this.inverseRecord = newRecord; - this._super$addRecord(newRecord); - }; - - BelongsToRelationship.prototype.setRecordPromise = function(newPromise) { - var content = newPromise.get && newPromise.get('content'); - Ember.assert("You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.", content !== undefined); - this.setRecord(content); - }; - - BelongsToRelationship.prototype.notifyRecordRelationshipAdded = function(newRecord) { - this.record.notifyBelongsToAdded(this.key, this); - }; - - BelongsToRelationship.prototype.notifyRecordRelationshipRemoved = function(record) { - this.record.notifyBelongsToRemoved(this.key, this); - }; - - BelongsToRelationship.prototype._super$removeRecordFromOwn = Relationship.prototype.removeRecordFromOwn; - BelongsToRelationship.prototype.removeRecordFromOwn = function(record) { - if (!this.members.has(record)) { return; } - this.inverseRecord = null; - this._super$removeRecordFromOwn(record); - }; - - BelongsToRelationship.prototype.findRecord = function() { - if (this.inverseRecord) { - return this.store._findByRecord(this.inverseRecord); - } else { - return Ember.RSVP.Promise.resolve(null); - } - }; - - BelongsToRelationship.prototype.fetchLink = function() { - var self = this; - return this.store.findBelongsTo(this.record, this.link, this.relationshipMeta).then(function(record){ - if (record) { - self.addRecord(record); - } - return record; - }); - }; - - BelongsToRelationship.prototype.getRecord = function() { - if (this.isAsync) { - var promise; - if (this.link){ - var self = this; - promise = this.findLink().then(function() { - return self.findRecord(); - }); - } else { - promise = this.findRecord(); - } - - return PromiseObject.create({ - promise: promise, - content: this.inverseRecord - }); - } else { - Ember.assert("You looked up the '" + this.key + "' relationship on a '" + this.record.constructor.typeKey + "' with id " + this.record.get('id') + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", this.inverseRecord === null || !this.inverseRecord.get('isEmpty')); - return this.inverseRecord; - } - }; - - function setForArray(array) { - var set = new OrderedSet(); - - if (array) { - for (var i=0, l=array.length; i<l; i++) { - set.add(array[i]); - } - } - - return set; - } - - var createRelationshipFor = function(record, relationshipMeta, store){ - var inverseKey; - var inverse = record.constructor.inverseFor(relationshipMeta.key); - - if (inverse) { - inverseKey = inverse.name; - } - - if (relationshipMeta.kind === 'hasMany'){ - return new ManyRelationship(store, record, inverseKey, relationshipMeta); - } - else { - return new BelongsToRelationship(store, record, inverseKey, relationshipMeta); - } - }; - - - __exports__.Relationship = Relationship; - __exports__.ManyRelationship = ManyRelationship; - __exports__.BelongsToRelationship = BelongsToRelationship; - __exports__.createRelationshipFor = createRelationshipFor; - }); -enifed("ember-data/system/store", - ["ember-data/system/adapter","ember-inflector/system/string","ember-data/system/map","ember-data/system/promise_proxies","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /*globals Ember*/ - /*jshint eqnull:true*/ - - /** - @module ember-data - */ - - var InvalidError = __dependency1__.InvalidError; - var Adapter = __dependency1__.Adapter; - var singularize = __dependency2__.singularize; - var Map = __dependency3__.Map; - - var promiseArray = __dependency4__.promiseArray; - var promiseObject = __dependency4__.promiseObject; - - - var get = Ember.get; - var set = Ember.set; - var once = Ember.run.once; - var isNone = Ember.isNone; - var forEach = Ember.EnumerableUtils.forEach; - var indexOf = Ember.EnumerableUtils.indexOf; - var map = Ember.EnumerableUtils.map; - var Promise = Ember.RSVP.Promise; - var copy = Ember.copy; - var Store, RecordArrayManager, Model; - - var camelize = Ember.String.camelize; - - // Implementors Note: - // - // The variables in this file are consistently named according to the following - // scheme: - // - // * +id+ means an identifier managed by an external source, provided inside - // the data provided by that source. These are always coerced to be strings - // before being used internally. - // * +clientId+ means a transient numerical identifier generated at runtime by - // the data store. It is important primarily because newly created objects may - // not yet have an externally generated id. - // * +reference+ means a record reference object, which holds metadata about a - // record, even if it has not yet been fully materialized. - // * +type+ means a subclass of DS.Model. - - // Used by the store to normalize IDs entering the store. Despite the fact - // that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`), - // it is important that internally we use strings, since IDs may be serialized - // and lose type information. For example, Ember's router may put a record's - // ID into the URL, and if we later try to deserialize that URL and find the - // corresponding record, we will not know if it is a string or a number. - function coerceId(id) { - return id == null ? null : id+''; - } - - /** - The store contains all of the data for records loaded from the server. - It is also responsible for creating instances of `DS.Model` that wrap - the individual data for a record, so that they can be bound to in your - Handlebars templates. - - Define your application's store like this: - - ```javascript - MyApp.Store = DS.Store.extend(); - ``` - - Most Ember.js applications will only have a single `DS.Store` that is - automatically created by their `Ember.Application`. - - You can retrieve models from the store in several ways. To retrieve a record - for a specific id, use `DS.Store`'s `find()` method: - - ```javascript - store.find('person', 123).then(function (person) { - }); - ``` - - By default, the store will talk to your backend using a standard - REST mechanism. You can customize how the store talks to your - backend by specifying a custom adapter: - - ```javascript - MyApp.ApplicationAdapter = MyApp.CustomAdapter - ``` - - You can learn more about writing a custom adapter by reading the `DS.Adapter` - documentation. - - ### Store createRecord() vs. push() vs. pushPayload() vs. update() - - The store provides multiple ways to create new record objects. They have - some subtle differences in their use which are detailed below: - - [createRecord](#method_createRecord) is used for creating new - records on the client side. This will return a new record in the - `created.uncommitted` state. In order to persist this record to the - backend you will need to call `record.save()`. - - [push](#method_push) is used to notify Ember Data's store of new or - updated records that exist in the backend. This will return a record - in the `loaded.saved` state. The primary use-case for `store#push` is - to notify Ember Data about record updates that happen - outside of the normal adapter methods (for example - [SSE](http://dev.w3.org/html5/eventsource/) or [Web - Sockets](http://www.w3.org/TR/2009/WD-websockets-20091222/)). - - [pushPayload](#method_pushPayload) is a convenience wrapper for - `store#push` that will deserialize payloads if the - Serializer implements a `pushPayload` method. - - [update](#method_update) works like `push`, except it can handle - partial attributes without overwriting the existing record - properties. - - Note: When creating a new record using any of the above methods - Ember Data will update `DS.RecordArray`s such as those returned by - `store#all()`, `store#findAll()` or `store#filter()`. This means any - data bindings or computed properties that depend on the RecordArray - will automatically be synced to include the new or updated record - values. - - @class Store - @namespace DS - @extends Ember.Object - */ - Store = Ember.Object.extend({ - - /** - @method init - @private - */ - init: function() { - // internal bookkeeping; not observable - if (!RecordArrayManager) { RecordArrayManager = requireModule("ember-data/system/record_array_manager")["default"]; } - this.typeMaps = {}; - this.recordArrayManager = RecordArrayManager.create({ - store: this - }); - this._pendingSave = []; - //Used to keep track of all the find requests that need to be coalesced - this._pendingFetch = Map.create(); - }, - - /** - The adapter to use to communicate to a backend server or other persistence layer. - - This can be specified as an instance, class, or string. - - If you want to specify `App.CustomAdapter` as a string, do: - - ```js - adapter: 'custom' - ``` - - @property adapter - @default DS.RESTAdapter - @type {DS.Adapter|String} - */ - adapter: '-rest', - - /** - Returns a JSON representation of the record using a custom - type-specific serializer, if one exists. - - The available options are: - - * `includeId`: `true` if the record's ID should be included in - the JSON representation - - @method serialize - @private - @param {DS.Model} record the record to serialize - @param {Object} options an options hash - */ - serialize: function(record, options) { - return this.serializerFor(record.constructor.typeKey).serialize(record, options); - }, - - /** - This property returns the adapter, after resolving a possible - string key. - - If the supplied `adapter` was a class, or a String property - path resolved to a class, this property will instantiate the - class. - - This property is cacheable, so the same instance of a specified - adapter class should be used for the lifetime of the store. - - @property defaultAdapter - @private - @return DS.Adapter - */ - defaultAdapter: Ember.computed('adapter', function() { - var adapter = get(this, 'adapter'); - - Ember.assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name or a factory', !(adapter instanceof Adapter)); - - if (typeof adapter === 'string') { - adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:-rest'); - } - - if (DS.Adapter.detect(adapter)) { - adapter = adapter.create({ - container: this.container - }); - } - - return adapter; - }), - - // ..................... - // . CREATE NEW RECORD . - // ..................... - - /** - Create a new record in the current store. The properties passed - to this method are set on the newly created record. - - To create a new instance of `App.Post`: - - ```js - store.createRecord('post', { - title: "Rails is omakase" - }); - ``` - - @method createRecord - @param {String} type - @param {Object} properties a hash of properties to set on the - newly created record. - @return {DS.Model} record - */ - createRecord: function(typeName, inputProperties) { - var type = this.modelFor(typeName); - var properties = copy(inputProperties) || {}; - - // If the passed properties do not include a primary key, - // give the adapter an opportunity to generate one. Typically, - // client-side ID generators will use something like uuid.js - // to avoid conflicts. - - if (isNone(properties.id)) { - properties.id = this._generateId(type); - } - - // Coerce ID to a string - properties.id = coerceId(properties.id); - - var record = this.buildRecord(type, properties.id); - - // Move the record out of its initial `empty` state into - // the `loaded` state. - record.loadedData(); - - // Set the properties specified on the record. - record.setProperties(properties); - - return record; - }, - - /** - If possible, this method asks the adapter to generate an ID for - a newly created record. - - @method _generateId - @private - @param {String} type - @return {String} if the adapter can generate one, an ID - */ - _generateId: function(type) { - var adapter = this.adapterFor(type); - - if (adapter && adapter.generateIdForRecord) { - return adapter.generateIdForRecord(this); - } - - return null; - }, - - // ................. - // . DELETE RECORD . - // ................. - - /** - For symmetry, a record can be deleted via the store. - - Example - - ```javascript - var post = store.createRecord('post', { - title: "Rails is omakase" - }); - - store.deleteRecord(post); - ``` - - @method deleteRecord - @param {DS.Model} record - */ - deleteRecord: function(record) { - record.deleteRecord(); - }, - - /** - For symmetry, a record can be unloaded via the store. Only - non-dirty records can be unloaded. - - Example - - ```javascript - store.find('post', 1).then(function(post) { - store.unloadRecord(post); - }); - ``` - - @method unloadRecord - @param {DS.Model} record - */ - unloadRecord: function(record) { - record.unloadRecord(); - }, - - // ................ - // . FIND RECORDS . - // ................ - - /** - This is the main entry point into finding records. The first parameter to - this method is the model's name as a string. - - --- - - To find a record by ID, pass the `id` as the second parameter: - - ```javascript - store.find('person', 1); - ``` - - The `find` method will always return a **promise** that will be resolved - with the record. If the record was already in the store, the promise will - be resolved immediately. Otherwise, the store will ask the adapter's `find` - method to find the necessary data. - - The `find` method will always resolve its promise with the same object for - a given type and `id`. - - --- - - You can optionally `preload` specific attributes and relationships that you know of - by passing them as the third argument to find. - - For example, if your Ember route looks like `/posts/1/comments/2` and your API route - for the comment also looks like `/posts/1/comments/2` if you want to fetch the comment - without fetching the post you can pass in the post to the `find` call: - - ```javascript - store.find('comment', 2, {post: 1}); - ``` - - If you have access to the post model you can also pass the model itself: - - ```javascript - store.find('post', 1).then(function (myPostModel) { - store.find('comment', 2, {post: myPostModel}); - }); - ``` - - This way, your adapter's `find` or `buildURL` method will be able to look up the - relationship on the record and construct the nested URL without having to first - fetch the post. - - --- - - To find all records for a type, call `find` with no additional parameters: - - ```javascript - store.find('person'); - ``` - - This will ask the adapter's `findAll` method to find the records for the - given type, and return a promise that will be resolved once the server - returns the values. - - --- - - To find a record by a query, call `find` with a hash as the second - parameter: - - ```javascript - store.find('person', { page: 1 }); - ``` - - This will ask the adapter's `findQuery` method to find the records for - the query, and return a promise that will be resolved once the server - responds. - - @method find - @param {String or subclass of DS.Model} type - @param {Object|String|Integer|null} id - @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models - @return {Promise} promise - */ - find: function(type, id, preload) { - Ember.assert("You need to pass a type to the store's find method", arguments.length >= 1); - Ember.assert("You may not pass `" + id + "` as id to the store's find method", arguments.length === 1 || !Ember.isNone(id)); - - if (arguments.length === 1) { - return this.findAll(type); - } - - // We are passed a query instead of an id. - if (Ember.typeOf(id) === 'object') { - return this.findQuery(type, id); - } - - return this.findById(type, coerceId(id), preload); - }, - - /** - This method returns a fresh record for a given type and id combination. - - If a record is available for the given type/id combination, then it will fetch this record from the store then reload it. If there's no record corresponding in the store it will simply call store.find. - - @method fetch - @param {String or subclass of DS.Model} type - @param {Object|String|Integer|null} id - @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models - @return {Promise} promise - */ - fetch: function(type, id, preload) { - if (this.hasRecordForId(type, id)) { - return this.getById(type, id).reload(); - } else { - return this.find(type, id, preload); - } - }, - - /** - This method returns a record for a given type and id combination. - - @method findById - @private - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @param {Object} preload - optional set of attributes and relationships passed in either as IDs or as actual models - @return {Promise} promise - */ - findById: function(typeName, id, preload) { - - var type = this.modelFor(typeName); - var record = this.recordForId(type, id); - - return this._findByRecord(record, preload); - }, - - _findByRecord: function(record, preload) { - var fetchedRecord; - - if (preload) { - record._preloadData(preload); - } - - if (get(record, 'isEmpty')) { - fetchedRecord = this.scheduleFetch(record); - //TODO double check about reloading - } else if (get(record, 'isLoading')){ - fetchedRecord = record._loadingPromise; - } - - return promiseObject(fetchedRecord || record, "DS: Store#findByRecord " + record.typeKey + " with id: " + get(record, 'id')); - }, - - /** - This method makes a series of requests to the adapter's `find` method - and returns a promise that resolves once they are all loaded. - - @private - @method findByIds - @param {String} type - @param {Array} ids - @return {Promise} promise - */ - findByIds: function(type, ids) { - var store = this; - - return promiseArray(Ember.RSVP.all(map(ids, function(id) { - return store.findById(type, id); - })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete")); - }, - - /** - This method is called by `findById` if it discovers that a particular - type/id pair hasn't been loaded yet to kick off a request to the - adapter. - - @method fetchRecord - @private - @param {DS.Model} record - @return {Promise} promise - */ - fetchRecord: function(record) { - var type = record.constructor; - var id = get(record, 'id'); - var adapter = this.adapterFor(type); - - Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", typeof adapter.find === 'function'); - - var promise = _find(adapter, this, type, id, record); - return promise; - }, - - scheduleFetchMany: function(records) { - return Promise.all(map(records, this.scheduleFetch, this)); - }, - - scheduleFetch: function(record) { - var type = record.constructor; - if (isNone(record)) { return null; } - if (record._loadingPromise) { return record._loadingPromise; } - - var resolver = Ember.RSVP.defer('Fetching ' + type + 'with id: ' + record.get('id')); - var recordResolverPair = { - record: record, - resolver: resolver - }; - var promise = resolver.promise; - - record.loadingData(promise); - - if (!this._pendingFetch.get(type)){ - this._pendingFetch.set(type, [recordResolverPair]); - } else { - this._pendingFetch.get(type).push(recordResolverPair); - } - Ember.run.scheduleOnce('afterRender', this, this.flushAllPendingFetches); - - return promise; - }, - - flushAllPendingFetches: function(){ - if (this.isDestroyed || this.isDestroying) { - return; - } - - this._pendingFetch.forEach(this._flushPendingFetchForType, this); - this._pendingFetch = Map.create(); - }, - - _flushPendingFetchForType: function (recordResolverPairs, type) { - var store = this; - var adapter = store.adapterFor(type); - var shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests; - var records = Ember.A(recordResolverPairs).mapBy('record'); - - function _fetchRecord(recordResolverPair) { - recordResolverPair.resolver.resolve(store.fetchRecord(recordResolverPair.record)); - } - - function resolveFoundRecords(records) { - forEach(records, function(record){ - var pair = Ember.A(recordResolverPairs).findBy('record', record); - if (pair){ - var resolver = pair.resolver; - resolver.resolve(record); - } - }); - } - - function makeMissingRecordsRejector(requestedRecords) { - return function rejectMissingRecords(resolvedRecords) { - var missingRecords = requestedRecords.without(resolvedRecords); - rejectRecords(missingRecords); - }; - } - - function makeRecordsRejector(records) { - return function (error) { - rejectRecords(records, error); - }; - } - - function rejectRecords(records, error) { - forEach(records, function(record){ - var pair = Ember.A(recordResolverPairs).findBy('record', record); - if (pair){ - var resolver = pair.resolver; - resolver.reject(error); - } - }); - } - - if (recordResolverPairs.length === 1) { - _fetchRecord(recordResolverPairs[0]); - } else if (shouldCoalesce) { - var groups = adapter.groupRecordsForFindMany(this, records); - forEach(groups, function (groupOfRecords) { - var requestedRecords = Ember.A(groupOfRecords); - var ids = requestedRecords.mapBy('id'); - if (ids.length > 1) { - _findMany(adapter, store, type, ids, requestedRecords). - then(resolveFoundRecords). - then(makeMissingRecordsRejector(requestedRecords)). - then(null, makeRecordsRejector(requestedRecords)); - } else if (ids.length === 1) { - var pair = Ember.A(recordResolverPairs).findBy('record', groupOfRecords[0]); - _fetchRecord(pair); - } else { - Ember.assert("You cannot return an empty array from adapter's method groupRecordsForFindMany", false); - } - }); - } else { - forEach(recordResolverPairs, _fetchRecord); - } - }, - - /** - Get a record by a given type and ID without triggering a fetch. - - This method will synchronously return the record if it is available in the store, - otherwise it will return `null`. A record is available if it has been fetched earlier, or - pushed manually into the store. - - _Note: This is an synchronous method and does not return a promise._ - - ```js - var post = store.getById('post', 1); - - post.get('id'); // 1 - ``` - - @method getById - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {DS.Model|null} record - */ - getById: function(type, id) { - if (this.hasRecordForId(type, id)) { - return this.recordForId(type, id); - } else { - return null; - } - }, - - /** - This method is called by the record's `reload` method. - - This method calls the adapter's `find` method, which returns a promise. When - **that** promise resolves, `reloadRecord` will resolve the promise returned - by the record's `reload`. - - @method reloadRecord - @private - @param {DS.Model} record - @return {Promise} promise - */ - reloadRecord: function(record) { - var type = record.constructor; - var adapter = this.adapterFor(type); - var id = get(record, 'id'); - - Ember.assert("You cannot reload a record without an ID", id); - Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to reload a record but your adapter does not implement `find`", typeof adapter.find === 'function'); - - return this.scheduleFetch(record); - }, - - /** - Returns true if a record for a given type and ID is already loaded. - - @method hasRecordForId - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {Boolean} - */ - hasRecordForId: function(typeName, inputId) { - var type = this.modelFor(typeName); - var id = coerceId(inputId); - return !!this.typeMapFor(type).idToRecord[id]; - }, - - /** - Returns id record for a given type and ID. If one isn't already loaded, - it builds a new record and leaves it in the `empty` state. - - @method recordForId - @private - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {DS.Model} record - */ - recordForId: function(typeName, inputId) { - var type = this.modelFor(typeName); - var id = coerceId(inputId); - var idToRecord = this.typeMapFor(type).idToRecord; - var record = idToRecord[id]; - - if (!record || !idToRecord[id]) { - record = this.buildRecord(type, id); - } - - return record; - }, - - /** - @method findMany - @private - @param {DS.Model} owner - @param {Array} records - @param {String or subclass of DS.Model} type - @param {Resolver} resolver - @return {DS.ManyArray} records - */ - findMany: function(records) { - var store = this; - return Promise.all(map(records, function(record) { - return store._findByRecord(record); - })); - }, - - - /** - If a relationship was originally populated by the adapter as a link - (as opposed to a list of IDs), this method is called when the - relationship is fetched. - - The link (which is usually a URL) is passed through unchanged, so the - adapter can make whatever request it wants. - - The usual use-case is for the server to register a URL as a link, and - then use that URL in the future to make a request for the relationship. - - @method findHasMany - @private - @param {DS.Model} owner - @param {any} link - @param {String or subclass of DS.Model} type - @return {Promise} promise - */ - findHasMany: function(owner, link, type) { - var adapter = this.adapterFor(owner.constructor); - - Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter); - Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", typeof adapter.findHasMany === 'function'); - - return _findHasMany(adapter, this, owner, link, type); - }, - - /** - @method findBelongsTo - @private - @param {DS.Model} owner - @param {any} link - @param {Relationship} relationship - @return {Promise} promise - */ - findBelongsTo: function(owner, link, relationship) { - var adapter = this.adapterFor(owner.constructor); - - Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter); - Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", typeof adapter.findBelongsTo === 'function'); - - return _findBelongsTo(adapter, this, owner, link, relationship); - }, - - /** - This method delegates a query to the adapter. This is the one place where - adapter-level semantics are exposed to the application. - - Exposing queries this way seems preferable to creating an abstract query - language for all server-side queries, and then require all adapters to - implement them. - - This method returns a promise, which is resolved with a `RecordArray` - once the server returns. - - @method findQuery - @private - @param {String or subclass of DS.Model} type - @param {any} query an opaque query to be used by the adapter - @return {Promise} promise - */ - findQuery: function(typeName, query) { - var type = this.modelFor(typeName); - var array = this.recordArrayManager - .createAdapterPopulatedRecordArray(type, query); - - var adapter = this.adapterFor(type); - - Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", typeof adapter.findQuery === 'function'); - - return promiseArray(_findQuery(adapter, this, type, query, array)); - }, - - /** - This method returns an array of all records adapter can find. - It triggers the adapter's `findAll` method to give it an opportunity to populate - the array with records of that type. - - @method findAll - @private - @param {String or subclass of DS.Model} type - @return {DS.AdapterPopulatedRecordArray} - */ - findAll: function(typeName) { - var type = this.modelFor(typeName); - - return this.fetchAll(type, this.all(type)); - }, - - /** - @method fetchAll - @private - @param {DS.Model} type - @param {DS.RecordArray} array - @return {Promise} promise - */ - fetchAll: function(type, array) { - var adapter = this.adapterFor(type); - var sinceToken = this.typeMapFor(type).metadata.since; - - set(array, 'isUpdating', true); - - Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to load all records but your adapter does not implement `findAll`", typeof adapter.findAll === 'function'); - - return promiseArray(_findAll(adapter, this, type, sinceToken)); - }, - - /** - @method didUpdateAll - @param {DS.Model} type - */ - didUpdateAll: function(type) { - var findAllCache = this.typeMapFor(type).findAllCache; - set(findAllCache, 'isUpdating', false); - }, - - /** - This method returns a filtered array that contains all of the - known records for a given type in the store. - - Note that because it's just a filter, the result will contain any - locally created records of the type, however, it will not make a - request to the backend to retrieve additional records. If you - would like to request all the records from the backend please use - [store.find](#method_find). - - Also note that multiple calls to `all` for a given type will always - return the same `RecordArray`. - - Example - - ```javascript - var localPosts = store.all('post'); - ``` - - @method all - @param {String or subclass of DS.Model} type - @return {DS.RecordArray} - */ - all: function(typeName) { - var type = this.modelFor(typeName); - var typeMap = this.typeMapFor(type); - var findAllCache = typeMap.findAllCache; - - if (findAllCache) { return findAllCache; } - - var array = this.recordArrayManager.createRecordArray(type); - - typeMap.findAllCache = array; - return array; - }, - - - /** - This method unloads all of the known records for a given type. - - ```javascript - store.unloadAll('post'); - ``` - - @method unloadAll - @param {String or subclass of DS.Model} type - */ - unloadAll: function(type) { - var modelType = this.modelFor(type); - var typeMap = this.typeMapFor(modelType); - var records = typeMap.records.slice(); - var record; - - for (var i = 0; i < records.length; i++) { - record = records[i]; - record.unloadRecord(); - record.destroy(); // maybe within unloadRecord - } - - typeMap.findAllCache = null; - }, - - /** - Takes a type and filter function, and returns a live RecordArray that - remains up to date as new records are loaded into the store or created - locally. - - The filter function takes a materialized record, and returns true - if the record should be included in the filter and false if it should - not. - - Example - - ```javascript - store.filter('post', function(post) { - return post.get('unread'); - }); - ``` - - The filter function is called once on all records for the type when - it is created, and then once on each newly loaded or created record. - - If any of a record's properties change, or if it changes state, the - filter function will be invoked again to determine whether it should - still be in the array. - - Optionally you can pass a query, which is the equivalent of calling - [find](#method_find) with that same query, to fetch additional records - from the server. The results returned by the server could then appear - in the filter if they match the filter function. - - The query itself is not used to filter records, it's only sent to your - server for you to be able to do server-side filtering. The filter - function will be applied on the returned results regardless. - - Example - - ```javascript - store.filter('post', { unread: true }, function(post) { - return post.get('unread'); - }).then(function(unreadPosts) { - unreadPosts.get('length'); // 5 - var unreadPost = unreadPosts.objectAt(0); - unreadPost.set('unread', false); - unreadPosts.get('length'); // 4 - }); - ``` - - @method filter - @param {String or subclass of DS.Model} type - @param {Object} query optional query - @param {Function} filter - @return {DS.PromiseArray} - */ - filter: function(type, query, filter) { - var promise; - var length = arguments.length; - var array; - var hasQuery = length === 3; - - // allow an optional server query - if (hasQuery) { - promise = this.findQuery(type, query); - } else if (arguments.length === 2) { - filter = query; - } - - type = this.modelFor(type); - - if (hasQuery) { - array = this.recordArrayManager.createFilteredRecordArray(type, filter, query); - } else { - array = this.recordArrayManager.createFilteredRecordArray(type, filter); - } - - promise = promise || Promise.cast(array); - - - return promiseArray(promise.then(function() { - return array; - }, null, "DS: Store#filter of " + type)); - }, - - /** - This method returns if a certain record is already loaded - in the store. Use this function to know beforehand if a find() - will result in a request or that it will be a cache hit. - - Example - - ```javascript - store.recordIsLoaded('post', 1); // false - store.find('post', 1).then(function() { - store.recordIsLoaded('post', 1); // true - }); - ``` - - @method recordIsLoaded - @param {String or subclass of DS.Model} type - @param {string} id - @return {boolean} - */ - recordIsLoaded: function(type, id) { - if (!this.hasRecordForId(type, id)) { return false; } - return !get(this.recordForId(type, id), 'isEmpty'); - }, - - /** - This method returns the metadata for a specific type. - - @method metadataFor - @param {String or subclass of DS.Model} type - @return {object} - */ - metadataFor: function(type) { - type = this.modelFor(type); - return this.typeMapFor(type).metadata; - }, - - // ............ - // . UPDATING . - // ............ - - /** - If the adapter updates attributes or acknowledges creation - or deletion, the record will notify the store to update its - membership in any filters. - To avoid thrashing, this method is invoked only once per - - run loop per record. - - @method dataWasUpdated - @private - @param {Class} type - @param {DS.Model} record - */ - dataWasUpdated: function(type, record) { - this.recordArrayManager.recordDidChange(record); - }, - - // .............. - // . PERSISTING . - // .............. - - /** - This method is called by `record.save`, and gets passed a - resolver for the promise that `record.save` returns. - - It schedules saving to happen at the end of the run loop. - - @method scheduleSave - @private - @param {DS.Model} record - @param {Resolver} resolver - */ - scheduleSave: function(record, resolver) { - record.adapterWillCommit(); - this._pendingSave.push([record, resolver]); - once(this, 'flushPendingSave'); - }, - - /** - This method is called at the end of the run loop, and - flushes any records passed into `scheduleSave` - - @method flushPendingSave - @private - */ - flushPendingSave: function() { - var pending = this._pendingSave.slice(); - this._pendingSave = []; - - forEach(pending, function(tuple) { - var record = tuple[0], resolver = tuple[1]; - var adapter = this.adapterFor(record.constructor); - var operation; - - if (get(record, 'currentState.stateName') === 'root.deleted.saved') { - return resolver.resolve(record); - } else if (get(record, 'isNew')) { - operation = 'createRecord'; - } else if (get(record, 'isDeleted')) { - operation = 'deleteRecord'; - } else { - operation = 'updateRecord'; - } - - resolver.resolve(_commit(adapter, this, operation, record)); - }, this); - }, - - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is resolved. - - If the data provides a server-generated ID, it will - update the record and the store's indexes. - - @method didSaveRecord - @private - @param {DS.Model} record the in-flight record - @param {Object} data optional data (see above) - */ - didSaveRecord: function(record, data) { - if (data) { - // normalize relationship IDs into records - data = normalizeRelationships(this, record.constructor, data, record); - setupRelationships(this, record, data); - - this.updateId(record, data); - } - - record.adapterDidCommit(data); - }, - - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected with a `DS.InvalidError`. - - @method recordWasInvalid - @private - @param {DS.Model} record - @param {Object} errors - */ - recordWasInvalid: function(record, errors) { - record.adapterDidInvalidate(errors); - }, - - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected (with anything other than a `DS.InvalidError`). - - @method recordWasError - @private - @param {DS.Model} record - */ - recordWasError: function(record) { - record.adapterDidError(); - }, - - /** - When an adapter's `createRecord`, `updateRecord` or `deleteRecord` - resolves with data, this method extracts the ID from the supplied - data. - - @method updateId - @private - @param {DS.Model} record - @param {Object} data - */ - updateId: function(record, data) { - var oldId = get(record, 'id'); - var id = coerceId(data.id); - - Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); - - this.typeMapFor(record.constructor).idToRecord[id] = record; - - set(record, 'id', id); - }, - - /** - Returns a map of IDs to client IDs for a given type. - - @method typeMapFor - @private - @param {subclass of DS.Model} type - @return {Object} typeMap - */ - typeMapFor: function(type) { - var typeMaps = get(this, 'typeMaps'); - var guid = Ember.guidFor(type); - var typeMap; - - typeMap = typeMaps[guid]; - - if (typeMap) { return typeMap; } - - typeMap = { - idToRecord: Ember.create(null), - records: [], - metadata: Ember.create(null), - type: type - }; - - typeMaps[guid] = typeMap; - - return typeMap; - }, - - // ................ - // . LOADING DATA . - // ................ - - /** - This internal method is used by `push`. - - @method _load - @private - @param {String or subclass of DS.Model} type - @param {Object} data - @param {Boolean} partial the data should be merged into - the existing data, not replace it. - */ - _load: function(type, data, partial) { - var id = coerceId(data.id); - var record = this.recordForId(type, id); - - record.setupData(data, partial); - this.recordArrayManager.recordDidChange(record); - - return record; - }, - - /** - Returns a model class for a particular key. Used by - methods that take a type key (like `find`, `createRecord`, - etc.) - - @method modelFor - @param {String or subclass of DS.Model} key - @return {subclass of DS.Model} - */ - modelFor: function(key) { - var factory; - - if (typeof key === 'string') { - factory = this.modelFactoryFor(key); - if (!factory) { - throw new Ember.Error("No model was found for '" + key + "'"); - } - factory.typeKey = factory.typeKey || this._normalizeTypeKey(key); - } else { - // A factory already supplied. Ensure it has a normalized key. - factory = key; - if (factory.typeKey) { - factory.typeKey = this._normalizeTypeKey(factory.typeKey); - } - } - - factory.store = this; - return factory; - }, - - modelFactoryFor: function(key){ - return this.container.lookupFactory('model:' + key); - }, - - /** - Push some data for a given type into the store. - - This method expects normalized data: - - * The ID is a key named `id` (an ID is mandatory) - * The names of attributes are the ones you used in - your model's `DS.attr`s. - * Your relationships must be: - * represented as IDs or Arrays of IDs - * represented as model instances - * represented as URLs, under the `links` key - - For this model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr(), - lastName: DS.attr(), - - children: DS.hasMany('person') - }); - ``` - - To represent the children as IDs: - - ```js - { - id: 1, - firstName: "Tom", - lastName: "Dale", - children: [1, 2, 3] - } - ``` - - To represent the children relationship as a URL: - - ```js - { - id: 1, - firstName: "Tom", - lastName: "Dale", - links: { - children: "/people/1/children" - } - } - ``` - - If you're streaming data or implementing an adapter, - make sure that you have converted the incoming data - into this form. - - This method can be used both to push in brand new - records, as well as to update existing records. - - @method push - @param {String or subclass of DS.Model} type - @param {Object} data - @return {DS.Model} the record that was created or - updated. - */ - push: function(typeName, data, _partial) { - // _partial is an internal param used by `update`. - // If passed, it means that the data should be - // merged into the existing data, not replace it. - Ember.assert("Expected an object as `data` in a call to `push`/`update` for " + typeName + " , but was " + data, Ember.typeOf(data) === 'object'); - Ember.assert("You must include an `id` for " + typeName + " in an object passed to `push`/`update`", data.id != null && data.id !== ''); - - var type = this.modelFor(typeName); - var filter = Ember.EnumerableUtils.filter; - - // If the payload contains relationships that are specified as - // IDs, normalizeRelationships will convert them into DS.Model instances - // (possibly unloaded) before we push the payload into the - // store. - - data = normalizeRelationships(this, type, data); - - Ember.warn("The payload for '" + typeName + "' contains these unknown keys: " + - Ember.inspect(filter(Ember.keys(data), function(key) { - return !get(type, 'fields').has(key) && key !== 'id' && key !== 'links'; - })) + ". Make sure they've been defined in your model.", - filter(Ember.keys(data), function(key) { - return !get(type, 'fields').has(key) && key !== 'id' && key !== 'links'; - }).length === 0 - ); - - // Actually load the record into the store. - - this._load(type, data, _partial); - - var record = this.recordForId(type, data.id); - - // Now that the pushed record as well as any related records - // are in the store, create the data structures used to track - // relationships. - setupRelationships(this, record, data); - - return record; - }, - - /** - Push some raw data into the store. - - This method can be used both to push in brand new - records, as well as to update existing records. You - can push in more than one type of object at once. - All objects should be in the format expected by the - serializer. - - ```js - App.ApplicationSerializer = DS.ActiveModelSerializer; - - var pushData = { - posts: [ - {id: 1, post_title: "Great post", comment_ids: [2]} - ], - comments: [ - {id: 2, comment_body: "Insightful comment"} - ] - } - - store.pushPayload(pushData); - ``` - - By default, the data will be deserialized using a default - serializer (the application serializer if it exists). - - Alternatively, `pushPayload` will accept a model type which - will determine which serializer will process the payload. - However, the serializer itself (processing this data via - `normalizePayload`) will not know which model it is - deserializing. - - ```js - App.ApplicationSerializer = DS.ActiveModelSerializer; - App.PostSerializer = DS.JSONSerializer; - store.pushPayload('comment', pushData); // Will use the ApplicationSerializer - store.pushPayload('post', pushData); // Will use the PostSerializer - ``` - - @method pushPayload - @param {String} type Optionally, a model used to determine which serializer will be used - @param {Object} payload - */ - pushPayload: function (type, inputPayload) { - var serializer; - var payload; - if (!inputPayload) { - payload = type; - serializer = defaultSerializer(this.container); - Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", typeof serializer.pushPayload === 'function'); - } else { - payload = inputPayload; - serializer = this.serializerFor(type); - } - serializer.pushPayload(this, payload); - }, - - /** - `normalize` converts a json payload into the normalized form that - [push](#method_push) expects. - - Example - - ```js - socket.on('message', function(message) { - var modelName = message.model; - var data = message.data; - store.push(modelName, store.normalize(modelName, data)); - }); - ``` - - @method normalize - @param {String} type The name of the model type for this payload - @param {Object} payload - @return {Object} The normalized payload - */ - normalize: function (type, payload) { - var serializer = this.serializerFor(type); - var model = this.modelFor(type); - return serializer.normalize(model, payload); - }, - - /** - Update existing records in the store. Unlike [push](#method_push), - update will merge the new data properties with the existing - properties. This makes it safe to use with a subset of record - attributes. This method expects normalized data. - - `update` is useful if your app broadcasts partial updates to - records. - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string') - }); - - store.get('person', 1).then(function(tom) { - tom.get('firstName'); // Tom - tom.get('lastName'); // Dale - - var updateEvent = {id: 1, firstName: "TomHuda"}; - store.update('person', updateEvent); - - tom.get('firstName'); // TomHuda - tom.get('lastName'); // Dale - }); - ``` - - @method update - @param {String} type - @param {Object} data - @return {DS.Model} the record that was updated. - */ - update: function(type, data) { - return this.push(type, data, true); - }, - - /** - If you have an Array of normalized data to push, - you can call `pushMany` with the Array, and it will - call `push` repeatedly for you. - - @method pushMany - @param {String or subclass of DS.Model} type - @param {Array} datas - @return {Array} - */ - pushMany: function(type, datas) { - var length = datas.length; - var result = new Array(length); - - for (var i = 0; i < length; i++) { - result[i] = this.push(type, datas[i]); - } - - return result; - }, - - /** - If you have some metadata to set for a type - you can call `metaForType`. - - @method metaForType - @param {String or subclass of DS.Model} type - @param {Object} metadata - */ - metaForType: function(typeName, metadata) { - var type = this.modelFor(typeName); - - Ember.merge(this.typeMapFor(type).metadata, metadata); - }, - - /** - Build a brand new record for a given type, ID, and - initial data. - - @method buildRecord - @private - @param {subclass of DS.Model} type - @param {String} id - @param {Object} data - @return {DS.Model} record - */ - buildRecord: function(type, id, data) { - var typeMap = this.typeMapFor(type); - var idToRecord = typeMap.idToRecord; - - Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]); - Ember.assert("`" + Ember.inspect(type)+ "` does not appear to be an ember-data model", (typeof type._create === 'function') ); - - // lookupFactory should really return an object that creates - // instances with the injections applied - var record = type._create({ - id: id, - store: this, - container: this.container - }); - - if (data) { - record.setupData(data); - } - - // if we're creating an item, this process will be done - // later, once the object has been persisted. - if (id) { - idToRecord[id] = record; - } - - typeMap.records.push(record); - - return record; - }, - - // ............... - // . DESTRUCTION . - // ............... - - /** - When a record is destroyed, this un-indexes it and - removes it from any record arrays so it can be GCed. - - @method dematerializeRecord - @private - @param {DS.Model} record - */ - dematerializeRecord: function(record) { - var type = record.constructor; - var typeMap = this.typeMapFor(type); - var id = get(record, 'id'); - - record.updateRecordArrays(); - - if (id) { - delete typeMap.idToRecord[id]; - } - - var loc = indexOf(typeMap.records, record); - typeMap.records.splice(loc, 1); - }, - - // ...................... - // . PER-TYPE ADAPTERS - // ...................... - - /** - Returns the adapter for a given type. - - @method adapterFor - @private - @param {subclass of DS.Model} type - @return DS.Adapter - */ - adapterFor: function(type) { - var container = this.container, adapter; - - if (container) { - adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application'); - } - - return adapter || get(this, 'defaultAdapter'); - }, - - // .............................. - // . RECORD CHANGE NOTIFICATION . - // .............................. - - /** - Returns an instance of the serializer for a given type. For - example, `serializerFor('person')` will return an instance of - `App.PersonSerializer`. - - If no `App.PersonSerializer` is found, this method will look - for an `App.ApplicationSerializer` (the default serializer for - your entire application). - - If no `App.ApplicationSerializer` is found, it will fall back - to an instance of `DS.JSONSerializer`. - - @method serializerFor - @private - @param {String} type the record to serialize - @return {DS.Serializer} - */ - serializerFor: function(type) { - type = this.modelFor(type); - var adapter = this.adapterFor(type); - - return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer); - }, - - willDestroy: function() { - var typeMaps = this.typeMaps; - var keys = Ember.keys(typeMaps); - - var types = map(keys, byType); - - this.recordArrayManager.destroy(); - - forEach(types, this.unloadAll, this); - - function byType(entry) { - return typeMaps[entry]['type']; - } - - }, - - /** - All typeKeys are camelCase internally. Changing this function may - require changes to other normalization hooks (such as typeForRoot). - - @method _normalizeTypeKey - @private - @param {String} type - @return {String} if the adapter can generate one, an ID - */ - _normalizeTypeKey: function(key) { - return camelize(singularize(key)); - } - }); - - - function normalizeRelationships(store, type, data, record) { - type.eachRelationship(function(key, relationship) { - var kind = relationship.kind; - var value = data[key]; - if (kind === 'belongsTo') { - deserializeRecordId(store, data, key, relationship, value); - } else if (kind === 'hasMany') { - deserializeRecordIds(store, data, key, relationship, value); - } - }); - - return data; - } - - function deserializeRecordId(store, data, key, relationship, id) { - if (!Model) { Model = requireModule("ember-data/system/model")["Model"]; } - if (isNone(id) || id instanceof Model) { - return; - } - Ember.assert("A " + relationship.parentType + " record was pushed into the store with the value of " + key + " being " + Ember.inspect(id) + ", but " + key + " is a belongsTo relationship so the value must not be an array. You should probably check your data payload or serializer.", !Ember.isArray(id)); - - var type; - - if (typeof id === 'number' || typeof id === 'string') { - type = typeFor(relationship, key, data); - data[key] = store.recordForId(type, id); - } else if (typeof id === 'object') { - // polymorphic - data[key] = store.recordForId(id.type, id.id); - } - } - - function typeFor(relationship, key, data) { - if (relationship.options.polymorphic) { - return data[key + "Type"]; - } else { - return relationship.type; - } - } - - function deserializeRecordIds(store, data, key, relationship, ids) { - if (isNone(ids)) { - return; - } - - Ember.assert("A " + relationship.parentType + " record was pushed into the store with the value of " + key + " being '" + Ember.inspect(ids) + "', but " + key + " is a hasMany relationship so the value must be an array. You should probably check your data payload or serializer.", Ember.isArray(ids)); - for (var i=0, l=ids.length; i<l; i++) { - deserializeRecordId(store, ids, i, relationship, ids[i]); - } - } - - // Delegation to the adapter and promise management - - - function serializerFor(container, type, defaultSerializer) { - return container.lookup('serializer:'+type) || - container.lookup('serializer:application') || - container.lookup('serializer:' + defaultSerializer) || - container.lookup('serializer:-default'); - } - - function defaultSerializer(container) { - return container.lookup('serializer:application') || - container.lookup('serializer:-default'); - } - - function serializerForAdapter(adapter, type) { - var serializer = adapter.serializer; - var defaultSerializer = adapter.defaultSerializer; - var container = adapter.container; - - if (container && serializer === undefined) { - serializer = serializerFor(container, type.typeKey, defaultSerializer); - } - - if (serializer === null || serializer === undefined) { - serializer = { - extract: function(store, type, payload) { return payload; } - }; - } - - return serializer; - } - - function _objectIsAlive(object) { - return !(get(object, "isDestroyed") || get(object, "isDestroying")); - } - - function _guard(promise, test) { - var guarded = promise['finally'](function() { - if (!test()) { - guarded._subscribers.length = 0; - } - }); - - return guarded; - } - - function _bind(fn) { - var args = Array.prototype.slice.call(arguments, 1); - - return function() { - return fn.apply(undefined, args); - }; - } - - function _find(adapter, store, type, id, record) { - var promise = adapter.find(store, type, id, record); - var serializer = serializerForAdapter(adapter, type); - var label = "DS: Handle Adapter#find of " + type + " with id: " + id; - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - - return promise.then(function(adapterPayload) { - Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", adapterPayload); - var payload = serializer.extract(store, type, adapterPayload, id, 'find'); - - return store.push(type, payload); - }, function(error) { - var record = store.getById(type, id); - if (record) { - record.notFound(); - } - throw error; - }, "DS: Extract payload of '" + type + "'"); - } - - - function _findMany(adapter, store, type, ids, records) { - var promise = adapter.findMany(store, type, ids, records); - var serializer = serializerForAdapter(adapter, type); - var label = "DS: Handle Adapter#findMany of " + type; - - if (promise === undefined) { - throw new Error('adapter.findMany returned undefined, this was very likely a mistake'); - } - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - - return promise.then(function(adapterPayload) { - var payload = serializer.extract(store, type, adapterPayload, null, 'findMany'); - - Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - - return store.pushMany(type, payload); - }, null, "DS: Extract payload of " + type); - } - - function _findHasMany(adapter, store, record, link, relationship) { - var promise = adapter.findHasMany(store, record, link, relationship); - var serializer = serializerForAdapter(adapter, relationship.type); - var label = "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type; - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - promise = _guard(promise, _bind(_objectIsAlive, record)); - - return promise.then(function(adapterPayload) { - var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findHasMany'); - - Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - - var records = store.pushMany(relationship.type, payload); - return records; - }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type); - } - - function _findBelongsTo(adapter, store, record, link, relationship) { - var promise = adapter.findBelongsTo(store, record, link, relationship); - var serializer = serializerForAdapter(adapter, relationship.type); - var label = "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type; - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - promise = _guard(promise, _bind(_objectIsAlive, record)); - - return promise.then(function(adapterPayload) { - var payload = serializer.extract(store, relationship.type, adapterPayload, null, 'findBelongsTo'); - - if (!payload) { - return null; - } - - var record = store.push(relationship.type, payload); - return record; - }, null, "DS: Extract payload of " + record + " : " + relationship.type); - } - - function _findAll(adapter, store, type, sinceToken) { - var promise = adapter.findAll(store, type, sinceToken); - var serializer = serializerForAdapter(adapter, type); - var label = "DS: Handle Adapter#findAll of " + type; - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - - return promise.then(function(adapterPayload) { - var payload = serializer.extract(store, type, adapterPayload, null, 'findAll'); - - Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - - store.pushMany(type, payload); - store.didUpdateAll(type); - return store.all(type); - }, null, "DS: Extract payload of findAll " + type); - } - - function _findQuery(adapter, store, type, query, recordArray) { - var promise = adapter.findQuery(store, type, query, recordArray); - var serializer = serializerForAdapter(adapter, type); - var label = "DS: Handle Adapter#findQuery of " + type; - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - - return promise.then(function(adapterPayload) { - var payload = serializer.extract(store, type, adapterPayload, null, 'findQuery'); - - Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); - - recordArray.load(payload); - return recordArray; - }, null, "DS: Extract payload of findQuery " + type); - } - - function _commit(adapter, store, operation, record) { - var type = record.constructor; - var promise = adapter[operation](store, type, record); - var serializer = serializerForAdapter(adapter, type); - var label = "DS: Extract and notify about " + operation + " completion of " + record; - - Ember.assert("Your adapter's '" + operation + "' method must return a value, but it returned `undefined", promise !==undefined); - - promise = Promise.cast(promise, label); - promise = _guard(promise, _bind(_objectIsAlive, store)); - promise = _guard(promise, _bind(_objectIsAlive, record)); - - return promise.then(function(adapterPayload) { - var payload; - - if (adapterPayload) { - payload = serializer.extract(store, type, adapterPayload, get(record, 'id'), operation); - } else { - payload = adapterPayload; - } - - store.didSaveRecord(record, payload); - return record; - }, function(reason) { - if (reason instanceof InvalidError) { - var errors = serializer.extractErrors(store, type, reason.errors, get(record, 'id')); - store.recordWasInvalid(record, errors); - reason = new InvalidError(errors); - } else { - store.recordWasError(record, reason); - } - - throw reason; - }, label); - } - - function setupRelationships(store, record, data) { - var type = record.constructor; - - type.eachRelationship(function(key, descriptor) { - var kind = descriptor.kind; - var value = data[key]; - var relationship = record._relationships[key]; - - if (data.links && data.links[key]) { - relationship.updateLink(data.links[key]); - } - - if (kind === 'belongsTo') { - if (value === undefined) { - return; - } - relationship.setRecord(value); - } else if (kind === 'hasMany' && value) { - relationship.updateRecordsFromAdapter(value); - } - }); - } - - __exports__.Store = Store; - __exports__["default"] = Store; - }); -enifed("ember-data/transforms", - ["ember-data/transforms/base","ember-data/transforms/number","ember-data/transforms/date","ember-data/transforms/string","ember-data/transforms/boolean","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; - var NumberTransform = __dependency2__["default"]; - var DateTransform = __dependency3__["default"]; - var StringTransform = __dependency4__["default"]; - var BooleanTransform = __dependency5__["default"]; - - __exports__.Transform = Transform; - __exports__.NumberTransform = NumberTransform; - __exports__.DateTransform = DateTransform; - __exports__.StringTransform = StringTransform; - __exports__.BooleanTransform = BooleanTransform; - }); -enifed("ember-data/transforms/base", - ["exports"], - function(__exports__) { - "use strict"; - /** - The `DS.Transform` class is used to serialize and deserialize model - attributes when they are saved or loaded from an - adapter. Subclassing `DS.Transform` is useful for creating custom - attributes. All subclasses of `DS.Transform` must implement a - `serialize` and a `deserialize` method. - - Example - - ```javascript - // Converts centigrade in the JSON to fahrenheit in the app - App.TemperatureTransform = DS.Transform.extend({ - deserialize: function(serialized) { - return (serialized * 1.8) + 32; - }, - serialize: function(deserialized) { - return (deserialized - 32) / 1.8; - } - }); - ``` - - Usage - - ```javascript - var attr = DS.attr; - App.Requirement = DS.Model.extend({ - name: attr('string'), - temperature: attr('temperature') - }); - ``` - - @class Transform - @namespace DS - */ - __exports__["default"] = Ember.Object.extend({ - /** - When given a deserialized value from a record attribute this - method must return the serialized value. - - Example - - ```javascript - serialize: function(deserialized) { - return Ember.isEmpty(deserialized) ? null : Number(deserialized); - } - ``` - - @method serialize - @param {mixed} deserialized The deserialized value - @return {mixed} The serialized value - */ - serialize: Ember.required(), - - /** - When given a serialize value from a JSON object this method must - return the deserialized value for the record attribute. - - Example - - ```javascript - deserialize: function(serialized) { - return empty(serialized) ? null : Number(serialized); - } - ``` - - @method deserialize - @param {mixed} serialized The serialized value - @return {mixed} The deserialized value - */ - deserialize: Ember.required() - }); - }); -enifed("ember-data/transforms/boolean", - ["ember-data/transforms/base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; - - /** - The `DS.BooleanTransform` class is used to serialize and deserialize - boolean attributes on Ember Data record objects. This transform is - used when `boolean` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. - - Usage - - ```javascript - var attr = DS.attr; - App.User = DS.Model.extend({ - isAdmin: attr('boolean'), - name: attr('string'), - email: attr('string') - }); - ``` - - @class BooleanTransform - @extends DS.Transform - @namespace DS - */ - __exports__["default"] = Transform.extend({ - deserialize: function(serialized) { - var type = typeof serialized; - - if (type === "boolean") { - return serialized; - } else if (type === "string") { - return serialized.match(/^true$|^t$|^1$/i) !== null; - } else if (type === "number") { - return serialized === 1; - } else { - return false; - } - }, - - serialize: function(deserialized) { - return Boolean(deserialized); - } - }); - }); -enifed("ember-data/transforms/date", - ["ember-data/transforms/base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - The `DS.DateTransform` class is used to serialize and deserialize - date attributes on Ember Data record objects. This transform is used - when `date` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. - - ```javascript - var attr = DS.attr; - App.Score = DS.Model.extend({ - value: attr('number'), - player: DS.belongsTo('player'), - date: attr('date') - }); - ``` - - @class DateTransform - @extends DS.Transform - @namespace DS - */ - var Transform = __dependency1__["default"]; - - // Date.prototype.toISOString shim - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString - var toISOString = Date.prototype.toISOString || function() { - function pad(number) { - if ( number < 10 ) { - return '0' + number; - } - return number; - } - - return this.getUTCFullYear() + - '-' + pad( this.getUTCMonth() + 1 ) + - '-' + pad( this.getUTCDate() ) + - 'T' + pad( this.getUTCHours() ) + - ':' + pad( this.getUTCMinutes() ) + - ':' + pad( this.getUTCSeconds() ) + - '.' + (this.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) + - 'Z'; - }; - - if (Ember.SHIM_ES5) { - if (!Date.prototype.toISOString) { - Date.prototype.toISOString = toISOString; - } - } - - __exports__["default"] = Transform.extend({ - deserialize: function(serialized) { - var type = typeof serialized; - - if (type === "string") { - return new Date(Ember.Date.parse(serialized)); - } else if (type === "number") { - return new Date(serialized); - } else if (serialized === null || serialized === undefined) { - // if the value is not present in the data, - // return undefined, not null. - return serialized; - } else { - return null; - } - }, - - serialize: function(date) { - if (date instanceof Date) { - return toISOString.call(date); - } else { - return null; - } - } - }); - }); -enifed("ember-data/transforms/number", - ["ember-data/transforms/base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; - - var empty = Ember.isEmpty; - - /** - The `DS.NumberTransform` class is used to serialize and deserialize - numeric attributes on Ember Data record objects. This transform is - used when `number` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. - - Usage - - ```javascript - var attr = DS.attr; - App.Score = DS.Model.extend({ - value: attr('number'), - player: DS.belongsTo('player'), - date: attr('date') - }); - ``` - - @class NumberTransform - @extends DS.Transform - @namespace DS - */ - __exports__["default"] = Transform.extend({ - deserialize: function(serialized) { - return empty(serialized) ? null : Number(serialized); - }, - - serialize: function(deserialized) { - return empty(deserialized) ? null : Number(deserialized); - } - }); - }); -enifed("ember-data/transforms/string", - ["ember-data/transforms/base","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Transform = __dependency1__["default"]; - var none = Ember.isNone; - - /** - The `DS.StringTransform` class is used to serialize and deserialize - string attributes on Ember Data record objects. This transform is - used when `string` is passed as the type parameter to the - [DS.attr](../../data#method_attr) function. - - Usage - - ```javascript - var attr = DS.attr; - App.User = DS.Model.extend({ - isAdmin: attr('boolean'), - name: attr('string'), - email: attr('string') - }); - ``` - - @class StringTransform - @extends DS.Transform - @namespace DS - */ - __exports__["default"] = Transform.extend({ - deserialize: function(serialized) { - return none(serialized) ? null : String(serialized); - }, - serialize: function(deserialized) { - return none(deserialized) ? null : String(deserialized); - } - }); - }); -enifed("ember-inflector", - ["./system","./helpers","./ext/string","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Inflector = __dependency1__.Inflector; - var defaultRules = __dependency1__.defaultRules; - var pluralize = __dependency1__.pluralize; - var singularize = __dependency1__.singularize; - - Inflector.defaultRules = defaultRules; - Ember.Inflector = Inflector; - - Ember.String.pluralize = pluralize; - Ember.String.singularize = singularize; - - - __exports__["default"] = Inflector; - - __exports__.pluralize = pluralize; - __exports__.singularize = singularize; - }); -enifed("ember-inflector/ext/string", - ["../system/string"], - function(__dependency1__) { - "use strict"; - var pluralize = __dependency1__.pluralize; - var singularize = __dependency1__.singularize; - - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { - /** - See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}} - - @method pluralize - @for String - */ - String.prototype.pluralize = function() { - return pluralize(this); - }; - - /** - See {{#crossLink "Ember.String/singularize"}}{{/crossLink}} - - @method singularize - @for String - */ - String.prototype.singularize = function() { - return singularize(this); - }; - } - }); -enifed("ember-inflector/helpers", - ["./system/string"], - function(__dependency1__) { - "use strict"; - var singularize = __dependency1__.singularize; - var pluralize = __dependency1__.pluralize; - - /** - * - * If you have Ember Inflector (such as if Ember Data is present), - * singularize a word. For example, turn "oxen" into "ox". - * - * Example: - * - * {{singularize myProperty}} - * {{singularize "oxen"}} - * - * @for Ember.Handlebars.helpers - * @method singularize - * @param {String|Property} word word to singularize - */ - Ember.Handlebars.helper('singularize', singularize); - - /** - * - * If you have Ember Inflector (such as if Ember Data is present), - * pluralize a word. For example, turn "ox" into "oxen". - * - * Example: - * - * {{pluralize myProperty}} - * {{pluralize "oxen"}} - * - * @for Ember.Handlebars.helpers - * @method pluralize - * @param {String|Property} word word to pluralize - */ - Ember.Handlebars.helper('pluralize', pluralize); - }); -enifed("ember-inflector/system", - ["./system/inflector","./system/string","./system/inflections","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Inflector = __dependency1__["default"]; - - var pluralize = __dependency2__.pluralize; - var singularize = __dependency2__.singularize; - - var defaultRules = __dependency3__["default"]; - - - Inflector.inflector = new Inflector(defaultRules); - - __exports__.Inflector = Inflector; - __exports__.singularize = singularize; - __exports__.pluralize = pluralize; - __exports__.defaultRules = defaultRules; - }); -enifed("ember-inflector/system/inflections", - ["exports"], - function(__exports__) { - "use strict"; - __exports__["default"] = { - 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' - ] - }; - }); -enifed("ember-inflector/system/inflector", - ["exports"], - function(__exports__) { - "use strict"; - var BLANK_REGEX = /^\s*$/; - var LAST_WORD_DASHED_REGEX = /(\w+[_-])([a-z\d]+$)/; - var LAST_WORD_CAMELIZED_REGEX = /(\w+)([A-Z][a-z\d]*$)/; - var CAMELIZED_REGEX = /[A-Z][a-z\d]*$/; - - function loadUncountable(rules, uncountable) { - for (var i = 0, length = uncountable.length; i < length; i++) { - rules.uncountable[uncountable[i].toLowerCase()] = true; - } - } - - function loadIrregular(rules, irregularPairs) { - var pair; - - for (var i = 0, length = irregularPairs.length; i < length; i++) { - pair = irregularPairs[i]; - - //pluralizing - rules.irregular[pair[0].toLowerCase()] = pair[1]; - rules.irregular[pair[1].toLowerCase()] = pair[1]; - - //singularizing - rules.irregularInverse[pair[1].toLowerCase()] = pair[0]; - rules.irregularInverse[pair[0].toLowerCase()] = pair[0]; - } - } - - /** - Inflector.Ember provides a mechanism for supplying inflection rules for your - application. Ember includes a default set of inflection rules, and provides an - API for providing additional rules. - - Examples: - - Creating an inflector with no rules. - - ```js - var inflector = new Ember.Inflector(); - ``` - - Creating an inflector with the default ember ruleset. - - ```js - var inflector = new Ember.Inflector(Ember.Inflector.defaultRules); - - inflector.pluralize('cow'); //=> 'kine' - inflector.singularize('kine'); //=> 'cow' - ``` - - Creating an inflector and adding rules later. - - ```javascript - var inflector = Ember.Inflector.inflector; - - inflector.pluralize('advice'); // => 'advices' - inflector.uncountable('advice'); - inflector.pluralize('advice'); // => 'advice' - - inflector.pluralize('formula'); // => 'formulas' - inflector.irregular('formula', 'formulae'); - inflector.pluralize('formula'); // => 'formulae' - - // you would not need to add these as they are the default rules - inflector.plural(/$/, 's'); - inflector.singular(/s$/i, ''); - ``` - - Creating an inflector with a nondefault ruleset. - - ```javascript - var rules = { - plurals: [ /$/, 's' ], - singular: [ /\s$/, '' ], - irregularPairs: [ - [ 'cow', 'kine' ] - ], - uncountable: [ 'fish' ] - }; - - var inflector = new Ember.Inflector(rules); - ``` - - @class Inflector - @namespace Ember - */ - function Inflector(ruleSet) { - ruleSet = ruleSet || {}; - ruleSet.uncountable = ruleSet.uncountable || makeDictionary(); - ruleSet.irregularPairs = ruleSet.irregularPairs || makeDictionary(); - - var rules = this.rules = { - plurals: ruleSet.plurals || [], - singular: ruleSet.singular || [], - irregular: makeDictionary(), - irregularInverse: makeDictionary(), - uncountable: makeDictionary() - }; - - loadUncountable(rules, ruleSet.uncountable); - loadIrregular(rules, ruleSet.irregularPairs); - - this.enableCache(); - } - - if (!Object.create && !Object.create(null).hasOwnProperty) { - throw new Error("This browser does not support Object.create(null), please polyfil with es5-sham: http://git.io/yBU2rg"); - } - - function makeDictionary() { - var cache = Object.create(null); - cache['_dict'] = null; - delete cache['_dict']; - return cache; - } - - Inflector.prototype = { - /** - @public - - As inflections can be costly, and commonly the same subset of words are repeatedly - inflected an optional cache is provided. - - @method enableCache - */ - enableCache: function() { - this.purgeCache(); - - this.singularize = function(word) { - this._cacheUsed = true; - return this._sCache[word] || (this._sCache[word] = this._singularize(word)); - }; - - this.pluralize = function(word) { - this._cacheUsed = true; - return this._pCache[word] || (this._pCache[word] = this._pluralize(word)); - }; - }, - - /** - @public - - @method purgedCache - */ - purgeCache: function() { - this._cacheUsed = false; - this._sCache = makeDictionary(); - this._pCache = makeDictionary(); - }, - - /** - @public - disable caching - - @method disableCache; - */ - disableCache: function() { - this._sCache = null; - this._pCache = null; - this.singularize = function(word) { - return this._singularize(word); - }; - - this.pluralize = function(word) { - return this._pluralize(word); - }; - }, - - /** - @method plural - @param {RegExp} regex - @param {String} string - */ - plural: function(regex, string) { - if (this._cacheUsed) { this.purgeCache(); } - this.rules.plurals.push([regex, string.toLowerCase()]); - }, - - /** - @method singular - @param {RegExp} regex - @param {String} string - */ - singular: function(regex, string) { - if (this._cacheUsed) { this.purgeCache(); } - this.rules.singular.push([regex, string.toLowerCase()]); - }, - - /** - @method uncountable - @param {String} regex - */ - uncountable: function(string) { - if (this._cacheUsed) { this.purgeCache(); } - loadUncountable(this.rules, [string.toLowerCase()]); - }, - - /** - @method irregular - @param {String} singular - @param {String} plural - */ - irregular: function (singular, plural) { - if (this._cacheUsed) { this.purgeCache(); } - loadIrregular(this.rules, [[singular, plural]]); - }, - - /** - @method pluralize - @param {String} word - */ - pluralize: function(word) { - return this._pluralize(word); - }, - - _pluralize: function(word) { - return this.inflect(word, this.rules.plurals, this.rules.irregular); - }, - /** - @method singularize - @param {String} word - */ - singularize: function(word) { - return this._singularize(word); - }, - - _singularize: function(word) { - return this.inflect(word, this.rules.singular, this.rules.irregularInverse); - }, - - /** - @protected - - @method inflect - @param {String} word - @param {Object} typeRules - @param {Object} irregular - */ - inflect: function(word, typeRules, irregular) { - var inflection, substitution, result, lowercase, wordSplit, - firstPhrase, lastWord, isBlank, isCamelized, isUncountable, - isIrregular, isIrregularInverse, rule; - - isBlank = BLANK_REGEX.test(word); - isCamelized = CAMELIZED_REGEX.test(word); - firstPhrase = ""; - - if (isBlank) { - return word; - } - - lowercase = word.toLowerCase(); - wordSplit = LAST_WORD_DASHED_REGEX.exec(word) || LAST_WORD_CAMELIZED_REGEX.exec(word); - if (wordSplit){ - firstPhrase = wordSplit[1]; - lastWord = wordSplit[2].toLowerCase(); - } - - isUncountable = this.rules.uncountable[lowercase] || this.rules.uncountable[lastWord]; - - if (isUncountable) { - return word; - } - - isIrregular = irregular && (irregular[lowercase] || irregular[lastWord]); - - if (isIrregular) { - if (irregular[lowercase]){ - return isIrregular; - } - else { - isIrregular = (isCamelized) ? isIrregular.capitalize() : isIrregular; - return firstPhrase + isIrregular; - } - } - - for (var i = typeRules.length, min = 0; i > min; i--) { - inflection = typeRules[i-1]; - rule = inflection[0]; - - if (rule.test(word)) { - break; - } - } - - inflection = inflection || []; - - rule = inflection[0]; - substitution = inflection[1]; - - result = word.replace(rule, substitution); - - return result; - } - }; - - __exports__["default"] = Inflector; - }); -enifed("ember-inflector/system/string", - ["./inflector","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Inflector = __dependency1__["default"]; - - function pluralize(word) { - return Inflector.inflector.pluralize(word); - } - - function singularize(word) { - return Inflector.inflector.singularize(word); - } - - __exports__.pluralize = pluralize; - __exports__.singularize = singularize; - }); - global.DS = requireModule('ember-data')['default']; - })(this);
\ No newline at end of file |