Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / new_ui / ui / javascripts / vendor / ember-data.js @ e15a992e

History | View | Annotate | Download (272.4 kB)

1 e4a6c3b8 Olga Brani
/*!
2 e4a6c3b8 Olga Brani
 * @overview  Ember Data
3 e4a6c3b8 Olga Brani
 * @copyright Copyright 2011-2014 Tilde Inc. and contributors.
4 e4a6c3b8 Olga Brani
 *            Portions Copyright 2011 LivingSocial Inc.
5 e4a6c3b8 Olga Brani
 * @license   Licensed under MIT license (see license.js)
6 e4a6c3b8 Olga Brani
 * @version   1.0.0-beta.6+canary.bf00e754
7 e4a6c3b8 Olga Brani
 */
8 e4a6c3b8 Olga Brani
9 e4a6c3b8 Olga Brani
10 e4a6c3b8 Olga Brani
(function() {
11 e4a6c3b8 Olga Brani
var define, requireModule;
12 e4a6c3b8 Olga Brani
13 e4a6c3b8 Olga Brani
(function() {
14 e4a6c3b8 Olga Brani
  var registry = {}, seen = {};
15 e4a6c3b8 Olga Brani
16 e4a6c3b8 Olga Brani
  define = function(name, deps, callback) {
17 e4a6c3b8 Olga Brani
    registry[name] = { deps: deps, callback: callback };
18 e4a6c3b8 Olga Brani
  };
19 e4a6c3b8 Olga Brani
20 e4a6c3b8 Olga Brani
  requireModule = function(name) {
21 e4a6c3b8 Olga Brani
    if (seen[name]) { return seen[name]; }
22 e4a6c3b8 Olga Brani
    seen[name] = {};
23 e4a6c3b8 Olga Brani
24 e4a6c3b8 Olga Brani
    var mod, deps, callback, reified , exports;
25 e4a6c3b8 Olga Brani
26 e4a6c3b8 Olga Brani
    mod = registry[name];
27 e4a6c3b8 Olga Brani
28 e4a6c3b8 Olga Brani
    if (!mod) {
29 e4a6c3b8 Olga Brani
      throw new Error("Module '" + name + "' not found.");
30 e4a6c3b8 Olga Brani
    }
31 e4a6c3b8 Olga Brani
32 e4a6c3b8 Olga Brani
    deps = mod.deps;
33 e4a6c3b8 Olga Brani
    callback = mod.callback;
34 e4a6c3b8 Olga Brani
    reified = [];
35 e4a6c3b8 Olga Brani
    exports;
36 e4a6c3b8 Olga Brani
37 e4a6c3b8 Olga Brani
    for (var i=0, l=deps.length; i<l; i++) {
38 e4a6c3b8 Olga Brani
      if (deps[i] === 'exports') {
39 e4a6c3b8 Olga Brani
        reified.push(exports = {});
40 e4a6c3b8 Olga Brani
      } else {
41 e4a6c3b8 Olga Brani
        reified.push(requireModule(deps[i]));
42 e4a6c3b8 Olga Brani
      }
43 e4a6c3b8 Olga Brani
    }
44 e4a6c3b8 Olga Brani
45 e4a6c3b8 Olga Brani
    var value = callback.apply(this, reified);
46 e4a6c3b8 Olga Brani
    return seen[name] = exports || value;
47 e4a6c3b8 Olga Brani
  };
48 e4a6c3b8 Olga Brani
})();
49 e4a6c3b8 Olga Brani
(function() {
50 e4a6c3b8 Olga Brani
/**
51 e4a6c3b8 Olga Brani
  @module ember-data
52 e4a6c3b8 Olga Brani
*/
53 e4a6c3b8 Olga Brani
54 e4a6c3b8 Olga Brani
/**
55 e4a6c3b8 Olga Brani
  All Ember Data methods and functions are defined inside of this namespace.
56 e4a6c3b8 Olga Brani

57 e4a6c3b8 Olga Brani
  @class DS
58 e4a6c3b8 Olga Brani
  @static
59 e4a6c3b8 Olga Brani
*/
60 e4a6c3b8 Olga Brani
var DS;
61 e4a6c3b8 Olga Brani
if ('undefined' === typeof DS) {
62 e4a6c3b8 Olga Brani
  /**
63 e4a6c3b8 Olga Brani
    @property VERSION
64 e4a6c3b8 Olga Brani
    @type String
65 e4a6c3b8 Olga Brani
    @default '1.0.0-beta.6+canary.bf00e754'
66 e4a6c3b8 Olga Brani
    @static
67 e4a6c3b8 Olga Brani
  */
68 e4a6c3b8 Olga Brani
  DS = Ember.Namespace.create({
69 e4a6c3b8 Olga Brani
    VERSION: '1.0.0-beta.6+canary.bf00e754'
70 e4a6c3b8 Olga Brani
  });
71 e4a6c3b8 Olga Brani
72 e4a6c3b8 Olga Brani
  if ('undefined' !== typeof window) {
73 e4a6c3b8 Olga Brani
    window.DS = DS;
74 e4a6c3b8 Olga Brani
  }
75 e4a6c3b8 Olga Brani
76 e4a6c3b8 Olga Brani
  if (Ember.libraries) {
77 e4a6c3b8 Olga Brani
    Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
78 e4a6c3b8 Olga Brani
  }
79 e4a6c3b8 Olga Brani
}
80 e4a6c3b8 Olga Brani
81 e4a6c3b8 Olga Brani
})();
82 e4a6c3b8 Olga Brani
83 e4a6c3b8 Olga Brani
84 e4a6c3b8 Olga Brani
85 e4a6c3b8 Olga Brani
(function() {
86 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
87 e4a6c3b8 Olga Brani
88 e4a6c3b8 Olga Brani
// Simple dispatcher to support overriding the aliased
89 e4a6c3b8 Olga Brani
// method in subclasses.
90 e4a6c3b8 Olga Brani
function aliasMethod(methodName) {
91 e4a6c3b8 Olga Brani
  return function() {
92 e4a6c3b8 Olga Brani
    return this[methodName].apply(this, arguments);
93 e4a6c3b8 Olga Brani
  };
94 e4a6c3b8 Olga Brani
}
95 e4a6c3b8 Olga Brani
96 e4a6c3b8 Olga Brani
/**
97 e4a6c3b8 Olga Brani
  In Ember Data a Serializer is used to serialize and deserialize
98 e4a6c3b8 Olga Brani
  records when they are transfered in and out of an external source.
99 e4a6c3b8 Olga Brani
  This process involves normalizing property names, transforming
100 e4a6c3b8 Olga Brani
  attribute values and serializeing relationships.
101 e4a6c3b8 Olga Brani

102 e4a6c3b8 Olga Brani
  For maximum performance Ember Data recomends you use the
103 e4a6c3b8 Olga Brani
  [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses.
104 e4a6c3b8 Olga Brani

105 e4a6c3b8 Olga Brani
  `JSONSerializer` is useful for simpler or legacy backends that may
106 e4a6c3b8 Olga Brani
  not support the http://jsonapi.org/ spec.
107 e4a6c3b8 Olga Brani

108 e4a6c3b8 Olga Brani
  @class JSONSerializer
109 e4a6c3b8 Olga Brani
  @namespace DS
110 e4a6c3b8 Olga Brani
*/
111 e4a6c3b8 Olga Brani
DS.JSONSerializer = Ember.Object.extend({
112 e4a6c3b8 Olga Brani
  /**
113 e4a6c3b8 Olga Brani
    The primaryKey is used when serializing and deserializing
114 e4a6c3b8 Olga Brani
    data. Ember Data always uses the `id` propery to store the id of
115 e4a6c3b8 Olga Brani
    the record. The external source may not always follow this
116 e4a6c3b8 Olga Brani
    convention. In these cases it is usesful to override the
117 e4a6c3b8 Olga Brani
    primaryKey property to match the primaryKey of your external
118 e4a6c3b8 Olga Brani
    store.
119 e4a6c3b8 Olga Brani

120 e4a6c3b8 Olga Brani
    Example
121 e4a6c3b8 Olga Brani

122 e4a6c3b8 Olga Brani
    ```javascript
123 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.JSONSerializer.extend({
124 e4a6c3b8 Olga Brani
      primaryKey: '_id'
125 e4a6c3b8 Olga Brani
    });
126 e4a6c3b8 Olga Brani
    ```
127 e4a6c3b8 Olga Brani

128 e4a6c3b8 Olga Brani
    @property primaryKey
129 e4a6c3b8 Olga Brani
    @type {String}
130 e4a6c3b8 Olga Brani
    @default 'id'
131 e4a6c3b8 Olga Brani
  */
132 e4a6c3b8 Olga Brani
  primaryKey: 'id',
133 e4a6c3b8 Olga Brani
134 e4a6c3b8 Olga Brani
  /**
135 e4a6c3b8 Olga Brani
   Given a subclass of `DS.Model` and a JSON object this method will
136 e4a6c3b8 Olga Brani
   iterate through each attribute of the `DS.Model` and invoke the
137 e4a6c3b8 Olga Brani
   `DS.Transform#deserialize` method on the matching property of the
138 e4a6c3b8 Olga Brani
   JSON object.  This method is typically called after the
139 e4a6c3b8 Olga Brani
   serializer's `normalize` method.
140 e4a6c3b8 Olga Brani

141 e4a6c3b8 Olga Brani
   @method applyTransforms
142 e4a6c3b8 Olga Brani
   @private
143 e4a6c3b8 Olga Brani
   @param {subclass of DS.Model} type
144 e4a6c3b8 Olga Brani
   @param {Object} data The data to transform
145 e4a6c3b8 Olga Brani
   @return {Object} data The transformed data object
146 e4a6c3b8 Olga Brani
  */
147 e4a6c3b8 Olga Brani
  applyTransforms: function(type, data) {
148 e4a6c3b8 Olga Brani
    type.eachTransformedAttribute(function(key, type) {
149 e4a6c3b8 Olga Brani
      var transform = this.transformFor(type);
150 e4a6c3b8 Olga Brani
      data[key] = transform.deserialize(data[key]);
151 e4a6c3b8 Olga Brani
    }, this);
152 e4a6c3b8 Olga Brani
153 e4a6c3b8 Olga Brani
    return data;
154 e4a6c3b8 Olga Brani
  },
155 e4a6c3b8 Olga Brani
156 e4a6c3b8 Olga Brani
  /**
157 e4a6c3b8 Olga Brani
    Normalizes a part of the JSON payload returned by
158 e4a6c3b8 Olga Brani
    the server. You should override this method, munge the hash
159 e4a6c3b8 Olga Brani
    and call super if you have generic normalization to do.
160 e4a6c3b8 Olga Brani

161 e4a6c3b8 Olga Brani
    It takes the type of the record that is being normalized
162 e4a6c3b8 Olga Brani
    (as a DS.Model class), the property where the hash was
163 e4a6c3b8 Olga Brani
    originally found, and the hash to normalize.
164 e4a6c3b8 Olga Brani

165 e4a6c3b8 Olga Brani
    You can use this method, for example, to normalize underscored keys to camelized
166 e4a6c3b8 Olga Brani
    or other general-purpose normalizations.
167 e4a6c3b8 Olga Brani

168 e4a6c3b8 Olga Brani
    Example
169 e4a6c3b8 Olga Brani

170 e4a6c3b8 Olga Brani
    ```javascript
171 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.JSONSerializer.extend({
172 e4a6c3b8 Olga Brani
      normalize: function(type, hash) {
173 e4a6c3b8 Olga Brani
        var fields = Ember.get(type, 'fields');
174 e4a6c3b8 Olga Brani
        fields.forEach(function(field) {
175 e4a6c3b8 Olga Brani
          var payloadField = Ember.String.underscore(field);
176 e4a6c3b8 Olga Brani
          if (field === payloadField) { return; }
177 e4a6c3b8 Olga Brani

178 e4a6c3b8 Olga Brani
          hash[field] = hash[payloadField];
179 e4a6c3b8 Olga Brani
          delete hash[payloadField];
180 e4a6c3b8 Olga Brani
        });
181 e4a6c3b8 Olga Brani
        return this._super.apply(this, arguments);
182 e4a6c3b8 Olga Brani
      }
183 e4a6c3b8 Olga Brani
    });
184 e4a6c3b8 Olga Brani
    ```
185 e4a6c3b8 Olga Brani

186 e4a6c3b8 Olga Brani
    @method normalize
187 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
188 e4a6c3b8 Olga Brani
    @param {Object} hash
189 e4a6c3b8 Olga Brani
    @return {Object}
190 e4a6c3b8 Olga Brani
  */
191 e4a6c3b8 Olga Brani
  normalize: function(type, hash) {
192 e4a6c3b8 Olga Brani
    if (!hash) { return hash; }
193 e4a6c3b8 Olga Brani
194 e4a6c3b8 Olga Brani
    this.applyTransforms(type, hash);
195 e4a6c3b8 Olga Brani
    return hash;
196 e4a6c3b8 Olga Brani
  },
197 e4a6c3b8 Olga Brani
198 e4a6c3b8 Olga Brani
  // SERIALIZE
199 e4a6c3b8 Olga Brani
  /**
200 e4a6c3b8 Olga Brani
    Called when a record is saved in order to convert the
201 e4a6c3b8 Olga Brani
    record into JSON.
202 e4a6c3b8 Olga Brani

203 e4a6c3b8 Olga Brani
    By default, it creates a JSON object with a key for
204 e4a6c3b8 Olga Brani
    each attribute and belongsTo relationship.
205 e4a6c3b8 Olga Brani

206 e4a6c3b8 Olga Brani
    For example, consider this model:
207 e4a6c3b8 Olga Brani

208 e4a6c3b8 Olga Brani
    ```javascript
209 e4a6c3b8 Olga Brani
    App.Comment = DS.Model.extend({
210 e4a6c3b8 Olga Brani
      title: DS.attr(),
211 e4a6c3b8 Olga Brani
      body: DS.attr(),
212 e4a6c3b8 Olga Brani

213 e4a6c3b8 Olga Brani
      author: DS.belongsTo('user')
214 e4a6c3b8 Olga Brani
    });
215 e4a6c3b8 Olga Brani
    ```
216 e4a6c3b8 Olga Brani

217 e4a6c3b8 Olga Brani
    The default serialization would create a JSON object like:
218 e4a6c3b8 Olga Brani

219 e4a6c3b8 Olga Brani
    ```javascript
220 e4a6c3b8 Olga Brani
    {
221 e4a6c3b8 Olga Brani
      "title": "Rails is unagi",
222 e4a6c3b8 Olga Brani
      "body": "Rails? Omakase? O_O",
223 e4a6c3b8 Olga Brani
      "author": 12
224 e4a6c3b8 Olga Brani
    }
225 e4a6c3b8 Olga Brani
    ```
226 e4a6c3b8 Olga Brani

227 e4a6c3b8 Olga Brani
    By default, attributes are passed through as-is, unless
228 e4a6c3b8 Olga Brani
    you specified an attribute type (`DS.attr('date')`). If
229 e4a6c3b8 Olga Brani
    you specify a transform, the JavaScript value will be
230 e4a6c3b8 Olga Brani
    serialized when inserted into the JSON hash.
231 e4a6c3b8 Olga Brani

232 e4a6c3b8 Olga Brani
    By default, belongs-to relationships are converted into
233 e4a6c3b8 Olga Brani
    IDs when inserted into the JSON hash.
234 e4a6c3b8 Olga Brani

235 e4a6c3b8 Olga Brani
    ## IDs
236 e4a6c3b8 Olga Brani

237 e4a6c3b8 Olga Brani
    `serialize` takes an options hash with a single option:
238 e4a6c3b8 Olga Brani
    `includeId`. If this option is `true`, `serialize` will,
239 e4a6c3b8 Olga Brani
    by default include the ID in the JSON object it builds.
240 e4a6c3b8 Olga Brani

241 e4a6c3b8 Olga Brani
    The adapter passes in `includeId: true` when serializing
242 e4a6c3b8 Olga Brani
    a record for `createRecord`, but not for `updateRecord`.
243 e4a6c3b8 Olga Brani

244 e4a6c3b8 Olga Brani
    ## Customization
245 e4a6c3b8 Olga Brani

246 e4a6c3b8 Olga Brani
    Your server may expect a different JSON format than the
247 e4a6c3b8 Olga Brani
    built-in serialization format.
248 e4a6c3b8 Olga Brani

249 e4a6c3b8 Olga Brani
    In that case, you can implement `serialize` yourself and
250 e4a6c3b8 Olga Brani
    return a JSON hash of your choosing.
251 e4a6c3b8 Olga Brani

252 e4a6c3b8 Olga Brani
    ```javascript
253 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.JSONSerializer.extend({
254 e4a6c3b8 Olga Brani
      serialize: function(post, options) {
255 e4a6c3b8 Olga Brani
        var json = {
256 e4a6c3b8 Olga Brani
          POST_TTL: post.get('title'),
257 e4a6c3b8 Olga Brani
          POST_BDY: post.get('body'),
258 e4a6c3b8 Olga Brani
          POST_CMS: post.get('comments').mapProperty('id')
259 e4a6c3b8 Olga Brani
        }
260 e4a6c3b8 Olga Brani

261 e4a6c3b8 Olga Brani
        if (options.includeId) {
262 e4a6c3b8 Olga Brani
          json.POST_ID_ = post.get('id');
263 e4a6c3b8 Olga Brani
        }
264 e4a6c3b8 Olga Brani

265 e4a6c3b8 Olga Brani
        return json;
266 e4a6c3b8 Olga Brani
      }
267 e4a6c3b8 Olga Brani
    });
268 e4a6c3b8 Olga Brani
    ```
269 e4a6c3b8 Olga Brani

270 e4a6c3b8 Olga Brani
    ## Customizing an App-Wide Serializer
271 e4a6c3b8 Olga Brani

272 e4a6c3b8 Olga Brani
    If you want to define a serializer for your entire
273 e4a6c3b8 Olga Brani
    application, you'll probably want to use `eachAttribute`
274 e4a6c3b8 Olga Brani
    and `eachRelationship` on the record.
275 e4a6c3b8 Olga Brani

276 e4a6c3b8 Olga Brani
    ```javascript
277 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.JSONSerializer.extend({
278 e4a6c3b8 Olga Brani
      serialize: function(record, options) {
279 e4a6c3b8 Olga Brani
        var json = {};
280 e4a6c3b8 Olga Brani

281 e4a6c3b8 Olga Brani
        record.eachAttribute(function(name) {
282 e4a6c3b8 Olga Brani
          json[serverAttributeName(name)] = record.get(name);
283 e4a6c3b8 Olga Brani
        })
284 e4a6c3b8 Olga Brani

285 e4a6c3b8 Olga Brani
        record.eachRelationship(function(name, relationship) {
286 e4a6c3b8 Olga Brani
          if (relationship.kind === 'hasMany') {
287 e4a6c3b8 Olga Brani
            json[serverHasManyName(name)] = record.get(name).mapBy('id');
288 e4a6c3b8 Olga Brani
          }
289 e4a6c3b8 Olga Brani
        });
290 e4a6c3b8 Olga Brani

291 e4a6c3b8 Olga Brani
        if (options.includeId) {
292 e4a6c3b8 Olga Brani
          json.ID_ = record.get('id');
293 e4a6c3b8 Olga Brani
        }
294 e4a6c3b8 Olga Brani

295 e4a6c3b8 Olga Brani
        return json;
296 e4a6c3b8 Olga Brani
      }
297 e4a6c3b8 Olga Brani
    });
298 e4a6c3b8 Olga Brani

299 e4a6c3b8 Olga Brani
    function serverAttributeName(attribute) {
300 e4a6c3b8 Olga Brani
      return attribute.underscore().toUpperCase();
301 e4a6c3b8 Olga Brani
    }
302 e4a6c3b8 Olga Brani

303 e4a6c3b8 Olga Brani
    function serverHasManyName(name) {
304 e4a6c3b8 Olga Brani
      return serverAttributeName(name.singularize()) + "_IDS";
305 e4a6c3b8 Olga Brani
    }
306 e4a6c3b8 Olga Brani
    ```
307 e4a6c3b8 Olga Brani

308 e4a6c3b8 Olga Brani
    This serializer will generate JSON that looks like this:
309 e4a6c3b8 Olga Brani

310 e4a6c3b8 Olga Brani
    ```javascript
311 e4a6c3b8 Olga Brani
    {
312 e4a6c3b8 Olga Brani
      "TITLE": "Rails is omakase",
313 e4a6c3b8 Olga Brani
      "BODY": "Yep. Omakase.",
314 e4a6c3b8 Olga Brani
      "COMMENT_IDS": [ 1, 2, 3 ]
315 e4a6c3b8 Olga Brani
    }
316 e4a6c3b8 Olga Brani
    ```
317 e4a6c3b8 Olga Brani

318 e4a6c3b8 Olga Brani
    ## Tweaking the Default JSON
319 e4a6c3b8 Olga Brani

320 e4a6c3b8 Olga Brani
    If you just want to do some small tweaks on the default JSON,
321 e4a6c3b8 Olga Brani
    you can call super first and make the tweaks on the returned
322 e4a6c3b8 Olga Brani
    JSON.
323 e4a6c3b8 Olga Brani

324 e4a6c3b8 Olga Brani
    ```javascript
325 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.JSONSerializer.extend({
326 e4a6c3b8 Olga Brani
      serialize: function(record, options) {
327 e4a6c3b8 Olga Brani
        var json = this._super.apply(this, arguments);
328 e4a6c3b8 Olga Brani

329 e4a6c3b8 Olga Brani
        json.subject = json.title;
330 e4a6c3b8 Olga Brani
        delete json.title;
331 e4a6c3b8 Olga Brani

332 e4a6c3b8 Olga Brani
        return json;
333 e4a6c3b8 Olga Brani
      }
334 e4a6c3b8 Olga Brani
    });
335 e4a6c3b8 Olga Brani
    ```
336 e4a6c3b8 Olga Brani

337 e4a6c3b8 Olga Brani
    @method serialize
338 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} record
339 e4a6c3b8 Olga Brani
    @param {Object} options
340 e4a6c3b8 Olga Brani
    @return {Object} json
341 e4a6c3b8 Olga Brani
  */
342 e4a6c3b8 Olga Brani
  serialize: function(record, options) {
343 e4a6c3b8 Olga Brani
    var json = {};
344 e4a6c3b8 Olga Brani
345 e4a6c3b8 Olga Brani
    if (options && options.includeId) {
346 e4a6c3b8 Olga Brani
      var id = get(record, 'id');
347 e4a6c3b8 Olga Brani
348 e4a6c3b8 Olga Brani
      if (id) {
349 e4a6c3b8 Olga Brani
        json[get(this, 'primaryKey')] = id;
350 e4a6c3b8 Olga Brani
      }
351 e4a6c3b8 Olga Brani
    }
352 e4a6c3b8 Olga Brani
353 e4a6c3b8 Olga Brani
    record.eachAttribute(function(key, attribute) {
354 e4a6c3b8 Olga Brani
      this.serializeAttribute(record, json, key, attribute);
355 e4a6c3b8 Olga Brani
    }, this);
356 e4a6c3b8 Olga Brani
357 e4a6c3b8 Olga Brani
    record.eachRelationship(function(key, relationship) {
358 e4a6c3b8 Olga Brani
      if (relationship.kind === 'belongsTo') {
359 e4a6c3b8 Olga Brani
        this.serializeBelongsTo(record, json, relationship);
360 e4a6c3b8 Olga Brani
      } else if (relationship.kind === 'hasMany') {
361 e4a6c3b8 Olga Brani
        this.serializeHasMany(record, json, relationship);
362 e4a6c3b8 Olga Brani
      }
363 e4a6c3b8 Olga Brani
    }, this);
364 e4a6c3b8 Olga Brani
365 e4a6c3b8 Olga Brani
    return json;
366 e4a6c3b8 Olga Brani
  },
367 e4a6c3b8 Olga Brani
368 e4a6c3b8 Olga Brani
  /**
369 e4a6c3b8 Olga Brani
   `serializeAttribute` can be used to customize how `DS.attr`
370 e4a6c3b8 Olga Brani
   properties are serialized
371 e4a6c3b8 Olga Brani

372 e4a6c3b8 Olga Brani
   For example if you wanted to ensure all you attributes were always
373 e4a6c3b8 Olga Brani
   serialized as properties on an `attributes` object you could
374 e4a6c3b8 Olga Brani
   write:
375 e4a6c3b8 Olga Brani

376 e4a6c3b8 Olga Brani
   ```javascript
377 e4a6c3b8 Olga Brani
   App.ApplicationSerializer = DS.JSONSerializer.extend({
378 e4a6c3b8 Olga Brani
     serializeAttribute: function(record, json, key, attributes) {
379 e4a6c3b8 Olga Brani
       json.attributes = json.attributes || {};
380 e4a6c3b8 Olga Brani
       this._super(record, json.attributes, key, attributes);
381 e4a6c3b8 Olga Brani
     }
382 e4a6c3b8 Olga Brani
   });
383 e4a6c3b8 Olga Brani
   ```
384 e4a6c3b8 Olga Brani

385 e4a6c3b8 Olga Brani
   @method serializeAttribute
386 e4a6c3b8 Olga Brani
   @param {DS.Model} record
387 e4a6c3b8 Olga Brani
   @param {Object} json
388 e4a6c3b8 Olga Brani
   @param {String} key
389 e4a6c3b8 Olga Brani
   @param {Object} attribute
390 e4a6c3b8 Olga Brani
  */
391 e4a6c3b8 Olga Brani
  serializeAttribute: function(record, json, key, attribute) {
392 e4a6c3b8 Olga Brani
    var attrs = get(this, 'attrs');
393 e4a6c3b8 Olga Brani
    var value = get(record, key), type = attribute.type;
394 e4a6c3b8 Olga Brani
395 e4a6c3b8 Olga Brani
    if (type) {
396 e4a6c3b8 Olga Brani
      var transform = this.transformFor(type);
397 e4a6c3b8 Olga Brani
      value = transform.serialize(value);
398 e4a6c3b8 Olga Brani
    }
399 e4a6c3b8 Olga Brani
400 e4a6c3b8 Olga Brani
    // if provided, use the mapping provided by `attrs` in
401 e4a6c3b8 Olga Brani
    // the serializer
402 e4a6c3b8 Olga Brani
    key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key);
403 e4a6c3b8 Olga Brani
404 e4a6c3b8 Olga Brani
    json[key] = value;
405 e4a6c3b8 Olga Brani
  },
406 e4a6c3b8 Olga Brani
407 e4a6c3b8 Olga Brani
  /**
408 e4a6c3b8 Olga Brani
   `serializeBelongsTo` can be used to customize how `DS.belongsTo`
409 e4a6c3b8 Olga Brani
   properties are serialized.
410 e4a6c3b8 Olga Brani

411 e4a6c3b8 Olga Brani
   Example
412 e4a6c3b8 Olga Brani

413 e4a6c3b8 Olga Brani
   ```javascript
414 e4a6c3b8 Olga Brani
   App.PostSerializer = DS.JSONSerializer.extend({
415 e4a6c3b8 Olga Brani
     serializeBelongsTo: function(record, json, relationship) {
416 e4a6c3b8 Olga Brani
       var key = relationship.key;
417 e4a6c3b8 Olga Brani

418 e4a6c3b8 Olga Brani
       var belongsTo = get(record, key);
419 e4a6c3b8 Olga Brani

420 e4a6c3b8 Olga Brani
       key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
421 e4a6c3b8 Olga Brani

422 e4a6c3b8 Olga Brani
       json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON();
423 e4a6c3b8 Olga Brani
     }
424 e4a6c3b8 Olga Brani
   });
425 e4a6c3b8 Olga Brani
   ```
426 e4a6c3b8 Olga Brani

427 e4a6c3b8 Olga Brani
   @method serializeBelongsTo
428 e4a6c3b8 Olga Brani
   @param {DS.Model} record
429 e4a6c3b8 Olga Brani
   @param {Object} json
430 e4a6c3b8 Olga Brani
   @param {Object} relationship
431 e4a6c3b8 Olga Brani
  */
432 e4a6c3b8 Olga Brani
  serializeBelongsTo: function(record, json, relationship) {
433 e4a6c3b8 Olga Brani
    var key = relationship.key;
434 e4a6c3b8 Olga Brani
435 e4a6c3b8 Olga Brani
    var belongsTo = get(record, key);
436 e4a6c3b8 Olga Brani
437 e4a6c3b8 Olga Brani
    key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
438 e4a6c3b8 Olga Brani
439 e4a6c3b8 Olga Brani
    if (isNone(belongsTo)) {
440 e4a6c3b8 Olga Brani
      json[key] = belongsTo;
441 e4a6c3b8 Olga Brani
    } else {
442 e4a6c3b8 Olga Brani
      json[key] = get(belongsTo, 'id');
443 e4a6c3b8 Olga Brani
    }
444 e4a6c3b8 Olga Brani
445 e4a6c3b8 Olga Brani
    if (relationship.options.polymorphic) {
446 e4a6c3b8 Olga Brani
      this.serializePolymorphicType(record, json, relationship);
447 e4a6c3b8 Olga Brani
    }
448 e4a6c3b8 Olga Brani
  },
449 e4a6c3b8 Olga Brani
450 e4a6c3b8 Olga Brani
  /**
451 e4a6c3b8 Olga Brani
   `serializeHasMany` can be used to customize how `DS.hasMany`
452 e4a6c3b8 Olga Brani
   properties are serialized.
453 e4a6c3b8 Olga Brani

454 e4a6c3b8 Olga Brani
   Example
455 e4a6c3b8 Olga Brani

456 e4a6c3b8 Olga Brani
   ```javascript
457 e4a6c3b8 Olga Brani
   App.PostSerializer = DS.JSONSerializer.extend({
458 e4a6c3b8 Olga Brani
     serializeHasMany: function(record, json, relationship) {
459 e4a6c3b8 Olga Brani
       var key = relationship.key;
460 e4a6c3b8 Olga Brani
       if (key === 'comments') {
461 e4a6c3b8 Olga Brani
         return;
462 e4a6c3b8 Olga Brani
       } else {
463 e4a6c3b8 Olga Brani
         this._super.apply(this, arguments);
464 e4a6c3b8 Olga Brani
       }
465 e4a6c3b8 Olga Brani
     }
466 e4a6c3b8 Olga Brani
   });
467 e4a6c3b8 Olga Brani
   ```
468 e4a6c3b8 Olga Brani

469 e4a6c3b8 Olga Brani
   @method serializeHasMany
470 e4a6c3b8 Olga Brani
   @param {DS.Model} record
471 e4a6c3b8 Olga Brani
   @param {Object} json
472 e4a6c3b8 Olga Brani
   @param {Object} relationship
473 e4a6c3b8 Olga Brani
  */
474 e4a6c3b8 Olga Brani
  serializeHasMany: function(record, json, relationship) {
475 e4a6c3b8 Olga Brani
    var key = relationship.key;
476 e4a6c3b8 Olga Brani
477 e4a6c3b8 Olga Brani
    var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
478 e4a6c3b8 Olga Brani
479 e4a6c3b8 Olga Brani
    if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') {
480 e4a6c3b8 Olga Brani
      json[key] = get(record, key).mapBy('id');
481 e4a6c3b8 Olga Brani
      // TODO support for polymorphic manyToNone and manyToMany relationships
482 e4a6c3b8 Olga Brani
    }
483 e4a6c3b8 Olga Brani
  },
484 e4a6c3b8 Olga Brani
485 e4a6c3b8 Olga Brani
  /**
486 e4a6c3b8 Olga Brani
    You can use this method to customize how polymorphic objects are
487 e4a6c3b8 Olga Brani
    serialized. Objects are considered to be polymorphic if
488 e4a6c3b8 Olga Brani
    `{polymorphic: true}` is pass as the second argument to the
489 e4a6c3b8 Olga Brani
    `DS.belongsTo` function.
490 e4a6c3b8 Olga Brani

491 e4a6c3b8 Olga Brani
    Example
492 e4a6c3b8 Olga Brani

493 e4a6c3b8 Olga Brani
    ```javascript
494 e4a6c3b8 Olga Brani
    App.CommentSerializer = DS.JSONSerializer.extend({
495 e4a6c3b8 Olga Brani
      serializePolymorphicType: function(record, json, relationship) {
496 e4a6c3b8 Olga Brani
        var key = relationship.key,
497 e4a6c3b8 Olga Brani
            belongsTo = get(record, key);
498 e4a6c3b8 Olga Brani
        key = this.keyForAttribute ? this.keyForAttribute(key) : key;
499 e4a6c3b8 Olga Brani
        json[key + "_type"] = belongsTo.constructor.typeKey;
500 e4a6c3b8 Olga Brani
      }
501 e4a6c3b8 Olga Brani
    });
502 e4a6c3b8 Olga Brani
   ```
503 e4a6c3b8 Olga Brani

504 e4a6c3b8 Olga Brani
    @method serializePolymorphicType
505 e4a6c3b8 Olga Brani
    @param {DS.Model} record
506 e4a6c3b8 Olga Brani
    @param {Object} json
507 e4a6c3b8 Olga Brani
    @param {Object} relationship
508 e4a6c3b8 Olga Brani
  */
509 e4a6c3b8 Olga Brani
  serializePolymorphicType: Ember.K,
510 e4a6c3b8 Olga Brani
511 e4a6c3b8 Olga Brani
  // EXTRACT
512 e4a6c3b8 Olga Brani
513 e4a6c3b8 Olga Brani
  /**
514 e4a6c3b8 Olga Brani
    The `extract` method is used to deserialize payload data from the
515 e4a6c3b8 Olga Brani
    server. By default the `JSONSerializer` does not push the records
516 e4a6c3b8 Olga Brani
    into the store. However records that subclass `JSONSerializer`
517 e4a6c3b8 Olga Brani
    such as the `RESTSerializer` may push records into the store as
518 e4a6c3b8 Olga Brani
    part of the extract call.
519 e4a6c3b8 Olga Brani

520 e4a6c3b8 Olga Brani
    This method deletegates to a more specific extract method based on
521 e4a6c3b8 Olga Brani
    the `requestType`.
522 e4a6c3b8 Olga Brani

523 e4a6c3b8 Olga Brani
    Example
524 e4a6c3b8 Olga Brani

525 e4a6c3b8 Olga Brani
    ```javascript
526 e4a6c3b8 Olga Brani
    var get = Ember.get;
527 e4a6c3b8 Olga Brani
    socket.on('message', function(message) {
528 e4a6c3b8 Olga Brani
      var modelName = message.model;
529 e4a6c3b8 Olga Brani
      var data = message.data;
530 e4a6c3b8 Olga Brani
      var type = store.modelFor(modelName);
531 e4a6c3b8 Olga Brani
      var serializer = store.serializerFor(type.typeKey);
532 e4a6c3b8 Olga Brani
      var record = serializer.extract(store, type, data, get(data, 'id'), 'single');
533 e4a6c3b8 Olga Brani
      store.push(modelName, record);
534 e4a6c3b8 Olga Brani
    });
535 e4a6c3b8 Olga Brani
    ```
536 e4a6c3b8 Olga Brani

537 e4a6c3b8 Olga Brani
    @method extract
538 e4a6c3b8 Olga Brani
    @param {DS.Store} store
539 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
540 e4a6c3b8 Olga Brani
    @param {Object} payload
541 e4a6c3b8 Olga Brani
    @param {String or Number} id
542 e4a6c3b8 Olga Brani
    @param {String} requestType
543 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
544 e4a6c3b8 Olga Brani
  */
545 e4a6c3b8 Olga Brani
  extract: function(store, type, payload, id, requestType) {
546 e4a6c3b8 Olga Brani
    this.extractMeta(store, type, payload);
547 e4a6c3b8 Olga Brani
548 e4a6c3b8 Olga Brani
    var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
549 e4a6c3b8 Olga Brani
    return this[specificExtract](store, type, payload, id, requestType);
550 e4a6c3b8 Olga Brani
  },
551 e4a6c3b8 Olga Brani
552 e4a6c3b8 Olga Brani
  /**
553 e4a6c3b8 Olga Brani
    `extractFindAll` is a hook into the extract method used when a
554 e4a6c3b8 Olga Brani
    call is made to `DS.Store#findAll`. By default this method is an
555 e4a6c3b8 Olga Brani
    alias for [extractArray](#method_extractArray).
556 e4a6c3b8 Olga Brani

557 e4a6c3b8 Olga Brani
    @method extractFindAll
558 e4a6c3b8 Olga Brani
    @param {DS.Store} store
559 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
560 e4a6c3b8 Olga Brani
    @param {Object} payload
561 e4a6c3b8 Olga Brani
    @return {Array} array An array of deserialized objects
562 e4a6c3b8 Olga Brani
  */
563 e4a6c3b8 Olga Brani
  extractFindAll: aliasMethod('extractArray'),
564 e4a6c3b8 Olga Brani
  /**
565 e4a6c3b8 Olga Brani
    `extractFindQuery` is a hook into the extract method used when a
566 e4a6c3b8 Olga Brani
    call is made to `DS.Store#findQuery`. By default this method is an
567 e4a6c3b8 Olga Brani
    alias for [extractArray](#method_extractArray).
568 e4a6c3b8 Olga Brani

569 e4a6c3b8 Olga Brani
    @method extractFindQuery
570 e4a6c3b8 Olga Brani
    @param {DS.Store} store
571 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
572 e4a6c3b8 Olga Brani
    @param {Object} payload
573 e4a6c3b8 Olga Brani
    @return {Array} array An array of deserialized objects
574 e4a6c3b8 Olga Brani
  */
575 e4a6c3b8 Olga Brani
  extractFindQuery: aliasMethod('extractArray'),
576 e4a6c3b8 Olga Brani
  /**
577 e4a6c3b8 Olga Brani
    `extractFindMany` is a hook into the extract method used when a
578 e4a6c3b8 Olga Brani
    call is made to `DS.Store#findMany`. By default this method is
579 e4a6c3b8 Olga Brani
    alias for [extractArray](#method_extractArray).
580 e4a6c3b8 Olga Brani

581 e4a6c3b8 Olga Brani
    @method extractFindMany
582 e4a6c3b8 Olga Brani
    @param {DS.Store} store
583 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
584 e4a6c3b8 Olga Brani
    @param {Object} payload
585 e4a6c3b8 Olga Brani
    @return {Array} array An array of deserialized objects
586 e4a6c3b8 Olga Brani
  */
587 e4a6c3b8 Olga Brani
  extractFindMany: aliasMethod('extractArray'),
588 e4a6c3b8 Olga Brani
  /**
589 e4a6c3b8 Olga Brani
    `extractFindHasMany` is a hook into the extract method used when a
590 e4a6c3b8 Olga Brani
    call is made to `DS.Store#findHasMany`. By default this method is
591 e4a6c3b8 Olga Brani
    alias for [extractArray](#method_extractArray).
592 e4a6c3b8 Olga Brani

593 e4a6c3b8 Olga Brani
    @method extractFindHasMany
594 e4a6c3b8 Olga Brani
    @param {DS.Store} store
595 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
596 e4a6c3b8 Olga Brani
    @param {Object} payload
597 e4a6c3b8 Olga Brani
    @return {Array} array An array of deserialized objects
598 e4a6c3b8 Olga Brani
  */
599 e4a6c3b8 Olga Brani
  extractFindHasMany: aliasMethod('extractArray'),
600 e4a6c3b8 Olga Brani
601 e4a6c3b8 Olga Brani
  /**
602 e4a6c3b8 Olga Brani
    `extractCreateRecord` is a hook into the extract method used when a
603 e4a6c3b8 Olga Brani
    call is made to `DS.Store#createRecord`. By default this method is
604 e4a6c3b8 Olga Brani
    alias for [extractSave](#method_extractSave).
605 e4a6c3b8 Olga Brani

606 e4a6c3b8 Olga Brani
    @method extractCreateRecord
607 e4a6c3b8 Olga Brani
    @param {DS.Store} store
608 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
609 e4a6c3b8 Olga Brani
    @param {Object} payload
610 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
611 e4a6c3b8 Olga Brani
  */
612 e4a6c3b8 Olga Brani
  extractCreateRecord: aliasMethod('extractSave'),
613 e4a6c3b8 Olga Brani
  /**
614 e4a6c3b8 Olga Brani
    `extractUpdateRecord` is a hook into the extract method used when
615 e4a6c3b8 Olga Brani
    a call is made to `DS.Store#update`. By default this method is alias
616 e4a6c3b8 Olga Brani
    for [extractSave](#method_extractSave).
617 e4a6c3b8 Olga Brani

618 e4a6c3b8 Olga Brani
    @method extractUpdateRecord
619 e4a6c3b8 Olga Brani
    @param {DS.Store} store
620 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
621 e4a6c3b8 Olga Brani
    @param {Object} payload
622 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
623 e4a6c3b8 Olga Brani
  */
624 e4a6c3b8 Olga Brani
  extractUpdateRecord: aliasMethod('extractSave'),
625 e4a6c3b8 Olga Brani
  /**
626 e4a6c3b8 Olga Brani
    `extractDeleteRecord` is a hook into the extract method used when
627 e4a6c3b8 Olga Brani
    a call is made to `DS.Store#deleteRecord`. By default this method is
628 e4a6c3b8 Olga Brani
    alias for [extractSave](#method_extractSave).
629 e4a6c3b8 Olga Brani

630 e4a6c3b8 Olga Brani
    @method extractDeleteRecord
631 e4a6c3b8 Olga Brani
    @param {DS.Store} store
632 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
633 e4a6c3b8 Olga Brani
    @param {Object} payload
634 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
635 e4a6c3b8 Olga Brani
  */
636 e4a6c3b8 Olga Brani
  extractDeleteRecord: aliasMethod('extractSave'),
637 e4a6c3b8 Olga Brani
638 e4a6c3b8 Olga Brani
  /**
639 e4a6c3b8 Olga Brani
    `extractFind` is a hook into the extract method used when
640 e4a6c3b8 Olga Brani
    a call is made to `DS.Store#find`. By default this method is
641 e4a6c3b8 Olga Brani
    alias for [extractSingle](#method_extractSingle).
642 e4a6c3b8 Olga Brani

643 e4a6c3b8 Olga Brani
    @method extractFind
644 e4a6c3b8 Olga Brani
    @param {DS.Store} store
645 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
646 e4a6c3b8 Olga Brani
    @param {Object} payload
647 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
648 e4a6c3b8 Olga Brani
  */
649 e4a6c3b8 Olga Brani
  extractFind: aliasMethod('extractSingle'),
650 e4a6c3b8 Olga Brani
  /**
651 e4a6c3b8 Olga Brani
    `extractFindBelongsTo` is a hook into the extract method used when
652 e4a6c3b8 Olga Brani
    a call is made to `DS.Store#findBelongsTo`. By default this method is
653 e4a6c3b8 Olga Brani
    alias for [extractSingle](#method_extractSingle).
654 e4a6c3b8 Olga Brani

655 e4a6c3b8 Olga Brani
    @method extractFindBelongsTo
656 e4a6c3b8 Olga Brani
    @param {DS.Store} store
657 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
658 e4a6c3b8 Olga Brani
    @param {Object} payload
659 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
660 e4a6c3b8 Olga Brani
  */
661 e4a6c3b8 Olga Brani
  extractFindBelongsTo: aliasMethod('extractSingle'),
662 e4a6c3b8 Olga Brani
  /**
663 e4a6c3b8 Olga Brani
    `extractSave` is a hook into the extract method used when a call
664 e4a6c3b8 Olga Brani
    is made to `DS.Model#save`. By default this method is alias
665 e4a6c3b8 Olga Brani
    for [extractSingle](#method_extractSingle).
666 e4a6c3b8 Olga Brani

667 e4a6c3b8 Olga Brani
    @method extractSave
668 e4a6c3b8 Olga Brani
    @param {DS.Store} store
669 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
670 e4a6c3b8 Olga Brani
    @param {Object} payload
671 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
672 e4a6c3b8 Olga Brani
  */
673 e4a6c3b8 Olga Brani
  extractSave: aliasMethod('extractSingle'),
674 e4a6c3b8 Olga Brani
675 e4a6c3b8 Olga Brani
  /**
676 e4a6c3b8 Olga Brani
    `extractSingle` is used to deserialize a single record returned
677 e4a6c3b8 Olga Brani
    from the adapter.
678 e4a6c3b8 Olga Brani

679 e4a6c3b8 Olga Brani
    Example
680 e4a6c3b8 Olga Brani

681 e4a6c3b8 Olga Brani
    ```javascript
682 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.JSONSerializer.extend({
683 e4a6c3b8 Olga Brani
      extractSingle: function(store, type, payload) {
684 e4a6c3b8 Olga Brani
        payload.comments = payload._embedded.comment;
685 e4a6c3b8 Olga Brani
        delete payload._embedded;
686 e4a6c3b8 Olga Brani

687 e4a6c3b8 Olga Brani
        return this._super(store, type, payload);
688 e4a6c3b8 Olga Brani
      },
689 e4a6c3b8 Olga Brani
    });
690 e4a6c3b8 Olga Brani
    ```
691 e4a6c3b8 Olga Brani

692 e4a6c3b8 Olga Brani
    @method extractSingle
693 e4a6c3b8 Olga Brani
    @param {DS.Store} store
694 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
695 e4a6c3b8 Olga Brani
    @param {Object} payload
696 e4a6c3b8 Olga Brani
    @return {Object} json The deserialized payload
697 e4a6c3b8 Olga Brani
  */
698 e4a6c3b8 Olga Brani
  extractSingle: function(store, type, payload) {
699 e4a6c3b8 Olga Brani
    return this.normalize(type, payload);
700 e4a6c3b8 Olga Brani
  },
701 e4a6c3b8 Olga Brani
702 e4a6c3b8 Olga Brani
  /**
703 e4a6c3b8 Olga Brani
    `extractArray` is used to deserialize an array of records
704 e4a6c3b8 Olga Brani
    returned from the adapter.
705 e4a6c3b8 Olga Brani

706 e4a6c3b8 Olga Brani
    Example
707 e4a6c3b8 Olga Brani

708 e4a6c3b8 Olga Brani
    ```javascript
709 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.JSONSerializer.extend({
710 e4a6c3b8 Olga Brani
      extractArray: function(store, type, payload) {
711 e4a6c3b8 Olga Brani
        return payload.map(function(json) {
712 e4a6c3b8 Olga Brani
          return this.extractSingle(json);
713 e4a6c3b8 Olga Brani
        }, this);
714 e4a6c3b8 Olga Brani
      }
715 e4a6c3b8 Olga Brani
    });
716 e4a6c3b8 Olga Brani
    ```
717 e4a6c3b8 Olga Brani

718 e4a6c3b8 Olga Brani
    @method extractArray
719 e4a6c3b8 Olga Brani
    @param {DS.Store} store
720 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
721 e4a6c3b8 Olga Brani
    @param {Object} payload
722 e4a6c3b8 Olga Brani
    @return {Array} array An array of deserialized objects
723 e4a6c3b8 Olga Brani
  */
724 e4a6c3b8 Olga Brani
  extractArray: function(store, type, payload) {
725 e4a6c3b8 Olga Brani
    return this.normalize(type, payload);
726 e4a6c3b8 Olga Brani
  },
727 e4a6c3b8 Olga Brani
728 e4a6c3b8 Olga Brani
  /**
729 e4a6c3b8 Olga Brani
    `extractMeta` is used to deserialize any meta information in the
730 e4a6c3b8 Olga Brani
    adapter payload. By default Ember Data expects meta information to
731 e4a6c3b8 Olga Brani
    be located on the `meta` property of the payload object.
732 e4a6c3b8 Olga Brani

733 e4a6c3b8 Olga Brani
    Example
734 e4a6c3b8 Olga Brani

735 e4a6c3b8 Olga Brani
    ```javascript
736 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.JSONSerializer.extend({
737 e4a6c3b8 Olga Brani
      extractMeta: function(store, type, payload) {
738 e4a6c3b8 Olga Brani
        if (payload && payload._pagination) {
739 e4a6c3b8 Olga Brani
          store.metaForType(type, payload._pagination);
740 e4a6c3b8 Olga Brani
          delete payload._pagination;
741 e4a6c3b8 Olga Brani
        }
742 e4a6c3b8 Olga Brani
      }
743 e4a6c3b8 Olga Brani
    });
744 e4a6c3b8 Olga Brani
    ```
745 e4a6c3b8 Olga Brani

746 e4a6c3b8 Olga Brani
    @method extractMeta
747 e4a6c3b8 Olga Brani
    @param {DS.Store} store
748 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
749 e4a6c3b8 Olga Brani
    @param {Object} payload
750 e4a6c3b8 Olga Brani
  */
751 e4a6c3b8 Olga Brani
  extractMeta: function(store, type, payload) {
752 e4a6c3b8 Olga Brani
    if (payload && payload.meta) {
753 e4a6c3b8 Olga Brani
      store.metaForType(type, payload.meta);
754 e4a6c3b8 Olga Brani
      delete payload.meta;
755 e4a6c3b8 Olga Brani
    }
756 e4a6c3b8 Olga Brani
  },
757 e4a6c3b8 Olga Brani
758 e4a6c3b8 Olga Brani
  /**
759 e4a6c3b8 Olga Brani
   `keyForAttribute` can be used to define rules for how to convert an
760 e4a6c3b8 Olga Brani
   attribute name in your model to a key in your JSON.
761 e4a6c3b8 Olga Brani

762 e4a6c3b8 Olga Brani
   Example
763 e4a6c3b8 Olga Brani

764 e4a6c3b8 Olga Brani
   ```javascript
765 e4a6c3b8 Olga Brani
   App.ApplicationSerializer = DS.RESTSerializer.extend({
766 e4a6c3b8 Olga Brani
     keyForAttribute: function(attr) {
767 e4a6c3b8 Olga Brani
       return Ember.String.underscore(attr).toUpperCase();
768 e4a6c3b8 Olga Brani
     }
769 e4a6c3b8 Olga Brani
   });
770 e4a6c3b8 Olga Brani
   ```
771 e4a6c3b8 Olga Brani

772 e4a6c3b8 Olga Brani
   @method keyForAttribute
773 e4a6c3b8 Olga Brani
   @param {String} key
774 e4a6c3b8 Olga Brani
   @return {String} normalized key
775 e4a6c3b8 Olga Brani
  */
776 e4a6c3b8 Olga Brani
777 e4a6c3b8 Olga Brani
778 e4a6c3b8 Olga Brani
  /**
779 e4a6c3b8 Olga Brani
   `keyForRelationship` can be used to define a custom key when
780 e4a6c3b8 Olga Brani
   serializeing relationship properties. By default `JSONSerializer`
781 e4a6c3b8 Olga Brani
   does not provide an implementation of this method.
782 e4a6c3b8 Olga Brani

783 e4a6c3b8 Olga Brani
   Example
784 e4a6c3b8 Olga Brani

785 e4a6c3b8 Olga Brani
    ```javascript
786 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.JSONSerializer.extend({
787 e4a6c3b8 Olga Brani
      keyForRelationship: function(key, relationship) {
788 e4a6c3b8 Olga Brani
         return 'rel_' + Ember.String.underscore(key);
789 e4a6c3b8 Olga Brani
      }
790 e4a6c3b8 Olga Brani
    });
791 e4a6c3b8 Olga Brani
    ```
792 e4a6c3b8 Olga Brani

793 e4a6c3b8 Olga Brani
   @method keyForRelationship
794 e4a6c3b8 Olga Brani
   @param {String} key
795 e4a6c3b8 Olga Brani
   @param {String} relationship type
796 e4a6c3b8 Olga Brani
   @return {String} normalized key
797 e4a6c3b8 Olga Brani
  */
798 e4a6c3b8 Olga Brani
799 e4a6c3b8 Olga Brani
  // HELPERS
800 e4a6c3b8 Olga Brani
801 e4a6c3b8 Olga Brani
  /**
802 e4a6c3b8 Olga Brani
   @method transformFor
803 e4a6c3b8 Olga Brani
   @private
804 e4a6c3b8 Olga Brani
   @param {String} attributeType
805 e4a6c3b8 Olga Brani
   @param {Boolean} skipAssertion
806 e4a6c3b8 Olga Brani
   @return {DS.Transform} transform
807 e4a6c3b8 Olga Brani
  */
808 e4a6c3b8 Olga Brani
  transformFor: function(attributeType, skipAssertion) {
809 e4a6c3b8 Olga Brani
    var transform = this.container.lookup('transform:' + attributeType);
810 e4a6c3b8 Olga Brani
    Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform);
811 e4a6c3b8 Olga Brani
    return transform;
812 e4a6c3b8 Olga Brani
  }
813 e4a6c3b8 Olga Brani
});
814 e4a6c3b8 Olga Brani
815 e4a6c3b8 Olga Brani
})();
816 e4a6c3b8 Olga Brani
817 e4a6c3b8 Olga Brani
818 e4a6c3b8 Olga Brani
819 e4a6c3b8 Olga Brani
(function() {
820 e4a6c3b8 Olga Brani
/**
821 e4a6c3b8 Olga Brani
  @module ember-data
822 e4a6c3b8 Olga Brani
*/
823 e4a6c3b8 Olga Brani
var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore, DS = window.DS ;
824 e4a6c3b8 Olga Brani
825 e4a6c3b8 Olga Brani
/**
826 e4a6c3b8 Olga Brani
  Extend `Ember.DataAdapter` with ED specific code.
827 e4a6c3b8 Olga Brani

828 e4a6c3b8 Olga Brani
  @class DebugAdapter
829 e4a6c3b8 Olga Brani
  @namespace DS
830 e4a6c3b8 Olga Brani
  @extends Ember.DataAdapter
831 e4a6c3b8 Olga Brani
  @private
832 e4a6c3b8 Olga Brani
*/
833 e4a6c3b8 Olga Brani
DS.DebugAdapter = Ember.DataAdapter.extend({
834 e4a6c3b8 Olga Brani
  getFilters: function() {
835 e4a6c3b8 Olga Brani
    return [
836 e4a6c3b8 Olga Brani
      { name: 'isNew', desc: 'New' },
837 e4a6c3b8 Olga Brani
      { name: 'isModified', desc: 'Modified' },
838 e4a6c3b8 Olga Brani
      { name: 'isClean', desc: 'Clean' }
839 e4a6c3b8 Olga Brani
    ];
840 e4a6c3b8 Olga Brani
  },
841 e4a6c3b8 Olga Brani
842 e4a6c3b8 Olga Brani
  detect: function(klass) {
843 e4a6c3b8 Olga Brani
    return klass !== DS.Model && DS.Model.detect(klass);
844 e4a6c3b8 Olga Brani
  },
845 e4a6c3b8 Olga Brani
846 e4a6c3b8 Olga Brani
  columnsForType: function(type) {
847 e4a6c3b8 Olga Brani
    var columns = [{ name: 'id', desc: 'Id' }], count = 0, self = this;
848 e4a6c3b8 Olga Brani
    get(type, 'attributes').forEach(function(name, meta) {
849 e4a6c3b8 Olga Brani
        if (count++ > self.attributeLimit) { return false; }
850 e4a6c3b8 Olga Brani
        var desc = capitalize(underscore(name).replace('_', ' '));
851 e4a6c3b8 Olga Brani
        columns.push({ name: name, desc: desc });
852 e4a6c3b8 Olga Brani
    });
853 e4a6c3b8 Olga Brani
    return columns;
854 e4a6c3b8 Olga Brani
  },
855 e4a6c3b8 Olga Brani
856 e4a6c3b8 Olga Brani
  getRecords: function(type) {
857 e4a6c3b8 Olga Brani
    return this.get('store').all(type);
858 e4a6c3b8 Olga Brani
  },
859 e4a6c3b8 Olga Brani
860 e4a6c3b8 Olga Brani
  getRecordColumnValues: function(record) {
861 e4a6c3b8 Olga Brani
    var self = this, count = 0,
862 e4a6c3b8 Olga Brani
        columnValues = { id: get(record, 'id') };
863 e4a6c3b8 Olga Brani
864 e4a6c3b8 Olga Brani
    record.eachAttribute(function(key) {
865 e4a6c3b8 Olga Brani
      if (count++ > self.attributeLimit) {
866 e4a6c3b8 Olga Brani
        return false;
867 e4a6c3b8 Olga Brani
      }
868 e4a6c3b8 Olga Brani
      var value = get(record, key);
869 e4a6c3b8 Olga Brani
      columnValues[key] = value;
870 e4a6c3b8 Olga Brani
    });
871 e4a6c3b8 Olga Brani
    return columnValues;
872 e4a6c3b8 Olga Brani
  },
873 e4a6c3b8 Olga Brani
874 e4a6c3b8 Olga Brani
  getRecordKeywords: function(record) {
875 e4a6c3b8 Olga Brani
    var keywords = [], keys = Ember.A(['id']);
876 e4a6c3b8 Olga Brani
    record.eachAttribute(function(key) {
877 e4a6c3b8 Olga Brani
      keys.push(key);
878 e4a6c3b8 Olga Brani
    });
879 e4a6c3b8 Olga Brani
    keys.forEach(function(key) {
880 e4a6c3b8 Olga Brani
      keywords.push(get(record, key));
881 e4a6c3b8 Olga Brani
    });
882 e4a6c3b8 Olga Brani
    return keywords;
883 e4a6c3b8 Olga Brani
  },
884 e4a6c3b8 Olga Brani
885 e4a6c3b8 Olga Brani
  getRecordFilterValues: function(record) {
886 e4a6c3b8 Olga Brani
    return {
887 e4a6c3b8 Olga Brani
      isNew: record.get('isNew'),
888 e4a6c3b8 Olga Brani
      isModified: record.get('isDirty') && !record.get('isNew'),
889 e4a6c3b8 Olga Brani
      isClean: !record.get('isDirty')
890 e4a6c3b8 Olga Brani
    };
891 e4a6c3b8 Olga Brani
  },
892 e4a6c3b8 Olga Brani
893 e4a6c3b8 Olga Brani
  getRecordColor: function(record) {
894 e4a6c3b8 Olga Brani
    var color = 'black';
895 e4a6c3b8 Olga Brani
    if (record.get('isNew')) {
896 e4a6c3b8 Olga Brani
      color = 'green';
897 e4a6c3b8 Olga Brani
    } else if (record.get('isDirty')) {
898 e4a6c3b8 Olga Brani
      color = 'blue';
899 e4a6c3b8 Olga Brani
    }
900 e4a6c3b8 Olga Brani
    return color;
901 e4a6c3b8 Olga Brani
  },
902 e4a6c3b8 Olga Brani
903 e4a6c3b8 Olga Brani
  observeRecord: function(record, recordUpdated) {
904 e4a6c3b8 Olga Brani
    var releaseMethods = Ember.A(), self = this,
905 e4a6c3b8 Olga Brani
        keysToObserve = Ember.A(['id', 'isNew', 'isDirty']);
906 e4a6c3b8 Olga Brani
907 e4a6c3b8 Olga Brani
    record.eachAttribute(function(key) {
908 e4a6c3b8 Olga Brani
      keysToObserve.push(key);
909 e4a6c3b8 Olga Brani
    });
910 e4a6c3b8 Olga Brani
911 e4a6c3b8 Olga Brani
    keysToObserve.forEach(function(key) {
912 e4a6c3b8 Olga Brani
      var handler = function() {
913 e4a6c3b8 Olga Brani
        recordUpdated(self.wrapRecord(record));
914 e4a6c3b8 Olga Brani
      };
915 e4a6c3b8 Olga Brani
      Ember.addObserver(record, key, handler);
916 e4a6c3b8 Olga Brani
      releaseMethods.push(function() {
917 e4a6c3b8 Olga Brani
        Ember.removeObserver(record, key, handler);
918 e4a6c3b8 Olga Brani
      });
919 e4a6c3b8 Olga Brani
    });
920 e4a6c3b8 Olga Brani
921 e4a6c3b8 Olga Brani
    var release = function() {
922 e4a6c3b8 Olga Brani
      releaseMethods.forEach(function(fn) { fn(); } );
923 e4a6c3b8 Olga Brani
    };
924 e4a6c3b8 Olga Brani
925 e4a6c3b8 Olga Brani
    return release;
926 e4a6c3b8 Olga Brani
  }
927 e4a6c3b8 Olga Brani
928 e4a6c3b8 Olga Brani
});
929 e4a6c3b8 Olga Brani
930 e4a6c3b8 Olga Brani
})();
931 e4a6c3b8 Olga Brani
932 e4a6c3b8 Olga Brani
933 e4a6c3b8 Olga Brani
934 e4a6c3b8 Olga Brani
(function() {
935 e4a6c3b8 Olga Brani
/**
936 e4a6c3b8 Olga Brani
  The `DS.Transform` class is used to serialize and deserialize model
937 e4a6c3b8 Olga Brani
  attributes when they are saved or loaded from an
938 e4a6c3b8 Olga Brani
  adapter. Subclassing `DS.Transform` is useful for creating custom
939 e4a6c3b8 Olga Brani
  attributes. All subclasses of `DS.Transform` must implement a
940 e4a6c3b8 Olga Brani
  `serialize` and a `deserialize` method.
941 e4a6c3b8 Olga Brani

942 e4a6c3b8 Olga Brani
  Example
943 e4a6c3b8 Olga Brani

944 e4a6c3b8 Olga Brani
  ```javascript
945 e4a6c3b8 Olga Brani
  App.RawTransform = DS.Transform.extend({
946 e4a6c3b8 Olga Brani
    deserialize: function(serialized) {
947 e4a6c3b8 Olga Brani
      return serialized;
948 e4a6c3b8 Olga Brani
    },
949 e4a6c3b8 Olga Brani
    serialize: function(deserialized) {
950 e4a6c3b8 Olga Brani
      return deserialized;
951 e4a6c3b8 Olga Brani
    }
952 e4a6c3b8 Olga Brani
  });
953 e4a6c3b8 Olga Brani
  ```
954 e4a6c3b8 Olga Brani

955 e4a6c3b8 Olga Brani
  Usage
956 e4a6c3b8 Olga Brani

957 e4a6c3b8 Olga Brani
  ```javascript
958 e4a6c3b8 Olga Brani
  var attr = DS.attr;
959 e4a6c3b8 Olga Brani
  App.Requirement = DS.Model.extend({
960 e4a6c3b8 Olga Brani
    name: attr('string'),
961 e4a6c3b8 Olga Brani
    optionsArray: attr('raw')
962 e4a6c3b8 Olga Brani
  });
963 e4a6c3b8 Olga Brani
  ```
964 e4a6c3b8 Olga Brani

965 e4a6c3b8 Olga Brani
  @class Transform
966 e4a6c3b8 Olga Brani
  @namespace DS
967 e4a6c3b8 Olga Brani
 */
968 e4a6c3b8 Olga Brani
DS.Transform = Ember.Object.extend({
969 e4a6c3b8 Olga Brani
  /**
970 e4a6c3b8 Olga Brani
    When given a deserialized value from a record attribute this
971 e4a6c3b8 Olga Brani
    method must return the serialized value.
972 e4a6c3b8 Olga Brani

973 e4a6c3b8 Olga Brani
    Example
974 e4a6c3b8 Olga Brani

975 e4a6c3b8 Olga Brani
    ```javascript
976 e4a6c3b8 Olga Brani
    serialize: function(deserialized) {
977 e4a6c3b8 Olga Brani
      return Ember.isEmpty(deserialized) ? null : Number(deserialized);
978 e4a6c3b8 Olga Brani
    }
979 e4a6c3b8 Olga Brani
    ```
980 e4a6c3b8 Olga Brani

981 e4a6c3b8 Olga Brani
    @method serialize
982 e4a6c3b8 Olga Brani
    @param deserialized The deserialized value
983 e4a6c3b8 Olga Brani
    @return The serialized value
984 e4a6c3b8 Olga Brani
  */
985 e4a6c3b8 Olga Brani
  serialize: Ember.required(),
986 e4a6c3b8 Olga Brani
987 e4a6c3b8 Olga Brani
  /**
988 e4a6c3b8 Olga Brani
    When given a serialize value from a JSON object this method must
989 e4a6c3b8 Olga Brani
    return the deserialized value for the record attribute.
990 e4a6c3b8 Olga Brani

991 e4a6c3b8 Olga Brani
    Example
992 e4a6c3b8 Olga Brani

993 e4a6c3b8 Olga Brani
    ```javascript
994 e4a6c3b8 Olga Brani
    deserialize: function(serialized) {
995 e4a6c3b8 Olga Brani
      return empty(serialized) ? null : Number(serialized);
996 e4a6c3b8 Olga Brani
    }
997 e4a6c3b8 Olga Brani
    ```
998 e4a6c3b8 Olga Brani

999 e4a6c3b8 Olga Brani
    @method deserialize
1000 e4a6c3b8 Olga Brani
    @param serialized The serialized value
1001 e4a6c3b8 Olga Brani
    @return The deserialized value
1002 e4a6c3b8 Olga Brani
  */
1003 e4a6c3b8 Olga Brani
  deserialize: Ember.required()
1004 e4a6c3b8 Olga Brani
1005 e4a6c3b8 Olga Brani
});
1006 e4a6c3b8 Olga Brani
1007 e4a6c3b8 Olga Brani
})();
1008 e4a6c3b8 Olga Brani
1009 e4a6c3b8 Olga Brani
1010 e4a6c3b8 Olga Brani
1011 e4a6c3b8 Olga Brani
(function() {
1012 e4a6c3b8 Olga Brani
1013 e4a6c3b8 Olga Brani
/**
1014 e4a6c3b8 Olga Brani
  The `DS.BooleanTransform` class is used to serialize and deserialize
1015 e4a6c3b8 Olga Brani
  boolean attributes on Ember Data record objects. This transform is
1016 e4a6c3b8 Olga Brani
  used when `boolean` is passed as the type parameter to the
1017 e4a6c3b8 Olga Brani
  [DS.attr](../../data#method_attr) function.
1018 e4a6c3b8 Olga Brani

1019 e4a6c3b8 Olga Brani
  Usage
1020 e4a6c3b8 Olga Brani

1021 e4a6c3b8 Olga Brani
  ```javascript
1022 e4a6c3b8 Olga Brani
  var attr = DS.attr;
1023 e4a6c3b8 Olga Brani
  App.User = DS.Model.extend({
1024 e4a6c3b8 Olga Brani
    isAdmin: attr('boolean'),
1025 e4a6c3b8 Olga Brani
    name: attr('string'),
1026 e4a6c3b8 Olga Brani
    email: attr('string')
1027 e4a6c3b8 Olga Brani
  });
1028 e4a6c3b8 Olga Brani
  ```
1029 e4a6c3b8 Olga Brani

1030 e4a6c3b8 Olga Brani
  @class BooleanTransform
1031 e4a6c3b8 Olga Brani
  @extends DS.Transform
1032 e4a6c3b8 Olga Brani
  @namespace DS
1033 e4a6c3b8 Olga Brani
 */
1034 e4a6c3b8 Olga Brani
DS.BooleanTransform = DS.Transform.extend({
1035 e4a6c3b8 Olga Brani
  deserialize: function(serialized) {
1036 e4a6c3b8 Olga Brani
    var type = typeof serialized;
1037 e4a6c3b8 Olga Brani
1038 e4a6c3b8 Olga Brani
    if (type === "boolean") {
1039 e4a6c3b8 Olga Brani
      return serialized;
1040 e4a6c3b8 Olga Brani
    } else if (type === "string") {
1041 e4a6c3b8 Olga Brani
      return serialized.match(/^true$|^t$|^1$/i) !== null;
1042 e4a6c3b8 Olga Brani
    } else if (type === "number") {
1043 e4a6c3b8 Olga Brani
      return serialized === 1;
1044 e4a6c3b8 Olga Brani
    } else {
1045 e4a6c3b8 Olga Brani
      return false;
1046 e4a6c3b8 Olga Brani
    }
1047 e4a6c3b8 Olga Brani
  },
1048 e4a6c3b8 Olga Brani
1049 e4a6c3b8 Olga Brani
  serialize: function(deserialized) {
1050 e4a6c3b8 Olga Brani
    return Boolean(deserialized);
1051 e4a6c3b8 Olga Brani
  }
1052 e4a6c3b8 Olga Brani
});
1053 e4a6c3b8 Olga Brani
1054 e4a6c3b8 Olga Brani
})();
1055 e4a6c3b8 Olga Brani
1056 e4a6c3b8 Olga Brani
1057 e4a6c3b8 Olga Brani
1058 e4a6c3b8 Olga Brani
(function() {
1059 e4a6c3b8 Olga Brani
/**
1060 e4a6c3b8 Olga Brani
  The `DS.DateTransform` class is used to serialize and deserialize
1061 e4a6c3b8 Olga Brani
  date attributes on Ember Data record objects. This transform is used
1062 e4a6c3b8 Olga Brani
  when `date` is passed as the type parameter to the
1063 e4a6c3b8 Olga Brani
  [DS.attr](../../data#method_attr) function.
1064 e4a6c3b8 Olga Brani

1065 e4a6c3b8 Olga Brani
  ```javascript
1066 e4a6c3b8 Olga Brani
  var attr = DS.attr;
1067 e4a6c3b8 Olga Brani
  App.Score = DS.Model.extend({
1068 e4a6c3b8 Olga Brani
    value: attr('number'),
1069 e4a6c3b8 Olga Brani
    player: DS.belongsTo('player'),
1070 e4a6c3b8 Olga Brani
    date: attr('date')
1071 e4a6c3b8 Olga Brani
  });
1072 e4a6c3b8 Olga Brani
  ```
1073 e4a6c3b8 Olga Brani

1074 e4a6c3b8 Olga Brani
  @class DateTransform
1075 e4a6c3b8 Olga Brani
  @extends DS.Transform
1076 e4a6c3b8 Olga Brani
  @namespace DS
1077 e4a6c3b8 Olga Brani
 */
1078 e4a6c3b8 Olga Brani
DS.DateTransform = DS.Transform.extend({
1079 e4a6c3b8 Olga Brani
1080 e4a6c3b8 Olga Brani
  deserialize: function(serialized) {
1081 e4a6c3b8 Olga Brani
    var type = typeof serialized;
1082 e4a6c3b8 Olga Brani
1083 e4a6c3b8 Olga Brani
    if (type === "string") {
1084 e4a6c3b8 Olga Brani
      return new Date(Ember.Date.parse(serialized));
1085 e4a6c3b8 Olga Brani
    } else if (type === "number") {
1086 e4a6c3b8 Olga Brani
      return new Date(serialized);
1087 e4a6c3b8 Olga Brani
    } else if (serialized === null || serialized === undefined) {
1088 e4a6c3b8 Olga Brani
      // if the value is not present in the data,
1089 e4a6c3b8 Olga Brani
      // return undefined, not null.
1090 e4a6c3b8 Olga Brani
      return serialized;
1091 e4a6c3b8 Olga Brani
    } else {
1092 e4a6c3b8 Olga Brani
      return null;
1093 e4a6c3b8 Olga Brani
    }
1094 e4a6c3b8 Olga Brani
  },
1095 e4a6c3b8 Olga Brani
1096 e4a6c3b8 Olga Brani
  serialize: function(date) {
1097 e4a6c3b8 Olga Brani
    if (date instanceof Date) {
1098 e4a6c3b8 Olga Brani
      var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1099 e4a6c3b8 Olga Brani
      var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1100 e4a6c3b8 Olga Brani
1101 e4a6c3b8 Olga Brani
      var pad = function(num) {
1102 e4a6c3b8 Olga Brani
        return num < 10 ? "0"+num : ""+num;
1103 e4a6c3b8 Olga Brani
      };
1104 e4a6c3b8 Olga Brani
1105 e4a6c3b8 Olga Brani
      var utcYear = date.getUTCFullYear(),
1106 e4a6c3b8 Olga Brani
          utcMonth = date.getUTCMonth(),
1107 e4a6c3b8 Olga Brani
          utcDayOfMonth = date.getUTCDate(),
1108 e4a6c3b8 Olga Brani
          utcDay = date.getUTCDay(),
1109 e4a6c3b8 Olga Brani
          utcHours = date.getUTCHours(),
1110 e4a6c3b8 Olga Brani
          utcMinutes = date.getUTCMinutes(),
1111 e4a6c3b8 Olga Brani
          utcSeconds = date.getUTCSeconds();
1112 e4a6c3b8 Olga Brani
1113 e4a6c3b8 Olga Brani
1114 e4a6c3b8 Olga Brani
      var dayOfWeek = days[utcDay];
1115 e4a6c3b8 Olga Brani
      var dayOfMonth = pad(utcDayOfMonth);
1116 e4a6c3b8 Olga Brani
      var month = months[utcMonth];
1117 e4a6c3b8 Olga Brani
1118 e4a6c3b8 Olga Brani
      return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
1119 e4a6c3b8 Olga Brani
             pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
1120 e4a6c3b8 Olga Brani
    } else {
1121 e4a6c3b8 Olga Brani
      return null;
1122 e4a6c3b8 Olga Brani
    }
1123 e4a6c3b8 Olga Brani
  } 
1124 e4a6c3b8 Olga Brani
1125 e4a6c3b8 Olga Brani
});
1126 e4a6c3b8 Olga Brani
1127 e4a6c3b8 Olga Brani
})();
1128 e4a6c3b8 Olga Brani
1129 e4a6c3b8 Olga Brani
1130 e4a6c3b8 Olga Brani
1131 e4a6c3b8 Olga Brani
(function() {
1132 e4a6c3b8 Olga Brani
var empty = Ember.isEmpty;
1133 e4a6c3b8 Olga Brani
/**
1134 e4a6c3b8 Olga Brani
  The `DS.NumberTransform` class is used to serialize and deserialize
1135 e4a6c3b8 Olga Brani
  numeric attributes on Ember Data record objects. This transform is
1136 e4a6c3b8 Olga Brani
  used when `number` is passed as the type parameter to the
1137 e4a6c3b8 Olga Brani
  [DS.attr](../../data#method_attr) function.
1138 e4a6c3b8 Olga Brani

1139 e4a6c3b8 Olga Brani
  Usage
1140 e4a6c3b8 Olga Brani

1141 e4a6c3b8 Olga Brani
  ```javascript
1142 e4a6c3b8 Olga Brani
  var attr = DS.attr;
1143 e4a6c3b8 Olga Brani
  App.Score = DS.Model.extend({
1144 e4a6c3b8 Olga Brani
    value: attr('number'),
1145 e4a6c3b8 Olga Brani
    player: DS.belongsTo('player'),
1146 e4a6c3b8 Olga Brani
    date: attr('date')
1147 e4a6c3b8 Olga Brani
  });
1148 e4a6c3b8 Olga Brani
  ```
1149 e4a6c3b8 Olga Brani

1150 e4a6c3b8 Olga Brani
  @class NumberTransform
1151 e4a6c3b8 Olga Brani
  @extends DS.Transform
1152 e4a6c3b8 Olga Brani
  @namespace DS
1153 e4a6c3b8 Olga Brani
 */
1154 e4a6c3b8 Olga Brani
DS.NumberTransform = DS.Transform.extend({
1155 e4a6c3b8 Olga Brani
1156 e4a6c3b8 Olga Brani
  deserialize: function(serialized) {
1157 e4a6c3b8 Olga Brani
    return empty(serialized) ? null : Number(serialized);
1158 e4a6c3b8 Olga Brani
  },
1159 e4a6c3b8 Olga Brani
1160 e4a6c3b8 Olga Brani
  serialize: function(deserialized) {
1161 e4a6c3b8 Olga Brani
    return empty(deserialized) ? null : Number(deserialized);
1162 e4a6c3b8 Olga Brani
  }
1163 e4a6c3b8 Olga Brani
});
1164 e4a6c3b8 Olga Brani
1165 e4a6c3b8 Olga Brani
})();
1166 e4a6c3b8 Olga Brani
1167 e4a6c3b8 Olga Brani
1168 e4a6c3b8 Olga Brani
1169 e4a6c3b8 Olga Brani
(function() {
1170 e4a6c3b8 Olga Brani
var none = Ember.isNone;
1171 e4a6c3b8 Olga Brani
1172 e4a6c3b8 Olga Brani
/**
1173 e4a6c3b8 Olga Brani
  The `DS.StringTransform` class is used to serialize and deserialize
1174 e4a6c3b8 Olga Brani
  string attributes on Ember Data record objects. This transform is
1175 e4a6c3b8 Olga Brani
  used when `string` is passed as the type parameter to the
1176 e4a6c3b8 Olga Brani
  [DS.attr](../../data#method_attr) function.
1177 e4a6c3b8 Olga Brani

1178 e4a6c3b8 Olga Brani
  Usage
1179 e4a6c3b8 Olga Brani

1180 e4a6c3b8 Olga Brani
  ```javascript
1181 e4a6c3b8 Olga Brani
  var attr = DS.attr;
1182 e4a6c3b8 Olga Brani
  App.User = DS.Model.extend({
1183 e4a6c3b8 Olga Brani
    isAdmin: attr('boolean'),
1184 e4a6c3b8 Olga Brani
    name: attr('string'),
1185 e4a6c3b8 Olga Brani
    email: attr('string')
1186 e4a6c3b8 Olga Brani
  });
1187 e4a6c3b8 Olga Brani
  ```
1188 e4a6c3b8 Olga Brani

1189 e4a6c3b8 Olga Brani
  @class StringTransform
1190 e4a6c3b8 Olga Brani
  @extends DS.Transform
1191 e4a6c3b8 Olga Brani
  @namespace DS
1192 e4a6c3b8 Olga Brani
 */
1193 e4a6c3b8 Olga Brani
DS.StringTransform = DS.Transform.extend({
1194 e4a6c3b8 Olga Brani
1195 e4a6c3b8 Olga Brani
  deserialize: function(serialized) {
1196 e4a6c3b8 Olga Brani
    return none(serialized) ? null : String(serialized);
1197 e4a6c3b8 Olga Brani
  },
1198 e4a6c3b8 Olga Brani
1199 e4a6c3b8 Olga Brani
  serialize: function(deserialized) {
1200 e4a6c3b8 Olga Brani
    return none(deserialized) ? null : String(deserialized);
1201 e4a6c3b8 Olga Brani
  }
1202 e4a6c3b8 Olga Brani
1203 e4a6c3b8 Olga Brani
});
1204 e4a6c3b8 Olga Brani
1205 e4a6c3b8 Olga Brani
})();
1206 e4a6c3b8 Olga Brani
1207 e4a6c3b8 Olga Brani
1208 e4a6c3b8 Olga Brani
1209 e4a6c3b8 Olga Brani
(function() {
1210 e4a6c3b8 Olga Brani
1211 e4a6c3b8 Olga Brani
})();
1212 e4a6c3b8 Olga Brani
1213 e4a6c3b8 Olga Brani
1214 e4a6c3b8 Olga Brani
1215 e4a6c3b8 Olga Brani
(function() {
1216 e4a6c3b8 Olga Brani
/**
1217 e4a6c3b8 Olga Brani
  @module ember-data
1218 e4a6c3b8 Olga Brani
*/
1219 e4a6c3b8 Olga Brani
1220 e4a6c3b8 Olga Brani
var set = Ember.set;
1221 e4a6c3b8 Olga Brani
1222 e4a6c3b8 Olga Brani
/*
1223 e4a6c3b8 Olga Brani
  This code registers an injection for Ember.Application.
1224 e4a6c3b8 Olga Brani

1225 e4a6c3b8 Olga Brani
  If an Ember.js developer defines a subclass of DS.Store on their application,
1226 e4a6c3b8 Olga Brani
  this code will automatically instantiate it and make it available on the
1227 e4a6c3b8 Olga Brani
  router.
1228 e4a6c3b8 Olga Brani

1229 e4a6c3b8 Olga Brani
  Additionally, after an application's controllers have been injected, they will
1230 e4a6c3b8 Olga Brani
  each have the store made available to them.
1231 e4a6c3b8 Olga Brani

1232 e4a6c3b8 Olga Brani
  For example, imagine an Ember.js application with the following classes:
1233 e4a6c3b8 Olga Brani

1234 e4a6c3b8 Olga Brani
  App.Store = DS.Store.extend({
1235 e4a6c3b8 Olga Brani
    adapter: 'custom'
1236 e4a6c3b8 Olga Brani
  });
1237 e4a6c3b8 Olga Brani

1238 e4a6c3b8 Olga Brani
  App.PostsController = Ember.ArrayController.extend({
1239 e4a6c3b8 Olga Brani
    // ...
1240 e4a6c3b8 Olga Brani
  });
1241 e4a6c3b8 Olga Brani

1242 e4a6c3b8 Olga Brani
  When the application is initialized, `App.Store` will automatically be
1243 e4a6c3b8 Olga Brani
  instantiated, and the instance of `App.PostsController` will have its `store`
1244 e4a6c3b8 Olga Brani
  property set to that instance.
1245 e4a6c3b8 Olga Brani

1246 e4a6c3b8 Olga Brani
  Note that this code will only be run if the `ember-application` package is
1247 e4a6c3b8 Olga Brani
  loaded. If Ember Data is being used in an environment other than a
1248 e4a6c3b8 Olga Brani
  typical application (e.g., node.js where only `ember-runtime` is available),
1249 e4a6c3b8 Olga Brani
  this code will be ignored.
1250 e4a6c3b8 Olga Brani
*/
1251 e4a6c3b8 Olga Brani
1252 e4a6c3b8 Olga Brani
Ember.onLoad('Ember.Application', function(Application) {
1253 e4a6c3b8 Olga Brani
  Application.initializer({
1254 e4a6c3b8 Olga Brani
    name: "store",
1255 e4a6c3b8 Olga Brani
1256 e4a6c3b8 Olga Brani
    initialize: function(container, application) {
1257 e4a6c3b8 Olga Brani
      application.register('store:main', application.Store || DS.Store);
1258 e4a6c3b8 Olga Brani
      application.register('serializer:_default', DS.JSONSerializer);
1259 e4a6c3b8 Olga Brani
      application.register('serializer:_rest', DS.RESTSerializer);
1260 e4a6c3b8 Olga Brani
      application.register('adapter:_rest', DS.RESTAdapter);
1261 e4a6c3b8 Olga Brani
1262 e4a6c3b8 Olga Brani
      // Eagerly generate the store so defaultStore is populated.
1263 e4a6c3b8 Olga Brani
      // TODO: Do this in a finisher hook
1264 e4a6c3b8 Olga Brani
      container.lookup('store:main');
1265 e4a6c3b8 Olga Brani
    }
1266 e4a6c3b8 Olga Brani
  });
1267 e4a6c3b8 Olga Brani
1268 e4a6c3b8 Olga Brani
  Application.initializer({
1269 e4a6c3b8 Olga Brani
    name: "transforms",
1270 e4a6c3b8 Olga Brani
    before: "store",
1271 e4a6c3b8 Olga Brani
1272 e4a6c3b8 Olga Brani
    initialize: function(container, application) {
1273 e4a6c3b8 Olga Brani
      application.register('transform:boolean', DS.BooleanTransform);
1274 e4a6c3b8 Olga Brani
      application.register('transform:date', DS.DateTransform);
1275 e4a6c3b8 Olga Brani
      application.register('transform:number', DS.NumberTransform);
1276 e4a6c3b8 Olga Brani
      application.register('transform:string', DS.StringTransform);
1277 e4a6c3b8 Olga Brani
    }
1278 e4a6c3b8 Olga Brani
  });
1279 e4a6c3b8 Olga Brani
1280 e4a6c3b8 Olga Brani
  Application.initializer({
1281 e4a6c3b8 Olga Brani
    name: "data-adapter",
1282 e4a6c3b8 Olga Brani
    before: "store",
1283 e4a6c3b8 Olga Brani
1284 e4a6c3b8 Olga Brani
    initialize: function(container, application) {
1285 e4a6c3b8 Olga Brani
      application.register('data-adapter:main', DS.DebugAdapter);
1286 e4a6c3b8 Olga Brani
    }
1287 e4a6c3b8 Olga Brani
  });
1288 e4a6c3b8 Olga Brani
1289 e4a6c3b8 Olga Brani
  Application.initializer({
1290 e4a6c3b8 Olga Brani
    name: "injectStore",
1291 e4a6c3b8 Olga Brani
    before: "store",
1292 e4a6c3b8 Olga Brani
1293 e4a6c3b8 Olga Brani
    initialize: function(container, application) {
1294 e4a6c3b8 Olga Brani
      application.inject('controller', 'store', 'store:main');
1295 e4a6c3b8 Olga Brani
      application.inject('route', 'store', 'store:main');
1296 e4a6c3b8 Olga Brani
      application.inject('serializer', 'store', 'store:main');
1297 e4a6c3b8 Olga Brani
      application.inject('data-adapter', 'store', 'store:main');
1298 e4a6c3b8 Olga Brani
    }
1299 e4a6c3b8 Olga Brani
  });
1300 e4a6c3b8 Olga Brani
1301 e4a6c3b8 Olga Brani
});
1302 e4a6c3b8 Olga Brani
1303 e4a6c3b8 Olga Brani
})();
1304 e4a6c3b8 Olga Brani
1305 e4a6c3b8 Olga Brani
1306 e4a6c3b8 Olga Brani
1307 e4a6c3b8 Olga Brani
(function() {
1308 e4a6c3b8 Olga Brani
/**
1309 e4a6c3b8 Olga Brani
  @module ember-data
1310 e4a6c3b8 Olga Brani
*/
1311 e4a6c3b8 Olga Brani
1312 e4a6c3b8 Olga Brani
/**
1313 e4a6c3b8 Olga Brani
  Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
1314 e4a6c3b8 Olga Brani

1315 e4a6c3b8 Olga Brani
  © 2011 Colin Snover <http://zetafleet.com>
1316 e4a6c3b8 Olga Brani

1317 e4a6c3b8 Olga Brani
  Released under MIT license.
1318 e4a6c3b8 Olga Brani

1319 e4a6c3b8 Olga Brani
  @class Date
1320 e4a6c3b8 Olga Brani
  @namespace Ember
1321 e4a6c3b8 Olga Brani
  @static
1322 e4a6c3b8 Olga Brani
*/
1323 e4a6c3b8 Olga Brani
Ember.Date = Ember.Date || {};
1324 e4a6c3b8 Olga Brani
1325 e4a6c3b8 Olga Brani
var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
1326 e4a6c3b8 Olga Brani
1327 e4a6c3b8 Olga Brani
/**
1328 e4a6c3b8 Olga Brani
  @method parse
1329 e4a6c3b8 Olga Brani
  @param date
1330 e4a6c3b8 Olga Brani
*/
1331 e4a6c3b8 Olga Brani
Ember.Date.parse = function (date) {
1332 e4a6c3b8 Olga Brani
    var timestamp, struct, minutesOffset = 0;
1333 e4a6c3b8 Olga Brani
1334 e4a6c3b8 Olga Brani
    // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
1335 e4a6c3b8 Olga Brani
    // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
1336 e4a6c3b8 Olga Brani
    // implementations could be faster
1337 e4a6c3b8 Olga Brani
    //              1 YYYY                2 MM       3 DD           4 HH    5 mm       6 ss        7 msec        8 Z 9 ±    10 tzHH    11 tzmm
1338 e4a6c3b8 Olga Brani
    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))) {
1339 e4a6c3b8 Olga Brani
        // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
1340 e4a6c3b8 Olga Brani
        for (var i = 0, k; (k = numericKeys[i]); ++i) {
1341 e4a6c3b8 Olga Brani
            struct[k] = +struct[k] || 0;
1342 e4a6c3b8 Olga Brani
        }
1343 e4a6c3b8 Olga Brani
1344 e4a6c3b8 Olga Brani
        // allow undefined days and months
1345 e4a6c3b8 Olga Brani
        struct[2] = (+struct[2] || 1) - 1;
1346 e4a6c3b8 Olga Brani
        struct[3] = +struct[3] || 1;
1347 e4a6c3b8 Olga Brani
1348 e4a6c3b8 Olga Brani
        if (struct[8] !== 'Z' && struct[9] !== undefined) {
1349 e4a6c3b8 Olga Brani
            minutesOffset = struct[10] * 60 + struct[11];
1350 e4a6c3b8 Olga Brani
1351 e4a6c3b8 Olga Brani
            if (struct[9] === '+') {
1352 e4a6c3b8 Olga Brani
                minutesOffset = 0 - minutesOffset;
1353 e4a6c3b8 Olga Brani
            }
1354 e4a6c3b8 Olga Brani
        }
1355 e4a6c3b8 Olga Brani
1356 e4a6c3b8 Olga Brani
        timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
1357 e4a6c3b8 Olga Brani
    }
1358 e4a6c3b8 Olga Brani
    else {
1359 e4a6c3b8 Olga Brani
        timestamp = origParse ? origParse(date) : NaN;
1360 e4a6c3b8 Olga Brani
    }
1361 e4a6c3b8 Olga Brani
1362 e4a6c3b8 Olga Brani
    return timestamp;
1363 e4a6c3b8 Olga Brani
};
1364 e4a6c3b8 Olga Brani
1365 e4a6c3b8 Olga Brani
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) {
1366 e4a6c3b8 Olga Brani
  Date.parse = Ember.Date.parse;
1367 e4a6c3b8 Olga Brani
}
1368 e4a6c3b8 Olga Brani
1369 e4a6c3b8 Olga Brani
})();
1370 e4a6c3b8 Olga Brani
1371 e4a6c3b8 Olga Brani
1372 e4a6c3b8 Olga Brani
1373 e4a6c3b8 Olga Brani
(function() {
1374 e4a6c3b8 Olga Brani
1375 e4a6c3b8 Olga Brani
})();
1376 e4a6c3b8 Olga Brani
1377 e4a6c3b8 Olga Brani
1378 e4a6c3b8 Olga Brani
1379 e4a6c3b8 Olga Brani
(function() {
1380 e4a6c3b8 Olga Brani
/**
1381 e4a6c3b8 Olga Brani
  @module ember-data
1382 e4a6c3b8 Olga Brani
*/
1383 e4a6c3b8 Olga Brani
1384 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
1385 e4a6c3b8 Olga Brani
1386 e4a6c3b8 Olga Brani
/**
1387 e4a6c3b8 Olga Brani
  A record array is an array that contains records of a certain type. The record
1388 e4a6c3b8 Olga Brani
  array materializes records as needed when they are retrieved for the first
1389 e4a6c3b8 Olga Brani
  time. You should not create record arrays yourself. Instead, an instance of
1390 e4a6c3b8 Olga Brani
  `DS.RecordArray` or its subclasses will be returned by your application's store
1391 e4a6c3b8 Olga Brani
  in response to queries.
1392 e4a6c3b8 Olga Brani

1393 e4a6c3b8 Olga Brani
  @class RecordArray
1394 e4a6c3b8 Olga Brani
  @namespace DS
1395 e4a6c3b8 Olga Brani
  @extends Ember.ArrayProxy
1396 e4a6c3b8 Olga Brani
  @uses Ember.Evented
1397 e4a6c3b8 Olga Brani
*/
1398 e4a6c3b8 Olga Brani
1399 e4a6c3b8 Olga Brani
DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
1400 e4a6c3b8 Olga Brani
  /**
1401 e4a6c3b8 Olga Brani
    The model type contained by this record array.
1402 e4a6c3b8 Olga Brani

1403 e4a6c3b8 Olga Brani
    @property type
1404 e4a6c3b8 Olga Brani
    @type DS.Model
1405 e4a6c3b8 Olga Brani
  */
1406 e4a6c3b8 Olga Brani
  type: null,
1407 e4a6c3b8 Olga Brani
1408 e4a6c3b8 Olga Brani
  /**
1409 e4a6c3b8 Olga Brani
    The array of client ids backing the record array. When a
1410 e4a6c3b8 Olga Brani
    record is requested from the record array, the record
1411 e4a6c3b8 Olga Brani
    for the client id at the same index is materialized, if
1412 e4a6c3b8 Olga Brani
    necessary, by the store.
1413 e4a6c3b8 Olga Brani

1414 e4a6c3b8 Olga Brani
    @property content
1415 e4a6c3b8 Olga Brani
    @private
1416 e4a6c3b8 Olga Brani
    @type Ember.Array
1417 e4a6c3b8 Olga Brani
  */
1418 e4a6c3b8 Olga Brani
  content: null,
1419 e4a6c3b8 Olga Brani
1420 e4a6c3b8 Olga Brani
  /**
1421 e4a6c3b8 Olga Brani
    The flag to signal a `RecordArray` is currently loading data.
1422 e4a6c3b8 Olga Brani

1423 e4a6c3b8 Olga Brani
    Example
1424 e4a6c3b8 Olga Brani

1425 e4a6c3b8 Olga Brani
    ```javascript
1426 e4a6c3b8 Olga Brani
    var people = store.all(App.Person);
1427 e4a6c3b8 Olga Brani
    people.get('isLoaded'); // true
1428 e4a6c3b8 Olga Brani
    ```
1429 e4a6c3b8 Olga Brani

1430 e4a6c3b8 Olga Brani
    @property isLoaded
1431 e4a6c3b8 Olga Brani
    @type Boolean
1432 e4a6c3b8 Olga Brani
  */
1433 e4a6c3b8 Olga Brani
  isLoaded: false,
1434 e4a6c3b8 Olga Brani
  /**
1435 e4a6c3b8 Olga Brani
    The flag to signal a `RecordArray` is currently loading data.
1436 e4a6c3b8 Olga Brani

1437 e4a6c3b8 Olga Brani
    Example
1438 e4a6c3b8 Olga Brani

1439 e4a6c3b8 Olga Brani
    ```javascript
1440 e4a6c3b8 Olga Brani
    var people = store.all(App.Person);
1441 e4a6c3b8 Olga Brani
    people.get('isUpdating'); // false
1442 e4a6c3b8 Olga Brani
    people.update();
1443 e4a6c3b8 Olga Brani
    people.get('isUpdating'); // true
1444 e4a6c3b8 Olga Brani
    ```
1445 e4a6c3b8 Olga Brani

1446 e4a6c3b8 Olga Brani
    @property isUpdating
1447 e4a6c3b8 Olga Brani
    @type Boolean
1448 e4a6c3b8 Olga Brani
  */
1449 e4a6c3b8 Olga Brani
  isUpdating: false,
1450 e4a6c3b8 Olga Brani
1451 e4a6c3b8 Olga Brani
  /**
1452 e4a6c3b8 Olga Brani
    The store that created this record array.
1453 e4a6c3b8 Olga Brani

1454 e4a6c3b8 Olga Brani
    @property store
1455 e4a6c3b8 Olga Brani
    @private
1456 e4a6c3b8 Olga Brani
    @type DS.Store
1457 e4a6c3b8 Olga Brani
  */
1458 e4a6c3b8 Olga Brani
  store: null,
1459 e4a6c3b8 Olga Brani
1460 e4a6c3b8 Olga Brani
  /**
1461 e4a6c3b8 Olga Brani
    Retrieves an object from the content by index.
1462 e4a6c3b8 Olga Brani

1463 e4a6c3b8 Olga Brani
    @method objectAtContent
1464 e4a6c3b8 Olga Brani
    @private
1465 e4a6c3b8 Olga Brani
    @param {Number} index
1466 e4a6c3b8 Olga Brani
    @return {DS.Model} record
1467 e4a6c3b8 Olga Brani
  */
1468 e4a6c3b8 Olga Brani
  objectAtContent: function(index) {
1469 e4a6c3b8 Olga Brani
    var content = get(this, 'content');
1470 e4a6c3b8 Olga Brani
1471 e4a6c3b8 Olga Brani
    return content.objectAt(index);
1472 e4a6c3b8 Olga Brani
  },
1473 e4a6c3b8 Olga Brani
1474 e4a6c3b8 Olga Brani
  /**
1475 e4a6c3b8 Olga Brani
    Used to get the latest version of all of the records in this array
1476 e4a6c3b8 Olga Brani
    from the adapter.
1477 e4a6c3b8 Olga Brani

1478 e4a6c3b8 Olga Brani
    Example
1479 e4a6c3b8 Olga Brani

1480 e4a6c3b8 Olga Brani
    ```javascript
1481 e4a6c3b8 Olga Brani
    var people = store.all(App.Person);
1482 e4a6c3b8 Olga Brani
    people.get('isUpdating'); // false
1483 e4a6c3b8 Olga Brani
    people.update();
1484 e4a6c3b8 Olga Brani
    people.get('isUpdating'); // true
1485 e4a6c3b8 Olga Brani
    ```
1486 e4a6c3b8 Olga Brani

1487 e4a6c3b8 Olga Brani
    @method update
1488 e4a6c3b8 Olga Brani
  */
1489 e4a6c3b8 Olga Brani
  update: function() {
1490 e4a6c3b8 Olga Brani
    if (get(this, 'isUpdating')) { return; }
1491 e4a6c3b8 Olga Brani
1492 e4a6c3b8 Olga Brani
    var store = get(this, 'store'),
1493 e4a6c3b8 Olga Brani
        type = get(this, 'type');
1494 e4a6c3b8 Olga Brani
1495 e4a6c3b8 Olga Brani
    store.fetchAll(type, this);
1496 e4a6c3b8 Olga Brani
  },
1497 e4a6c3b8 Olga Brani
1498 e4a6c3b8 Olga Brani
  /**
1499 e4a6c3b8 Olga Brani
    Adds a record to the `RecordArray`.
1500 e4a6c3b8 Olga Brani

1501 e4a6c3b8 Olga Brani
    @method addRecord
1502 e4a6c3b8 Olga Brani
    @private
1503 e4a6c3b8 Olga Brani
    @param {DS.Model} record
1504 e4a6c3b8 Olga Brani
  */
1505 e4a6c3b8 Olga Brani
  addRecord: function(record) {
1506 e4a6c3b8 Olga Brani
    get(this, 'content').addObject(record);
1507 e4a6c3b8 Olga Brani
  },
1508 e4a6c3b8 Olga Brani
1509 e4a6c3b8 Olga Brani
  /**
1510 e4a6c3b8 Olga Brani
    Removes a record to the `RecordArray`.
1511 e4a6c3b8 Olga Brani

1512 e4a6c3b8 Olga Brani
    @method removeRecord
1513 e4a6c3b8 Olga Brani
    @private
1514 e4a6c3b8 Olga Brani
    @param {DS.Model} record
1515 e4a6c3b8 Olga Brani
  */
1516 e4a6c3b8 Olga Brani
  removeRecord: function(record) {
1517 e4a6c3b8 Olga Brani
    get(this, 'content').removeObject(record);
1518 e4a6c3b8 Olga Brani
  },
1519 e4a6c3b8 Olga Brani
1520 e4a6c3b8 Olga Brani
  /**
1521 e4a6c3b8 Olga Brani
    Saves all of the records in the `RecordArray`.
1522 e4a6c3b8 Olga Brani

1523 e4a6c3b8 Olga Brani
    Example
1524 e4a6c3b8 Olga Brani

1525 e4a6c3b8 Olga Brani
    ```javascript
1526 e4a6c3b8 Olga Brani
    var messages = store.all(App.Message);
1527 e4a6c3b8 Olga Brani
    messages.forEach(function(message) {
1528 e4a6c3b8 Olga Brani
      message.set('hasBeenSeen', true);
1529 e4a6c3b8 Olga Brani
    });
1530 e4a6c3b8 Olga Brani
    messages.save();
1531 e4a6c3b8 Olga Brani
    ```
1532 e4a6c3b8 Olga Brani

1533 e4a6c3b8 Olga Brani
    @method save
1534 e4a6c3b8 Olga Brani
    @return {DS.PromiseArray} promise
1535 e4a6c3b8 Olga Brani
  */
1536 e4a6c3b8 Olga Brani
  save: function() {
1537 e4a6c3b8 Olga Brani
    var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
1538 e4a6c3b8 Olga Brani
    var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) {
1539 e4a6c3b8 Olga Brani
      return Ember.A(array);
1540 e4a6c3b8 Olga Brani
    }, null, "DS: RecordArray#save apply Ember.NativeArray");
1541 e4a6c3b8 Olga Brani
1542 e4a6c3b8 Olga Brani
    return DS.PromiseArray.create({ promise: promise });
1543 e4a6c3b8 Olga Brani
  }
1544 e4a6c3b8 Olga Brani
});
1545 e4a6c3b8 Olga Brani
1546 e4a6c3b8 Olga Brani
})();
1547 e4a6c3b8 Olga Brani
1548 e4a6c3b8 Olga Brani
1549 e4a6c3b8 Olga Brani
1550 e4a6c3b8 Olga Brani
(function() {
1551 e4a6c3b8 Olga Brani
/**
1552 e4a6c3b8 Olga Brani
  @module ember-data
1553 e4a6c3b8 Olga Brani
*/
1554 e4a6c3b8 Olga Brani
1555 e4a6c3b8 Olga Brani
var get = Ember.get;
1556 e4a6c3b8 Olga Brani
1557 e4a6c3b8 Olga Brani
/**
1558 e4a6c3b8 Olga Brani
  Represents a list of records whose membership is determined by the
1559 e4a6c3b8 Olga Brani
  store. As records are created, loaded, or modified, the store
1560 e4a6c3b8 Olga Brani
  evaluates them to determine if they should be part of the record
1561 e4a6c3b8 Olga Brani
  array.
1562 e4a6c3b8 Olga Brani

1563 e4a6c3b8 Olga Brani
  @class FilteredRecordArray
1564 e4a6c3b8 Olga Brani
  @namespace DS
1565 e4a6c3b8 Olga Brani
  @extends DS.RecordArray
1566 e4a6c3b8 Olga Brani
*/
1567 e4a6c3b8 Olga Brani
DS.FilteredRecordArray = DS.RecordArray.extend({
1568 e4a6c3b8 Olga Brani
  /**
1569 e4a6c3b8 Olga Brani
    The filterFunction is a function used to test records from the store to
1570 e4a6c3b8 Olga Brani
    determine if they should be part of the record array.
1571 e4a6c3b8 Olga Brani

1572 e4a6c3b8 Olga Brani
    Example
1573 e4a6c3b8 Olga Brani

1574 e4a6c3b8 Olga Brani
    ```javascript
1575 e4a6c3b8 Olga Brani
    var allPeople = store.all('person');
1576 e4a6c3b8 Olga Brani
    allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"]
1577 e4a6c3b8 Olga Brani

1578 e4a6c3b8 Olga Brani
    var people = store.filter('person', function(person) {
1579 e4a6c3b8 Olga Brani
      if (person.get('name').match(/Katz$/)) { return true; }
1580 e4a6c3b8 Olga Brani
    });
1581 e4a6c3b8 Olga Brani
    people.mapBy('name'); // ["Yehuda Katz"]
1582 e4a6c3b8 Olga Brani

1583 e4a6c3b8 Olga Brani
    var notKatzFilter = function(person) {
1584 e4a6c3b8 Olga Brani
      return !person.get('name').match(/Katz$/);
1585 e4a6c3b8 Olga Brani
    };
1586 e4a6c3b8 Olga Brani
    people.set('filterFunction', notKatzFilter);
1587 e4a6c3b8 Olga Brani
    people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"]
1588 e4a6c3b8 Olga Brani
    ```
1589 e4a6c3b8 Olga Brani

1590 e4a6c3b8 Olga Brani
    @method filterFunction
1591 e4a6c3b8 Olga Brani
    @param {DS.Model} record
1592 e4a6c3b8 Olga Brani
    @return {Boolean} `true` if the record should be in the array
1593 e4a6c3b8 Olga Brani
  */
1594 e4a6c3b8 Olga Brani
  filterFunction: null,
1595 e4a6c3b8 Olga Brani
  isLoaded: true,
1596 e4a6c3b8 Olga Brani
1597 e4a6c3b8 Olga Brani
  replace: function() {
1598 e4a6c3b8 Olga Brani
    var type = get(this, 'type').toString();
1599 e4a6c3b8 Olga Brani
    throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
1600 e4a6c3b8 Olga Brani
  },
1601 e4a6c3b8 Olga Brani
1602 e4a6c3b8 Olga Brani
  /**
1603 e4a6c3b8 Olga Brani
    @method updateFilter
1604 e4a6c3b8 Olga Brani
    @private
1605 e4a6c3b8 Olga Brani
  */
1606 e4a6c3b8 Olga Brani
  updateFilter: Ember.observer(function() {
1607 e4a6c3b8 Olga Brani
    var manager = get(this, 'manager');
1608 e4a6c3b8 Olga Brani
    manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
1609 e4a6c3b8 Olga Brani
  }, 'filterFunction')
1610 e4a6c3b8 Olga Brani
});
1611 e4a6c3b8 Olga Brani
1612 e4a6c3b8 Olga Brani
})();
1613 e4a6c3b8 Olga Brani
1614 e4a6c3b8 Olga Brani
1615 e4a6c3b8 Olga Brani
1616 e4a6c3b8 Olga Brani
(function() {
1617 e4a6c3b8 Olga Brani
/**
1618 e4a6c3b8 Olga Brani
  @module ember-data
1619 e4a6c3b8 Olga Brani
*/
1620 e4a6c3b8 Olga Brani
1621 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
1622 e4a6c3b8 Olga Brani
1623 e4a6c3b8 Olga Brani
/**
1624 e4a6c3b8 Olga Brani
  Represents an ordered list of records whose order and membership is
1625 e4a6c3b8 Olga Brani
  determined by the adapter. For example, a query sent to the adapter
1626 e4a6c3b8 Olga Brani
  may trigger a search on the server, whose results would be loaded
1627 e4a6c3b8 Olga Brani
  into an instance of the `AdapterPopulatedRecordArray`.
1628 e4a6c3b8 Olga Brani

1629 e4a6c3b8 Olga Brani
  @class AdapterPopulatedRecordArray
1630 e4a6c3b8 Olga Brani
  @namespace DS
1631 e4a6c3b8 Olga Brani
  @extends DS.RecordArray
1632 e4a6c3b8 Olga Brani
*/
1633 e4a6c3b8 Olga Brani
DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
1634 e4a6c3b8 Olga Brani
  query: null,
1635 e4a6c3b8 Olga Brani
1636 e4a6c3b8 Olga Brani
  replace: function() {
1637 e4a6c3b8 Olga Brani
    var type = get(this, 'type').toString();
1638 e4a6c3b8 Olga Brani
    throw new Error("The result of a server query (on " + type + ") is immutable.");
1639 e4a6c3b8 Olga Brani
  },
1640 e4a6c3b8 Olga Brani
1641 e4a6c3b8 Olga Brani
  /**
1642 e4a6c3b8 Olga Brani
    @method load
1643 e4a6c3b8 Olga Brani
    @private
1644 e4a6c3b8 Olga Brani
    @param {Array} data
1645 e4a6c3b8 Olga Brani
  */
1646 e4a6c3b8 Olga Brani
  load: function(data) {
1647 e4a6c3b8 Olga Brani
    var store = get(this, 'store'),
1648 e4a6c3b8 Olga Brani
        type = get(this, 'type'),
1649 e4a6c3b8 Olga Brani
        records = store.pushMany(type, data),
1650 e4a6c3b8 Olga Brani
        meta = store.metadataFor(type);
1651 e4a6c3b8 Olga Brani
1652 e4a6c3b8 Olga Brani
    this.setProperties({
1653 e4a6c3b8 Olga Brani
      content: Ember.A(records),
1654 e4a6c3b8 Olga Brani
      isLoaded: true,
1655 e4a6c3b8 Olga Brani
      meta: meta
1656 e4a6c3b8 Olga Brani
    });
1657 e4a6c3b8 Olga Brani
1658 e4a6c3b8 Olga Brani
    // TODO: does triggering didLoad event should be the last action of the runLoop?
1659 e4a6c3b8 Olga Brani
    Ember.run.once(this, 'trigger', 'didLoad');
1660 e4a6c3b8 Olga Brani
  }
1661 e4a6c3b8 Olga Brani
});
1662 e4a6c3b8 Olga Brani
1663 e4a6c3b8 Olga Brani
})();
1664 e4a6c3b8 Olga Brani
1665 e4a6c3b8 Olga Brani
1666 e4a6c3b8 Olga Brani
1667 e4a6c3b8 Olga Brani
(function() {
1668 e4a6c3b8 Olga Brani
/**
1669 e4a6c3b8 Olga Brani
  @module ember-data
1670 e4a6c3b8 Olga Brani
*/
1671 e4a6c3b8 Olga Brani
1672 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
1673 e4a6c3b8 Olga Brani
var map = Ember.EnumerableUtils.map;
1674 e4a6c3b8 Olga Brani
1675 e4a6c3b8 Olga Brani
/**
1676 e4a6c3b8 Olga Brani
  A `ManyArray` is a `RecordArray` that represents the contents of a has-many
1677 e4a6c3b8 Olga Brani
  relationship.
1678 e4a6c3b8 Olga Brani

1679 e4a6c3b8 Olga Brani
  The `ManyArray` is instantiated lazily the first time the relationship is
1680 e4a6c3b8 Olga Brani
  requested.
1681 e4a6c3b8 Olga Brani

1682 e4a6c3b8 Olga Brani
  ### Inverses
1683 e4a6c3b8 Olga Brani

1684 e4a6c3b8 Olga Brani
  Often, the relationships in Ember Data applications will have
1685 e4a6c3b8 Olga Brani
  an inverse. For example, imagine the following models are
1686 e4a6c3b8 Olga Brani
  defined:
1687 e4a6c3b8 Olga Brani

1688 e4a6c3b8 Olga Brani
  ```javascript
1689 e4a6c3b8 Olga Brani
  App.Post = DS.Model.extend({
1690 e4a6c3b8 Olga Brani
    comments: DS.hasMany('comment')
1691 e4a6c3b8 Olga Brani
  });
1692 e4a6c3b8 Olga Brani

1693 e4a6c3b8 Olga Brani
  App.Comment = DS.Model.extend({
1694 e4a6c3b8 Olga Brani
    post: DS.belongsTo('post')
1695 e4a6c3b8 Olga Brani
  });
1696 e4a6c3b8 Olga Brani
  ```
1697 e4a6c3b8 Olga Brani

1698 e4a6c3b8 Olga Brani
  If you created a new instance of `App.Post` and added
1699 e4a6c3b8 Olga Brani
  a `App.Comment` record to its `comments` has-many
1700 e4a6c3b8 Olga Brani
  relationship, you would expect the comment's `post`
1701 e4a6c3b8 Olga Brani
  property to be set to the post that contained
1702 e4a6c3b8 Olga Brani
  the has-many.
1703 e4a6c3b8 Olga Brani

1704 e4a6c3b8 Olga Brani
  We call the record to which a relationship belongs the
1705 e4a6c3b8 Olga Brani
  relationship's _owner_.
1706 e4a6c3b8 Olga Brani

1707 e4a6c3b8 Olga Brani
  @class ManyArray
1708 e4a6c3b8 Olga Brani
  @namespace DS
1709 e4a6c3b8 Olga Brani
  @extends DS.RecordArray
1710 e4a6c3b8 Olga Brani
*/
1711 e4a6c3b8 Olga Brani
DS.ManyArray = DS.RecordArray.extend({
1712 e4a6c3b8 Olga Brani
  init: function() {
1713 e4a6c3b8 Olga Brani
    this._super.apply(this, arguments);
1714 e4a6c3b8 Olga Brani
    this._changesToSync = Ember.OrderedSet.create();
1715 e4a6c3b8 Olga Brani
  },
1716 e4a6c3b8 Olga Brani
1717 e4a6c3b8 Olga Brani
  /**
1718 e4a6c3b8 Olga Brani
    The property name of the relationship
1719 e4a6c3b8 Olga Brani

1720 e4a6c3b8 Olga Brani
    @property {String} name
1721 e4a6c3b8 Olga Brani
    @private
1722 e4a6c3b8 Olga Brani
  */
1723 e4a6c3b8 Olga Brani
  name: null,
1724 e4a6c3b8 Olga Brani
1725 e4a6c3b8 Olga Brani
  /**
1726 e4a6c3b8 Olga Brani
    The record to which this relationship belongs.
1727 e4a6c3b8 Olga Brani

1728 e4a6c3b8 Olga Brani
    @property {DS.Model} owner
1729 e4a6c3b8 Olga Brani
    @private
1730 e4a6c3b8 Olga Brani
  */
1731 e4a6c3b8 Olga Brani
  owner: null,
1732 e4a6c3b8 Olga Brani
1733 e4a6c3b8 Olga Brani
  /**
1734 e4a6c3b8 Olga Brani
    `true` if the relationship is polymorphic, `false` otherwise.
1735 e4a6c3b8 Olga Brani

1736 e4a6c3b8 Olga Brani
    @property {Boolean} isPolymorphic
1737 e4a6c3b8 Olga Brani
    @private
1738 e4a6c3b8 Olga Brani
  */
1739 e4a6c3b8 Olga Brani
  isPolymorphic: false,
1740 e4a6c3b8 Olga Brani
1741 e4a6c3b8 Olga Brani
  // LOADING STATE
1742 e4a6c3b8 Olga Brani
1743 e4a6c3b8 Olga Brani
  isLoaded: false,
1744 e4a6c3b8 Olga Brani
1745 e4a6c3b8 Olga Brani
  /**
1746 e4a6c3b8 Olga Brani
    Used for async `hasMany` arrays
1747 e4a6c3b8 Olga Brani
    to keep track of when they will resolve.
1748 e4a6c3b8 Olga Brani

1749 e4a6c3b8 Olga Brani
    @property {Ember.RSVP.Promise} promise
1750 e4a6c3b8 Olga Brani
    @private
1751 e4a6c3b8 Olga Brani
  */
1752 e4a6c3b8 Olga Brani
  promise: null,
1753 e4a6c3b8 Olga Brani
1754 e4a6c3b8 Olga Brani
  /**
1755 e4a6c3b8 Olga Brani
    @method loadingRecordsCount
1756 e4a6c3b8 Olga Brani
    @param {Number} count
1757 e4a6c3b8 Olga Brani
    @private
1758 e4a6c3b8 Olga Brani
  */
1759 e4a6c3b8 Olga Brani
  loadingRecordsCount: function(count) {
1760 e4a6c3b8 Olga Brani
    this.loadingRecordsCount = count;
1761 e4a6c3b8 Olga Brani
  },
1762 e4a6c3b8 Olga Brani
1763 e4a6c3b8 Olga Brani
  /**
1764 e4a6c3b8 Olga Brani
    @method loadedRecord
1765 e4a6c3b8 Olga Brani
    @private
1766 e4a6c3b8 Olga Brani
  */
1767 e4a6c3b8 Olga Brani
  loadedRecord: function() {
1768 e4a6c3b8 Olga Brani
    this.loadingRecordsCount--;
1769 e4a6c3b8 Olga Brani
    if (this.loadingRecordsCount === 0) {
1770 e4a6c3b8 Olga Brani
      set(this, 'isLoaded', true);
1771 e4a6c3b8 Olga Brani
      this.trigger('didLoad');
1772 e4a6c3b8 Olga Brani
    }
1773 e4a6c3b8 Olga Brani
  },
1774 e4a6c3b8 Olga Brani
1775 e4a6c3b8 Olga Brani
  /**
1776 e4a6c3b8 Olga Brani
    @method fetch
1777 e4a6c3b8 Olga Brani
    @private
1778 e4a6c3b8 Olga Brani
  */
1779 e4a6c3b8 Olga Brani
  fetch: function() {
1780 e4a6c3b8 Olga Brani
    var records = get(this, 'content'),
1781 e4a6c3b8 Olga Brani
        store = get(this, 'store'),
1782 e4a6c3b8 Olga Brani
        owner = get(this, 'owner'),
1783 e4a6c3b8 Olga Brani
        resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type'));
1784 e4a6c3b8 Olga Brani
1785 e4a6c3b8 Olga Brani
    var unloadedRecords = records.filterProperty('isEmpty', true);
1786 e4a6c3b8 Olga Brani
    store.fetchMany(unloadedRecords, owner, resolver);
1787 e4a6c3b8 Olga Brani
  },
1788 e4a6c3b8 Olga Brani
1789 e4a6c3b8 Olga Brani
  // Overrides Ember.Array's replace method to implement
1790 e4a6c3b8 Olga Brani
  replaceContent: function(index, removed, added) {
1791 e4a6c3b8 Olga Brani
    // Map the array of record objects into an array of  client ids.
1792 e4a6c3b8 Olga Brani
    added = map(added, function(record) {
1793 e4a6c3b8 Olga Brani
      Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.type.typeKey + "' allowed)", !this.type || record instanceof this.type);
1794 e4a6c3b8 Olga Brani
      return record;
1795 e4a6c3b8 Olga Brani
    }, this);
1796 e4a6c3b8 Olga Brani
1797 e4a6c3b8 Olga Brani
    this._super(index, removed, added);
1798 e4a6c3b8 Olga Brani
  },
1799 e4a6c3b8 Olga Brani
1800 e4a6c3b8 Olga Brani
  arrangedContentDidChange: function() {
1801 e4a6c3b8 Olga Brani
    Ember.run.once(this, 'fetch');
1802 e4a6c3b8 Olga Brani
  },
1803 e4a6c3b8 Olga Brani
1804 e4a6c3b8 Olga Brani
  arrayContentWillChange: function(index, removed, added) {
1805 e4a6c3b8 Olga Brani
    var owner = get(this, 'owner'),
1806 e4a6c3b8 Olga Brani
        name = get(this, 'name');
1807 e4a6c3b8 Olga Brani
1808 e4a6c3b8 Olga Brani
    if (!owner._suspendedRelationships) {
1809 e4a6c3b8 Olga Brani
      // This code is the first half of code that continues inside
1810 e4a6c3b8 Olga Brani
      // of arrayContentDidChange. It gets or creates a change from
1811 e4a6c3b8 Olga Brani
      // the child object, adds the current owner as the old
1812 e4a6c3b8 Olga Brani
      // parent if this is the first time the object was removed
1813 e4a6c3b8 Olga Brani
      // from a ManyArray, and sets `newParent` to null.
1814 e4a6c3b8 Olga Brani
      //
1815 e4a6c3b8 Olga Brani
      // Later, if the object is added to another ManyArray,
1816 e4a6c3b8 Olga Brani
      // the `arrayContentDidChange` will set `newParent` on
1817 e4a6c3b8 Olga Brani
      // the change.
1818 e4a6c3b8 Olga Brani
      for (var i=index; i<index+removed; i++) {
1819 e4a6c3b8 Olga Brani
        var record = get(this, 'content').objectAt(i);
1820 e4a6c3b8 Olga Brani
1821 e4a6c3b8 Olga Brani
        var change = DS.RelationshipChange.createChange(owner, record, get(this, 'store'), {
1822 e4a6c3b8 Olga Brani
          parentType: owner.constructor,
1823 e4a6c3b8 Olga Brani
          changeType: "remove",
1824 e4a6c3b8 Olga Brani
          kind: "hasMany",
1825 e4a6c3b8 Olga Brani
          key: name
1826 e4a6c3b8 Olga Brani
        });
1827 e4a6c3b8 Olga Brani
1828 e4a6c3b8 Olga Brani
        this._changesToSync.add(change);
1829 e4a6c3b8 Olga Brani
      }
1830 e4a6c3b8 Olga Brani
    }
1831 e4a6c3b8 Olga Brani
1832 e4a6c3b8 Olga Brani
    return this._super.apply(this, arguments);
1833 e4a6c3b8 Olga Brani
  },
1834 e4a6c3b8 Olga Brani
1835 e4a6c3b8 Olga Brani
  arrayContentDidChange: function(index, removed, added) {
1836 e4a6c3b8 Olga Brani
    this._super.apply(this, arguments);
1837 e4a6c3b8 Olga Brani
1838 e4a6c3b8 Olga Brani
    var owner = get(this, 'owner'),
1839 e4a6c3b8 Olga Brani
        name = get(this, 'name'),
1840 e4a6c3b8 Olga Brani
        store = get(this, 'store');
1841 e4a6c3b8 Olga Brani
1842 e4a6c3b8 Olga Brani
    if (!owner._suspendedRelationships) {
1843 e4a6c3b8 Olga Brani
      // This code is the second half of code that started in
1844 e4a6c3b8 Olga Brani
      // `arrayContentWillChange`. It gets or creates a change
1845 e4a6c3b8 Olga Brani
      // from the child object, and adds the current owner as
1846 e4a6c3b8 Olga Brani
      // the new parent.
1847 e4a6c3b8 Olga Brani
      for (var i=index; i<index+added; i++) {
1848 e4a6c3b8 Olga Brani
        var record = get(this, 'content').objectAt(i);
1849 e4a6c3b8 Olga Brani
1850 e4a6c3b8 Olga Brani
        var change = DS.RelationshipChange.createChange(owner, record, store, {
1851 e4a6c3b8 Olga Brani
          parentType: owner.constructor,
1852 e4a6c3b8 Olga Brani
          changeType: "add",
1853 e4a6c3b8 Olga Brani
          kind:"hasMany",
1854 e4a6c3b8 Olga Brani
          key: name
1855 e4a6c3b8 Olga Brani
        });
1856 e4a6c3b8 Olga Brani
        change.hasManyName = name;
1857 e4a6c3b8 Olga Brani
1858 e4a6c3b8 Olga Brani
        this._changesToSync.add(change);
1859 e4a6c3b8 Olga Brani
      }
1860 e4a6c3b8 Olga Brani
1861 e4a6c3b8 Olga Brani
      // We wait until the array has finished being
1862 e4a6c3b8 Olga Brani
      // mutated before syncing the OneToManyChanges created
1863 e4a6c3b8 Olga Brani
      // in arrayContentWillChange, so that the array
1864 e4a6c3b8 Olga Brani
      // membership test in the sync() logic operates
1865 e4a6c3b8 Olga Brani
      // on the final results.
1866 e4a6c3b8 Olga Brani
      this._changesToSync.forEach(function(change) {
1867 e4a6c3b8 Olga Brani
        change.sync();
1868 e4a6c3b8 Olga Brani
      });
1869 e4a6c3b8 Olga Brani
1870 e4a6c3b8 Olga Brani
      this._changesToSync.clear();
1871 e4a6c3b8 Olga Brani
    }
1872 e4a6c3b8 Olga Brani
  },
1873 e4a6c3b8 Olga Brani
1874 e4a6c3b8 Olga Brani
  /**
1875 e4a6c3b8 Olga Brani
    Create a child record within the owner
1876 e4a6c3b8 Olga Brani

1877 e4a6c3b8 Olga Brani
    @method createRecord
1878 e4a6c3b8 Olga Brani
    @private
1879 e4a6c3b8 Olga Brani
    @param {Object} hash
1880 e4a6c3b8 Olga Brani
    @return {DS.Model} record
1881 e4a6c3b8 Olga Brani
  */
1882 e4a6c3b8 Olga Brani
  createRecord: function(hash) {
1883 e4a6c3b8 Olga Brani
    var owner = get(this, 'owner'),
1884 e4a6c3b8 Olga Brani
        store = get(owner, 'store'),
1885 e4a6c3b8 Olga Brani
        type = get(this, 'type'),
1886 e4a6c3b8 Olga Brani
        record;
1887 e4a6c3b8 Olga Brani
1888 e4a6c3b8 Olga Brani
    Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic'));
1889 e4a6c3b8 Olga Brani
1890 e4a6c3b8 Olga Brani
    record = store.createRecord.call(store, type, hash);
1891 e4a6c3b8 Olga Brani
    this.pushObject(record);
1892 e4a6c3b8 Olga Brani
1893 e4a6c3b8 Olga Brani
    return record;
1894 e4a6c3b8 Olga Brani
  }
1895 e4a6c3b8 Olga Brani
1896 e4a6c3b8 Olga Brani
});
1897 e4a6c3b8 Olga Brani
1898 e4a6c3b8 Olga Brani
})();
1899 e4a6c3b8 Olga Brani
1900 e4a6c3b8 Olga Brani
1901 e4a6c3b8 Olga Brani
1902 e4a6c3b8 Olga Brani
(function() {
1903 e4a6c3b8 Olga Brani
/**
1904 e4a6c3b8 Olga Brani
  @module ember-data
1905 e4a6c3b8 Olga Brani
*/
1906 e4a6c3b8 Olga Brani
1907 e4a6c3b8 Olga Brani
})();
1908 e4a6c3b8 Olga Brani
1909 e4a6c3b8 Olga Brani
1910 e4a6c3b8 Olga Brani
1911 e4a6c3b8 Olga Brani
(function() {
1912 e4a6c3b8 Olga Brani
/*globals Ember*/
1913 e4a6c3b8 Olga Brani
/*jshint eqnull:true*/
1914 e4a6c3b8 Olga Brani
/**
1915 e4a6c3b8 Olga Brani
  @module ember-data
1916 e4a6c3b8 Olga Brani
*/
1917 e4a6c3b8 Olga Brani
1918 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
1919 e4a6c3b8 Olga Brani
var once = Ember.run.once;
1920 e4a6c3b8 Olga Brani
var isNone = Ember.isNone;
1921 e4a6c3b8 Olga Brani
var forEach = Ember.EnumerableUtils.forEach;
1922 e4a6c3b8 Olga Brani
var indexOf = Ember.EnumerableUtils.indexOf;
1923 e4a6c3b8 Olga Brani
var map = Ember.EnumerableUtils.map;
1924 e4a6c3b8 Olga Brani
var resolve = Ember.RSVP.resolve;
1925 e4a6c3b8 Olga Brani
var copy = Ember.copy;
1926 e4a6c3b8 Olga Brani
1927 e4a6c3b8 Olga Brani
// Implementors Note:
1928 e4a6c3b8 Olga Brani
//
1929 e4a6c3b8 Olga Brani
//   The variables in this file are consistently named according to the following
1930 e4a6c3b8 Olga Brani
//   scheme:
1931 e4a6c3b8 Olga Brani
//
1932 e4a6c3b8 Olga Brani
//   * +id+ means an identifier managed by an external source, provided inside
1933 e4a6c3b8 Olga Brani
//     the data provided by that source. These are always coerced to be strings
1934 e4a6c3b8 Olga Brani
//     before being used internally.
1935 e4a6c3b8 Olga Brani
//   * +clientId+ means a transient numerical identifier generated at runtime by
1936 e4a6c3b8 Olga Brani
//     the data store. It is important primarily because newly created objects may
1937 e4a6c3b8 Olga Brani
//     not yet have an externally generated id.
1938 e4a6c3b8 Olga Brani
//   * +reference+ means a record reference object, which holds metadata about a
1939 e4a6c3b8 Olga Brani
//     record, even if it has not yet been fully materialized.
1940 e4a6c3b8 Olga Brani
//   * +type+ means a subclass of DS.Model.
1941 e4a6c3b8 Olga Brani
1942 e4a6c3b8 Olga Brani
// Used by the store to normalize IDs entering the store.  Despite the fact
1943 e4a6c3b8 Olga Brani
// that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`),
1944 e4a6c3b8 Olga Brani
// it is important that internally we use strings, since IDs may be serialized
1945 e4a6c3b8 Olga Brani
// and lose type information.  For example, Ember's router may put a record's
1946 e4a6c3b8 Olga Brani
// ID into the URL, and if we later try to deserialize that URL and find the
1947 e4a6c3b8 Olga Brani
// corresponding record, we will not know if it is a string or a number.
1948 e4a6c3b8 Olga Brani
var coerceId = function(id) {
1949 e4a6c3b8 Olga Brani
  return id == null ? null : id+'';
1950 e4a6c3b8 Olga Brani
};
1951 e4a6c3b8 Olga Brani
1952 e4a6c3b8 Olga Brani
/**
1953 e4a6c3b8 Olga Brani
  The store contains all of the data for records loaded from the server.
1954 e4a6c3b8 Olga Brani
  It is also responsible for creating instances of `DS.Model` that wrap
1955 e4a6c3b8 Olga Brani
  the individual data for a record, so that they can be bound to in your
1956 e4a6c3b8 Olga Brani
  Handlebars templates.
1957 e4a6c3b8 Olga Brani

1958 e4a6c3b8 Olga Brani
  Define your application's store like this:
1959 e4a6c3b8 Olga Brani

1960 e4a6c3b8 Olga Brani
  ```javascript
1961 e4a6c3b8 Olga Brani
  MyApp.Store = DS.Store.extend();
1962 e4a6c3b8 Olga Brani
  ```
1963 e4a6c3b8 Olga Brani

1964 e4a6c3b8 Olga Brani
  Most Ember.js applications will only have a single `DS.Store` that is
1965 e4a6c3b8 Olga Brani
  automatically created by their `Ember.Application`.
1966 e4a6c3b8 Olga Brani

1967 e4a6c3b8 Olga Brani
  You can retrieve models from the store in several ways. To retrieve a record
1968 e4a6c3b8 Olga Brani
  for a specific id, use `DS.Store`'s `find()` method:
1969 e4a6c3b8 Olga Brani

1970 e4a6c3b8 Olga Brani
  ```javascript
1971 e4a6c3b8 Olga Brani
  var person = store.find('person', 123);
1972 e4a6c3b8 Olga Brani
  ```
1973 e4a6c3b8 Olga Brani

1974 e4a6c3b8 Olga Brani
  If your application has multiple `DS.Store` instances (an unusual case), you can
1975 e4a6c3b8 Olga Brani
  specify which store should be used:
1976 e4a6c3b8 Olga Brani

1977 e4a6c3b8 Olga Brani
  ```javascript
1978 e4a6c3b8 Olga Brani
  var person = store.find(App.Person, 123);
1979 e4a6c3b8 Olga Brani
  ```
1980 e4a6c3b8 Olga Brani

1981 e4a6c3b8 Olga Brani
  By default, the store will talk to your backend using a standard
1982 e4a6c3b8 Olga Brani
  REST mechanism. You can customize how the store talks to your
1983 e4a6c3b8 Olga Brani
  backend by specifying a custom adapter:
1984 e4a6c3b8 Olga Brani

1985 e4a6c3b8 Olga Brani
  ```javascript
1986 e4a6c3b8 Olga Brani
   MyApp.store = DS.Store.create({
1987 e4a6c3b8 Olga Brani
     adapter: 'MyApp.CustomAdapter'
1988 e4a6c3b8 Olga Brani
   });
1989 e4a6c3b8 Olga Brani
   ```
1990 e4a6c3b8 Olga Brani

1991 e4a6c3b8 Olga Brani
  You can learn more about writing a custom adapter by reading the `DS.Adapter`
1992 e4a6c3b8 Olga Brani
  documentation.
1993 e4a6c3b8 Olga Brani

1994 e4a6c3b8 Olga Brani
  @class Store
1995 e4a6c3b8 Olga Brani
  @namespace DS
1996 e4a6c3b8 Olga Brani
  @extends Ember.Object
1997 e4a6c3b8 Olga Brani
*/
1998 e4a6c3b8 Olga Brani
DS.Store = Ember.Object.extend({
1999 e4a6c3b8 Olga Brani
2000 e4a6c3b8 Olga Brani
  /**
2001 e4a6c3b8 Olga Brani
    @method init
2002 e4a6c3b8 Olga Brani
    @private
2003 e4a6c3b8 Olga Brani
  */
2004 e4a6c3b8 Olga Brani
  init: function() {
2005 e4a6c3b8 Olga Brani
    // internal bookkeeping; not observable
2006 e4a6c3b8 Olga Brani
    this.typeMaps = {};
2007 e4a6c3b8 Olga Brani
    this.recordArrayManager = DS.RecordArrayManager.create({
2008 e4a6c3b8 Olga Brani
      store: this
2009 e4a6c3b8 Olga Brani
    });
2010 e4a6c3b8 Olga Brani
    this._relationshipChanges = {};
2011 e4a6c3b8 Olga Brani
    this._pendingSave = [];
2012 e4a6c3b8 Olga Brani
  },
2013 e4a6c3b8 Olga Brani
2014 e4a6c3b8 Olga Brani
  /**
2015 e4a6c3b8 Olga Brani
    The adapter to use to communicate to a backend server or other persistence layer.
2016 e4a6c3b8 Olga Brani

2017 e4a6c3b8 Olga Brani
    This can be specified as an instance, class, or string.
2018 e4a6c3b8 Olga Brani

2019 e4a6c3b8 Olga Brani
    If you want to specify `App.CustomAdapter` as a string, do:
2020 e4a6c3b8 Olga Brani

2021 e4a6c3b8 Olga Brani
    ```js
2022 e4a6c3b8 Olga Brani
    adapter: 'custom'
2023 e4a6c3b8 Olga Brani
    ```
2024 e4a6c3b8 Olga Brani

2025 e4a6c3b8 Olga Brani
    @property adapter
2026 e4a6c3b8 Olga Brani
    @default DS.RESTAdapter
2027 e4a6c3b8 Olga Brani
    @type {DS.Adapter|String}
2028 e4a6c3b8 Olga Brani
  */
2029 e4a6c3b8 Olga Brani
  adapter: '_rest',
2030 e4a6c3b8 Olga Brani
2031 e4a6c3b8 Olga Brani
  /**
2032 e4a6c3b8 Olga Brani
    Returns a JSON representation of the record using a custom
2033 e4a6c3b8 Olga Brani
    type-specific serializer, if one exists.
2034 e4a6c3b8 Olga Brani

2035 e4a6c3b8 Olga Brani
    The available options are:
2036 e4a6c3b8 Olga Brani

2037 e4a6c3b8 Olga Brani
    * `includeId`: `true` if the record's ID should be included in
2038 e4a6c3b8 Olga Brani
      the JSON representation
2039 e4a6c3b8 Olga Brani

2040 e4a6c3b8 Olga Brani
    @method serialize
2041 e4a6c3b8 Olga Brani
    @private
2042 e4a6c3b8 Olga Brani
    @param {DS.Model} record the record to serialize
2043 e4a6c3b8 Olga Brani
    @param {Object} options an options hash
2044 e4a6c3b8 Olga Brani
  */
2045 e4a6c3b8 Olga Brani
  serialize: function(record, options) {
2046 e4a6c3b8 Olga Brani
    return this.serializerFor(record.constructor.typeKey).serialize(record, options);
2047 e4a6c3b8 Olga Brani
  },
2048 e4a6c3b8 Olga Brani
2049 e4a6c3b8 Olga Brani
  /**
2050 e4a6c3b8 Olga Brani
    This property returns the adapter, after resolving a possible
2051 e4a6c3b8 Olga Brani
    string key.
2052 e4a6c3b8 Olga Brani

2053 e4a6c3b8 Olga Brani
    If the supplied `adapter` was a class, or a String property
2054 e4a6c3b8 Olga Brani
    path resolved to a class, this property will instantiate the
2055 e4a6c3b8 Olga Brani
    class.
2056 e4a6c3b8 Olga Brani

2057 e4a6c3b8 Olga Brani
    This property is cacheable, so the same instance of a specified
2058 e4a6c3b8 Olga Brani
    adapter class should be used for the lifetime of the store.
2059 e4a6c3b8 Olga Brani

2060 e4a6c3b8 Olga Brani
    @property defaultAdapter
2061 e4a6c3b8 Olga Brani
    @private
2062 e4a6c3b8 Olga Brani
    @returns DS.Adapter
2063 e4a6c3b8 Olga Brani
  */
2064 e4a6c3b8 Olga Brani
  defaultAdapter: Ember.computed('adapter', function() {
2065 e4a6c3b8 Olga Brani
    var adapter = get(this, 'adapter');
2066 e4a6c3b8 Olga Brani
2067 e4a6c3b8 Olga Brani
    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 DS.Adapter));
2068 e4a6c3b8 Olga Brani
2069 e4a6c3b8 Olga Brani
    if (typeof adapter === 'string') {
2070 e4a6c3b8 Olga Brani
      adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:_rest');
2071 e4a6c3b8 Olga Brani
    }
2072 e4a6c3b8 Olga Brani
2073 e4a6c3b8 Olga Brani
    if (DS.Adapter.detect(adapter)) {
2074 e4a6c3b8 Olga Brani
      adapter = adapter.create({ container: this.container });
2075 e4a6c3b8 Olga Brani
    }
2076 e4a6c3b8 Olga Brani
2077 e4a6c3b8 Olga Brani
    return adapter;
2078 e4a6c3b8 Olga Brani
  }),
2079 e4a6c3b8 Olga Brani
2080 e4a6c3b8 Olga Brani
  // .....................
2081 e4a6c3b8 Olga Brani
  // . CREATE NEW RECORD .
2082 e4a6c3b8 Olga Brani
  // .....................
2083 e4a6c3b8 Olga Brani
2084 e4a6c3b8 Olga Brani
  /**
2085 e4a6c3b8 Olga Brani
    Create a new record in the current store. The properties passed
2086 e4a6c3b8 Olga Brani
    to this method are set on the newly created record.
2087 e4a6c3b8 Olga Brani

2088 e4a6c3b8 Olga Brani
    To create a new instance of `App.Post`:
2089 e4a6c3b8 Olga Brani

2090 e4a6c3b8 Olga Brani
    ```js
2091 e4a6c3b8 Olga Brani
    store.createRecord('post', {
2092 e4a6c3b8 Olga Brani
      title: "Rails is omakase"
2093 e4a6c3b8 Olga Brani
    });
2094 e4a6c3b8 Olga Brani
    ```
2095 e4a6c3b8 Olga Brani

2096 e4a6c3b8 Olga Brani
    @method createRecord
2097 e4a6c3b8 Olga Brani
    @param {String} type
2098 e4a6c3b8 Olga Brani
    @param {Object} properties a hash of properties to set on the
2099 e4a6c3b8 Olga Brani
      newly created record.
2100 e4a6c3b8 Olga Brani
    @returns {DS.Model} record
2101 e4a6c3b8 Olga Brani
  */
2102 e4a6c3b8 Olga Brani
  createRecord: function(type, properties) {
2103 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2104 e4a6c3b8 Olga Brani
2105 e4a6c3b8 Olga Brani
    properties = copy(properties) || {};
2106 e4a6c3b8 Olga Brani
2107 e4a6c3b8 Olga Brani
    // If the passed properties do not include a primary key,
2108 e4a6c3b8 Olga Brani
    // give the adapter an opportunity to generate one. Typically,
2109 e4a6c3b8 Olga Brani
    // client-side ID generators will use something like uuid.js
2110 e4a6c3b8 Olga Brani
    // to avoid conflicts.
2111 e4a6c3b8 Olga Brani
2112 e4a6c3b8 Olga Brani
    if (isNone(properties.id)) {
2113 e4a6c3b8 Olga Brani
      properties.id = this._generateId(type);
2114 e4a6c3b8 Olga Brani
    }
2115 e4a6c3b8 Olga Brani
2116 e4a6c3b8 Olga Brani
    // Coerce ID to a string
2117 e4a6c3b8 Olga Brani
    properties.id = coerceId(properties.id);
2118 e4a6c3b8 Olga Brani
2119 e4a6c3b8 Olga Brani
    var record = this.buildRecord(type, properties.id);
2120 e4a6c3b8 Olga Brani
2121 e4a6c3b8 Olga Brani
    // Move the record out of its initial `empty` state into
2122 e4a6c3b8 Olga Brani
    // the `loaded` state.
2123 e4a6c3b8 Olga Brani
    record.loadedData();
2124 e4a6c3b8 Olga Brani
2125 e4a6c3b8 Olga Brani
    // Set the properties specified on the record.
2126 e4a6c3b8 Olga Brani
    record.setProperties(properties);
2127 e4a6c3b8 Olga Brani
2128 e4a6c3b8 Olga Brani
    return record;
2129 e4a6c3b8 Olga Brani
  },
2130 e4a6c3b8 Olga Brani
2131 e4a6c3b8 Olga Brani
  /**
2132 e4a6c3b8 Olga Brani
    If possible, this method asks the adapter to generate an ID for
2133 e4a6c3b8 Olga Brani
    a newly created record.
2134 e4a6c3b8 Olga Brani

2135 e4a6c3b8 Olga Brani
    @method _generateId
2136 e4a6c3b8 Olga Brani
    @private
2137 e4a6c3b8 Olga Brani
    @param {String} type
2138 e4a6c3b8 Olga Brani
    @returns {String} if the adapter can generate one, an ID
2139 e4a6c3b8 Olga Brani
  */
2140 e4a6c3b8 Olga Brani
  _generateId: function(type) {
2141 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(type);
2142 e4a6c3b8 Olga Brani
2143 e4a6c3b8 Olga Brani
    if (adapter && adapter.generateIdForRecord) {
2144 e4a6c3b8 Olga Brani
      return adapter.generateIdForRecord(this);
2145 e4a6c3b8 Olga Brani
    }
2146 e4a6c3b8 Olga Brani
2147 e4a6c3b8 Olga Brani
    return null;
2148 e4a6c3b8 Olga Brani
  },
2149 e4a6c3b8 Olga Brani
2150 e4a6c3b8 Olga Brani
  // .................
2151 e4a6c3b8 Olga Brani
  // . DELETE RECORD .
2152 e4a6c3b8 Olga Brani
  // .................
2153 e4a6c3b8 Olga Brani
2154 e4a6c3b8 Olga Brani
  /**
2155 e4a6c3b8 Olga Brani
    For symmetry, a record can be deleted via the store.
2156 e4a6c3b8 Olga Brani

2157 e4a6c3b8 Olga Brani
    Example
2158 e4a6c3b8 Olga Brani

2159 e4a6c3b8 Olga Brani
    ```javascript
2160 e4a6c3b8 Olga Brani
    var post = store.createRecord('post', {
2161 e4a6c3b8 Olga Brani
      title: "Rails is omakase"
2162 e4a6c3b8 Olga Brani
    });
2163 e4a6c3b8 Olga Brani

2164 e4a6c3b8 Olga Brani
    store.deleteRecord(post);
2165 e4a6c3b8 Olga Brani
    ```
2166 e4a6c3b8 Olga Brani

2167 e4a6c3b8 Olga Brani
    @method deleteRecord
2168 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2169 e4a6c3b8 Olga Brani
  */
2170 e4a6c3b8 Olga Brani
  deleteRecord: function(record) {
2171 e4a6c3b8 Olga Brani
    record.deleteRecord();
2172 e4a6c3b8 Olga Brani
  },
2173 e4a6c3b8 Olga Brani
2174 e4a6c3b8 Olga Brani
  /**
2175 e4a6c3b8 Olga Brani
    For symmetry, a record can be unloaded via the store. Only
2176 e4a6c3b8 Olga Brani
    non-dirty records can be unloaded.
2177 e4a6c3b8 Olga Brani

2178 e4a6c3b8 Olga Brani
    Example
2179 e4a6c3b8 Olga Brani

2180 e4a6c3b8 Olga Brani
    ```javascript
2181 e4a6c3b8 Olga Brani
    store.find('post', 1).then(function(post) {
2182 e4a6c3b8 Olga Brani
      store.unloadRecord(post);
2183 e4a6c3b8 Olga Brani
    });
2184 e4a6c3b8 Olga Brani
    ```
2185 e4a6c3b8 Olga Brani

2186 e4a6c3b8 Olga Brani
    @method unloadRecord
2187 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2188 e4a6c3b8 Olga Brani
  */
2189 e4a6c3b8 Olga Brani
  unloadRecord: function(record) {
2190 e4a6c3b8 Olga Brani
    record.unloadRecord();
2191 e4a6c3b8 Olga Brani
  },
2192 e4a6c3b8 Olga Brani
2193 e4a6c3b8 Olga Brani
  // ................
2194 e4a6c3b8 Olga Brani
  // . FIND RECORDS .
2195 e4a6c3b8 Olga Brani
  // ................
2196 e4a6c3b8 Olga Brani
2197 e4a6c3b8 Olga Brani
  /**
2198 e4a6c3b8 Olga Brani
    This is the main entry point into finding records. The first parameter to
2199 e4a6c3b8 Olga Brani
    this method is the model's name as a string.
2200 e4a6c3b8 Olga Brani

2201 e4a6c3b8 Olga Brani
    ---
2202 e4a6c3b8 Olga Brani

2203 e4a6c3b8 Olga Brani
    To find a record by ID, pass the `id` as the second parameter:
2204 e4a6c3b8 Olga Brani

2205 e4a6c3b8 Olga Brani
    ```javascript
2206 e4a6c3b8 Olga Brani
    store.find('person', 1);
2207 e4a6c3b8 Olga Brani
    ```
2208 e4a6c3b8 Olga Brani

2209 e4a6c3b8 Olga Brani
    The `find` method will always return a **promise** that will be resolved
2210 e4a6c3b8 Olga Brani
    with the record. If the record was already in the store, the promise will
2211 e4a6c3b8 Olga Brani
    be resolved immediately. Otherwise, the store will ask the adapter's `find`
2212 e4a6c3b8 Olga Brani
    method to find the necessary data.
2213 e4a6c3b8 Olga Brani

2214 e4a6c3b8 Olga Brani
    The `find` method will always resolve its promise with the same object for
2215 e4a6c3b8 Olga Brani
    a given type and `id`.
2216 e4a6c3b8 Olga Brani

2217 e4a6c3b8 Olga Brani
    ---
2218 e4a6c3b8 Olga Brani

2219 e4a6c3b8 Olga Brani
    To find all records for a type, call `find` with no additional parameters:
2220 e4a6c3b8 Olga Brani

2221 e4a6c3b8 Olga Brani
    ```javascript
2222 e4a6c3b8 Olga Brani
    store.find('person');
2223 e4a6c3b8 Olga Brani
    ```
2224 e4a6c3b8 Olga Brani

2225 e4a6c3b8 Olga Brani
    This will ask the adapter's `findAll` method to find the records for the
2226 e4a6c3b8 Olga Brani
    given type, and return a promise that will be resolved once the server
2227 e4a6c3b8 Olga Brani
    returns the values.
2228 e4a6c3b8 Olga Brani

2229 e4a6c3b8 Olga Brani
    ---
2230 e4a6c3b8 Olga Brani

2231 e4a6c3b8 Olga Brani
    To find a record by a query, call `find` with a hash as the second
2232 e4a6c3b8 Olga Brani
    parameter:
2233 e4a6c3b8 Olga Brani

2234 e4a6c3b8 Olga Brani
    ```javascript
2235 e4a6c3b8 Olga Brani
    store.find(App.Person, { page: 1 });
2236 e4a6c3b8 Olga Brani
    ```
2237 e4a6c3b8 Olga Brani

2238 e4a6c3b8 Olga Brani
    This will ask the adapter's `findQuery` method to find the records for
2239 e4a6c3b8 Olga Brani
    the query, and return a promise that will be resolved once the server
2240 e4a6c3b8 Olga Brani
    responds.
2241 e4a6c3b8 Olga Brani

2242 e4a6c3b8 Olga Brani
    @method find
2243 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2244 e4a6c3b8 Olga Brani
    @param {Object|String|Integer|null} id
2245 e4a6c3b8 Olga Brani
    @return {Promise} promise
2246 e4a6c3b8 Olga Brani
  */
2247 e4a6c3b8 Olga Brani
  find: function(type, id) {
2248 e4a6c3b8 Olga Brani
    if (id === undefined) {
2249 e4a6c3b8 Olga Brani
      return this.findAll(type);
2250 e4a6c3b8 Olga Brani
    }
2251 e4a6c3b8 Olga Brani
2252 e4a6c3b8 Olga Brani
    // We are passed a query instead of an id.
2253 e4a6c3b8 Olga Brani
    if (Ember.typeOf(id) === 'object') {
2254 e4a6c3b8 Olga Brani
      return this.findQuery(type, id);
2255 e4a6c3b8 Olga Brani
    }
2256 e4a6c3b8 Olga Brani
2257 e4a6c3b8 Olga Brani
    return this.findById(type, coerceId(id));
2258 e4a6c3b8 Olga Brani
  },
2259 e4a6c3b8 Olga Brani
2260 e4a6c3b8 Olga Brani
  /**
2261 e4a6c3b8 Olga Brani
    This method returns a record for a given type and id combination.
2262 e4a6c3b8 Olga Brani

2263 e4a6c3b8 Olga Brani
    @method findById
2264 e4a6c3b8 Olga Brani
    @private
2265 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2266 e4a6c3b8 Olga Brani
    @param {String|Integer} id
2267 e4a6c3b8 Olga Brani
    @return {Promise} promise
2268 e4a6c3b8 Olga Brani
  */
2269 e4a6c3b8 Olga Brani
  findById: function(type, id) {
2270 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2271 e4a6c3b8 Olga Brani
2272 e4a6c3b8 Olga Brani
    var record = this.recordForId(type, id);
2273 e4a6c3b8 Olga Brani
2274 e4a6c3b8 Olga Brani
    var promise = this.fetchRecord(record) || resolve(record, "DS: Store#findById " + type + " with id: " + id);
2275 e4a6c3b8 Olga Brani
    return promiseObject(promise);
2276 e4a6c3b8 Olga Brani
  },
2277 e4a6c3b8 Olga Brani
2278 e4a6c3b8 Olga Brani
  /**
2279 e4a6c3b8 Olga Brani
    This method makes a series of requests to the adapter's `find` method
2280 e4a6c3b8 Olga Brani
    and returns a promise that resolves once they are all loaded.
2281 e4a6c3b8 Olga Brani

2282 e4a6c3b8 Olga Brani
    @private
2283 e4a6c3b8 Olga Brani
    @method findByIds
2284 e4a6c3b8 Olga Brani
    @param {String} type
2285 e4a6c3b8 Olga Brani
    @param {Array} ids
2286 e4a6c3b8 Olga Brani
    @returns {Promise} promise
2287 e4a6c3b8 Olga Brani
  */
2288 e4a6c3b8 Olga Brani
  findByIds: function(type, ids) {
2289 e4a6c3b8 Olga Brani
    var store = this;
2290 e4a6c3b8 Olga Brani
    var promiseLabel = "DS: Store#findByIds " + type;
2291 e4a6c3b8 Olga Brani
    return promiseArray(Ember.RSVP.all(map(ids, function(id) {
2292 e4a6c3b8 Olga Brani
      return store.findById(type, id);
2293 e4a6c3b8 Olga Brani
    })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete"));
2294 e4a6c3b8 Olga Brani
  },
2295 e4a6c3b8 Olga Brani
2296 e4a6c3b8 Olga Brani
  /**
2297 e4a6c3b8 Olga Brani
    This method is called by `findById` if it discovers that a particular
2298 e4a6c3b8 Olga Brani
    type/id pair hasn't been loaded yet to kick off a request to the
2299 e4a6c3b8 Olga Brani
    adapter.
2300 e4a6c3b8 Olga Brani

2301 e4a6c3b8 Olga Brani
    @method fetchRecord
2302 e4a6c3b8 Olga Brani
    @private
2303 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2304 e4a6c3b8 Olga Brani
    @returns {Promise} promise
2305 e4a6c3b8 Olga Brani
  */
2306 e4a6c3b8 Olga Brani
  fetchRecord: function(record) {
2307 e4a6c3b8 Olga Brani
    if (isNone(record)) { return null; }
2308 e4a6c3b8 Olga Brani
    if (record._loadingPromise) { return record._loadingPromise; }
2309 e4a6c3b8 Olga Brani
    if (!get(record, 'isEmpty')) { return null; }
2310 e4a6c3b8 Olga Brani
2311 e4a6c3b8 Olga Brani
    var type = record.constructor,
2312 e4a6c3b8 Olga Brani
        id = get(record, 'id');
2313 e4a6c3b8 Olga Brani
2314 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(type);
2315 e4a6c3b8 Olga Brani
2316 e4a6c3b8 Olga Brani
    Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter);
2317 e4a6c3b8 Olga Brani
    Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find);
2318 e4a6c3b8 Olga Brani
2319 e4a6c3b8 Olga Brani
    var promise = _find(adapter, this, type, id);
2320 e4a6c3b8 Olga Brani
    record.loadingData(promise);
2321 e4a6c3b8 Olga Brani
    return promise;
2322 e4a6c3b8 Olga Brani
  },
2323 e4a6c3b8 Olga Brani
2324 e4a6c3b8 Olga Brani
  /**
2325 e4a6c3b8 Olga Brani
    Get a record by a given type and ID without triggering a fetch.
2326 e4a6c3b8 Olga Brani

2327 e4a6c3b8 Olga Brani
    This method will synchronously return the record if it's available.
2328 e4a6c3b8 Olga Brani
    Otherwise, it will return null.
2329 e4a6c3b8 Olga Brani

2330 e4a6c3b8 Olga Brani
    ```js
2331 e4a6c3b8 Olga Brani
    var post = store.getById('post', 1);
2332 e4a6c3b8 Olga Brani
    ```
2333 e4a6c3b8 Olga Brani

2334 e4a6c3b8 Olga Brani
    @method getById
2335 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2336 e4a6c3b8 Olga Brani
    @param {String|Integer} id
2337 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2338 e4a6c3b8 Olga Brani
  */
2339 e4a6c3b8 Olga Brani
  getById: function(type, id) {
2340 e4a6c3b8 Olga Brani
    if (this.hasRecordForId(type, id)) {
2341 e4a6c3b8 Olga Brani
      return this.recordForId(type, id);
2342 e4a6c3b8 Olga Brani
    } else {
2343 e4a6c3b8 Olga Brani
      return null;
2344 e4a6c3b8 Olga Brani
    }
2345 e4a6c3b8 Olga Brani
  },
2346 e4a6c3b8 Olga Brani
2347 e4a6c3b8 Olga Brani
  /**
2348 e4a6c3b8 Olga Brani
    This method is called by the record's `reload` method.
2349 e4a6c3b8 Olga Brani

2350 e4a6c3b8 Olga Brani
    This method calls the adapter's `find` method, which returns a promise. When
2351 e4a6c3b8 Olga Brani
    **that** promise resolves, `reloadRecord` will resolve the promise returned
2352 e4a6c3b8 Olga Brani
    by the record's `reload`.
2353 e4a6c3b8 Olga Brani

2354 e4a6c3b8 Olga Brani
    @method reloadRecord
2355 e4a6c3b8 Olga Brani
    @private
2356 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2357 e4a6c3b8 Olga Brani
    @return {Promise} promise
2358 e4a6c3b8 Olga Brani
  */
2359 e4a6c3b8 Olga Brani
  reloadRecord: function(record) {
2360 e4a6c3b8 Olga Brani
    var type = record.constructor,
2361 e4a6c3b8 Olga Brani
        adapter = this.adapterFor(type),
2362 e4a6c3b8 Olga Brani
        id = get(record, 'id');
2363 e4a6c3b8 Olga Brani
2364 e4a6c3b8 Olga Brani
    Ember.assert("You cannot reload a record without an ID", id);
2365 e4a6c3b8 Olga Brani
    Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter);
2366 e4a6c3b8 Olga Brani
    Ember.assert("You tried to reload a record but your adapter does not implement `find`", adapter.find);
2367 e4a6c3b8 Olga Brani
2368 e4a6c3b8 Olga Brani
    return _find(adapter, this, type, id);
2369 e4a6c3b8 Olga Brani
  },
2370 e4a6c3b8 Olga Brani
2371 e4a6c3b8 Olga Brani
  /**
2372 e4a6c3b8 Olga Brani
    This method takes a list of records, groups the records by type,
2373 e4a6c3b8 Olga Brani
    converts the records into IDs, and then invokes the adapter's `findMany`
2374 e4a6c3b8 Olga Brani
    method.
2375 e4a6c3b8 Olga Brani

2376 e4a6c3b8 Olga Brani
    The records are grouped by type to invoke `findMany` on adapters
2377 e4a6c3b8 Olga Brani
    for each unique type in records.
2378 e4a6c3b8 Olga Brani

2379 e4a6c3b8 Olga Brani
    It is used both by a brand new relationship (via the `findMany`
2380 e4a6c3b8 Olga Brani
    method) or when the data underlying an existing relationship
2381 e4a6c3b8 Olga Brani
    changes.
2382 e4a6c3b8 Olga Brani

2383 e4a6c3b8 Olga Brani
    @method fetchMany
2384 e4a6c3b8 Olga Brani
    @private
2385 e4a6c3b8 Olga Brani
    @param {Array} records
2386 e4a6c3b8 Olga Brani
    @param {DS.Model} owner
2387 e4a6c3b8 Olga Brani
    @param {Resolver} resolver
2388 e4a6c3b8 Olga Brani
  */
2389 e4a6c3b8 Olga Brani
  fetchMany: function(records, owner, resolver) {
2390 e4a6c3b8 Olga Brani
    if (!records.length) { return; }
2391 e4a6c3b8 Olga Brani
2392 e4a6c3b8 Olga Brani
    // Group By Type
2393 e4a6c3b8 Olga Brani
    var recordsByTypeMap = Ember.MapWithDefault.create({
2394 e4a6c3b8 Olga Brani
      defaultValue: function() { return Ember.A(); }
2395 e4a6c3b8 Olga Brani
    });
2396 e4a6c3b8 Olga Brani
2397 e4a6c3b8 Olga Brani
    forEach(records, function(record) {
2398 e4a6c3b8 Olga Brani
      recordsByTypeMap.get(record.constructor).push(record);
2399 e4a6c3b8 Olga Brani
    });
2400 e4a6c3b8 Olga Brani
2401 e4a6c3b8 Olga Brani
    forEach(recordsByTypeMap, function(type, records) {
2402 e4a6c3b8 Olga Brani
      var ids = records.mapProperty('id'),
2403 e4a6c3b8 Olga Brani
          adapter = this.adapterFor(type);
2404 e4a6c3b8 Olga Brani
2405 e4a6c3b8 Olga Brani
      Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter);
2406 e4a6c3b8 Olga Brani
      Ember.assert("You tried to load many records but your adapter does not implement `findMany`", adapter.findMany);
2407 e4a6c3b8 Olga Brani
2408 e4a6c3b8 Olga Brani
      resolver.resolve(_findMany(adapter, this, type, ids, owner));
2409 e4a6c3b8 Olga Brani
    }, this);
2410 e4a6c3b8 Olga Brani
  },
2411 e4a6c3b8 Olga Brani
2412 e4a6c3b8 Olga Brani
  /**
2413 e4a6c3b8 Olga Brani
    Returns true if a record for a given type and ID is already loaded.
2414 e4a6c3b8 Olga Brani

2415 e4a6c3b8 Olga Brani
    @method hasRecordForId
2416 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2417 e4a6c3b8 Olga Brani
    @param {String|Integer} id
2418 e4a6c3b8 Olga Brani
    @returns {Boolean}
2419 e4a6c3b8 Olga Brani
  */
2420 e4a6c3b8 Olga Brani
  hasRecordForId: function(type, id) {
2421 e4a6c3b8 Olga Brani
    id = coerceId(id);
2422 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2423 e4a6c3b8 Olga Brani
    return !!this.typeMapFor(type).idToRecord[id];
2424 e4a6c3b8 Olga Brani
  },
2425 e4a6c3b8 Olga Brani
2426 e4a6c3b8 Olga Brani
  /**
2427 e4a6c3b8 Olga Brani
    Returns id record for a given type and ID. If one isn't already loaded,
2428 e4a6c3b8 Olga Brani
    it builds a new record and leaves it in the `empty` state.
2429 e4a6c3b8 Olga Brani

2430 e4a6c3b8 Olga Brani
    @method recordForId
2431 e4a6c3b8 Olga Brani
    @private
2432 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2433 e4a6c3b8 Olga Brani
    @param {String|Integer} id
2434 e4a6c3b8 Olga Brani
    @returns {DS.Model} record
2435 e4a6c3b8 Olga Brani
  */
2436 e4a6c3b8 Olga Brani
  recordForId: function(type, id) {
2437 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2438 e4a6c3b8 Olga Brani
2439 e4a6c3b8 Olga Brani
    id = coerceId(id);
2440 e4a6c3b8 Olga Brani
2441 e4a6c3b8 Olga Brani
    var record = this.typeMapFor(type).idToRecord[id];
2442 e4a6c3b8 Olga Brani
2443 e4a6c3b8 Olga Brani
    if (!record) {
2444 e4a6c3b8 Olga Brani
      record = this.buildRecord(type, id);
2445 e4a6c3b8 Olga Brani
    }
2446 e4a6c3b8 Olga Brani
2447 e4a6c3b8 Olga Brani
    return record;
2448 e4a6c3b8 Olga Brani
  },
2449 e4a6c3b8 Olga Brani
2450 e4a6c3b8 Olga Brani
  /**
2451 e4a6c3b8 Olga Brani
    @method findMany
2452 e4a6c3b8 Olga Brani
    @private
2453 e4a6c3b8 Olga Brani
    @param {DS.Model} owner
2454 e4a6c3b8 Olga Brani
    @param {Array} records
2455 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2456 e4a6c3b8 Olga Brani
    @param {Resolver} resolver
2457 e4a6c3b8 Olga Brani
    @return {DS.ManyArray} records
2458 e4a6c3b8 Olga Brani
  */
2459 e4a6c3b8 Olga Brani
  findMany: function(owner, records, type, resolver) {
2460 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2461 e4a6c3b8 Olga Brani
2462 e4a6c3b8 Olga Brani
    records = Ember.A(records);
2463 e4a6c3b8 Olga Brani
2464 e4a6c3b8 Olga Brani
    var unloadedRecords = records.filterProperty('isEmpty', true),
2465 e4a6c3b8 Olga Brani
        manyArray = this.recordArrayManager.createManyArray(type, records);
2466 e4a6c3b8 Olga Brani
2467 e4a6c3b8 Olga Brani
    forEach(unloadedRecords, function(record) {
2468 e4a6c3b8 Olga Brani
      record.loadingData();
2469 e4a6c3b8 Olga Brani
    });
2470 e4a6c3b8 Olga Brani
2471 e4a6c3b8 Olga Brani
    manyArray.loadingRecordsCount = unloadedRecords.length;
2472 e4a6c3b8 Olga Brani
2473 e4a6c3b8 Olga Brani
    if (unloadedRecords.length) {
2474 e4a6c3b8 Olga Brani
      forEach(unloadedRecords, function(record) {
2475 e4a6c3b8 Olga Brani
        this.recordArrayManager.registerWaitingRecordArray(record, manyArray);
2476 e4a6c3b8 Olga Brani
      }, this);
2477 e4a6c3b8 Olga Brani
2478 e4a6c3b8 Olga Brani
      this.fetchMany(unloadedRecords, owner, resolver);
2479 e4a6c3b8 Olga Brani
    } else {
2480 e4a6c3b8 Olga Brani
      if (resolver) { resolver.resolve(); }
2481 e4a6c3b8 Olga Brani
      manyArray.set('isLoaded', true);
2482 e4a6c3b8 Olga Brani
      Ember.run.once(manyArray, 'trigger', 'didLoad');
2483 e4a6c3b8 Olga Brani
    }
2484 e4a6c3b8 Olga Brani
2485 e4a6c3b8 Olga Brani
    return manyArray;
2486 e4a6c3b8 Olga Brani
  },
2487 e4a6c3b8 Olga Brani
2488 e4a6c3b8 Olga Brani
  /**
2489 e4a6c3b8 Olga Brani
    If a relationship was originally populated by the adapter as a link
2490 e4a6c3b8 Olga Brani
    (as opposed to a list of IDs), this method is called when the
2491 e4a6c3b8 Olga Brani
    relationship is fetched.
2492 e4a6c3b8 Olga Brani

2493 e4a6c3b8 Olga Brani
    The link (which is usually a URL) is passed through unchanged, so the
2494 e4a6c3b8 Olga Brani
    adapter can make whatever request it wants.
2495 e4a6c3b8 Olga Brani

2496 e4a6c3b8 Olga Brani
    The usual use-case is for the server to register a URL as a link, and
2497 e4a6c3b8 Olga Brani
    then use that URL in the future to make a request for the relationship.
2498 e4a6c3b8 Olga Brani

2499 e4a6c3b8 Olga Brani
    @method findHasMany
2500 e4a6c3b8 Olga Brani
    @private
2501 e4a6c3b8 Olga Brani
    @param {DS.Model} owner
2502 e4a6c3b8 Olga Brani
    @param {any} link
2503 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2504 e4a6c3b8 Olga Brani
    @param {Resolver} resolver
2505 e4a6c3b8 Olga Brani
    @return {DS.ManyArray}
2506 e4a6c3b8 Olga Brani
  */
2507 e4a6c3b8 Olga Brani
  findHasMany: function(owner, link, relationship, resolver) {
2508 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(owner.constructor);
2509 e4a6c3b8 Olga Brani
2510 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter);
2511 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", adapter.findHasMany);
2512 e4a6c3b8 Olga Brani
2513 e4a6c3b8 Olga Brani
    var records = this.recordArrayManager.createManyArray(relationship.type, Ember.A([]));
2514 e4a6c3b8 Olga Brani
    resolver.resolve(_findHasMany(adapter, this, owner, link, relationship));
2515 e4a6c3b8 Olga Brani
    return records;
2516 e4a6c3b8 Olga Brani
  },
2517 e4a6c3b8 Olga Brani
2518 e4a6c3b8 Olga Brani
  /**
2519 e4a6c3b8 Olga Brani
    @method findBelongsTo
2520 e4a6c3b8 Olga Brani
    @private
2521 e4a6c3b8 Olga Brani
    @param {DS.Model} owner
2522 e4a6c3b8 Olga Brani
    @param {any} link
2523 e4a6c3b8 Olga Brani
    @param {Relationship} relationship
2524 e4a6c3b8 Olga Brani
    @param {Resolver} resolver
2525 e4a6c3b8 Olga Brani
  */
2526 e4a6c3b8 Olga Brani
  findBelongsTo: function(owner, link, relationship, resolver) {
2527 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(owner.constructor);
2528 e4a6c3b8 Olga Brani
2529 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter);
2530 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", adapter.findBelongsTo);
2531 e4a6c3b8 Olga Brani
2532 e4a6c3b8 Olga Brani
    resolver.resolve(_findBelongsTo(adapter, this, owner, link, relationship));
2533 e4a6c3b8 Olga Brani
  },
2534 e4a6c3b8 Olga Brani
2535 e4a6c3b8 Olga Brani
  /**
2536 e4a6c3b8 Olga Brani
    This method delegates a query to the adapter. This is the one place where
2537 e4a6c3b8 Olga Brani
    adapter-level semantics are exposed to the application.
2538 e4a6c3b8 Olga Brani

2539 e4a6c3b8 Olga Brani
    Exposing queries this way seems preferable to creating an abstract query
2540 e4a6c3b8 Olga Brani
    language for all server-side queries, and then require all adapters to
2541 e4a6c3b8 Olga Brani
    implement them.
2542 e4a6c3b8 Olga Brani

2543 e4a6c3b8 Olga Brani
    This method returns a promise, which is resolved with a `RecordArray`
2544 e4a6c3b8 Olga Brani
    once the server returns.
2545 e4a6c3b8 Olga Brani

2546 e4a6c3b8 Olga Brani
    @method findQuery
2547 e4a6c3b8 Olga Brani
    @private
2548 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2549 e4a6c3b8 Olga Brani
    @param {any} query an opaque query to be used by the adapter
2550 e4a6c3b8 Olga Brani
    @return {Promise} promise
2551 e4a6c3b8 Olga Brani
  */
2552 e4a6c3b8 Olga Brani
  findQuery: function(type, query) {
2553 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2554 e4a6c3b8 Olga Brani
2555 e4a6c3b8 Olga Brani
    var array = this.recordArrayManager
2556 e4a6c3b8 Olga Brani
      .createAdapterPopulatedRecordArray(type, query);
2557 e4a6c3b8 Olga Brani
2558 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(type),
2559 e4a6c3b8 Olga Brani
        promiseLabel = "DS: Store#findQuery " + type,
2560 e4a6c3b8 Olga Brani
        resolver = Ember.RSVP.defer(promiseLabel);
2561 e4a6c3b8 Olga Brani
2562 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter);
2563 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", adapter.findQuery);
2564 e4a6c3b8 Olga Brani
2565 e4a6c3b8 Olga Brani
    resolver.resolve(_findQuery(adapter, this, type, query, array));
2566 e4a6c3b8 Olga Brani
2567 e4a6c3b8 Olga Brani
    return promiseArray(resolver.promise);
2568 e4a6c3b8 Olga Brani
  },
2569 e4a6c3b8 Olga Brani
2570 e4a6c3b8 Olga Brani
  /**
2571 e4a6c3b8 Olga Brani
    This method returns an array of all records adapter can find.
2572 e4a6c3b8 Olga Brani
    It triggers the adapter's `findAll` method to give it an opportunity to populate
2573 e4a6c3b8 Olga Brani
    the array with records of that type.
2574 e4a6c3b8 Olga Brani

2575 e4a6c3b8 Olga Brani
    @method findAll
2576 e4a6c3b8 Olga Brani
    @private
2577 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2578 e4a6c3b8 Olga Brani
    @return {DS.AdapterPopulatedRecordArray}
2579 e4a6c3b8 Olga Brani
  */
2580 e4a6c3b8 Olga Brani
  findAll: function(type) {
2581 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2582 e4a6c3b8 Olga Brani
2583 e4a6c3b8 Olga Brani
    return this.fetchAll(type, this.all(type));
2584 e4a6c3b8 Olga Brani
  },
2585 e4a6c3b8 Olga Brani
2586 e4a6c3b8 Olga Brani
  /**
2587 e4a6c3b8 Olga Brani
    @method fetchAll
2588 e4a6c3b8 Olga Brani
    @private
2589 e4a6c3b8 Olga Brani
    @param {DS.Model} type
2590 e4a6c3b8 Olga Brani
    @param {DS.RecordArray} array
2591 e4a6c3b8 Olga Brani
    @returns {Promise} promise
2592 e4a6c3b8 Olga Brani
  */
2593 e4a6c3b8 Olga Brani
  fetchAll: function(type, array) {
2594 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(type),
2595 e4a6c3b8 Olga Brani
        sinceToken = this.typeMapFor(type).metadata.since;
2596 e4a6c3b8 Olga Brani
2597 e4a6c3b8 Olga Brani
    set(array, 'isUpdating', true);
2598 e4a6c3b8 Olga Brani
2599 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter);
2600 e4a6c3b8 Olga Brani
    Ember.assert("You tried to load all records but your adapter does not implement `findAll`", adapter.findAll);
2601 e4a6c3b8 Olga Brani
2602 e4a6c3b8 Olga Brani
    return promiseArray(_findAll(adapter, this, type, sinceToken));
2603 e4a6c3b8 Olga Brani
  },
2604 e4a6c3b8 Olga Brani
2605 e4a6c3b8 Olga Brani
  /**
2606 e4a6c3b8 Olga Brani
    @method didUpdateAll
2607 e4a6c3b8 Olga Brani
    @param {DS.Model} type
2608 e4a6c3b8 Olga Brani
  */
2609 e4a6c3b8 Olga Brani
  didUpdateAll: function(type) {
2610 e4a6c3b8 Olga Brani
    var findAllCache = this.typeMapFor(type).findAllCache;
2611 e4a6c3b8 Olga Brani
    set(findAllCache, 'isUpdating', false);
2612 e4a6c3b8 Olga Brani
  },
2613 e4a6c3b8 Olga Brani
2614 e4a6c3b8 Olga Brani
  /**
2615 e4a6c3b8 Olga Brani
    This method returns a filtered array that contains all of the known records
2616 e4a6c3b8 Olga Brani
    for a given type.
2617 e4a6c3b8 Olga Brani

2618 e4a6c3b8 Olga Brani
    Note that because it's just a filter, it will have any locally
2619 e4a6c3b8 Olga Brani
    created records of the type.
2620 e4a6c3b8 Olga Brani

2621 e4a6c3b8 Olga Brani
    Also note that multiple calls to `all` for a given type will always
2622 e4a6c3b8 Olga Brani
    return the same RecordArray.
2623 e4a6c3b8 Olga Brani

2624 e4a6c3b8 Olga Brani
    Example
2625 e4a6c3b8 Olga Brani

2626 e4a6c3b8 Olga Brani
    ```javascript
2627 e4a6c3b8 Olga Brani
    var local_posts = store.all(App.Post);
2628 e4a6c3b8 Olga Brani
    ```
2629 e4a6c3b8 Olga Brani

2630 e4a6c3b8 Olga Brani
    @method all
2631 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2632 e4a6c3b8 Olga Brani
    @return {DS.RecordArray}
2633 e4a6c3b8 Olga Brani
  */
2634 e4a6c3b8 Olga Brani
  all: function(type) {
2635 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2636 e4a6c3b8 Olga Brani
2637 e4a6c3b8 Olga Brani
    var typeMap = this.typeMapFor(type),
2638 e4a6c3b8 Olga Brani
        findAllCache = typeMap.findAllCache;
2639 e4a6c3b8 Olga Brani
2640 e4a6c3b8 Olga Brani
    if (findAllCache) { return findAllCache; }
2641 e4a6c3b8 Olga Brani
2642 e4a6c3b8 Olga Brani
    var array = this.recordArrayManager.createRecordArray(type);
2643 e4a6c3b8 Olga Brani
2644 e4a6c3b8 Olga Brani
    typeMap.findAllCache = array;
2645 e4a6c3b8 Olga Brani
    return array;
2646 e4a6c3b8 Olga Brani
  },
2647 e4a6c3b8 Olga Brani
2648 e4a6c3b8 Olga Brani
2649 e4a6c3b8 Olga Brani
  /**
2650 e4a6c3b8 Olga Brani
    This method unloads all of the known records for a given type.
2651 e4a6c3b8 Olga Brani

2652 e4a6c3b8 Olga Brani
    ```javascript
2653 e4a6c3b8 Olga Brani
    store.unloadAll(App.Post);
2654 e4a6c3b8 Olga Brani
    ```
2655 e4a6c3b8 Olga Brani

2656 e4a6c3b8 Olga Brani
    @method unloadAll
2657 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2658 e4a6c3b8 Olga Brani
  */
2659 e4a6c3b8 Olga Brani
  unloadAll: function(type) {
2660 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2661 e4a6c3b8 Olga Brani
2662 e4a6c3b8 Olga Brani
    var typeMap = this.typeMapFor(type),
2663 e4a6c3b8 Olga Brani
        records = typeMap.records.splice(0), record;
2664 e4a6c3b8 Olga Brani
2665 e4a6c3b8 Olga Brani
    while(record = records.pop()) {
2666 e4a6c3b8 Olga Brani
      record.unloadRecord();
2667 e4a6c3b8 Olga Brani
    }
2668 e4a6c3b8 Olga Brani
2669 e4a6c3b8 Olga Brani
    typeMap.findAllCache = null;
2670 e4a6c3b8 Olga Brani
  },
2671 e4a6c3b8 Olga Brani
2672 e4a6c3b8 Olga Brani
  /**
2673 e4a6c3b8 Olga Brani
    Takes a type and filter function, and returns a live RecordArray that
2674 e4a6c3b8 Olga Brani
    remains up to date as new records are loaded into the store or created
2675 e4a6c3b8 Olga Brani
    locally.
2676 e4a6c3b8 Olga Brani

2677 e4a6c3b8 Olga Brani
    The callback function takes a materialized record, and returns true
2678 e4a6c3b8 Olga Brani
    if the record should be included in the filter and false if it should
2679 e4a6c3b8 Olga Brani
    not.
2680 e4a6c3b8 Olga Brani

2681 e4a6c3b8 Olga Brani
    The filter function is called once on all records for the type when
2682 e4a6c3b8 Olga Brani
    it is created, and then once on each newly loaded or created record.
2683 e4a6c3b8 Olga Brani

2684 e4a6c3b8 Olga Brani
    If any of a record's properties change, or if it changes state, the
2685 e4a6c3b8 Olga Brani
    filter function will be invoked again to determine whether it should
2686 e4a6c3b8 Olga Brani
    still be in the array.
2687 e4a6c3b8 Olga Brani

2688 e4a6c3b8 Olga Brani
    Optionally you can pass a query which will be triggered at first. The
2689 e4a6c3b8 Olga Brani
    results returned by the server could then appear in the filter if they
2690 e4a6c3b8 Olga Brani
    match the filter function.
2691 e4a6c3b8 Olga Brani

2692 e4a6c3b8 Olga Brani
    Example
2693 e4a6c3b8 Olga Brani

2694 e4a6c3b8 Olga Brani
    ```javascript
2695 e4a6c3b8 Olga Brani
    store.filter(App.Post, {unread: true}, function(post) {
2696 e4a6c3b8 Olga Brani
      return post.get('unread');
2697 e4a6c3b8 Olga Brani
    }).then(function(unreadPosts) {
2698 e4a6c3b8 Olga Brani
      unreadPosts.get('length'); // 5
2699 e4a6c3b8 Olga Brani
      var unreadPost = unreadPosts.objectAt(0);
2700 e4a6c3b8 Olga Brani
      unreadPosts.set('unread', false);
2701 e4a6c3b8 Olga Brani
      unreadPosts.get('length'); // 4
2702 e4a6c3b8 Olga Brani
    });
2703 e4a6c3b8 Olga Brani
    ```
2704 e4a6c3b8 Olga Brani

2705 e4a6c3b8 Olga Brani
    @method filter
2706 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2707 e4a6c3b8 Olga Brani
    @param {Object} query optional query
2708 e4a6c3b8 Olga Brani
    @param {Function} filter
2709 e4a6c3b8 Olga Brani
    @return {DS.PromiseArray}
2710 e4a6c3b8 Olga Brani
  */
2711 e4a6c3b8 Olga Brani
  filter: function(type, query, filter) {
2712 e4a6c3b8 Olga Brani
    var promise;
2713 e4a6c3b8 Olga Brani
2714 e4a6c3b8 Olga Brani
    // allow an optional server query
2715 e4a6c3b8 Olga Brani
    if (arguments.length === 3) {
2716 e4a6c3b8 Olga Brani
      promise = this.findQuery(type, query);
2717 e4a6c3b8 Olga Brani
    } else if (arguments.length === 2) {
2718 e4a6c3b8 Olga Brani
      filter = query;
2719 e4a6c3b8 Olga Brani
    }
2720 e4a6c3b8 Olga Brani
2721 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2722 e4a6c3b8 Olga Brani
2723 e4a6c3b8 Olga Brani
    var array = this.recordArrayManager
2724 e4a6c3b8 Olga Brani
      .createFilteredRecordArray(type, filter);
2725 e4a6c3b8 Olga Brani
    promise = promise || resolve(array);
2726 e4a6c3b8 Olga Brani
2727 e4a6c3b8 Olga Brani
    return promiseArray(promise.then(function() {
2728 e4a6c3b8 Olga Brani
      return array;
2729 e4a6c3b8 Olga Brani
    }, null, "DS: Store#filter of " + type));
2730 e4a6c3b8 Olga Brani
  },
2731 e4a6c3b8 Olga Brani
2732 e4a6c3b8 Olga Brani
  /**
2733 e4a6c3b8 Olga Brani
    This method returns if a certain record is already loaded
2734 e4a6c3b8 Olga Brani
    in the store. Use this function to know beforehand if a find()
2735 e4a6c3b8 Olga Brani
    will result in a request or that it will be a cache hit.
2736 e4a6c3b8 Olga Brani

2737 e4a6c3b8 Olga Brani
     Example
2738 e4a6c3b8 Olga Brani

2739 e4a6c3b8 Olga Brani
    ```javascript
2740 e4a6c3b8 Olga Brani
    store.recordIsLoaded(App.Post, 1); // false
2741 e4a6c3b8 Olga Brani
    store.find(App.Post, 1).then(function() {
2742 e4a6c3b8 Olga Brani
      store.recordIsLoaded(App.Post, 1); // true
2743 e4a6c3b8 Olga Brani
    });
2744 e4a6c3b8 Olga Brani
    ```
2745 e4a6c3b8 Olga Brani

2746 e4a6c3b8 Olga Brani
    @method recordIsLoaded
2747 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2748 e4a6c3b8 Olga Brani
    @param {string} id
2749 e4a6c3b8 Olga Brani
    @return {boolean}
2750 e4a6c3b8 Olga Brani
  */
2751 e4a6c3b8 Olga Brani
  recordIsLoaded: function(type, id) {
2752 e4a6c3b8 Olga Brani
    if (!this.hasRecordForId(type, id)) { return false; }
2753 e4a6c3b8 Olga Brani
    return !get(this.recordForId(type, id), 'isEmpty');
2754 e4a6c3b8 Olga Brani
  },
2755 e4a6c3b8 Olga Brani
2756 e4a6c3b8 Olga Brani
  /**
2757 e4a6c3b8 Olga Brani
    This method returns the metadata for a specific type.
2758 e4a6c3b8 Olga Brani

2759 e4a6c3b8 Olga Brani
    @method metadataFor
2760 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2761 e4a6c3b8 Olga Brani
    @return {object}
2762 e4a6c3b8 Olga Brani
  */
2763 e4a6c3b8 Olga Brani
  metadataFor: function(type) {
2764 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
2765 e4a6c3b8 Olga Brani
    return this.typeMapFor(type).metadata;
2766 e4a6c3b8 Olga Brani
  },
2767 e4a6c3b8 Olga Brani
2768 e4a6c3b8 Olga Brani
  // ............
2769 e4a6c3b8 Olga Brani
  // . UPDATING .
2770 e4a6c3b8 Olga Brani
  // ............
2771 e4a6c3b8 Olga Brani
2772 e4a6c3b8 Olga Brani
  /**
2773 e4a6c3b8 Olga Brani
    If the adapter updates attributes or acknowledges creation
2774 e4a6c3b8 Olga Brani
    or deletion, the record will notify the store to update its
2775 e4a6c3b8 Olga Brani
    membership in any filters.
2776 e4a6c3b8 Olga Brani
    To avoid thrashing, this method is invoked only once per
2777 e4a6c3b8 Olga Brani

2778 e4a6c3b8 Olga Brani
    run loop per record.
2779 e4a6c3b8 Olga Brani

2780 e4a6c3b8 Olga Brani
    @method dataWasUpdated
2781 e4a6c3b8 Olga Brani
    @private
2782 e4a6c3b8 Olga Brani
    @param {Class} type
2783 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2784 e4a6c3b8 Olga Brani
  */
2785 e4a6c3b8 Olga Brani
  dataWasUpdated: function(type, record) {
2786 e4a6c3b8 Olga Brani
    this.recordArrayManager.recordDidChange(record);
2787 e4a6c3b8 Olga Brani
  },
2788 e4a6c3b8 Olga Brani
2789 e4a6c3b8 Olga Brani
  // ..............
2790 e4a6c3b8 Olga Brani
  // . PERSISTING .
2791 e4a6c3b8 Olga Brani
  // ..............
2792 e4a6c3b8 Olga Brani
2793 e4a6c3b8 Olga Brani
  /**
2794 e4a6c3b8 Olga Brani
    This method is called by `record.save`, and gets passed a
2795 e4a6c3b8 Olga Brani
    resolver for the promise that `record.save` returns.
2796 e4a6c3b8 Olga Brani

2797 e4a6c3b8 Olga Brani
    It schedules saving to happen at the end of the run loop.
2798 e4a6c3b8 Olga Brani

2799 e4a6c3b8 Olga Brani
    @method scheduleSave
2800 e4a6c3b8 Olga Brani
    @private
2801 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2802 e4a6c3b8 Olga Brani
    @param {Resolver} resolver
2803 e4a6c3b8 Olga Brani
  */
2804 e4a6c3b8 Olga Brani
  scheduleSave: function(record, resolver) {
2805 e4a6c3b8 Olga Brani
    record.adapterWillCommit();
2806 e4a6c3b8 Olga Brani
    this._pendingSave.push([record, resolver]);
2807 e4a6c3b8 Olga Brani
    once(this, 'flushPendingSave');
2808 e4a6c3b8 Olga Brani
  },
2809 e4a6c3b8 Olga Brani
2810 e4a6c3b8 Olga Brani
  /**
2811 e4a6c3b8 Olga Brani
    This method is called at the end of the run loop, and
2812 e4a6c3b8 Olga Brani
    flushes any records passed into `scheduleSave`
2813 e4a6c3b8 Olga Brani

2814 e4a6c3b8 Olga Brani
    @method flushPendingSave
2815 e4a6c3b8 Olga Brani
    @private
2816 e4a6c3b8 Olga Brani
  */
2817 e4a6c3b8 Olga Brani
  flushPendingSave: function() {
2818 e4a6c3b8 Olga Brani
    var pending = this._pendingSave.slice();
2819 e4a6c3b8 Olga Brani
    this._pendingSave = [];
2820 e4a6c3b8 Olga Brani
2821 e4a6c3b8 Olga Brani
    forEach(pending, function(tuple) {
2822 e4a6c3b8 Olga Brani
      var record = tuple[0], resolver = tuple[1],
2823 e4a6c3b8 Olga Brani
          adapter = this.adapterFor(record.constructor),
2824 e4a6c3b8 Olga Brani
          operation;
2825 e4a6c3b8 Olga Brani
2826 e4a6c3b8 Olga Brani
      if (get(record, 'isNew')) {
2827 e4a6c3b8 Olga Brani
        operation = 'createRecord';
2828 e4a6c3b8 Olga Brani
      } else if (get(record, 'isDeleted')) {
2829 e4a6c3b8 Olga Brani
        operation = 'deleteRecord';
2830 e4a6c3b8 Olga Brani
      } else {
2831 e4a6c3b8 Olga Brani
        operation = 'updateRecord';
2832 e4a6c3b8 Olga Brani
      }
2833 e4a6c3b8 Olga Brani
2834 e4a6c3b8 Olga Brani
      resolver.resolve(_commit(adapter, this, operation, record));
2835 e4a6c3b8 Olga Brani
    }, this);
2836 e4a6c3b8 Olga Brani
  },
2837 e4a6c3b8 Olga Brani
2838 e4a6c3b8 Olga Brani
  /**
2839 e4a6c3b8 Olga Brani
    This method is called once the promise returned by an
2840 e4a6c3b8 Olga Brani
    adapter's `createRecord`, `updateRecord` or `deleteRecord`
2841 e4a6c3b8 Olga Brani
    is resolved.
2842 e4a6c3b8 Olga Brani

2843 e4a6c3b8 Olga Brani
    If the data provides a server-generated ID, it will
2844 e4a6c3b8 Olga Brani
    update the record and the store's indexes.
2845 e4a6c3b8 Olga Brani

2846 e4a6c3b8 Olga Brani
    @method didSaveRecord
2847 e4a6c3b8 Olga Brani
    @private
2848 e4a6c3b8 Olga Brani
    @param {DS.Model} record the in-flight record
2849 e4a6c3b8 Olga Brani
    @param {Object} data optional data (see above)
2850 e4a6c3b8 Olga Brani
  */
2851 e4a6c3b8 Olga Brani
  didSaveRecord: function(record, data) {
2852 e4a6c3b8 Olga Brani
    if (data) {
2853 e4a6c3b8 Olga Brani
      // normalize relationship IDs into records
2854 e4a6c3b8 Olga Brani
      data = normalizeRelationships(this, record.constructor, data, record);
2855 e4a6c3b8 Olga Brani
2856 e4a6c3b8 Olga Brani
      this.updateId(record, data);
2857 e4a6c3b8 Olga Brani
    }
2858 e4a6c3b8 Olga Brani
2859 e4a6c3b8 Olga Brani
    record.adapterDidCommit(data);
2860 e4a6c3b8 Olga Brani
  },
2861 e4a6c3b8 Olga Brani
2862 e4a6c3b8 Olga Brani
  /**
2863 e4a6c3b8 Olga Brani
    This method is called once the promise returned by an
2864 e4a6c3b8 Olga Brani
    adapter's `createRecord`, `updateRecord` or `deleteRecord`
2865 e4a6c3b8 Olga Brani
    is rejected with a `DS.InvalidError`.
2866 e4a6c3b8 Olga Brani

2867 e4a6c3b8 Olga Brani
    @method recordWasInvalid
2868 e4a6c3b8 Olga Brani
    @private
2869 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2870 e4a6c3b8 Olga Brani
    @param {Object} errors
2871 e4a6c3b8 Olga Brani
  */
2872 e4a6c3b8 Olga Brani
  recordWasInvalid: function(record, errors) {
2873 e4a6c3b8 Olga Brani
    record.adapterDidInvalidate(errors);
2874 e4a6c3b8 Olga Brani
  },
2875 e4a6c3b8 Olga Brani
2876 e4a6c3b8 Olga Brani
  /**
2877 e4a6c3b8 Olga Brani
    This method is called once the promise returned by an
2878 e4a6c3b8 Olga Brani
    adapter's `createRecord`, `updateRecord` or `deleteRecord`
2879 e4a6c3b8 Olga Brani
    is rejected (with anything other than a `DS.InvalidError`).
2880 e4a6c3b8 Olga Brani

2881 e4a6c3b8 Olga Brani
    @method recordWasError
2882 e4a6c3b8 Olga Brani
    @private
2883 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2884 e4a6c3b8 Olga Brani
  */
2885 e4a6c3b8 Olga Brani
  recordWasError: function(record) {
2886 e4a6c3b8 Olga Brani
    record.adapterDidError();
2887 e4a6c3b8 Olga Brani
  },
2888 e4a6c3b8 Olga Brani
2889 e4a6c3b8 Olga Brani
  /**
2890 e4a6c3b8 Olga Brani
    When an adapter's `createRecord`, `updateRecord` or `deleteRecord`
2891 e4a6c3b8 Olga Brani
    resolves with data, this method extracts the ID from the supplied
2892 e4a6c3b8 Olga Brani
    data.
2893 e4a6c3b8 Olga Brani

2894 e4a6c3b8 Olga Brani
    @method updateId
2895 e4a6c3b8 Olga Brani
    @private
2896 e4a6c3b8 Olga Brani
    @param {DS.Model} record
2897 e4a6c3b8 Olga Brani
    @param {Object} data
2898 e4a6c3b8 Olga Brani
  */
2899 e4a6c3b8 Olga Brani
  updateId: function(record, data) {
2900 e4a6c3b8 Olga Brani
    var oldId = get(record, 'id'),
2901 e4a6c3b8 Olga Brani
        id = coerceId(data.id);
2902 e4a6c3b8 Olga Brani
2903 e4a6c3b8 Olga Brani
    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);
2904 e4a6c3b8 Olga Brani
2905 e4a6c3b8 Olga Brani
    this.typeMapFor(record.constructor).idToRecord[id] = record;
2906 e4a6c3b8 Olga Brani
2907 e4a6c3b8 Olga Brani
    set(record, 'id', id);
2908 e4a6c3b8 Olga Brani
  },
2909 e4a6c3b8 Olga Brani
2910 e4a6c3b8 Olga Brani
  /**
2911 e4a6c3b8 Olga Brani
    Returns a map of IDs to client IDs for a given type.
2912 e4a6c3b8 Olga Brani

2913 e4a6c3b8 Olga Brani
    @method typeMapFor
2914 e4a6c3b8 Olga Brani
    @private
2915 e4a6c3b8 Olga Brani
    @param type
2916 e4a6c3b8 Olga Brani
    @return {Object} typeMap
2917 e4a6c3b8 Olga Brani
  */
2918 e4a6c3b8 Olga Brani
  typeMapFor: function(type) {
2919 e4a6c3b8 Olga Brani
    var typeMaps = get(this, 'typeMaps'),
2920 e4a6c3b8 Olga Brani
        guid = Ember.guidFor(type),
2921 e4a6c3b8 Olga Brani
        typeMap;
2922 e4a6c3b8 Olga Brani
2923 e4a6c3b8 Olga Brani
    typeMap = typeMaps[guid];
2924 e4a6c3b8 Olga Brani
2925 e4a6c3b8 Olga Brani
    if (typeMap) { return typeMap; }
2926 e4a6c3b8 Olga Brani
2927 e4a6c3b8 Olga Brani
    typeMap = {
2928 e4a6c3b8 Olga Brani
      idToRecord: {},
2929 e4a6c3b8 Olga Brani
      records: [],
2930 e4a6c3b8 Olga Brani
      metadata: {}
2931 e4a6c3b8 Olga Brani
    };
2932 e4a6c3b8 Olga Brani
2933 e4a6c3b8 Olga Brani
    typeMaps[guid] = typeMap;
2934 e4a6c3b8 Olga Brani
2935 e4a6c3b8 Olga Brani
    return typeMap;
2936 e4a6c3b8 Olga Brani
  },
2937 e4a6c3b8 Olga Brani
2938 e4a6c3b8 Olga Brani
  // ................
2939 e4a6c3b8 Olga Brani
  // . LOADING DATA .
2940 e4a6c3b8 Olga Brani
  // ................
2941 e4a6c3b8 Olga Brani
2942 e4a6c3b8 Olga Brani
  /**
2943 e4a6c3b8 Olga Brani
    This internal method is used by `push`.
2944 e4a6c3b8 Olga Brani

2945 e4a6c3b8 Olga Brani
    @method _load
2946 e4a6c3b8 Olga Brani
    @private
2947 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
2948 e4a6c3b8 Olga Brani
    @param {Object} data
2949 e4a6c3b8 Olga Brani
    @param {Boolean} partial the data should be merged into
2950 e4a6c3b8 Olga Brani
      the existing data, not replace it.
2951 e4a6c3b8 Olga Brani
  */
2952 e4a6c3b8 Olga Brani
  _load: function(type, data, partial) {
2953 e4a6c3b8 Olga Brani
    var id = coerceId(data.id),
2954 e4a6c3b8 Olga Brani
        record = this.recordForId(type, id);
2955 e4a6c3b8 Olga Brani
2956 e4a6c3b8 Olga Brani
    record.setupData(data, partial);
2957 e4a6c3b8 Olga Brani
    this.recordArrayManager.recordDidChange(record);
2958 e4a6c3b8 Olga Brani
2959 e4a6c3b8 Olga Brani
    return record;
2960 e4a6c3b8 Olga Brani
  },
2961 e4a6c3b8 Olga Brani
2962 e4a6c3b8 Olga Brani
  /**
2963 e4a6c3b8 Olga Brani
    Returns a model class for a particular key. Used by
2964 e4a6c3b8 Olga Brani
    methods that take a type key (like `find`, `createRecord`,
2965 e4a6c3b8 Olga Brani
    etc.)
2966 e4a6c3b8 Olga Brani

2967 e4a6c3b8 Olga Brani
    @method modelFor
2968 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} key
2969 e4a6c3b8 Olga Brani
    @returns {subclass of DS.Model}
2970 e4a6c3b8 Olga Brani
  */
2971 e4a6c3b8 Olga Brani
  modelFor: function(key) {
2972 e4a6c3b8 Olga Brani
    var factory;
2973 e4a6c3b8 Olga Brani
2974 e4a6c3b8 Olga Brani
2975 e4a6c3b8 Olga Brani
    if (typeof key === 'string') {
2976 e4a6c3b8 Olga Brani
      var normalizedKey = this.container.normalize('model:' + key);
2977 e4a6c3b8 Olga Brani
2978 e4a6c3b8 Olga Brani
      factory = this.container.lookupFactory(normalizedKey);
2979 e4a6c3b8 Olga Brani
      if (!factory) { throw new Ember.Error("No model was found for '" + key + "'"); }
2980 e4a6c3b8 Olga Brani
      factory.typeKey = normalizedKey.split(':', 2)[1];
2981 e4a6c3b8 Olga Brani
    } else {
2982 e4a6c3b8 Olga Brani
      // A factory already supplied.
2983 e4a6c3b8 Olga Brani
      factory = key;
2984 e4a6c3b8 Olga Brani
    }
2985 e4a6c3b8 Olga Brani
2986 e4a6c3b8 Olga Brani
    factory.store = this;
2987 e4a6c3b8 Olga Brani
    return factory;
2988 e4a6c3b8 Olga Brani
  },
2989 e4a6c3b8 Olga Brani
2990 e4a6c3b8 Olga Brani
  /**
2991 e4a6c3b8 Olga Brani
    Push some data for a given type into the store.
2992 e4a6c3b8 Olga Brani

2993 e4a6c3b8 Olga Brani
    This method expects normalized data:
2994 e4a6c3b8 Olga Brani

2995 e4a6c3b8 Olga Brani
    * The ID is a key named `id` (an ID is mandatory)
2996 e4a6c3b8 Olga Brani
    * The names of attributes are the ones you used in
2997 e4a6c3b8 Olga Brani
      your model's `DS.attr`s.
2998 e4a6c3b8 Olga Brani
    * Your relationships must be:
2999 e4a6c3b8 Olga Brani
      * represented as IDs or Arrays of IDs
3000 e4a6c3b8 Olga Brani
      * represented as model instances
3001 e4a6c3b8 Olga Brani
      * represented as URLs, under the `links` key
3002 e4a6c3b8 Olga Brani

3003 e4a6c3b8 Olga Brani
    For this model:
3004 e4a6c3b8 Olga Brani

3005 e4a6c3b8 Olga Brani
    ```js
3006 e4a6c3b8 Olga Brani
    App.Person = DS.Model.extend({
3007 e4a6c3b8 Olga Brani
      firstName: DS.attr(),
3008 e4a6c3b8 Olga Brani
      lastName: DS.attr(),
3009 e4a6c3b8 Olga Brani

3010 e4a6c3b8 Olga Brani
      children: DS.hasMany('person')
3011 e4a6c3b8 Olga Brani
    });
3012 e4a6c3b8 Olga Brani
    ```
3013 e4a6c3b8 Olga Brani

3014 e4a6c3b8 Olga Brani
    To represent the children as IDs:
3015 e4a6c3b8 Olga Brani

3016 e4a6c3b8 Olga Brani
    ```js
3017 e4a6c3b8 Olga Brani
    {
3018 e4a6c3b8 Olga Brani
      id: 1,
3019 e4a6c3b8 Olga Brani
      firstName: "Tom",
3020 e4a6c3b8 Olga Brani
      lastName: "Dale",
3021 e4a6c3b8 Olga Brani
      children: [1, 2, 3]
3022 e4a6c3b8 Olga Brani
    }
3023 e4a6c3b8 Olga Brani
    ```
3024 e4a6c3b8 Olga Brani

3025 e4a6c3b8 Olga Brani
    To represent the children relationship as a URL:
3026 e4a6c3b8 Olga Brani

3027 e4a6c3b8 Olga Brani
    ```js
3028 e4a6c3b8 Olga Brani
    {
3029 e4a6c3b8 Olga Brani
      id: 1,
3030 e4a6c3b8 Olga Brani
      firstName: "Tom",
3031 e4a6c3b8 Olga Brani
      lastName: "Dale",
3032 e4a6c3b8 Olga Brani
      links: {
3033 e4a6c3b8 Olga Brani
        children: "/people/1/children"
3034 e4a6c3b8 Olga Brani
      }
3035 e4a6c3b8 Olga Brani
    }
3036 e4a6c3b8 Olga Brani
    ```
3037 e4a6c3b8 Olga Brani

3038 e4a6c3b8 Olga Brani
    If you're streaming data or implementing an adapter,
3039 e4a6c3b8 Olga Brani
    make sure that you have converted the incoming data
3040 e4a6c3b8 Olga Brani
    into this form.
3041 e4a6c3b8 Olga Brani

3042 e4a6c3b8 Olga Brani
    This method can be used both to push in brand new
3043 e4a6c3b8 Olga Brani
    records, as well as to update existing records.
3044 e4a6c3b8 Olga Brani

3045 e4a6c3b8 Olga Brani
    @method push
3046 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
3047 e4a6c3b8 Olga Brani
    @param {Object} data
3048 e4a6c3b8 Olga Brani
    @returns {DS.Model} the record that was created or
3049 e4a6c3b8 Olga Brani
      updated.
3050 e4a6c3b8 Olga Brani
  */
3051 e4a6c3b8 Olga Brani
  push: function(type, data, _partial) {
3052 e4a6c3b8 Olga Brani
    // _partial is an internal param used by `update`.
3053 e4a6c3b8 Olga Brani
    // If passed, it means that the data should be
3054 e4a6c3b8 Olga Brani
    // merged into the existing data, not replace it.
3055 e4a6c3b8 Olga Brani
3056 e4a6c3b8 Olga Brani
    Ember.assert("You must include an `id` in a hash passed to `push`", data.id != null);
3057 e4a6c3b8 Olga Brani
3058 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
3059 e4a6c3b8 Olga Brani
3060 e4a6c3b8 Olga Brani
    // normalize relationship IDs into records
3061 e4a6c3b8 Olga Brani
    data = normalizeRelationships(this, type, data);
3062 e4a6c3b8 Olga Brani
3063 e4a6c3b8 Olga Brani
    this._load(type, data, _partial);
3064 e4a6c3b8 Olga Brani
3065 e4a6c3b8 Olga Brani
    return this.recordForId(type, data.id);
3066 e4a6c3b8 Olga Brani
  },
3067 e4a6c3b8 Olga Brani
3068 e4a6c3b8 Olga Brani
  /**
3069 e4a6c3b8 Olga Brani
    Push some raw data into the store.
3070 e4a6c3b8 Olga Brani

3071 e4a6c3b8 Olga Brani
    The data will be automatically deserialized using the
3072 e4a6c3b8 Olga Brani
    serializer for the `type` param.
3073 e4a6c3b8 Olga Brani

3074 e4a6c3b8 Olga Brani
    This method can be used both to push in brand new
3075 e4a6c3b8 Olga Brani
    records, as well as to update existing records.
3076 e4a6c3b8 Olga Brani

3077 e4a6c3b8 Olga Brani
    You can push in more than one type of object at once.
3078 e4a6c3b8 Olga Brani
    All objects should be in the format expected by the
3079 e4a6c3b8 Olga Brani
    serializer.
3080 e4a6c3b8 Olga Brani

3081 e4a6c3b8 Olga Brani
    ```js
3082 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.ActiveModelSerializer;
3083 e4a6c3b8 Olga Brani

3084 e4a6c3b8 Olga Brani
    var pushData = {
3085 e4a6c3b8 Olga Brani
      posts: [
3086 e4a6c3b8 Olga Brani
        {id: 1, post_title: "Great post", comment_ids: [2]}
3087 e4a6c3b8 Olga Brani
      ],
3088 e4a6c3b8 Olga Brani
      comments: [
3089 e4a6c3b8 Olga Brani
        {id: 2, comment_body: "Insightful comment"}
3090 e4a6c3b8 Olga Brani
      ]
3091 e4a6c3b8 Olga Brani
    }
3092 e4a6c3b8 Olga Brani

3093 e4a6c3b8 Olga Brani
    store.pushPayload('post', pushData);
3094 e4a6c3b8 Olga Brani
    ```
3095 e4a6c3b8 Olga Brani

3096 e4a6c3b8 Olga Brani
    @method pushPayload
3097 e4a6c3b8 Olga Brani
    @param {String} type
3098 e4a6c3b8 Olga Brani
    @param {Object} payload
3099 e4a6c3b8 Olga Brani
    @return {DS.Model} the record that was created or updated.
3100 e4a6c3b8 Olga Brani
  */
3101 e4a6c3b8 Olga Brani
  pushPayload: function (type, payload) {
3102 e4a6c3b8 Olga Brani
    var serializer;
3103 e4a6c3b8 Olga Brani
    if (!payload) {
3104 e4a6c3b8 Olga Brani
      payload = type;
3105 e4a6c3b8 Olga Brani
      serializer = defaultSerializer(this.container);
3106 e4a6c3b8 Olga Brani
      Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", serializer.pushPayload);
3107 e4a6c3b8 Olga Brani
    } else {
3108 e4a6c3b8 Olga Brani
      serializer = this.serializerFor(type);
3109 e4a6c3b8 Olga Brani
    }
3110 e4a6c3b8 Olga Brani
    serializer.pushPayload(this, payload);
3111 e4a6c3b8 Olga Brani
  },
3112 e4a6c3b8 Olga Brani
3113 e4a6c3b8 Olga Brani
  update: function(type, data) {
3114 e4a6c3b8 Olga Brani
    Ember.assert("You must include an `id` in a hash passed to `update`", data.id != null);
3115 e4a6c3b8 Olga Brani
3116 e4a6c3b8 Olga Brani
    return this.push(type, data, true);
3117 e4a6c3b8 Olga Brani
  },
3118 e4a6c3b8 Olga Brani
3119 e4a6c3b8 Olga Brani
  /**
3120 e4a6c3b8 Olga Brani
    If you have an Array of normalized data to push,
3121 e4a6c3b8 Olga Brani
    you can call `pushMany` with the Array, and it will
3122 e4a6c3b8 Olga Brani
    call `push` repeatedly for you.
3123 e4a6c3b8 Olga Brani

3124 e4a6c3b8 Olga Brani
    @method pushMany
3125 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
3126 e4a6c3b8 Olga Brani
    @param {Array} datas
3127 e4a6c3b8 Olga Brani
    @return {Array}
3128 e4a6c3b8 Olga Brani
  */
3129 e4a6c3b8 Olga Brani
  pushMany: function(type, datas) {
3130 e4a6c3b8 Olga Brani
    return map(datas, function(data) {
3131 e4a6c3b8 Olga Brani
      return this.push(type, data);
3132 e4a6c3b8 Olga Brani
    }, this);
3133 e4a6c3b8 Olga Brani
  },
3134 e4a6c3b8 Olga Brani
3135 e4a6c3b8 Olga Brani
  /**
3136 e4a6c3b8 Olga Brani
    If you have some metadata to set for a type
3137 e4a6c3b8 Olga Brani
    you can call `metaForType`.
3138 e4a6c3b8 Olga Brani

3139 e4a6c3b8 Olga Brani
    @method metaForType
3140 e4a6c3b8 Olga Brani
    @param {String or subclass of DS.Model} type
3141 e4a6c3b8 Olga Brani
    @param {Object} metadata
3142 e4a6c3b8 Olga Brani
  */
3143 e4a6c3b8 Olga Brani
  metaForType: function(type, metadata) {
3144 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
3145 e4a6c3b8 Olga Brani
3146 e4a6c3b8 Olga Brani
    Ember.merge(this.typeMapFor(type).metadata, metadata);
3147 e4a6c3b8 Olga Brani
  },
3148 e4a6c3b8 Olga Brani
3149 e4a6c3b8 Olga Brani
  /**
3150 e4a6c3b8 Olga Brani
    Build a brand new record for a given type, ID, and
3151 e4a6c3b8 Olga Brani
    initial data.
3152 e4a6c3b8 Olga Brani

3153 e4a6c3b8 Olga Brani
    @method buildRecord
3154 e4a6c3b8 Olga Brani
    @private
3155 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
3156 e4a6c3b8 Olga Brani
    @param {String} id
3157 e4a6c3b8 Olga Brani
    @param {Object} data
3158 e4a6c3b8 Olga Brani
    @returns {DS.Model} record
3159 e4a6c3b8 Olga Brani
  */
3160 e4a6c3b8 Olga Brani
  buildRecord: function(type, id, data) {
3161 e4a6c3b8 Olga Brani
    var typeMap = this.typeMapFor(type),
3162 e4a6c3b8 Olga Brani
        idToRecord = typeMap.idToRecord;
3163 e4a6c3b8 Olga Brani
3164 e4a6c3b8 Olga Brani
    Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]);
3165 e4a6c3b8 Olga Brani
3166 e4a6c3b8 Olga Brani
    // lookupFactory should really return an object that creates
3167 e4a6c3b8 Olga Brani
    // instances with the injections applied
3168 e4a6c3b8 Olga Brani
    var record = type._create({
3169 e4a6c3b8 Olga Brani
      id: id,
3170 e4a6c3b8 Olga Brani
      store: this,
3171 e4a6c3b8 Olga Brani
      container: this.container
3172 e4a6c3b8 Olga Brani
    });
3173 e4a6c3b8 Olga Brani
3174 e4a6c3b8 Olga Brani
    if (data) {
3175 e4a6c3b8 Olga Brani
      record.setupData(data);
3176 e4a6c3b8 Olga Brani
    }
3177 e4a6c3b8 Olga Brani
3178 e4a6c3b8 Olga Brani
    // if we're creating an item, this process will be done
3179 e4a6c3b8 Olga Brani
    // later, once the object has been persisted.
3180 e4a6c3b8 Olga Brani
    if (id) {
3181 e4a6c3b8 Olga Brani
      idToRecord[id] = record;
3182 e4a6c3b8 Olga Brani
    }
3183 e4a6c3b8 Olga Brani
3184 e4a6c3b8 Olga Brani
    typeMap.records.push(record);
3185 e4a6c3b8 Olga Brani
3186 e4a6c3b8 Olga Brani
    return record;
3187 e4a6c3b8 Olga Brani
  },
3188 e4a6c3b8 Olga Brani
3189 e4a6c3b8 Olga Brani
  // ...............
3190 e4a6c3b8 Olga Brani
  // . DESTRUCTION .
3191 e4a6c3b8 Olga Brani
  // ...............
3192 e4a6c3b8 Olga Brani
3193 e4a6c3b8 Olga Brani
  /**
3194 e4a6c3b8 Olga Brani
    When a record is destroyed, this un-indexes it and
3195 e4a6c3b8 Olga Brani
    removes it from any record arrays so it can be GCed.
3196 e4a6c3b8 Olga Brani

3197 e4a6c3b8 Olga Brani
    @method dematerializeRecord
3198 e4a6c3b8 Olga Brani
    @private
3199 e4a6c3b8 Olga Brani
    @param {DS.Model} record
3200 e4a6c3b8 Olga Brani
  */
3201 e4a6c3b8 Olga Brani
  dematerializeRecord: function(record) {
3202 e4a6c3b8 Olga Brani
    var type = record.constructor,
3203 e4a6c3b8 Olga Brani
        typeMap = this.typeMapFor(type),
3204 e4a6c3b8 Olga Brani
        id = get(record, 'id');
3205 e4a6c3b8 Olga Brani
3206 e4a6c3b8 Olga Brani
    record.updateRecordArrays();
3207 e4a6c3b8 Olga Brani
3208 e4a6c3b8 Olga Brani
    if (id) {
3209 e4a6c3b8 Olga Brani
      delete typeMap.idToRecord[id];
3210 e4a6c3b8 Olga Brani
    }
3211 e4a6c3b8 Olga Brani
3212 e4a6c3b8 Olga Brani
    var loc = indexOf(typeMap.records, record);
3213 e4a6c3b8 Olga Brani
    typeMap.records.splice(loc, 1);
3214 e4a6c3b8 Olga Brani
  },
3215 e4a6c3b8 Olga Brani
3216 e4a6c3b8 Olga Brani
  // ........................
3217 e4a6c3b8 Olga Brani
  // . RELATIONSHIP CHANGES .
3218 e4a6c3b8 Olga Brani
  // ........................
3219 e4a6c3b8 Olga Brani
3220 e4a6c3b8 Olga Brani
  addRelationshipChangeFor: function(childRecord, childKey, parentRecord, parentKey, change) {
3221 e4a6c3b8 Olga Brani
    var clientId = childRecord.clientId,
3222 e4a6c3b8 Olga Brani
        parentClientId = parentRecord ? parentRecord : parentRecord;
3223 e4a6c3b8 Olga Brani
    var key = childKey + parentKey;
3224 e4a6c3b8 Olga Brani
    var changes = this._relationshipChanges;
3225 e4a6c3b8 Olga Brani
    if (!(clientId in changes)) {
3226 e4a6c3b8 Olga Brani
      changes[clientId] = {};
3227 e4a6c3b8 Olga Brani
    }
3228 e4a6c3b8 Olga Brani
    if (!(parentClientId in changes[clientId])) {
3229 e4a6c3b8 Olga Brani
      changes[clientId][parentClientId] = {};
3230 e4a6c3b8 Olga Brani
    }
3231 e4a6c3b8 Olga Brani
    if (!(key in changes[clientId][parentClientId])) {
3232 e4a6c3b8 Olga Brani
      changes[clientId][parentClientId][key] = {};
3233 e4a6c3b8 Olga Brani
    }
3234 e4a6c3b8 Olga Brani
    changes[clientId][parentClientId][key][change.changeType] = change;
3235 e4a6c3b8 Olga Brani
  },
3236 e4a6c3b8 Olga Brani
3237 e4a6c3b8 Olga Brani
  removeRelationshipChangeFor: function(clientRecord, childKey, parentRecord, parentKey, type) {
3238 e4a6c3b8 Olga Brani
    var clientId = clientRecord.clientId,
3239 e4a6c3b8 Olga Brani
        parentClientId = parentRecord ? parentRecord.clientId : parentRecord;
3240 e4a6c3b8 Olga Brani
    var changes = this._relationshipChanges;
3241 e4a6c3b8 Olga Brani
    var key = childKey + parentKey;
3242 e4a6c3b8 Olga Brani
    if (!(clientId in changes) || !(parentClientId in changes[clientId]) || !(key in changes[clientId][parentClientId])){
3243 e4a6c3b8 Olga Brani
      return;
3244 e4a6c3b8 Olga Brani
    }
3245 e4a6c3b8 Olga Brani
    delete changes[clientId][parentClientId][key][type];
3246 e4a6c3b8 Olga Brani
  },
3247 e4a6c3b8 Olga Brani
3248 e4a6c3b8 Olga Brani
  relationshipChangePairsFor: function(record){
3249 e4a6c3b8 Olga Brani
    var toReturn = [];
3250 e4a6c3b8 Olga Brani
3251 e4a6c3b8 Olga Brani
    if( !record ) { return toReturn; }
3252 e4a6c3b8 Olga Brani
3253 e4a6c3b8 Olga Brani
    //TODO(Igor) What about the other side
3254 e4a6c3b8 Olga Brani
    var changesObject = this._relationshipChanges[record.clientId];
3255 e4a6c3b8 Olga Brani
    for (var objKey in changesObject){
3256 e4a6c3b8 Olga Brani
      if(changesObject.hasOwnProperty(objKey)){
3257 e4a6c3b8 Olga Brani
        for (var changeKey in changesObject[objKey]){
3258 e4a6c3b8 Olga Brani
          if(changesObject[objKey].hasOwnProperty(changeKey)){
3259 e4a6c3b8 Olga Brani
            toReturn.push(changesObject[objKey][changeKey]);
3260 e4a6c3b8 Olga Brani
          }
3261 e4a6c3b8 Olga Brani
        }
3262 e4a6c3b8 Olga Brani
      }
3263 e4a6c3b8 Olga Brani
    }
3264 e4a6c3b8 Olga Brani
    return toReturn;
3265 e4a6c3b8 Olga Brani
  },
3266 e4a6c3b8 Olga Brani
3267 e4a6c3b8 Olga Brani
  // ......................
3268 e4a6c3b8 Olga Brani
  // . PER-TYPE ADAPTERS
3269 e4a6c3b8 Olga Brani
  // ......................
3270 e4a6c3b8 Olga Brani
3271 e4a6c3b8 Olga Brani
  /**
3272 e4a6c3b8 Olga Brani
    Returns the adapter for a given type.
3273 e4a6c3b8 Olga Brani

3274 e4a6c3b8 Olga Brani
    @method adapterFor
3275 e4a6c3b8 Olga Brani
    @private
3276 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
3277 e4a6c3b8 Olga Brani
    @returns DS.Adapter
3278 e4a6c3b8 Olga Brani
  */
3279 e4a6c3b8 Olga Brani
  adapterFor: function(type) {
3280 e4a6c3b8 Olga Brani
    var container = this.container, adapter;
3281 e4a6c3b8 Olga Brani
3282 e4a6c3b8 Olga Brani
    if (container) {
3283 e4a6c3b8 Olga Brani
      adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application');
3284 e4a6c3b8 Olga Brani
    }
3285 e4a6c3b8 Olga Brani
3286 e4a6c3b8 Olga Brani
    return adapter || get(this, 'defaultAdapter');
3287 e4a6c3b8 Olga Brani
  },
3288 e4a6c3b8 Olga Brani
3289 e4a6c3b8 Olga Brani
  // ..............................
3290 e4a6c3b8 Olga Brani
  // . RECORD CHANGE NOTIFICATION .
3291 e4a6c3b8 Olga Brani
  // ..............................
3292 e4a6c3b8 Olga Brani
3293 e4a6c3b8 Olga Brani
  /**
3294 e4a6c3b8 Olga Brani
    Returns an instance of the serializer for a given type. For
3295 e4a6c3b8 Olga Brani
    example, `serializerFor('person')` will return an instance of
3296 e4a6c3b8 Olga Brani
    `App.PersonSerializer`.
3297 e4a6c3b8 Olga Brani

3298 e4a6c3b8 Olga Brani
    If no `App.PersonSerializer` is found, this method will look
3299 e4a6c3b8 Olga Brani
    for an `App.ApplicationSerializer` (the default serializer for
3300 e4a6c3b8 Olga Brani
    your entire application).
3301 e4a6c3b8 Olga Brani

3302 e4a6c3b8 Olga Brani
    If no `App.ApplicationSerializer` is found, it will fall back
3303 e4a6c3b8 Olga Brani
    to an instance of `DS.JSONSerializer`.
3304 e4a6c3b8 Olga Brani

3305 e4a6c3b8 Olga Brani
    @method serializerFor
3306 e4a6c3b8 Olga Brani
    @private
3307 e4a6c3b8 Olga Brani
    @param {String} type the record to serialize
3308 e4a6c3b8 Olga Brani
    @return {DS.Serializer}
3309 e4a6c3b8 Olga Brani
  */
3310 e4a6c3b8 Olga Brani
  serializerFor: function(type) {
3311 e4a6c3b8 Olga Brani
    type = this.modelFor(type);
3312 e4a6c3b8 Olga Brani
    var adapter = this.adapterFor(type);
3313 e4a6c3b8 Olga Brani
3314 e4a6c3b8 Olga Brani
    return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer);
3315 e4a6c3b8 Olga Brani
  }
3316 e4a6c3b8 Olga Brani
});
3317 e4a6c3b8 Olga Brani
3318 e4a6c3b8 Olga Brani
function normalizeRelationships(store, type, data, record) {
3319 e4a6c3b8 Olga Brani
  type.eachRelationship(function(key, relationship) {
3320 e4a6c3b8 Olga Brani
    // A link (usually a URL) was already provided in
3321 e4a6c3b8 Olga Brani
    // normalized form
3322 e4a6c3b8 Olga Brani
    if (data.links && data.links[key]) {
3323 e4a6c3b8 Olga Brani
      if (record && relationship.options.async) { record._relationships[key] = null; }
3324 e4a6c3b8 Olga Brani
      return;
3325 e4a6c3b8 Olga Brani
    }
3326 e4a6c3b8 Olga Brani
3327 e4a6c3b8 Olga Brani
    var kind = relationship.kind,
3328 e4a6c3b8 Olga Brani
        value = data[key];
3329 e4a6c3b8 Olga Brani
3330 e4a6c3b8 Olga Brani
    if (value == null) { return; }
3331 e4a6c3b8 Olga Brani
3332 e4a6c3b8 Olga Brani
    if (kind === 'belongsTo') {
3333 e4a6c3b8 Olga Brani
      deserializeRecordId(store, data, key, relationship, value);
3334 e4a6c3b8 Olga Brani
    } else if (kind === 'hasMany') {
3335 e4a6c3b8 Olga Brani
      deserializeRecordIds(store, data, key, relationship, value);
3336 e4a6c3b8 Olga Brani
      addUnsavedRecords(record, key, value);
3337 e4a6c3b8 Olga Brani
    }
3338 e4a6c3b8 Olga Brani
  });
3339 e4a6c3b8 Olga Brani
3340 e4a6c3b8 Olga Brani
  return data;
3341 e4a6c3b8 Olga Brani
}
3342 e4a6c3b8 Olga Brani
3343 e4a6c3b8 Olga Brani
function deserializeRecordId(store, data, key, relationship, id) {
3344 e4a6c3b8 Olga Brani
  if (isNone(id) || id instanceof DS.Model) {
3345 e4a6c3b8 Olga Brani
    return;
3346 e4a6c3b8 Olga Brani
  }
3347 e4a6c3b8 Olga Brani
3348 e4a6c3b8 Olga Brani
  var type;
3349 e4a6c3b8 Olga Brani
3350 e4a6c3b8 Olga Brani
  if (typeof id === 'number' || typeof id === 'string') {
3351 e4a6c3b8 Olga Brani
    type = typeFor(relationship, key, data);
3352 e4a6c3b8 Olga Brani
    data[key] = store.recordForId(type, id);
3353 e4a6c3b8 Olga Brani
  } else if (typeof id === 'object') {
3354 e4a6c3b8 Olga Brani
    // polymorphic
3355 e4a6c3b8 Olga Brani
    data[key] = store.recordForId(id.type, id.id);
3356 e4a6c3b8 Olga Brani
  }
3357 e4a6c3b8 Olga Brani
}
3358 e4a6c3b8 Olga Brani
3359 e4a6c3b8 Olga Brani
function typeFor(relationship, key, data) {
3360 e4a6c3b8 Olga Brani
  if (relationship.options.polymorphic) {
3361 e4a6c3b8 Olga Brani
    return data[key + "Type"];
3362 e4a6c3b8 Olga Brani
  } else {
3363 e4a6c3b8 Olga Brani
    return relationship.type;
3364 e4a6c3b8 Olga Brani
  }
3365 e4a6c3b8 Olga Brani
}
3366 e4a6c3b8 Olga Brani
3367 e4a6c3b8 Olga Brani
function deserializeRecordIds(store, data, key, relationship, ids) {
3368 e4a6c3b8 Olga Brani
  for (var i=0, l=ids.length; i<l; i++) {
3369 e4a6c3b8 Olga Brani
    deserializeRecordId(store, ids, i, relationship, ids[i]);
3370 e4a6c3b8 Olga Brani
  }
3371 e4a6c3b8 Olga Brani
}
3372 e4a6c3b8 Olga Brani
3373 e4a6c3b8 Olga Brani
// If there are any unsaved records that are in a hasMany they won't be
3374 e4a6c3b8 Olga Brani
// in the payload, so add them back in manually.
3375 e4a6c3b8 Olga Brani
function addUnsavedRecords(record, key, data) {
3376 e4a6c3b8 Olga Brani
  if(record) {
3377 e4a6c3b8 Olga Brani
    data.pushObjects(record.get(key).filterBy('isNew'));
3378 e4a6c3b8 Olga Brani
  }
3379 e4a6c3b8 Olga Brani
}
3380 e4a6c3b8 Olga Brani
3381 e4a6c3b8 Olga Brani
// Delegation to the adapter and promise management
3382 e4a6c3b8 Olga Brani
/**
3383 e4a6c3b8 Olga Brani
  A `PromiseArray` is an object that acts like both an `Ember.Array`
3384 e4a6c3b8 Olga Brani
  and a promise. When the promise is resolved the the resulting value
3385 e4a6c3b8 Olga Brani
  will be set to the `PromiseArray`'s `content` property. This makes
3386 e4a6c3b8 Olga Brani
  it easy to create data bindings with the `PromiseArray` that will be
3387 e4a6c3b8 Olga Brani
  updated when the promise resolves.
3388 e4a6c3b8 Olga Brani

3389 e4a6c3b8 Olga Brani
  For more information see the [Ember.PromiseProxyMixin
3390 e4a6c3b8 Olga Brani
  documentation](/api/classes/Ember.PromiseProxyMixin.html).
3391 e4a6c3b8 Olga Brani

3392 e4a6c3b8 Olga Brani
  Example
3393 e4a6c3b8 Olga Brani

3394 e4a6c3b8 Olga Brani
  ```javascript
3395 e4a6c3b8 Olga Brani
  var promiseArray = DS.PromiseArray.create({
3396 e4a6c3b8 Olga Brani
    promise: $.getJSON('/some/remote/data.json')
3397 e4a6c3b8 Olga Brani
  });
3398 e4a6c3b8 Olga Brani

3399 e4a6c3b8 Olga Brani
  promiseArray.get('length'); // 0
3400 e4a6c3b8 Olga Brani

3401 e4a6c3b8 Olga Brani
  promiseArray.then(function() {
3402 e4a6c3b8 Olga Brani
    promiseArray.get('length'); // 100
3403 e4a6c3b8 Olga Brani
  });
3404 e4a6c3b8 Olga Brani
  ```
3405 e4a6c3b8 Olga Brani

3406 e4a6c3b8 Olga Brani
  @class PromiseArray
3407 e4a6c3b8 Olga Brani
  @namespace DS
3408 e4a6c3b8 Olga Brani
  @extends Ember.ArrayProxy
3409 e4a6c3b8 Olga Brani
  @uses Ember.PromiseProxyMixin
3410 e4a6c3b8 Olga Brani
*/
3411 e4a6c3b8 Olga Brani
DS.PromiseArray = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin);
3412 e4a6c3b8 Olga Brani
/**
3413 e4a6c3b8 Olga Brani
  A `PromiseObject` is an object that acts like both an `Ember.Object`
3414 e4a6c3b8 Olga Brani
  and a promise. When the promise is resolved the the resulting value
3415 e4a6c3b8 Olga Brani
  will be set to the `PromiseObject`'s `content` property. This makes
3416 e4a6c3b8 Olga Brani
  it easy to create data bindings with the `PromiseObject` that will
3417 e4a6c3b8 Olga Brani
  be updated when the promise resolves.
3418 e4a6c3b8 Olga Brani

3419 e4a6c3b8 Olga Brani
  For more information see the [Ember.PromiseProxyMixin
3420 e4a6c3b8 Olga Brani
  documentation](/api/classes/Ember.PromiseProxyMixin.html).
3421 e4a6c3b8 Olga Brani

3422 e4a6c3b8 Olga Brani
  Example
3423 e4a6c3b8 Olga Brani

3424 e4a6c3b8 Olga Brani
  ```javascript
3425 e4a6c3b8 Olga Brani
  var promiseObject = DS.PromiseObject.create({
3426 e4a6c3b8 Olga Brani
    promise: $.getJSON('/some/remote/data.json')
3427 e4a6c3b8 Olga Brani
  });
3428 e4a6c3b8 Olga Brani

3429 e4a6c3b8 Olga Brani
  promiseObject.get('name'); // null
3430 e4a6c3b8 Olga Brani

3431 e4a6c3b8 Olga Brani
  promiseObject.then(function() {
3432 e4a6c3b8 Olga Brani
    promiseObject.get('name'); // 'Tomster'
3433 e4a6c3b8 Olga Brani
  });
3434 e4a6c3b8 Olga Brani
  ```
3435 e4a6c3b8 Olga Brani

3436 e4a6c3b8 Olga Brani
  @class PromiseObject
3437 e4a6c3b8 Olga Brani
  @namespace DS
3438 e4a6c3b8 Olga Brani
  @extends Ember.ObjectProxy
3439 e4a6c3b8 Olga Brani
  @uses Ember.PromiseProxyMixin
3440 e4a6c3b8 Olga Brani
*/
3441 e4a6c3b8 Olga Brani
DS.PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin);
3442 e4a6c3b8 Olga Brani
3443 e4a6c3b8 Olga Brani
function promiseObject(promise) {
3444 e4a6c3b8 Olga Brani
  return DS.PromiseObject.create({ promise: promise });
3445 e4a6c3b8 Olga Brani
}
3446 e4a6c3b8 Olga Brani
3447 e4a6c3b8 Olga Brani
function promiseArray(promise) {
3448 e4a6c3b8 Olga Brani
  return DS.PromiseArray.create({ promise: promise });
3449 e4a6c3b8 Olga Brani
}
3450 e4a6c3b8 Olga Brani
3451 e4a6c3b8 Olga Brani
function isThenable(object) {
3452 e4a6c3b8 Olga Brani
  return object && typeof object.then === 'function';
3453 e4a6c3b8 Olga Brani
}
3454 e4a6c3b8 Olga Brani
3455 e4a6c3b8 Olga Brani
function serializerFor(container, type, defaultSerializer) {
3456 e4a6c3b8 Olga Brani
  return container.lookup('serializer:'+type) ||
3457 e4a6c3b8 Olga Brani
                 container.lookup('serializer:application') ||
3458 e4a6c3b8 Olga Brani
                 container.lookup('serializer:' + defaultSerializer) ||
3459 e4a6c3b8 Olga Brani
                 container.lookup('serializer:_default');
3460 e4a6c3b8 Olga Brani
}
3461 e4a6c3b8 Olga Brani
3462 e4a6c3b8 Olga Brani
function defaultSerializer(container) {
3463 e4a6c3b8 Olga Brani
  return container.lookup('serializer:application') ||
3464 e4a6c3b8 Olga Brani
         container.lookup('serializer:_default');
3465 e4a6c3b8 Olga Brani
}
3466 e4a6c3b8 Olga Brani
3467 e4a6c3b8 Olga Brani
function serializerForAdapter(adapter, type) {
3468 e4a6c3b8 Olga Brani
  var serializer = adapter.serializer,
3469 e4a6c3b8 Olga Brani
      defaultSerializer = adapter.defaultSerializer,
3470 e4a6c3b8 Olga Brani
      container = adapter.container;
3471 e4a6c3b8 Olga Brani
3472 e4a6c3b8 Olga Brani
  if (container && serializer === undefined) {
3473 e4a6c3b8 Olga Brani
    serializer = serializerFor(container, type.typeKey, defaultSerializer);
3474 e4a6c3b8 Olga Brani
  }
3475 e4a6c3b8 Olga Brani
3476 e4a6c3b8 Olga Brani
  if (serializer === null || serializer === undefined) {
3477 e4a6c3b8 Olga Brani
    serializer = {
3478 e4a6c3b8 Olga Brani
      extract: function(store, type, payload) { return payload; }
3479 e4a6c3b8 Olga Brani
    };
3480 e4a6c3b8 Olga Brani
  }
3481 e4a6c3b8 Olga Brani
3482 e4a6c3b8 Olga Brani
  return serializer;
3483 e4a6c3b8 Olga Brani
}
3484 e4a6c3b8 Olga Brani
3485 e4a6c3b8 Olga Brani
function _find(adapter, store, type, id) {
3486 e4a6c3b8 Olga Brani
  var promise = adapter.find(store, type, id),
3487 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, type);
3488 e4a6c3b8 Olga Brani
3489 e4a6c3b8 Olga Brani
  return resolve(promise, "DS: Handle Adapter#find of " + type + " with id: " + id).then(function(payload) {
3490 e4a6c3b8 Olga Brani
    Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", payload);
3491 e4a6c3b8 Olga Brani
    payload = serializer.extract(store, type, payload, id, 'find');
3492 e4a6c3b8 Olga Brani
3493 e4a6c3b8 Olga Brani
    return store.push(type, payload);
3494 e4a6c3b8 Olga Brani
  }, function(error) {
3495 e4a6c3b8 Olga Brani
    var record = store.getById(type, id);
3496 e4a6c3b8 Olga Brani
    record.notFound();
3497 e4a6c3b8 Olga Brani
    throw error;
3498 e4a6c3b8 Olga Brani
  }, "DS: Extract payload of '" + type + "'");
3499 e4a6c3b8 Olga Brani
}
3500 e4a6c3b8 Olga Brani
3501 e4a6c3b8 Olga Brani
function _findMany(adapter, store, type, ids, owner) {
3502 e4a6c3b8 Olga Brani
  var promise = adapter.findMany(store, type, ids, owner),
3503 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, type);
3504 e4a6c3b8 Olga Brani
3505 e4a6c3b8 Olga Brani
  return resolve(promise, "DS: Handle Adapter#findMany of " + type).then(function(payload) {
3506 e4a6c3b8 Olga Brani
    payload = serializer.extract(store, type, payload, null, 'findMany');
3507 e4a6c3b8 Olga Brani
3508 e4a6c3b8 Olga Brani
    Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3509 e4a6c3b8 Olga Brani
3510 e4a6c3b8 Olga Brani
    store.pushMany(type, payload);
3511 e4a6c3b8 Olga Brani
  }, null, "DS: Extract payload of " + type);
3512 e4a6c3b8 Olga Brani
}
3513 e4a6c3b8 Olga Brani
3514 e4a6c3b8 Olga Brani
function _findHasMany(adapter, store, record, link, relationship) {
3515 e4a6c3b8 Olga Brani
  var promise = adapter.findHasMany(store, record, link, relationship),
3516 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, relationship.type);
3517 e4a6c3b8 Olga Brani
3518 e4a6c3b8 Olga Brani
  return resolve(promise, "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type).then(function(payload) {
3519 e4a6c3b8 Olga Brani
    payload = serializer.extract(store, relationship.type, payload, null, 'findHasMany');
3520 e4a6c3b8 Olga Brani
3521 e4a6c3b8 Olga Brani
    Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3522 e4a6c3b8 Olga Brani
3523 e4a6c3b8 Olga Brani
    var records = store.pushMany(relationship.type, payload);
3524 e4a6c3b8 Olga Brani
    record.updateHasMany(relationship.key, records);
3525 e4a6c3b8 Olga Brani
  }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type);
3526 e4a6c3b8 Olga Brani
}
3527 e4a6c3b8 Olga Brani
3528 e4a6c3b8 Olga Brani
function _findBelongsTo(adapter, store, record, link, relationship) {
3529 e4a6c3b8 Olga Brani
  var promise = adapter.findBelongsTo(store, record, link, relationship),
3530 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, relationship.type);
3531 e4a6c3b8 Olga Brani
3532 e4a6c3b8 Olga Brani
  return resolve(promise, "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type).then(function(payload) {
3533 e4a6c3b8 Olga Brani
    payload = serializer.extract(store, relationship.type, payload, null, 'findBelongsTo');
3534 e4a6c3b8 Olga Brani
3535 e4a6c3b8 Olga Brani
    var record = store.push(relationship.type, payload);
3536 e4a6c3b8 Olga Brani
    record.updateBelongsTo(relationship.key, record);
3537 e4a6c3b8 Olga Brani
    return record;
3538 e4a6c3b8 Olga Brani
  }, null, "DS: Extract payload of " + record + " : " + relationship.type);
3539 e4a6c3b8 Olga Brani
}
3540 e4a6c3b8 Olga Brani
3541 e4a6c3b8 Olga Brani
function _findAll(adapter, store, type, sinceToken) {
3542 e4a6c3b8 Olga Brani
  var promise = adapter.findAll(store, type, sinceToken),
3543 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, type);
3544 e4a6c3b8 Olga Brani
3545 e4a6c3b8 Olga Brani
  return resolve(promise, "DS: Handle Adapter#findAll of " + type).then(function(payload) {
3546 e4a6c3b8 Olga Brani
    payload = serializer.extract(store, type, payload, null, 'findAll');
3547 e4a6c3b8 Olga Brani
3548 e4a6c3b8 Olga Brani
    Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3549 e4a6c3b8 Olga Brani
3550 e4a6c3b8 Olga Brani
    store.pushMany(type, payload);
3551 e4a6c3b8 Olga Brani
    store.didUpdateAll(type);
3552 e4a6c3b8 Olga Brani
    return store.all(type);
3553 e4a6c3b8 Olga Brani
  }, null, "DS: Extract payload of findAll " + type);
3554 e4a6c3b8 Olga Brani
}
3555 e4a6c3b8 Olga Brani
3556 e4a6c3b8 Olga Brani
function _findQuery(adapter, store, type, query, recordArray) {
3557 e4a6c3b8 Olga Brani
  var promise = adapter.findQuery(store, type, query, recordArray),
3558 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, type);
3559 e4a6c3b8 Olga Brani
3560 e4a6c3b8 Olga Brani
  return resolve(promise, "DS: Handle Adapter#findQuery of " + type).then(function(payload) {
3561 e4a6c3b8 Olga Brani
    payload = serializer.extract(store, type, payload, null, 'findQuery');
3562 e4a6c3b8 Olga Brani
3563 e4a6c3b8 Olga Brani
    Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3564 e4a6c3b8 Olga Brani
3565 e4a6c3b8 Olga Brani
    recordArray.load(payload);
3566 e4a6c3b8 Olga Brani
    return recordArray;
3567 e4a6c3b8 Olga Brani
  }, null, "DS: Extract payload of findQuery " + type);
3568 e4a6c3b8 Olga Brani
}
3569 e4a6c3b8 Olga Brani
3570 e4a6c3b8 Olga Brani
function _commit(adapter, store, operation, record) {
3571 e4a6c3b8 Olga Brani
  var type = record.constructor,
3572 e4a6c3b8 Olga Brani
      promise = adapter[operation](store, type, record),
3573 e4a6c3b8 Olga Brani
      serializer = serializerForAdapter(adapter, type);
3574 e4a6c3b8 Olga Brani
3575 e4a6c3b8 Olga Brani
  Ember.assert("Your adapter's '" + operation + "' method must return a promise, but it returned " + promise, isThenable(promise));
3576 e4a6c3b8 Olga Brani
3577 e4a6c3b8 Olga Brani
  return promise.then(function(payload) {
3578 e4a6c3b8 Olga Brani
    if (payload) { payload = serializer.extract(store, type, payload, get(record, 'id'), operation); }
3579 e4a6c3b8 Olga Brani
    store.didSaveRecord(record, payload);
3580 e4a6c3b8 Olga Brani
    return record;
3581 e4a6c3b8 Olga Brani
  }, function(reason) {
3582 e4a6c3b8 Olga Brani
    if (reason instanceof DS.InvalidError) {
3583 e4a6c3b8 Olga Brani
      store.recordWasInvalid(record, reason.errors);
3584 e4a6c3b8 Olga Brani
    } else {
3585 e4a6c3b8 Olga Brani
      store.recordWasError(record, reason);
3586 e4a6c3b8 Olga Brani
    }
3587 e4a6c3b8 Olga Brani
3588 e4a6c3b8 Olga Brani
    throw reason;
3589 e4a6c3b8 Olga Brani
  }, "DS: Extract and notify about " + operation + " completion of " + record);
3590 e4a6c3b8 Olga Brani
}
3591 e4a6c3b8 Olga Brani
3592 e4a6c3b8 Olga Brani
})();
3593 e4a6c3b8 Olga Brani
3594 e4a6c3b8 Olga Brani
3595 e4a6c3b8 Olga Brani
3596 e4a6c3b8 Olga Brani
(function() {
3597 e4a6c3b8 Olga Brani
/**
3598 e4a6c3b8 Olga Brani
  @module ember-data
3599 e4a6c3b8 Olga Brani
*/
3600 e4a6c3b8 Olga Brani
3601 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
3602 e4a6c3b8 Olga Brani
/*
3603 e4a6c3b8 Olga Brani
  This file encapsulates the various states that a record can transition
3604 e4a6c3b8 Olga Brani
  through during its lifecycle.
3605 e4a6c3b8 Olga Brani
*/
3606 e4a6c3b8 Olga Brani
/**
3607 e4a6c3b8 Olga Brani
  ### State
3608 e4a6c3b8 Olga Brani

3609 e4a6c3b8 Olga Brani
  Each record has a `currentState` property that explicitly tracks what
3610 e4a6c3b8 Olga Brani
  state a record is in at any given time. For instance, if a record is
3611 e4a6c3b8 Olga Brani
  newly created and has not yet been sent to the adapter to be saved,
3612 e4a6c3b8 Olga Brani
  it would be in the `root.loaded.created.uncommitted` state.  If a
3613 e4a6c3b8 Olga Brani
  record has had local modifications made to it that are in the
3614 e4a6c3b8 Olga Brani
  process of being saved, the record would be in the
3615 e4a6c3b8 Olga Brani
  `root.loaded.updated.inFlight` state. (These state paths will be
3616 e4a6c3b8 Olga Brani
  explained in more detail below.)
3617 e4a6c3b8 Olga Brani

3618 e4a6c3b8 Olga Brani
  Events are sent by the record or its store to the record's
3619 e4a6c3b8 Olga Brani
  `currentState` property. How the state reacts to these events is
3620 e4a6c3b8 Olga Brani
  dependent on which state it is in. In some states, certain events
3621 e4a6c3b8 Olga Brani
  will be invalid and will cause an exception to be raised.
3622 e4a6c3b8 Olga Brani

3623 e4a6c3b8 Olga Brani
  States are hierarchical and every state is a substate of the
3624 e4a6c3b8 Olga Brani
  `RootState`. For example, a record can be in the
3625 e4a6c3b8 Olga Brani
  `root.deleted.uncommitted` state, then transition into the
3626 e4a6c3b8 Olga Brani
  `root.deleted.inFlight` state. If a child state does not implement
3627 e4a6c3b8 Olga Brani
  an event handler, the state manager will attempt to invoke the event
3628 e4a6c3b8 Olga Brani
  on all parent states until the root state is reached. The state
3629 e4a6c3b8 Olga Brani
  hierarchy of a record is described in terms of a path string. You
3630 e4a6c3b8 Olga Brani
  can determine a record's current state by getting the state's
3631 e4a6c3b8 Olga Brani
  `stateName` property:
3632 e4a6c3b8 Olga Brani

3633 e4a6c3b8 Olga Brani
  ```javascript
3634 e4a6c3b8 Olga Brani
  record.get('currentState.stateName');
3635 e4a6c3b8 Olga Brani
  //=> "root.created.uncommitted"
3636 e4a6c3b8 Olga Brani
   ```
3637 e4a6c3b8 Olga Brani

3638 e4a6c3b8 Olga Brani
  The hierarchy of valid states that ship with ember data looks like
3639 e4a6c3b8 Olga Brani
  this:
3640 e4a6c3b8 Olga Brani

3641 e4a6c3b8 Olga Brani
  ```text
3642 e4a6c3b8 Olga Brani
  * root
3643 e4a6c3b8 Olga Brani
    * deleted
3644 e4a6c3b8 Olga Brani
      * saved
3645 e4a6c3b8 Olga Brani
      * uncommitted
3646 e4a6c3b8 Olga Brani
      * inFlight
3647 e4a6c3b8 Olga Brani
    * empty
3648 e4a6c3b8 Olga Brani
    * loaded
3649 e4a6c3b8 Olga Brani
      * created
3650 e4a6c3b8 Olga Brani
        * uncommitted
3651 e4a6c3b8 Olga Brani
        * inFlight
3652 e4a6c3b8 Olga Brani
      * saved
3653 e4a6c3b8 Olga Brani
      * updated
3654 e4a6c3b8 Olga Brani
        * uncommitted
3655 e4a6c3b8 Olga Brani
        * inFlight
3656 e4a6c3b8 Olga Brani
    * loading
3657 e4a6c3b8 Olga Brani
  ```
3658 e4a6c3b8 Olga Brani

3659 e4a6c3b8 Olga Brani
  The `DS.Model` states are themselves stateless. What we mean is
3660 e4a6c3b8 Olga Brani
  that, the hierarchical states that each of *those* points to is a
3661 e4a6c3b8 Olga Brani
  shared data structure. For performance reasons, instead of each
3662 e4a6c3b8 Olga Brani
  record getting its own copy of the hierarchy of states, each record
3663 e4a6c3b8 Olga Brani
  points to this global, immutable shared instance. How does a state
3664 e4a6c3b8 Olga Brani
  know which record it should be acting on? We pass the record
3665 e4a6c3b8 Olga Brani
  instance into the state's event handlers as the first argument.
3666 e4a6c3b8 Olga Brani

3667 e4a6c3b8 Olga Brani
  The record passed as the first parameter is where you should stash
3668 e4a6c3b8 Olga Brani
  state about the record if needed; you should never store data on the state
3669 e4a6c3b8 Olga Brani
  object itself.
3670 e4a6c3b8 Olga Brani

3671 e4a6c3b8 Olga Brani
  ### Events and Flags
3672 e4a6c3b8 Olga Brani

3673 e4a6c3b8 Olga Brani
  A state may implement zero or more events and flags.
3674 e4a6c3b8 Olga Brani

3675 e4a6c3b8 Olga Brani
  #### Events
3676 e4a6c3b8 Olga Brani

3677 e4a6c3b8 Olga Brani
  Events are named functions that are invoked when sent to a record. The
3678 e4a6c3b8 Olga Brani
  record will first look for a method with the given name on the
3679 e4a6c3b8 Olga Brani
  current state. If no method is found, it will search the current
3680 e4a6c3b8 Olga Brani
  state's parent, and then its grandparent, and so on until reaching
3681 e4a6c3b8 Olga Brani
  the top of the hierarchy. If the root is reached without an event
3682 e4a6c3b8 Olga Brani
  handler being found, an exception will be raised. This can be very
3683 e4a6c3b8 Olga Brani
  helpful when debugging new features.
3684 e4a6c3b8 Olga Brani

3685 e4a6c3b8 Olga Brani
  Here's an example implementation of a state with a `myEvent` event handler:
3686 e4a6c3b8 Olga Brani

3687 e4a6c3b8 Olga Brani
  ```javascript
3688 e4a6c3b8 Olga Brani
  aState: DS.State.create({
3689 e4a6c3b8 Olga Brani
    myEvent: function(manager, param) {
3690 e4a6c3b8 Olga Brani
      console.log("Received myEvent with", param);
3691 e4a6c3b8 Olga Brani
    }
3692 e4a6c3b8 Olga Brani
  })
3693 e4a6c3b8 Olga Brani
  ```
3694 e4a6c3b8 Olga Brani

3695 e4a6c3b8 Olga Brani
  To trigger this event:
3696 e4a6c3b8 Olga Brani

3697 e4a6c3b8 Olga Brani
  ```javascript
3698 e4a6c3b8 Olga Brani
  record.send('myEvent', 'foo');
3699 e4a6c3b8 Olga Brani
  //=> "Received myEvent with foo"
3700 e4a6c3b8 Olga Brani
  ```
3701 e4a6c3b8 Olga Brani

3702 e4a6c3b8 Olga Brani
  Note that an optional parameter can be sent to a record's `send()` method,
3703 e4a6c3b8 Olga Brani
  which will be passed as the second parameter to the event handler.
3704 e4a6c3b8 Olga Brani

3705 e4a6c3b8 Olga Brani
  Events should transition to a different state if appropriate. This can be
3706 e4a6c3b8 Olga Brani
  done by calling the record's `transitionTo()` method with a path to the
3707 e4a6c3b8 Olga Brani
  desired state. The state manager will attempt to resolve the state path
3708 e4a6c3b8 Olga Brani
  relative to the current state. If no state is found at that path, it will
3709 e4a6c3b8 Olga Brani
  attempt to resolve it relative to the current state's parent, and then its
3710 e4a6c3b8 Olga Brani
  parent, and so on until the root is reached. For example, imagine a hierarchy
3711 e4a6c3b8 Olga Brani
  like this:
3712 e4a6c3b8 Olga Brani

3713 e4a6c3b8 Olga Brani
      * created
3714 e4a6c3b8 Olga Brani
        * uncommitted <-- currentState
3715 e4a6c3b8 Olga Brani
        * inFlight
3716 e4a6c3b8 Olga Brani
      * updated
3717 e4a6c3b8 Olga Brani
        * inFlight
3718 e4a6c3b8 Olga Brani

3719 e4a6c3b8 Olga Brani
  If we are currently in the `uncommitted` state, calling
3720 e4a6c3b8 Olga Brani
  `transitionTo('inFlight')` would transition to the `created.inFlight` state,
3721 e4a6c3b8 Olga Brani
  while calling `transitionTo('updated.inFlight')` would transition to
3722 e4a6c3b8 Olga Brani
  the `updated.inFlight` state.
3723 e4a6c3b8 Olga Brani

3724 e4a6c3b8 Olga Brani
  Remember that *only events* should ever cause a state transition. You should
3725 e4a6c3b8 Olga Brani
  never call `transitionTo()` from outside a state's event handler. If you are
3726 e4a6c3b8 Olga Brani
  tempted to do so, create a new event and send that to the state manager.
3727 e4a6c3b8 Olga Brani

3728 e4a6c3b8 Olga Brani
  #### Flags
3729 e4a6c3b8 Olga Brani

3730 e4a6c3b8 Olga Brani
  Flags are Boolean values that can be used to introspect a record's current
3731 e4a6c3b8 Olga Brani
  state in a more user-friendly way than examining its state path. For example,
3732 e4a6c3b8 Olga Brani
  instead of doing this:
3733 e4a6c3b8 Olga Brani

3734 e4a6c3b8 Olga Brani
  ```javascript
3735 e4a6c3b8 Olga Brani
  var statePath = record.get('stateManager.currentPath');
3736 e4a6c3b8 Olga Brani
  if (statePath === 'created.inFlight') {
3737 e4a6c3b8 Olga Brani
    doSomething();
3738 e4a6c3b8 Olga Brani
  }
3739 e4a6c3b8 Olga Brani
  ```
3740 e4a6c3b8 Olga Brani

3741 e4a6c3b8 Olga Brani
  You can say:
3742 e4a6c3b8 Olga Brani

3743 e4a6c3b8 Olga Brani
  ```javascript
3744 e4a6c3b8 Olga Brani
  if (record.get('isNew') && record.get('isSaving')) {
3745 e4a6c3b8 Olga Brani
    doSomething();
3746 e4a6c3b8 Olga Brani
  }
3747 e4a6c3b8 Olga Brani
  ```
3748 e4a6c3b8 Olga Brani

3749 e4a6c3b8 Olga Brani
  If your state does not set a value for a given flag, the value will
3750 e4a6c3b8 Olga Brani
  be inherited from its parent (or the first place in the state hierarchy
3751 e4a6c3b8 Olga Brani
  where it is defined).
3752 e4a6c3b8 Olga Brani

3753 e4a6c3b8 Olga Brani
  The current set of flags are defined below. If you want to add a new flag,
3754 e4a6c3b8 Olga Brani
  in addition to the area below, you will also need to declare it in the
3755 e4a6c3b8 Olga Brani
  `DS.Model` class.
3756 e4a6c3b8 Olga Brani

3757 e4a6c3b8 Olga Brani

3758 e4a6c3b8 Olga Brani
   * [isEmpty](DS.Model.html#property_isEmpty)
3759 e4a6c3b8 Olga Brani
   * [isLoading](DS.Model.html#property_isLoading)
3760 e4a6c3b8 Olga Brani
   * [isLoaded](DS.Model.html#property_isLoaded)
3761 e4a6c3b8 Olga Brani
   * [isDirty](DS.Model.html#property_isDirty)
3762 e4a6c3b8 Olga Brani
   * [isSaving](DS.Model.html#property_isSaving)
3763 e4a6c3b8 Olga Brani
   * [isDeleted](DS.Model.html#property_isDeleted)
3764 e4a6c3b8 Olga Brani
   * [isNew](DS.Model.html#property_isNew)
3765 e4a6c3b8 Olga Brani
   * [isValid](DS.Model.html#property_isValid)
3766 e4a6c3b8 Olga Brani

3767 e4a6c3b8 Olga Brani
  @namespace DS
3768 e4a6c3b8 Olga Brani
  @class RootState
3769 e4a6c3b8 Olga Brani
*/
3770 e4a6c3b8 Olga Brani
3771 e4a6c3b8 Olga Brani
var hasDefinedProperties = function(object) {
3772 e4a6c3b8 Olga Brani
  // Ignore internal property defined by simulated `Ember.create`.
3773 e4a6c3b8 Olga Brani
  var names = Ember.keys(object);
3774 e4a6c3b8 Olga Brani
  var i, l, name;
3775 e4a6c3b8 Olga Brani
  for (i = 0, l = names.length; i < l; i++ ) {
3776 e4a6c3b8 Olga Brani
    name = names[i];
3777 e4a6c3b8 Olga Brani
    if (object.hasOwnProperty(name) && object[name]) { return true; }
3778 e4a6c3b8 Olga Brani
  }
3779 e4a6c3b8 Olga Brani
3780 e4a6c3b8 Olga Brani
  return false;
3781 e4a6c3b8 Olga Brani
};
3782 e4a6c3b8 Olga Brani
3783 e4a6c3b8 Olga Brani
var didSetProperty = function(record, context) {
3784 e4a6c3b8 Olga Brani
  if (context.value === context.originalValue) {
3785 e4a6c3b8 Olga Brani
    delete record._attributes[context.name];
3786 e4a6c3b8 Olga Brani
    record.send('propertyWasReset', context.name);
3787 e4a6c3b8 Olga Brani
  } else if (context.value !== context.oldValue) {
3788 e4a6c3b8 Olga Brani
    record.send('becomeDirty');
3789 e4a6c3b8 Olga Brani
  }
3790 e4a6c3b8 Olga Brani
3791 e4a6c3b8 Olga Brani
  record.updateRecordArraysLater();
3792 e4a6c3b8 Olga Brani
};
3793 e4a6c3b8 Olga Brani
3794 e4a6c3b8 Olga Brani
// Implementation notes:
3795 e4a6c3b8 Olga Brani
//
3796 e4a6c3b8 Olga Brani
// Each state has a boolean value for all of the following flags:
3797 e4a6c3b8 Olga Brani
//
3798 e4a6c3b8 Olga Brani
// * isLoaded: The record has a populated `data` property. When a
3799 e4a6c3b8 Olga Brani
//   record is loaded via `store.find`, `isLoaded` is false
3800 e4a6c3b8 Olga Brani
//   until the adapter sets it. When a record is created locally,
3801 e4a6c3b8 Olga Brani
//   its `isLoaded` property is always true.
3802 e4a6c3b8 Olga Brani
// * isDirty: The record has local changes that have not yet been
3803 e4a6c3b8 Olga Brani
//   saved by the adapter. This includes records that have been
3804 e4a6c3b8 Olga Brani
//   created (but not yet saved) or deleted.
3805 e4a6c3b8 Olga Brani
// * isSaving: The record has been committed, but
3806 e4a6c3b8 Olga Brani
//   the adapter has not yet acknowledged that the changes have
3807 e4a6c3b8 Olga Brani
//   been persisted to the backend.
3808 e4a6c3b8 Olga Brani
// * isDeleted: The record was marked for deletion. When `isDeleted`
3809 e4a6c3b8 Olga Brani
//   is true and `isDirty` is true, the record is deleted locally
3810 e4a6c3b8 Olga Brani
//   but the deletion was not yet persisted. When `isSaving` is
3811 e4a6c3b8 Olga Brani
//   true, the change is in-flight. When both `isDirty` and
3812 e4a6c3b8 Olga Brani
//   `isSaving` are false, the change has persisted.
3813 e4a6c3b8 Olga Brani
// * isError: The adapter reported that it was unable to save
3814 e4a6c3b8 Olga Brani
//   local changes to the backend. This may also result in the
3815 e4a6c3b8 Olga Brani
//   record having its `isValid` property become false if the
3816 e4a6c3b8 Olga Brani
//   adapter reported that server-side validations failed.
3817 e4a6c3b8 Olga Brani
// * isNew: The record was created on the client and the adapter
3818 e4a6c3b8 Olga Brani
//   did not yet report that it was successfully saved.
3819 e4a6c3b8 Olga Brani
// * isValid: No client-side validations have failed and the
3820 e4a6c3b8 Olga Brani
//   adapter did not report any server-side validation failures.
3821 e4a6c3b8 Olga Brani
3822 e4a6c3b8 Olga Brani
// The dirty state is a abstract state whose functionality is
3823 e4a6c3b8 Olga Brani
// shared between the `created` and `updated` states.
3824 e4a6c3b8 Olga Brani
//
3825 e4a6c3b8 Olga Brani
// The deleted state shares the `isDirty` flag with the
3826 e4a6c3b8 Olga Brani
// subclasses of `DirtyState`, but with a very different
3827 e4a6c3b8 Olga Brani
// implementation.
3828 e4a6c3b8 Olga Brani
//
3829 e4a6c3b8 Olga Brani
// Dirty states have three child states:
3830 e4a6c3b8 Olga Brani
//
3831 e4a6c3b8 Olga Brani
// `uncommitted`: the store has not yet handed off the record
3832 e4a6c3b8 Olga Brani
//   to be saved.
3833 e4a6c3b8 Olga Brani
// `inFlight`: the store has handed off the record to be saved,
3834 e4a6c3b8 Olga Brani
//   but the adapter has not yet acknowledged success.
3835 e4a6c3b8 Olga Brani
// `invalid`: the record has invalid information and cannot be
3836 e4a6c3b8 Olga Brani
//   send to the adapter yet.
3837 e4a6c3b8 Olga Brani
var DirtyState = {
3838 e4a6c3b8 Olga Brani
  initialState: 'uncommitted',
3839 e4a6c3b8 Olga Brani
3840 e4a6c3b8 Olga Brani
  // FLAGS
3841 e4a6c3b8 Olga Brani
  isDirty: true,
3842 e4a6c3b8 Olga Brani
3843 e4a6c3b8 Olga Brani
  // SUBSTATES
3844 e4a6c3b8 Olga Brani
3845 e4a6c3b8 Olga Brani
  // When a record first becomes dirty, it is `uncommitted`.
3846 e4a6c3b8 Olga Brani
  // This means that there are local pending changes, but they
3847 e4a6c3b8 Olga Brani
  // have not yet begun to be saved, and are not invalid.
3848 e4a6c3b8 Olga Brani
  uncommitted: {
3849 e4a6c3b8 Olga Brani
    // EVENTS
3850 e4a6c3b8 Olga Brani
    didSetProperty: didSetProperty,
3851 e4a6c3b8 Olga Brani
3852 e4a6c3b8 Olga Brani
    propertyWasReset: function(record, name) {
3853 e4a6c3b8 Olga Brani
      var stillDirty = false;
3854 e4a6c3b8 Olga Brani
3855 e4a6c3b8 Olga Brani
      for (var prop in record._attributes) {
3856 e4a6c3b8 Olga Brani
        stillDirty = true;
3857 e4a6c3b8 Olga Brani
        break;
3858 e4a6c3b8 Olga Brani
      }
3859 e4a6c3b8 Olga Brani
3860 e4a6c3b8 Olga Brani
      if (!stillDirty) { record.send('rolledBack'); }
3861 e4a6c3b8 Olga Brani
    },
3862 e4a6c3b8 Olga Brani
3863 e4a6c3b8 Olga Brani
    pushedData: Ember.K,
3864 e4a6c3b8 Olga Brani
3865 e4a6c3b8 Olga Brani
    becomeDirty: Ember.K,
3866 e4a6c3b8 Olga Brani
3867 e4a6c3b8 Olga Brani
    willCommit: function(record) {
3868 e4a6c3b8 Olga Brani
      record.transitionTo('inFlight');
3869 e4a6c3b8 Olga Brani
    },
3870 e4a6c3b8 Olga Brani
3871 e4a6c3b8 Olga Brani
    reloadRecord: function(record, resolve) {
3872 e4a6c3b8 Olga Brani
      resolve(get(record, 'store').reloadRecord(record));
3873 e4a6c3b8 Olga Brani
    },
3874 e4a6c3b8 Olga Brani
3875 e4a6c3b8 Olga Brani
    rolledBack: function(record) {
3876 e4a6c3b8 Olga Brani
      record.transitionTo('loaded.saved');
3877 e4a6c3b8 Olga Brani
    },
3878 e4a6c3b8 Olga Brani
3879 e4a6c3b8 Olga Brani
    becameInvalid: function(record) {
3880 e4a6c3b8 Olga Brani
      record.transitionTo('invalid');
3881 e4a6c3b8 Olga Brani
    },
3882 e4a6c3b8 Olga Brani
3883 e4a6c3b8 Olga Brani
    rollback: function(record) {
3884 e4a6c3b8 Olga Brani
      record.rollback();
3885 e4a6c3b8 Olga Brani
    }
3886 e4a6c3b8 Olga Brani
  },
3887 e4a6c3b8 Olga Brani
3888 e4a6c3b8 Olga Brani
  // Once a record has been handed off to the adapter to be
3889 e4a6c3b8 Olga Brani
  // saved, it is in the 'in flight' state. Changes to the
3890 e4a6c3b8 Olga Brani
  // record cannot be made during this window.
3891 e4a6c3b8 Olga Brani
  inFlight: {
3892 e4a6c3b8 Olga Brani
    // FLAGS
3893 e4a6c3b8 Olga Brani
    isSaving: true,
3894 e4a6c3b8 Olga Brani
3895 e4a6c3b8 Olga Brani
    // EVENTS
3896 e4a6c3b8 Olga Brani
    didSetProperty: didSetProperty,
3897 e4a6c3b8 Olga Brani
    becomeDirty: Ember.K,
3898 e4a6c3b8 Olga Brani
    pushedData: Ember.K,
3899 e4a6c3b8 Olga Brani
3900 e4a6c3b8 Olga Brani
    // TODO: More robust semantics around save-while-in-flight
3901 e4a6c3b8 Olga Brani
    willCommit: Ember.K,
3902 e4a6c3b8 Olga Brani
3903 e4a6c3b8 Olga Brani
    didCommit: function(record) {
3904 e4a6c3b8 Olga Brani
      var dirtyType = get(this, 'dirtyType');
3905 e4a6c3b8 Olga Brani
3906 e4a6c3b8 Olga Brani
      record.transitionTo('saved');
3907 e4a6c3b8 Olga Brani
      record.send('invokeLifecycleCallbacks', dirtyType);
3908 e4a6c3b8 Olga Brani
    },
3909 e4a6c3b8 Olga Brani
3910 e4a6c3b8 Olga Brani
    becameInvalid: function(record) {
3911 e4a6c3b8 Olga Brani
      record.transitionTo('invalid');
3912 e4a6c3b8 Olga Brani
      record.send('invokeLifecycleCallbacks');
3913 e4a6c3b8 Olga Brani
    },
3914 e4a6c3b8 Olga Brani
3915 e4a6c3b8 Olga Brani
    becameError: function(record) {
3916 e4a6c3b8 Olga Brani
      record.transitionTo('uncommitted');
3917 e4a6c3b8 Olga Brani
      record.triggerLater('becameError', record);
3918 e4a6c3b8 Olga Brani
    }
3919 e4a6c3b8 Olga Brani
  },
3920 e4a6c3b8 Olga Brani
3921 e4a6c3b8 Olga Brani
  // A record is in the `invalid` state when its client-side
3922 e4a6c3b8 Olga Brani
  // invalidations have failed, or if the adapter has indicated
3923 e4a6c3b8 Olga Brani
  // the the record failed server-side invalidations.
3924 e4a6c3b8 Olga Brani
  invalid: {
3925 e4a6c3b8 Olga Brani
    // FLAGS
3926 e4a6c3b8 Olga Brani
    isValid: false,
3927 e4a6c3b8 Olga Brani
3928 e4a6c3b8 Olga Brani
    // EVENTS
3929 e4a6c3b8 Olga Brani
    deleteRecord: function(record) {
3930 e4a6c3b8 Olga Brani
      record.transitionTo('deleted.uncommitted');
3931 e4a6c3b8 Olga Brani
      record.clearRelationships();
3932 e4a6c3b8 Olga Brani
    },
3933 e4a6c3b8 Olga Brani
3934 e4a6c3b8 Olga Brani
    didSetProperty: function(record, context) {
3935 e4a6c3b8 Olga Brani
      get(record, 'errors').remove(context.name);
3936 e4a6c3b8 Olga Brani
3937 e4a6c3b8 Olga Brani
      didSetProperty(record, context);
3938 e4a6c3b8 Olga Brani
    },
3939 e4a6c3b8 Olga Brani
3940 e4a6c3b8 Olga Brani
    becomeDirty: Ember.K,
3941 e4a6c3b8 Olga Brani
3942 e4a6c3b8 Olga Brani
    rolledBack: function(record) {
3943 e4a6c3b8 Olga Brani
      get(record, 'errors').clear();
3944 e4a6c3b8 Olga Brani
    },
3945 e4a6c3b8 Olga Brani
3946 e4a6c3b8 Olga Brani
    becameValid: function(record) {
3947 e4a6c3b8 Olga Brani
      record.transitionTo('uncommitted');
3948 e4a6c3b8 Olga Brani
    },
3949 e4a6c3b8 Olga Brani
3950 e4a6c3b8 Olga Brani
    invokeLifecycleCallbacks: function(record) {
3951 e4a6c3b8 Olga Brani
      record.triggerLater('becameInvalid', record);
3952 e4a6c3b8 Olga Brani
    }
3953 e4a6c3b8 Olga Brani
  }
3954 e4a6c3b8 Olga Brani
};
3955 e4a6c3b8 Olga Brani
3956 e4a6c3b8 Olga Brani
// The created and updated states are created outside the state
3957 e4a6c3b8 Olga Brani
// chart so we can reopen their substates and add mixins as
3958 e4a6c3b8 Olga Brani
// necessary.
3959 e4a6c3b8 Olga Brani
3960 e4a6c3b8 Olga Brani
function deepClone(object) {
3961 e4a6c3b8 Olga Brani
  var clone = {}, value;
3962 e4a6c3b8 Olga Brani
3963 e4a6c3b8 Olga Brani
  for (var prop in object) {
3964 e4a6c3b8 Olga Brani
    value = object[prop];
3965 e4a6c3b8 Olga Brani
    if (value && typeof value === 'object') {
3966 e4a6c3b8 Olga Brani
      clone[prop] = deepClone(value);
3967 e4a6c3b8 Olga Brani
    } else {
3968 e4a6c3b8 Olga Brani
      clone[prop] = value;
3969 e4a6c3b8 Olga Brani
    }
3970 e4a6c3b8 Olga Brani
  }
3971 e4a6c3b8 Olga Brani
3972 e4a6c3b8 Olga Brani
  return clone;
3973 e4a6c3b8 Olga Brani
}
3974 e4a6c3b8 Olga Brani
3975 e4a6c3b8 Olga Brani
function mixin(original, hash) {
3976 e4a6c3b8 Olga Brani
  for (var prop in hash) {
3977 e4a6c3b8 Olga Brani
    original[prop] = hash[prop];
3978 e4a6c3b8 Olga Brani
  }
3979 e4a6c3b8 Olga Brani
3980 e4a6c3b8 Olga Brani
  return original;
3981 e4a6c3b8 Olga Brani
}
3982 e4a6c3b8 Olga Brani
3983 e4a6c3b8 Olga Brani
function dirtyState(options) {
3984 e4a6c3b8 Olga Brani
  var newState = deepClone(DirtyState);
3985 e4a6c3b8 Olga Brani
  return mixin(newState, options);
3986 e4a6c3b8 Olga Brani
}
3987 e4a6c3b8 Olga Brani
3988 e4a6c3b8 Olga Brani
var createdState = dirtyState({
3989 e4a6c3b8 Olga Brani
  dirtyType: 'created',
3990 e4a6c3b8 Olga Brani
3991 e4a6c3b8 Olga Brani
  // FLAGS
3992 e4a6c3b8 Olga Brani
  isNew: true
3993 e4a6c3b8 Olga Brani
});
3994 e4a6c3b8 Olga Brani
3995 e4a6c3b8 Olga Brani
createdState.uncommitted.rolledBack = function(record) {
3996 e4a6c3b8 Olga Brani
  record.transitionTo('deleted.saved');
3997 e4a6c3b8 Olga Brani
};
3998 e4a6c3b8 Olga Brani
3999 e4a6c3b8 Olga Brani
var updatedState = dirtyState({
4000 e4a6c3b8 Olga Brani
  dirtyType: 'updated'
4001 e4a6c3b8 Olga Brani
});
4002 e4a6c3b8 Olga Brani
4003 e4a6c3b8 Olga Brani
createdState.uncommitted.deleteRecord = function(record) {
4004 e4a6c3b8 Olga Brani
  record.clearRelationships();
4005 e4a6c3b8 Olga Brani
  record.transitionTo('deleted.saved');
4006 e4a6c3b8 Olga Brani
};
4007 e4a6c3b8 Olga Brani
4008 e4a6c3b8 Olga Brani
createdState.uncommitted.rollback = function(record) {
4009 e4a6c3b8 Olga Brani
  DirtyState.uncommitted.rollback.apply(this, arguments);
4010 e4a6c3b8 Olga Brani
  record.transitionTo('deleted.saved');
4011 e4a6c3b8 Olga Brani
};
4012 e4a6c3b8 Olga Brani
4013 e4a6c3b8 Olga Brani
updatedState.uncommitted.deleteRecord = function(record) {
4014 e4a6c3b8 Olga Brani
  record.transitionTo('deleted.uncommitted');
4015 e4a6c3b8 Olga Brani
  record.clearRelationships();
4016 e4a6c3b8 Olga Brani
};
4017 e4a6c3b8 Olga Brani
4018 e4a6c3b8 Olga Brani
var RootState = {
4019 e4a6c3b8 Olga Brani
  // FLAGS
4020 e4a6c3b8 Olga Brani
  isEmpty: false,
4021 e4a6c3b8 Olga Brani
  isLoading: false,
4022 e4a6c3b8 Olga Brani
  isLoaded: false,
4023 e4a6c3b8 Olga Brani
  isDirty: false,
4024 e4a6c3b8 Olga Brani
  isSaving: false,
4025 e4a6c3b8 Olga Brani
  isDeleted: false,
4026 e4a6c3b8 Olga Brani
  isNew: false,
4027 e4a6c3b8 Olga Brani
  isValid: true,
4028 e4a6c3b8 Olga Brani
4029 e4a6c3b8 Olga Brani
  // DEFAULT EVENTS
4030 e4a6c3b8 Olga Brani
4031 e4a6c3b8 Olga Brani
  // Trying to roll back if you're not in the dirty state
4032 e4a6c3b8 Olga Brani
  // doesn't change your state. For example, if you're in the
4033 e4a6c3b8 Olga Brani
  // in-flight state, rolling back the record doesn't move
4034 e4a6c3b8 Olga Brani
  // you out of the in-flight state.
4035 e4a6c3b8 Olga Brani
  rolledBack: Ember.K,
4036 e4a6c3b8 Olga Brani
4037 e4a6c3b8 Olga Brani
  propertyWasReset: Ember.K,
4038 e4a6c3b8 Olga Brani
4039 e4a6c3b8 Olga Brani
  // SUBSTATES
4040 e4a6c3b8 Olga Brani
4041 e4a6c3b8 Olga Brani
  // A record begins its lifecycle in the `empty` state.
4042 e4a6c3b8 Olga Brani
  // If its data will come from the adapter, it will
4043 e4a6c3b8 Olga Brani
  // transition into the `loading` state. Otherwise, if
4044 e4a6c3b8 Olga Brani
  // the record is being created on the client, it will
4045 e4a6c3b8 Olga Brani
  // transition into the `created` state.
4046 e4a6c3b8 Olga Brani
  empty: {
4047 e4a6c3b8 Olga Brani
    isEmpty: true,
4048 e4a6c3b8 Olga Brani
4049 e4a6c3b8 Olga Brani
    // EVENTS
4050 e4a6c3b8 Olga Brani
    loadingData: function(record, promise) {
4051 e4a6c3b8 Olga Brani
      record._loadingPromise = promise;
4052 e4a6c3b8 Olga Brani
      record.transitionTo('loading');
4053 e4a6c3b8 Olga Brani
    },
4054 e4a6c3b8 Olga Brani
4055 e4a6c3b8 Olga Brani
    loadedData: function(record) {
4056 e4a6c3b8 Olga Brani
      record.transitionTo('loaded.created.uncommitted');
4057 e4a6c3b8 Olga Brani
4058 e4a6c3b8 Olga Brani
      record.suspendRelationshipObservers(function() {
4059 e4a6c3b8 Olga Brani
        record.notifyPropertyChange('data');
4060 e4a6c3b8 Olga Brani
      });
4061 e4a6c3b8 Olga Brani
    },
4062 e4a6c3b8 Olga Brani
4063 e4a6c3b8 Olga Brani
    pushedData: function(record) {
4064 e4a6c3b8 Olga Brani
      record.transitionTo('loaded.saved');
4065 e4a6c3b8 Olga Brani
      record.triggerLater('didLoad');
4066 e4a6c3b8 Olga Brani
    }
4067 e4a6c3b8 Olga Brani
  },
4068 e4a6c3b8 Olga Brani
4069 e4a6c3b8 Olga Brani
  // A record enters this state when the store askes
4070 e4a6c3b8 Olga Brani
  // the adapter for its data. It remains in this state
4071 e4a6c3b8 Olga Brani
  // until the adapter provides the requested data.
4072 e4a6c3b8 Olga Brani
  //
4073 e4a6c3b8 Olga Brani
  // Usually, this process is asynchronous, using an
4074 e4a6c3b8 Olga Brani
  // XHR to retrieve the data.
4075 e4a6c3b8 Olga Brani
  loading: {
4076 e4a6c3b8 Olga Brani
    // FLAGS
4077 e4a6c3b8 Olga Brani
    isLoading: true,
4078 e4a6c3b8 Olga Brani
4079 e4a6c3b8 Olga Brani
    exit: function(record) {
4080 e4a6c3b8 Olga Brani
      record._loadingPromise = null;
4081 e4a6c3b8 Olga Brani
    },
4082 e4a6c3b8 Olga Brani
4083 e4a6c3b8 Olga Brani
    // EVENTS
4084 e4a6c3b8 Olga Brani
    pushedData: function(record) {
4085 e4a6c3b8 Olga Brani
      record.transitionTo('loaded.saved');
4086 e4a6c3b8 Olga Brani
      record.triggerLater('didLoad');
4087 e4a6c3b8 Olga Brani
      set(record, 'isError', false);
4088 e4a6c3b8 Olga Brani
    },
4089 e4a6c3b8 Olga Brani
4090 e4a6c3b8 Olga Brani
    becameError: function(record) {
4091 e4a6c3b8 Olga Brani
      record.triggerLater('becameError', record);
4092 e4a6c3b8 Olga Brani
    },
4093 e4a6c3b8 Olga Brani
4094 e4a6c3b8 Olga Brani
    notFound: function(record) {
4095 e4a6c3b8 Olga Brani
      record.transitionTo('empty');
4096 e4a6c3b8 Olga Brani
    }
4097 e4a6c3b8 Olga Brani
  },
4098 e4a6c3b8 Olga Brani
4099 e4a6c3b8 Olga Brani
  // A record enters this state when its data is populated.
4100 e4a6c3b8 Olga Brani
  // Most of a record's lifecycle is spent inside substates
4101 e4a6c3b8 Olga Brani
  // of the `loaded` state.
4102 e4a6c3b8 Olga Brani
  loaded: {
4103 e4a6c3b8 Olga Brani
    initialState: 'saved',
4104 e4a6c3b8 Olga Brani
4105 e4a6c3b8 Olga Brani
    // FLAGS
4106 e4a6c3b8 Olga Brani
    isLoaded: true,
4107 e4a6c3b8 Olga Brani
4108 e4a6c3b8 Olga Brani
    // SUBSTATES
4109 e4a6c3b8 Olga Brani
4110 e4a6c3b8 Olga Brani
    // If there are no local changes to a record, it remains
4111 e4a6c3b8 Olga Brani
    // in the `saved` state.
4112 e4a6c3b8 Olga Brani
    saved: {
4113 e4a6c3b8 Olga Brani
      setup: function(record) {
4114 e4a6c3b8 Olga Brani
        var attrs = record._attributes,
4115 e4a6c3b8 Olga Brani
            isDirty = false;
4116 e4a6c3b8 Olga Brani
4117 e4a6c3b8 Olga Brani
        for (var prop in attrs) {
4118 e4a6c3b8 Olga Brani
          if (attrs.hasOwnProperty(prop)) {
4119 e4a6c3b8 Olga Brani
            isDirty = true;
4120 e4a6c3b8 Olga Brani
            break;
4121 e4a6c3b8 Olga Brani
          }
4122 e4a6c3b8 Olga Brani
        }
4123 e4a6c3b8 Olga Brani
4124 e4a6c3b8 Olga Brani
        if (isDirty) {
4125 e4a6c3b8 Olga Brani
          record.adapterDidDirty();
4126 e4a6c3b8 Olga Brani
        }
4127 e4a6c3b8 Olga Brani
      },
4128 e4a6c3b8 Olga Brani
4129 e4a6c3b8 Olga Brani
      // EVENTS
4130 e4a6c3b8 Olga Brani
      didSetProperty: didSetProperty,
4131 e4a6c3b8 Olga Brani
4132 e4a6c3b8 Olga Brani
      pushedData: Ember.K,
4133 e4a6c3b8 Olga Brani
4134 e4a6c3b8 Olga Brani
      becomeDirty: function(record) {
4135 e4a6c3b8 Olga Brani
        record.transitionTo('updated.uncommitted');
4136 e4a6c3b8 Olga Brani
      },
4137 e4a6c3b8 Olga Brani
4138 e4a6c3b8 Olga Brani
      willCommit: function(record) {
4139 e4a6c3b8 Olga Brani
        record.transitionTo('updated.inFlight');
4140 e4a6c3b8 Olga Brani
      },
4141 e4a6c3b8 Olga Brani
4142 e4a6c3b8 Olga Brani
      reloadRecord: function(record, resolve) {
4143 e4a6c3b8 Olga Brani
        resolve(get(record, 'store').reloadRecord(record));
4144 e4a6c3b8 Olga Brani
      },
4145 e4a6c3b8 Olga Brani
4146 e4a6c3b8 Olga Brani
      deleteRecord: function(record) {
4147 e4a6c3b8 Olga Brani
        record.transitionTo('deleted.uncommitted');
4148 e4a6c3b8 Olga Brani
        record.clearRelationships();
4149 e4a6c3b8 Olga Brani
      },
4150 e4a6c3b8 Olga Brani
4151 e4a6c3b8 Olga Brani
      unloadRecord: function(record) {
4152 e4a6c3b8 Olga Brani
        // clear relationships before moving to deleted state
4153 e4a6c3b8 Olga Brani
        // otherwise it fails
4154 e4a6c3b8 Olga Brani
        record.clearRelationships();
4155 e4a6c3b8 Olga Brani
        record.transitionTo('deleted.saved');
4156 e4a6c3b8 Olga Brani
      },
4157 e4a6c3b8 Olga Brani
4158 e4a6c3b8 Olga Brani
      didCommit: function(record) {
4159 e4a6c3b8 Olga Brani
        record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType'));
4160 e4a6c3b8 Olga Brani
      },
4161 e4a6c3b8 Olga Brani
4162 e4a6c3b8 Olga Brani
      // loaded.saved.notFound would be triggered by a failed
4163 e4a6c3b8 Olga Brani
      // `reload()` on an unchanged record
4164 e4a6c3b8 Olga Brani
      notFound: Ember.K
4165 e4a6c3b8 Olga Brani
4166 e4a6c3b8 Olga Brani
    },
4167 e4a6c3b8 Olga Brani
4168 e4a6c3b8 Olga Brani
    // A record is in this state after it has been locally
4169 e4a6c3b8 Olga Brani
    // created but before the adapter has indicated that
4170 e4a6c3b8 Olga Brani
    // it has been saved.
4171 e4a6c3b8 Olga Brani
    created: createdState,
4172 e4a6c3b8 Olga Brani
4173 e4a6c3b8 Olga Brani
    // A record is in this state if it has already been
4174 e4a6c3b8 Olga Brani
    // saved to the server, but there are new local changes
4175 e4a6c3b8 Olga Brani
    // that have not yet been saved.
4176 e4a6c3b8 Olga Brani
    updated: updatedState
4177 e4a6c3b8 Olga Brani
  },
4178 e4a6c3b8 Olga Brani
4179 e4a6c3b8 Olga Brani
  // A record is in this state if it was deleted from the store.
4180 e4a6c3b8 Olga Brani
  deleted: {
4181 e4a6c3b8 Olga Brani
    initialState: 'uncommitted',
4182 e4a6c3b8 Olga Brani
    dirtyType: 'deleted',
4183 e4a6c3b8 Olga Brani
4184 e4a6c3b8 Olga Brani
    // FLAGS
4185 e4a6c3b8 Olga Brani
    isDeleted: true,
4186 e4a6c3b8 Olga Brani
    isLoaded: true,
4187 e4a6c3b8 Olga Brani
    isDirty: true,
4188 e4a6c3b8 Olga Brani
4189 e4a6c3b8 Olga Brani
    // TRANSITIONS
4190 e4a6c3b8 Olga Brani
    setup: function(record) {
4191 e4a6c3b8 Olga Brani
      record.updateRecordArrays();
4192 e4a6c3b8 Olga Brani
    },
4193 e4a6c3b8 Olga Brani
4194 e4a6c3b8 Olga Brani
    // SUBSTATES
4195 e4a6c3b8 Olga Brani
4196 e4a6c3b8 Olga Brani
    // When a record is deleted, it enters the `start`
4197 e4a6c3b8 Olga Brani
    // state. It will exit this state when the record
4198 e4a6c3b8 Olga Brani
    // starts to commit.
4199 e4a6c3b8 Olga Brani
    uncommitted: {
4200 e4a6c3b8 Olga Brani
4201 e4a6c3b8 Olga Brani
      // EVENTS
4202 e4a6c3b8 Olga Brani
4203 e4a6c3b8 Olga Brani
      willCommit: function(record) {
4204 e4a6c3b8 Olga Brani
        record.transitionTo('inFlight');
4205 e4a6c3b8 Olga Brani
      },
4206 e4a6c3b8 Olga Brani
4207 e4a6c3b8 Olga Brani
      rollback: function(record) {
4208 e4a6c3b8 Olga Brani
        record.rollback();
4209 e4a6c3b8 Olga Brani
      },
4210 e4a6c3b8 Olga Brani
4211 e4a6c3b8 Olga Brani
      becomeDirty: Ember.K,
4212 e4a6c3b8 Olga Brani
      deleteRecord: Ember.K,
4213 e4a6c3b8 Olga Brani
4214 e4a6c3b8 Olga Brani
      rolledBack: function(record) {
4215 e4a6c3b8 Olga Brani
        record.transitionTo('loaded.saved');
4216 e4a6c3b8 Olga Brani
      }
4217 e4a6c3b8 Olga Brani
    },
4218 e4a6c3b8 Olga Brani
4219 e4a6c3b8 Olga Brani
    // After a record starts committing, but
4220 e4a6c3b8 Olga Brani
    // before the adapter indicates that the deletion
4221 e4a6c3b8 Olga Brani
    // has saved to the server, a record is in the
4222 e4a6c3b8 Olga Brani
    // `inFlight` substate of `deleted`.
4223 e4a6c3b8 Olga Brani
    inFlight: {
4224 e4a6c3b8 Olga Brani
      // FLAGS
4225 e4a6c3b8 Olga Brani
      isSaving: true,
4226 e4a6c3b8 Olga Brani
4227 e4a6c3b8 Olga Brani
      // EVENTS
4228 e4a6c3b8 Olga Brani
4229 e4a6c3b8 Olga Brani
      // TODO: More robust semantics around save-while-in-flight
4230 e4a6c3b8 Olga Brani
      willCommit: Ember.K,
4231 e4a6c3b8 Olga Brani
      didCommit: function(record) {
4232 e4a6c3b8 Olga Brani
        record.transitionTo('saved');
4233 e4a6c3b8 Olga Brani
4234 e4a6c3b8 Olga Brani
        record.send('invokeLifecycleCallbacks');
4235 e4a6c3b8 Olga Brani
      },
4236 e4a6c3b8 Olga Brani
4237 e4a6c3b8 Olga Brani
      becameError: function(record) {
4238 e4a6c3b8 Olga Brani
        record.transitionTo('uncommitted');
4239 e4a6c3b8 Olga Brani
        record.triggerLater('becameError', record);
4240 e4a6c3b8 Olga Brani
      }
4241 e4a6c3b8 Olga Brani
    },
4242 e4a6c3b8 Olga Brani
4243 e4a6c3b8 Olga Brani
    // Once the adapter indicates that the deletion has
4244 e4a6c3b8 Olga Brani
    // been saved, the record enters the `saved` substate
4245 e4a6c3b8 Olga Brani
    // of `deleted`.
4246 e4a6c3b8 Olga Brani
    saved: {
4247 e4a6c3b8 Olga Brani
      // FLAGS
4248 e4a6c3b8 Olga Brani
      isDirty: false,
4249 e4a6c3b8 Olga Brani
4250 e4a6c3b8 Olga Brani
      setup: function(record) {
4251 e4a6c3b8 Olga Brani
        var store = get(record, 'store');
4252 e4a6c3b8 Olga Brani
        store.dematerializeRecord(record);
4253 e4a6c3b8 Olga Brani
      },
4254 e4a6c3b8 Olga Brani
4255 e4a6c3b8 Olga Brani
      invokeLifecycleCallbacks: function(record) {
4256 e4a6c3b8 Olga Brani
        record.triggerLater('didDelete', record);
4257 e4a6c3b8 Olga Brani
        record.triggerLater('didCommit', record);
4258 e4a6c3b8 Olga Brani
      }
4259 e4a6c3b8 Olga Brani
    }
4260 e4a6c3b8 Olga Brani
  },
4261 e4a6c3b8 Olga Brani
4262 e4a6c3b8 Olga Brani
  invokeLifecycleCallbacks: function(record, dirtyType) {
4263 e4a6c3b8 Olga Brani
    if (dirtyType === 'created') {
4264 e4a6c3b8 Olga Brani
      record.triggerLater('didCreate', record);
4265 e4a6c3b8 Olga Brani
    } else {
4266 e4a6c3b8 Olga Brani
      record.triggerLater('didUpdate', record);
4267 e4a6c3b8 Olga Brani
    }
4268 e4a6c3b8 Olga Brani
4269 e4a6c3b8 Olga Brani
    record.triggerLater('didCommit', record);
4270 e4a6c3b8 Olga Brani
  }
4271 e4a6c3b8 Olga Brani
};
4272 e4a6c3b8 Olga Brani
4273 e4a6c3b8 Olga Brani
function wireState(object, parent, name) {
4274 e4a6c3b8 Olga Brani
  /*jshint proto:true*/
4275 e4a6c3b8 Olga Brani
  // TODO: Use Object.create and copy instead
4276 e4a6c3b8 Olga Brani
  object = mixin(parent ? Ember.create(parent) : {}, object);
4277 e4a6c3b8 Olga Brani
  object.parentState = parent;
4278 e4a6c3b8 Olga Brani
  object.stateName = name;
4279 e4a6c3b8 Olga Brani
4280 e4a6c3b8 Olga Brani
  for (var prop in object) {
4281 e4a6c3b8 Olga Brani
    if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; }
4282 e4a6c3b8 Olga Brani
    if (typeof object[prop] === 'object') {
4283 e4a6c3b8 Olga Brani
      object[prop] = wireState(object[prop], object, name + "." + prop);
4284 e4a6c3b8 Olga Brani
    }
4285 e4a6c3b8 Olga Brani
  }
4286 e4a6c3b8 Olga Brani
4287 e4a6c3b8 Olga Brani
  return object;
4288 e4a6c3b8 Olga Brani
}
4289 e4a6c3b8 Olga Brani
4290 e4a6c3b8 Olga Brani
RootState = wireState(RootState, null, "root");
4291 e4a6c3b8 Olga Brani
4292 e4a6c3b8 Olga Brani
DS.RootState = RootState;
4293 e4a6c3b8 Olga Brani
4294 e4a6c3b8 Olga Brani
})();
4295 e4a6c3b8 Olga Brani
4296 e4a6c3b8 Olga Brani
4297 e4a6c3b8 Olga Brani
4298 e4a6c3b8 Olga Brani
(function() {
4299 e4a6c3b8 Olga Brani
var get = Ember.get, isEmpty = Ember.isEmpty;
4300 e4a6c3b8 Olga Brani
4301 e4a6c3b8 Olga Brani
/**
4302 e4a6c3b8 Olga Brani
@module ember-data
4303 e4a6c3b8 Olga Brani
*/
4304 e4a6c3b8 Olga Brani
4305 e4a6c3b8 Olga Brani
/**
4306 e4a6c3b8 Olga Brani
  Holds validation errors for a given record organized by attribute names.
4307 e4a6c3b8 Olga Brani

4308 e4a6c3b8 Olga Brani
  @class Errors
4309 e4a6c3b8 Olga Brani
  @namespace DS
4310 e4a6c3b8 Olga Brani
  @extends Ember.Object
4311 e4a6c3b8 Olga Brani
  @uses Ember.Enumerable
4312 e4a6c3b8 Olga Brani
  @uses Ember.Evented
4313 e4a6c3b8 Olga Brani
 */
4314 e4a6c3b8 Olga Brani
DS.Errors = Ember.Object.extend(Ember.Enumerable, Ember.Evented, {
4315 e4a6c3b8 Olga Brani
  /**
4316 e4a6c3b8 Olga Brani
    Register with target handler
4317 e4a6c3b8 Olga Brani

4318 e4a6c3b8 Olga Brani
    @method registerHandlers
4319 e4a6c3b8 Olga Brani
    @param {Object} target
4320 e4a6c3b8 Olga Brani
    @param {Function} becameInvalid
4321 e4a6c3b8 Olga Brani
    @param {Function} becameValid
4322 e4a6c3b8 Olga Brani
  */
4323 e4a6c3b8 Olga Brani
  registerHandlers: function(target, becameInvalid, becameValid) {
4324 e4a6c3b8 Olga Brani
    this.on('becameInvalid', target, becameInvalid);
4325 e4a6c3b8 Olga Brani
    this.on('becameValid', target, becameValid);
4326 e4a6c3b8 Olga Brani
  },
4327 e4a6c3b8 Olga Brani
4328 e4a6c3b8 Olga Brani
  /**
4329 e4a6c3b8 Olga Brani
    @property errorsByAttributeName
4330 e4a6c3b8 Olga Brani
    @type {Ember.MapWithDefault}
4331 e4a6c3b8 Olga Brani
    @private
4332 e4a6c3b8 Olga Brani
  */
4333 e4a6c3b8 Olga Brani
  errorsByAttributeName: Ember.reduceComputed("content", {
4334 e4a6c3b8 Olga Brani
    initialValue: function() {
4335 e4a6c3b8 Olga Brani
      return Ember.MapWithDefault.create({
4336 e4a6c3b8 Olga Brani
        defaultValue: function() {
4337 e4a6c3b8 Olga Brani
          return Ember.A();
4338 e4a6c3b8 Olga Brani
        }
4339 e4a6c3b8 Olga Brani
      });
4340 e4a6c3b8 Olga Brani
    },
4341 e4a6c3b8 Olga Brani
4342 e4a6c3b8 Olga Brani
    addedItem: function(errors, error) {
4343 e4a6c3b8 Olga Brani
      errors.get(error.attribute).pushObject(error);
4344 e4a6c3b8 Olga Brani
4345 e4a6c3b8 Olga Brani
      return errors;
4346 e4a6c3b8 Olga Brani
    },
4347 e4a6c3b8 Olga Brani
4348 e4a6c3b8 Olga Brani
    removedItem: function(errors, error) {
4349 e4a6c3b8 Olga Brani
      errors.get(error.attribute).removeObject(error);
4350 e4a6c3b8 Olga Brani
4351 e4a6c3b8 Olga Brani
      return errors;
4352 e4a6c3b8 Olga Brani
    }
4353 e4a6c3b8 Olga Brani
  }),
4354 e4a6c3b8 Olga Brani
4355 e4a6c3b8 Olga Brani
  /**
4356 e4a6c3b8 Olga Brani
    Returns errors for a given attribute
4357 e4a6c3b8 Olga Brani

4358 e4a6c3b8 Olga Brani
    @method errorsFor
4359 e4a6c3b8 Olga Brani
    @param {String} attribute
4360 e4a6c3b8 Olga Brani
    @returns {Array}
4361 e4a6c3b8 Olga Brani
  */
4362 e4a6c3b8 Olga Brani
  errorsFor: function(attribute) {
4363 e4a6c3b8 Olga Brani
    return get(this, 'errorsByAttributeName').get(attribute);
4364 e4a6c3b8 Olga Brani
  },
4365 e4a6c3b8 Olga Brani
4366 e4a6c3b8 Olga Brani
  /**
4367 e4a6c3b8 Olga Brani
  */
4368 e4a6c3b8 Olga Brani
  messages: Ember.computed.mapBy('content', 'message'),
4369 e4a6c3b8 Olga Brani
4370 e4a6c3b8 Olga Brani
  /**
4371 e4a6c3b8 Olga Brani
    @property content
4372 e4a6c3b8 Olga Brani
    @type {Array}
4373 e4a6c3b8 Olga Brani
    @private
4374 e4a6c3b8 Olga Brani
  */
4375 e4a6c3b8 Olga Brani
  content: Ember.computed(function() {
4376 e4a6c3b8 Olga Brani
    return Ember.A();
4377 e4a6c3b8 Olga Brani
  }),
4378 e4a6c3b8 Olga Brani
4379 e4a6c3b8 Olga Brani
  /**
4380 e4a6c3b8 Olga Brani
    @method unknownProperty
4381 e4a6c3b8 Olga Brani
    @private
4382 e4a6c3b8 Olga Brani
  */
4383 e4a6c3b8 Olga Brani
  unknownProperty: function(attribute) {
4384 e4a6c3b8 Olga Brani
    var errors = this.errorsFor(attribute);
4385 e4a6c3b8 Olga Brani
    if (isEmpty(errors)) { return null; }
4386 e4a6c3b8 Olga Brani
    return errors;
4387 e4a6c3b8 Olga Brani
  },
4388 e4a6c3b8 Olga Brani
4389 e4a6c3b8 Olga Brani
  /**
4390 e4a6c3b8 Olga Brani
    @method nextObject
4391 e4a6c3b8 Olga Brani
    @private
4392 e4a6c3b8 Olga Brani
  */
4393 e4a6c3b8 Olga Brani
  nextObject: function(index, previousObject, context) {
4394 e4a6c3b8 Olga Brani
    return get(this, 'content').objectAt(index);
4395 e4a6c3b8 Olga Brani
  },
4396 e4a6c3b8 Olga Brani
4397 e4a6c3b8 Olga Brani
  /**
4398 e4a6c3b8 Olga Brani
    Total number of errors.
4399 e4a6c3b8 Olga Brani

4400 e4a6c3b8 Olga Brani
    @property length
4401 e4a6c3b8 Olga Brani
    @type {Number}
4402 e4a6c3b8 Olga Brani
    @readOnly
4403 e4a6c3b8 Olga Brani
  */
4404 e4a6c3b8 Olga Brani
  length: Ember.computed.oneWay('content.length').readOnly(),
4405 e4a6c3b8 Olga Brani
4406 e4a6c3b8 Olga Brani
  /**
4407 e4a6c3b8 Olga Brani
    @property isEmpty
4408 e4a6c3b8 Olga Brani
    @type {Boolean}
4409 e4a6c3b8 Olga Brani
    @readOnly
4410 e4a6c3b8 Olga Brani
  */
4411 e4a6c3b8 Olga Brani
  isEmpty: Ember.computed.not('length').readOnly(),
4412 e4a6c3b8 Olga Brani
4413 e4a6c3b8 Olga Brani
  /**
4414 e4a6c3b8 Olga Brani
    Adds error messages to a given attribute and sends
4415 e4a6c3b8 Olga Brani
    `becameInvalid` event to the record.
4416 e4a6c3b8 Olga Brani

4417 e4a6c3b8 Olga Brani
    @method add
4418 e4a6c3b8 Olga Brani
    @param {String} attribute
4419 e4a6c3b8 Olga Brani
    @param {Array|String} messages
4420 e4a6c3b8 Olga Brani
  */
4421 e4a6c3b8 Olga Brani
  add: function(attribute, messages) {
4422 e4a6c3b8 Olga Brani
    var wasEmpty = get(this, 'isEmpty');
4423 e4a6c3b8 Olga Brani
4424 e4a6c3b8 Olga Brani
    messages = this._findOrCreateMessages(attribute, messages);
4425 e4a6c3b8 Olga Brani
    get(this, 'content').addObjects(messages);
4426 e4a6c3b8 Olga Brani
4427 e4a6c3b8 Olga Brani
    this.notifyPropertyChange(attribute);
4428 e4a6c3b8 Olga Brani
    this.enumerableContentDidChange();
4429 e4a6c3b8 Olga Brani
4430 e4a6c3b8 Olga Brani
    if (wasEmpty && !get(this, 'isEmpty')) {
4431 e4a6c3b8 Olga Brani
      this.trigger('becameInvalid');
4432 e4a6c3b8 Olga Brani
    }
4433 e4a6c3b8 Olga Brani
  },
4434 e4a6c3b8 Olga Brani
4435 e4a6c3b8 Olga Brani
  /**
4436 e4a6c3b8 Olga Brani
    @method _findOrCreateMessages
4437 e4a6c3b8 Olga Brani
    @private
4438 e4a6c3b8 Olga Brani
  */
4439 e4a6c3b8 Olga Brani
  _findOrCreateMessages: function(attribute, messages) {
4440 e4a6c3b8 Olga Brani
    var errors = this.errorsFor(attribute);
4441 e4a6c3b8 Olga Brani
4442 e4a6c3b8 Olga Brani
    return Ember.makeArray(messages).map(function(message) {
4443 e4a6c3b8 Olga Brani
      return errors.findBy('message', message) || {
4444 e4a6c3b8 Olga Brani
        attribute: attribute,
4445 e4a6c3b8 Olga Brani
        message: message
4446 e4a6c3b8 Olga Brani
      };
4447 e4a6c3b8 Olga Brani
    });
4448 e4a6c3b8 Olga Brani
  },
4449 e4a6c3b8 Olga Brani
4450 e4a6c3b8 Olga Brani
  /**
4451 e4a6c3b8 Olga Brani
    Removes all error messages from the given attribute and sends
4452 e4a6c3b8 Olga Brani
    `becameValid` event to the record if there no more errors left.
4453 e4a6c3b8 Olga Brani

4454 e4a6c3b8 Olga Brani
    @method remove
4455 e4a6c3b8 Olga Brani
    @param {String} attribute
4456 e4a6c3b8 Olga Brani
  */
4457 e4a6c3b8 Olga Brani
  remove: function(attribute) {
4458 e4a6c3b8 Olga Brani
    if (get(this, 'isEmpty')) { return; }
4459 e4a6c3b8 Olga Brani
4460 e4a6c3b8 Olga Brani
    var content = get(this, 'content').rejectBy('attribute', attribute);
4461 e4a6c3b8 Olga Brani
    get(this, 'content').setObjects(content);
4462 e4a6c3b8 Olga Brani
4463 e4a6c3b8 Olga Brani
    this.notifyPropertyChange(attribute);
4464 e4a6c3b8 Olga Brani
    this.enumerableContentDidChange();
4465 e4a6c3b8 Olga Brani
4466 e4a6c3b8 Olga Brani
    if (get(this, 'isEmpty')) {
4467 e4a6c3b8 Olga Brani
      this.trigger('becameValid');
4468 e4a6c3b8 Olga Brani
    }
4469 e4a6c3b8 Olga Brani
  },
4470 e4a6c3b8 Olga Brani
4471 e4a6c3b8 Olga Brani
  /**
4472 e4a6c3b8 Olga Brani
    Removes all error messages and sends `becameValid` event
4473 e4a6c3b8 Olga Brani
    to the record.
4474 e4a6c3b8 Olga Brani

4475 e4a6c3b8 Olga Brani
    @method clear
4476 e4a6c3b8 Olga Brani
  */
4477 e4a6c3b8 Olga Brani
  clear: function() {
4478 e4a6c3b8 Olga Brani
    if (get(this, 'isEmpty')) { return; }
4479 e4a6c3b8 Olga Brani
4480 e4a6c3b8 Olga Brani
    get(this, 'content').clear();
4481 e4a6c3b8 Olga Brani
    this.enumerableContentDidChange();
4482 e4a6c3b8 Olga Brani
4483 e4a6c3b8 Olga Brani
    this.trigger('becameValid');
4484 e4a6c3b8 Olga Brani
  },
4485 e4a6c3b8 Olga Brani
4486 e4a6c3b8 Olga Brani
  /**
4487 e4a6c3b8 Olga Brani
    Checks if there is error messages for the given attribute.
4488 e4a6c3b8 Olga Brani

4489 e4a6c3b8 Olga Brani
    @method has
4490 e4a6c3b8 Olga Brani
    @param {String} attribute
4491 e4a6c3b8 Olga Brani
    @returns {Boolean} true if there some errors on given attribute
4492 e4a6c3b8 Olga Brani
  */
4493 e4a6c3b8 Olga Brani
  has: function(attribute) {
4494 e4a6c3b8 Olga Brani
    return !isEmpty(this.errorsFor(attribute));
4495 e4a6c3b8 Olga Brani
  }
4496 e4a6c3b8 Olga Brani
});
4497 e4a6c3b8 Olga Brani
4498 e4a6c3b8 Olga Brani
})();
4499 e4a6c3b8 Olga Brani
4500 e4a6c3b8 Olga Brani
4501 e4a6c3b8 Olga Brani
4502 e4a6c3b8 Olga Brani
(function() {
4503 e4a6c3b8 Olga Brani
/**
4504 e4a6c3b8 Olga Brani
  @module ember-data
4505 e4a6c3b8 Olga Brani
*/
4506 e4a6c3b8 Olga Brani
4507 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set,
4508 e4a6c3b8 Olga Brani
    merge = Ember.merge, once = Ember.run.once;
4509 e4a6c3b8 Olga Brani
4510 e4a6c3b8 Olga Brani
var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) {
4511 e4a6c3b8 Olga Brani
  return get(get(this, 'currentState'), key);
4512 e4a6c3b8 Olga Brani
}).readOnly();
4513 e4a6c3b8 Olga Brani
4514 e4a6c3b8 Olga Brani
/**
4515 e4a6c3b8 Olga Brani

4516 e4a6c3b8 Olga Brani
  The model class that all Ember Data records descend from.
4517 e4a6c3b8 Olga Brani

4518 e4a6c3b8 Olga Brani
  @class Model
4519 e4a6c3b8 Olga Brani
  @namespace DS
4520 e4a6c3b8 Olga Brani
  @extends Ember.Object
4521 e4a6c3b8 Olga Brani
  @uses Ember.Evented
4522 e4a6c3b8 Olga Brani
*/
4523 e4a6c3b8 Olga Brani
DS.Model = Ember.Object.extend(Ember.Evented, {
4524 e4a6c3b8 Olga Brani
  /**
4525 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `empty`
4526 e4a6c3b8 Olga Brani
    state. Empty is the first state all records enter after they have
4527 e4a6c3b8 Olga Brani
    been created. Most records created by the store will quickly
4528 e4a6c3b8 Olga Brani
    transition to the `loading` state if data needs to be fetched from
4529 e4a6c3b8 Olga Brani
    the server or the `created` state if the record is created on the
4530 e4a6c3b8 Olga Brani
    client. A record can also enter the empty state if the adapter is
4531 e4a6c3b8 Olga Brani
    unable to locate the record.
4532 e4a6c3b8 Olga Brani

4533 e4a6c3b8 Olga Brani
    @property isEmpty
4534 e4a6c3b8 Olga Brani
    @type {Boolean}
4535 e4a6c3b8 Olga Brani
    @readOnly
4536 e4a6c3b8 Olga Brani
  */
4537 e4a6c3b8 Olga Brani
  isEmpty: retrieveFromCurrentState,
4538 e4a6c3b8 Olga Brani
  /**
4539 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `loading` state. A
4540 e4a6c3b8 Olga Brani
    record enters this state when the store askes the adapter for its
4541 e4a6c3b8 Olga Brani
    data. It remains in this state until the adapter provides the
4542 e4a6c3b8 Olga Brani
    requested data.
4543 e4a6c3b8 Olga Brani

4544 e4a6c3b8 Olga Brani
    @property isLoading
4545 e4a6c3b8 Olga Brani
    @type {Boolean}
4546 e4a6c3b8 Olga Brani
    @readOnly
4547 e4a6c3b8 Olga Brani
  */
4548 e4a6c3b8 Olga Brani
  isLoading: retrieveFromCurrentState,
4549 e4a6c3b8 Olga Brani
  /**
4550 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `loaded` state. A
4551 e4a6c3b8 Olga Brani
    record enters this state when its data is populated. Most of a
4552 e4a6c3b8 Olga Brani
    record's lifecycle is spent inside substates of the `loaded`
4553 e4a6c3b8 Olga Brani
    state.
4554 e4a6c3b8 Olga Brani

4555 e4a6c3b8 Olga Brani
    Example
4556 e4a6c3b8 Olga Brani

4557 e4a6c3b8 Olga Brani
    ```javascript
4558 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4559 e4a6c3b8 Olga Brani
    record.get('isLoaded'); // true
4560 e4a6c3b8 Olga Brani

4561 e4a6c3b8 Olga Brani
    store.find('model', 1).then(function(model) {
4562 e4a6c3b8 Olga Brani
      model.get('isLoaded'); // true
4563 e4a6c3b8 Olga Brani
    });
4564 e4a6c3b8 Olga Brani
    ```
4565 e4a6c3b8 Olga Brani

4566 e4a6c3b8 Olga Brani
    @property isLoaded
4567 e4a6c3b8 Olga Brani
    @type {Boolean}
4568 e4a6c3b8 Olga Brani
    @readOnly
4569 e4a6c3b8 Olga Brani
  */
4570 e4a6c3b8 Olga Brani
  isLoaded: retrieveFromCurrentState,
4571 e4a6c3b8 Olga Brani
  /**
4572 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `dirty` state. The
4573 e4a6c3b8 Olga Brani
    record has local changes that have not yet been saved by the
4574 e4a6c3b8 Olga Brani
    adapter. This includes records that have been created (but not yet
4575 e4a6c3b8 Olga Brani
    saved) or deleted.
4576 e4a6c3b8 Olga Brani

4577 e4a6c3b8 Olga Brani
    Example
4578 e4a6c3b8 Olga Brani

4579 e4a6c3b8 Olga Brani
    ```javascript
4580 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4581 e4a6c3b8 Olga Brani
    record.get('isDirty'); // true
4582 e4a6c3b8 Olga Brani

4583 e4a6c3b8 Olga Brani
    store.find('model', 1).then(function(model) {
4584 e4a6c3b8 Olga Brani
      model.get('isDirty'); // false
4585 e4a6c3b8 Olga Brani
      model.set('foo', 'some value');
4586 e4a6c3b8 Olga Brani
      model.set('isDirty'); // true
4587 e4a6c3b8 Olga Brani
    });
4588 e4a6c3b8 Olga Brani
    ```
4589 e4a6c3b8 Olga Brani

4590 e4a6c3b8 Olga Brani
    @property isDirty
4591 e4a6c3b8 Olga Brani
    @type {Boolean}
4592 e4a6c3b8 Olga Brani
    @readOnly
4593 e4a6c3b8 Olga Brani
  */
4594 e4a6c3b8 Olga Brani
  isDirty: retrieveFromCurrentState,
4595 e4a6c3b8 Olga Brani
  /**
4596 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `saving` state. A
4597 e4a6c3b8 Olga Brani
    record enters the saving state when `save` is called, but the
4598 e4a6c3b8 Olga Brani
    adapter has not yet acknowledged that the changes have been
4599 e4a6c3b8 Olga Brani
    persisted to the backend.
4600 e4a6c3b8 Olga Brani

4601 e4a6c3b8 Olga Brani
    Example
4602 e4a6c3b8 Olga Brani

4603 e4a6c3b8 Olga Brani
    ```javascript
4604 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4605 e4a6c3b8 Olga Brani
    record.get('isSaving'); // false
4606 e4a6c3b8 Olga Brani
    var promise = record.save();
4607 e4a6c3b8 Olga Brani
    record.get('isSaving'); // true
4608 e4a6c3b8 Olga Brani
    promise.then(function() {
4609 e4a6c3b8 Olga Brani
      record.get('isSaving'); // false
4610 e4a6c3b8 Olga Brani
    });
4611 e4a6c3b8 Olga Brani
    ```
4612 e4a6c3b8 Olga Brani

4613 e4a6c3b8 Olga Brani
    @property isSaving
4614 e4a6c3b8 Olga Brani
    @type {Boolean}
4615 e4a6c3b8 Olga Brani
    @readOnly
4616 e4a6c3b8 Olga Brani
  */
4617 e4a6c3b8 Olga Brani
  isSaving: retrieveFromCurrentState,
4618 e4a6c3b8 Olga Brani
  /**
4619 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `deleted` state
4620 e4a6c3b8 Olga Brani
    and has been marked for deletion. When `isDeleted` is true and
4621 e4a6c3b8 Olga Brani
    `isDirty` is true, the record is deleted locally but the deletion
4622 e4a6c3b8 Olga Brani
    was not yet persisted. When `isSaving` is true, the change is
4623 e4a6c3b8 Olga Brani
    in-flight. When both `isDirty` and `isSaving` are false, the
4624 e4a6c3b8 Olga Brani
    change has persisted.
4625 e4a6c3b8 Olga Brani

4626 e4a6c3b8 Olga Brani
    Example
4627 e4a6c3b8 Olga Brani

4628 e4a6c3b8 Olga Brani
    ```javascript
4629 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4630 e4a6c3b8 Olga Brani
    record.get('isDeleted'); // false
4631 e4a6c3b8 Olga Brani
    record.deleteRecord();
4632 e4a6c3b8 Olga Brani
    record.get('isDeleted'); // true
4633 e4a6c3b8 Olga Brani
    ```
4634 e4a6c3b8 Olga Brani

4635 e4a6c3b8 Olga Brani
    @property isDeleted
4636 e4a6c3b8 Olga Brani
    @type {Boolean}
4637 e4a6c3b8 Olga Brani
    @readOnly
4638 e4a6c3b8 Olga Brani
  */
4639 e4a6c3b8 Olga Brani
  isDeleted: retrieveFromCurrentState,
4640 e4a6c3b8 Olga Brani
  /**
4641 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `new` state. A
4642 e4a6c3b8 Olga Brani
    record will be in the `new` state when it has been created on the
4643 e4a6c3b8 Olga Brani
    client and the adapter has not yet report that it was successfully
4644 e4a6c3b8 Olga Brani
    saved.
4645 e4a6c3b8 Olga Brani

4646 e4a6c3b8 Olga Brani
    Example
4647 e4a6c3b8 Olga Brani

4648 e4a6c3b8 Olga Brani
    ```javascript
4649 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4650 e4a6c3b8 Olga Brani
    record.get('isNew'); // true
4651 e4a6c3b8 Olga Brani

4652 e4a6c3b8 Olga Brani
    store.find('model', 1).then(function(model) {
4653 e4a6c3b8 Olga Brani
      model.get('isNew'); // false
4654 e4a6c3b8 Olga Brani
    });
4655 e4a6c3b8 Olga Brani
    ```
4656 e4a6c3b8 Olga Brani

4657 e4a6c3b8 Olga Brani
    @property isNew
4658 e4a6c3b8 Olga Brani
    @type {Boolean}
4659 e4a6c3b8 Olga Brani
    @readOnly
4660 e4a6c3b8 Olga Brani
  */
4661 e4a6c3b8 Olga Brani
  isNew: retrieveFromCurrentState,
4662 e4a6c3b8 Olga Brani
  /**
4663 e4a6c3b8 Olga Brani
    If this property is `true` the record is in the `valid` state. A
4664 e4a6c3b8 Olga Brani
    record will be in the `valid` state when no client-side
4665 e4a6c3b8 Olga Brani
    validations have failed and the adapter did not report any
4666 e4a6c3b8 Olga Brani
    server-side validation failures.
4667 e4a6c3b8 Olga Brani

4668 e4a6c3b8 Olga Brani
    @property isValid
4669 e4a6c3b8 Olga Brani
    @type {Boolean}
4670 e4a6c3b8 Olga Brani
    @readOnly
4671 e4a6c3b8 Olga Brani
  */
4672 e4a6c3b8 Olga Brani
  isValid: retrieveFromCurrentState,
4673 e4a6c3b8 Olga Brani
  /**
4674 e4a6c3b8 Olga Brani
    If the record is in the dirty state this property will report what
4675 e4a6c3b8 Olga Brani
    kind of change has caused it to move into the dirty
4676 e4a6c3b8 Olga Brani
    state. Possible values are:
4677 e4a6c3b8 Olga Brani

4678 e4a6c3b8 Olga Brani
    - `created` The record has been created by the client and not yet saved to the adapter.
4679 e4a6c3b8 Olga Brani
    - `updated` The record has been updated by the client and not yet saved to the adapter.
4680 e4a6c3b8 Olga Brani
    - `deleted` The record has been deleted by the client and not yet saved to the adapter.
4681 e4a6c3b8 Olga Brani

4682 e4a6c3b8 Olga Brani
    Example
4683 e4a6c3b8 Olga Brani

4684 e4a6c3b8 Olga Brani
    ```javascript
4685 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4686 e4a6c3b8 Olga Brani
    record.get('dirtyType'); // 'created'
4687 e4a6c3b8 Olga Brani
    ```
4688 e4a6c3b8 Olga Brani

4689 e4a6c3b8 Olga Brani
    @property dirtyType
4690 e4a6c3b8 Olga Brani
    @type {String}
4691 e4a6c3b8 Olga Brani
    @readOnly
4692 e4a6c3b8 Olga Brani
  */
4693 e4a6c3b8 Olga Brani
  dirtyType: retrieveFromCurrentState,
4694 e4a6c3b8 Olga Brani
4695 e4a6c3b8 Olga Brani
  /**
4696 e4a6c3b8 Olga Brani
    If `true` the adapter reported that it was unable to save local
4697 e4a6c3b8 Olga Brani
    changes to the backend. This may also result in the record having
4698 e4a6c3b8 Olga Brani
    its `isValid` property become false if the adapter reported that
4699 e4a6c3b8 Olga Brani
    server-side validations failed.
4700 e4a6c3b8 Olga Brani

4701 e4a6c3b8 Olga Brani
    Example
4702 e4a6c3b8 Olga Brani

4703 e4a6c3b8 Olga Brani
    ```javascript
4704 e4a6c3b8 Olga Brani
    record.get('isError'); // false
4705 e4a6c3b8 Olga Brani
    record.set('foo', 'invalid value');
4706 e4a6c3b8 Olga Brani
    record.save().then(null, function() {
4707 e4a6c3b8 Olga Brani
      record.get('isError'); // true
4708 e4a6c3b8 Olga Brani
    });
4709 e4a6c3b8 Olga Brani
    ```
4710 e4a6c3b8 Olga Brani

4711 e4a6c3b8 Olga Brani
    @property isError
4712 e4a6c3b8 Olga Brani
    @type {Boolean}
4713 e4a6c3b8 Olga Brani
    @readOnly
4714 e4a6c3b8 Olga Brani
  */
4715 e4a6c3b8 Olga Brani
  isError: false,
4716 e4a6c3b8 Olga Brani
  /**
4717 e4a6c3b8 Olga Brani
    If `true` the store is attempting to reload the record form the adapter.
4718 e4a6c3b8 Olga Brani

4719 e4a6c3b8 Olga Brani
    Example
4720 e4a6c3b8 Olga Brani

4721 e4a6c3b8 Olga Brani
    ```javascript
4722 e4a6c3b8 Olga Brani
    record.get('isReloading'); // false
4723 e4a6c3b8 Olga Brani
    record.reload();
4724 e4a6c3b8 Olga Brani
    record.get('isReloading'); // true
4725 e4a6c3b8 Olga Brani
    ```
4726 e4a6c3b8 Olga Brani

4727 e4a6c3b8 Olga Brani
    @property isReloading
4728 e4a6c3b8 Olga Brani
    @type {Boolean}
4729 e4a6c3b8 Olga Brani
    @readOnly
4730 e4a6c3b8 Olga Brani
  */
4731 e4a6c3b8 Olga Brani
  isReloading: false,
4732 e4a6c3b8 Olga Brani
4733 e4a6c3b8 Olga Brani
  /**
4734 e4a6c3b8 Olga Brani
    The `clientId` property is a transient numerical identifier
4735 e4a6c3b8 Olga Brani
    generated at runtime by the data store. It is important
4736 e4a6c3b8 Olga Brani
    primarily because newly created objects may not yet have an
4737 e4a6c3b8 Olga Brani
    externally generated id.
4738 e4a6c3b8 Olga Brani

4739 e4a6c3b8 Olga Brani
    @property clientId
4740 e4a6c3b8 Olga Brani
    @private
4741 e4a6c3b8 Olga Brani
    @type {Number|String}
4742 e4a6c3b8 Olga Brani
  */
4743 e4a6c3b8 Olga Brani
  clientId: null,
4744 e4a6c3b8 Olga Brani
  /**
4745 e4a6c3b8 Olga Brani
    All ember models have an id property. This is an identifier
4746 e4a6c3b8 Olga Brani
    managed by an external source. These are always coerced to be
4747 e4a6c3b8 Olga Brani
    strings before being used internally. Note when declaring the
4748 e4a6c3b8 Olga Brani
    attributes for a model it is an error to declare an id
4749 e4a6c3b8 Olga Brani
    attribute.
4750 e4a6c3b8 Olga Brani

4751 e4a6c3b8 Olga Brani
    ```javascript
4752 e4a6c3b8 Olga Brani
    var record = store.createRecord(App.Model);
4753 e4a6c3b8 Olga Brani
    record.get('id'); // null
4754 e4a6c3b8 Olga Brani

4755 e4a6c3b8 Olga Brani
    store.find('model', 1).then(function(model) {
4756 e4a6c3b8 Olga Brani
      model.get('id'); // '1'
4757 e4a6c3b8 Olga Brani
    });
4758 e4a6c3b8 Olga Brani
    ```
4759 e4a6c3b8 Olga Brani

4760 e4a6c3b8 Olga Brani
    @property id
4761 e4a6c3b8 Olga Brani
    @type {String}
4762 e4a6c3b8 Olga Brani
  */
4763 e4a6c3b8 Olga Brani
  id: null,
4764 e4a6c3b8 Olga Brani
  transaction: null,
4765 e4a6c3b8 Olga Brani
  /**
4766 e4a6c3b8 Olga Brani
    @property currentState
4767 e4a6c3b8 Olga Brani
    @private
4768 e4a6c3b8 Olga Brani
    @type {Object}
4769 e4a6c3b8 Olga Brani
  */
4770 e4a6c3b8 Olga Brani
  currentState: null,
4771 e4a6c3b8 Olga Brani
  /**
4772 e4a6c3b8 Olga Brani
    When the record is in the `invalid` state this object will contain
4773 e4a6c3b8 Olga Brani
    any errors returned by the adapter. When present the errors hash
4774 e4a6c3b8 Olga Brani
    typically contains keys coresponding to the invalid property names
4775 e4a6c3b8 Olga Brani
    and values which are an array of error messages.
4776 e4a6c3b8 Olga Brani

4777 e4a6c3b8 Olga Brani
    ```javascript
4778 e4a6c3b8 Olga Brani
    record.get('errors.length'); // 0
4779 e4a6c3b8 Olga Brani
    record.set('foo', 'invalid value');
4780 e4a6c3b8 Olga Brani
    record.save().then(null, function() {
4781 e4a6c3b8 Olga Brani
      record.get('errors').get('foo'); // ['foo should be a number.']
4782 e4a6c3b8 Olga Brani
    });
4783 e4a6c3b8 Olga Brani
    ```
4784 e4a6c3b8 Olga Brani

4785 e4a6c3b8 Olga Brani
    @property errors
4786 e4a6c3b8 Olga Brani
    @type {Object}
4787 e4a6c3b8 Olga Brani
  */
4788 e4a6c3b8 Olga Brani
  errors: null,
4789 e4a6c3b8 Olga Brani
4790 e4a6c3b8 Olga Brani
  /**
4791 e4a6c3b8 Olga Brani
    Create a JSON representation of the record, using the serialization
4792 e4a6c3b8 Olga Brani
    strategy of the store's adapter.
4793 e4a6c3b8 Olga Brani

4794 e4a6c3b8 Olga Brani
   `serialize` takes an optional hash as a parameter, currently
4795 e4a6c3b8 Olga Brani
    supported options are:
4796 e4a6c3b8 Olga Brani

4797 e4a6c3b8 Olga Brani
   - `includeId`: `true` if the record's ID should be included in the
4798 e4a6c3b8 Olga Brani
      JSON representation.
4799 e4a6c3b8 Olga Brani

4800 e4a6c3b8 Olga Brani
    @method serialize
4801 e4a6c3b8 Olga Brani
    @param {Object} options
4802 e4a6c3b8 Olga Brani
    @returns {Object} an object whose values are primitive JSON values only
4803 e4a6c3b8 Olga Brani
  */
4804 e4a6c3b8 Olga Brani
  serialize: function(options) {
4805 e4a6c3b8 Olga Brani
    var store = get(this, 'store');
4806 e4a6c3b8 Olga Brani
    return store.serialize(this, options);
4807 e4a6c3b8 Olga Brani
  },
4808 e4a6c3b8 Olga Brani
4809 e4a6c3b8 Olga Brani
  /**
4810 e4a6c3b8 Olga Brani
    Use [DS.JSONSerializer](DS.JSONSerializer.html) to
4811 e4a6c3b8 Olga Brani
    get the JSON representation of a record.
4812 e4a6c3b8 Olga Brani

4813 e4a6c3b8 Olga Brani
    `toJSON` takes an optional hash as a parameter, currently
4814 e4a6c3b8 Olga Brani
    supported options are:
4815 e4a6c3b8 Olga Brani

4816 e4a6c3b8 Olga Brani
    - `includeId`: `true` if the record's ID should be included in the
4817 e4a6c3b8 Olga Brani
      JSON representation.
4818 e4a6c3b8 Olga Brani

4819 e4a6c3b8 Olga Brani
    @method toJSON
4820 e4a6c3b8 Olga Brani
    @param {Object} options
4821 e4a6c3b8 Olga Brani
    @returns {Object} A JSON representation of the object.
4822 e4a6c3b8 Olga Brani
  */
4823 e4a6c3b8 Olga Brani
  toJSON: function(options) {
4824 e4a6c3b8 Olga Brani
    // container is for lazy transform lookups
4825 e4a6c3b8 Olga Brani
    var serializer = DS.JSONSerializer.create({ container: this.container });
4826 e4a6c3b8 Olga Brani
    return serializer.serialize(this, options);
4827 e4a6c3b8 Olga Brani
  },
4828 e4a6c3b8 Olga Brani
4829 e4a6c3b8 Olga Brani
  /**
4830 e4a6c3b8 Olga Brani
    Fired when the record is loaded from the server.
4831 e4a6c3b8 Olga Brani

4832 e4a6c3b8 Olga Brani
    @event didLoad
4833 e4a6c3b8 Olga Brani
  */
4834 e4a6c3b8 Olga Brani
  didLoad: Ember.K,
4835 e4a6c3b8 Olga Brani
4836 e4a6c3b8 Olga Brani
  /**
4837 e4a6c3b8 Olga Brani
    Fired when the record is updated.
4838 e4a6c3b8 Olga Brani

4839 e4a6c3b8 Olga Brani
    @event didUpdate
4840 e4a6c3b8 Olga Brani
  */
4841 e4a6c3b8 Olga Brani
  didUpdate: Ember.K,
4842 e4a6c3b8 Olga Brani
4843 e4a6c3b8 Olga Brani
  /**
4844 e4a6c3b8 Olga Brani
    Fired when the record is created.
4845 e4a6c3b8 Olga Brani

4846 e4a6c3b8 Olga Brani
    @event didCreate
4847 e4a6c3b8 Olga Brani
  */
4848 e4a6c3b8 Olga Brani
  didCreate: Ember.K,
4849 e4a6c3b8 Olga Brani
4850 e4a6c3b8 Olga Brani
  /**
4851 e4a6c3b8 Olga Brani
    Fired when the record is deleted.
4852 e4a6c3b8 Olga Brani

4853 e4a6c3b8 Olga Brani
    @event didDelete
4854 e4a6c3b8 Olga Brani
  */
4855 e4a6c3b8 Olga Brani
  didDelete: Ember.K,
4856 e4a6c3b8 Olga Brani
4857 e4a6c3b8 Olga Brani
  /**
4858 e4a6c3b8 Olga Brani
    Fired when the record becomes invalid.
4859 e4a6c3b8 Olga Brani

4860 e4a6c3b8 Olga Brani
    @event becameInvalid
4861 e4a6c3b8 Olga Brani
  */
4862 e4a6c3b8 Olga Brani
  becameInvalid: Ember.K,
4863 e4a6c3b8 Olga Brani
4864 e4a6c3b8 Olga Brani
  /**
4865 e4a6c3b8 Olga Brani
    Fired when the record enters the error state.
4866 e4a6c3b8 Olga Brani

4867 e4a6c3b8 Olga Brani
    @event becameError
4868 e4a6c3b8 Olga Brani
  */
4869 e4a6c3b8 Olga Brani
  becameError: Ember.K,
4870 e4a6c3b8 Olga Brani
4871 e4a6c3b8 Olga Brani
  /**
4872 e4a6c3b8 Olga Brani
    @property data
4873 e4a6c3b8 Olga Brani
    @private
4874 e4a6c3b8 Olga Brani
    @type {Object}
4875 e4a6c3b8 Olga Brani
  */
4876 e4a6c3b8 Olga Brani
  data: Ember.computed(function() {
4877 e4a6c3b8 Olga Brani
    this._data = this._data || {};
4878 e4a6c3b8 Olga Brani
    return this._data;
4879 e4a6c3b8 Olga Brani
  }).property(),
4880 e4a6c3b8 Olga Brani
4881 e4a6c3b8 Olga Brani
  _data: null,
4882 e4a6c3b8 Olga Brani
4883 e4a6c3b8 Olga Brani
  init: function() {
4884 e4a6c3b8 Olga Brani
    set(this, 'currentState', DS.RootState.empty);
4885 e4a6c3b8 Olga Brani
    var errors = DS.Errors.create();
4886 e4a6c3b8 Olga Brani
    errors.registerHandlers(this, function() {
4887 e4a6c3b8 Olga Brani
      this.send('becameInvalid');
4888 e4a6c3b8 Olga Brani
    }, function() {
4889 e4a6c3b8 Olga Brani
      this.send('becameValid');
4890 e4a6c3b8 Olga Brani
    });
4891 e4a6c3b8 Olga Brani
    set(this, 'errors', errors);
4892 e4a6c3b8 Olga Brani
    this._super();
4893 e4a6c3b8 Olga Brani
    this._setup();
4894 e4a6c3b8 Olga Brani
  },
4895 e4a6c3b8 Olga Brani
4896 e4a6c3b8 Olga Brani
  _setup: function() {
4897 e4a6c3b8 Olga Brani
    this._changesToSync = {};
4898 e4a6c3b8 Olga Brani
    this._deferredTriggers = [];
4899 e4a6c3b8 Olga Brani
    this._data = {};
4900 e4a6c3b8 Olga Brani
    this._attributes = {};
4901 e4a6c3b8 Olga Brani
    this._inFlightAttributes = {};
4902 e4a6c3b8 Olga Brani
    this._relationships = {};
4903 e4a6c3b8 Olga Brani
  },
4904 e4a6c3b8 Olga Brani
4905 e4a6c3b8 Olga Brani
  /**
4906 e4a6c3b8 Olga Brani
    @method send
4907 e4a6c3b8 Olga Brani
    @private
4908 e4a6c3b8 Olga Brani
    @param {String} name
4909 e4a6c3b8 Olga Brani
    @param {Object} context
4910 e4a6c3b8 Olga Brani
  */
4911 e4a6c3b8 Olga Brani
  send: function(name, context) {
4912 e4a6c3b8 Olga Brani
    var currentState = get(this, 'currentState');
4913 e4a6c3b8 Olga Brani
4914 e4a6c3b8 Olga Brani
    if (!currentState[name]) {
4915 e4a6c3b8 Olga Brani
      this._unhandledEvent(currentState, name, context);
4916 e4a6c3b8 Olga Brani
    }
4917 e4a6c3b8 Olga Brani
4918 e4a6c3b8 Olga Brani
    return currentState[name](this, context);
4919 e4a6c3b8 Olga Brani
  },
4920 e4a6c3b8 Olga Brani
4921 e4a6c3b8 Olga Brani
  /**
4922 e4a6c3b8 Olga Brani
    @method transitionTo
4923 e4a6c3b8 Olga Brani
    @private
4924 e4a6c3b8 Olga Brani
    @param {String} name
4925 e4a6c3b8 Olga Brani
  */
4926 e4a6c3b8 Olga Brani
  transitionTo: function(name) {
4927 e4a6c3b8 Olga Brani
    // POSSIBLE TODO: Remove this code and replace with
4928 e4a6c3b8 Olga Brani
    // always having direct references to state objects
4929 e4a6c3b8 Olga Brani
4930 e4a6c3b8 Olga Brani
    var pivotName = name.split(".", 1),
4931 e4a6c3b8 Olga Brani
        currentState = get(this, 'currentState'),
4932 e4a6c3b8 Olga Brani
        state = currentState;
4933 e4a6c3b8 Olga Brani
4934 e4a6c3b8 Olga Brani
    do {
4935 e4a6c3b8 Olga Brani
      if (state.exit) { state.exit(this); }
4936 e4a6c3b8 Olga Brani
      state = state.parentState;
4937 e4a6c3b8 Olga Brani
    } while (!state.hasOwnProperty(pivotName));
4938 e4a6c3b8 Olga Brani
4939 e4a6c3b8 Olga Brani
    var path = name.split(".");
4940 e4a6c3b8 Olga Brani
4941 e4a6c3b8 Olga Brani
    var setups = [], enters = [], i, l;
4942 e4a6c3b8 Olga Brani
4943 e4a6c3b8 Olga Brani
    for (i=0, l=path.length; i<l; i++) {
4944 e4a6c3b8 Olga Brani
      state = state[path[i]];
4945 e4a6c3b8 Olga Brani
4946 e4a6c3b8 Olga Brani
      if (state.enter) { enters.push(state); }
4947 e4a6c3b8 Olga Brani
      if (state.setup) { setups.push(state); }
4948 e4a6c3b8 Olga Brani
    }
4949 e4a6c3b8 Olga Brani
4950 e4a6c3b8 Olga Brani
    for (i=0, l=enters.length; i<l; i++) {
4951 e4a6c3b8 Olga Brani
      enters[i].enter(this);
4952 e4a6c3b8 Olga Brani
    }
4953 e4a6c3b8 Olga Brani
4954 e4a6c3b8 Olga Brani
    set(this, 'currentState', state);
4955 e4a6c3b8 Olga Brani
4956 e4a6c3b8 Olga Brani
    for (i=0, l=setups.length; i<l; i++) {
4957 e4a6c3b8 Olga Brani
      setups[i].setup(this);
4958 e4a6c3b8 Olga Brani
    }
4959 e4a6c3b8 Olga Brani
4960 e4a6c3b8 Olga Brani
    this.updateRecordArraysLater();
4961 e4a6c3b8 Olga Brani
  },
4962 e4a6c3b8 Olga Brani
4963 e4a6c3b8 Olga Brani
  _unhandledEvent: function(state, name, context) {
4964 e4a6c3b8 Olga Brani
    var errorMessage = "Attempted to handle event `" + name + "` ";
4965 e4a6c3b8 Olga Brani
    errorMessage    += "on " + String(this) + " while in state ";
4966 e4a6c3b8 Olga Brani
    errorMessage    += state.stateName + ". ";
4967 e4a6c3b8 Olga Brani
4968 e4a6c3b8 Olga Brani
    if (context !== undefined) {
4969 e4a6c3b8 Olga Brani
      errorMessage  += "Called with " + Ember.inspect(context) + ".";
4970 e4a6c3b8 Olga Brani
    }
4971 e4a6c3b8 Olga Brani
4972 e4a6c3b8 Olga Brani
    throw new Ember.Error(errorMessage);
4973 e4a6c3b8 Olga Brani
  },
4974 e4a6c3b8 Olga Brani
4975 e4a6c3b8 Olga Brani
  withTransaction: function(fn) {
4976 e4a6c3b8 Olga Brani
    var transaction = get(this, 'transaction');
4977 e4a6c3b8 Olga Brani
    if (transaction) { fn(transaction); }
4978 e4a6c3b8 Olga Brani
  },
4979 e4a6c3b8 Olga Brani
4980 e4a6c3b8 Olga Brani
  /**
4981 e4a6c3b8 Olga Brani
    @method loadingData
4982 e4a6c3b8 Olga Brani
    @private
4983 e4a6c3b8 Olga Brani
    @param {Promise} promise
4984 e4a6c3b8 Olga Brani
  */
4985 e4a6c3b8 Olga Brani
  loadingData: function(promise) {
4986 e4a6c3b8 Olga Brani
    this.send('loadingData', promise);
4987 e4a6c3b8 Olga Brani
  },
4988 e4a6c3b8 Olga Brani
4989 e4a6c3b8 Olga Brani
  /**
4990 e4a6c3b8 Olga Brani
    @method loadedData
4991 e4a6c3b8 Olga Brani
    @private
4992 e4a6c3b8 Olga Brani
  */
4993 e4a6c3b8 Olga Brani
  loadedData: function() {
4994 e4a6c3b8 Olga Brani
    this.send('loadedData');
4995 e4a6c3b8 Olga Brani
  },
4996 e4a6c3b8 Olga Brani
4997 e4a6c3b8 Olga Brani
  /**
4998 e4a6c3b8 Olga Brani
    @method notFound
4999 e4a6c3b8 Olga Brani
    @private
5000 e4a6c3b8 Olga Brani
  */
5001 e4a6c3b8 Olga Brani
  notFound: function() {
5002 e4a6c3b8 Olga Brani
    this.send('notFound');
5003 e4a6c3b8 Olga Brani
  },
5004 e4a6c3b8 Olga Brani
5005 e4a6c3b8 Olga Brani
  /**
5006 e4a6c3b8 Olga Brani
    @method pushedData
5007 e4a6c3b8 Olga Brani
    @private
5008 e4a6c3b8 Olga Brani
  */
5009 e4a6c3b8 Olga Brani
  pushedData: function() {
5010 e4a6c3b8 Olga Brani
    this.send('pushedData');
5011 e4a6c3b8 Olga Brani
  },
5012 e4a6c3b8 Olga Brani
5013 e4a6c3b8 Olga Brani
  /**
5014 e4a6c3b8 Olga Brani
    Marks the record as deleted but does not save it. You must call
5015 e4a6c3b8 Olga Brani
    `save` afterwards if you want to persist it. You might use this
5016 e4a6c3b8 Olga Brani
    method if you want to allow the user to still `rollback()` a
5017 e4a6c3b8 Olga Brani
    delete after it was made.
5018 e4a6c3b8 Olga Brani

5019 e4a6c3b8 Olga Brani
    Example
5020 e4a6c3b8 Olga Brani

5021 e4a6c3b8 Olga Brani
    ```javascript
5022 e4a6c3b8 Olga Brani
    App.ModelDeleteRoute = Ember.Route.extend({
5023 e4a6c3b8 Olga Brani
      actions: {
5024 e4a6c3b8 Olga Brani
        softDelete: function() {
5025 e4a6c3b8 Olga Brani
          this.get('model').deleteRecord();
5026 e4a6c3b8 Olga Brani
        },
5027 e4a6c3b8 Olga Brani
        confirm: function() {
5028 e4a6c3b8 Olga Brani
          this.get('model').save();
5029 e4a6c3b8 Olga Brani
        },
5030 e4a6c3b8 Olga Brani
        undo: function() {
5031 e4a6c3b8 Olga Brani
          this.get('model').rollback();
5032 e4a6c3b8 Olga Brani
        }
5033 e4a6c3b8 Olga Brani
      }
5034 e4a6c3b8 Olga Brani
    });
5035 e4a6c3b8 Olga Brani
    ```
5036 e4a6c3b8 Olga Brani

5037 e4a6c3b8 Olga Brani
    @method deleteRecord
5038 e4a6c3b8 Olga Brani
  */
5039 e4a6c3b8 Olga Brani
  deleteRecord: function() {
5040 e4a6c3b8 Olga Brani
    this.send('deleteRecord');
5041 e4a6c3b8 Olga Brani
  },
5042 e4a6c3b8 Olga Brani
5043 e4a6c3b8 Olga Brani
  /**
5044 e4a6c3b8 Olga Brani
    Same as `deleteRecord`, but saves the record immediately.
5045 e4a6c3b8 Olga Brani

5046 e4a6c3b8 Olga Brani
    Example
5047 e4a6c3b8 Olga Brani

5048 e4a6c3b8 Olga Brani
    ```javascript
5049 e4a6c3b8 Olga Brani
    App.ModelDeleteRoute = Ember.Route.extend({
5050 e4a6c3b8 Olga Brani
      actions: {
5051 e4a6c3b8 Olga Brani
        delete: function() {
5052 e4a6c3b8 Olga Brani
          var controller = this.controller;
5053 e4a6c3b8 Olga Brani
          this.get('model').destroyRecord().then(function() {
5054 e4a6c3b8 Olga Brani
            controller.transitionToRoute('model.index');
5055 e4a6c3b8 Olga Brani
          });
5056 e4a6c3b8 Olga Brani
        }
5057 e4a6c3b8 Olga Brani
      }
5058 e4a6c3b8 Olga Brani
    });
5059 e4a6c3b8 Olga Brani
    ```
5060 e4a6c3b8 Olga Brani

5061 e4a6c3b8 Olga Brani
    @method destroyRecord
5062 e4a6c3b8 Olga Brani
    @return {Promise} a promise that will be resolved when the adapter returns
5063 e4a6c3b8 Olga Brani
    successfully or rejected if the adapter returns with an error.
5064 e4a6c3b8 Olga Brani
  */
5065 e4a6c3b8 Olga Brani
  destroyRecord: function() {
5066 e4a6c3b8 Olga Brani
    this.deleteRecord();
5067 e4a6c3b8 Olga Brani
    return this.save();
5068 e4a6c3b8 Olga Brani
  },
5069 e4a6c3b8 Olga Brani
5070 e4a6c3b8 Olga Brani
  /**
5071 e4a6c3b8 Olga Brani
    @method unloadRecord
5072 e4a6c3b8 Olga Brani
    @private
5073 e4a6c3b8 Olga Brani
  */
5074 e4a6c3b8 Olga Brani
  unloadRecord: function() {
5075 e4a6c3b8 Olga Brani
    Ember.assert("You can only unload a loaded, non-dirty record.", !get(this, 'isDirty'));
5076 e4a6c3b8 Olga Brani
5077 e4a6c3b8 Olga Brani
    this.send('unloadRecord');
5078 e4a6c3b8 Olga Brani
  },
5079 e4a6c3b8 Olga Brani
5080 e4a6c3b8 Olga Brani
  /**
5081 e4a6c3b8 Olga Brani
    @method clearRelationships
5082 e4a6c3b8 Olga Brani
    @private
5083 e4a6c3b8 Olga Brani
  */
5084 e4a6c3b8 Olga Brani
  clearRelationships: function() {
5085 e4a6c3b8 Olga Brani
    this.eachRelationship(function(name, relationship) {
5086 e4a6c3b8 Olga Brani
      if (relationship.kind === 'belongsTo') {
5087 e4a6c3b8 Olga Brani
        set(this, name, null);
5088 e4a6c3b8 Olga Brani
      } else if (relationship.kind === 'hasMany') {
5089 e4a6c3b8 Olga Brani
        var hasMany = this._relationships[relationship.name];
5090 e4a6c3b8 Olga Brani
        if (hasMany) { hasMany.clear(); }
5091 e4a6c3b8 Olga Brani
      }
5092 e4a6c3b8 Olga Brani
    }, this);
5093 e4a6c3b8 Olga Brani
  },
5094 e4a6c3b8 Olga Brani
5095 e4a6c3b8 Olga Brani
  /**
5096 e4a6c3b8 Olga Brani
    @method updateRecordArrays
5097 e4a6c3b8 Olga Brani
    @private
5098 e4a6c3b8 Olga Brani
  */
5099 e4a6c3b8 Olga Brani
  updateRecordArrays: function() {
5100 e4a6c3b8 Olga Brani
    get(this, 'store').dataWasUpdated(this.constructor, this);
5101 e4a6c3b8 Olga Brani
  },
5102 e4a6c3b8 Olga Brani
5103 e4a6c3b8 Olga Brani
  /**
5104 e4a6c3b8 Olga Brani
    Returns an object, whose keys are changed properties, and value is
5105 e4a6c3b8 Olga Brani
    an [oldProp, newProp] array.
5106 e4a6c3b8 Olga Brani

5107 e4a6c3b8 Olga Brani
    Example
5108 e4a6c3b8 Olga Brani

5109 e4a6c3b8 Olga Brani
    ```javascript
5110 e4a6c3b8 Olga Brani
    App.Mascot = DS.Model.extend({
5111 e4a6c3b8 Olga Brani
      name: attr('string')
5112 e4a6c3b8 Olga Brani
    });
5113 e4a6c3b8 Olga Brani

5114 e4a6c3b8 Olga Brani
    var person = store.createRecord('person');
5115 e4a6c3b8 Olga Brani
    person.changedAttributes(); // {}
5116 e4a6c3b8 Olga Brani
    person.set('name', 'Tomster');
5117 e4a6c3b8 Olga Brani
    person.changedAttributes(); // {name: [undefined, 'Tomster']}
5118 e4a6c3b8 Olga Brani
    ```
5119 e4a6c3b8 Olga Brani

5120 e4a6c3b8 Olga Brani
    @method changedAttributes
5121 e4a6c3b8 Olga Brani
    @return {Object} an object, whose keys are changed properties,
5122 e4a6c3b8 Olga Brani
      and value is an [oldProp, newProp] array.
5123 e4a6c3b8 Olga Brani
  */
5124 e4a6c3b8 Olga Brani
  changedAttributes: function() {
5125 e4a6c3b8 Olga Brani
    var oldData = get(this, '_data'),
5126 e4a6c3b8 Olga Brani
        newData = get(this, '_attributes'),
5127 e4a6c3b8 Olga Brani
        diffData = {},
5128 e4a6c3b8 Olga Brani
        prop;
5129 e4a6c3b8 Olga Brani
5130 e4a6c3b8 Olga Brani
    for (prop in newData) {
5131 e4a6c3b8 Olga Brani
      diffData[prop] = [oldData[prop], newData[prop]];
5132 e4a6c3b8 Olga Brani
    }
5133 e4a6c3b8 Olga Brani
5134 e4a6c3b8 Olga Brani
    return diffData;
5135 e4a6c3b8 Olga Brani
  },
5136 e4a6c3b8 Olga Brani
5137 e4a6c3b8 Olga Brani
  /**
5138 e4a6c3b8 Olga Brani
    @method adapterWillCommit
5139 e4a6c3b8 Olga Brani
    @private
5140 e4a6c3b8 Olga Brani
  */
5141 e4a6c3b8 Olga Brani
  adapterWillCommit: function() {
5142 e4a6c3b8 Olga Brani
    this.send('willCommit');
5143 e4a6c3b8 Olga Brani
  },
5144 e4a6c3b8 Olga Brani
5145 e4a6c3b8 Olga Brani
  /**
5146 e4a6c3b8 Olga Brani
    If the adapter did not return a hash in response to a commit,
5147 e4a6c3b8 Olga Brani
    merge the changed attributes and relationships into the existing
5148 e4a6c3b8 Olga Brani
    saved data.
5149 e4a6c3b8 Olga Brani

5150 e4a6c3b8 Olga Brani
    @method adapterDidCommit
5151 e4a6c3b8 Olga Brani
  */
5152 e4a6c3b8 Olga Brani
  adapterDidCommit: function(data) {
5153 e4a6c3b8 Olga Brani
    set(this, 'isError', false);
5154 e4a6c3b8 Olga Brani
5155 e4a6c3b8 Olga Brani
    if (data) {
5156 e4a6c3b8 Olga Brani
      this._data = data;
5157 e4a6c3b8 Olga Brani
    } else {
5158 e4a6c3b8 Olga Brani
      Ember.mixin(this._data, this._inFlightAttributes);
5159 e4a6c3b8 Olga Brani
    }
5160 e4a6c3b8 Olga Brani
5161 e4a6c3b8 Olga Brani
    this._inFlightAttributes = {};
5162 e4a6c3b8 Olga Brani
5163 e4a6c3b8 Olga Brani
    this.send('didCommit');
5164 e4a6c3b8 Olga Brani
    this.updateRecordArraysLater();
5165 e4a6c3b8 Olga Brani
5166 e4a6c3b8 Olga Brani
    if (!data) { return; }
5167 e4a6c3b8 Olga Brani
5168 e4a6c3b8 Olga Brani
    this.suspendRelationshipObservers(function() {
5169 e4a6c3b8 Olga Brani
      this.notifyPropertyChange('data');
5170 e4a6c3b8 Olga Brani
    });
5171 e4a6c3b8 Olga Brani
  },
5172 e4a6c3b8 Olga Brani
5173 e4a6c3b8 Olga Brani
  /**
5174 e4a6c3b8 Olga Brani
    @method adapterDidDirty
5175 e4a6c3b8 Olga Brani
    @private
5176 e4a6c3b8 Olga Brani
  */
5177 e4a6c3b8 Olga Brani
  adapterDidDirty: function() {
5178 e4a6c3b8 Olga Brani
    this.send('becomeDirty');
5179 e4a6c3b8 Olga Brani
    this.updateRecordArraysLater();
5180 e4a6c3b8 Olga Brani
  },
5181 e4a6c3b8 Olga Brani
5182 e4a6c3b8 Olga Brani
  dataDidChange: Ember.observer(function() {
5183 e4a6c3b8 Olga Brani
    this.reloadHasManys();
5184 e4a6c3b8 Olga Brani
  }, 'data'),
5185 e4a6c3b8 Olga Brani
5186 e4a6c3b8 Olga Brani
  reloadHasManys: function() {
5187 e4a6c3b8 Olga Brani
    var relationships = get(this.constructor, 'relationshipsByName');
5188 e4a6c3b8 Olga Brani
    this.updateRecordArraysLater();
5189 e4a6c3b8 Olga Brani
    relationships.forEach(function(name, relationship) {
5190 e4a6c3b8 Olga Brani
      if (this._data.links && this._data.links[name]) { return; }
5191 e4a6c3b8 Olga Brani
      if (relationship.kind === 'hasMany') {
5192 e4a6c3b8 Olga Brani
        this.hasManyDidChange(relationship.key);
5193 e4a6c3b8 Olga Brani
      }
5194 e4a6c3b8 Olga Brani
    }, this);
5195 e4a6c3b8 Olga Brani
  },
5196 e4a6c3b8 Olga Brani
5197 e4a6c3b8 Olga Brani
  hasManyDidChange: function(key) {
5198 e4a6c3b8 Olga Brani
    var hasMany = this._relationships[key];
5199 e4a6c3b8 Olga Brani
5200 e4a6c3b8 Olga Brani
    if (hasMany) {
5201 e4a6c3b8 Olga Brani
      var records = this._data[key] || [];
5202 e4a6c3b8 Olga Brani
5203 e4a6c3b8 Olga Brani
      set(hasMany, 'content', Ember.A(records));
5204 e4a6c3b8 Olga Brani
      set(hasMany, 'isLoaded', true);
5205 e4a6c3b8 Olga Brani
      hasMany.trigger('didLoad');
5206 e4a6c3b8 Olga Brani
    }
5207 e4a6c3b8 Olga Brani
  },
5208 e4a6c3b8 Olga Brani
5209 e4a6c3b8 Olga Brani
  /**
5210 e4a6c3b8 Olga Brani
    @method updateRecordArraysLater
5211 e4a6c3b8 Olga Brani
    @private
5212 e4a6c3b8 Olga Brani
  */
5213 e4a6c3b8 Olga Brani
  updateRecordArraysLater: function() {
5214 e4a6c3b8 Olga Brani
    Ember.run.once(this, this.updateRecordArrays);
5215 e4a6c3b8 Olga Brani
  },
5216 e4a6c3b8 Olga Brani
5217 e4a6c3b8 Olga Brani
  /**
5218 e4a6c3b8 Olga Brani
    @method setupData
5219 e4a6c3b8 Olga Brani
    @private
5220 e4a6c3b8 Olga Brani
    @param {Object} data
5221 e4a6c3b8 Olga Brani
    @param {Boolean} partial the data should be merged into
5222 e4a6c3b8 Olga Brani
      the existing data, not replace it.
5223 e4a6c3b8 Olga Brani
  */
5224 e4a6c3b8 Olga Brani
  setupData: function(data, partial) {
5225 e4a6c3b8 Olga Brani
    if (partial) {
5226 e4a6c3b8 Olga Brani
      Ember.merge(this._data, data);
5227 e4a6c3b8 Olga Brani
    } else {
5228 e4a6c3b8 Olga Brani
      this._data = data;
5229 e4a6c3b8 Olga Brani
    }
5230 e4a6c3b8 Olga Brani
5231 e4a6c3b8 Olga Brani
    var relationships = this._relationships;
5232 e4a6c3b8 Olga Brani
5233 e4a6c3b8 Olga Brani
    this.eachRelationship(function(name, rel) {
5234 e4a6c3b8 Olga Brani
      if (data.links && data.links[name]) { return; }
5235 e4a6c3b8 Olga Brani
      if (rel.options.async) { relationships[name] = null; }
5236 e4a6c3b8 Olga Brani
    });
5237 e4a6c3b8 Olga Brani
5238 e4a6c3b8 Olga Brani
    if (data) { this.pushedData(); }
5239 e4a6c3b8 Olga Brani
5240 e4a6c3b8 Olga Brani
    this.suspendRelationshipObservers(function() {
5241 e4a6c3b8 Olga Brani
      this.notifyPropertyChange('data');
5242 e4a6c3b8 Olga Brani
    });
5243 e4a6c3b8 Olga Brani
  },
5244 e4a6c3b8 Olga Brani
5245 e4a6c3b8 Olga Brani
  materializeId: function(id) {
5246 e4a6c3b8 Olga Brani
    set(this, 'id', id);
5247 e4a6c3b8 Olga Brani
  },
5248 e4a6c3b8 Olga Brani
5249 e4a6c3b8 Olga Brani
  materializeAttributes: function(attributes) {
5250 e4a6c3b8 Olga Brani
    Ember.assert("Must pass a hash of attributes to materializeAttributes", !!attributes);
5251 e4a6c3b8 Olga Brani
    merge(this._data, attributes);
5252 e4a6c3b8 Olga Brani
  },
5253 e4a6c3b8 Olga Brani
5254 e4a6c3b8 Olga Brani
  materializeAttribute: function(name, value) {
5255 e4a6c3b8 Olga Brani
    this._data[name] = value;
5256 e4a6c3b8 Olga Brani
  },
5257 e4a6c3b8 Olga Brani
5258 e4a6c3b8 Olga Brani
  /**
5259 e4a6c3b8 Olga Brani
    @method updateHasMany
5260 e4a6c3b8 Olga Brani
    @private
5261 e4a6c3b8 Olga Brani
    @param {String} name
5262 e4a6c3b8 Olga Brani
    @param {Array} records
5263 e4a6c3b8 Olga Brani
  */
5264 e4a6c3b8 Olga Brani
  updateHasMany: function(name, records) {
5265 e4a6c3b8 Olga Brani
    this._data[name] = records;
5266 e4a6c3b8 Olga Brani
    this.hasManyDidChange(name);
5267 e4a6c3b8 Olga Brani
  },
5268 e4a6c3b8 Olga Brani
5269 e4a6c3b8 Olga Brani
  /**
5270 e4a6c3b8 Olga Brani
    @method updateBelongsTo
5271 e4a6c3b8 Olga Brani
    @private
5272 e4a6c3b8 Olga Brani
    @param {String} name
5273 e4a6c3b8 Olga Brani
    @param {DS.Model} record
5274 e4a6c3b8 Olga Brani
  */
5275 e4a6c3b8 Olga Brani
  updateBelongsTo: function(name, record) {
5276 e4a6c3b8 Olga Brani
    this._data[name] = record;
5277 e4a6c3b8 Olga Brani
  },
5278 e4a6c3b8 Olga Brani
5279 e4a6c3b8 Olga Brani
  /**
5280 e4a6c3b8 Olga Brani
    If the model `isDirty` this function will which discard any unsaved
5281 e4a6c3b8 Olga Brani
    changes
5282 e4a6c3b8 Olga Brani

5283 e4a6c3b8 Olga Brani
    Example
5284 e4a6c3b8 Olga Brani

5285 e4a6c3b8 Olga Brani
    ```javascript
5286 e4a6c3b8 Olga Brani
    record.get('name'); // 'Untitled Document'
5287 e4a6c3b8 Olga Brani
    record.set('name', 'Doc 1');
5288 e4a6c3b8 Olga Brani
    record.get('name'); // 'Doc 1'
5289 e4a6c3b8 Olga Brani
    record.rollback();
5290 e4a6c3b8 Olga Brani
    record.get('name'); // 'Untitled Document'
5291 e4a6c3b8 Olga Brani
    ```
5292 e4a6c3b8 Olga Brani

5293 e4a6c3b8 Olga Brani
    @method rollback
5294 e4a6c3b8 Olga Brani
  */
5295 e4a6c3b8 Olga Brani
  rollback: function() {
5296 e4a6c3b8 Olga Brani
    this._attributes = {};
5297 e4a6c3b8 Olga Brani
5298 e4a6c3b8 Olga Brani
    if (get(this, 'isError')) {
5299 e4a6c3b8 Olga Brani
      this._inFlightAttributes = {};
5300 e4a6c3b8 Olga Brani
      set(this, 'isError', false);
5301 e4a6c3b8 Olga Brani
    }
5302 e4a6c3b8 Olga Brani
5303 e4a6c3b8 Olga Brani
    if (!get(this, 'isValid')) {
5304 e4a6c3b8 Olga Brani
      this._inFlightAttributes = {};
5305 e4a6c3b8 Olga Brani
    }
5306 e4a6c3b8 Olga Brani
5307 e4a6c3b8 Olga Brani
    this.send('rolledBack');
5308 e4a6c3b8 Olga Brani
5309 e4a6c3b8 Olga Brani
    this.suspendRelationshipObservers(function() {
5310 e4a6c3b8 Olga Brani
      this.notifyPropertyChange('data');
5311 e4a6c3b8 Olga Brani
    });
5312 e4a6c3b8 Olga Brani
  },
5313 e4a6c3b8 Olga Brani
5314 e4a6c3b8 Olga Brani
  toStringExtension: function() {
5315 e4a6c3b8 Olga Brani
    return get(this, 'id');
5316 e4a6c3b8 Olga Brani
  },
5317 e4a6c3b8 Olga Brani
5318 e4a6c3b8 Olga Brani
  /**
5319 e4a6c3b8 Olga Brani
    The goal of this method is to temporarily disable specific observers
5320 e4a6c3b8 Olga Brani
    that take action in response to application changes.
5321 e4a6c3b8 Olga Brani

5322 e4a6c3b8 Olga Brani
    This allows the system to make changes (such as materialization and
5323 e4a6c3b8 Olga Brani
    rollback) that should not trigger secondary behavior (such as setting an
5324 e4a6c3b8 Olga Brani
    inverse relationship or marking records as dirty).
5325 e4a6c3b8 Olga Brani

5326 e4a6c3b8 Olga Brani
    The specific implementation will likely change as Ember proper provides
5327 e4a6c3b8 Olga Brani
    better infrastructure for suspending groups of observers, and if Array
5328 e4a6c3b8 Olga Brani
    observation becomes more unified with regular observers.
5329 e4a6c3b8 Olga Brani

5330 e4a6c3b8 Olga Brani
    @method suspendRelationshipObservers
5331 e4a6c3b8 Olga Brani
    @private
5332 e4a6c3b8 Olga Brani
    @param callback
5333 e4a6c3b8 Olga Brani
    @param binding
5334 e4a6c3b8 Olga Brani
  */
5335 e4a6c3b8 Olga Brani
  suspendRelationshipObservers: function(callback, binding) {
5336 e4a6c3b8 Olga Brani
    var observers = get(this.constructor, 'relationshipNames').belongsTo;
5337 e4a6c3b8 Olga Brani
    var self = this;
5338 e4a6c3b8 Olga Brani
5339 e4a6c3b8 Olga Brani
    try {
5340 e4a6c3b8 Olga Brani
      this._suspendedRelationships = true;
5341 e4a6c3b8 Olga Brani
      Ember._suspendObservers(self, observers, null, 'belongsToDidChange', function() {
5342 e4a6c3b8 Olga Brani
        Ember._suspendBeforeObservers(self, observers, null, 'belongsToWillChange', function() {
5343 e4a6c3b8 Olga Brani
          callback.call(binding || self);
5344 e4a6c3b8 Olga Brani
        });
5345 e4a6c3b8 Olga Brani
      });
5346 e4a6c3b8 Olga Brani
    } finally {
5347 e4a6c3b8 Olga Brani
      this._suspendedRelationships = false;
5348 e4a6c3b8 Olga Brani
    }
5349 e4a6c3b8 Olga Brani
  },
5350 e4a6c3b8 Olga Brani
5351 e4a6c3b8 Olga Brani
  /**
5352 e4a6c3b8 Olga Brani
    Save the record and persist any changes to the record to an
5353 e4a6c3b8 Olga Brani
    extenal source via the adapter.
5354 e4a6c3b8 Olga Brani

5355 e4a6c3b8 Olga Brani
    Example
5356 e4a6c3b8 Olga Brani

5357 e4a6c3b8 Olga Brani
    ```javascript
5358 e4a6c3b8 Olga Brani
    record.set('name', 'Tomster');
5359 e4a6c3b8 Olga Brani
    record.save().then(function(){
5360 e4a6c3b8 Olga Brani
      // Success callback
5361 e4a6c3b8 Olga Brani
    }, function() {
5362 e4a6c3b8 Olga Brani
      // Error callback
5363 e4a6c3b8 Olga Brani
    });
5364 e4a6c3b8 Olga Brani
    ```
5365 e4a6c3b8 Olga Brani
    @method save
5366 e4a6c3b8 Olga Brani
    @return {Promise} a promise that will be resolved when the adapter returns
5367 e4a6c3b8 Olga Brani
    successfully or rejected if the adapter returns with an error.
5368 e4a6c3b8 Olga Brani
  */
5369 e4a6c3b8 Olga Brani
  save: function() {
5370 e4a6c3b8 Olga Brani
    var promiseLabel = "DS: Model#save " + this;
5371 e4a6c3b8 Olga Brani
    var resolver = Ember.RSVP.defer(promiseLabel);
5372 e4a6c3b8 Olga Brani
5373 e4a6c3b8 Olga Brani
    this.get('store').scheduleSave(this, resolver);
5374 e4a6c3b8 Olga Brani
    this._inFlightAttributes = this._attributes;
5375 e4a6c3b8 Olga Brani
    this._attributes = {};
5376 e4a6c3b8 Olga Brani
5377 e4a6c3b8 Olga Brani
    return DS.PromiseObject.create({ promise: resolver.promise });
5378 e4a6c3b8 Olga Brani
  },
5379 e4a6c3b8 Olga Brani
5380 e4a6c3b8 Olga Brani
  /**
5381 e4a6c3b8 Olga Brani
    Reload the record from the adapter.
5382 e4a6c3b8 Olga Brani

5383 e4a6c3b8 Olga Brani
    This will only work if the record has already finished loading
5384 e4a6c3b8 Olga Brani
    and has not yet been modified (`isLoaded` but not `isDirty`,
5385 e4a6c3b8 Olga Brani
    or `isSaving`).
5386 e4a6c3b8 Olga Brani

5387 e4a6c3b8 Olga Brani
    Example
5388 e4a6c3b8 Olga Brani

5389 e4a6c3b8 Olga Brani
    ```javascript
5390 e4a6c3b8 Olga Brani
    App.ModelViewRoute = Ember.Route.extend({
5391 e4a6c3b8 Olga Brani
      actions: {
5392 e4a6c3b8 Olga Brani
        reload: function() {
5393 e4a6c3b8 Olga Brani
          this.get('model').reload();
5394 e4a6c3b8 Olga Brani
        }
5395 e4a6c3b8 Olga Brani
      }
5396 e4a6c3b8 Olga Brani
    });
5397 e4a6c3b8 Olga Brani
    ```
5398 e4a6c3b8 Olga Brani

5399 e4a6c3b8 Olga Brani
    @method reload
5400 e4a6c3b8 Olga Brani
    @return {Promise} a promise that will be resolved with the record when the
5401 e4a6c3b8 Olga Brani
    adapter returns successfully or rejected if the adapter returns
5402 e4a6c3b8 Olga Brani
    with an error.
5403 e4a6c3b8 Olga Brani
  */
5404 e4a6c3b8 Olga Brani
  reload: function() {
5405 e4a6c3b8 Olga Brani
    set(this, 'isReloading', true);
5406 e4a6c3b8 Olga Brani
5407 e4a6c3b8 Olga Brani
    var  record = this;
5408 e4a6c3b8 Olga Brani
5409 e4a6c3b8 Olga Brani
    var promiseLabel = "DS: Model#reload of " + this;
5410 e4a6c3b8 Olga Brani
    var promise = new Ember.RSVP.Promise(function(resolve){
5411 e4a6c3b8 Olga Brani
       record.send('reloadRecord', resolve);
5412 e4a6c3b8 Olga Brani
    }, promiseLabel).then(function() {
5413 e4a6c3b8 Olga Brani
      record.set('isReloading', false);
5414 e4a6c3b8 Olga Brani
      record.set('isError', false);
5415 e4a6c3b8 Olga Brani
      return record;
5416 e4a6c3b8 Olga Brani
    }, function(reason) {
5417 e4a6c3b8 Olga Brani
      record.set('isError', true);
5418 e4a6c3b8 Olga Brani
      throw reason;
5419 e4a6c3b8 Olga Brani
    }, "DS: Model#reload complete, update flags");
5420 e4a6c3b8 Olga Brani
5421 e4a6c3b8 Olga Brani
    return DS.PromiseObject.create({ promise: promise });
5422 e4a6c3b8 Olga Brani
  },
5423 e4a6c3b8 Olga Brani
5424 e4a6c3b8 Olga Brani
  // FOR USE DURING COMMIT PROCESS
5425 e4a6c3b8 Olga Brani
5426 e4a6c3b8 Olga Brani
  adapterDidUpdateAttribute: function(attributeName, value) {
5427 e4a6c3b8 Olga Brani
5428 e4a6c3b8 Olga Brani
    // If a value is passed in, update the internal attributes and clear
5429 e4a6c3b8 Olga Brani
    // the attribute cache so it picks up the new value. Otherwise,
5430 e4a6c3b8 Olga Brani
    // collapse the current value into the internal attributes because
5431 e4a6c3b8 Olga Brani
    // the adapter has acknowledged it.
5432 e4a6c3b8 Olga Brani
    if (value !== undefined) {
5433 e4a6c3b8 Olga Brani
      this._data[attributeName] = value;
5434 e4a6c3b8 Olga Brani
      this.notifyPropertyChange(attributeName);
5435 e4a6c3b8 Olga Brani
    } else {
5436 e4a6c3b8 Olga Brani
      this._data[attributeName] = this._inFlightAttributes[attributeName];
5437 e4a6c3b8 Olga Brani
    }
5438 e4a6c3b8 Olga Brani
5439 e4a6c3b8 Olga Brani
    this.updateRecordArraysLater();
5440 e4a6c3b8 Olga Brani
  },
5441 e4a6c3b8 Olga Brani
5442 e4a6c3b8 Olga Brani
  /**
5443 e4a6c3b8 Olga Brani
    @method adapterDidInvalidate
5444 e4a6c3b8 Olga Brani
    @private
5445 e4a6c3b8 Olga Brani
  */
5446 e4a6c3b8 Olga Brani
  adapterDidInvalidate: function(errors) {
5447 e4a6c3b8 Olga Brani
    var recordErrors = get(this, 'errors');
5448 e4a6c3b8 Olga Brani
    function addError(name) {
5449 e4a6c3b8 Olga Brani
      if (errors[name]) {
5450 e4a6c3b8 Olga Brani
        recordErrors.add(name, errors[name]);
5451 e4a6c3b8 Olga Brani
      }
5452 e4a6c3b8 Olga Brani
    }
5453 e4a6c3b8 Olga Brani
5454 e4a6c3b8 Olga Brani
    this.eachAttribute(addError);
5455 e4a6c3b8 Olga Brani
    this.eachRelationship(addError);
5456 e4a6c3b8 Olga Brani
  },
5457 e4a6c3b8 Olga Brani
5458 e4a6c3b8 Olga Brani
  /**
5459 e4a6c3b8 Olga Brani
    @method adapterDidError
5460 e4a6c3b8 Olga Brani
    @private
5461 e4a6c3b8 Olga Brani
  */
5462 e4a6c3b8 Olga Brani
  adapterDidError: function() {
5463 e4a6c3b8 Olga Brani
    this.send('becameError');
5464 e4a6c3b8 Olga Brani
    set(this, 'isError', true);
5465 e4a6c3b8 Olga Brani
  },
5466 e4a6c3b8 Olga Brani
5467 e4a6c3b8 Olga Brani
  /**
5468 e4a6c3b8 Olga Brani
    Override the default event firing from Ember.Evented to
5469 e4a6c3b8 Olga Brani
    also call methods with the given name.
5470 e4a6c3b8 Olga Brani

5471 e4a6c3b8 Olga Brani
    @method trigger
5472 e4a6c3b8 Olga Brani
    @private
5473 e4a6c3b8 Olga Brani
    @param name
5474 e4a6c3b8 Olga Brani
  */
5475 e4a6c3b8 Olga Brani
  trigger: function(name) {
5476 e4a6c3b8 Olga Brani
    Ember.tryInvoke(this, name, [].slice.call(arguments, 1));
5477 e4a6c3b8 Olga Brani
    this._super.apply(this, arguments);
5478 e4a6c3b8 Olga Brani
  },
5479 e4a6c3b8 Olga Brani
5480 e4a6c3b8 Olga Brani
  triggerLater: function() {
5481 e4a6c3b8 Olga Brani
    this._deferredTriggers.push(arguments);
5482 e4a6c3b8 Olga Brani
    once(this, '_triggerDeferredTriggers');
5483 e4a6c3b8 Olga Brani
  },
5484 e4a6c3b8 Olga Brani
5485 e4a6c3b8 Olga Brani
  _triggerDeferredTriggers: function() {
5486 e4a6c3b8 Olga Brani
    for (var i=0, l=this._deferredTriggers.length; i<l; i++) {
5487 e4a6c3b8 Olga Brani
      this.trigger.apply(this, this._deferredTriggers[i]);
5488 e4a6c3b8 Olga Brani
    }
5489 e4a6c3b8 Olga Brani
5490 e4a6c3b8 Olga Brani
    this._deferredTriggers = [];
5491 e4a6c3b8 Olga Brani
  }
5492 e4a6c3b8 Olga Brani
});
5493 e4a6c3b8 Olga Brani
5494 e4a6c3b8 Olga Brani
DS.Model.reopenClass({
5495 e4a6c3b8 Olga Brani
5496 e4a6c3b8 Olga Brani
  /**
5497 e4a6c3b8 Olga Brani
    Alias DS.Model's `create` method to `_create`. This allows us to create DS.Model
5498 e4a6c3b8 Olga Brani
    instances from within the store, but if end users accidentally call `create()`
5499 e4a6c3b8 Olga Brani
    (instead of `createRecord()`), we can raise an error.
5500 e4a6c3b8 Olga Brani

5501 e4a6c3b8 Olga Brani
    @method _create
5502 e4a6c3b8 Olga Brani
    @private
5503 e4a6c3b8 Olga Brani
    @static
5504 e4a6c3b8 Olga Brani
  */
5505 e4a6c3b8 Olga Brani
  _create: DS.Model.create,
5506 e4a6c3b8 Olga Brani
5507 e4a6c3b8 Olga Brani
  /**
5508 e4a6c3b8 Olga Brani
    Override the class' `create()` method to raise an error. This
5509 e4a6c3b8 Olga Brani
    prevents end users from inadvertently calling `create()` instead
5510 e4a6c3b8 Olga Brani
    of `createRecord()`. The store is still able to create instances
5511 e4a6c3b8 Olga Brani
    by calling the `_create()` method. To create an instance of a
5512 e4a6c3b8 Olga Brani
    `DS.Model` use [store.createRecord](DS.Store.html#method_createRecord).
5513 e4a6c3b8 Olga Brani

5514 e4a6c3b8 Olga Brani
    @method create
5515 e4a6c3b8 Olga Brani
    @private
5516 e4a6c3b8 Olga Brani
    @static
5517 e4a6c3b8 Olga Brani
  */
5518 e4a6c3b8 Olga Brani
  create: function() {
5519 e4a6c3b8 Olga Brani
    throw new Ember.Error("You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.");
5520 e4a6c3b8 Olga Brani
  }
5521 e4a6c3b8 Olga Brani
});
5522 e4a6c3b8 Olga Brani
5523 e4a6c3b8 Olga Brani
})();
5524 e4a6c3b8 Olga Brani
5525 e4a6c3b8 Olga Brani
5526 e4a6c3b8 Olga Brani
5527 e4a6c3b8 Olga Brani
(function() {
5528 e4a6c3b8 Olga Brani
/**
5529 e4a6c3b8 Olga Brani
  @module ember-data
5530 e4a6c3b8 Olga Brani
*/
5531 e4a6c3b8 Olga Brani
5532 e4a6c3b8 Olga Brani
var get = Ember.get;
5533 e4a6c3b8 Olga Brani
5534 e4a6c3b8 Olga Brani
/**
5535 e4a6c3b8 Olga Brani
  @class Model
5536 e4a6c3b8 Olga Brani
  @namespace DS
5537 e4a6c3b8 Olga Brani
*/
5538 e4a6c3b8 Olga Brani
DS.Model.reopenClass({
5539 e4a6c3b8 Olga Brani
  /**
5540 e4a6c3b8 Olga Brani
    A map whose keys are the attributes of the model (properties
5541 e4a6c3b8 Olga Brani
    described by DS.attr) and whose values are the meta object for the
5542 e4a6c3b8 Olga Brani
    property.
5543 e4a6c3b8 Olga Brani

5544 e4a6c3b8 Olga Brani
    Example
5545 e4a6c3b8 Olga Brani

5546 e4a6c3b8 Olga Brani
    ```javascript
5547 e4a6c3b8 Olga Brani

5548 e4a6c3b8 Olga Brani
    App.Person = DS.Model.extend({
5549 e4a6c3b8 Olga Brani
      firstName: attr('string'),
5550 e4a6c3b8 Olga Brani
      lastName: attr('string'),
5551 e4a6c3b8 Olga Brani
      birthday: attr('date')
5552 e4a6c3b8 Olga Brani
    });
5553 e4a6c3b8 Olga Brani

5554 e4a6c3b8 Olga Brani
    var attributes = Ember.get(App.Person, 'attributes')
5555 e4a6c3b8 Olga Brani

5556 e4a6c3b8 Olga Brani
    attributes.forEach(function(name, meta) {
5557 e4a6c3b8 Olga Brani
      console.log(name, meta);
5558 e4a6c3b8 Olga Brani
    });
5559 e4a6c3b8 Olga Brani

5560 e4a6c3b8 Olga Brani
    // prints:
5561 e4a6c3b8 Olga Brani
    // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
5562 e4a6c3b8 Olga Brani
    // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
5563 e4a6c3b8 Olga Brani
    // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
5564 e4a6c3b8 Olga Brani
    ```
5565 e4a6c3b8 Olga Brani

5566 e4a6c3b8 Olga Brani
    @property attributes
5567 e4a6c3b8 Olga Brani
    @static
5568 e4a6c3b8 Olga Brani
    @type {Ember.Map}
5569 e4a6c3b8 Olga Brani
    @readOnly
5570 e4a6c3b8 Olga Brani
  */
5571 e4a6c3b8 Olga Brani
  attributes: Ember.computed(function() {
5572 e4a6c3b8 Olga Brani
    var map = Ember.Map.create();
5573 e4a6c3b8 Olga Brani
5574 e4a6c3b8 Olga Brani
    this.eachComputedProperty(function(name, meta) {
5575 e4a6c3b8 Olga Brani
      if (meta.isAttribute) {
5576 e4a6c3b8 Olga Brani
        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');
5577 e4a6c3b8 Olga Brani
5578 e4a6c3b8 Olga Brani
        meta.name = name;
5579 e4a6c3b8 Olga Brani
        map.set(name, meta);
5580 e4a6c3b8 Olga Brani
      }
5581 e4a6c3b8 Olga Brani
    });
5582 e4a6c3b8 Olga Brani
5583 e4a6c3b8 Olga Brani
    return map;
5584 e4a6c3b8 Olga Brani
  }),
5585 e4a6c3b8 Olga Brani
5586 e4a6c3b8 Olga Brani
  /**
5587 e4a6c3b8 Olga Brani
    A map whose keys are the attributes of the model (properties
5588 e4a6c3b8 Olga Brani
    described by DS.attr) and whose values are type of transformation
5589 e4a6c3b8 Olga Brani
    applied to each attribute. This map does not include any
5590 e4a6c3b8 Olga Brani
    attributes that do not have an transformation type.
5591 e4a6c3b8 Olga Brani

5592 e4a6c3b8 Olga Brani
    Example
5593 e4a6c3b8 Olga Brani

5594 e4a6c3b8 Olga Brani
    ```javascript
5595 e4a6c3b8 Olga Brani
    App.Person = DS.Model.extend({
5596 e4a6c3b8 Olga Brani
      firstName: attr(),
5597 e4a6c3b8 Olga Brani
      lastName: attr('string'),
5598 e4a6c3b8 Olga Brani
      birthday: attr('date')
5599 e4a6c3b8 Olga Brani
    });
5600 e4a6c3b8 Olga Brani

5601 e4a6c3b8 Olga Brani
    var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')
5602 e4a6c3b8 Olga Brani

5603 e4a6c3b8 Olga Brani
    transformedAttributes.forEach(function(field, type) {
5604 e4a6c3b8 Olga Brani
      console.log(field, type);
5605 e4a6c3b8 Olga Brani
    });
5606 e4a6c3b8 Olga Brani

5607 e4a6c3b8 Olga Brani
    // prints:
5608 e4a6c3b8 Olga Brani
    // lastName string
5609 e4a6c3b8 Olga Brani
    // birthday date
5610 e4a6c3b8 Olga Brani
    ```
5611 e4a6c3b8 Olga Brani

5612 e4a6c3b8 Olga Brani
    @property transformedAttributes
5613 e4a6c3b8 Olga Brani
    @static
5614 e4a6c3b8 Olga Brani
    @type {Ember.Map}
5615 e4a6c3b8 Olga Brani
    @readOnly
5616 e4a6c3b8 Olga Brani
  */
5617 e4a6c3b8 Olga Brani
  transformedAttributes: Ember.computed(function() {
5618 e4a6c3b8 Olga Brani
    var map = Ember.Map.create();
5619 e4a6c3b8 Olga Brani
5620 e4a6c3b8 Olga Brani
    this.eachAttribute(function(key, meta) {
5621 e4a6c3b8 Olga Brani
      if (meta.type) {
5622 e4a6c3b8 Olga Brani
        map.set(key, meta.type);
5623 e4a6c3b8 Olga Brani
      }
5624 e4a6c3b8 Olga Brani
    });
5625 e4a6c3b8 Olga Brani
5626 e4a6c3b8 Olga Brani
    return map;
5627 e4a6c3b8 Olga Brani
  }),
5628 e4a6c3b8 Olga Brani
5629 e4a6c3b8 Olga Brani
  /**
5630 e4a6c3b8 Olga Brani
    Iterates through the attributes of the model, calling the passed function on each
5631 e4a6c3b8 Olga Brani
    attribute.
5632 e4a6c3b8 Olga Brani

5633 e4a6c3b8 Olga Brani
    The callback method you provide should have the following signature (all
5634 e4a6c3b8 Olga Brani
    parameters are optional):
5635 e4a6c3b8 Olga Brani

5636 e4a6c3b8 Olga Brani
    ```javascript
5637 e4a6c3b8 Olga Brani
    function(name, meta);
5638 e4a6c3b8 Olga Brani
    ```
5639 e4a6c3b8 Olga Brani

5640 e4a6c3b8 Olga Brani
    - `name` the name of the current property in the iteration
5641 e4a6c3b8 Olga Brani
    - `meta` the meta object for the attribute property in the iteration
5642 e4a6c3b8 Olga Brani

5643 e4a6c3b8 Olga Brani
    Note that in addition to a callback, you can also pass an optional target
5644 e4a6c3b8 Olga Brani
    object that will be set as `this` on the context.
5645 e4a6c3b8 Olga Brani

5646 e4a6c3b8 Olga Brani
    Example
5647 e4a6c3b8 Olga Brani

5648 e4a6c3b8 Olga Brani
    ```javascript
5649 e4a6c3b8 Olga Brani
    App.Person = DS.Model.extend({
5650 e4a6c3b8 Olga Brani
      firstName: attr('string'),
5651 e4a6c3b8 Olga Brani
      lastName: attr('string'),
5652 e4a6c3b8 Olga Brani
      birthday: attr('date')
5653 e4a6c3b8 Olga Brani
    });
5654 e4a6c3b8 Olga Brani

5655 e4a6c3b8 Olga Brani
    App.Person.eachAttribute(function(name, meta) {
5656 e4a6c3b8 Olga Brani
      console.log(name, meta);
5657 e4a6c3b8 Olga Brani
    });
5658 e4a6c3b8 Olga Brani

5659 e4a6c3b8 Olga Brani
    // prints:
5660 e4a6c3b8 Olga Brani
    // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
5661 e4a6c3b8 Olga Brani
    // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
5662 e4a6c3b8 Olga Brani
    // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
5663 e4a6c3b8 Olga Brani
   ```
5664 e4a6c3b8 Olga Brani

5665 e4a6c3b8 Olga Brani
    @method eachAttribute
5666 e4a6c3b8 Olga Brani
    @param {Function} callback The callback to execute
5667 e4a6c3b8 Olga Brani
    @param {Object} [target] The target object to use
5668 e4a6c3b8 Olga Brani
    @static
5669 e4a6c3b8 Olga Brani
  */
5670 e4a6c3b8 Olga Brani
  eachAttribute: function(callback, binding) {
5671 e4a6c3b8 Olga Brani
    get(this, 'attributes').forEach(function(name, meta) {
5672 e4a6c3b8 Olga Brani
      callback.call(binding, name, meta);
5673 e4a6c3b8 Olga Brani
    }, binding);
5674 e4a6c3b8 Olga Brani
  },
5675 e4a6c3b8 Olga Brani
5676 e4a6c3b8 Olga Brani
  /**
5677 e4a6c3b8 Olga Brani
    Iterates through the transformedAttributes of the model, calling
5678 e4a6c3b8 Olga Brani
    the passed function on each attribute. Note the callback will not be
5679 e4a6c3b8 Olga Brani
    called for any attributes that do not have an transformation type.
5680 e4a6c3b8 Olga Brani

5681 e4a6c3b8 Olga Brani
    The callback method you provide should have the following signature (all
5682 e4a6c3b8 Olga Brani
    parameters are optional):
5683 e4a6c3b8 Olga Brani

5684 e4a6c3b8 Olga Brani
    ```javascript
5685 e4a6c3b8 Olga Brani
    function(name, type);
5686 e4a6c3b8 Olga Brani
    ```
5687 e4a6c3b8 Olga Brani

5688 e4a6c3b8 Olga Brani
    - `name` the name of the current property in the iteration
5689 e4a6c3b8 Olga Brani
    - `type` a tring contrining the name of the type of transformed
5690 e4a6c3b8 Olga Brani
      applied to the attribute
5691 e4a6c3b8 Olga Brani

5692 e4a6c3b8 Olga Brani
    Note that in addition to a callback, you can also pass an optional target
5693 e4a6c3b8 Olga Brani
    object that will be set as `this` on the context.
5694 e4a6c3b8 Olga Brani

5695 e4a6c3b8 Olga Brani
    Example
5696 e4a6c3b8 Olga Brani

5697 e4a6c3b8 Olga Brani
    ```javascript
5698 e4a6c3b8 Olga Brani
    App.Person = DS.Model.extend({
5699 e4a6c3b8 Olga Brani
      firstName: attr(),
5700 e4a6c3b8 Olga Brani
      lastName: attr('string'),
5701 e4a6c3b8 Olga Brani
      birthday: attr('date')
5702 e4a6c3b8 Olga Brani
    });
5703 e4a6c3b8 Olga Brani

5704 e4a6c3b8 Olga Brani
    App.Person.eachTransformedAttribute(function(name, type) {
5705 e4a6c3b8 Olga Brani
      console.log(name, type);
5706 e4a6c3b8 Olga Brani
    });
5707 e4a6c3b8 Olga Brani

5708 e4a6c3b8 Olga Brani
    // prints:
5709 e4a6c3b8 Olga Brani
    // lastName string
5710 e4a6c3b8 Olga Brani
    // birthday date
5711 e4a6c3b8 Olga Brani
   ```
5712 e4a6c3b8 Olga Brani

5713 e4a6c3b8 Olga Brani
    @method eachTransformedAttribute
5714 e4a6c3b8 Olga Brani
    @param {Function} callback The callback to execute
5715 e4a6c3b8 Olga Brani
    @param {Object} [target] The target object to use
5716 e4a6c3b8 Olga Brani
    @static
5717 e4a6c3b8 Olga Brani
  */
5718 e4a6c3b8 Olga Brani
  eachTransformedAttribute: function(callback, binding) {
5719 e4a6c3b8 Olga Brani
    get(this, 'transformedAttributes').forEach(function(name, type) {
5720 e4a6c3b8 Olga Brani
      callback.call(binding, name, type);
5721 e4a6c3b8 Olga Brani
    });
5722 e4a6c3b8 Olga Brani
  }
5723 e4a6c3b8 Olga Brani
});
5724 e4a6c3b8 Olga Brani
5725 e4a6c3b8 Olga Brani
5726 e4a6c3b8 Olga Brani
DS.Model.reopen({
5727 e4a6c3b8 Olga Brani
  eachAttribute: function(callback, binding) {
5728 e4a6c3b8 Olga Brani
    this.constructor.eachAttribute(callback, binding);
5729 e4a6c3b8 Olga Brani
  }
5730 e4a6c3b8 Olga Brani
});
5731 e4a6c3b8 Olga Brani
5732 e4a6c3b8 Olga Brani
function getDefaultValue(record, options, key) {
5733 e4a6c3b8 Olga Brani
  if (typeof options.defaultValue === "function") {
5734 e4a6c3b8 Olga Brani
    return options.defaultValue();
5735 e4a6c3b8 Olga Brani
  } else {
5736 e4a6c3b8 Olga Brani
    return options.defaultValue;
5737 e4a6c3b8 Olga Brani
  }
5738 e4a6c3b8 Olga Brani
}
5739 e4a6c3b8 Olga Brani
5740 e4a6c3b8 Olga Brani
function hasValue(record, key) {
5741 e4a6c3b8 Olga Brani
  return record._attributes.hasOwnProperty(key) ||
5742 e4a6c3b8 Olga Brani
         record._inFlightAttributes.hasOwnProperty(key) ||
5743 e4a6c3b8 Olga Brani
         record._data.hasOwnProperty(key);
5744 e4a6c3b8 Olga Brani
}
5745 e4a6c3b8 Olga Brani
5746 e4a6c3b8 Olga Brani
function getValue(record, key) {
5747 e4a6c3b8 Olga Brani
  if (record._attributes.hasOwnProperty(key)) {
5748 e4a6c3b8 Olga Brani
    return record._attributes[key];
5749 e4a6c3b8 Olga Brani
  } else if (record._inFlightAttributes.hasOwnProperty(key)) {
5750 e4a6c3b8 Olga Brani
    return record._inFlightAttributes[key];
5751 e4a6c3b8 Olga Brani
  } else {
5752 e4a6c3b8 Olga Brani
    return record._data[key];
5753 e4a6c3b8 Olga Brani
  }
5754 e4a6c3b8 Olga Brani
}
5755 e4a6c3b8 Olga Brani
5756 e4a6c3b8 Olga Brani
/**
5757 e4a6c3b8 Olga Brani
  `DS.attr` defines an attribute on a [DS.Model](DS.Model.html).
5758 e4a6c3b8 Olga Brani
  By default, attributes are passed through as-is, however you can specify an
5759 e4a6c3b8 Olga Brani
  optional type to have the value automatically transformed.
5760 e4a6c3b8 Olga Brani
  Ember Data ships with four basic transform types: `string`, `number`,
5761 e4a6c3b8 Olga Brani
  `boolean` and `date`. You can define your own transforms by subclassing
5762 e4a6c3b8 Olga Brani
  [DS.Transform](DS.Transform.html).
5763 e4a6c3b8 Olga Brani

5764 e4a6c3b8 Olga Brani
  `DS.attr` takes an optional hash as a second parameter, currently
5765 e4a6c3b8 Olga Brani
  supported options are:
5766 e4a6c3b8 Olga Brani

5767 e4a6c3b8 Olga Brani
  - `defaultValue`: Pass a string or a function to be called to set the attribute
5768 e4a6c3b8 Olga Brani
                    to a default value if none is supplied.
5769 e4a6c3b8 Olga Brani

5770 e4a6c3b8 Olga Brani
  Example
5771 e4a6c3b8 Olga Brani

5772 e4a6c3b8 Olga Brani
  ```javascript
5773 e4a6c3b8 Olga Brani
  var attr = DS.attr;
5774 e4a6c3b8 Olga Brani

5775 e4a6c3b8 Olga Brani
  App.User = DS.Model.extend({
5776 e4a6c3b8 Olga Brani
    username: attr('string'),
5777 e4a6c3b8 Olga Brani
    email: attr('string'),
5778 e4a6c3b8 Olga Brani
    verified: attr('boolean', {defaultValue: false})
5779 e4a6c3b8 Olga Brani
  });
5780 e4a6c3b8 Olga Brani
  ```
5781 e4a6c3b8 Olga Brani

5782 e4a6c3b8 Olga Brani
  @namespace
5783 e4a6c3b8 Olga Brani
  @method attr
5784 e4a6c3b8 Olga Brani
  @for DS
5785 e4a6c3b8 Olga Brani
  @param {String} type the attribute type
5786 e4a6c3b8 Olga Brani
  @param {Object} options a hash of options
5787 e4a6c3b8 Olga Brani
  @return {Attribute}
5788 e4a6c3b8 Olga Brani
*/
5789 e4a6c3b8 Olga Brani
5790 e4a6c3b8 Olga Brani
DS.attr = function(type, options) {
5791 e4a6c3b8 Olga Brani
  options = options || {};
5792 e4a6c3b8 Olga Brani
5793 e4a6c3b8 Olga Brani
  var meta = {
5794 e4a6c3b8 Olga Brani
    type: type,
5795 e4a6c3b8 Olga Brani
    isAttribute: true,
5796 e4a6c3b8 Olga Brani
    options: options
5797 e4a6c3b8 Olga Brani
  };
5798 e4a6c3b8 Olga Brani
5799 e4a6c3b8 Olga Brani
  return Ember.computed(function(key, value) {
5800 e4a6c3b8 Olga Brani
    if (arguments.length > 1) {
5801 e4a6c3b8 Olga Brani
      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');
5802 e4a6c3b8 Olga Brani
      var oldValue = this._attributes[key] || this._inFlightAttributes[key] || this._data[key];
5803 e4a6c3b8 Olga Brani
5804 e4a6c3b8 Olga Brani
      this.send('didSetProperty', {
5805 e4a6c3b8 Olga Brani
        name: key,
5806 e4a6c3b8 Olga Brani
        oldValue: oldValue,
5807 e4a6c3b8 Olga Brani
        originalValue: this._data[key],
5808 e4a6c3b8 Olga Brani
        value: value
5809 e4a6c3b8 Olga Brani
      });
5810 e4a6c3b8 Olga Brani
5811 e4a6c3b8 Olga Brani
      this._attributes[key] = value;
5812 e4a6c3b8 Olga Brani
      return value;
5813 e4a6c3b8 Olga Brani
    } else if (hasValue(this, key)) {
5814 e4a6c3b8 Olga Brani
      return getValue(this, key);
5815 e4a6c3b8 Olga Brani
    } else {
5816 e4a6c3b8 Olga Brani
      return getDefaultValue(this, options, key);
5817 e4a6c3b8 Olga Brani
    }
5818 e4a6c3b8 Olga Brani
5819 e4a6c3b8 Olga Brani
  // `data` is never set directly. However, it may be
5820 e4a6c3b8 Olga Brani
  // invalidated from the state manager's setData
5821 e4a6c3b8 Olga Brani
  // event.
5822 e4a6c3b8 Olga Brani
  }).property('data').meta(meta);
5823 e4a6c3b8 Olga Brani
};
5824 e4a6c3b8 Olga Brani
5825 e4a6c3b8 Olga Brani
5826 e4a6c3b8 Olga Brani
})();
5827 e4a6c3b8 Olga Brani
5828 e4a6c3b8 Olga Brani
5829 e4a6c3b8 Olga Brani
5830 e4a6c3b8 Olga Brani
(function() {
5831 e4a6c3b8 Olga Brani
/**
5832 e4a6c3b8 Olga Brani
  @module ember-data
5833 e4a6c3b8 Olga Brani
*/
5834 e4a6c3b8 Olga Brani
5835 e4a6c3b8 Olga Brani
})();
5836 e4a6c3b8 Olga Brani
5837 e4a6c3b8 Olga Brani
5838 e4a6c3b8 Olga Brani
5839 e4a6c3b8 Olga Brani
(function() {
5840 e4a6c3b8 Olga Brani
/**
5841 e4a6c3b8 Olga Brani
  @module ember-data
5842 e4a6c3b8 Olga Brani
*/
5843 e4a6c3b8 Olga Brani
5844 e4a6c3b8 Olga Brani
/**
5845 e4a6c3b8 Olga Brani
  An AttributeChange object is created whenever a record's
5846 e4a6c3b8 Olga Brani
  attribute changes value. It is used to track changes to a
5847 e4a6c3b8 Olga Brani
  record between transaction commits.
5848 e4a6c3b8 Olga Brani

5849 e4a6c3b8 Olga Brani
  @class AttributeChange
5850 e4a6c3b8 Olga Brani
  @namespace DS
5851 e4a6c3b8 Olga Brani
  @private
5852 e4a6c3b8 Olga Brani
  @constructor
5853 e4a6c3b8 Olga Brani
*/
5854 e4a6c3b8 Olga Brani
var AttributeChange = DS.AttributeChange = function(options) {
5855 e4a6c3b8 Olga Brani
  this.record = options.record;
5856 e4a6c3b8 Olga Brani
  this.store = options.store;
5857 e4a6c3b8 Olga Brani
  this.name = options.name;
5858 e4a6c3b8 Olga Brani
  this.value = options.value;
5859 e4a6c3b8 Olga Brani
  this.oldValue = options.oldValue;
5860 e4a6c3b8 Olga Brani
};
5861 e4a6c3b8 Olga Brani
5862 e4a6c3b8 Olga Brani
AttributeChange.createChange = function(options) {
5863 e4a6c3b8 Olga Brani
  return new AttributeChange(options);
5864 e4a6c3b8 Olga Brani
};
5865 e4a6c3b8 Olga Brani
5866 e4a6c3b8 Olga Brani
AttributeChange.prototype = {
5867 e4a6c3b8 Olga Brani
  sync: function() {
5868 e4a6c3b8 Olga Brani
    if (this.value !== this.oldValue) {
5869 e4a6c3b8 Olga Brani
      this.record.send('becomeDirty');
5870 e4a6c3b8 Olga Brani
      this.record.updateRecordArraysLater();
5871 e4a6c3b8 Olga Brani
    }
5872 e4a6c3b8 Olga Brani
5873 e4a6c3b8 Olga Brani
    // TODO: Use this object in the commit process
5874 e4a6c3b8 Olga Brani
    this.destroy();
5875 e4a6c3b8 Olga Brani
  },
5876 e4a6c3b8 Olga Brani
5877 e4a6c3b8 Olga Brani
  /**
5878 e4a6c3b8 Olga Brani
    If the AttributeChange is destroyed (either by being rolled back
5879 e4a6c3b8 Olga Brani
    or being committed), remove it from the list of pending changes
5880 e4a6c3b8 Olga Brani
    on the record.
5881 e4a6c3b8 Olga Brani

5882 e4a6c3b8 Olga Brani
    @method destroy
5883 e4a6c3b8 Olga Brani
  */
5884 e4a6c3b8 Olga Brani
  destroy: function() {
5885 e4a6c3b8 Olga Brani
    delete this.record._changesToSync[this.name];
5886 e4a6c3b8 Olga Brani
  }
5887 e4a6c3b8 Olga Brani
};
5888 e4a6c3b8 Olga Brani
5889 e4a6c3b8 Olga Brani
})();
5890 e4a6c3b8 Olga Brani
5891 e4a6c3b8 Olga Brani
5892 e4a6c3b8 Olga Brani
5893 e4a6c3b8 Olga Brani
(function() {
5894 e4a6c3b8 Olga Brani
/**
5895 e4a6c3b8 Olga Brani
  @module ember-data
5896 e4a6c3b8 Olga Brani
*/
5897 e4a6c3b8 Olga Brani
5898 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
5899 e4a6c3b8 Olga Brani
var forEach = Ember.EnumerableUtils.forEach;
5900 e4a6c3b8 Olga Brani
5901 e4a6c3b8 Olga Brani
/**
5902 e4a6c3b8 Olga Brani
  @class RelationshipChange
5903 e4a6c3b8 Olga Brani
  @namespace DS
5904 e4a6c3b8 Olga Brani
  @private
5905 e4a6c3b8 Olga Brani
  @construtor
5906 e4a6c3b8 Olga Brani
*/
5907 e4a6c3b8 Olga Brani
DS.RelationshipChange = function(options) {
5908 e4a6c3b8 Olga Brani
  this.parentRecord = options.parentRecord;
5909 e4a6c3b8 Olga Brani
  this.childRecord = options.childRecord;
5910 e4a6c3b8 Olga Brani
  this.firstRecord = options.firstRecord;
5911 e4a6c3b8 Olga Brani
  this.firstRecordKind = options.firstRecordKind;
5912 e4a6c3b8 Olga Brani
  this.firstRecordName = options.firstRecordName;
5913 e4a6c3b8 Olga Brani
  this.secondRecord = options.secondRecord;
5914 e4a6c3b8 Olga Brani
  this.secondRecordKind = options.secondRecordKind;
5915 e4a6c3b8 Olga Brani
  this.secondRecordName = options.secondRecordName;
5916 e4a6c3b8 Olga Brani
  this.changeType = options.changeType;
5917 e4a6c3b8 Olga Brani
  this.store = options.store;
5918 e4a6c3b8 Olga Brani
5919 e4a6c3b8 Olga Brani
  this.committed = {};
5920 e4a6c3b8 Olga Brani
};
5921 e4a6c3b8 Olga Brani
5922 e4a6c3b8 Olga Brani
/**
5923 e4a6c3b8 Olga Brani
  @class RelationshipChangeAdd
5924 e4a6c3b8 Olga Brani
  @namespace DS
5925 e4a6c3b8 Olga Brani
  @private
5926 e4a6c3b8 Olga Brani
  @construtor
5927 e4a6c3b8 Olga Brani
*/
5928 e4a6c3b8 Olga Brani
DS.RelationshipChangeAdd = function(options){
5929 e4a6c3b8 Olga Brani
  DS.RelationshipChange.call(this, options);
5930 e4a6c3b8 Olga Brani
};
5931 e4a6c3b8 Olga Brani
5932 e4a6c3b8 Olga Brani
/**
5933 e4a6c3b8 Olga Brani
  @class RelationshipChangeRemove
5934 e4a6c3b8 Olga Brani
  @namespace DS
5935 e4a6c3b8 Olga Brani
  @private
5936 e4a6c3b8 Olga Brani
  @construtor
5937 e4a6c3b8 Olga Brani
*/
5938 e4a6c3b8 Olga Brani
DS.RelationshipChangeRemove = function(options){
5939 e4a6c3b8 Olga Brani
  DS.RelationshipChange.call(this, options);
5940 e4a6c3b8 Olga Brani
};
5941 e4a6c3b8 Olga Brani
5942 e4a6c3b8 Olga Brani
DS.RelationshipChange.create = function(options) {
5943 e4a6c3b8 Olga Brani
  return new DS.RelationshipChange(options);
5944 e4a6c3b8 Olga Brani
};
5945 e4a6c3b8 Olga Brani
5946 e4a6c3b8 Olga Brani
DS.RelationshipChangeAdd.create = function(options) {
5947 e4a6c3b8 Olga Brani
  return new DS.RelationshipChangeAdd(options);
5948 e4a6c3b8 Olga Brani
};
5949 e4a6c3b8 Olga Brani
5950 e4a6c3b8 Olga Brani
DS.RelationshipChangeRemove.create = function(options) {
5951 e4a6c3b8 Olga Brani
  return new DS.RelationshipChangeRemove(options);
5952 e4a6c3b8 Olga Brani
};
5953 e4a6c3b8 Olga Brani
5954 e4a6c3b8 Olga Brani
DS.OneToManyChange = {};
5955 e4a6c3b8 Olga Brani
DS.OneToNoneChange = {};
5956 e4a6c3b8 Olga Brani
DS.ManyToNoneChange = {};
5957 e4a6c3b8 Olga Brani
DS.OneToOneChange = {};
5958 e4a6c3b8 Olga Brani
DS.ManyToManyChange = {};
5959 e4a6c3b8 Olga Brani
5960 e4a6c3b8 Olga Brani
DS.RelationshipChange._createChange = function(options){
5961 e4a6c3b8 Olga Brani
  if(options.changeType === "add"){
5962 e4a6c3b8 Olga Brani
    return DS.RelationshipChangeAdd.create(options);
5963 e4a6c3b8 Olga Brani
  }
5964 e4a6c3b8 Olga Brani
  if(options.changeType === "remove"){
5965 e4a6c3b8 Olga Brani
    return DS.RelationshipChangeRemove.create(options);
5966 e4a6c3b8 Olga Brani
  }
5967 e4a6c3b8 Olga Brani
};
5968 e4a6c3b8 Olga Brani
5969 e4a6c3b8 Olga Brani
5970 e4a6c3b8 Olga Brani
DS.RelationshipChange.determineRelationshipType = function(recordType, knownSide){
5971 e4a6c3b8 Olga Brani
  var knownKey = knownSide.key, key, otherKind;
5972 e4a6c3b8 Olga Brani
  var knownKind = knownSide.kind;
5973 e4a6c3b8 Olga Brani
5974 e4a6c3b8 Olga Brani
  var inverse = recordType.inverseFor(knownKey);
5975 e4a6c3b8 Olga Brani
5976 e4a6c3b8 Olga Brani
  if (inverse){
5977 e4a6c3b8 Olga Brani
    key = inverse.name;
5978 e4a6c3b8 Olga Brani
    otherKind = inverse.kind;
5979 e4a6c3b8 Olga Brani
  }
5980 e4a6c3b8 Olga Brani
5981 e4a6c3b8 Olga Brani
  if (!inverse){
5982 e4a6c3b8 Olga Brani
    return knownKind === "belongsTo" ? "oneToNone" : "manyToNone";
5983 e4a6c3b8 Olga Brani
  }
5984 e4a6c3b8 Olga Brani
  else{
5985 e4a6c3b8 Olga Brani
    if(otherKind === "belongsTo"){
5986 e4a6c3b8 Olga Brani
      return knownKind === "belongsTo" ? "oneToOne" : "manyToOne";
5987 e4a6c3b8 Olga Brani
    }
5988 e4a6c3b8 Olga Brani
    else{
5989 e4a6c3b8 Olga Brani
      return knownKind === "belongsTo" ? "oneToMany" : "manyToMany";
5990 e4a6c3b8 Olga Brani
    }
5991 e4a6c3b8 Olga Brani
  }
5992 e4a6c3b8 Olga Brani
5993 e4a6c3b8 Olga Brani
};
5994 e4a6c3b8 Olga Brani
5995 e4a6c3b8 Olga Brani
DS.RelationshipChange.createChange = function(firstRecord, secondRecord, store, options){
5996 e4a6c3b8 Olga Brani
  // Get the type of the child based on the child's client ID
5997 e4a6c3b8 Olga Brani
  var firstRecordType = firstRecord.constructor, changeType;
5998 e4a6c3b8 Olga Brani
  changeType = DS.RelationshipChange.determineRelationshipType(firstRecordType, options);
5999 e4a6c3b8 Olga Brani
  if (changeType === "oneToMany"){
6000 e4a6c3b8 Olga Brani
    return DS.OneToManyChange.createChange(firstRecord, secondRecord, store, options);
6001 e4a6c3b8 Olga Brani
  }
6002 e4a6c3b8 Olga Brani
  else if (changeType === "manyToOne"){
6003 e4a6c3b8 Olga Brani
    return DS.OneToManyChange.createChange(secondRecord, firstRecord, store, options);
6004 e4a6c3b8 Olga Brani
  }
6005 e4a6c3b8 Olga Brani
  else if (changeType === "oneToNone"){
6006 e4a6c3b8 Olga Brani
    return DS.OneToNoneChange.createChange(firstRecord, secondRecord, store, options);
6007 e4a6c3b8 Olga Brani
  }
6008 e4a6c3b8 Olga Brani
  else if (changeType === "manyToNone"){
6009 e4a6c3b8 Olga Brani
    return DS.ManyToNoneChange.createChange(firstRecord, secondRecord, store, options);
6010 e4a6c3b8 Olga Brani
  }
6011 e4a6c3b8 Olga Brani
  else if (changeType === "oneToOne"){
6012 e4a6c3b8 Olga Brani
    return DS.OneToOneChange.createChange(firstRecord, secondRecord, store, options);
6013 e4a6c3b8 Olga Brani
  }
6014 e4a6c3b8 Olga Brani
  else if (changeType === "manyToMany"){
6015 e4a6c3b8 Olga Brani
    return DS.ManyToManyChange.createChange(firstRecord, secondRecord, store, options);
6016 e4a6c3b8 Olga Brani
  }
6017 e4a6c3b8 Olga Brani
};
6018 e4a6c3b8 Olga Brani
6019 e4a6c3b8 Olga Brani
DS.OneToNoneChange.createChange = function(childRecord, parentRecord, store, options) {
6020 e4a6c3b8 Olga Brani
  var key = options.key;
6021 e4a6c3b8 Olga Brani
  var change = DS.RelationshipChange._createChange({
6022 e4a6c3b8 Olga Brani
      parentRecord: parentRecord,
6023 e4a6c3b8 Olga Brani
      childRecord: childRecord,
6024 e4a6c3b8 Olga Brani
      firstRecord: childRecord,
6025 e4a6c3b8 Olga Brani
      store: store,
6026 e4a6c3b8 Olga Brani
      changeType: options.changeType,
6027 e4a6c3b8 Olga Brani
      firstRecordName: key,
6028 e4a6c3b8 Olga Brani
      firstRecordKind: "belongsTo"
6029 e4a6c3b8 Olga Brani
  });
6030 e4a6c3b8 Olga Brani
6031 e4a6c3b8 Olga Brani
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6032 e4a6c3b8 Olga Brani
6033 e4a6c3b8 Olga Brani
  return change;
6034 e4a6c3b8 Olga Brani
};
6035 e4a6c3b8 Olga Brani
6036 e4a6c3b8 Olga Brani
DS.ManyToNoneChange.createChange = function(childRecord, parentRecord, store, options) {
6037 e4a6c3b8 Olga Brani
  var key = options.key;
6038 e4a6c3b8 Olga Brani
  var change = DS.RelationshipChange._createChange({
6039 e4a6c3b8 Olga Brani
      parentRecord: childRecord,
6040 e4a6c3b8 Olga Brani
      childRecord: parentRecord,
6041 e4a6c3b8 Olga Brani
      secondRecord: childRecord,
6042 e4a6c3b8 Olga Brani
      store: store,
6043 e4a6c3b8 Olga Brani
      changeType: options.changeType,
6044 e4a6c3b8 Olga Brani
      secondRecordName: options.key,
6045 e4a6c3b8 Olga Brani
      secondRecordKind: "hasMany"
6046 e4a6c3b8 Olga Brani
  });
6047 e4a6c3b8 Olga Brani
6048 e4a6c3b8 Olga Brani
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6049 e4a6c3b8 Olga Brani
  return change;
6050 e4a6c3b8 Olga Brani
};
6051 e4a6c3b8 Olga Brani
6052 e4a6c3b8 Olga Brani
6053 e4a6c3b8 Olga Brani
DS.ManyToManyChange.createChange = function(childRecord, parentRecord, store, options) {
6054 e4a6c3b8 Olga Brani
  // If the name of the belongsTo side of the relationship is specified,
6055 e4a6c3b8 Olga Brani
  // use that
6056 e4a6c3b8 Olga Brani
  // If the type of the parent is specified, look it up on the child's type
6057 e4a6c3b8 Olga Brani
  // definition.
6058 e4a6c3b8 Olga Brani
  var key = options.key;
6059 e4a6c3b8 Olga Brani
6060 e4a6c3b8 Olga Brani
  var change = DS.RelationshipChange._createChange({
6061 e4a6c3b8 Olga Brani
      parentRecord: parentRecord,
6062 e4a6c3b8 Olga Brani
      childRecord: childRecord,
6063 e4a6c3b8 Olga Brani
      firstRecord: childRecord,
6064 e4a6c3b8 Olga Brani
      secondRecord: parentRecord,
6065 e4a6c3b8 Olga Brani
      firstRecordKind: "hasMany",
6066 e4a6c3b8 Olga Brani
      secondRecordKind: "hasMany",
6067 e4a6c3b8 Olga Brani
      store: store,
6068 e4a6c3b8 Olga Brani
      changeType: options.changeType,
6069 e4a6c3b8 Olga Brani
      firstRecordName:  key
6070 e4a6c3b8 Olga Brani
  });
6071 e4a6c3b8 Olga Brani
6072 e4a6c3b8 Olga Brani
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6073 e4a6c3b8 Olga Brani
6074 e4a6c3b8 Olga Brani
6075 e4a6c3b8 Olga Brani
  return change;
6076 e4a6c3b8 Olga Brani
};
6077 e4a6c3b8 Olga Brani
6078 e4a6c3b8 Olga Brani
DS.OneToOneChange.createChange = function(childRecord, parentRecord, store, options) {
6079 e4a6c3b8 Olga Brani
  var key;
6080 e4a6c3b8 Olga Brani
6081 e4a6c3b8 Olga Brani
  // If the name of the belongsTo side of the relationship is specified,
6082 e4a6c3b8 Olga Brani
  // use that
6083 e4a6c3b8 Olga Brani
  // If the type of the parent is specified, look it up on the child's type
6084 e4a6c3b8 Olga Brani
  // definition.
6085 e4a6c3b8 Olga Brani
  if (options.parentType) {
6086 e4a6c3b8 Olga Brani
    key = options.parentType.inverseFor(options.key).name;
6087 e4a6c3b8 Olga Brani
  } else if (options.key) {
6088 e4a6c3b8 Olga Brani
    key = options.key;
6089 e4a6c3b8 Olga Brani
  } else {
6090 e4a6c3b8 Olga Brani
    Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false);
6091 e4a6c3b8 Olga Brani
  }
6092 e4a6c3b8 Olga Brani
6093 e4a6c3b8 Olga Brani
  var change = DS.RelationshipChange._createChange({
6094 e4a6c3b8 Olga Brani
      parentRecord: parentRecord,
6095 e4a6c3b8 Olga Brani
      childRecord: childRecord,
6096 e4a6c3b8 Olga Brani
      firstRecord: childRecord,
6097 e4a6c3b8 Olga Brani
      secondRecord: parentRecord,
6098 e4a6c3b8 Olga Brani
      firstRecordKind: "belongsTo",
6099 e4a6c3b8 Olga Brani
      secondRecordKind: "belongsTo",
6100 e4a6c3b8 Olga Brani
      store: store,
6101 e4a6c3b8 Olga Brani
      changeType: options.changeType,
6102 e4a6c3b8 Olga Brani
      firstRecordName:  key
6103 e4a6c3b8 Olga Brani
  });
6104 e4a6c3b8 Olga Brani
6105 e4a6c3b8 Olga Brani
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6106 e4a6c3b8 Olga Brani
6107 e4a6c3b8 Olga Brani
6108 e4a6c3b8 Olga Brani
  return change;
6109 e4a6c3b8 Olga Brani
};
6110 e4a6c3b8 Olga Brani
6111 e4a6c3b8 Olga Brani
DS.OneToOneChange.maintainInvariant = function(options, store, childRecord, key){
6112 e4a6c3b8 Olga Brani
  if (options.changeType === "add" && store.recordIsMaterialized(childRecord)) {
6113 e4a6c3b8 Olga Brani
    var oldParent = get(childRecord, key);
6114 e4a6c3b8 Olga Brani
    if (oldParent){
6115 e4a6c3b8 Olga Brani
      var correspondingChange = DS.OneToOneChange.createChange(childRecord, oldParent, store, {
6116 e4a6c3b8 Olga Brani
          parentType: options.parentType,
6117 e4a6c3b8 Olga Brani
          hasManyName: options.hasManyName,
6118 e4a6c3b8 Olga Brani
          changeType: "remove",
6119 e4a6c3b8 Olga Brani
          key: options.key
6120 e4a6c3b8 Olga Brani
        });
6121 e4a6c3b8 Olga Brani
      store.addRelationshipChangeFor(childRecord, key, options.parentRecord , null, correspondingChange);
6122 e4a6c3b8 Olga Brani
     correspondingChange.sync();
6123 e4a6c3b8 Olga Brani
    }
6124 e4a6c3b8 Olga Brani
  }
6125 e4a6c3b8 Olga Brani
};
6126 e4a6c3b8 Olga Brani
6127 e4a6c3b8 Olga Brani
DS.OneToManyChange.createChange = function(childRecord, parentRecord, store, options) {
6128 e4a6c3b8 Olga Brani
  var key;
6129 e4a6c3b8 Olga Brani
6130 e4a6c3b8 Olga Brani
  // If the name of the belongsTo side of the relationship is specified,
6131 e4a6c3b8 Olga Brani
  // use that
6132 e4a6c3b8 Olga Brani
  // If the type of the parent is specified, look it up on the child's type
6133 e4a6c3b8 Olga Brani
  // definition.
6134 e4a6c3b8 Olga Brani
  if (options.parentType) {
6135 e4a6c3b8 Olga Brani
    key = options.parentType.inverseFor(options.key).name;
6136 e4a6c3b8 Olga Brani
    DS.OneToManyChange.maintainInvariant( options, store, childRecord, key );
6137 e4a6c3b8 Olga Brani
  } else if (options.key) {
6138 e4a6c3b8 Olga Brani
    key = options.key;
6139 e4a6c3b8 Olga Brani
  } else {
6140 e4a6c3b8 Olga Brani
    Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false);
6141 e4a6c3b8 Olga Brani
  }
6142 e4a6c3b8 Olga Brani
6143 e4a6c3b8 Olga Brani
  var change = DS.RelationshipChange._createChange({
6144 e4a6c3b8 Olga Brani
      parentRecord: parentRecord,
6145 e4a6c3b8 Olga Brani
      childRecord: childRecord,
6146 e4a6c3b8 Olga Brani
      firstRecord: childRecord,
6147 e4a6c3b8 Olga Brani
      secondRecord: parentRecord,
6148 e4a6c3b8 Olga Brani
      firstRecordKind: "belongsTo",
6149 e4a6c3b8 Olga Brani
      secondRecordKind: "hasMany",
6150 e4a6c3b8 Olga Brani
      store: store,
6151 e4a6c3b8 Olga Brani
      changeType: options.changeType,
6152 e4a6c3b8 Olga Brani
      firstRecordName:  key
6153 e4a6c3b8 Olga Brani
  });
6154 e4a6c3b8 Olga Brani
6155 e4a6c3b8 Olga Brani
  store.addRelationshipChangeFor(childRecord, key, parentRecord, change.getSecondRecordName(), change);
6156 e4a6c3b8 Olga Brani
6157 e4a6c3b8 Olga Brani
6158 e4a6c3b8 Olga Brani
  return change;
6159 e4a6c3b8 Olga Brani
};
6160 e4a6c3b8 Olga Brani
6161 e4a6c3b8 Olga Brani
6162 e4a6c3b8 Olga Brani
DS.OneToManyChange.maintainInvariant = function(options, store, childRecord, key){
6163 e4a6c3b8 Olga Brani
  if (options.changeType === "add" && childRecord) {
6164 e4a6c3b8 Olga Brani
    var oldParent = get(childRecord, key);
6165 e4a6c3b8 Olga Brani
    if (oldParent){
6166 e4a6c3b8 Olga Brani
      var correspondingChange = DS.OneToManyChange.createChange(childRecord, oldParent, store, {
6167 e4a6c3b8 Olga Brani
          parentType: options.parentType,
6168 e4a6c3b8 Olga Brani
          hasManyName: options.hasManyName,
6169 e4a6c3b8 Olga Brani
          changeType: "remove",
6170 e4a6c3b8 Olga Brani
          key: options.key
6171 e4a6c3b8 Olga Brani
        });
6172 e4a6c3b8 Olga Brani
      store.addRelationshipChangeFor(childRecord, key, options.parentRecord, correspondingChange.getSecondRecordName(), correspondingChange);
6173 e4a6c3b8 Olga Brani
      correspondingChange.sync();
6174 e4a6c3b8 Olga Brani
    }
6175 e4a6c3b8 Olga Brani
  }
6176 e4a6c3b8 Olga Brani
};
6177 e4a6c3b8 Olga Brani
6178 e4a6c3b8 Olga Brani
/**
6179 e4a6c3b8 Olga Brani
  @class RelationshipChange
6180 e4a6c3b8 Olga Brani
  @namespace DS
6181 e4a6c3b8 Olga Brani
*/
6182 e4a6c3b8 Olga Brani
DS.RelationshipChange.prototype = {
6183 e4a6c3b8 Olga Brani
6184 e4a6c3b8 Olga Brani
  getSecondRecordName: function() {
6185 e4a6c3b8 Olga Brani
    var name = this.secondRecordName, parent;
6186 e4a6c3b8 Olga Brani
6187 e4a6c3b8 Olga Brani
    if (!name) {
6188 e4a6c3b8 Olga Brani
      parent = this.secondRecord;
6189 e4a6c3b8 Olga Brani
      if (!parent) { return; }
6190 e4a6c3b8 Olga Brani
6191 e4a6c3b8 Olga Brani
      var childType = this.firstRecord.constructor;
6192 e4a6c3b8 Olga Brani
      var inverse = childType.inverseFor(this.firstRecordName);
6193 e4a6c3b8 Olga Brani
      this.secondRecordName = inverse.name;
6194 e4a6c3b8 Olga Brani
    }
6195 e4a6c3b8 Olga Brani
6196 e4a6c3b8 Olga Brani
    return this.secondRecordName;
6197 e4a6c3b8 Olga Brani
  },
6198 e4a6c3b8 Olga Brani
6199 e4a6c3b8 Olga Brani
  /**
6200 e4a6c3b8 Olga Brani
    Get the name of the relationship on the belongsTo side.
6201 e4a6c3b8 Olga Brani

6202 e4a6c3b8 Olga Brani
    @method getFirstRecordName
6203 e4a6c3b8 Olga Brani
    @return {String}
6204 e4a6c3b8 Olga Brani
  */
6205 e4a6c3b8 Olga Brani
  getFirstRecordName: function() {
6206 e4a6c3b8 Olga Brani
    var name = this.firstRecordName;
6207 e4a6c3b8 Olga Brani
    return name;
6208 e4a6c3b8 Olga Brani
  },
6209 e4a6c3b8 Olga Brani
6210 e4a6c3b8 Olga Brani
  /**
6211 e4a6c3b8 Olga Brani
    @method destroy
6212 e4a6c3b8 Olga Brani
    @private
6213 e4a6c3b8 Olga Brani
  */
6214 e4a6c3b8 Olga Brani
  destroy: function() {
6215 e4a6c3b8 Olga Brani
    var childRecord = this.childRecord,
6216 e4a6c3b8 Olga Brani
        belongsToName = this.getFirstRecordName(),
6217 e4a6c3b8 Olga Brani
        hasManyName = this.getSecondRecordName(),
6218 e4a6c3b8 Olga Brani
        store = this.store;
6219 e4a6c3b8 Olga Brani
6220 e4a6c3b8 Olga Brani
    store.removeRelationshipChangeFor(childRecord, belongsToName, this.parentRecord, hasManyName, this.changeType);
6221 e4a6c3b8 Olga Brani
  },
6222 e4a6c3b8 Olga Brani
6223 e4a6c3b8 Olga Brani
  getSecondRecord: function(){
6224 e4a6c3b8 Olga Brani
    return this.secondRecord;
6225 e4a6c3b8 Olga Brani
  },
6226 e4a6c3b8 Olga Brani
6227 e4a6c3b8 Olga Brani
  /**
6228 e4a6c3b8 Olga Brani
    @method getFirstRecord
6229 e4a6c3b8 Olga Brani
    @private
6230 e4a6c3b8 Olga Brani
  */
6231 e4a6c3b8 Olga Brani
  getFirstRecord: function() {
6232 e4a6c3b8 Olga Brani
    return this.firstRecord;
6233 e4a6c3b8 Olga Brani
  },
6234 e4a6c3b8 Olga Brani
6235 e4a6c3b8 Olga Brani
  coalesce: function(){
6236 e4a6c3b8 Olga Brani
    var relationshipPairs = this.store.relationshipChangePairsFor(this.firstRecord);
6237 e4a6c3b8 Olga Brani
    forEach(relationshipPairs, function(pair){
6238 e4a6c3b8 Olga Brani
      var addedChange = pair["add"];
6239 e4a6c3b8 Olga Brani
      var removedChange = pair["remove"];
6240 e4a6c3b8 Olga Brani
      if(addedChange && removedChange) {
6241 e4a6c3b8 Olga Brani
        addedChange.destroy();
6242 e4a6c3b8 Olga Brani
        removedChange.destroy();
6243 e4a6c3b8 Olga Brani
      }
6244 e4a6c3b8 Olga Brani
    });
6245 e4a6c3b8 Olga Brani
  }
6246 e4a6c3b8 Olga Brani
};
6247 e4a6c3b8 Olga Brani
6248 e4a6c3b8 Olga Brani
DS.RelationshipChangeAdd.prototype = Ember.create(DS.RelationshipChange.create({}));
6249 e4a6c3b8 Olga Brani
DS.RelationshipChangeRemove.prototype = Ember.create(DS.RelationshipChange.create({}));
6250 e4a6c3b8 Olga Brani
6251 e4a6c3b8 Olga Brani
// the object is a value, and not a promise
6252 e4a6c3b8 Olga Brani
function isValue(object) {
6253 e4a6c3b8 Olga Brani
  return typeof object === 'object' && (!object.then || typeof object.then !== 'function');
6254 e4a6c3b8 Olga Brani
}
6255 e4a6c3b8 Olga Brani
6256 e4a6c3b8 Olga Brani
DS.RelationshipChangeAdd.prototype.changeType = "add";
6257 e4a6c3b8 Olga Brani
DS.RelationshipChangeAdd.prototype.sync = function() {
6258 e4a6c3b8 Olga Brani
  var secondRecordName = this.getSecondRecordName(),
6259 e4a6c3b8 Olga Brani
      firstRecordName = this.getFirstRecordName(),
6260 e4a6c3b8 Olga Brani
      firstRecord = this.getFirstRecord(),
6261 e4a6c3b8 Olga Brani
      secondRecord = this.getSecondRecord();
6262 e4a6c3b8 Olga Brani
6263 e4a6c3b8 Olga Brani
  //Ember.assert("You specified a hasMany (" + hasManyName + ") on " + (!belongsToName && (newParent || oldParent || this.lastParent).constructor) + " but did not specify an inverse belongsTo on " + child.constructor, belongsToName);
6264 e4a6c3b8 Olga Brani
  //Ember.assert("You specified a belongsTo (" + belongsToName + ") on " + child.constructor + " but did not specify an inverse hasMany on " + (!hasManyName && (newParent || oldParent || this.lastParentRecord).constructor), hasManyName);
6265 e4a6c3b8 Olga Brani
6266 e4a6c3b8 Olga Brani
  if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) {
6267 e4a6c3b8 Olga Brani
    if(this.secondRecordKind === "belongsTo"){
6268 e4a6c3b8 Olga Brani
      secondRecord.suspendRelationshipObservers(function(){
6269 e4a6c3b8 Olga Brani
        set(secondRecord, secondRecordName, firstRecord);
6270 e4a6c3b8 Olga Brani
      });
6271 e4a6c3b8 Olga Brani
6272 e4a6c3b8 Olga Brani
     }
6273 e4a6c3b8 Olga Brani
     else if(this.secondRecordKind === "hasMany"){
6274 e4a6c3b8 Olga Brani
      secondRecord.suspendRelationshipObservers(function(){
6275 e4a6c3b8 Olga Brani
        var relationship = get(secondRecord, secondRecordName);
6276 e4a6c3b8 Olga Brani
        if (isValue(relationship)) { relationship.addObject(firstRecord); }
6277 e4a6c3b8 Olga Brani
      });
6278 e4a6c3b8 Olga Brani
    }
6279 e4a6c3b8 Olga Brani
  }
6280 e4a6c3b8 Olga Brani
6281 e4a6c3b8 Olga Brani
  if (firstRecord instanceof DS.Model && secondRecord instanceof DS.Model && get(firstRecord, firstRecordName) !== secondRecord) {
6282 e4a6c3b8 Olga Brani
    if(this.firstRecordKind === "belongsTo"){
6283 e4a6c3b8 Olga Brani
      firstRecord.suspendRelationshipObservers(function(){
6284 e4a6c3b8 Olga Brani
        set(firstRecord, firstRecordName, secondRecord);
6285 e4a6c3b8 Olga Brani
      });
6286 e4a6c3b8 Olga Brani
    }
6287 e4a6c3b8 Olga Brani
    else if(this.firstRecordKind === "hasMany"){
6288 e4a6c3b8 Olga Brani
      firstRecord.suspendRelationshipObservers(function(){
6289 e4a6c3b8 Olga Brani
        var relationship = get(firstRecord, firstRecordName);
6290 e4a6c3b8 Olga Brani
        if (isValue(relationship)) { relationship.addObject(secondRecord); }
6291 e4a6c3b8 Olga Brani
      });
6292 e4a6c3b8 Olga Brani
    }
6293 e4a6c3b8 Olga Brani
  }
6294 e4a6c3b8 Olga Brani
6295 e4a6c3b8 Olga Brani
  this.coalesce();
6296 e4a6c3b8 Olga Brani
};
6297 e4a6c3b8 Olga Brani
6298 e4a6c3b8 Olga Brani
DS.RelationshipChangeRemove.prototype.changeType = "remove";
6299 e4a6c3b8 Olga Brani
DS.RelationshipChangeRemove.prototype.sync = function() {
6300 e4a6c3b8 Olga Brani
  var secondRecordName = this.getSecondRecordName(),
6301 e4a6c3b8 Olga Brani
      firstRecordName = this.getFirstRecordName(),
6302 e4a6c3b8 Olga Brani
      firstRecord = this.getFirstRecord(),
6303 e4a6c3b8 Olga Brani
      secondRecord = this.getSecondRecord();
6304 e4a6c3b8 Olga Brani
6305 e4a6c3b8 Olga Brani
  //Ember.assert("You specified a hasMany (" + hasManyName + ") on " + (!belongsToName && (newParent || oldParent || this.lastParent).constructor) + " but did not specify an inverse belongsTo on " + child.constructor, belongsToName);
6306 e4a6c3b8 Olga Brani
  //Ember.assert("You specified a belongsTo (" + belongsToName + ") on " + child.constructor + " but did not specify an inverse hasMany on " + (!hasManyName && (newParent || oldParent || this.lastParentRecord).constructor), hasManyName);
6307 e4a6c3b8 Olga Brani
6308 e4a6c3b8 Olga Brani
  if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) {
6309 e4a6c3b8 Olga Brani
    if(this.secondRecordKind === "belongsTo"){
6310 e4a6c3b8 Olga Brani
      secondRecord.suspendRelationshipObservers(function(){
6311 e4a6c3b8 Olga Brani
        set(secondRecord, secondRecordName, null);
6312 e4a6c3b8 Olga Brani
      });
6313 e4a6c3b8 Olga Brani
    }
6314 e4a6c3b8 Olga Brani
    else if(this.secondRecordKind === "hasMany"){
6315 e4a6c3b8 Olga Brani
      secondRecord.suspendRelationshipObservers(function(){
6316 e4a6c3b8 Olga Brani
        var relationship = get(secondRecord, secondRecordName);
6317 e4a6c3b8 Olga Brani
        if (isValue(relationship)) { relationship.removeObject(firstRecord); }
6318 e4a6c3b8 Olga Brani
      });
6319 e4a6c3b8 Olga Brani
    }
6320 e4a6c3b8 Olga Brani
  }
6321 e4a6c3b8 Olga Brani
6322 e4a6c3b8 Olga Brani
  if (firstRecord instanceof DS.Model && get(firstRecord, firstRecordName)) {
6323 e4a6c3b8 Olga Brani
    if(this.firstRecordKind === "belongsTo"){
6324 e4a6c3b8 Olga Brani
      firstRecord.suspendRelationshipObservers(function(){
6325 e4a6c3b8 Olga Brani
        set(firstRecord, firstRecordName, null);
6326 e4a6c3b8 Olga Brani
      });
6327 e4a6c3b8 Olga Brani
     }
6328 e4a6c3b8 Olga Brani
     else if(this.firstRecordKind === "hasMany"){
6329 e4a6c3b8 Olga Brani
       firstRecord.suspendRelationshipObservers(function(){
6330 e4a6c3b8 Olga Brani
         var relationship = get(firstRecord, firstRecordName);
6331 e4a6c3b8 Olga Brani
         if (isValue(relationship)) { relationship.removeObject(secondRecord); }
6332 e4a6c3b8 Olga Brani
      });
6333 e4a6c3b8 Olga Brani
    }
6334 e4a6c3b8 Olga Brani
  }
6335 e4a6c3b8 Olga Brani
6336 e4a6c3b8 Olga Brani
  this.coalesce();
6337 e4a6c3b8 Olga Brani
};
6338 e4a6c3b8 Olga Brani
6339 e4a6c3b8 Olga Brani
})();
6340 e4a6c3b8 Olga Brani
6341 e4a6c3b8 Olga Brani
6342 e4a6c3b8 Olga Brani
6343 e4a6c3b8 Olga Brani
(function() {
6344 e4a6c3b8 Olga Brani
/**
6345 e4a6c3b8 Olga Brani
  @module ember-data
6346 e4a6c3b8 Olga Brani
*/
6347 e4a6c3b8 Olga Brani
6348 e4a6c3b8 Olga Brani
})();
6349 e4a6c3b8 Olga Brani
6350 e4a6c3b8 Olga Brani
6351 e4a6c3b8 Olga Brani
6352 e4a6c3b8 Olga Brani
(function() {
6353 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set,
6354 e4a6c3b8 Olga Brani
    isNone = Ember.isNone;
6355 e4a6c3b8 Olga Brani
6356 e4a6c3b8 Olga Brani
/**
6357 e4a6c3b8 Olga Brani
  @module ember-data
6358 e4a6c3b8 Olga Brani
*/
6359 e4a6c3b8 Olga Brani
6360 e4a6c3b8 Olga Brani
function asyncBelongsTo(type, options, meta) {
6361 e4a6c3b8 Olga Brani
  return Ember.computed(function(key, value) {
6362 e4a6c3b8 Olga Brani
    var data = get(this, 'data'),
6363 e4a6c3b8 Olga Brani
        store = get(this, 'store'),
6364 e4a6c3b8 Olga Brani
        promiseLabel = "DS: Async belongsTo " + this + " : " + key;
6365 e4a6c3b8 Olga Brani
6366 e4a6c3b8 Olga Brani
    if (arguments.length === 2) {
6367 e4a6c3b8 Olga Brani
      Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof store.modelFor(type));
6368 e4a6c3b8 Olga Brani
      return value === undefined ? null : DS.PromiseObject.create({ promise: Ember.RSVP.resolve(value, promiseLabel) });
6369 e4a6c3b8 Olga Brani
    }
6370 e4a6c3b8 Olga Brani
6371 e4a6c3b8 Olga Brani
    var link = data.links && data.links[key],
6372 e4a6c3b8 Olga Brani
        belongsTo = data[key];
6373 e4a6c3b8 Olga Brani
6374 e4a6c3b8 Olga Brani
    if(!isNone(belongsTo)) {
6375 e4a6c3b8 Olga Brani
      var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo, promiseLabel);
6376 e4a6c3b8 Olga Brani
      return DS.PromiseObject.create({ promise: promise});
6377 e4a6c3b8 Olga Brani
    } else if (link) {
6378 e4a6c3b8 Olga Brani
      var resolver = Ember.RSVP.defer("DS: Async belongsTo (link) " + this + " : " + key);
6379 e4a6c3b8 Olga Brani
      store.findBelongsTo(this, link, meta, resolver);
6380 e4a6c3b8 Olga Brani
      return DS.PromiseObject.create({ promise: resolver.promise });
6381 e4a6c3b8 Olga Brani
    } else {
6382 e4a6c3b8 Olga Brani
      return null;
6383 e4a6c3b8 Olga Brani
    }
6384 e4a6c3b8 Olga Brani
  }).property('data').meta(meta);
6385 e4a6c3b8 Olga Brani
}
6386 e4a6c3b8 Olga Brani
6387 e4a6c3b8 Olga Brani
/**
6388 e4a6c3b8 Olga Brani
  `DS.belongsTo` is used to define One-To-One and One-To-Many
6389 e4a6c3b8 Olga Brani
  relationships on a [DS.Model](DS.Model.html).
6390 e4a6c3b8 Olga Brani

6391 e4a6c3b8 Olga Brani

6392 e4a6c3b8 Olga Brani
  `DS.belongsTo` takes an optional hash as a second parameter, currently
6393 e4a6c3b8 Olga Brani
  supported options are:
6394 e4a6c3b8 Olga Brani

6395 e4a6c3b8 Olga Brani
  - `async`: A boolean value used to explicitly declare this to be an async relationship.
6396 e4a6c3b8 Olga Brani
  - `inverse`: A string used to identify the inverse property on a
6397 e4a6c3b8 Olga Brani
    related model in a One-To-Many relationship. See [Explicit Inverses](#toc_explicit-inverses)
6398 e4a6c3b8 Olga Brani

6399 e4a6c3b8 Olga Brani
  #### One-To-One
6400 e4a6c3b8 Olga Brani
  To declare a one-to-one relationship between two models, use
6401 e4a6c3b8 Olga Brani
  `DS.belongsTo`:
6402 e4a6c3b8 Olga Brani

6403 e4a6c3b8 Olga Brani
  ```javascript
6404 e4a6c3b8 Olga Brani
  App.User = DS.Model.extend({
6405 e4a6c3b8 Olga Brani
    profile: DS.belongsTo('profile')
6406 e4a6c3b8 Olga Brani
  });
6407 e4a6c3b8 Olga Brani

6408 e4a6c3b8 Olga Brani
  App.Profile = DS.Model.extend({
6409 e4a6c3b8 Olga Brani
    user: DS.belongsTo('user')
6410 e4a6c3b8 Olga Brani
  });
6411 e4a6c3b8 Olga Brani
  ```
6412 e4a6c3b8 Olga Brani

6413 e4a6c3b8 Olga Brani
  #### One-To-Many
6414 e4a6c3b8 Olga Brani
  To declare a one-to-many relationship between two models, use
6415 e4a6c3b8 Olga Brani
  `DS.belongsTo` in combination with `DS.hasMany`, like this:
6416 e4a6c3b8 Olga Brani

6417 e4a6c3b8 Olga Brani
  ```javascript
6418 e4a6c3b8 Olga Brani
  App.Post = DS.Model.extend({
6419 e4a6c3b8 Olga Brani
    comments: DS.hasMany('comment')
6420 e4a6c3b8 Olga Brani
  });
6421 e4a6c3b8 Olga Brani

6422 e4a6c3b8 Olga Brani
  App.Comment = DS.Model.extend({
6423 e4a6c3b8 Olga Brani
    post: DS.belongsTo('post')
6424 e4a6c3b8 Olga Brani
  });
6425 e4a6c3b8 Olga Brani
  ```
6426 e4a6c3b8 Olga Brani

6427 e4a6c3b8 Olga Brani
  @namespace
6428 e4a6c3b8 Olga Brani
  @method belongsTo
6429 e4a6c3b8 Olga Brani
  @for DS
6430 e4a6c3b8 Olga Brani
  @param {String or DS.Model} type the model type of the relationship
6431 e4a6c3b8 Olga Brani
  @param {Object} options a hash of options
6432 e4a6c3b8 Olga Brani
  @return {Ember.computed} relationship
6433 e4a6c3b8 Olga Brani
*/
6434 e4a6c3b8 Olga Brani
DS.belongsTo = function(type, options) {
6435 e4a6c3b8 Olga Brani
  if (typeof type === 'object') {
6436 e4a6c3b8 Olga Brani
    options = type;
6437 e4a6c3b8 Olga Brani
    type = undefined;
6438 e4a6c3b8 Olga Brani
  } else {
6439 e4a6c3b8 Olga Brani
    Ember.assert("The first argument DS.belongsTo must be a model type or string, like DS.belongsTo(App.Person)", !!type && (typeof type === 'string' || DS.Model.detect(type)));
6440 e4a6c3b8 Olga Brani
  }
6441 e4a6c3b8 Olga Brani
6442 e4a6c3b8 Olga Brani
  options = options || {};
6443 e4a6c3b8 Olga Brani
6444 e4a6c3b8 Olga Brani
  var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo' };
6445 e4a6c3b8 Olga Brani
6446 e4a6c3b8 Olga Brani
  if (options.async) {
6447 e4a6c3b8 Olga Brani
    return asyncBelongsTo(type, options, meta);
6448 e4a6c3b8 Olga Brani
  }
6449 e4a6c3b8 Olga Brani
6450 e4a6c3b8 Olga Brani
  return Ember.computed(function(key, value) {
6451 e4a6c3b8 Olga Brani
    var data = get(this, 'data'),
6452 e4a6c3b8 Olga Brani
        store = get(this, 'store'), belongsTo, typeClass;
6453 e4a6c3b8 Olga Brani
6454 e4a6c3b8 Olga Brani
    if (typeof type === 'string') {
6455 e4a6c3b8 Olga Brani
      typeClass = store.modelFor(type);
6456 e4a6c3b8 Olga Brani
    } else {
6457 e4a6c3b8 Olga Brani
      typeClass = type;
6458 e4a6c3b8 Olga Brani
    }
6459 e4a6c3b8 Olga Brani
6460 e4a6c3b8 Olga Brani
    if (arguments.length === 2) {
6461 e4a6c3b8 Olga Brani
      Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof typeClass);
6462 e4a6c3b8 Olga Brani
      return value === undefined ? null : value;
6463 e4a6c3b8 Olga Brani
    }
6464 e4a6c3b8 Olga Brani
6465 e4a6c3b8 Olga Brani
    belongsTo = data[key];
6466 e4a6c3b8 Olga Brani
6467 e4a6c3b8 Olga Brani
    if (isNone(belongsTo)) { return null; }
6468 e4a6c3b8 Olga Brani
6469 e4a6c3b8 Olga Brani
    store.fetchRecord(belongsTo);
6470 e4a6c3b8 Olga Brani
6471 e4a6c3b8 Olga Brani
    return belongsTo;
6472 e4a6c3b8 Olga Brani
  }).property('data').meta(meta);
6473 e4a6c3b8 Olga Brani
};
6474 e4a6c3b8 Olga Brani
6475 e4a6c3b8 Olga Brani
/**
6476 e4a6c3b8 Olga Brani
  These observers observe all `belongsTo` relationships on the record. See
6477 e4a6c3b8 Olga Brani
  `relationships/ext` to see how these observers get their dependencies.
6478 e4a6c3b8 Olga Brani

6479 e4a6c3b8 Olga Brani
  @class Model
6480 e4a6c3b8 Olga Brani
  @namespace DS
6481 e4a6c3b8 Olga Brani
*/
6482 e4a6c3b8 Olga Brani
DS.Model.reopen({
6483 e4a6c3b8 Olga Brani
6484 e4a6c3b8 Olga Brani
  /**
6485 e4a6c3b8 Olga Brani
    @method belongsToWillChange
6486 e4a6c3b8 Olga Brani
    @private
6487 e4a6c3b8 Olga Brani
    @static
6488 e4a6c3b8 Olga Brani
    @param record
6489 e4a6c3b8 Olga Brani
    @param key
6490 e4a6c3b8 Olga Brani
  */
6491 e4a6c3b8 Olga Brani
  belongsToWillChange: Ember.beforeObserver(function(record, key) {
6492 e4a6c3b8 Olga Brani
    if (get(record, 'isLoaded')) {
6493 e4a6c3b8 Olga Brani
      var oldParent = get(record, key);
6494 e4a6c3b8 Olga Brani
6495 e4a6c3b8 Olga Brani
      if (oldParent) {
6496 e4a6c3b8 Olga Brani
        var store = get(record, 'store'),
6497 e4a6c3b8 Olga Brani
            change = DS.RelationshipChange.createChange(record, oldParent, store, { key: key, kind: "belongsTo", changeType: "remove" });
6498 e4a6c3b8 Olga Brani
6499 e4a6c3b8 Olga Brani
        change.sync();
6500 e4a6c3b8 Olga Brani
        this._changesToSync[key] = change;
6501 e4a6c3b8 Olga Brani
      }
6502 e4a6c3b8 Olga Brani
    }
6503 e4a6c3b8 Olga Brani
  }),
6504 e4a6c3b8 Olga Brani
6505 e4a6c3b8 Olga Brani
  /**
6506 e4a6c3b8 Olga Brani
    @method belongsToDidChange
6507 e4a6c3b8 Olga Brani
    @private
6508 e4a6c3b8 Olga Brani
    @static
6509 e4a6c3b8 Olga Brani
    @param record
6510 e4a6c3b8 Olga Brani
    @param key
6511 e4a6c3b8 Olga Brani
  */
6512 e4a6c3b8 Olga Brani
  belongsToDidChange: Ember.immediateObserver(function(record, key) {
6513 e4a6c3b8 Olga Brani
    if (get(record, 'isLoaded')) {
6514 e4a6c3b8 Olga Brani
      var newParent = get(record, key);
6515 e4a6c3b8 Olga Brani
6516 e4a6c3b8 Olga Brani
      if (newParent) {
6517 e4a6c3b8 Olga Brani
        var store = get(record, 'store'),
6518 e4a6c3b8 Olga Brani
            change = DS.RelationshipChange.createChange(record, newParent, store, { key: key, kind: "belongsTo", changeType: "add" });
6519 e4a6c3b8 Olga Brani
6520 e4a6c3b8 Olga Brani
        change.sync();
6521 e4a6c3b8 Olga Brani
      }
6522 e4a6c3b8 Olga Brani
    }
6523 e4a6c3b8 Olga Brani
6524 e4a6c3b8 Olga Brani
    delete this._changesToSync[key];
6525 e4a6c3b8 Olga Brani
  })
6526 e4a6c3b8 Olga Brani
});
6527 e4a6c3b8 Olga Brani
6528 e4a6c3b8 Olga Brani
})();
6529 e4a6c3b8 Olga Brani
6530 e4a6c3b8 Olga Brani
6531 e4a6c3b8 Olga Brani
6532 e4a6c3b8 Olga Brani
(function() {
6533 e4a6c3b8 Olga Brani
/**
6534 e4a6c3b8 Olga Brani
  @module ember-data
6535 e4a6c3b8 Olga Brani
*/
6536 e4a6c3b8 Olga Brani
6537 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
6538 e4a6c3b8 Olga Brani
6539 e4a6c3b8 Olga Brani
function asyncHasMany(type, options, meta) {
6540 e4a6c3b8 Olga Brani
  return Ember.computed(function(key, value) {
6541 e4a6c3b8 Olga Brani
    var relationship = this._relationships[key],
6542 e4a6c3b8 Olga Brani
        promiseLabel = "DS: Async hasMany " + this + " : " + key;
6543 e4a6c3b8 Olga Brani
6544 e4a6c3b8 Olga Brani
    if (!relationship) {
6545 e4a6c3b8 Olga Brani
      var resolver = Ember.RSVP.defer(promiseLabel);
6546 e4a6c3b8 Olga Brani
      relationship = buildRelationship(this, key, options, function(store, data) {
6547 e4a6c3b8 Olga Brani
        var link = data.links && data.links[key];
6548 e4a6c3b8 Olga Brani
        var rel;
6549 e4a6c3b8 Olga Brani
        if (link) {
6550 e4a6c3b8 Olga Brani
          rel = store.findHasMany(this, link, meta, resolver);
6551 e4a6c3b8 Olga Brani
        } else {
6552 e4a6c3b8 Olga Brani
          rel = store.findMany(this, data[key], meta.type, resolver);
6553 e4a6c3b8 Olga Brani
        }
6554 e4a6c3b8 Olga Brani
        // cache the promise so we can use it
6555 e4a6c3b8 Olga Brani
        // when we come back and don't need to rebuild
6556 e4a6c3b8 Olga Brani
        // the relationship.
6557 e4a6c3b8 Olga Brani
        set(rel, 'promise', resolver.promise);
6558 e4a6c3b8 Olga Brani
        return rel;
6559 e4a6c3b8 Olga Brani
      });
6560 e4a6c3b8 Olga Brani
    }
6561 e4a6c3b8 Olga Brani
6562 e4a6c3b8 Olga Brani
    var promise = relationship.get('promise').then(function() {
6563 e4a6c3b8 Olga Brani
      return relationship;
6564 e4a6c3b8 Olga Brani
    }, null, "DS: Async hasMany records received");
6565 e4a6c3b8 Olga Brani
6566 e4a6c3b8 Olga Brani
    return DS.PromiseArray.create({ promise: promise });
6567 e4a6c3b8 Olga Brani
  }).property('data').meta(meta);
6568 e4a6c3b8 Olga Brani
}
6569 e4a6c3b8 Olga Brani
6570 e4a6c3b8 Olga Brani
function buildRelationship(record, key, options, callback) {
6571 e4a6c3b8 Olga Brani
  var rels = record._relationships;
6572 e4a6c3b8 Olga Brani
6573 e4a6c3b8 Olga Brani
  if (rels[key]) { return rels[key]; }
6574 e4a6c3b8 Olga Brani
6575 e4a6c3b8 Olga Brani
  var data = get(record, 'data'),
6576 e4a6c3b8 Olga Brani
      store = get(record, 'store');
6577 e4a6c3b8 Olga Brani
6578 e4a6c3b8 Olga Brani
  var relationship = rels[key] = callback.call(record, store, data);
6579 e4a6c3b8 Olga Brani
6580 e4a6c3b8 Olga Brani
  return setProperties(relationship, {
6581 e4a6c3b8 Olga Brani
    owner: record, name: key, isPolymorphic: options.polymorphic
6582 e4a6c3b8 Olga Brani
  });
6583 e4a6c3b8 Olga Brani
}
6584 e4a6c3b8 Olga Brani
6585 e4a6c3b8 Olga Brani
function hasRelationship(type, options) {
6586 e4a6c3b8 Olga Brani
  options = options || {};
6587 e4a6c3b8 Olga Brani
6588 e4a6c3b8 Olga Brani
  var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' };
6589 e4a6c3b8 Olga Brani
6590 e4a6c3b8 Olga Brani
  if (options.async) {
6591 e4a6c3b8 Olga Brani
    return asyncHasMany(type, options, meta);
6592 e4a6c3b8 Olga Brani
  }
6593 e4a6c3b8 Olga Brani
6594 e4a6c3b8 Olga Brani
  return Ember.computed(function(key, value) {
6595 e4a6c3b8 Olga Brani
    return buildRelationship(this, key, options, function(store, data) {
6596 e4a6c3b8 Olga Brani
      var records = data[key];
6597 e4a6c3b8 Olga Brani
      Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' 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 })`)", Ember.A(records).everyProperty('isEmpty', false));
6598 e4a6c3b8 Olga Brani
      return store.findMany(this, data[key], meta.type);
6599 e4a6c3b8 Olga Brani
    });
6600 e4a6c3b8 Olga Brani
  }).property('data').meta(meta);
6601 e4a6c3b8 Olga Brani
}
6602 e4a6c3b8 Olga Brani
6603 e4a6c3b8 Olga Brani
/**
6604 e4a6c3b8 Olga Brani
  `DS.hasMany` is used to define One-To-Many and Many-To-Many
6605 e4a6c3b8 Olga Brani
  relationships on a [DS.Model](DS.Model.html).
6606 e4a6c3b8 Olga Brani

6607 e4a6c3b8 Olga Brani
  `DS.hasMany` takes an optional hash as a second parameter, currently
6608 e4a6c3b8 Olga Brani
  supported options are:
6609 e4a6c3b8 Olga Brani

6610 e4a6c3b8 Olga Brani
  - `async`: A boolean value used to explicitly declare this to be an async relationship.
6611 e4a6c3b8 Olga Brani
  - `inverse`: A string used to identify the inverse property on a related model.
6612 e4a6c3b8 Olga Brani

6613 e4a6c3b8 Olga Brani
  #### One-To-Many
6614 e4a6c3b8 Olga Brani
  To declare a one-to-many relationship between two models, use
6615 e4a6c3b8 Olga Brani
  `DS.belongsTo` in combination with `DS.hasMany`, like this:
6616 e4a6c3b8 Olga Brani

6617 e4a6c3b8 Olga Brani
  ```javascript
6618 e4a6c3b8 Olga Brani
  App.Post = DS.Model.extend({
6619 e4a6c3b8 Olga Brani
    comments: DS.hasMany('comment')
6620 e4a6c3b8 Olga Brani
  });
6621 e4a6c3b8 Olga Brani

6622 e4a6c3b8 Olga Brani
  App.Comment = DS.Model.extend({
6623 e4a6c3b8 Olga Brani
    post: DS.belongsTo('post')
6624 e4a6c3b8 Olga Brani
  });
6625 e4a6c3b8 Olga Brani
  ```
6626 e4a6c3b8 Olga Brani

6627 e4a6c3b8 Olga Brani
  #### Many-To-Many
6628 e4a6c3b8 Olga Brani
  To declare a many-to-many relationship between two models, use
6629 e4a6c3b8 Olga Brani
  `DS.hasMany`:
6630 e4a6c3b8 Olga Brani

6631 e4a6c3b8 Olga Brani
  ```javascript
6632 e4a6c3b8 Olga Brani
  App.Post = DS.Model.extend({
6633 e4a6c3b8 Olga Brani
    tags: DS.hasMany('tag')
6634 e4a6c3b8 Olga Brani
  });
6635 e4a6c3b8 Olga Brani

6636 e4a6c3b8 Olga Brani
  App.Tag = DS.Model.extend({
6637 e4a6c3b8 Olga Brani
    posts: DS.hasMany('post')
6638 e4a6c3b8 Olga Brani
  });
6639 e4a6c3b8 Olga Brani
  ```
6640 e4a6c3b8 Olga Brani

6641 e4a6c3b8 Olga Brani
  #### Explicit Inverses
6642 e4a6c3b8 Olga Brani

6643 e4a6c3b8 Olga Brani
  Ember Data will do its best to discover which relationships map to
6644 e4a6c3b8 Olga Brani
  one another. In the one-to-many code above, for example, Ember Data
6645 e4a6c3b8 Olga Brani
  can figure out that changing the `comments` relationship should update
6646 e4a6c3b8 Olga Brani
  the `post` relationship on the inverse because post is the only
6647 e4a6c3b8 Olga Brani
  relationship to that model.
6648 e4a6c3b8 Olga Brani

6649 e4a6c3b8 Olga Brani
  However, sometimes you may have multiple `belongsTo`/`hasManys` for the
6650 e4a6c3b8 Olga Brani
  same type. You can specify which property on the related model is
6651 e4a6c3b8 Olga Brani
  the inverse using `DS.hasMany`'s `inverse` option:
6652 e4a6c3b8 Olga Brani

6653 e4a6c3b8 Olga Brani
  ```javascript
6654 e4a6c3b8 Olga Brani
  var belongsTo = DS.belongsTo,
6655 e4a6c3b8 Olga Brani
      hasMany = DS.hasMany;
6656 e4a6c3b8 Olga Brani

6657 e4a6c3b8 Olga Brani
  App.Comment = DS.Model.extend({
6658 e4a6c3b8 Olga Brani
    onePost: belongsTo('post'),
6659 e4a6c3b8 Olga Brani
    twoPost: belongsTo('post'),
6660 e4a6c3b8 Olga Brani
    redPost: belongsTo('post'),
6661 e4a6c3b8 Olga Brani
    bluePost: belongsTo('post')
6662 e4a6c3b8 Olga Brani
  });
6663 e4a6c3b8 Olga Brani

6664 e4a6c3b8 Olga Brani
  App.Post = DS.Model.extend({
6665 e4a6c3b8 Olga Brani
    comments: hasMany('comment', {
6666 e4a6c3b8 Olga Brani
      inverse: 'redPost'
6667 e4a6c3b8 Olga Brani
    })
6668 e4a6c3b8 Olga Brani
  });
6669 e4a6c3b8 Olga Brani
  ```
6670 e4a6c3b8 Olga Brani

6671 e4a6c3b8 Olga Brani
  You can also specify an inverse on a `belongsTo`, which works how
6672 e4a6c3b8 Olga Brani
  you'd expect.
6673 e4a6c3b8 Olga Brani

6674 e4a6c3b8 Olga Brani
  @namespace
6675 e4a6c3b8 Olga Brani
  @method hasMany
6676 e4a6c3b8 Olga Brani
  @for DS
6677 e4a6c3b8 Olga Brani
  @param {String or DS.Model} type the model type of the relationship
6678 e4a6c3b8 Olga Brani
  @param {Object} options a hash of options
6679 e4a6c3b8 Olga Brani
  @return {Ember.computed} relationship
6680 e4a6c3b8 Olga Brani
*/
6681 e4a6c3b8 Olga Brani
DS.hasMany = function(type, options) {
6682 e4a6c3b8 Olga Brani
  if (typeof type === 'object') {
6683 e4a6c3b8 Olga Brani
    options = type;
6684 e4a6c3b8 Olga Brani
    type = undefined;
6685 e4a6c3b8 Olga Brani
  }
6686 e4a6c3b8 Olga Brani
  return hasRelationship(type, options);
6687 e4a6c3b8 Olga Brani
};
6688 e4a6c3b8 Olga Brani
6689 e4a6c3b8 Olga Brani
})();
6690 e4a6c3b8 Olga Brani
6691 e4a6c3b8 Olga Brani
6692 e4a6c3b8 Olga Brani
6693 e4a6c3b8 Olga Brani
(function() {
6694 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
6695 e4a6c3b8 Olga Brani
6696 e4a6c3b8 Olga Brani
/**
6697 e4a6c3b8 Olga Brani
  @module ember-data
6698 e4a6c3b8 Olga Brani
*/
6699 e4a6c3b8 Olga Brani
6700 e4a6c3b8 Olga Brani
/*
6701 e4a6c3b8 Olga Brani
  This file defines several extensions to the base `DS.Model` class that
6702 e4a6c3b8 Olga Brani
  add support for one-to-many relationships.
6703 e4a6c3b8 Olga Brani
*/
6704 e4a6c3b8 Olga Brani
6705 e4a6c3b8 Olga Brani
/**
6706 e4a6c3b8 Olga Brani
  @class Model
6707 e4a6c3b8 Olga Brani
  @namespace DS
6708 e4a6c3b8 Olga Brani
*/
6709 e4a6c3b8 Olga Brani
DS.Model.reopen({
6710 e4a6c3b8 Olga Brani
6711 e4a6c3b8 Olga Brani
  /**
6712 e4a6c3b8 Olga Brani
    This Ember.js hook allows an object to be notified when a property
6713 e4a6c3b8 Olga Brani
    is defined.
6714 e4a6c3b8 Olga Brani

6715 e4a6c3b8 Olga Brani
    In this case, we use it to be notified when an Ember Data user defines a
6716 e4a6c3b8 Olga Brani
    belongs-to relationship. In that case, we need to set up observers for
6717 e4a6c3b8 Olga Brani
    each one, allowing us to track relationship changes and automatically
6718 e4a6c3b8 Olga Brani
    reflect changes in the inverse has-many array.
6719 e4a6c3b8 Olga Brani

6720 e4a6c3b8 Olga Brani
    This hook passes the class being set up, as well as the key and value
6721 e4a6c3b8 Olga Brani
    being defined. So, for example, when the user does this:
6722 e4a6c3b8 Olga Brani

6723 e4a6c3b8 Olga Brani
    ```javascript
6724 e4a6c3b8 Olga Brani
    DS.Model.extend({
6725 e4a6c3b8 Olga Brani
      parent: DS.belongsTo('user')
6726 e4a6c3b8 Olga Brani
    });
6727 e4a6c3b8 Olga Brani
    ```
6728 e4a6c3b8 Olga Brani

6729 e4a6c3b8 Olga Brani
    This hook would be called with "parent" as the key and the computed
6730 e4a6c3b8 Olga Brani
    property returned by `DS.belongsTo` as the value.
6731 e4a6c3b8 Olga Brani

6732 e4a6c3b8 Olga Brani
    @method didDefineProperty
6733 e4a6c3b8 Olga Brani
    @param proto
6734 e4a6c3b8 Olga Brani
    @param key
6735 e4a6c3b8 Olga Brani
    @param value
6736 e4a6c3b8 Olga Brani
  */
6737 e4a6c3b8 Olga Brani
  didDefineProperty: function(proto, key, value) {
6738 e4a6c3b8 Olga Brani
    // Check if the value being set is a computed property.
6739 e4a6c3b8 Olga Brani
    if (value instanceof Ember.Descriptor) {
6740 e4a6c3b8 Olga Brani
6741 e4a6c3b8 Olga Brani
      // If it is, get the metadata for the relationship. This is
6742 e4a6c3b8 Olga Brani
      // populated by the `DS.belongsTo` helper when it is creating
6743 e4a6c3b8 Olga Brani
      // the computed property.
6744 e4a6c3b8 Olga Brani
      var meta = value.meta();
6745 e4a6c3b8 Olga Brani
6746 e4a6c3b8 Olga Brani
      if (meta.isRelationship && meta.kind === 'belongsTo') {
6747 e4a6c3b8 Olga Brani
        Ember.addObserver(proto, key, null, 'belongsToDidChange');
6748 e4a6c3b8 Olga Brani
        Ember.addBeforeObserver(proto, key, null, 'belongsToWillChange');
6749 e4a6c3b8 Olga Brani
      }
6750 e4a6c3b8 Olga Brani
6751 e4a6c3b8 Olga Brani
      meta.parentType = proto.constructor;
6752 e4a6c3b8 Olga Brani
    }
6753 e4a6c3b8 Olga Brani
  }
6754 e4a6c3b8 Olga Brani
});
6755 e4a6c3b8 Olga Brani
6756 e4a6c3b8 Olga Brani
/*
6757 e4a6c3b8 Olga Brani
  These DS.Model extensions add class methods that provide relationship
6758 e4a6c3b8 Olga Brani
  introspection abilities about relationships.
6759 e4a6c3b8 Olga Brani

6760 e4a6c3b8 Olga Brani
  A note about the computed properties contained here:
6761 e4a6c3b8 Olga Brani

6762 e4a6c3b8 Olga Brani
  **These properties are effectively sealed once called for the first time.**
6763 e4a6c3b8 Olga Brani
  To avoid repeatedly doing expensive iteration over a model's fields, these
6764 e4a6c3b8 Olga Brani
  values are computed once and then cached for the remainder of the runtime of
6765 e4a6c3b8 Olga Brani
  your application.
6766 e4a6c3b8 Olga Brani

6767 e4a6c3b8 Olga Brani
  If your application needs to modify a class after its initial definition
6768 e4a6c3b8 Olga Brani
  (for example, using `reopen()` to add additional attributes), make sure you
6769 e4a6c3b8 Olga Brani
  do it before using your model with the store, which uses these properties
6770 e4a6c3b8 Olga Brani
  extensively.
6771 e4a6c3b8 Olga Brani
*/
6772 e4a6c3b8 Olga Brani
6773 e4a6c3b8 Olga Brani
DS.Model.reopenClass({
6774 e4a6c3b8 Olga Brani
  /**
6775 e4a6c3b8 Olga Brani
    For a given relationship name, returns the model type of the relationship.
6776 e4a6c3b8 Olga Brani

6777 e4a6c3b8 Olga Brani
    For example, if you define a model like this:
6778 e4a6c3b8 Olga Brani

6779 e4a6c3b8 Olga Brani
   ```javascript
6780 e4a6c3b8 Olga Brani
    App.Post = DS.Model.extend({
6781 e4a6c3b8 Olga Brani
      comments: DS.hasMany('comment')
6782 e4a6c3b8 Olga Brani
    });
6783 e4a6c3b8 Olga Brani
   ```
6784 e4a6c3b8 Olga Brani

6785 e4a6c3b8 Olga Brani
    Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
6786 e4a6c3b8 Olga Brani

6787 e4a6c3b8 Olga Brani
    @method typeForRelationship
6788 e4a6c3b8 Olga Brani
    @static
6789 e4a6c3b8 Olga Brani
    @param {String} name the name of the relationship
6790 e4a6c3b8 Olga Brani
    @return {subclass of DS.Model} the type of the relationship, or undefined
6791 e4a6c3b8 Olga Brani
  */
6792 e4a6c3b8 Olga Brani
  typeForRelationship: function(name) {
6793 e4a6c3b8 Olga Brani
    var relationship = get(this, 'relationshipsByName').get(name);
6794 e4a6c3b8 Olga Brani
    return relationship && relationship.type;
6795 e4a6c3b8 Olga Brani
  },
6796 e4a6c3b8 Olga Brani
6797 e4a6c3b8 Olga Brani
  inverseFor: function(name) {
6798 e4a6c3b8 Olga Brani
    var inverseType = this.typeForRelationship(name);
6799 e4a6c3b8 Olga Brani
6800 e4a6c3b8 Olga Brani
    if (!inverseType) { return null; }
6801 e4a6c3b8 Olga Brani
6802 e4a6c3b8 Olga Brani
    var options = this.metaForProperty(name).options;
6803 e4a6c3b8 Olga Brani
6804 e4a6c3b8 Olga Brani
    if (options.inverse === null) { return null; }
6805 e4a6c3b8 Olga Brani
    
6806 e4a6c3b8 Olga Brani
    var inverseName, inverseKind;
6807 e4a6c3b8 Olga Brani
6808 e4a6c3b8 Olga Brani
    if (options.inverse) {
6809 e4a6c3b8 Olga Brani
      inverseName = options.inverse;
6810 e4a6c3b8 Olga Brani
      inverseKind = Ember.get(inverseType, 'relationshipsByName').get(inverseName).kind;
6811 e4a6c3b8 Olga Brani
    } else {
6812 e4a6c3b8 Olga Brani
      var possibleRelationships = findPossibleInverses(this, inverseType);
6813 e4a6c3b8 Olga Brani
6814 e4a6c3b8 Olga Brani
      if (possibleRelationships.length === 0) { return null; }
6815 e4a6c3b8 Olga Brani
6816 e4a6c3b8 Olga Brani
      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);
6817 e4a6c3b8 Olga Brani
6818 e4a6c3b8 Olga Brani
      inverseName = possibleRelationships[0].name;
6819 e4a6c3b8 Olga Brani
      inverseKind = possibleRelationships[0].kind;
6820 e4a6c3b8 Olga Brani
    }
6821 e4a6c3b8 Olga Brani
6822 e4a6c3b8 Olga Brani
    function findPossibleInverses(type, inverseType, possibleRelationships) {
6823 e4a6c3b8 Olga Brani
      possibleRelationships = possibleRelationships || [];
6824 e4a6c3b8 Olga Brani
6825 e4a6c3b8 Olga Brani
      var relationshipMap = get(inverseType, 'relationships');
6826 e4a6c3b8 Olga Brani
      if (!relationshipMap) { return; }
6827 e4a6c3b8 Olga Brani
6828 e4a6c3b8 Olga Brani
      var relationships = relationshipMap.get(type);
6829 e4a6c3b8 Olga Brani
      if (relationships) {
6830 e4a6c3b8 Olga Brani
        possibleRelationships.push.apply(possibleRelationships, relationshipMap.get(type));
6831 e4a6c3b8 Olga Brani
      }
6832 e4a6c3b8 Olga Brani
6833 e4a6c3b8 Olga Brani
      if (type.superclass) {
6834 e4a6c3b8 Olga Brani
        findPossibleInverses(type.superclass, inverseType, possibleRelationships);
6835 e4a6c3b8 Olga Brani
      }
6836 e4a6c3b8 Olga Brani
6837 e4a6c3b8 Olga Brani
      return possibleRelationships;
6838 e4a6c3b8 Olga Brani
    }
6839 e4a6c3b8 Olga Brani
6840 e4a6c3b8 Olga Brani
    return {
6841 e4a6c3b8 Olga Brani
      type: inverseType,
6842 e4a6c3b8 Olga Brani
      name: inverseName,
6843 e4a6c3b8 Olga Brani
      kind: inverseKind
6844 e4a6c3b8 Olga Brani
    };
6845 e4a6c3b8 Olga Brani
  },
6846 e4a6c3b8 Olga Brani
6847 e4a6c3b8 Olga Brani
  /**
6848 e4a6c3b8 Olga Brani
    The model's relationships as a map, keyed on the type of the
6849 e4a6c3b8 Olga Brani
    relationship. The value of each entry is an array containing a descriptor
6850 e4a6c3b8 Olga Brani
    for each relationship with that type, describing the name of the relationship
6851 e4a6c3b8 Olga Brani
    as well as the type.
6852 e4a6c3b8 Olga Brani

6853 e4a6c3b8 Olga Brani
    For example, given the following model definition:
6854 e4a6c3b8 Olga Brani

6855 e4a6c3b8 Olga Brani
    ```javascript
6856 e4a6c3b8 Olga Brani
    App.Blog = DS.Model.extend({
6857 e4a6c3b8 Olga Brani
      users: DS.hasMany('user'),
6858 e4a6c3b8 Olga Brani
      owner: DS.belongsTo('user'),
6859 e4a6c3b8 Olga Brani
      posts: DS.hasMany('post')
6860 e4a6c3b8 Olga Brani
    });
6861 e4a6c3b8 Olga Brani
    ```
6862 e4a6c3b8 Olga Brani

6863 e4a6c3b8 Olga Brani
    This computed property would return a map describing these
6864 e4a6c3b8 Olga Brani
    relationships, like this:
6865 e4a6c3b8 Olga Brani

6866 e4a6c3b8 Olga Brani
    ```javascript
6867 e4a6c3b8 Olga Brani
    var relationships = Ember.get(App.Blog, 'relationships');
6868 e4a6c3b8 Olga Brani
    relationships.get(App.User);
6869 e4a6c3b8 Olga Brani
    //=> [ { name: 'users', kind: 'hasMany' },
6870 e4a6c3b8 Olga Brani
    //     { name: 'owner', kind: 'belongsTo' } ]
6871 e4a6c3b8 Olga Brani
    relationships.get(App.Post);
6872 e4a6c3b8 Olga Brani
    //=> [ { name: 'posts', kind: 'hasMany' } ]
6873 e4a6c3b8 Olga Brani
    ```
6874 e4a6c3b8 Olga Brani

6875 e4a6c3b8 Olga Brani
    @property relationships
6876 e4a6c3b8 Olga Brani
    @static
6877 e4a6c3b8 Olga Brani
    @type Ember.Map
6878 e4a6c3b8 Olga Brani
    @readOnly
6879 e4a6c3b8 Olga Brani
  */
6880 e4a6c3b8 Olga Brani
  relationships: Ember.computed(function() {
6881 e4a6c3b8 Olga Brani
    var map = new Ember.MapWithDefault({
6882 e4a6c3b8 Olga Brani
      defaultValue: function() { return []; }
6883 e4a6c3b8 Olga Brani
    });
6884 e4a6c3b8 Olga Brani
6885 e4a6c3b8 Olga Brani
    // Loop through each computed property on the class
6886 e4a6c3b8 Olga Brani
    this.eachComputedProperty(function(name, meta) {
6887 e4a6c3b8 Olga Brani
6888 e4a6c3b8 Olga Brani
      // If the computed property is a relationship, add
6889 e4a6c3b8 Olga Brani
      // it to the map.
6890 e4a6c3b8 Olga Brani
      if (meta.isRelationship) {
6891 e4a6c3b8 Olga Brani
        if (typeof meta.type === 'string') {
6892 e4a6c3b8 Olga Brani
          meta.type = this.store.modelFor(meta.type);
6893 e4a6c3b8 Olga Brani
        }
6894 e4a6c3b8 Olga Brani
6895 e4a6c3b8 Olga Brani
        var relationshipsForType = map.get(meta.type);
6896 e4a6c3b8 Olga Brani
6897 e4a6c3b8 Olga Brani
        relationshipsForType.push({ name: name, kind: meta.kind });
6898 e4a6c3b8 Olga Brani
      }
6899 e4a6c3b8 Olga Brani
    });
6900 e4a6c3b8 Olga Brani
6901 e4a6c3b8 Olga Brani
    return map;
6902 e4a6c3b8 Olga Brani
  }),
6903 e4a6c3b8 Olga Brani
6904 e4a6c3b8 Olga Brani
  /**
6905 e4a6c3b8 Olga Brani
    A hash containing lists of the model's relationships, grouped
6906 e4a6c3b8 Olga Brani
    by the relationship kind. For example, given a model with this
6907 e4a6c3b8 Olga Brani
    definition:
6908 e4a6c3b8 Olga Brani

6909 e4a6c3b8 Olga Brani
    ```javascript
6910 e4a6c3b8 Olga Brani
    App.Blog = DS.Model.extend({
6911 e4a6c3b8 Olga Brani
      users: DS.hasMany('user'),
6912 e4a6c3b8 Olga Brani
      owner: DS.belongsTo('user'),
6913 e4a6c3b8 Olga Brani

6914 e4a6c3b8 Olga Brani
      posts: DS.hasMany('post')
6915 e4a6c3b8 Olga Brani
    });
6916 e4a6c3b8 Olga Brani
    ```
6917 e4a6c3b8 Olga Brani

6918 e4a6c3b8 Olga Brani
    This property would contain the following:
6919 e4a6c3b8 Olga Brani

6920 e4a6c3b8 Olga Brani
    ```javascript
6921 e4a6c3b8 Olga Brani
    var relationshipNames = Ember.get(App.Blog, 'relationshipNames');
6922 e4a6c3b8 Olga Brani
    relationshipNames.hasMany;
6923 e4a6c3b8 Olga Brani
    //=> ['users', 'posts']
6924 e4a6c3b8 Olga Brani
    relationshipNames.belongsTo;
6925 e4a6c3b8 Olga Brani
    //=> ['owner']
6926 e4a6c3b8 Olga Brani
    ```
6927 e4a6c3b8 Olga Brani

6928 e4a6c3b8 Olga Brani
    @property relationshipNames
6929 e4a6c3b8 Olga Brani
    @static
6930 e4a6c3b8 Olga Brani
    @type Object
6931 e4a6c3b8 Olga Brani
    @readOnly
6932 e4a6c3b8 Olga Brani
  */
6933 e4a6c3b8 Olga Brani
  relationshipNames: Ember.computed(function() {
6934 e4a6c3b8 Olga Brani
    var names = { hasMany: [], belongsTo: [] };
6935 e4a6c3b8 Olga Brani
6936 e4a6c3b8 Olga Brani
    this.eachComputedProperty(function(name, meta) {
6937 e4a6c3b8 Olga Brani
      if (meta.isRelationship) {
6938 e4a6c3b8 Olga Brani
        names[meta.kind].push(name);
6939 e4a6c3b8 Olga Brani
      }
6940 e4a6c3b8 Olga Brani
    });
6941 e4a6c3b8 Olga Brani
6942 e4a6c3b8 Olga Brani
    return names;
6943 e4a6c3b8 Olga Brani
  }),
6944 e4a6c3b8 Olga Brani
6945 e4a6c3b8 Olga Brani
  /**
6946 e4a6c3b8 Olga Brani
    An array of types directly related to a model. Each type will be
6947 e4a6c3b8 Olga Brani
    included once, regardless of the number of relationships it has with
6948 e4a6c3b8 Olga Brani
    the model.
6949 e4a6c3b8 Olga Brani

6950 e4a6c3b8 Olga Brani
    For example, given a model with this definition:
6951 e4a6c3b8 Olga Brani

6952 e4a6c3b8 Olga Brani
    ```javascript
6953 e4a6c3b8 Olga Brani
    App.Blog = DS.Model.extend({
6954 e4a6c3b8 Olga Brani
      users: DS.hasMany('user'),
6955 e4a6c3b8 Olga Brani
      owner: DS.belongsTo('user'),
6956 e4a6c3b8 Olga Brani

6957 e4a6c3b8 Olga Brani
      posts: DS.hasMany('post')
6958 e4a6c3b8 Olga Brani
    });
6959 e4a6c3b8 Olga Brani
    ```
6960 e4a6c3b8 Olga Brani

6961 e4a6c3b8 Olga Brani
    This property would contain the following:
6962 e4a6c3b8 Olga Brani

6963 e4a6c3b8 Olga Brani
    ```javascript
6964 e4a6c3b8 Olga Brani
    var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
6965 e4a6c3b8 Olga Brani
    //=> [ App.User, App.Post ]
6966 e4a6c3b8 Olga Brani
    ```
6967 e4a6c3b8 Olga Brani

6968 e4a6c3b8 Olga Brani
    @property relatedTypes
6969 e4a6c3b8 Olga Brani
    @static
6970 e4a6c3b8 Olga Brani
    @type Ember.Array
6971 e4a6c3b8 Olga Brani
    @readOnly
6972 e4a6c3b8 Olga Brani
  */
6973 e4a6c3b8 Olga Brani
  relatedTypes: Ember.computed(function() {
6974 e4a6c3b8 Olga Brani
    var type,
6975 e4a6c3b8 Olga Brani
        types = Ember.A();
6976 e4a6c3b8 Olga Brani
6977 e4a6c3b8 Olga Brani
    // Loop through each computed property on the class,
6978 e4a6c3b8 Olga Brani
    // and create an array of the unique types involved
6979 e4a6c3b8 Olga Brani
    // in relationships
6980 e4a6c3b8 Olga Brani
    this.eachComputedProperty(function(name, meta) {
6981 e4a6c3b8 Olga Brani
      if (meta.isRelationship) {
6982 e4a6c3b8 Olga Brani
        type = meta.type;
6983 e4a6c3b8 Olga Brani
6984 e4a6c3b8 Olga Brani
        if (typeof type === 'string') {
6985 e4a6c3b8 Olga Brani
          type = get(this, type, false) || this.store.modelFor(type);
6986 e4a6c3b8 Olga Brani
        }
6987 e4a6c3b8 Olga Brani
6988 e4a6c3b8 Olga Brani
        Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.",  type);
6989 e4a6c3b8 Olga Brani
6990 e4a6c3b8 Olga Brani
        if (!types.contains(type)) {
6991 e4a6c3b8 Olga Brani
          Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type);
6992 e4a6c3b8 Olga Brani
          types.push(type);
6993 e4a6c3b8 Olga Brani
        }
6994 e4a6c3b8 Olga Brani
      }
6995 e4a6c3b8 Olga Brani
    });
6996 e4a6c3b8 Olga Brani
6997 e4a6c3b8 Olga Brani
    return types;
6998 e4a6c3b8 Olga Brani
  }),
6999 e4a6c3b8 Olga Brani
7000 e4a6c3b8 Olga Brani
  /**
7001 e4a6c3b8 Olga Brani
    A map whose keys are the relationships of a model and whose values are
7002 e4a6c3b8 Olga Brani
    relationship descriptors.
7003 e4a6c3b8 Olga Brani

7004 e4a6c3b8 Olga Brani
    For example, given a model with this
7005 e4a6c3b8 Olga Brani
    definition:
7006 e4a6c3b8 Olga Brani

7007 e4a6c3b8 Olga Brani
    ```javascript
7008 e4a6c3b8 Olga Brani
    App.Blog = DS.Model.extend({
7009 e4a6c3b8 Olga Brani
      users: DS.hasMany('user'),
7010 e4a6c3b8 Olga Brani
      owner: DS.belongsTo('user'),
7011 e4a6c3b8 Olga Brani

7012 e4a6c3b8 Olga Brani
      posts: DS.hasMany('post')
7013 e4a6c3b8 Olga Brani
    });
7014 e4a6c3b8 Olga Brani
    ```
7015 e4a6c3b8 Olga Brani

7016 e4a6c3b8 Olga Brani
    This property would contain the following:
7017 e4a6c3b8 Olga Brani

7018 e4a6c3b8 Olga Brani
    ```javascript
7019 e4a6c3b8 Olga Brani
    var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName');
7020 e4a6c3b8 Olga Brani
    relationshipsByName.get('users');
7021 e4a6c3b8 Olga Brani
    //=> { key: 'users', kind: 'hasMany', type: App.User }
7022 e4a6c3b8 Olga Brani
    relationshipsByName.get('owner');
7023 e4a6c3b8 Olga Brani
    //=> { key: 'owner', kind: 'belongsTo', type: App.User }
7024 e4a6c3b8 Olga Brani
    ```
7025 e4a6c3b8 Olga Brani

7026 e4a6c3b8 Olga Brani
    @property relationshipsByName
7027 e4a6c3b8 Olga Brani
    @static
7028 e4a6c3b8 Olga Brani
    @type Ember.Map
7029 e4a6c3b8 Olga Brani
    @readOnly
7030 e4a6c3b8 Olga Brani
  */
7031 e4a6c3b8 Olga Brani
  relationshipsByName: Ember.computed(function() {
7032 e4a6c3b8 Olga Brani
    var map = Ember.Map.create(), type;
7033 e4a6c3b8 Olga Brani
7034 e4a6c3b8 Olga Brani
    this.eachComputedProperty(function(name, meta) {
7035 e4a6c3b8 Olga Brani
      if (meta.isRelationship) {
7036 e4a6c3b8 Olga Brani
        meta.key = name;
7037 e4a6c3b8 Olga Brani
        type = meta.type;
7038 e4a6c3b8 Olga Brani
7039 e4a6c3b8 Olga Brani
        if (!type && meta.kind === 'hasMany') {
7040 e4a6c3b8 Olga Brani
          type = Ember.String.singularize(name);
7041 e4a6c3b8 Olga Brani
        } else if (!type) {
7042 e4a6c3b8 Olga Brani
          type = name;
7043 e4a6c3b8 Olga Brani
        }
7044 e4a6c3b8 Olga Brani
7045 e4a6c3b8 Olga Brani
        if (typeof type === 'string') {
7046 e4a6c3b8 Olga Brani
          meta.type = this.store.modelFor(type);
7047 e4a6c3b8 Olga Brani
        }
7048 e4a6c3b8 Olga Brani
7049 e4a6c3b8 Olga Brani
        map.set(name, meta);
7050 e4a6c3b8 Olga Brani
      }
7051 e4a6c3b8 Olga Brani
    });
7052 e4a6c3b8 Olga Brani
7053 e4a6c3b8 Olga Brani
    return map;
7054 e4a6c3b8 Olga Brani
  }),
7055 e4a6c3b8 Olga Brani
7056 e4a6c3b8 Olga Brani
  /**
7057 e4a6c3b8 Olga Brani
    A map whose keys are the fields of the model and whose values are strings
7058 e4a6c3b8 Olga Brani
    describing the kind of the field. A model's fields are the union of all of its
7059 e4a6c3b8 Olga Brani
    attributes and relationships.
7060 e4a6c3b8 Olga Brani

7061 e4a6c3b8 Olga Brani
    For example:
7062 e4a6c3b8 Olga Brani

7063 e4a6c3b8 Olga Brani
    ```javascript
7064 e4a6c3b8 Olga Brani

7065 e4a6c3b8 Olga Brani
    App.Blog = DS.Model.extend({
7066 e4a6c3b8 Olga Brani
      users: DS.hasMany('user'),
7067 e4a6c3b8 Olga Brani
      owner: DS.belongsTo('user'),
7068 e4a6c3b8 Olga Brani

7069 e4a6c3b8 Olga Brani
      posts: DS.hasMany('post'),
7070 e4a6c3b8 Olga Brani

7071 e4a6c3b8 Olga Brani
      title: DS.attr('string')
7072 e4a6c3b8 Olga Brani
    });
7073 e4a6c3b8 Olga Brani

7074 e4a6c3b8 Olga Brani
    var fields = Ember.get(App.Blog, 'fields');
7075 e4a6c3b8 Olga Brani
    fields.forEach(function(field, kind) {
7076 e4a6c3b8 Olga Brani
      console.log(field, kind);
7077 e4a6c3b8 Olga Brani
    });
7078 e4a6c3b8 Olga Brani

7079 e4a6c3b8 Olga Brani
    // prints:
7080 e4a6c3b8 Olga Brani
    // users, hasMany
7081 e4a6c3b8 Olga Brani
    // owner, belongsTo
7082 e4a6c3b8 Olga Brani
    // posts, hasMany
7083 e4a6c3b8 Olga Brani
    // title, attribute
7084 e4a6c3b8 Olga Brani
    ```
7085 e4a6c3b8 Olga Brani

7086 e4a6c3b8 Olga Brani
    @property fields
7087 e4a6c3b8 Olga Brani
    @static
7088 e4a6c3b8 Olga Brani
    @type Ember.Map
7089 e4a6c3b8 Olga Brani
    @readOnly
7090 e4a6c3b8 Olga Brani
  */
7091 e4a6c3b8 Olga Brani
  fields: Ember.computed(function() {
7092 e4a6c3b8 Olga Brani
    var map = Ember.Map.create();
7093 e4a6c3b8 Olga Brani
7094 e4a6c3b8 Olga Brani
    this.eachComputedProperty(function(name, meta) {
7095 e4a6c3b8 Olga Brani
      if (meta.isRelationship) {
7096 e4a6c3b8 Olga Brani
        map.set(name, meta.kind);
7097 e4a6c3b8 Olga Brani
      } else if (meta.isAttribute) {
7098 e4a6c3b8 Olga Brani
        map.set(name, 'attribute');
7099 e4a6c3b8 Olga Brani
      }
7100 e4a6c3b8 Olga Brani
    });
7101 e4a6c3b8 Olga Brani
7102 e4a6c3b8 Olga Brani
    return map;
7103 e4a6c3b8 Olga Brani
  }),
7104 e4a6c3b8 Olga Brani
7105 e4a6c3b8 Olga Brani
  /**
7106 e4a6c3b8 Olga Brani
    Given a callback, iterates over each of the relationships in the model,
7107 e4a6c3b8 Olga Brani
    invoking the callback with the name of each relationship and its relationship
7108 e4a6c3b8 Olga Brani
    descriptor.
7109 e4a6c3b8 Olga Brani

7110 e4a6c3b8 Olga Brani
    @method eachRelationship
7111 e4a6c3b8 Olga Brani
    @static
7112 e4a6c3b8 Olga Brani
    @param {Function} callback the callback to invoke
7113 e4a6c3b8 Olga Brani
    @param {any} binding the value to which the callback's `this` should be bound
7114 e4a6c3b8 Olga Brani
  */
7115 e4a6c3b8 Olga Brani
  eachRelationship: function(callback, binding) {
7116 e4a6c3b8 Olga Brani
    get(this, 'relationshipsByName').forEach(function(name, relationship) {
7117 e4a6c3b8 Olga Brani
      callback.call(binding, name, relationship);
7118 e4a6c3b8 Olga Brani
    });
7119 e4a6c3b8 Olga Brani
  },
7120 e4a6c3b8 Olga Brani
7121 e4a6c3b8 Olga Brani
  /**
7122 e4a6c3b8 Olga Brani
    Given a callback, iterates over each of the types related to a model,
7123 e4a6c3b8 Olga Brani
    invoking the callback with the related type's class. Each type will be
7124 e4a6c3b8 Olga Brani
    returned just once, regardless of how many different relationships it has
7125 e4a6c3b8 Olga Brani
    with a model.
7126 e4a6c3b8 Olga Brani

7127 e4a6c3b8 Olga Brani
    @method eachRelatedType
7128 e4a6c3b8 Olga Brani
    @static
7129 e4a6c3b8 Olga Brani
    @param {Function} callback the callback to invoke
7130 e4a6c3b8 Olga Brani
    @param {any} binding the value to which the callback's `this` should be bound
7131 e4a6c3b8 Olga Brani
  */
7132 e4a6c3b8 Olga Brani
  eachRelatedType: function(callback, binding) {
7133 e4a6c3b8 Olga Brani
    get(this, 'relatedTypes').forEach(function(type) {
7134 e4a6c3b8 Olga Brani
      callback.call(binding, type);
7135 e4a6c3b8 Olga Brani
    });
7136 e4a6c3b8 Olga Brani
  }
7137 e4a6c3b8 Olga Brani
});
7138 e4a6c3b8 Olga Brani
7139 e4a6c3b8 Olga Brani
DS.Model.reopen({
7140 e4a6c3b8 Olga Brani
  /**
7141 e4a6c3b8 Olga Brani
    Given a callback, iterates over each of the relationships in the model,
7142 e4a6c3b8 Olga Brani
    invoking the callback with the name of each relationship and its relationship
7143 e4a6c3b8 Olga Brani
    descriptor.
7144 e4a6c3b8 Olga Brani

7145 e4a6c3b8 Olga Brani
    @method eachRelationship
7146 e4a6c3b8 Olga Brani
    @param {Function} callback the callback to invoke
7147 e4a6c3b8 Olga Brani
    @param {any} binding the value to which the callback's `this` should be bound
7148 e4a6c3b8 Olga Brani
  */
7149 e4a6c3b8 Olga Brani
  eachRelationship: function(callback, binding) {
7150 e4a6c3b8 Olga Brani
    this.constructor.eachRelationship(callback, binding);
7151 e4a6c3b8 Olga Brani
  }
7152 e4a6c3b8 Olga Brani
});
7153 e4a6c3b8 Olga Brani
7154 e4a6c3b8 Olga Brani
})();
7155 e4a6c3b8 Olga Brani
7156 e4a6c3b8 Olga Brani
7157 e4a6c3b8 Olga Brani
7158 e4a6c3b8 Olga Brani
(function() {
7159 e4a6c3b8 Olga Brani
/**
7160 e4a6c3b8 Olga Brani
  @module ember-data
7161 e4a6c3b8 Olga Brani
*/
7162 e4a6c3b8 Olga Brani
7163 e4a6c3b8 Olga Brani
})();
7164 e4a6c3b8 Olga Brani
7165 e4a6c3b8 Olga Brani
7166 e4a6c3b8 Olga Brani
7167 e4a6c3b8 Olga Brani
(function() {
7168 e4a6c3b8 Olga Brani
/**
7169 e4a6c3b8 Olga Brani
  @module ember-data
7170 e4a6c3b8 Olga Brani
*/
7171 e4a6c3b8 Olga Brani
7172 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
7173 e4a6c3b8 Olga Brani
var once = Ember.run.once;
7174 e4a6c3b8 Olga Brani
var forEach = Ember.EnumerableUtils.forEach;
7175 e4a6c3b8 Olga Brani
7176 e4a6c3b8 Olga Brani
/**
7177 e4a6c3b8 Olga Brani
  @class RecordArrayManager
7178 e4a6c3b8 Olga Brani
  @namespace DS
7179 e4a6c3b8 Olga Brani
  @private
7180 e4a6c3b8 Olga Brani
  @extends Ember.Object
7181 e4a6c3b8 Olga Brani
*/
7182 e4a6c3b8 Olga Brani
DS.RecordArrayManager = Ember.Object.extend({
7183 e4a6c3b8 Olga Brani
  init: function() {
7184 e4a6c3b8 Olga Brani
    this.filteredRecordArrays = Ember.MapWithDefault.create({
7185 e4a6c3b8 Olga Brani
      defaultValue: function() { return []; }
7186 e4a6c3b8 Olga Brani
    });
7187 e4a6c3b8 Olga Brani
7188 e4a6c3b8 Olga Brani
    this.changedRecords = [];
7189 e4a6c3b8 Olga Brani
  },
7190 e4a6c3b8 Olga Brani
7191 e4a6c3b8 Olga Brani
  recordDidChange: function(record) {
7192 e4a6c3b8 Olga Brani
    this.changedRecords.push(record);
7193 e4a6c3b8 Olga Brani
    once(this, this.updateRecordArrays);
7194 e4a6c3b8 Olga Brani
  },
7195 e4a6c3b8 Olga Brani
7196 e4a6c3b8 Olga Brani
  recordArraysForRecord: function(record) {
7197 e4a6c3b8 Olga Brani
    record._recordArrays = record._recordArrays || Ember.OrderedSet.create();
7198 e4a6c3b8 Olga Brani
    return record._recordArrays;
7199 e4a6c3b8 Olga Brani
  },
7200 e4a6c3b8 Olga Brani
7201 e4a6c3b8 Olga Brani
  /**
7202 e4a6c3b8 Olga Brani
    This method is invoked whenever data is loaded into the store by the
7203 e4a6c3b8 Olga Brani
    adapter or updated by the adapter, or when a record has changed.
7204 e4a6c3b8 Olga Brani

7205 e4a6c3b8 Olga Brani
    It updates all record arrays that a record belongs to.
7206 e4a6c3b8 Olga Brani

7207 e4a6c3b8 Olga Brani
    To avoid thrashing, it only runs at most once per run loop.
7208 e4a6c3b8 Olga Brani

7209 e4a6c3b8 Olga Brani
    @method updateRecordArrays
7210 e4a6c3b8 Olga Brani
    @param {Class} type
7211 e4a6c3b8 Olga Brani
    @param {Number|String} clientId
7212 e4a6c3b8 Olga Brani
  */
7213 e4a6c3b8 Olga Brani
  updateRecordArrays: function() {
7214 e4a6c3b8 Olga Brani
    forEach(this.changedRecords, function(record) {
7215 e4a6c3b8 Olga Brani
      if (get(record, 'isDeleted')) {
7216 e4a6c3b8 Olga Brani
        this._recordWasDeleted(record);
7217 e4a6c3b8 Olga Brani
      } else {
7218 e4a6c3b8 Olga Brani
        this._recordWasChanged(record);
7219 e4a6c3b8 Olga Brani
      }
7220 e4a6c3b8 Olga Brani
    }, this);
7221 e4a6c3b8 Olga Brani
7222 e4a6c3b8 Olga Brani
    this.changedRecords = [];
7223 e4a6c3b8 Olga Brani
  },
7224 e4a6c3b8 Olga Brani
7225 e4a6c3b8 Olga Brani
  _recordWasDeleted: function (record) {
7226 e4a6c3b8 Olga Brani
    var recordArrays = record._recordArrays;
7227 e4a6c3b8 Olga Brani
7228 e4a6c3b8 Olga Brani
    if (!recordArrays) { return; }
7229 e4a6c3b8 Olga Brani
7230 e4a6c3b8 Olga Brani
    forEach(recordArrays, function(array) {
7231 e4a6c3b8 Olga Brani
      array.removeRecord(record);
7232 e4a6c3b8 Olga Brani
    });
7233 e4a6c3b8 Olga Brani
  },
7234 e4a6c3b8 Olga Brani
7235 e4a6c3b8 Olga Brani
  _recordWasChanged: function (record) {
7236 e4a6c3b8 Olga Brani
    var type = record.constructor,
7237 e4a6c3b8 Olga Brani
        recordArrays = this.filteredRecordArrays.get(type),
7238 e4a6c3b8 Olga Brani
        filter;
7239 e4a6c3b8 Olga Brani
7240 e4a6c3b8 Olga Brani
    forEach(recordArrays, function(array) {
7241 e4a6c3b8 Olga Brani
      filter = get(array, 'filterFunction');
7242 e4a6c3b8 Olga Brani
      this.updateRecordArray(array, filter, type, record);
7243 e4a6c3b8 Olga Brani
    }, this);
7244 e4a6c3b8 Olga Brani
7245 e4a6c3b8 Olga Brani
    // loop through all manyArrays containing an unloaded copy of this
7246 e4a6c3b8 Olga Brani
    // clientId and notify them that the record was loaded.
7247 e4a6c3b8 Olga Brani
    var manyArrays = record._loadingRecordArrays;
7248 e4a6c3b8 Olga Brani
7249 e4a6c3b8 Olga Brani
    if (manyArrays) {
7250 e4a6c3b8 Olga Brani
      for (var i=0, l=manyArrays.length; i<l; i++) {
7251 e4a6c3b8 Olga Brani
        manyArrays[i].loadedRecord();
7252 e4a6c3b8 Olga Brani
      }
7253 e4a6c3b8 Olga Brani
7254 e4a6c3b8 Olga Brani
      record._loadingRecordArrays = [];
7255 e4a6c3b8 Olga Brani
    }
7256 e4a6c3b8 Olga Brani
  },
7257 e4a6c3b8 Olga Brani
7258 e4a6c3b8 Olga Brani
  /**
7259 e4a6c3b8 Olga Brani
    Update an individual filter.
7260 e4a6c3b8 Olga Brani

7261 e4a6c3b8 Olga Brani
    @method updateRecordArray
7262 e4a6c3b8 Olga Brani
    @param {DS.FilteredRecordArray} array
7263 e4a6c3b8 Olga Brani
    @param {Function} filter
7264 e4a6c3b8 Olga Brani
    @param {Class} type
7265 e4a6c3b8 Olga Brani
    @param {Number|String} clientId
7266 e4a6c3b8 Olga Brani
  */
7267 e4a6c3b8 Olga Brani
  updateRecordArray: function(array, filter, type, record) {
7268 e4a6c3b8 Olga Brani
    var shouldBeInArray;
7269 e4a6c3b8 Olga Brani
7270 e4a6c3b8 Olga Brani
    if (!filter) {
7271 e4a6c3b8 Olga Brani
      shouldBeInArray = true;
7272 e4a6c3b8 Olga Brani
    } else {
7273 e4a6c3b8 Olga Brani
      shouldBeInArray = filter(record);
7274 e4a6c3b8 Olga Brani
    }
7275 e4a6c3b8 Olga Brani
7276 e4a6c3b8 Olga Brani
    var recordArrays = this.recordArraysForRecord(record);
7277 e4a6c3b8 Olga Brani
7278 e4a6c3b8 Olga Brani
    if (shouldBeInArray) {
7279 e4a6c3b8 Olga Brani
      recordArrays.add(array);
7280 e4a6c3b8 Olga Brani
      array.addRecord(record);
7281 e4a6c3b8 Olga Brani
    } else if (!shouldBeInArray) {
7282 e4a6c3b8 Olga Brani
      recordArrays.remove(array);
7283 e4a6c3b8 Olga Brani
      array.removeRecord(record);
7284 e4a6c3b8 Olga Brani
    }
7285 e4a6c3b8 Olga Brani
  },
7286 e4a6c3b8 Olga Brani
7287 e4a6c3b8 Olga Brani
  /**
7288 e4a6c3b8 Olga Brani
    This method is invoked if the `filterFunction` property is
7289 e4a6c3b8 Olga Brani
    changed on a `DS.FilteredRecordArray`.
7290 e4a6c3b8 Olga Brani

7291 e4a6c3b8 Olga Brani
    It essentially re-runs the filter from scratch. This same
7292 e4a6c3b8 Olga Brani
    method is invoked when the filter is created in th first place.
7293 e4a6c3b8 Olga Brani

7294 e4a6c3b8 Olga Brani
    @method updateFilter
7295 e4a6c3b8 Olga Brani
    @param array
7296 e4a6c3b8 Olga Brani
    @param type
7297 e4a6c3b8 Olga Brani
    @param filter
7298 e4a6c3b8 Olga Brani
  */
7299 e4a6c3b8 Olga Brani
  updateFilter: function(array, type, filter) {
7300 e4a6c3b8 Olga Brani
    var typeMap = this.store.typeMapFor(type),
7301 e4a6c3b8 Olga Brani
        records = typeMap.records, record;
7302 e4a6c3b8 Olga Brani
7303 e4a6c3b8 Olga Brani
    for (var i=0, l=records.length; i<l; i++) {
7304 e4a6c3b8 Olga Brani
      record = records[i];
7305 e4a6c3b8 Olga Brani
7306 e4a6c3b8 Olga Brani
      if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) {
7307 e4a6c3b8 Olga Brani
        this.updateRecordArray(array, filter, type, record);
7308 e4a6c3b8 Olga Brani
      }
7309 e4a6c3b8 Olga Brani
    }
7310 e4a6c3b8 Olga Brani
  },
7311 e4a6c3b8 Olga Brani
7312 e4a6c3b8 Olga Brani
  /**
7313 e4a6c3b8 Olga Brani
    Create a `DS.ManyArray` for a type and list of record references, and index
7314 e4a6c3b8 Olga Brani
    the `ManyArray` under each reference. This allows us to efficiently remove
7315 e4a6c3b8 Olga Brani
    records from `ManyArray`s when they are deleted.
7316 e4a6c3b8 Olga Brani

7317 e4a6c3b8 Olga Brani
    @method createManyArray
7318 e4a6c3b8 Olga Brani
    @param {Class} type
7319 e4a6c3b8 Olga Brani
    @param {Array} references
7320 e4a6c3b8 Olga Brani
    @return {DS.ManyArray}
7321 e4a6c3b8 Olga Brani
  */
7322 e4a6c3b8 Olga Brani
  createManyArray: function(type, records) {
7323 e4a6c3b8 Olga Brani
    var manyArray = DS.ManyArray.create({
7324 e4a6c3b8 Olga Brani
      type: type,
7325 e4a6c3b8 Olga Brani
      content: records,
7326 e4a6c3b8 Olga Brani
      store: this.store
7327 e4a6c3b8 Olga Brani
    });
7328 e4a6c3b8 Olga Brani
7329 e4a6c3b8 Olga Brani
    forEach(records, function(record) {
7330 e4a6c3b8 Olga Brani
      var arrays = this.recordArraysForRecord(record);
7331 e4a6c3b8 Olga Brani
      arrays.add(manyArray);
7332 e4a6c3b8 Olga Brani
    }, this);
7333 e4a6c3b8 Olga Brani
7334 e4a6c3b8 Olga Brani
    return manyArray;
7335 e4a6c3b8 Olga Brani
  },
7336 e4a6c3b8 Olga Brani
7337 e4a6c3b8 Olga Brani
  /**
7338 e4a6c3b8 Olga Brani
    Create a `DS.RecordArray` for a type and register it for updates.
7339 e4a6c3b8 Olga Brani

7340 e4a6c3b8 Olga Brani
    @method createRecordArray
7341 e4a6c3b8 Olga Brani
    @param {Class} type
7342 e4a6c3b8 Olga Brani
    @return {DS.RecordArray}
7343 e4a6c3b8 Olga Brani
  */
7344 e4a6c3b8 Olga Brani
  createRecordArray: function(type) {
7345 e4a6c3b8 Olga Brani
    var array = DS.RecordArray.create({
7346 e4a6c3b8 Olga Brani
      type: type,
7347 e4a6c3b8 Olga Brani
      content: Ember.A(),
7348 e4a6c3b8 Olga Brani
      store: this.store,
7349 e4a6c3b8 Olga Brani
      isLoaded: true
7350 e4a6c3b8 Olga Brani
    });
7351 e4a6c3b8 Olga Brani
7352 e4a6c3b8 Olga Brani
    this.registerFilteredRecordArray(array, type);
7353 e4a6c3b8 Olga Brani
7354 e4a6c3b8 Olga Brani
    return array;
7355 e4a6c3b8 Olga Brani
  },
7356 e4a6c3b8 Olga Brani
7357 e4a6c3b8 Olga Brani
  /**
7358 e4a6c3b8 Olga Brani
    Create a `DS.FilteredRecordArray` for a type and register it for updates.
7359 e4a6c3b8 Olga Brani

7360 e4a6c3b8 Olga Brani
    @method createFilteredRecordArray
7361 e4a6c3b8 Olga Brani
    @param {Class} type
7362 e4a6c3b8 Olga Brani
    @param {Function} filter
7363 e4a6c3b8 Olga Brani
    @return {DS.FilteredRecordArray}
7364 e4a6c3b8 Olga Brani
  */
7365 e4a6c3b8 Olga Brani
  createFilteredRecordArray: function(type, filter) {
7366 e4a6c3b8 Olga Brani
    var array = DS.FilteredRecordArray.create({
7367 e4a6c3b8 Olga Brani
      type: type,
7368 e4a6c3b8 Olga Brani
      content: Ember.A(),
7369 e4a6c3b8 Olga Brani
      store: this.store,
7370 e4a6c3b8 Olga Brani
      manager: this,
7371 e4a6c3b8 Olga Brani
      filterFunction: filter
7372 e4a6c3b8 Olga Brani
    });
7373 e4a6c3b8 Olga Brani
7374 e4a6c3b8 Olga Brani
    this.registerFilteredRecordArray(array, type, filter);
7375 e4a6c3b8 Olga Brani
7376 e4a6c3b8 Olga Brani
    return array;
7377 e4a6c3b8 Olga Brani
  },
7378 e4a6c3b8 Olga Brani
7379 e4a6c3b8 Olga Brani
  /**
7380 e4a6c3b8 Olga Brani
    Create a `DS.AdapterPopulatedRecordArray` for a type with given query.
7381 e4a6c3b8 Olga Brani

7382 e4a6c3b8 Olga Brani
    @method createAdapterPopulatedRecordArray
7383 e4a6c3b8 Olga Brani
    @param {Class} type
7384 e4a6c3b8 Olga Brani
    @param {Object} query
7385 e4a6c3b8 Olga Brani
    @return {DS.AdapterPopulatedRecordArray}
7386 e4a6c3b8 Olga Brani
  */
7387 e4a6c3b8 Olga Brani
  createAdapterPopulatedRecordArray: function(type, query) {
7388 e4a6c3b8 Olga Brani
    return DS.AdapterPopulatedRecordArray.create({
7389 e4a6c3b8 Olga Brani
      type: type,
7390 e4a6c3b8 Olga Brani
      query: query,
7391 e4a6c3b8 Olga Brani
      content: Ember.A(),
7392 e4a6c3b8 Olga Brani
      store: this.store
7393 e4a6c3b8 Olga Brani
    });
7394 e4a6c3b8 Olga Brani
  },
7395 e4a6c3b8 Olga Brani
7396 e4a6c3b8 Olga Brani
  /**
7397 e4a6c3b8 Olga Brani
    Register a RecordArray for a given type to be backed by
7398 e4a6c3b8 Olga Brani
    a filter function. This will cause the array to update
7399 e4a6c3b8 Olga Brani
    automatically when records of that type change attribute
7400 e4a6c3b8 Olga Brani
    values or states.
7401 e4a6c3b8 Olga Brani

7402 e4a6c3b8 Olga Brani
    @method registerFilteredRecordArray
7403 e4a6c3b8 Olga Brani
    @param {DS.RecordArray} array
7404 e4a6c3b8 Olga Brani
    @param {Class} type
7405 e4a6c3b8 Olga Brani
    @param {Function} filter
7406 e4a6c3b8 Olga Brani
  */
7407 e4a6c3b8 Olga Brani
  registerFilteredRecordArray: function(array, type, filter) {
7408 e4a6c3b8 Olga Brani
    var recordArrays = this.filteredRecordArrays.get(type);
7409 e4a6c3b8 Olga Brani
    recordArrays.push(array);
7410 e4a6c3b8 Olga Brani
7411 e4a6c3b8 Olga Brani
    this.updateFilter(array, type, filter);
7412 e4a6c3b8 Olga Brani
  },
7413 e4a6c3b8 Olga Brani
7414 e4a6c3b8 Olga Brani
  // Internally, we maintain a map of all unloaded IDs requested by
7415 e4a6c3b8 Olga Brani
  // a ManyArray. As the adapter loads data into the store, the
7416 e4a6c3b8 Olga Brani
  // store notifies any interested ManyArrays. When the ManyArray's
7417 e4a6c3b8 Olga Brani
  // total number of loading records drops to zero, it becomes
7418 e4a6c3b8 Olga Brani
  // `isLoaded` and fires a `didLoad` event.
7419 e4a6c3b8 Olga Brani
  registerWaitingRecordArray: function(record, array) {
7420 e4a6c3b8 Olga Brani
    var loadingRecordArrays = record._loadingRecordArrays || [];
7421 e4a6c3b8 Olga Brani
    loadingRecordArrays.push(array);
7422 e4a6c3b8 Olga Brani
    record._loadingRecordArrays = loadingRecordArrays;
7423 e4a6c3b8 Olga Brani
  }
7424 e4a6c3b8 Olga Brani
});
7425 e4a6c3b8 Olga Brani
7426 e4a6c3b8 Olga Brani
})();
7427 e4a6c3b8 Olga Brani
7428 e4a6c3b8 Olga Brani
7429 e4a6c3b8 Olga Brani
7430 e4a6c3b8 Olga Brani
(function() {
7431 e4a6c3b8 Olga Brani
/**
7432 e4a6c3b8 Olga Brani
  @module ember-data
7433 e4a6c3b8 Olga Brani
*/
7434 e4a6c3b8 Olga Brani
7435 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
7436 e4a6c3b8 Olga Brani
var map = Ember.ArrayPolyfills.map;
7437 e4a6c3b8 Olga Brani
7438 e4a6c3b8 Olga Brani
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
7439 e4a6c3b8 Olga Brani
7440 e4a6c3b8 Olga Brani
/**
7441 e4a6c3b8 Olga Brani
  A `DS.InvalidError` is used by an adapter to signal the external API
7442 e4a6c3b8 Olga Brani
  was unable to process a request because the content was not
7443 e4a6c3b8 Olga Brani
  semantically correct or meaningful per the API. Usually this means a
7444 e4a6c3b8 Olga Brani
  record failed some form of server side validation. When a promise
7445 e4a6c3b8 Olga Brani
  from an adapter is rejected with a `DS.InvalidError` the record will
7446 e4a6c3b8 Olga Brani
  transition to the `invalid` state and the errors will be set to the
7447 e4a6c3b8 Olga Brani
  `errors` property on the record.
7448 e4a6c3b8 Olga Brani

7449 e4a6c3b8 Olga Brani
  Example
7450 e4a6c3b8 Olga Brani

7451 e4a6c3b8 Olga Brani
  ```javascript
7452 e4a6c3b8 Olga Brani
  App.ApplicationAdapter = DS.RESTAdapter.extend({
7453 e4a6c3b8 Olga Brani
    ajaxError: function(jqXHR) {
7454 e4a6c3b8 Olga Brani
      var error = this._super(jqXHR);
7455 e4a6c3b8 Olga Brani

7456 e4a6c3b8 Olga Brani
      if (jqXHR && jqXHR.status === 422) {
7457 e4a6c3b8 Olga Brani
        var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
7458 e4a6c3b8 Olga Brani
        return new DS.InvalidError(jsonErrors);
7459 e4a6c3b8 Olga Brani
      } else {
7460 e4a6c3b8 Olga Brani
        return error;
7461 e4a6c3b8 Olga Brani
      }
7462 e4a6c3b8 Olga Brani
    }
7463 e4a6c3b8 Olga Brani
  });
7464 e4a6c3b8 Olga Brani
  ```
7465 e4a6c3b8 Olga Brani

7466 e4a6c3b8 Olga Brani
  @class InvalidError
7467 e4a6c3b8 Olga Brani
  @namespace DS
7468 e4a6c3b8 Olga Brani
*/
7469 e4a6c3b8 Olga Brani
DS.InvalidError = function(errors) {
7470 e4a6c3b8 Olga Brani
  var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors));
7471 e4a6c3b8 Olga Brani
  this.errors = errors;
7472 e4a6c3b8 Olga Brani
7473 e4a6c3b8 Olga Brani
  for (var i=0, l=errorProps.length; i<l; i++) {
7474 e4a6c3b8 Olga Brani
    this[errorProps[i]] = tmp[errorProps[i]];
7475 e4a6c3b8 Olga Brani
  }
7476 e4a6c3b8 Olga Brani
};
7477 e4a6c3b8 Olga Brani
DS.InvalidError.prototype = Ember.create(Error.prototype);
7478 e4a6c3b8 Olga Brani
7479 e4a6c3b8 Olga Brani
/**
7480 e4a6c3b8 Olga Brani
  An adapter is an object that receives requests from a store and
7481 e4a6c3b8 Olga Brani
  translates them into the appropriate action to take against your
7482 e4a6c3b8 Olga Brani
  persistence layer. The persistence layer is usually an HTTP API, but
7483 e4a6c3b8 Olga Brani
  may be anything, such as the browser's local storage. Typically the
7484 e4a6c3b8 Olga Brani
  adapter is not invoked directly instead its functionality is accessed
7485 e4a6c3b8 Olga Brani
  through the `store`.
7486 e4a6c3b8 Olga Brani

7487 e4a6c3b8 Olga Brani
  ### Creating an Adapter
7488 e4a6c3b8 Olga Brani

7489 e4a6c3b8 Olga Brani
  First, create a new subclass of `DS.Adapter`:
7490 e4a6c3b8 Olga Brani

7491 e4a6c3b8 Olga Brani
  ```javascript
7492 e4a6c3b8 Olga Brani
  App.MyAdapter = DS.Adapter.extend({
7493 e4a6c3b8 Olga Brani
    // ...your code here
7494 e4a6c3b8 Olga Brani
  });
7495 e4a6c3b8 Olga Brani
  ```
7496 e4a6c3b8 Olga Brani

7497 e4a6c3b8 Olga Brani
  To tell your store which adapter to use, set its `adapter` property:
7498 e4a6c3b8 Olga Brani

7499 e4a6c3b8 Olga Brani
  ```javascript
7500 e4a6c3b8 Olga Brani
  App.store = DS.Store.create({
7501 e4a6c3b8 Olga Brani
    adapter: 'MyAdapter'
7502 e4a6c3b8 Olga Brani
  });
7503 e4a6c3b8 Olga Brani
  ```
7504 e4a6c3b8 Olga Brani

7505 e4a6c3b8 Olga Brani
  `DS.Adapter` is an abstract base class that you should override in your
7506 e4a6c3b8 Olga Brani
  application to customize it for your backend. The minimum set of methods
7507 e4a6c3b8 Olga Brani
  that you should implement is:
7508 e4a6c3b8 Olga Brani

7509 e4a6c3b8 Olga Brani
    * `find()`
7510 e4a6c3b8 Olga Brani
    * `createRecord()`
7511 e4a6c3b8 Olga Brani
    * `updateRecord()`
7512 e4a6c3b8 Olga Brani
    * `deleteRecord()`
7513 e4a6c3b8 Olga Brani
    * `findAll()`
7514 e4a6c3b8 Olga Brani
    * `findQuery()`
7515 e4a6c3b8 Olga Brani

7516 e4a6c3b8 Olga Brani
  To improve the network performance of your application, you can optimize
7517 e4a6c3b8 Olga Brani
  your adapter by overriding these lower-level methods:
7518 e4a6c3b8 Olga Brani

7519 e4a6c3b8 Olga Brani
    * `findMany()`
7520 e4a6c3b8 Olga Brani

7521 e4a6c3b8 Olga Brani

7522 e4a6c3b8 Olga Brani
  For an example implementation, see `DS.RESTAdapter`, the
7523 e4a6c3b8 Olga Brani
  included REST adapter.
7524 e4a6c3b8 Olga Brani

7525 e4a6c3b8 Olga Brani
  @class Adapter
7526 e4a6c3b8 Olga Brani
  @namespace DS
7527 e4a6c3b8 Olga Brani
  @extends Ember.Object
7528 e4a6c3b8 Olga Brani
*/
7529 e4a6c3b8 Olga Brani
7530 e4a6c3b8 Olga Brani
DS.Adapter = Ember.Object.extend({
7531 e4a6c3b8 Olga Brani
7532 e4a6c3b8 Olga Brani
  /**
7533 e4a6c3b8 Olga Brani
    If you would like your adapter to use a custom serializer you can
7534 e4a6c3b8 Olga Brani
    set the `defaultSerializer` property to be the name of the custom
7535 e4a6c3b8 Olga Brani
    serializer.
7536 e4a6c3b8 Olga Brani

7537 e4a6c3b8 Olga Brani
    Note the `defaultSerializer` serializer has a lower priority then
7538 e4a6c3b8 Olga Brani
    a model specific serializer (i.e. `PostSerializer`) or the
7539 e4a6c3b8 Olga Brani
    `application` serializer.
7540 e4a6c3b8 Olga Brani

7541 e4a6c3b8 Olga Brani
    ```javascript
7542 e4a6c3b8 Olga Brani
    var DjangoAdapter = DS.Adapter.extend({
7543 e4a6c3b8 Olga Brani
      defaultSerializer: 'django'
7544 e4a6c3b8 Olga Brani
    });
7545 e4a6c3b8 Olga Brani
    ```
7546 e4a6c3b8 Olga Brani

7547 e4a6c3b8 Olga Brani
    @property defaultSerializer
7548 e4a6c3b8 Olga Brani
    @type {String}
7549 e4a6c3b8 Olga Brani
  */
7550 e4a6c3b8 Olga Brani
7551 e4a6c3b8 Olga Brani
  /**
7552 e4a6c3b8 Olga Brani
    The `find()` method is invoked when the store is asked for a record that
7553 e4a6c3b8 Olga Brani
    has not previously been loaded. In response to `find()` being called, you
7554 e4a6c3b8 Olga Brani
    should query your persistence layer for a record with the given ID. Once
7555 e4a6c3b8 Olga Brani
    found, you can asynchronously call the store's `push()` method to push
7556 e4a6c3b8 Olga Brani
    the record into the store.
7557 e4a6c3b8 Olga Brani

7558 e4a6c3b8 Olga Brani
    Here is an example `find` implementation:
7559 e4a6c3b8 Olga Brani

7560 e4a6c3b8 Olga Brani
    ```javascript
7561 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7562 e4a6c3b8 Olga Brani
      find: function(store, type, id) {
7563 e4a6c3b8 Olga Brani
        var url = [type, id].join('/');
7564 e4a6c3b8 Olga Brani

7565 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7566 e4a6c3b8 Olga Brani
          jQuery.getJSON(url).then(function(data) {
7567 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7568 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7569 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7570 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7571 e4a6c3b8 Olga Brani
          });
7572 e4a6c3b8 Olga Brani
        });
7573 e4a6c3b8 Olga Brani
      }
7574 e4a6c3b8 Olga Brani
    });
7575 e4a6c3b8 Olga Brani
    ```
7576 e4a6c3b8 Olga Brani

7577 e4a6c3b8 Olga Brani
    @method find
7578 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7579 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
7580 e4a6c3b8 Olga Brani
    @param {String} id
7581 e4a6c3b8 Olga Brani
    @return {Promise} promise
7582 e4a6c3b8 Olga Brani
  */
7583 e4a6c3b8 Olga Brani
  find: Ember.required(Function),
7584 e4a6c3b8 Olga Brani
7585 e4a6c3b8 Olga Brani
  /**
7586 e4a6c3b8 Olga Brani
    The `findAll()` method is called when you call `find` on the store
7587 e4a6c3b8 Olga Brani
    without an ID (i.e. `store.find('post')`).
7588 e4a6c3b8 Olga Brani

7589 e4a6c3b8 Olga Brani
    Example
7590 e4a6c3b8 Olga Brani

7591 e4a6c3b8 Olga Brani
    ```javascript
7592 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7593 e4a6c3b8 Olga Brani
      findAll: function(store, type, sinceToken) {
7594 e4a6c3b8 Olga Brani
        var url = type;
7595 e4a6c3b8 Olga Brani
        var query = { since: sinceToken };
7596 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7597 e4a6c3b8 Olga Brani
          jQuery.getJSON(url, query).then(function(data) {
7598 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7599 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7600 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7601 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7602 e4a6c3b8 Olga Brani
          });
7603 e4a6c3b8 Olga Brani
        });
7604 e4a6c3b8 Olga Brani
      }
7605 e4a6c3b8 Olga Brani
    });
7606 e4a6c3b8 Olga Brani
    ```
7607 e4a6c3b8 Olga Brani

7608 e4a6c3b8 Olga Brani
    @private
7609 e4a6c3b8 Olga Brani
    @method findAll
7610 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7611 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
7612 e4a6c3b8 Olga Brani
    @param {String} sinceToken
7613 e4a6c3b8 Olga Brani
    @return {Promise} promise
7614 e4a6c3b8 Olga Brani
  */
7615 e4a6c3b8 Olga Brani
  findAll: null,
7616 e4a6c3b8 Olga Brani
7617 e4a6c3b8 Olga Brani
  /**
7618 e4a6c3b8 Olga Brani
    This method is called when you call `find` on the store with a
7619 e4a6c3b8 Olga Brani
    query object as the second parameter (i.e. `store.find('person', {
7620 e4a6c3b8 Olga Brani
    page: 1 })`).
7621 e4a6c3b8 Olga Brani

7622 e4a6c3b8 Olga Brani
    Example
7623 e4a6c3b8 Olga Brani

7624 e4a6c3b8 Olga Brani
    ```javascript
7625 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7626 e4a6c3b8 Olga Brani
      findQuery: function(store, type, query) {
7627 e4a6c3b8 Olga Brani
        var url = type;
7628 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7629 e4a6c3b8 Olga Brani
          jQuery.getJSON(url, query).then(function(data) {
7630 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7631 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7632 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7633 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7634 e4a6c3b8 Olga Brani
          });
7635 e4a6c3b8 Olga Brani
        });
7636 e4a6c3b8 Olga Brani
      }
7637 e4a6c3b8 Olga Brani
    });
7638 e4a6c3b8 Olga Brani
    ```
7639 e4a6c3b8 Olga Brani

7640 e4a6c3b8 Olga Brani
    @private
7641 e4a6c3b8 Olga Brani
    @method findQuery
7642 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7643 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
7644 e4a6c3b8 Olga Brani
    @param {Object} query
7645 e4a6c3b8 Olga Brani
    @param {DS.AdapterPopulatedRecordArray} recordArray
7646 e4a6c3b8 Olga Brani
    @return {Promise} promise
7647 e4a6c3b8 Olga Brani
  */
7648 e4a6c3b8 Olga Brani
  findQuery: null,
7649 e4a6c3b8 Olga Brani
7650 e4a6c3b8 Olga Brani
  /**
7651 e4a6c3b8 Olga Brani
    If the globally unique IDs for your records should be generated on the client,
7652 e4a6c3b8 Olga Brani
    implement the `generateIdForRecord()` method. This method will be invoked
7653 e4a6c3b8 Olga Brani
    each time you create a new record, and the value returned from it will be
7654 e4a6c3b8 Olga Brani
    assigned to the record's `primaryKey`.
7655 e4a6c3b8 Olga Brani

7656 e4a6c3b8 Olga Brani
    Most traditional REST-like HTTP APIs will not use this method. Instead, the ID
7657 e4a6c3b8 Olga Brani
    of the record will be set by the server, and your adapter will update the store
7658 e4a6c3b8 Olga Brani
    with the new ID when it calls `didCreateRecord()`. Only implement this method if
7659 e4a6c3b8 Olga Brani
    you intend to generate record IDs on the client-side.
7660 e4a6c3b8 Olga Brani

7661 e4a6c3b8 Olga Brani
    The `generateIdForRecord()` method will be invoked with the requesting store as
7662 e4a6c3b8 Olga Brani
    the first parameter and the newly created record as the second parameter:
7663 e4a6c3b8 Olga Brani

7664 e4a6c3b8 Olga Brani
    ```javascript
7665 e4a6c3b8 Olga Brani
    generateIdForRecord: function(store, record) {
7666 e4a6c3b8 Olga Brani
      var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision();
7667 e4a6c3b8 Olga Brani
      return uuid;
7668 e4a6c3b8 Olga Brani
    }
7669 e4a6c3b8 Olga Brani
    ```
7670 e4a6c3b8 Olga Brani

7671 e4a6c3b8 Olga Brani
    @method generateIdForRecord
7672 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7673 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7674 e4a6c3b8 Olga Brani
    @return {String|Number} id
7675 e4a6c3b8 Olga Brani
  */
7676 e4a6c3b8 Olga Brani
  generateIdForRecord: null,
7677 e4a6c3b8 Olga Brani
7678 e4a6c3b8 Olga Brani
  /**
7679 e4a6c3b8 Olga Brani
    Proxies to the serializer's `serialize` method.
7680 e4a6c3b8 Olga Brani

7681 e4a6c3b8 Olga Brani
    Example
7682 e4a6c3b8 Olga Brani

7683 e4a6c3b8 Olga Brani
    ```javascript
7684 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7685 e4a6c3b8 Olga Brani
      createRecord: function(store, type, record) {
7686 e4a6c3b8 Olga Brani
        var data = this.serialize(record, { includeId: true });
7687 e4a6c3b8 Olga Brani
        var url = type;
7688 e4a6c3b8 Olga Brani

7689 e4a6c3b8 Olga Brani
        // ...
7690 e4a6c3b8 Olga Brani
      }
7691 e4a6c3b8 Olga Brani
    });
7692 e4a6c3b8 Olga Brani
    ```
7693 e4a6c3b8 Olga Brani

7694 e4a6c3b8 Olga Brani
    @method serialize
7695 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7696 e4a6c3b8 Olga Brani
    @param {Object}   options
7697 e4a6c3b8 Olga Brani
    @return {Object} serialized record
7698 e4a6c3b8 Olga Brani
  */
7699 e4a6c3b8 Olga Brani
  serialize: function(record, options) {
7700 e4a6c3b8 Olga Brani
    return get(record, 'store').serializerFor(record.constructor.typeKey).serialize(record, options);
7701 e4a6c3b8 Olga Brani
  },
7702 e4a6c3b8 Olga Brani
7703 e4a6c3b8 Olga Brani
  /**
7704 e4a6c3b8 Olga Brani
    Implement this method in a subclass to handle the creation of
7705 e4a6c3b8 Olga Brani
    new records.
7706 e4a6c3b8 Olga Brani

7707 e4a6c3b8 Olga Brani
    Serializes the record and send it to the server.
7708 e4a6c3b8 Olga Brani

7709 e4a6c3b8 Olga Brani
    Example
7710 e4a6c3b8 Olga Brani

7711 e4a6c3b8 Olga Brani
    ```javascript
7712 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7713 e4a6c3b8 Olga Brani
      createRecord: function(store, type, record) {
7714 e4a6c3b8 Olga Brani
        var data = this.serialize(record, { includeId: true });
7715 e4a6c3b8 Olga Brani
        var url = type;
7716 e4a6c3b8 Olga Brani

7717 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7718 e4a6c3b8 Olga Brani
          jQuery.ajax({
7719 e4a6c3b8 Olga Brani
            type: 'POST',
7720 e4a6c3b8 Olga Brani
            url: url,
7721 e4a6c3b8 Olga Brani
            dataType: 'json',
7722 e4a6c3b8 Olga Brani
            data: data
7723 e4a6c3b8 Olga Brani
          }).then(function(data) {
7724 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7725 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7726 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7727 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7728 e4a6c3b8 Olga Brani
          });
7729 e4a6c3b8 Olga Brani
        });
7730 e4a6c3b8 Olga Brani
      }
7731 e4a6c3b8 Olga Brani
    });
7732 e4a6c3b8 Olga Brani
    ```
7733 e4a6c3b8 Olga Brani

7734 e4a6c3b8 Olga Brani
    @method createRecord
7735 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7736 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type   the DS.Model class of the record
7737 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7738 e4a6c3b8 Olga Brani
    @return {Promise} promise
7739 e4a6c3b8 Olga Brani
  */
7740 e4a6c3b8 Olga Brani
  createRecord: Ember.required(Function),
7741 e4a6c3b8 Olga Brani
7742 e4a6c3b8 Olga Brani
  /**
7743 e4a6c3b8 Olga Brani
    Implement this method in a subclass to handle the updating of
7744 e4a6c3b8 Olga Brani
    a record.
7745 e4a6c3b8 Olga Brani

7746 e4a6c3b8 Olga Brani
    Serializes the record update and send it to the server.
7747 e4a6c3b8 Olga Brani

7748 e4a6c3b8 Olga Brani
    Example
7749 e4a6c3b8 Olga Brani

7750 e4a6c3b8 Olga Brani
    ```javascript
7751 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7752 e4a6c3b8 Olga Brani
      updateRecord: function(store, type, record) {
7753 e4a6c3b8 Olga Brani
        var data = this.serialize(record, { includeId: true });
7754 e4a6c3b8 Olga Brani
        var id = record.get('id');
7755 e4a6c3b8 Olga Brani
        var url = [type, id].join('/');
7756 e4a6c3b8 Olga Brani

7757 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7758 e4a6c3b8 Olga Brani
          jQuery.ajax({
7759 e4a6c3b8 Olga Brani
            type: 'PUT',
7760 e4a6c3b8 Olga Brani
            url: url,
7761 e4a6c3b8 Olga Brani
            dataType: 'json',
7762 e4a6c3b8 Olga Brani
            data: data
7763 e4a6c3b8 Olga Brani
          }).then(function(data) {
7764 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7765 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7766 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7767 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7768 e4a6c3b8 Olga Brani
          });
7769 e4a6c3b8 Olga Brani
        });
7770 e4a6c3b8 Olga Brani
      }
7771 e4a6c3b8 Olga Brani
    });
7772 e4a6c3b8 Olga Brani
    ```
7773 e4a6c3b8 Olga Brani

7774 e4a6c3b8 Olga Brani
    @method updateRecord
7775 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7776 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type   the DS.Model class of the record
7777 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7778 e4a6c3b8 Olga Brani
    @return {Promise} promise
7779 e4a6c3b8 Olga Brani
  */
7780 e4a6c3b8 Olga Brani
  updateRecord: Ember.required(Function),
7781 e4a6c3b8 Olga Brani
7782 e4a6c3b8 Olga Brani
  /**
7783 e4a6c3b8 Olga Brani
    Implement this method in a subclass to handle the deletion of
7784 e4a6c3b8 Olga Brani
    a record.
7785 e4a6c3b8 Olga Brani

7786 e4a6c3b8 Olga Brani
    Sends a delete request for the record to the server.
7787 e4a6c3b8 Olga Brani

7788 e4a6c3b8 Olga Brani
    Example
7789 e4a6c3b8 Olga Brani

7790 e4a6c3b8 Olga Brani
    ```javascript
7791 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7792 e4a6c3b8 Olga Brani
      deleteRecord: function(store, type, record) {
7793 e4a6c3b8 Olga Brani
        var data = this.serialize(record, { includeId: true });
7794 e4a6c3b8 Olga Brani
        var id = record.get('id');
7795 e4a6c3b8 Olga Brani
        var url = [type, id].join('/');
7796 e4a6c3b8 Olga Brani

7797 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7798 e4a6c3b8 Olga Brani
          jQuery.ajax({
7799 e4a6c3b8 Olga Brani
            type: 'DELETE',
7800 e4a6c3b8 Olga Brani
            url: url,
7801 e4a6c3b8 Olga Brani
            dataType: 'json',
7802 e4a6c3b8 Olga Brani
            data: data
7803 e4a6c3b8 Olga Brani
          }).then(function(data) {
7804 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7805 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7806 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7807 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7808 e4a6c3b8 Olga Brani
          });
7809 e4a6c3b8 Olga Brani
        });
7810 e4a6c3b8 Olga Brani
      }
7811 e4a6c3b8 Olga Brani
    });
7812 e4a6c3b8 Olga Brani
    ```
7813 e4a6c3b8 Olga Brani

7814 e4a6c3b8 Olga Brani
    @method deleteRecord
7815 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7816 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type   the DS.Model class of the record
7817 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7818 e4a6c3b8 Olga Brani
    @return {Promise} promise
7819 e4a6c3b8 Olga Brani
  */
7820 e4a6c3b8 Olga Brani
  deleteRecord: Ember.required(Function),
7821 e4a6c3b8 Olga Brani
7822 e4a6c3b8 Olga Brani
  /**
7823 e4a6c3b8 Olga Brani
    Find multiple records at once.
7824 e4a6c3b8 Olga Brani

7825 e4a6c3b8 Olga Brani
    By default, it loops over the provided ids and calls `find` on each.
7826 e4a6c3b8 Olga Brani
    May be overwritten to improve performance and reduce the number of
7827 e4a6c3b8 Olga Brani
    server requests.
7828 e4a6c3b8 Olga Brani

7829 e4a6c3b8 Olga Brani
    Example
7830 e4a6c3b8 Olga Brani

7831 e4a6c3b8 Olga Brani
    ```javascript
7832 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.Adapter.extend({
7833 e4a6c3b8 Olga Brani
      findMany: function(store, type, ids) {
7834 e4a6c3b8 Olga Brani
        var url = type;
7835 e4a6c3b8 Olga Brani
        return new Ember.RSVP.Promise(function(resolve, reject) {
7836 e4a6c3b8 Olga Brani
          jQuery.getJSON(url, {ids: ids}).then(function(data) {
7837 e4a6c3b8 Olga Brani
            Ember.run(null, resolve, data);
7838 e4a6c3b8 Olga Brani
          }, function(jqXHR) {
7839 e4a6c3b8 Olga Brani
            jqXHR.then = null; // tame jQuery's ill mannered promises
7840 e4a6c3b8 Olga Brani
            Ember.run(null, reject, jqXHR);
7841 e4a6c3b8 Olga Brani
          });
7842 e4a6c3b8 Olga Brani
        });
7843 e4a6c3b8 Olga Brani
      }
7844 e4a6c3b8 Olga Brani
    });
7845 e4a6c3b8 Olga Brani
    ```
7846 e4a6c3b8 Olga Brani

7847 e4a6c3b8 Olga Brani
    @method findMany
7848 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7849 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type   the DS.Model class of the records
7850 e4a6c3b8 Olga Brani
    @param {Array}    ids
7851 e4a6c3b8 Olga Brani
    @return {Promise} promise
7852 e4a6c3b8 Olga Brani
  */
7853 e4a6c3b8 Olga Brani
  findMany: function(store, type, ids) {
7854 e4a6c3b8 Olga Brani
    var promises = map.call(ids, function(id) {
7855 e4a6c3b8 Olga Brani
      return this.find(store, type, id);
7856 e4a6c3b8 Olga Brani
    }, this);
7857 e4a6c3b8 Olga Brani
7858 e4a6c3b8 Olga Brani
    return Ember.RSVP.all(promises);
7859 e4a6c3b8 Olga Brani
  }
7860 e4a6c3b8 Olga Brani
});
7861 e4a6c3b8 Olga Brani
7862 e4a6c3b8 Olga Brani
})();
7863 e4a6c3b8 Olga Brani
7864 e4a6c3b8 Olga Brani
7865 e4a6c3b8 Olga Brani
7866 e4a6c3b8 Olga Brani
(function() {
7867 e4a6c3b8 Olga Brani
/**
7868 e4a6c3b8 Olga Brani
  @module ember-data
7869 e4a6c3b8 Olga Brani
*/
7870 e4a6c3b8 Olga Brani
7871 e4a6c3b8 Olga Brani
var get = Ember.get, fmt = Ember.String.fmt,
7872 e4a6c3b8 Olga Brani
    indexOf = Ember.EnumerableUtils.indexOf;
7873 e4a6c3b8 Olga Brani
7874 e4a6c3b8 Olga Brani
var counter = 0;
7875 e4a6c3b8 Olga Brani
7876 e4a6c3b8 Olga Brani
/**
7877 e4a6c3b8 Olga Brani
  `DS.FixtureAdapter` is an adapter that loads records from memory.
7878 e4a6c3b8 Olga Brani
  Its primarily used for development and testing. You can also use
7879 e4a6c3b8 Olga Brani
  `DS.FixtureAdapter` while working on the API but are not ready to
7880 e4a6c3b8 Olga Brani
  integrate yet. It is a fully functioning adapter. All CRUD methods
7881 e4a6c3b8 Olga Brani
  are implemented. You can also implement query logic that a remote
7882 e4a6c3b8 Olga Brani
  system would do. Its possible to do develop your entire application
7883 e4a6c3b8 Olga Brani
  with `DS.FixtureAdapter`.
7884 e4a6c3b8 Olga Brani

7885 e4a6c3b8 Olga Brani
  For information on how to use the `FixtureAdapter` in your
7886 e4a6c3b8 Olga Brani
  application please see the [FixtureAdapter
7887 e4a6c3b8 Olga Brani
  guide](/guides/models/the-fixture-adapter/).
7888 e4a6c3b8 Olga Brani

7889 e4a6c3b8 Olga Brani
  @class FixtureAdapter
7890 e4a6c3b8 Olga Brani
  @namespace DS
7891 e4a6c3b8 Olga Brani
  @extends DS.Adapter
7892 e4a6c3b8 Olga Brani
*/
7893 e4a6c3b8 Olga Brani
DS.FixtureAdapter = DS.Adapter.extend({
7894 e4a6c3b8 Olga Brani
  // by default, fixtures are already in normalized form
7895 e4a6c3b8 Olga Brani
  serializer: null,
7896 e4a6c3b8 Olga Brani
7897 e4a6c3b8 Olga Brani
  /**
7898 e4a6c3b8 Olga Brani
    If `simulateRemoteResponse` is `true` the `FixtureAdapter` will
7899 e4a6c3b8 Olga Brani
    wait a number of milliseconds before resolving promises with the
7900 e4a6c3b8 Olga Brani
    fixture values. The wait time can be configured via the `latency`
7901 e4a6c3b8 Olga Brani
    property.
7902 e4a6c3b8 Olga Brani

7903 e4a6c3b8 Olga Brani
    @property simulateRemoteResponse
7904 e4a6c3b8 Olga Brani
    @type {Boolean}
7905 e4a6c3b8 Olga Brani
    @default true
7906 e4a6c3b8 Olga Brani
  */
7907 e4a6c3b8 Olga Brani
  simulateRemoteResponse: true,
7908 e4a6c3b8 Olga Brani
7909 e4a6c3b8 Olga Brani
  /**
7910 e4a6c3b8 Olga Brani
    By default the `FixtureAdapter` will simulate a wait of the
7911 e4a6c3b8 Olga Brani
    `latency` milliseconds before resolving promises with the fixture
7912 e4a6c3b8 Olga Brani
    values. This behavior can be turned off via the
7913 e4a6c3b8 Olga Brani
    `simulateRemoteResponse` property.
7914 e4a6c3b8 Olga Brani

7915 e4a6c3b8 Olga Brani
    @property latency
7916 e4a6c3b8 Olga Brani
    @type {Number}
7917 e4a6c3b8 Olga Brani
    @default 50
7918 e4a6c3b8 Olga Brani
  */
7919 e4a6c3b8 Olga Brani
  latency: 50,
7920 e4a6c3b8 Olga Brani
7921 e4a6c3b8 Olga Brani
  /**
7922 e4a6c3b8 Olga Brani
    Implement this method in order to provide data associated with a type
7923 e4a6c3b8 Olga Brani

7924 e4a6c3b8 Olga Brani
    @method fixturesForType
7925 e4a6c3b8 Olga Brani
    @param {Subclass of DS.Model} type
7926 e4a6c3b8 Olga Brani
    @return {Array}
7927 e4a6c3b8 Olga Brani
  */
7928 e4a6c3b8 Olga Brani
  fixturesForType: function(type) {
7929 e4a6c3b8 Olga Brani
    if (type.FIXTURES) {
7930 e4a6c3b8 Olga Brani
      var fixtures = Ember.A(type.FIXTURES);
7931 e4a6c3b8 Olga Brani
      return fixtures.map(function(fixture){
7932 e4a6c3b8 Olga Brani
        var fixtureIdType = typeof fixture.id;
7933 e4a6c3b8 Olga Brani
        if(fixtureIdType !== "number" && fixtureIdType !== "string"){
7934 e4a6c3b8 Olga Brani
          throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture]));
7935 e4a6c3b8 Olga Brani
        }
7936 e4a6c3b8 Olga Brani
        fixture.id = fixture.id + '';
7937 e4a6c3b8 Olga Brani
        return fixture;
7938 e4a6c3b8 Olga Brani
      });
7939 e4a6c3b8 Olga Brani
    }
7940 e4a6c3b8 Olga Brani
    return null;
7941 e4a6c3b8 Olga Brani
  },
7942 e4a6c3b8 Olga Brani
7943 e4a6c3b8 Olga Brani
  /**
7944 e4a6c3b8 Olga Brani
    Implement this method in order to query fixtures data
7945 e4a6c3b8 Olga Brani

7946 e4a6c3b8 Olga Brani
    @method queryFixtures
7947 e4a6c3b8 Olga Brani
    @param {Array} fixture
7948 e4a6c3b8 Olga Brani
    @param {Object} query
7949 e4a6c3b8 Olga Brani
    @param {Subclass of DS.Model} type
7950 e4a6c3b8 Olga Brani
    @return {Promise|Array}
7951 e4a6c3b8 Olga Brani
  */
7952 e4a6c3b8 Olga Brani
  queryFixtures: function(fixtures, query, type) {
7953 e4a6c3b8 Olga Brani
    Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
7954 e4a6c3b8 Olga Brani
  },
7955 e4a6c3b8 Olga Brani
7956 e4a6c3b8 Olga Brani
  /**
7957 e4a6c3b8 Olga Brani
    @method updateFixtures
7958 e4a6c3b8 Olga Brani
    @param {Subclass of DS.Model} type
7959 e4a6c3b8 Olga Brani
    @param {Array} fixture
7960 e4a6c3b8 Olga Brani
  */
7961 e4a6c3b8 Olga Brani
  updateFixtures: function(type, fixture) {
7962 e4a6c3b8 Olga Brani
    if(!type.FIXTURES) {
7963 e4a6c3b8 Olga Brani
      type.FIXTURES = [];
7964 e4a6c3b8 Olga Brani
    }
7965 e4a6c3b8 Olga Brani
7966 e4a6c3b8 Olga Brani
    var fixtures = type.FIXTURES;
7967 e4a6c3b8 Olga Brani
7968 e4a6c3b8 Olga Brani
    this.deleteLoadedFixture(type, fixture);
7969 e4a6c3b8 Olga Brani
7970 e4a6c3b8 Olga Brani
    fixtures.push(fixture);
7971 e4a6c3b8 Olga Brani
  },
7972 e4a6c3b8 Olga Brani
7973 e4a6c3b8 Olga Brani
  /**
7974 e4a6c3b8 Olga Brani
    Implement this method in order to provide json for CRUD methods
7975 e4a6c3b8 Olga Brani

7976 e4a6c3b8 Olga Brani
    @method mockJSON
7977 e4a6c3b8 Olga Brani
    @param {Subclass of DS.Model} type
7978 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7979 e4a6c3b8 Olga Brani
  */
7980 e4a6c3b8 Olga Brani
  mockJSON: function(store, type, record) {
7981 e4a6c3b8 Olga Brani
    return store.serializerFor(type).serialize(record, { includeId: true });
7982 e4a6c3b8 Olga Brani
  },
7983 e4a6c3b8 Olga Brani
7984 e4a6c3b8 Olga Brani
  /**
7985 e4a6c3b8 Olga Brani
    @method generateIdForRecord
7986 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7987 e4a6c3b8 Olga Brani
    @param {DS.Model} record
7988 e4a6c3b8 Olga Brani
    @return {String} id
7989 e4a6c3b8 Olga Brani
  */
7990 e4a6c3b8 Olga Brani
  generateIdForRecord: function(store) {
7991 e4a6c3b8 Olga Brani
    return "fixture-" + counter++;
7992 e4a6c3b8 Olga Brani
  },
7993 e4a6c3b8 Olga Brani
7994 e4a6c3b8 Olga Brani
  /**
7995 e4a6c3b8 Olga Brani
    @method find
7996 e4a6c3b8 Olga Brani
    @param {DS.Store} store
7997 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
7998 e4a6c3b8 Olga Brani
    @param {String} id
7999 e4a6c3b8 Olga Brani
    @return {Promise} promise
8000 e4a6c3b8 Olga Brani
  */
8001 e4a6c3b8 Olga Brani
  find: function(store, type, id) {
8002 e4a6c3b8 Olga Brani
    var fixtures = this.fixturesForType(type),
8003 e4a6c3b8 Olga Brani
        fixture;
8004 e4a6c3b8 Olga Brani
8005 e4a6c3b8 Olga Brani
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8006 e4a6c3b8 Olga Brani
8007 e4a6c3b8 Olga Brani
    if (fixtures) {
8008 e4a6c3b8 Olga Brani
      fixture = Ember.A(fixtures).findProperty('id', id);
8009 e4a6c3b8 Olga Brani
    }
8010 e4a6c3b8 Olga Brani
8011 e4a6c3b8 Olga Brani
    if (fixture) {
8012 e4a6c3b8 Olga Brani
      return this.simulateRemoteCall(function() {
8013 e4a6c3b8 Olga Brani
        return fixture;
8014 e4a6c3b8 Olga Brani
      }, this);
8015 e4a6c3b8 Olga Brani
    }
8016 e4a6c3b8 Olga Brani
  },
8017 e4a6c3b8 Olga Brani
8018 e4a6c3b8 Olga Brani
  /**
8019 e4a6c3b8 Olga Brani
    @method findMany
8020 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8021 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8022 e4a6c3b8 Olga Brani
    @param {Array} ids
8023 e4a6c3b8 Olga Brani
    @return {Promise} promise
8024 e4a6c3b8 Olga Brani
  */
8025 e4a6c3b8 Olga Brani
  findMany: function(store, type, ids) {
8026 e4a6c3b8 Olga Brani
    var fixtures = this.fixturesForType(type);
8027 e4a6c3b8 Olga Brani
8028 e4a6c3b8 Olga Brani
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8029 e4a6c3b8 Olga Brani
8030 e4a6c3b8 Olga Brani
    if (fixtures) {
8031 e4a6c3b8 Olga Brani
      fixtures = fixtures.filter(function(item) {
8032 e4a6c3b8 Olga Brani
        return indexOf(ids, item.id) !== -1;
8033 e4a6c3b8 Olga Brani
      });
8034 e4a6c3b8 Olga Brani
    }
8035 e4a6c3b8 Olga Brani
8036 e4a6c3b8 Olga Brani
    if (fixtures) {
8037 e4a6c3b8 Olga Brani
      return this.simulateRemoteCall(function() {
8038 e4a6c3b8 Olga Brani
        return fixtures;
8039 e4a6c3b8 Olga Brani
      }, this);
8040 e4a6c3b8 Olga Brani
    }
8041 e4a6c3b8 Olga Brani
  },
8042 e4a6c3b8 Olga Brani
8043 e4a6c3b8 Olga Brani
  /**
8044 e4a6c3b8 Olga Brani
    @private
8045 e4a6c3b8 Olga Brani
    @method findAll
8046 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8047 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8048 e4a6c3b8 Olga Brani
    @param {String} sinceToken
8049 e4a6c3b8 Olga Brani
    @return {Promise} promise
8050 e4a6c3b8 Olga Brani
  */
8051 e4a6c3b8 Olga Brani
  findAll: function(store, type) {
8052 e4a6c3b8 Olga Brani
    var fixtures = this.fixturesForType(type);
8053 e4a6c3b8 Olga Brani
8054 e4a6c3b8 Olga Brani
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8055 e4a6c3b8 Olga Brani
8056 e4a6c3b8 Olga Brani
    return this.simulateRemoteCall(function() {
8057 e4a6c3b8 Olga Brani
      return fixtures;
8058 e4a6c3b8 Olga Brani
    }, this);
8059 e4a6c3b8 Olga Brani
  },
8060 e4a6c3b8 Olga Brani
8061 e4a6c3b8 Olga Brani
  /**
8062 e4a6c3b8 Olga Brani
    @private
8063 e4a6c3b8 Olga Brani
    @method findQuery
8064 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8065 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8066 e4a6c3b8 Olga Brani
    @param {Object} query
8067 e4a6c3b8 Olga Brani
    @param {DS.AdapterPopulatedRecordArray} recordArray
8068 e4a6c3b8 Olga Brani
    @return {Promise} promise
8069 e4a6c3b8 Olga Brani
  */
8070 e4a6c3b8 Olga Brani
  findQuery: function(store, type, query, array) {
8071 e4a6c3b8 Olga Brani
    var fixtures = this.fixturesForType(type);
8072 e4a6c3b8 Olga Brani
8073 e4a6c3b8 Olga Brani
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8074 e4a6c3b8 Olga Brani
8075 e4a6c3b8 Olga Brani
    fixtures = this.queryFixtures(fixtures, query, type);
8076 e4a6c3b8 Olga Brani
8077 e4a6c3b8 Olga Brani
    if (fixtures) {
8078 e4a6c3b8 Olga Brani
      return this.simulateRemoteCall(function() {
8079 e4a6c3b8 Olga Brani
        return fixtures;
8080 e4a6c3b8 Olga Brani
      }, this);
8081 e4a6c3b8 Olga Brani
    }
8082 e4a6c3b8 Olga Brani
  },
8083 e4a6c3b8 Olga Brani
8084 e4a6c3b8 Olga Brani
  /**
8085 e4a6c3b8 Olga Brani
    @method createRecord
8086 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8087 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8088 e4a6c3b8 Olga Brani
    @param {DS.Model} record
8089 e4a6c3b8 Olga Brani
    @return {Promise} promise
8090 e4a6c3b8 Olga Brani
  */
8091 e4a6c3b8 Olga Brani
  createRecord: function(store, type, record) {
8092 e4a6c3b8 Olga Brani
    var fixture = this.mockJSON(store, type, record);
8093 e4a6c3b8 Olga Brani
8094 e4a6c3b8 Olga Brani
    this.updateFixtures(type, fixture);
8095 e4a6c3b8 Olga Brani
8096 e4a6c3b8 Olga Brani
    return this.simulateRemoteCall(function() {
8097 e4a6c3b8 Olga Brani
      return fixture;
8098 e4a6c3b8 Olga Brani
    }, this);
8099 e4a6c3b8 Olga Brani
  },
8100 e4a6c3b8 Olga Brani
8101 e4a6c3b8 Olga Brani
  /**
8102 e4a6c3b8 Olga Brani
    @method updateRecord
8103 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8104 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8105 e4a6c3b8 Olga Brani
    @param {DS.Model} record
8106 e4a6c3b8 Olga Brani
    @return {Promise} promise
8107 e4a6c3b8 Olga Brani
  */
8108 e4a6c3b8 Olga Brani
  updateRecord: function(store, type, record) {
8109 e4a6c3b8 Olga Brani
    var fixture = this.mockJSON(store, type, record);
8110 e4a6c3b8 Olga Brani
8111 e4a6c3b8 Olga Brani
    this.updateFixtures(type, fixture);
8112 e4a6c3b8 Olga Brani
8113 e4a6c3b8 Olga Brani
    return this.simulateRemoteCall(function() {
8114 e4a6c3b8 Olga Brani
      return fixture;
8115 e4a6c3b8 Olga Brani
    }, this);
8116 e4a6c3b8 Olga Brani
  },
8117 e4a6c3b8 Olga Brani
8118 e4a6c3b8 Olga Brani
  /**
8119 e4a6c3b8 Olga Brani
    @method deleteRecord
8120 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8121 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8122 e4a6c3b8 Olga Brani
    @param {DS.Model} record
8123 e4a6c3b8 Olga Brani
    @return {Promise} promise
8124 e4a6c3b8 Olga Brani
  */
8125 e4a6c3b8 Olga Brani
  deleteRecord: function(store, type, record) {
8126 e4a6c3b8 Olga Brani
    var fixture = this.mockJSON(store, type, record);
8127 e4a6c3b8 Olga Brani
8128 e4a6c3b8 Olga Brani
    this.deleteLoadedFixture(type, fixture);
8129 e4a6c3b8 Olga Brani
8130 e4a6c3b8 Olga Brani
    return this.simulateRemoteCall(function() {
8131 e4a6c3b8 Olga Brani
      // no payload in a deletion
8132 e4a6c3b8 Olga Brani
      return null;
8133 e4a6c3b8 Olga Brani
    });
8134 e4a6c3b8 Olga Brani
  },
8135 e4a6c3b8 Olga Brani
8136 e4a6c3b8 Olga Brani
  /*
8137 e4a6c3b8 Olga Brani
    @method deleteLoadedFixture
8138 e4a6c3b8 Olga Brani
    @private
8139 e4a6c3b8 Olga Brani
    @param type
8140 e4a6c3b8 Olga Brani
    @param record
8141 e4a6c3b8 Olga Brani
  */
8142 e4a6c3b8 Olga Brani
  deleteLoadedFixture: function(type, record) {
8143 e4a6c3b8 Olga Brani
    var existingFixture = this.findExistingFixture(type, record);
8144 e4a6c3b8 Olga Brani
8145 e4a6c3b8 Olga Brani
    if(existingFixture) {
8146 e4a6c3b8 Olga Brani
      var index = indexOf(type.FIXTURES, existingFixture);
8147 e4a6c3b8 Olga Brani
      type.FIXTURES.splice(index, 1);
8148 e4a6c3b8 Olga Brani
      return true;
8149 e4a6c3b8 Olga Brani
    }
8150 e4a6c3b8 Olga Brani
  },
8151 e4a6c3b8 Olga Brani
8152 e4a6c3b8 Olga Brani
  /*
8153 e4a6c3b8 Olga Brani
    @method findExistingFixture
8154 e4a6c3b8 Olga Brani
    @private
8155 e4a6c3b8 Olga Brani
    @param type
8156 e4a6c3b8 Olga Brani
    @param record
8157 e4a6c3b8 Olga Brani
  */
8158 e4a6c3b8 Olga Brani
  findExistingFixture: function(type, record) {
8159 e4a6c3b8 Olga Brani
    var fixtures = this.fixturesForType(type);
8160 e4a6c3b8 Olga Brani
    var id = get(record, 'id');
8161 e4a6c3b8 Olga Brani
8162 e4a6c3b8 Olga Brani
    return this.findFixtureById(fixtures, id);
8163 e4a6c3b8 Olga Brani
  },
8164 e4a6c3b8 Olga Brani
8165 e4a6c3b8 Olga Brani
  /*
8166 e4a6c3b8 Olga Brani
    @method findFixtureById
8167 e4a6c3b8 Olga Brani
    @private
8168 e4a6c3b8 Olga Brani
    @param fixtures
8169 e4a6c3b8 Olga Brani
    @param id
8170 e4a6c3b8 Olga Brani
  */
8171 e4a6c3b8 Olga Brani
  findFixtureById: function(fixtures, id) {
8172 e4a6c3b8 Olga Brani
    return Ember.A(fixtures).find(function(r) {
8173 e4a6c3b8 Olga Brani
      if(''+get(r, 'id') === ''+id) {
8174 e4a6c3b8 Olga Brani
        return true;
8175 e4a6c3b8 Olga Brani
      } else {
8176 e4a6c3b8 Olga Brani
        return false;
8177 e4a6c3b8 Olga Brani
      }
8178 e4a6c3b8 Olga Brani
    });
8179 e4a6c3b8 Olga Brani
  },
8180 e4a6c3b8 Olga Brani
8181 e4a6c3b8 Olga Brani
  /*
8182 e4a6c3b8 Olga Brani
    @method simulateRemoteCall
8183 e4a6c3b8 Olga Brani
    @private
8184 e4a6c3b8 Olga Brani
    @param callback
8185 e4a6c3b8 Olga Brani
    @param context
8186 e4a6c3b8 Olga Brani
  */
8187 e4a6c3b8 Olga Brani
  simulateRemoteCall: function(callback, context) {
8188 e4a6c3b8 Olga Brani
    var adapter = this;
8189 e4a6c3b8 Olga Brani
8190 e4a6c3b8 Olga Brani
    return new Ember.RSVP.Promise(function(resolve) {
8191 e4a6c3b8 Olga Brani
      if (get(adapter, 'simulateRemoteResponse')) {
8192 e4a6c3b8 Olga Brani
        // Schedule with setTimeout
8193 e4a6c3b8 Olga Brani
        Ember.run.later(function() {
8194 e4a6c3b8 Olga Brani
          resolve(callback.call(context));
8195 e4a6c3b8 Olga Brani
        }, get(adapter, 'latency'));
8196 e4a6c3b8 Olga Brani
      } else {
8197 e4a6c3b8 Olga Brani
        // Asynchronous, but at the of the runloop with zero latency
8198 e4a6c3b8 Olga Brani
        Ember.run.schedule('actions', null, function() {
8199 e4a6c3b8 Olga Brani
          resolve(callback.call(context));
8200 e4a6c3b8 Olga Brani
        });
8201 e4a6c3b8 Olga Brani
      }
8202 e4a6c3b8 Olga Brani
    }, "DS: FixtureAdapter#simulateRemoteCall");
8203 e4a6c3b8 Olga Brani
  }
8204 e4a6c3b8 Olga Brani
});
8205 e4a6c3b8 Olga Brani
8206 e4a6c3b8 Olga Brani
})();
8207 e4a6c3b8 Olga Brani
8208 e4a6c3b8 Olga Brani
8209 e4a6c3b8 Olga Brani
8210 e4a6c3b8 Olga Brani
(function() {
8211 e4a6c3b8 Olga Brani
/**
8212 e4a6c3b8 Olga Brani
  @module ember-data
8213 e4a6c3b8 Olga Brani
*/
8214 e4a6c3b8 Olga Brani
8215 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
8216 e4a6c3b8 Olga Brani
var forEach = Ember.ArrayPolyfills.forEach;
8217 e4a6c3b8 Olga Brani
var map = Ember.ArrayPolyfills.map;
8218 e4a6c3b8 Olga Brani
8219 e4a6c3b8 Olga Brani
function coerceId(id) {
8220 e4a6c3b8 Olga Brani
  return id == null ? null : id+'';
8221 e4a6c3b8 Olga Brani
}
8222 e4a6c3b8 Olga Brani
8223 e4a6c3b8 Olga Brani
/**
8224 e4a6c3b8 Olga Brani
  Normally, applications will use the `RESTSerializer` by implementing
8225 e4a6c3b8 Olga Brani
  the `normalize` method and individual normalizations under
8226 e4a6c3b8 Olga Brani
  `normalizeHash`.
8227 e4a6c3b8 Olga Brani

8228 e4a6c3b8 Olga Brani
  This allows you to do whatever kind of munging you need, and is
8229 e4a6c3b8 Olga Brani
  especially useful if your server is inconsistent and you need to
8230 e4a6c3b8 Olga Brani
  do munging differently for many different kinds of responses.
8231 e4a6c3b8 Olga Brani

8232 e4a6c3b8 Olga Brani
  See the `normalize` documentation for more information.
8233 e4a6c3b8 Olga Brani

8234 e4a6c3b8 Olga Brani
  ## Across the Board Normalization
8235 e4a6c3b8 Olga Brani

8236 e4a6c3b8 Olga Brani
  There are also a number of hooks that you might find useful to defined
8237 e4a6c3b8 Olga Brani
  across-the-board rules for your payload. These rules will be useful
8238 e4a6c3b8 Olga Brani
  if your server is consistent, or if you're building an adapter for
8239 e4a6c3b8 Olga Brani
  an infrastructure service, like Parse, and want to encode service
8240 e4a6c3b8 Olga Brani
  conventions.
8241 e4a6c3b8 Olga Brani

8242 e4a6c3b8 Olga Brani
  For example, if all of your keys are underscored and all-caps, but
8243 e4a6c3b8 Olga Brani
  otherwise consistent with the names you use in your models, you
8244 e4a6c3b8 Olga Brani
  can implement across-the-board rules for how to convert an attribute
8245 e4a6c3b8 Olga Brani
  name in your model to a key in your JSON.
8246 e4a6c3b8 Olga Brani

8247 e4a6c3b8 Olga Brani
  ```js
8248 e4a6c3b8 Olga Brani
  App.ApplicationSerializer = DS.RESTSerializer.extend({
8249 e4a6c3b8 Olga Brani
    keyForAttribute: function(attr) {
8250 e4a6c3b8 Olga Brani
      return Ember.String.underscore(attr).toUpperCase();
8251 e4a6c3b8 Olga Brani
    }
8252 e4a6c3b8 Olga Brani
  });
8253 e4a6c3b8 Olga Brani
  ```
8254 e4a6c3b8 Olga Brani

8255 e4a6c3b8 Olga Brani
  You can also implement `keyForRelationship`, which takes the name
8256 e4a6c3b8 Olga Brani
  of the relationship as the first parameter, and the kind of
8257 e4a6c3b8 Olga Brani
  relationship (`hasMany` or `belongsTo`) as the second parameter.
8258 e4a6c3b8 Olga Brani

8259 e4a6c3b8 Olga Brani
  @class RESTSerializer
8260 e4a6c3b8 Olga Brani
  @namespace DS
8261 e4a6c3b8 Olga Brani
  @extends DS.JSONSerializer
8262 e4a6c3b8 Olga Brani
*/
8263 e4a6c3b8 Olga Brani
DS.RESTSerializer = DS.JSONSerializer.extend({
8264 e4a6c3b8 Olga Brani
  /**
8265 e4a6c3b8 Olga Brani
    If you want to do normalizations specific to some part of the payload, you
8266 e4a6c3b8 Olga Brani
    can specify those under `normalizeHash`.
8267 e4a6c3b8 Olga Brani

8268 e4a6c3b8 Olga Brani
    For example, given the following json where the the `IDs` under
8269 e4a6c3b8 Olga Brani
    `"comments"` are provided as `_id` instead of `id`.
8270 e4a6c3b8 Olga Brani

8271 e4a6c3b8 Olga Brani
    ```javascript
8272 e4a6c3b8 Olga Brani
    {
8273 e4a6c3b8 Olga Brani
      "post": {
8274 e4a6c3b8 Olga Brani
        "id": 1,
8275 e4a6c3b8 Olga Brani
        "title": "Rails is omakase",
8276 e4a6c3b8 Olga Brani
        "comments": [ 1, 2 ]
8277 e4a6c3b8 Olga Brani
      },
8278 e4a6c3b8 Olga Brani
      "comments": [{
8279 e4a6c3b8 Olga Brani
        "_id": 1,
8280 e4a6c3b8 Olga Brani
        "body": "FIRST"
8281 e4a6c3b8 Olga Brani
      }, {
8282 e4a6c3b8 Olga Brani
        "_id": 2,
8283 e4a6c3b8 Olga Brani
        "body": "Rails is unagi"
8284 e4a6c3b8 Olga Brani
      }]
8285 e4a6c3b8 Olga Brani
    }
8286 e4a6c3b8 Olga Brani
    ```
8287 e4a6c3b8 Olga Brani

8288 e4a6c3b8 Olga Brani
    You use `normalizeHash` to normalize just the comments:
8289 e4a6c3b8 Olga Brani

8290 e4a6c3b8 Olga Brani
    ```javascript
8291 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.RESTSerializer.extend({
8292 e4a6c3b8 Olga Brani
      normalizeHash: {
8293 e4a6c3b8 Olga Brani
        comments: function(hash) {
8294 e4a6c3b8 Olga Brani
          hash.id = hash._id;
8295 e4a6c3b8 Olga Brani
          delete hash._id;
8296 e4a6c3b8 Olga Brani
          return hash;
8297 e4a6c3b8 Olga Brani
        }
8298 e4a6c3b8 Olga Brani
      }
8299 e4a6c3b8 Olga Brani
    });
8300 e4a6c3b8 Olga Brani
    ```
8301 e4a6c3b8 Olga Brani

8302 e4a6c3b8 Olga Brani
    The key under `normalizeHash` is usually just the original key
8303 e4a6c3b8 Olga Brani
    that was in the original payload. However, key names will be
8304 e4a6c3b8 Olga Brani
    impacted by any modifications done in the `normalizePayload`
8305 e4a6c3b8 Olga Brani
    method. The `DS.RESTSerializer`'s default implemention makes no
8306 e4a6c3b8 Olga Brani
    changes to the payload keys.
8307 e4a6c3b8 Olga Brani

8308 e4a6c3b8 Olga Brani
    @property normalizeHash
8309 e4a6c3b8 Olga Brani
    @type {Object}
8310 e4a6c3b8 Olga Brani
    @default undefined
8311 e4a6c3b8 Olga Brani
  */
8312 e4a6c3b8 Olga Brani
8313 e4a6c3b8 Olga Brani
  /**
8314 e4a6c3b8 Olga Brani
    Normalizes a part of the JSON payload returned by
8315 e4a6c3b8 Olga Brani
    the server. You should override this method, munge the hash
8316 e4a6c3b8 Olga Brani
    and call super if you have generic normalization to do.
8317 e4a6c3b8 Olga Brani

8318 e4a6c3b8 Olga Brani
    It takes the type of the record that is being normalized
8319 e4a6c3b8 Olga Brani
    (as a DS.Model class), the property where the hash was
8320 e4a6c3b8 Olga Brani
    originally found, and the hash to normalize.
8321 e4a6c3b8 Olga Brani

8322 e4a6c3b8 Olga Brani
    For example, if you have a payload that looks like this:
8323 e4a6c3b8 Olga Brani

8324 e4a6c3b8 Olga Brani
    ```js
8325 e4a6c3b8 Olga Brani
    {
8326 e4a6c3b8 Olga Brani
      "post": {
8327 e4a6c3b8 Olga Brani
        "id": 1,
8328 e4a6c3b8 Olga Brani
        "title": "Rails is omakase",
8329 e4a6c3b8 Olga Brani
        "comments": [ 1, 2 ]
8330 e4a6c3b8 Olga Brani
      },
8331 e4a6c3b8 Olga Brani
      "comments": [{
8332 e4a6c3b8 Olga Brani
        "id": 1,
8333 e4a6c3b8 Olga Brani
        "body": "FIRST"
8334 e4a6c3b8 Olga Brani
      }, {
8335 e4a6c3b8 Olga Brani
        "id": 2,
8336 e4a6c3b8 Olga Brani
        "body": "Rails is unagi"
8337 e4a6c3b8 Olga Brani
      }]
8338 e4a6c3b8 Olga Brani
    }
8339 e4a6c3b8 Olga Brani
    ```
8340 e4a6c3b8 Olga Brani

8341 e4a6c3b8 Olga Brani
    The `normalize` method will be called three times:
8342 e4a6c3b8 Olga Brani

8343 e4a6c3b8 Olga Brani
    * With `App.Post`, `"posts"` and `{ id: 1, title: "Rails is omakase", ... }`
8344 e4a6c3b8 Olga Brani
    * With `App.Comment`, `"comments"` and `{ id: 1, body: "FIRST" }`
8345 e4a6c3b8 Olga Brani
    * With `App.Comment`, `"comments"` and `{ id: 2, body: "Rails is unagi" }`
8346 e4a6c3b8 Olga Brani

8347 e4a6c3b8 Olga Brani
    You can use this method, for example, to normalize underscored keys to camelized
8348 e4a6c3b8 Olga Brani
    or other general-purpose normalizations.
8349 e4a6c3b8 Olga Brani

8350 e4a6c3b8 Olga Brani
    If you want to do normalizations specific to some part of the payload, you
8351 e4a6c3b8 Olga Brani
    can specify those under `normalizeHash`.
8352 e4a6c3b8 Olga Brani

8353 e4a6c3b8 Olga Brani
    For example, if the `IDs` under `"comments"` are provided as `_id` instead of
8354 e4a6c3b8 Olga Brani
    `id`, you can specify how to normalize just the comments:
8355 e4a6c3b8 Olga Brani

8356 e4a6c3b8 Olga Brani
    ```js
8357 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.RESTSerializer.extend({
8358 e4a6c3b8 Olga Brani
      normalizeHash: {
8359 e4a6c3b8 Olga Brani
        comments: function(hash) {
8360 e4a6c3b8 Olga Brani
          hash.id = hash._id;
8361 e4a6c3b8 Olga Brani
          delete hash._id;
8362 e4a6c3b8 Olga Brani
          return hash;
8363 e4a6c3b8 Olga Brani
        }
8364 e4a6c3b8 Olga Brani
      }
8365 e4a6c3b8 Olga Brani
    });
8366 e4a6c3b8 Olga Brani
    ```
8367 e4a6c3b8 Olga Brani

8368 e4a6c3b8 Olga Brani
    The key under `normalizeHash` is just the original key that was in the original
8369 e4a6c3b8 Olga Brani
    payload.
8370 e4a6c3b8 Olga Brani

8371 e4a6c3b8 Olga Brani
    @method normalize
8372 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8373 e4a6c3b8 Olga Brani
    @param {Object} hash
8374 e4a6c3b8 Olga Brani
    @param {String} prop
8375 e4a6c3b8 Olga Brani
    @returns {Object}
8376 e4a6c3b8 Olga Brani
  */
8377 e4a6c3b8 Olga Brani
  normalize: function(type, hash, prop) {
8378 e4a6c3b8 Olga Brani
    this.normalizeId(hash);
8379 e4a6c3b8 Olga Brani
    this.normalizeAttributes(type, hash);
8380 e4a6c3b8 Olga Brani
    this.normalizeRelationships(type, hash);
8381 e4a6c3b8 Olga Brani
8382 e4a6c3b8 Olga Brani
    this.normalizeUsingDeclaredMapping(type, hash);
8383 e4a6c3b8 Olga Brani
8384 e4a6c3b8 Olga Brani
    if (this.normalizeHash && this.normalizeHash[prop]) {
8385 e4a6c3b8 Olga Brani
      this.normalizeHash[prop](hash);
8386 e4a6c3b8 Olga Brani
    }
8387 e4a6c3b8 Olga Brani
8388 e4a6c3b8 Olga Brani
    return this._super(type, hash, prop);
8389 e4a6c3b8 Olga Brani
  },
8390 e4a6c3b8 Olga Brani
8391 e4a6c3b8 Olga Brani
  /**
8392 e4a6c3b8 Olga Brani
    You can use this method to normalize all payloads, regardless of whether they
8393 e4a6c3b8 Olga Brani
    represent single records or an array.
8394 e4a6c3b8 Olga Brani

8395 e4a6c3b8 Olga Brani
    For example, you might want to remove some extraneous data from the payload:
8396 e4a6c3b8 Olga Brani

8397 e4a6c3b8 Olga Brani
    ```js
8398 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.RESTSerializer.extend({
8399 e4a6c3b8 Olga Brani
      normalizePayload: function(type, payload) {
8400 e4a6c3b8 Olga Brani
        delete payload.version;
8401 e4a6c3b8 Olga Brani
        delete payload.status;
8402 e4a6c3b8 Olga Brani
        return payload;
8403 e4a6c3b8 Olga Brani
      }
8404 e4a6c3b8 Olga Brani
    });
8405 e4a6c3b8 Olga Brani
    ```
8406 e4a6c3b8 Olga Brani

8407 e4a6c3b8 Olga Brani
    @method normalizePayload
8408 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8409 e4a6c3b8 Olga Brani
    @param {Object} hash
8410 e4a6c3b8 Olga Brani
    @returns {Object} the normalized payload
8411 e4a6c3b8 Olga Brani
  */
8412 e4a6c3b8 Olga Brani
  normalizePayload: function(type, payload) {
8413 e4a6c3b8 Olga Brani
    return payload;
8414 e4a6c3b8 Olga Brani
  },
8415 e4a6c3b8 Olga Brani
8416 e4a6c3b8 Olga Brani
  /**
8417 e4a6c3b8 Olga Brani
    @method normalizeId
8418 e4a6c3b8 Olga Brani
    @private
8419 e4a6c3b8 Olga Brani
  */
8420 e4a6c3b8 Olga Brani
  normalizeId: function(hash) {
8421 e4a6c3b8 Olga Brani
    var primaryKey = get(this, 'primaryKey');
8422 e4a6c3b8 Olga Brani
8423 e4a6c3b8 Olga Brani
    if (primaryKey === 'id') { return; }
8424 e4a6c3b8 Olga Brani
8425 e4a6c3b8 Olga Brani
    hash.id = hash[primaryKey];
8426 e4a6c3b8 Olga Brani
    delete hash[primaryKey];
8427 e4a6c3b8 Olga Brani
  },
8428 e4a6c3b8 Olga Brani
8429 e4a6c3b8 Olga Brani
  /**
8430 e4a6c3b8 Olga Brani
    @method normalizeUsingDeclaredMapping
8431 e4a6c3b8 Olga Brani
    @private
8432 e4a6c3b8 Olga Brani
  */
8433 e4a6c3b8 Olga Brani
  normalizeUsingDeclaredMapping: function(type, hash) {
8434 e4a6c3b8 Olga Brani
    var attrs = get(this, 'attrs'), payloadKey, key;
8435 e4a6c3b8 Olga Brani
8436 e4a6c3b8 Olga Brani
    if (attrs) {
8437 e4a6c3b8 Olga Brani
      for (key in attrs) {
8438 e4a6c3b8 Olga Brani
        payloadKey = attrs[key];
8439 e4a6c3b8 Olga Brani
        if (payloadKey && payloadKey.key) {
8440 e4a6c3b8 Olga Brani
          payloadKey = payloadKey.key;
8441 e4a6c3b8 Olga Brani
        }
8442 e4a6c3b8 Olga Brani
        if (typeof payloadKey === 'string') {
8443 e4a6c3b8 Olga Brani
          hash[key] = hash[payloadKey];
8444 e4a6c3b8 Olga Brani
          delete hash[payloadKey];
8445 e4a6c3b8 Olga Brani
        }
8446 e4a6c3b8 Olga Brani
      }
8447 e4a6c3b8 Olga Brani
    }
8448 e4a6c3b8 Olga Brani
  },
8449 e4a6c3b8 Olga Brani
8450 e4a6c3b8 Olga Brani
  /**
8451 e4a6c3b8 Olga Brani
    @method normalizeAttributes
8452 e4a6c3b8 Olga Brani
    @private
8453 e4a6c3b8 Olga Brani
  */
8454 e4a6c3b8 Olga Brani
  normalizeAttributes: function(type, hash) {
8455 e4a6c3b8 Olga Brani
    var payloadKey, key;
8456 e4a6c3b8 Olga Brani
8457 e4a6c3b8 Olga Brani
    if (this.keyForAttribute) {
8458 e4a6c3b8 Olga Brani
      type.eachAttribute(function(key) {
8459 e4a6c3b8 Olga Brani
        payloadKey = this.keyForAttribute(key);
8460 e4a6c3b8 Olga Brani
        if (key === payloadKey) { return; }
8461 e4a6c3b8 Olga Brani
8462 e4a6c3b8 Olga Brani
        hash[key] = hash[payloadKey];
8463 e4a6c3b8 Olga Brani
        delete hash[payloadKey];
8464 e4a6c3b8 Olga Brani
      }, this);
8465 e4a6c3b8 Olga Brani
    }
8466 e4a6c3b8 Olga Brani
  },
8467 e4a6c3b8 Olga Brani
8468 e4a6c3b8 Olga Brani
  /**
8469 e4a6c3b8 Olga Brani
    @method normalizeRelationships
8470 e4a6c3b8 Olga Brani
    @private
8471 e4a6c3b8 Olga Brani
  */
8472 e4a6c3b8 Olga Brani
  normalizeRelationships: function(type, hash) {
8473 e4a6c3b8 Olga Brani
    var payloadKey, key;
8474 e4a6c3b8 Olga Brani
8475 e4a6c3b8 Olga Brani
    if (this.keyForRelationship) {
8476 e4a6c3b8 Olga Brani
      type.eachRelationship(function(key, relationship) {
8477 e4a6c3b8 Olga Brani
        payloadKey = this.keyForRelationship(key, relationship.kind);
8478 e4a6c3b8 Olga Brani
        if (key === payloadKey) { return; }
8479 e4a6c3b8 Olga Brani
8480 e4a6c3b8 Olga Brani
        hash[key] = hash[payloadKey];
8481 e4a6c3b8 Olga Brani
        delete hash[payloadKey];
8482 e4a6c3b8 Olga Brani
      }, this);
8483 e4a6c3b8 Olga Brani
    }
8484 e4a6c3b8 Olga Brani
  },
8485 e4a6c3b8 Olga Brani
8486 e4a6c3b8 Olga Brani
  /**
8487 e4a6c3b8 Olga Brani
    Called when the server has returned a payload representing
8488 e4a6c3b8 Olga Brani
    a single record, such as in response to a `find` or `save`.
8489 e4a6c3b8 Olga Brani

8490 e4a6c3b8 Olga Brani
    It is your opportunity to clean up the server's response into the normalized
8491 e4a6c3b8 Olga Brani
    form expected by Ember Data.
8492 e4a6c3b8 Olga Brani

8493 e4a6c3b8 Olga Brani
    If you want, you can just restructure the top-level of your payload, and
8494 e4a6c3b8 Olga Brani
    do more fine-grained normalization in the `normalize` method.
8495 e4a6c3b8 Olga Brani

8496 e4a6c3b8 Olga Brani
    For example, if you have a payload like this in response to a request for
8497 e4a6c3b8 Olga Brani
    post 1:
8498 e4a6c3b8 Olga Brani

8499 e4a6c3b8 Olga Brani
    ```js
8500 e4a6c3b8 Olga Brani
    {
8501 e4a6c3b8 Olga Brani
      "id": 1,
8502 e4a6c3b8 Olga Brani
      "title": "Rails is omakase",
8503 e4a6c3b8 Olga Brani

8504 e4a6c3b8 Olga Brani
      "_embedded": {
8505 e4a6c3b8 Olga Brani
        "comment": [{
8506 e4a6c3b8 Olga Brani
          "_id": 1,
8507 e4a6c3b8 Olga Brani
          "comment_title": "FIRST"
8508 e4a6c3b8 Olga Brani
        }, {
8509 e4a6c3b8 Olga Brani
          "_id": 2,
8510 e4a6c3b8 Olga Brani
          "comment_title": "Rails is unagi"
8511 e4a6c3b8 Olga Brani
        }]
8512 e4a6c3b8 Olga Brani
      }
8513 e4a6c3b8 Olga Brani
    }
8514 e4a6c3b8 Olga Brani
    ```
8515 e4a6c3b8 Olga Brani

8516 e4a6c3b8 Olga Brani
    You could implement a serializer that looks like this to get your payload
8517 e4a6c3b8 Olga Brani
    into shape:
8518 e4a6c3b8 Olga Brani

8519 e4a6c3b8 Olga Brani
    ```js
8520 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.RESTSerializer.extend({
8521 e4a6c3b8 Olga Brani
      // First, restructure the top-level so it's organized by type
8522 e4a6c3b8 Olga Brani
      extractSingle: function(store, type, payload, id, requestType) {
8523 e4a6c3b8 Olga Brani
        var comments = payload._embedded.comment;
8524 e4a6c3b8 Olga Brani
        delete payload._embedded;
8525 e4a6c3b8 Olga Brani

8526 e4a6c3b8 Olga Brani
        payload = { comments: comments, post: payload };
8527 e4a6c3b8 Olga Brani
        return this._super(store, type, payload, id, requestType);
8528 e4a6c3b8 Olga Brani
      },
8529 e4a6c3b8 Olga Brani

8530 e4a6c3b8 Olga Brani
      normalizeHash: {
8531 e4a6c3b8 Olga Brani
        // Next, normalize individual comments, which (after `extract`)
8532 e4a6c3b8 Olga Brani
        // are now located under `comments`
8533 e4a6c3b8 Olga Brani
        comments: function(hash) {
8534 e4a6c3b8 Olga Brani
          hash.id = hash._id;
8535 e4a6c3b8 Olga Brani
          hash.title = hash.comment_title;
8536 e4a6c3b8 Olga Brani
          delete hash._id;
8537 e4a6c3b8 Olga Brani
          delete hash.comment_title;
8538 e4a6c3b8 Olga Brani
          return hash;
8539 e4a6c3b8 Olga Brani
        }
8540 e4a6c3b8 Olga Brani
      }
8541 e4a6c3b8 Olga Brani
    })
8542 e4a6c3b8 Olga Brani
    ```
8543 e4a6c3b8 Olga Brani

8544 e4a6c3b8 Olga Brani
    When you call super from your own implementation of `extractSingle`, the
8545 e4a6c3b8 Olga Brani
    built-in implementation will find the primary record in your normalized
8546 e4a6c3b8 Olga Brani
    payload and push the remaining records into the store.
8547 e4a6c3b8 Olga Brani

8548 e4a6c3b8 Olga Brani
    The primary record is the single hash found under `post` or the first
8549 e4a6c3b8 Olga Brani
    element of the `posts` array.
8550 e4a6c3b8 Olga Brani

8551 e4a6c3b8 Olga Brani
    The primary record has special meaning when the record is being created
8552 e4a6c3b8 Olga Brani
    for the first time or updated (`createRecord` or `updateRecord`). In
8553 e4a6c3b8 Olga Brani
    particular, it will update the properties of the record that was saved.
8554 e4a6c3b8 Olga Brani

8555 e4a6c3b8 Olga Brani
    @method extractSingle
8556 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8557 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8558 e4a6c3b8 Olga Brani
    @param {Object} payload
8559 e4a6c3b8 Olga Brani
    @param {String} id
8560 e4a6c3b8 Olga Brani
    @param {'find'|'createRecord'|'updateRecord'|'deleteRecord'} requestType
8561 e4a6c3b8 Olga Brani
    @returns {Object} the primary response to the original request
8562 e4a6c3b8 Olga Brani
  */
8563 e4a6c3b8 Olga Brani
  extractSingle: function(store, primaryType, payload, recordId, requestType) {
8564 e4a6c3b8 Olga Brani
    payload = this.normalizePayload(primaryType, payload);
8565 e4a6c3b8 Olga Brani
8566 e4a6c3b8 Olga Brani
    var primaryTypeName = primaryType.typeKey,
8567 e4a6c3b8 Olga Brani
        primaryRecord;
8568 e4a6c3b8 Olga Brani
8569 e4a6c3b8 Olga Brani
    for (var prop in payload) {
8570 e4a6c3b8 Olga Brani
      var typeName  = this.typeForRoot(prop),
8571 e4a6c3b8 Olga Brani
          isPrimary = typeName === primaryTypeName;
8572 e4a6c3b8 Olga Brani
8573 e4a6c3b8 Olga Brani
      // legacy support for singular resources
8574 e4a6c3b8 Olga Brani
      if (isPrimary && Ember.typeOf(payload[prop]) !== "array" ) {
8575 e4a6c3b8 Olga Brani
        primaryRecord = this.normalize(primaryType, payload[prop], prop);
8576 e4a6c3b8 Olga Brani
        continue;
8577 e4a6c3b8 Olga Brani
      }
8578 e4a6c3b8 Olga Brani
8579 e4a6c3b8 Olga Brani
      var type = store.modelFor(typeName);
8580 e4a6c3b8 Olga Brani
8581 e4a6c3b8 Olga Brani
      /*jshint loopfunc:true*/
8582 e4a6c3b8 Olga Brani
      forEach.call(payload[prop], function(hash) {
8583 e4a6c3b8 Olga Brani
        var typeName = this.typeForRoot(prop),
8584 e4a6c3b8 Olga Brani
            type = store.modelFor(typeName),
8585 e4a6c3b8 Olga Brani
            typeSerializer = store.serializerFor(type);
8586 e4a6c3b8 Olga Brani
8587 e4a6c3b8 Olga Brani
        hash = typeSerializer.normalize(type, hash, prop);
8588 e4a6c3b8 Olga Brani
8589 e4a6c3b8 Olga Brani
        var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord,
8590 e4a6c3b8 Olga Brani
            isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId;
8591 e4a6c3b8 Olga Brani
8592 e4a6c3b8 Olga Brani
        // find the primary record.
8593 e4a6c3b8 Olga Brani
        //
8594 e4a6c3b8 Olga Brani
        // It's either:
8595 e4a6c3b8 Olga Brani
        // * the record with the same ID as the original request
8596 e4a6c3b8 Olga Brani
        // * in the case of a newly created record that didn't have an ID, the first
8597 e4a6c3b8 Olga Brani
        //   record in the Array
8598 e4a6c3b8 Olga Brani
        if (isFirstCreatedRecord || isUpdatedRecord) {
8599 e4a6c3b8 Olga Brani
          primaryRecord = hash;
8600 e4a6c3b8 Olga Brani
        } else {
8601 e4a6c3b8 Olga Brani
          store.push(typeName, hash);
8602 e4a6c3b8 Olga Brani
        }
8603 e4a6c3b8 Olga Brani
      }, this);
8604 e4a6c3b8 Olga Brani
    }
8605 e4a6c3b8 Olga Brani
8606 e4a6c3b8 Olga Brani
    return primaryRecord;
8607 e4a6c3b8 Olga Brani
  },
8608 e4a6c3b8 Olga Brani
8609 e4a6c3b8 Olga Brani
  /**
8610 e4a6c3b8 Olga Brani
    Called when the server has returned a payload representing
8611 e4a6c3b8 Olga Brani
    multiple records, such as in response to a `findAll` or `findQuery`.
8612 e4a6c3b8 Olga Brani

8613 e4a6c3b8 Olga Brani
    It is your opportunity to clean up the server's response into the normalized
8614 e4a6c3b8 Olga Brani
    form expected by Ember Data.
8615 e4a6c3b8 Olga Brani

8616 e4a6c3b8 Olga Brani
    If you want, you can just restructure the top-level of your payload, and
8617 e4a6c3b8 Olga Brani
    do more fine-grained normalization in the `normalize` method.
8618 e4a6c3b8 Olga Brani

8619 e4a6c3b8 Olga Brani
    For example, if you have a payload like this in response to a request for
8620 e4a6c3b8 Olga Brani
    all posts:
8621 e4a6c3b8 Olga Brani

8622 e4a6c3b8 Olga Brani
    ```js
8623 e4a6c3b8 Olga Brani
    {
8624 e4a6c3b8 Olga Brani
      "_embedded": {
8625 e4a6c3b8 Olga Brani
        "post": [{
8626 e4a6c3b8 Olga Brani
          "id": 1,
8627 e4a6c3b8 Olga Brani
          "title": "Rails is omakase"
8628 e4a6c3b8 Olga Brani
        }, {
8629 e4a6c3b8 Olga Brani
          "id": 2,
8630 e4a6c3b8 Olga Brani
          "title": "The Parley Letter"
8631 e4a6c3b8 Olga Brani
        }],
8632 e4a6c3b8 Olga Brani
        "comment": [{
8633 e4a6c3b8 Olga Brani
          "_id": 1,
8634 e4a6c3b8 Olga Brani
          "comment_title": "Rails is unagi"
8635 e4a6c3b8 Olga Brani
          "post_id": 1
8636 e4a6c3b8 Olga Brani
        }, {
8637 e4a6c3b8 Olga Brani
          "_id": 2,
8638 e4a6c3b8 Olga Brani
          "comment_title": "Don't tread on me",
8639 e4a6c3b8 Olga Brani
          "post_id": 2
8640 e4a6c3b8 Olga Brani
        }]
8641 e4a6c3b8 Olga Brani
      }
8642 e4a6c3b8 Olga Brani
    }
8643 e4a6c3b8 Olga Brani
    ```
8644 e4a6c3b8 Olga Brani

8645 e4a6c3b8 Olga Brani
    You could implement a serializer that looks like this to get your payload
8646 e4a6c3b8 Olga Brani
    into shape:
8647 e4a6c3b8 Olga Brani

8648 e4a6c3b8 Olga Brani
    ```js
8649 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.RESTSerializer.extend({
8650 e4a6c3b8 Olga Brani
      // First, restructure the top-level so it's organized by type
8651 e4a6c3b8 Olga Brani
      // and the comments are listed under a post's `comments` key.
8652 e4a6c3b8 Olga Brani
      extractArray: function(store, type, payload, id, requestType) {
8653 e4a6c3b8 Olga Brani
        var posts = payload._embedded.post;
8654 e4a6c3b8 Olga Brani
        var comments = [];
8655 e4a6c3b8 Olga Brani
        var postCache = {};
8656 e4a6c3b8 Olga Brani

8657 e4a6c3b8 Olga Brani
        posts.forEach(function(post) {
8658 e4a6c3b8 Olga Brani
          post.comments = [];
8659 e4a6c3b8 Olga Brani
          postCache[post.id] = post;
8660 e4a6c3b8 Olga Brani
        });
8661 e4a6c3b8 Olga Brani

8662 e4a6c3b8 Olga Brani
        payload._embedded.comment.forEach(function(comment) {
8663 e4a6c3b8 Olga Brani
          comments.push(comment);
8664 e4a6c3b8 Olga Brani
          postCache[comment.post_id].comments.push(comment);
8665 e4a6c3b8 Olga Brani
          delete comment.post_id;
8666 e4a6c3b8 Olga Brani
        }
8667 e4a6c3b8 Olga Brani

8668 e4a6c3b8 Olga Brani
        payload = { comments: comments, posts: payload };
8669 e4a6c3b8 Olga Brani

8670 e4a6c3b8 Olga Brani
        return this._super(store, type, payload, id, requestType);
8671 e4a6c3b8 Olga Brani
      },
8672 e4a6c3b8 Olga Brani

8673 e4a6c3b8 Olga Brani
      normalizeHash: {
8674 e4a6c3b8 Olga Brani
        // Next, normalize individual comments, which (after `extract`)
8675 e4a6c3b8 Olga Brani
        // are now located under `comments`
8676 e4a6c3b8 Olga Brani
        comments: function(hash) {
8677 e4a6c3b8 Olga Brani
          hash.id = hash._id;
8678 e4a6c3b8 Olga Brani
          hash.title = hash.comment_title;
8679 e4a6c3b8 Olga Brani
          delete hash._id;
8680 e4a6c3b8 Olga Brani
          delete hash.comment_title;
8681 e4a6c3b8 Olga Brani
          return hash;
8682 e4a6c3b8 Olga Brani
        }
8683 e4a6c3b8 Olga Brani
      }
8684 e4a6c3b8 Olga Brani
    })
8685 e4a6c3b8 Olga Brani
    ```
8686 e4a6c3b8 Olga Brani

8687 e4a6c3b8 Olga Brani
    When you call super from your own implementation of `extractArray`, the
8688 e4a6c3b8 Olga Brani
    built-in implementation will find the primary array in your normalized
8689 e4a6c3b8 Olga Brani
    payload and push the remaining records into the store.
8690 e4a6c3b8 Olga Brani

8691 e4a6c3b8 Olga Brani
    The primary array is the array found under `posts`.
8692 e4a6c3b8 Olga Brani

8693 e4a6c3b8 Olga Brani
    The primary record has special meaning when responding to `findQuery`
8694 e4a6c3b8 Olga Brani
    or `findHasMany`. In particular, the primary array will become the
8695 e4a6c3b8 Olga Brani
    list of records in the record array that kicked off the request.
8696 e4a6c3b8 Olga Brani

8697 e4a6c3b8 Olga Brani
    If your primary array contains secondary (embedded) records of the same type,
8698 e4a6c3b8 Olga Brani
    you cannot place these into the primary array `posts`. Instead, place the
8699 e4a6c3b8 Olga Brani
    secondary items into an underscore prefixed property `_posts`, which will
8700 e4a6c3b8 Olga Brani
    push these items into the store and will not affect the resulting query.
8701 e4a6c3b8 Olga Brani

8702 e4a6c3b8 Olga Brani
    @method extractArray
8703 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8704 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8705 e4a6c3b8 Olga Brani
    @param {Object} payload
8706 e4a6c3b8 Olga Brani
    @param {'findAll'|'findMany'|'findHasMany'|'findQuery'} requestType
8707 e4a6c3b8 Olga Brani
    @returns {Array} The primary array that was returned in response
8708 e4a6c3b8 Olga Brani
      to the original query.
8709 e4a6c3b8 Olga Brani
  */
8710 e4a6c3b8 Olga Brani
  extractArray: function(store, primaryType, payload) {
8711 e4a6c3b8 Olga Brani
    payload = this.normalizePayload(primaryType, payload);
8712 e4a6c3b8 Olga Brani
8713 e4a6c3b8 Olga Brani
    var primaryTypeName = primaryType.typeKey,
8714 e4a6c3b8 Olga Brani
        primaryArray;
8715 e4a6c3b8 Olga Brani
8716 e4a6c3b8 Olga Brani
    for (var prop in payload) {
8717 e4a6c3b8 Olga Brani
      var typeKey = prop,
8718 e4a6c3b8 Olga Brani
          forcedSecondary = false;
8719 e4a6c3b8 Olga Brani
8720 e4a6c3b8 Olga Brani
      if (prop.charAt(0) === '_') {
8721 e4a6c3b8 Olga Brani
        forcedSecondary = true;
8722 e4a6c3b8 Olga Brani
        typeKey = prop.substr(1);
8723 e4a6c3b8 Olga Brani
      }
8724 e4a6c3b8 Olga Brani
8725 e4a6c3b8 Olga Brani
      var typeName = this.typeForRoot(typeKey),
8726 e4a6c3b8 Olga Brani
          type = store.modelFor(typeName),
8727 e4a6c3b8 Olga Brani
          typeSerializer = store.serializerFor(type),
8728 e4a6c3b8 Olga Brani
          isPrimary = (!forcedSecondary && (typeName === primaryTypeName));
8729 e4a6c3b8 Olga Brani
8730 e4a6c3b8 Olga Brani
      /*jshint loopfunc:true*/
8731 e4a6c3b8 Olga Brani
      var normalizedArray = map.call(payload[prop], function(hash) {
8732 e4a6c3b8 Olga Brani
        return typeSerializer.normalize(type, hash, prop);
8733 e4a6c3b8 Olga Brani
      }, this);
8734 e4a6c3b8 Olga Brani
8735 e4a6c3b8 Olga Brani
      if (isPrimary) {
8736 e4a6c3b8 Olga Brani
        primaryArray = normalizedArray;
8737 e4a6c3b8 Olga Brani
      } else {
8738 e4a6c3b8 Olga Brani
        store.pushMany(typeName, normalizedArray);
8739 e4a6c3b8 Olga Brani
      }
8740 e4a6c3b8 Olga Brani
    }
8741 e4a6c3b8 Olga Brani
8742 e4a6c3b8 Olga Brani
    return primaryArray;
8743 e4a6c3b8 Olga Brani
  },
8744 e4a6c3b8 Olga Brani
8745 e4a6c3b8 Olga Brani
  /**
8746 e4a6c3b8 Olga Brani
    This method allows you to push a payload containing top-level
8747 e4a6c3b8 Olga Brani
    collections of records organized per type.
8748 e4a6c3b8 Olga Brani

8749 e4a6c3b8 Olga Brani
    ```js
8750 e4a6c3b8 Olga Brani
    {
8751 e4a6c3b8 Olga Brani
      "posts": [{
8752 e4a6c3b8 Olga Brani
        "id": "1",
8753 e4a6c3b8 Olga Brani
        "title": "Rails is omakase",
8754 e4a6c3b8 Olga Brani
        "author", "1",
8755 e4a6c3b8 Olga Brani
        "comments": [ "1" ]
8756 e4a6c3b8 Olga Brani
      }],
8757 e4a6c3b8 Olga Brani
      "comments": [{
8758 e4a6c3b8 Olga Brani
        "id": "1",
8759 e4a6c3b8 Olga Brani
        "body": "FIRST"
8760 e4a6c3b8 Olga Brani
      }],
8761 e4a6c3b8 Olga Brani
      "users": [{
8762 e4a6c3b8 Olga Brani
        "id": "1",
8763 e4a6c3b8 Olga Brani
        "name": "@d2h"
8764 e4a6c3b8 Olga Brani
      }]
8765 e4a6c3b8 Olga Brani
    }
8766 e4a6c3b8 Olga Brani
    ```
8767 e4a6c3b8 Olga Brani

8768 e4a6c3b8 Olga Brani
    It will first normalize the payload, so you can use this to push
8769 e4a6c3b8 Olga Brani
    in data streaming in from your server structured the same way
8770 e4a6c3b8 Olga Brani
    that fetches and saves are structured.
8771 e4a6c3b8 Olga Brani

8772 e4a6c3b8 Olga Brani
    @method pushPayload
8773 e4a6c3b8 Olga Brani
    @param {DS.Store} store
8774 e4a6c3b8 Olga Brani
    @param {Object} payload
8775 e4a6c3b8 Olga Brani
  */
8776 e4a6c3b8 Olga Brani
  pushPayload: function(store, payload) {
8777 e4a6c3b8 Olga Brani
    payload = this.normalizePayload(null, payload);
8778 e4a6c3b8 Olga Brani
8779 e4a6c3b8 Olga Brani
    for (var prop in payload) {
8780 e4a6c3b8 Olga Brani
      var typeName = this.typeForRoot(prop),
8781 e4a6c3b8 Olga Brani
          type = store.modelFor(typeName);
8782 e4a6c3b8 Olga Brani
8783 e4a6c3b8 Olga Brani
      /*jshint loopfunc:true*/
8784 e4a6c3b8 Olga Brani
      var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) {
8785 e4a6c3b8 Olga Brani
        return this.normalize(type, hash, prop);
8786 e4a6c3b8 Olga Brani
      }, this);
8787 e4a6c3b8 Olga Brani
8788 e4a6c3b8 Olga Brani
      store.pushMany(typeName, normalizedArray);
8789 e4a6c3b8 Olga Brani
    }
8790 e4a6c3b8 Olga Brani
  },
8791 e4a6c3b8 Olga Brani
8792 e4a6c3b8 Olga Brani
  /**
8793 e4a6c3b8 Olga Brani
    You can use this method to normalize the JSON root keys returned
8794 e4a6c3b8 Olga Brani
    into the model type expected by your store.
8795 e4a6c3b8 Olga Brani

8796 e4a6c3b8 Olga Brani
    For example, your server may return underscored root keys rather than
8797 e4a6c3b8 Olga Brani
    the expected camelcased versions.
8798 e4a6c3b8 Olga Brani

8799 e4a6c3b8 Olga Brani
    ```js
8800 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.RESTSerializer.extend({
8801 e4a6c3b8 Olga Brani
      typeForRoot: function(root) {
8802 e4a6c3b8 Olga Brani
        var camelized = Ember.String.camelize(root);
8803 e4a6c3b8 Olga Brani
        return Ember.String.singularize(camelized);
8804 e4a6c3b8 Olga Brani
      }
8805 e4a6c3b8 Olga Brani
    });
8806 e4a6c3b8 Olga Brani
    ```
8807 e4a6c3b8 Olga Brani

8808 e4a6c3b8 Olga Brani
    @method typeForRoot
8809 e4a6c3b8 Olga Brani
    @param {String} root
8810 e4a6c3b8 Olga Brani
    @returns {String} the model's typeKey
8811 e4a6c3b8 Olga Brani
  */
8812 e4a6c3b8 Olga Brani
  typeForRoot: function(root) {
8813 e4a6c3b8 Olga Brani
    return Ember.String.singularize(root);
8814 e4a6c3b8 Olga Brani
  },
8815 e4a6c3b8 Olga Brani
8816 e4a6c3b8 Olga Brani
  // SERIALIZE
8817 e4a6c3b8 Olga Brani
8818 e4a6c3b8 Olga Brani
  /**
8819 e4a6c3b8 Olga Brani
    Called when a record is saved in order to convert the
8820 e4a6c3b8 Olga Brani
    record into JSON.
8821 e4a6c3b8 Olga Brani

8822 e4a6c3b8 Olga Brani
    By default, it creates a JSON object with a key for
8823 e4a6c3b8 Olga Brani
    each attribute and belongsTo relationship.
8824 e4a6c3b8 Olga Brani

8825 e4a6c3b8 Olga Brani
    For example, consider this model:
8826 e4a6c3b8 Olga Brani

8827 e4a6c3b8 Olga Brani
    ```js
8828 e4a6c3b8 Olga Brani
    App.Comment = DS.Model.extend({
8829 e4a6c3b8 Olga Brani
      title: DS.attr(),
8830 e4a6c3b8 Olga Brani
      body: DS.attr(),
8831 e4a6c3b8 Olga Brani

8832 e4a6c3b8 Olga Brani
      author: DS.belongsTo('user')
8833 e4a6c3b8 Olga Brani
    });
8834 e4a6c3b8 Olga Brani
    ```
8835 e4a6c3b8 Olga Brani

8836 e4a6c3b8 Olga Brani
    The default serialization would create a JSON object like:
8837 e4a6c3b8 Olga Brani

8838 e4a6c3b8 Olga Brani
    ```js
8839 e4a6c3b8 Olga Brani
    {
8840 e4a6c3b8 Olga Brani
      "title": "Rails is unagi",
8841 e4a6c3b8 Olga Brani
      "body": "Rails? Omakase? O_O",
8842 e4a6c3b8 Olga Brani
      "author": 12
8843 e4a6c3b8 Olga Brani
    }
8844 e4a6c3b8 Olga Brani
    ```
8845 e4a6c3b8 Olga Brani

8846 e4a6c3b8 Olga Brani
    By default, attributes are passed through as-is, unless
8847 e4a6c3b8 Olga Brani
    you specified an attribute type (`DS.attr('date')`). If
8848 e4a6c3b8 Olga Brani
    you specify a transform, the JavaScript value will be
8849 e4a6c3b8 Olga Brani
    serialized when inserted into the JSON hash.
8850 e4a6c3b8 Olga Brani

8851 e4a6c3b8 Olga Brani
    By default, belongs-to relationships are converted into
8852 e4a6c3b8 Olga Brani
    IDs when inserted into the JSON hash.
8853 e4a6c3b8 Olga Brani

8854 e4a6c3b8 Olga Brani
    ## IDs
8855 e4a6c3b8 Olga Brani

8856 e4a6c3b8 Olga Brani
    `serialize` takes an options hash with a single option:
8857 e4a6c3b8 Olga Brani
    `includeId`. If this option is `true`, `serialize` will,
8858 e4a6c3b8 Olga Brani
    by default include the ID in the JSON object it builds.
8859 e4a6c3b8 Olga Brani

8860 e4a6c3b8 Olga Brani
    The adapter passes in `includeId: true` when serializing
8861 e4a6c3b8 Olga Brani
    a record for `createRecord`, but not for `updateRecord`.
8862 e4a6c3b8 Olga Brani

8863 e4a6c3b8 Olga Brani
    ## Customization
8864 e4a6c3b8 Olga Brani

8865 e4a6c3b8 Olga Brani
    Your server may expect a different JSON format than the
8866 e4a6c3b8 Olga Brani
    built-in serialization format.
8867 e4a6c3b8 Olga Brani

8868 e4a6c3b8 Olga Brani
    In that case, you can implement `serialize` yourself and
8869 e4a6c3b8 Olga Brani
    return a JSON hash of your choosing.
8870 e4a6c3b8 Olga Brani

8871 e4a6c3b8 Olga Brani
    ```js
8872 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.RESTSerializer.extend({
8873 e4a6c3b8 Olga Brani
      serialize: function(post, options) {
8874 e4a6c3b8 Olga Brani
        var json = {
8875 e4a6c3b8 Olga Brani
          POST_TTL: post.get('title'),
8876 e4a6c3b8 Olga Brani
          POST_BDY: post.get('body'),
8877 e4a6c3b8 Olga Brani
          POST_CMS: post.get('comments').mapProperty('id')
8878 e4a6c3b8 Olga Brani
        }
8879 e4a6c3b8 Olga Brani

8880 e4a6c3b8 Olga Brani
        if (options.includeId) {
8881 e4a6c3b8 Olga Brani
          json.POST_ID_ = post.get('id');
8882 e4a6c3b8 Olga Brani
        }
8883 e4a6c3b8 Olga Brani

8884 e4a6c3b8 Olga Brani
        return json;
8885 e4a6c3b8 Olga Brani
      }
8886 e4a6c3b8 Olga Brani
    });
8887 e4a6c3b8 Olga Brani
    ```
8888 e4a6c3b8 Olga Brani

8889 e4a6c3b8 Olga Brani
    ## Customizing an App-Wide Serializer
8890 e4a6c3b8 Olga Brani

8891 e4a6c3b8 Olga Brani
    If you want to define a serializer for your entire
8892 e4a6c3b8 Olga Brani
    application, you'll probably want to use `eachAttribute`
8893 e4a6c3b8 Olga Brani
    and `eachRelationship` on the record.
8894 e4a6c3b8 Olga Brani

8895 e4a6c3b8 Olga Brani
    ```js
8896 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.RESTSerializer.extend({
8897 e4a6c3b8 Olga Brani
      serialize: function(record, options) {
8898 e4a6c3b8 Olga Brani
        var json = {};
8899 e4a6c3b8 Olga Brani

8900 e4a6c3b8 Olga Brani
        record.eachAttribute(function(name) {
8901 e4a6c3b8 Olga Brani
          json[serverAttributeName(name)] = record.get(name);
8902 e4a6c3b8 Olga Brani
        })
8903 e4a6c3b8 Olga Brani

8904 e4a6c3b8 Olga Brani
        record.eachRelationship(function(name, relationship) {
8905 e4a6c3b8 Olga Brani
          if (relationship.kind === 'hasMany') {
8906 e4a6c3b8 Olga Brani
            json[serverHasManyName(name)] = record.get(name).mapBy('id');
8907 e4a6c3b8 Olga Brani
          }
8908 e4a6c3b8 Olga Brani
        });
8909 e4a6c3b8 Olga Brani

8910 e4a6c3b8 Olga Brani
        if (options.includeId) {
8911 e4a6c3b8 Olga Brani
          json.ID_ = record.get('id');
8912 e4a6c3b8 Olga Brani
        }
8913 e4a6c3b8 Olga Brani

8914 e4a6c3b8 Olga Brani
        return json;
8915 e4a6c3b8 Olga Brani
      }
8916 e4a6c3b8 Olga Brani
    });
8917 e4a6c3b8 Olga Brani

8918 e4a6c3b8 Olga Brani
    function serverAttributeName(attribute) {
8919 e4a6c3b8 Olga Brani
      return attribute.underscore().toUpperCase();
8920 e4a6c3b8 Olga Brani
    }
8921 e4a6c3b8 Olga Brani

8922 e4a6c3b8 Olga Brani
    function serverHasManyName(name) {
8923 e4a6c3b8 Olga Brani
      return serverAttributeName(name.singularize()) + "_IDS";
8924 e4a6c3b8 Olga Brani
    }
8925 e4a6c3b8 Olga Brani
    ```
8926 e4a6c3b8 Olga Brani

8927 e4a6c3b8 Olga Brani
    This serializer will generate JSON that looks like this:
8928 e4a6c3b8 Olga Brani

8929 e4a6c3b8 Olga Brani
    ```js
8930 e4a6c3b8 Olga Brani
    {
8931 e4a6c3b8 Olga Brani
      "TITLE": "Rails is omakase",
8932 e4a6c3b8 Olga Brani
      "BODY": "Yep. Omakase.",
8933 e4a6c3b8 Olga Brani
      "COMMENT_IDS": [ 1, 2, 3 ]
8934 e4a6c3b8 Olga Brani
    }
8935 e4a6c3b8 Olga Brani
    ```
8936 e4a6c3b8 Olga Brani

8937 e4a6c3b8 Olga Brani
    ## Tweaking the Default JSON
8938 e4a6c3b8 Olga Brani

8939 e4a6c3b8 Olga Brani
    If you just want to do some small tweaks on the default JSON,
8940 e4a6c3b8 Olga Brani
    you can call super first and make the tweaks on the returned
8941 e4a6c3b8 Olga Brani
    JSON.
8942 e4a6c3b8 Olga Brani

8943 e4a6c3b8 Olga Brani
    ```js
8944 e4a6c3b8 Olga Brani
    App.PostSerializer = DS.RESTSerializer.extend({
8945 e4a6c3b8 Olga Brani
      serialize: function(record, options) {
8946 e4a6c3b8 Olga Brani
        var json = this._super(record, options);
8947 e4a6c3b8 Olga Brani

8948 e4a6c3b8 Olga Brani
        json.subject = json.title;
8949 e4a6c3b8 Olga Brani
        delete json.title;
8950 e4a6c3b8 Olga Brani

8951 e4a6c3b8 Olga Brani
        return json;
8952 e4a6c3b8 Olga Brani
      }
8953 e4a6c3b8 Olga Brani
    });
8954 e4a6c3b8 Olga Brani
    ```
8955 e4a6c3b8 Olga Brani

8956 e4a6c3b8 Olga Brani
    @method serialize
8957 e4a6c3b8 Olga Brani
    @param record
8958 e4a6c3b8 Olga Brani
    @param options
8959 e4a6c3b8 Olga Brani
  */
8960 e4a6c3b8 Olga Brani
  serialize: function(record, options) {
8961 e4a6c3b8 Olga Brani
    return this._super.apply(this, arguments);
8962 e4a6c3b8 Olga Brani
  },
8963 e4a6c3b8 Olga Brani
8964 e4a6c3b8 Olga Brani
  /**
8965 e4a6c3b8 Olga Brani
    You can use this method to customize the root keys serialized into the JSON.
8966 e4a6c3b8 Olga Brani
    By default the REST Serializer sends camelized root keys.
8967 e4a6c3b8 Olga Brani
    For example, your server may expect underscored root objects.
8968 e4a6c3b8 Olga Brani

8969 e4a6c3b8 Olga Brani
    ```js
8970 e4a6c3b8 Olga Brani
    App.ApplicationSerializer = DS.RESTSerializer.extend({
8971 e4a6c3b8 Olga Brani
      serializeIntoHash: function(data, type, record, options) {
8972 e4a6c3b8 Olga Brani
        var root = Ember.String.decamelize(type.typeKey);
8973 e4a6c3b8 Olga Brani
        data[root] = this.serialize(record, options);
8974 e4a6c3b8 Olga Brani
      }
8975 e4a6c3b8 Olga Brani
    });
8976 e4a6c3b8 Olga Brani
    ```
8977 e4a6c3b8 Olga Brani

8978 e4a6c3b8 Olga Brani
    @method serializeIntoHash
8979 e4a6c3b8 Olga Brani
    @param {Object} hash
8980 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
8981 e4a6c3b8 Olga Brani
    @param {DS.Model} record
8982 e4a6c3b8 Olga Brani
    @param {Object} options
8983 e4a6c3b8 Olga Brani
  */
8984 e4a6c3b8 Olga Brani
  serializeIntoHash: function(hash, type, record, options) {
8985 e4a6c3b8 Olga Brani
    hash[type.typeKey] = this.serialize(record, options);
8986 e4a6c3b8 Olga Brani
  },
8987 e4a6c3b8 Olga Brani
8988 e4a6c3b8 Olga Brani
  /**
8989 e4a6c3b8 Olga Brani
    You can use this method to customize how polymorphic objects are serialized.
8990 e4a6c3b8 Olga Brani
    By default the JSON Serializer creates the key by appending `Type` to
8991 e4a6c3b8 Olga Brani
    the attribute and value from the model's camelcased model name.
8992 e4a6c3b8 Olga Brani

8993 e4a6c3b8 Olga Brani
    @method serializePolymorphicType
8994 e4a6c3b8 Olga Brani
    @param {DS.Model} record
8995 e4a6c3b8 Olga Brani
    @param {Object} json
8996 e4a6c3b8 Olga Brani
    @param {Object} relationship
8997 e4a6c3b8 Olga Brani
  */
8998 e4a6c3b8 Olga Brani
  serializePolymorphicType: function(record, json, relationship) {
8999 e4a6c3b8 Olga Brani
    var key = relationship.key,
9000 e4a6c3b8 Olga Brani
        belongsTo = get(record, key);
9001 e4a6c3b8 Olga Brani
    key = this.keyForAttribute ? this.keyForAttribute(key) : key;
9002 e4a6c3b8 Olga Brani
    json[key + "Type"] = belongsTo.constructor.typeKey;
9003 e4a6c3b8 Olga Brani
  }
9004 e4a6c3b8 Olga Brani
});
9005 e4a6c3b8 Olga Brani
9006 e4a6c3b8 Olga Brani
})();
9007 e4a6c3b8 Olga Brani
9008 e4a6c3b8 Olga Brani
9009 e4a6c3b8 Olga Brani
9010 e4a6c3b8 Olga Brani
(function() {
9011 e4a6c3b8 Olga Brani
/**
9012 e4a6c3b8 Olga Brani
  @module ember-data
9013 e4a6c3b8 Olga Brani
*/
9014 e4a6c3b8 Olga Brani
9015 e4a6c3b8 Olga Brani
var get = Ember.get, set = Ember.set;
9016 e4a6c3b8 Olga Brani
var forEach = Ember.ArrayPolyfills.forEach;
9017 e4a6c3b8 Olga Brani
9018 e4a6c3b8 Olga Brani
/**
9019 e4a6c3b8 Olga Brani
  The REST adapter allows your store to communicate with an HTTP server by
9020 e4a6c3b8 Olga Brani
  transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
9021 e4a6c3b8 Olga Brani
  should use the REST adapter.
9022 e4a6c3b8 Olga Brani

9023 e4a6c3b8 Olga Brani
  This adapter is designed around the idea that the JSON exchanged with
9024 e4a6c3b8 Olga Brani
  the server should be conventional.
9025 e4a6c3b8 Olga Brani

9026 e4a6c3b8 Olga Brani
  ## JSON Structure
9027 e4a6c3b8 Olga Brani

9028 e4a6c3b8 Olga Brani
  The REST adapter expects the JSON returned from your server to follow
9029 e4a6c3b8 Olga Brani
  these conventions.
9030 e4a6c3b8 Olga Brani

9031 e4a6c3b8 Olga Brani
  ### Object Root
9032 e4a6c3b8 Olga Brani

9033 e4a6c3b8 Olga Brani
  The JSON payload should be an object that contains the record inside a
9034 e4a6c3b8 Olga Brani
  root property. For example, in response to a `GET` request for
9035 e4a6c3b8 Olga Brani
  `/posts/1`, the JSON should look like this:
9036 e4a6c3b8 Olga Brani

9037 e4a6c3b8 Olga Brani
  ```js
9038 e4a6c3b8 Olga Brani
  {
9039 e4a6c3b8 Olga Brani
    "post": {
9040 e4a6c3b8 Olga Brani
      "title": "I'm Running to Reform the W3C's Tag",
9041 e4a6c3b8 Olga Brani
      "author": "Yehuda Katz"
9042 e4a6c3b8 Olga Brani
    }
9043 e4a6c3b8 Olga Brani
  }
9044 e4a6c3b8 Olga Brani
  ```
9045 e4a6c3b8 Olga Brani

9046 e4a6c3b8 Olga Brani
  ### Conventional Names
9047 e4a6c3b8 Olga Brani

9048 e4a6c3b8 Olga Brani
  Attribute names in your JSON payload should be the camelCased versions of
9049 e4a6c3b8 Olga Brani
  the attributes in your Ember.js models.
9050 e4a6c3b8 Olga Brani

9051 e4a6c3b8 Olga Brani
  For example, if you have a `Person` model:
9052 e4a6c3b8 Olga Brani

9053 e4a6c3b8 Olga Brani
  ```js
9054 e4a6c3b8 Olga Brani
  App.Person = DS.Model.extend({
9055 e4a6c3b8 Olga Brani
    firstName: DS.attr('string'),
9056 e4a6c3b8 Olga Brani
    lastName: DS.attr('string'),
9057 e4a6c3b8 Olga Brani
    occupation: DS.attr('string')
9058 e4a6c3b8 Olga Brani
  });
9059 e4a6c3b8 Olga Brani
  ```
9060 e4a6c3b8 Olga Brani

9061 e4a6c3b8 Olga Brani
  The JSON returned should look like this:
9062 e4a6c3b8 Olga Brani

9063 e4a6c3b8 Olga Brani
  ```js
9064 e4a6c3b8 Olga Brani
  {
9065 e4a6c3b8 Olga Brani
    "person": {
9066 e4a6c3b8 Olga Brani
      "firstName": "Barack",
9067 e4a6c3b8 Olga Brani
      "lastName": "Obama",
9068 e4a6c3b8 Olga Brani
      "occupation": "President"
9069 e4a6c3b8 Olga Brani
    }
9070 e4a6c3b8 Olga Brani
  }
9071 e4a6c3b8 Olga Brani
  ```
9072 e4a6c3b8 Olga Brani

9073 e4a6c3b8 Olga Brani
  ## Customization
9074 e4a6c3b8 Olga Brani

9075 e4a6c3b8 Olga Brani
  ### Endpoint path customization
9076 e4a6c3b8 Olga Brani

9077 e4a6c3b8 Olga Brani
  Endpoint paths can be prefixed with a `namespace` by setting the namespace
9078 e4a6c3b8 Olga Brani
  property on the adapter:
9079 e4a6c3b8 Olga Brani

9080 e4a6c3b8 Olga Brani
  ```js
9081 e4a6c3b8 Olga Brani
  DS.RESTAdapter.reopen({
9082 e4a6c3b8 Olga Brani
    namespace: 'api/1'
9083 e4a6c3b8 Olga Brani
  });
9084 e4a6c3b8 Olga Brani
  ```
9085 e4a6c3b8 Olga Brani
  Requests for `App.Person` would now target `/api/1/people/1`.
9086 e4a6c3b8 Olga Brani

9087 e4a6c3b8 Olga Brani
  ### Host customization
9088 e4a6c3b8 Olga Brani

9089 e4a6c3b8 Olga Brani
  An adapter can target other hosts by setting the `host` property.
9090 e4a6c3b8 Olga Brani

9091 e4a6c3b8 Olga Brani
  ```js
9092 e4a6c3b8 Olga Brani
  DS.RESTAdapter.reopen({
9093 e4a6c3b8 Olga Brani
    host: 'https://api.example.com'
9094 e4a6c3b8 Olga Brani
  });
9095 e4a6c3b8 Olga Brani
  ```
9096 e4a6c3b8 Olga Brani

9097 e4a6c3b8 Olga Brani
  ### Headers customization
9098 e4a6c3b8 Olga Brani

9099 e4a6c3b8 Olga Brani
  Some APIs require HTTP headers, e.g. to provide an API key. An array of
9100 e4a6c3b8 Olga Brani
  headers can be added to the adapter which are passed with every request:
9101 e4a6c3b8 Olga Brani

9102 e4a6c3b8 Olga Brani
  ```js
9103 e4a6c3b8 Olga Brani
  DS.RESTAdapter.reopen({
9104 e4a6c3b8 Olga Brani
    headers: {
9105 e4a6c3b8 Olga Brani
      "API_KEY": "secret key",
9106 e4a6c3b8 Olga Brani
      "ANOTHER_HEADER": "Some header value"
9107 e4a6c3b8 Olga Brani
    }
9108 e4a6c3b8 Olga Brani
  });
9109 e4a6c3b8 Olga Brani
  ```
9110 e4a6c3b8 Olga Brani

9111 e4a6c3b8 Olga Brani
  @class RESTAdapter
9112 e4a6c3b8 Olga Brani
  @constructor
9113 e4a6c3b8 Olga Brani
  @namespace DS
9114 e4a6c3b8 Olga Brani
  @extends DS.Adapter
9115 e4a6c3b8 Olga Brani
*/
9116 e4a6c3b8 Olga Brani
DS.RESTAdapter = DS.Adapter.extend({
9117 e4a6c3b8 Olga Brani
  defaultSerializer: '_rest',
9118 e4a6c3b8 Olga Brani
9119 e4a6c3b8 Olga Brani
9120 e4a6c3b8 Olga Brani
  /**
9121 e4a6c3b8 Olga Brani
    Endpoint paths can be prefixed with a `namespace` by setting the namespace
9122 e4a6c3b8 Olga Brani
    property on the adapter:
9123 e4a6c3b8 Olga Brani

9124 e4a6c3b8 Olga Brani
    ```javascript
9125 e4a6c3b8 Olga Brani
    DS.RESTAdapter.reopen({
9126 e4a6c3b8 Olga Brani
      namespace: 'api/1'
9127 e4a6c3b8 Olga Brani
    });
9128 e4a6c3b8 Olga Brani
    ```
9129 e4a6c3b8 Olga Brani

9130 e4a6c3b8 Olga Brani
    Requests for `App.Post` would now target `/api/1/post/`.
9131 e4a6c3b8 Olga Brani

9132 e4a6c3b8 Olga Brani
    @property namespace
9133 e4a6c3b8 Olga Brani
    @type {String}
9134 e4a6c3b8 Olga Brani
  */
9135 e4a6c3b8 Olga Brani
9136 e4a6c3b8 Olga Brani
  /**
9137 e4a6c3b8 Olga Brani
    An adapter can target other hosts by setting the `host` property.
9138 e4a6c3b8 Olga Brani

9139 e4a6c3b8 Olga Brani
    ```javascript
9140 e4a6c3b8 Olga Brani
    DS.RESTAdapter.reopen({
9141 e4a6c3b8 Olga Brani
      host: 'https://api.example.com'
9142 e4a6c3b8 Olga Brani
    });
9143 e4a6c3b8 Olga Brani
    ```
9144 e4a6c3b8 Olga Brani

9145 e4a6c3b8 Olga Brani
    Requests for `App.Post` would now target `https://api.example.com/post/`.
9146 e4a6c3b8 Olga Brani

9147 e4a6c3b8 Olga Brani
    @property host
9148 e4a6c3b8 Olga Brani
    @type {String}
9149 e4a6c3b8 Olga Brani
  */
9150 e4a6c3b8 Olga Brani
9151 e4a6c3b8 Olga Brani
  /**
9152 e4a6c3b8 Olga Brani
    Some APIs require HTTP headers, e.g. to provide an API key. An array of
9153 e4a6c3b8 Olga Brani
    headers can be added to the adapter which are passed with every request:
9154 e4a6c3b8 Olga Brani

9155 e4a6c3b8 Olga Brani
    ```javascript
9156 e4a6c3b8 Olga Brani
    DS.RESTAdapter.reopen({
9157 e4a6c3b8 Olga Brani
      headers: {
9158 e4a6c3b8 Olga Brani
        "API_KEY": "secret key",
9159 e4a6c3b8 Olga Brani
        "ANOTHER_HEADER": "Some header value"
9160 e4a6c3b8 Olga Brani
      }
9161 e4a6c3b8 Olga Brani
    });
9162 e4a6c3b8 Olga Brani
    ```
9163 e4a6c3b8 Olga Brani

9164 e4a6c3b8 Olga Brani
    @property headers
9165 e4a6c3b8 Olga Brani
    @type {Object}
9166 e4a6c3b8 Olga Brani
  */
9167 e4a6c3b8 Olga Brani
9168 e4a6c3b8 Olga Brani
  /**
9169 e4a6c3b8 Olga Brani
    Called by the store in order to fetch the JSON for a given
9170 e4a6c3b8 Olga Brani
    type and ID.
9171 e4a6c3b8 Olga Brani

9172 e4a6c3b8 Olga Brani
    The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a
9173 e4a6c3b8 Olga Brani
    promise for the resulting payload.
9174 e4a6c3b8 Olga Brani

9175 e4a6c3b8 Olga Brani
    This method performs an HTTP `GET` request with the id provided as part of the querystring.
9176 e4a6c3b8 Olga Brani

9177 e4a6c3b8 Olga Brani
    @method find
9178 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9179 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9180 e4a6c3b8 Olga Brani
    @param {String} id
9181 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9182 e4a6c3b8 Olga Brani
  */
9183 e4a6c3b8 Olga Brani
  find: function(store, type, id) {
9184 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey, id), 'GET');
9185 e4a6c3b8 Olga Brani
  },
9186 e4a6c3b8 Olga Brani
9187 e4a6c3b8 Olga Brani
  /**
9188 e4a6c3b8 Olga Brani
    Called by the store in order to fetch a JSON array for all
9189 e4a6c3b8 Olga Brani
    of the records for a given type.
9190 e4a6c3b8 Olga Brani

9191 e4a6c3b8 Olga Brani
    The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
9192 e4a6c3b8 Olga Brani
    promise for the resulting payload.
9193 e4a6c3b8 Olga Brani

9194 e4a6c3b8 Olga Brani
    @private
9195 e4a6c3b8 Olga Brani
    @method findAll
9196 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9197 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9198 e4a6c3b8 Olga Brani
    @param {String} sinceToken
9199 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9200 e4a6c3b8 Olga Brani
  */
9201 e4a6c3b8 Olga Brani
  findAll: function(store, type, sinceToken) {
9202 e4a6c3b8 Olga Brani
    var query;
9203 e4a6c3b8 Olga Brani
9204 e4a6c3b8 Olga Brani
    if (sinceToken) {
9205 e4a6c3b8 Olga Brani
      query = { since: sinceToken };
9206 e4a6c3b8 Olga Brani
    }
9207 e4a6c3b8 Olga Brani
9208 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
9209 e4a6c3b8 Olga Brani
  },
9210 e4a6c3b8 Olga Brani
9211 e4a6c3b8 Olga Brani
  /**
9212 e4a6c3b8 Olga Brani
    Called by the store in order to fetch a JSON array for
9213 e4a6c3b8 Olga Brani
    the records that match a particular query.
9214 e4a6c3b8 Olga Brani

9215 e4a6c3b8 Olga Brani
    The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
9216 e4a6c3b8 Olga Brani
    promise for the resulting payload.
9217 e4a6c3b8 Olga Brani

9218 e4a6c3b8 Olga Brani
    The `query` argument is a simple JavaScript object that will be passed directly
9219 e4a6c3b8 Olga Brani
    to the server as parameters.
9220 e4a6c3b8 Olga Brani

9221 e4a6c3b8 Olga Brani
    @private
9222 e4a6c3b8 Olga Brani
    @method findQuery
9223 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9224 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9225 e4a6c3b8 Olga Brani
    @param {Object} query
9226 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9227 e4a6c3b8 Olga Brani
  */
9228 e4a6c3b8 Olga Brani
  findQuery: function(store, type, query) {
9229 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
9230 e4a6c3b8 Olga Brani
  },
9231 e4a6c3b8 Olga Brani
9232 e4a6c3b8 Olga Brani
  /**
9233 e4a6c3b8 Olga Brani
    Called by the store in order to fetch a JSON array for
9234 e4a6c3b8 Olga Brani
    the unloaded records in a has-many relationship that were originally
9235 e4a6c3b8 Olga Brani
    specified as IDs.
9236 e4a6c3b8 Olga Brani

9237 e4a6c3b8 Olga Brani
    For example, if the original payload looks like:
9238 e4a6c3b8 Olga Brani

9239 e4a6c3b8 Olga Brani
    ```js
9240 e4a6c3b8 Olga Brani
    {
9241 e4a6c3b8 Olga Brani
      "id": 1,
9242 e4a6c3b8 Olga Brani
      "title": "Rails is omakase",
9243 e4a6c3b8 Olga Brani
      "comments": [ 1, 2, 3 ]
9244 e4a6c3b8 Olga Brani
    }
9245 e4a6c3b8 Olga Brani
    ```
9246 e4a6c3b8 Olga Brani

9247 e4a6c3b8 Olga Brani
    The IDs will be passed as a URL-encoded Array of IDs, in this form:
9248 e4a6c3b8 Olga Brani

9249 e4a6c3b8 Olga Brani
    ```
9250 e4a6c3b8 Olga Brani
    ids[]=1&ids[]=2&ids[]=3
9251 e4a6c3b8 Olga Brani
    ```
9252 e4a6c3b8 Olga Brani

9253 e4a6c3b8 Olga Brani
    Many servers, such as Rails and PHP, will automatically convert this URL-encoded array
9254 e4a6c3b8 Olga Brani
    into an Array for you on the server-side. If you want to encode the
9255 e4a6c3b8 Olga Brani
    IDs, differently, just override this (one-line) method.
9256 e4a6c3b8 Olga Brani

9257 e4a6c3b8 Olga Brani
    The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
9258 e4a6c3b8 Olga Brani
    promise for the resulting payload.
9259 e4a6c3b8 Olga Brani

9260 e4a6c3b8 Olga Brani
    @method findMany
9261 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9262 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9263 e4a6c3b8 Olga Brani
    @param {Array} ids
9264 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9265 e4a6c3b8 Olga Brani
  */
9266 e4a6c3b8 Olga Brani
  findMany: function(store, type, ids) {
9267 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } });
9268 e4a6c3b8 Olga Brani
  },
9269 e4a6c3b8 Olga Brani
9270 e4a6c3b8 Olga Brani
  /**
9271 e4a6c3b8 Olga Brani
    Called by the store in order to fetch a JSON array for
9272 e4a6c3b8 Olga Brani
    the unloaded records in a has-many relationship that were originally
9273 e4a6c3b8 Olga Brani
    specified as a URL (inside of `links`).
9274 e4a6c3b8 Olga Brani

9275 e4a6c3b8 Olga Brani
    For example, if your original payload looks like this:
9276 e4a6c3b8 Olga Brani

9277 e4a6c3b8 Olga Brani
    ```js
9278 e4a6c3b8 Olga Brani
    {
9279 e4a6c3b8 Olga Brani
      "post": {
9280 e4a6c3b8 Olga Brani
        "id": 1,
9281 e4a6c3b8 Olga Brani
        "title": "Rails is omakase",
9282 e4a6c3b8 Olga Brani
        "links": { "comments": "/posts/1/comments" }
9283 e4a6c3b8 Olga Brani
      }
9284 e4a6c3b8 Olga Brani
    }
9285 e4a6c3b8 Olga Brani
    ```
9286 e4a6c3b8 Olga Brani

9287 e4a6c3b8 Olga Brani
    This method will be called with the parent record and `/posts/1/comments`.
9288 e4a6c3b8 Olga Brani

9289 e4a6c3b8 Olga Brani
    The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL.
9290 e4a6c3b8 Olga Brani
    If the URL is host-relative (starting with a single slash), the
9291 e4a6c3b8 Olga Brani
    request will use the host specified on the adapter (if any).
9292 e4a6c3b8 Olga Brani

9293 e4a6c3b8 Olga Brani
    @method findHasMany
9294 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9295 e4a6c3b8 Olga Brani
    @param {DS.Model} record
9296 e4a6c3b8 Olga Brani
    @param {String} url
9297 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9298 e4a6c3b8 Olga Brani
  */
9299 e4a6c3b8 Olga Brani
  findHasMany: function(store, record, url) {
9300 e4a6c3b8 Olga Brani
    var host = get(this, 'host'),
9301 e4a6c3b8 Olga Brani
        id   = get(record, 'id'),
9302 e4a6c3b8 Olga Brani
        type = record.constructor.typeKey;
9303 e4a6c3b8 Olga Brani
9304 e4a6c3b8 Olga Brani
    if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') {
9305 e4a6c3b8 Olga Brani
      url = host + url;
9306 e4a6c3b8 Olga Brani
    }
9307 e4a6c3b8 Olga Brani
9308 e4a6c3b8 Olga Brani
    return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
9309 e4a6c3b8 Olga Brani
  },
9310 e4a6c3b8 Olga Brani
9311 e4a6c3b8 Olga Brani
  /**
9312 e4a6c3b8 Olga Brani
    Called by the store in order to fetch a JSON array for
9313 e4a6c3b8 Olga Brani
    the unloaded records in a belongs-to relationship that were originally
9314 e4a6c3b8 Olga Brani
    specified as a URL (inside of `links`).
9315 e4a6c3b8 Olga Brani

9316 e4a6c3b8 Olga Brani
    For example, if your original payload looks like this:
9317 e4a6c3b8 Olga Brani

9318 e4a6c3b8 Olga Brani
    ```js
9319 e4a6c3b8 Olga Brani
    {
9320 e4a6c3b8 Olga Brani
      "person": {
9321 e4a6c3b8 Olga Brani
        "id": 1,
9322 e4a6c3b8 Olga Brani
        "name": "Tom Dale",
9323 e4a6c3b8 Olga Brani
        "links": { "group": "/people/1/group" }
9324 e4a6c3b8 Olga Brani
      }
9325 e4a6c3b8 Olga Brani
    }
9326 e4a6c3b8 Olga Brani
    ```
9327 e4a6c3b8 Olga Brani

9328 e4a6c3b8 Olga Brani
    This method will be called with the parent record and `/people/1/group`.
9329 e4a6c3b8 Olga Brani

9330 e4a6c3b8 Olga Brani
    The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL.
9331 e4a6c3b8 Olga Brani

9332 e4a6c3b8 Olga Brani
    @method findBelongsTo
9333 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9334 e4a6c3b8 Olga Brani
    @param {DS.Model} record
9335 e4a6c3b8 Olga Brani
    @param {String} url
9336 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9337 e4a6c3b8 Olga Brani
  */
9338 e4a6c3b8 Olga Brani
  findBelongsTo: function(store, record, url) {
9339 e4a6c3b8 Olga Brani
    var id   = get(record, 'id'),
9340 e4a6c3b8 Olga Brani
        type = record.constructor.typeKey;
9341 e4a6c3b8 Olga Brani
9342 e4a6c3b8 Olga Brani
    return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
9343 e4a6c3b8 Olga Brani
  },
9344 e4a6c3b8 Olga Brani
9345 e4a6c3b8 Olga Brani
  /**
9346 e4a6c3b8 Olga Brani
    Called by the store when a newly created record is
9347 e4a6c3b8 Olga Brani
    saved via the `save` method on a model record instance.
9348 e4a6c3b8 Olga Brani

9349 e4a6c3b8 Olga Brani
    The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request
9350 e4a6c3b8 Olga Brani
    to a URL computed by `buildURL`.
9351 e4a6c3b8 Olga Brani

9352 e4a6c3b8 Olga Brani
    See `serialize` for information on how to customize the serialized form
9353 e4a6c3b8 Olga Brani
    of a record.
9354 e4a6c3b8 Olga Brani

9355 e4a6c3b8 Olga Brani
    @method createRecord
9356 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9357 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9358 e4a6c3b8 Olga Brani
    @param {DS.Model} record
9359 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9360 e4a6c3b8 Olga Brani
  */
9361 e4a6c3b8 Olga Brani
  createRecord: function(store, type, record) {
9362 e4a6c3b8 Olga Brani
    var data = {};
9363 e4a6c3b8 Olga Brani
    var serializer = store.serializerFor(type.typeKey);
9364 e4a6c3b8 Olga Brani
9365 e4a6c3b8 Olga Brani
    serializer.serializeIntoHash(data, type, record, { includeId: true });
9366 e4a6c3b8 Olga Brani
9367 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
9368 e4a6c3b8 Olga Brani
  },
9369 e4a6c3b8 Olga Brani
9370 e4a6c3b8 Olga Brani
  /**
9371 e4a6c3b8 Olga Brani
    Called by the store when an existing record is saved
9372 e4a6c3b8 Olga Brani
    via the `save` method on a model record instance.
9373 e4a6c3b8 Olga Brani

9374 e4a6c3b8 Olga Brani
    The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request
9375 e4a6c3b8 Olga Brani
    to a URL computed by `buildURL`.
9376 e4a6c3b8 Olga Brani

9377 e4a6c3b8 Olga Brani
    See `serialize` for information on how to customize the serialized form
9378 e4a6c3b8 Olga Brani
    of a record.
9379 e4a6c3b8 Olga Brani

9380 e4a6c3b8 Olga Brani
    @method updateRecord
9381 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9382 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9383 e4a6c3b8 Olga Brani
    @param {DS.Model} record
9384 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9385 e4a6c3b8 Olga Brani
  */
9386 e4a6c3b8 Olga Brani
  updateRecord: function(store, type, record) {
9387 e4a6c3b8 Olga Brani
    var data = {};
9388 e4a6c3b8 Olga Brani
    var serializer = store.serializerFor(type.typeKey);
9389 e4a6c3b8 Olga Brani
9390 e4a6c3b8 Olga Brani
    serializer.serializeIntoHash(data, type, record);
9391 e4a6c3b8 Olga Brani
9392 e4a6c3b8 Olga Brani
    var id = get(record, 'id');
9393 e4a6c3b8 Olga Brani
9394 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
9395 e4a6c3b8 Olga Brani
  },
9396 e4a6c3b8 Olga Brani
9397 e4a6c3b8 Olga Brani
  /**
9398 e4a6c3b8 Olga Brani
    Called by the store when a record is deleted.
9399 e4a6c3b8 Olga Brani

9400 e4a6c3b8 Olga Brani
    The `deleteRecord` method  makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`.
9401 e4a6c3b8 Olga Brani

9402 e4a6c3b8 Olga Brani
    @method deleteRecord
9403 e4a6c3b8 Olga Brani
    @param {DS.Store} store
9404 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
9405 e4a6c3b8 Olga Brani
    @param {DS.Model} record
9406 e4a6c3b8 Olga Brani
    @returns {Promise} promise
9407 e4a6c3b8 Olga Brani
  */
9408 e4a6c3b8 Olga Brani
  deleteRecord: function(store, type, record) {
9409 e4a6c3b8 Olga Brani
    var id = get(record, 'id');
9410 e4a6c3b8 Olga Brani
9411 e4a6c3b8 Olga Brani
    return this.ajax(this.buildURL(type.typeKey, id), "DELETE");
9412 e4a6c3b8 Olga Brani
  },
9413 e4a6c3b8 Olga Brani
9414 e4a6c3b8 Olga Brani
  /**
9415 e4a6c3b8 Olga Brani
    Builds a URL for a given type and optional ID.
9416 e4a6c3b8 Olga Brani

9417 e4a6c3b8 Olga Brani
    By default, it pluralizes the type's name (for example, 'post'
9418 e4a6c3b8 Olga Brani
    becomes 'posts' and 'person' becomes 'people'). To override the
9419 e4a6c3b8 Olga Brani
    pluralization see [pathForType](#method_pathForType).
9420 e4a6c3b8 Olga Brani

9421 e4a6c3b8 Olga Brani
    If an ID is specified, it adds the ID to the path generated
9422 e4a6c3b8 Olga Brani
    for the type, separated by a `/`.
9423 e4a6c3b8 Olga Brani

9424 e4a6c3b8 Olga Brani
    @method buildURL
9425 e4a6c3b8 Olga Brani
    @param {String} type
9426 e4a6c3b8 Olga Brani
    @param {String} id
9427 e4a6c3b8 Olga Brani
    @returns {String} url
9428 e4a6c3b8 Olga Brani
  */
9429 e4a6c3b8 Olga Brani
  buildURL: function(type, id) {
9430 e4a6c3b8 Olga Brani
    var url = [],
9431 e4a6c3b8 Olga Brani
        host = get(this, 'host'),
9432 e4a6c3b8 Olga Brani
        prefix = this.urlPrefix();
9433 e4a6c3b8 Olga Brani
9434 e4a6c3b8 Olga Brani
    if (type) { url.push(this.pathForType(type)); }
9435 e4a6c3b8 Olga Brani
    if (id) { url.push(id); }
9436 e4a6c3b8 Olga Brani
9437 e4a6c3b8 Olga Brani
    if (prefix) { url.unshift(prefix); }
9438 e4a6c3b8 Olga Brani
9439 e4a6c3b8 Olga Brani
    url = url.join('/');
9440 e4a6c3b8 Olga Brani
    if (!host && url) { url = '/' + url; }
9441 e4a6c3b8 Olga Brani
9442 e4a6c3b8 Olga Brani
    return url;
9443 e4a6c3b8 Olga Brani
  },
9444 e4a6c3b8 Olga Brani
9445 e4a6c3b8 Olga Brani
  /**
9446 e4a6c3b8 Olga Brani
    @method urlPrefix
9447 e4a6c3b8 Olga Brani
    @private
9448 e4a6c3b8 Olga Brani
    @param {String} path
9449 e4a6c3b8 Olga Brani
    @param {String} parentUrl
9450 e4a6c3b8 Olga Brani
    @return {String} urlPrefix
9451 e4a6c3b8 Olga Brani
  */
9452 e4a6c3b8 Olga Brani
  urlPrefix: function(path, parentURL) {
9453 e4a6c3b8 Olga Brani
    var host = get(this, 'host'),
9454 e4a6c3b8 Olga Brani
        namespace = get(this, 'namespace'),
9455 e4a6c3b8 Olga Brani
        url = [];
9456 e4a6c3b8 Olga Brani
9457 e4a6c3b8 Olga Brani
    if (path) {
9458 e4a6c3b8 Olga Brani
      // Absolute path
9459 e4a6c3b8 Olga Brani
      if (path.charAt(0) === '/') {
9460 e4a6c3b8 Olga Brani
        if (host) {
9461 e4a6c3b8 Olga Brani
          path = path.slice(1);
9462 e4a6c3b8 Olga Brani
          url.push(host);
9463 e4a6c3b8 Olga Brani
        }
9464 e4a6c3b8 Olga Brani
      // Relative path
9465 e4a6c3b8 Olga Brani
      } else if (!/^http(s)?:\/\//.test(path)) {
9466 e4a6c3b8 Olga Brani
        url.push(parentURL);
9467 e4a6c3b8 Olga Brani
      }
9468 e4a6c3b8 Olga Brani
    } else {
9469 e4a6c3b8 Olga Brani
      if (host) { url.push(host); }
9470 e4a6c3b8 Olga Brani
      if (namespace) { url.push(namespace); }
9471 e4a6c3b8 Olga Brani
    }
9472 e4a6c3b8 Olga Brani
9473 e4a6c3b8 Olga Brani
    if (path) {
9474 e4a6c3b8 Olga Brani
      url.push(path);
9475 e4a6c3b8 Olga Brani
    }
9476 e4a6c3b8 Olga Brani
9477 e4a6c3b8 Olga Brani
    return url.join('/');
9478 e4a6c3b8 Olga Brani
  },
9479 e4a6c3b8 Olga Brani
9480 e4a6c3b8 Olga Brani
  /**
9481 e4a6c3b8 Olga Brani
    Determines the pathname for a given type.
9482 e4a6c3b8 Olga Brani

9483 e4a6c3b8 Olga Brani
    By default, it pluralizes the type's name (for example,
9484 e4a6c3b8 Olga Brani
    'post' becomes 'posts' and 'person' becomes 'people').
9485 e4a6c3b8 Olga Brani

9486 e4a6c3b8 Olga Brani
    ### Pathname customization
9487 e4a6c3b8 Olga Brani

9488 e4a6c3b8 Olga Brani
    For example if you have an object LineItem with an
9489 e4a6c3b8 Olga Brani
    endpoint of "/line_items/".
9490 e4a6c3b8 Olga Brani

9491 e4a6c3b8 Olga Brani
    ```js
9492 e4a6c3b8 Olga Brani
    DS.RESTAdapter.reopen({
9493 e4a6c3b8 Olga Brani
      pathForType: function(type) {
9494 e4a6c3b8 Olga Brani
        var decamelized = Ember.String.decamelize(type);
9495 e4a6c3b8 Olga Brani
        return Ember.String.pluralize(decamelized);
9496 e4a6c3b8 Olga Brani
      };
9497 e4a6c3b8 Olga Brani
    });
9498 e4a6c3b8 Olga Brani
    ```
9499 e4a6c3b8 Olga Brani

9500 e4a6c3b8 Olga Brani
    @method pathForType
9501 e4a6c3b8 Olga Brani
    @param {String} type
9502 e4a6c3b8 Olga Brani
    @returns {String} path
9503 e4a6c3b8 Olga Brani
  **/
9504 e4a6c3b8 Olga Brani
  pathForType: function(type) {
9505 e4a6c3b8 Olga Brani
    return Ember.String.pluralize(type);
9506 e4a6c3b8 Olga Brani
  },
9507 e4a6c3b8 Olga Brani
9508 e4a6c3b8 Olga Brani
  /**
9509 e4a6c3b8 Olga Brani
    Takes an ajax response, and returns a relavant error.
9510 e4a6c3b8 Olga Brani

9511 e4a6c3b8 Olga Brani
    Returning a `DS.InvalidError` from this method will cause the
9512 e4a6c3b8 Olga Brani
    record to transition into the `invalid` state and make the
9513 e4a6c3b8 Olga Brani
    `errors` object available on the record.
9514 e4a6c3b8 Olga Brani

9515 e4a6c3b8 Olga Brani
    ```javascript
9516 e4a6c3b8 Olga Brani
    App.ApplicationAdapter = DS.RESTAdapter.extend({
9517 e4a6c3b8 Olga Brani
      ajaxError: function(jqXHR) {
9518 e4a6c3b8 Olga Brani
        var error = this._super(jqXHR);
9519 e4a6c3b8 Olga Brani

9520 e4a6c3b8 Olga Brani
        if (jqXHR && jqXHR.status === 422) {
9521 e4a6c3b8 Olga Brani
          var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
9522 e4a6c3b8 Olga Brani

9523 e4a6c3b8 Olga Brani
          return new DS.InvalidError(jsonErrors);
9524 e4a6c3b8 Olga Brani
        } else {
9525 e4a6c3b8 Olga Brani
          return error;
9526 e4a6c3b8 Olga Brani
        }
9527 e4a6c3b8 Olga Brani
      }
9528 e4a6c3b8 Olga Brani
    });
9529 e4a6c3b8 Olga Brani
    ```
9530 e4a6c3b8 Olga Brani

9531 e4a6c3b8 Olga Brani
    Note: As a correctness optimization, the default implementation of
9532 e4a6c3b8 Olga Brani
    the `ajaxError` method strips out the `then` method from jquery's
9533 e4a6c3b8 Olga Brani
    ajax response (jqXHR). This is important because the jqXHR's
9534 e4a6c3b8 Olga Brani
    `then` method fulfills the promise with itself resulting in a
9535 e4a6c3b8 Olga Brani
    circular "thenable" chain which may cause problems for some
9536 e4a6c3b8 Olga Brani
    promise libraries.
9537 e4a6c3b8 Olga Brani

9538 e4a6c3b8 Olga Brani
    @method ajaxError
9539 e4a6c3b8 Olga Brani
    @param  {Object} jqXHR
9540 e4a6c3b8 Olga Brani
    @return {Object} jqXHR
9541 e4a6c3b8 Olga Brani
  */
9542 e4a6c3b8 Olga Brani
  ajaxError: function(jqXHR) {
9543 e4a6c3b8 Olga Brani
    if (jqXHR) {
9544 e4a6c3b8 Olga Brani
      jqXHR.then = null;
9545 e4a6c3b8 Olga Brani
    }
9546 e4a6c3b8 Olga Brani
9547 e4a6c3b8 Olga Brani
    return jqXHR;
9548 e4a6c3b8 Olga Brani
  },
9549 e4a6c3b8 Olga Brani
9550 e4a6c3b8 Olga Brani
  /**
9551 e4a6c3b8 Olga Brani
    Takes a URL, an HTTP method and a hash of data, and makes an
9552 e4a6c3b8 Olga Brani
    HTTP request.
9553 e4a6c3b8 Olga Brani

9554 e4a6c3b8 Olga Brani
    When the server responds with a payload, Ember Data will call into `extractSingle`
9555 e4a6c3b8 Olga Brani
    or `extractArray` (depending on whether the original query was for one record or
9556 e4a6c3b8 Olga Brani
    many records).
9557 e4a6c3b8 Olga Brani

9558 e4a6c3b8 Olga Brani
    By default, `ajax` method has the following behavior:
9559 e4a6c3b8 Olga Brani

9560 e4a6c3b8 Olga Brani
    * It sets the response `dataType` to `"json"`
9561 e4a6c3b8 Olga Brani
    * If the HTTP method is not `"GET"`, it sets the `Content-Type` to be
9562 e4a6c3b8 Olga Brani
      `application/json; charset=utf-8`
9563 e4a6c3b8 Olga Brani
    * If the HTTP method is not `"GET"`, it stringifies the data passed in. The
9564 e4a6c3b8 Olga Brani
      data is the serialized record in the case of a save.
9565 e4a6c3b8 Olga Brani
    * Registers success and failure handlers.
9566 e4a6c3b8 Olga Brani

9567 e4a6c3b8 Olga Brani
    @method ajax
9568 e4a6c3b8 Olga Brani
    @private
9569 e4a6c3b8 Olga Brani
    @param {String} url
9570 e4a6c3b8 Olga Brani
    @param {String} type The request type GET, POST, PUT, DELETE ect.
9571 e4a6c3b8 Olga Brani
    @param {Object} hash
9572 e4a6c3b8 Olga Brani
    @return {Promise} promise
9573 e4a6c3b8 Olga Brani
  */
9574 e4a6c3b8 Olga Brani
  ajax: function(url, type, hash) {
9575 e4a6c3b8 Olga Brani
    var adapter = this;
9576 e4a6c3b8 Olga Brani
9577 e4a6c3b8 Olga Brani
    return new Ember.RSVP.Promise(function(resolve, reject) {
9578 e4a6c3b8 Olga Brani
      hash = adapter.ajaxOptions(url, type, hash);
9579 e4a6c3b8 Olga Brani
9580 e4a6c3b8 Olga Brani
      hash.success = function(json) {
9581 e4a6c3b8 Olga Brani
        Ember.run(null, resolve, json);
9582 e4a6c3b8 Olga Brani
      };
9583 e4a6c3b8 Olga Brani
9584 e4a6c3b8 Olga Brani
      hash.error = function(jqXHR, textStatus, errorThrown) {
9585 e4a6c3b8 Olga Brani
        Ember.run(null, reject, adapter.ajaxError(jqXHR));
9586 e4a6c3b8 Olga Brani
      };
9587 e4a6c3b8 Olga Brani
9588 e4a6c3b8 Olga Brani
      Ember.$.ajax(hash);
9589 e4a6c3b8 Olga Brani
    }, "DS: RestAdapter#ajax " + type + " to " + url);
9590 e4a6c3b8 Olga Brani
  },
9591 e4a6c3b8 Olga Brani
9592 e4a6c3b8 Olga Brani
  /**
9593 e4a6c3b8 Olga Brani
    @method ajaxOptions
9594 e4a6c3b8 Olga Brani
    @private
9595 e4a6c3b8 Olga Brani
    @param {String} url
9596 e4a6c3b8 Olga Brani
    @param {String} type The request type GET, POST, PUT, DELETE ect.
9597 e4a6c3b8 Olga Brani
    @param {Object} hash
9598 e4a6c3b8 Olga Brani
    @return {Object} hash
9599 e4a6c3b8 Olga Brani
  */
9600 e4a6c3b8 Olga Brani
  ajaxOptions: function(url, type, hash) {
9601 e4a6c3b8 Olga Brani
    hash = hash || {};
9602 e4a6c3b8 Olga Brani
    hash.url = url;
9603 e4a6c3b8 Olga Brani
    hash.type = type;
9604 e4a6c3b8 Olga Brani
    hash.dataType = 'json';
9605 e4a6c3b8 Olga Brani
    hash.context = this;
9606 e4a6c3b8 Olga Brani
9607 e4a6c3b8 Olga Brani
    if (hash.data && type !== 'GET') {
9608 e4a6c3b8 Olga Brani
      hash.contentType = 'application/json; charset=utf-8';
9609 e4a6c3b8 Olga Brani
      hash.data = JSON.stringify(hash.data);
9610 e4a6c3b8 Olga Brani
    }
9611 e4a6c3b8 Olga Brani
9612 e4a6c3b8 Olga Brani
    if (this.headers !== undefined) {
9613 e4a6c3b8 Olga Brani
      var headers = this.headers;
9614 e4a6c3b8 Olga Brani
      hash.beforeSend = function (xhr) {
9615 e4a6c3b8 Olga Brani
        forEach.call(Ember.keys(headers), function(key) {
9616 e4a6c3b8 Olga Brani
          xhr.setRequestHeader(key, headers[key]);
9617 e4a6c3b8 Olga Brani
        });
9618 e4a6c3b8 Olga Brani
      };
9619 e4a6c3b8 Olga Brani
    }
9620 e4a6c3b8 Olga Brani
9621 e4a6c3b8 Olga Brani
9622 e4a6c3b8 Olga Brani
    return hash;
9623 e4a6c3b8 Olga Brani
  }
9624 e4a6c3b8 Olga Brani
9625 e4a6c3b8 Olga Brani
});
9626 e4a6c3b8 Olga Brani
9627 e4a6c3b8 Olga Brani
})();
9628 e4a6c3b8 Olga Brani
9629 e4a6c3b8 Olga Brani
9630 e4a6c3b8 Olga Brani
9631 e4a6c3b8 Olga Brani
(function() {
9632 e4a6c3b8 Olga Brani
/**
9633 e4a6c3b8 Olga Brani
  @module ember-data
9634 e4a6c3b8 Olga Brani
*/
9635 e4a6c3b8 Olga Brani
9636 e4a6c3b8 Olga Brani
})();
9637 e4a6c3b8 Olga Brani
9638 e4a6c3b8 Olga Brani
9639 e4a6c3b8 Olga Brani
9640 e4a6c3b8 Olga Brani
(function() {
9641 e4a6c3b8 Olga Brani
DS.Model.reopen({
9642 e4a6c3b8 Olga Brani
9643 e4a6c3b8 Olga Brani
  /**
9644 e4a6c3b8 Olga Brani
    Provides info about the model for debugging purposes
9645 e4a6c3b8 Olga Brani
    by grouping the properties into more semantic groups.
9646 e4a6c3b8 Olga Brani

9647 e4a6c3b8 Olga Brani
    Meant to be used by debugging tools such as the Chrome Ember Extension.
9648 e4a6c3b8 Olga Brani

9649 e4a6c3b8 Olga Brani
    - Groups all attributes in "Attributes" group.
9650 e4a6c3b8 Olga Brani
    - Groups all belongsTo relationships in "Belongs To" group.
9651 e4a6c3b8 Olga Brani
    - Groups all hasMany relationships in "Has Many" group.
9652 e4a6c3b8 Olga Brani
    - Groups all flags in "Flags" group.
9653 e4a6c3b8 Olga Brani
    - Flags relationship CPs as expensive properties.
9654 e4a6c3b8 Olga Brani

9655 e4a6c3b8 Olga Brani
    @method _debugInfo
9656 e4a6c3b8 Olga Brani
    @for DS.Model
9657 e4a6c3b8 Olga Brani
    @private
9658 e4a6c3b8 Olga Brani
  */
9659 e4a6c3b8 Olga Brani
  _debugInfo: function() {
9660 e4a6c3b8 Olga Brani
    var attributes = ['id'],
9661 e4a6c3b8 Olga Brani
        relationships = { belongsTo: [], hasMany: [] },
9662 e4a6c3b8 Olga Brani
        expensiveProperties = [];
9663 e4a6c3b8 Olga Brani
9664 e4a6c3b8 Olga Brani
    this.eachAttribute(function(name, meta) {
9665 e4a6c3b8 Olga Brani
      attributes.push(name);
9666 e4a6c3b8 Olga Brani
    }, this);
9667 e4a6c3b8 Olga Brani
9668 e4a6c3b8 Olga Brani
    this.eachRelationship(function(name, relationship) {
9669 e4a6c3b8 Olga Brani
      relationships[relationship.kind].push(name);
9670 e4a6c3b8 Olga Brani
      expensiveProperties.push(name);
9671 e4a6c3b8 Olga Brani
    });
9672 e4a6c3b8 Olga Brani
9673 e4a6c3b8 Olga Brani
    var groups = [
9674 e4a6c3b8 Olga Brani
      {
9675 e4a6c3b8 Olga Brani
        name: 'Attributes',
9676 e4a6c3b8 Olga Brani
        properties: attributes,
9677 e4a6c3b8 Olga Brani
        expand: true
9678 e4a6c3b8 Olga Brani
      },
9679 e4a6c3b8 Olga Brani
      {
9680 e4a6c3b8 Olga Brani
        name: 'Belongs To',
9681 e4a6c3b8 Olga Brani
        properties: relationships.belongsTo,
9682 e4a6c3b8 Olga Brani
        expand: true
9683 e4a6c3b8 Olga Brani
      },
9684 e4a6c3b8 Olga Brani
      {
9685 e4a6c3b8 Olga Brani
        name: 'Has Many',
9686 e4a6c3b8 Olga Brani
        properties: relationships.hasMany,
9687 e4a6c3b8 Olga Brani
        expand: true
9688 e4a6c3b8 Olga Brani
      },
9689 e4a6c3b8 Olga Brani
      {
9690 e4a6c3b8 Olga Brani
        name: 'Flags',
9691 e4a6c3b8 Olga Brani
        properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid']
9692 e4a6c3b8 Olga Brani
      }
9693 e4a6c3b8 Olga Brani
    ];
9694 e4a6c3b8 Olga Brani
9695 e4a6c3b8 Olga Brani
    return {
9696 e4a6c3b8 Olga Brani
      propertyInfo: {
9697 e4a6c3b8 Olga Brani
        // include all other mixins / properties (not just the grouped ones)
9698 e4a6c3b8 Olga Brani
        includeOtherProperties: true,
9699 e4a6c3b8 Olga Brani
        groups: groups,
9700 e4a6c3b8 Olga Brani
        // don't pre-calculate unless cached
9701 e4a6c3b8 Olga Brani
        expensiveProperties: expensiveProperties
9702 e4a6c3b8 Olga Brani
      }
9703 e4a6c3b8 Olga Brani
    };
9704 e4a6c3b8 Olga Brani
  }
9705 e4a6c3b8 Olga Brani
9706 e4a6c3b8 Olga Brani
});
9707 e4a6c3b8 Olga Brani
9708 e4a6c3b8 Olga Brani
})();
9709 e4a6c3b8 Olga Brani
9710 e4a6c3b8 Olga Brani
9711 e4a6c3b8 Olga Brani
9712 e4a6c3b8 Olga Brani
(function() {
9713 e4a6c3b8 Olga Brani
/**
9714 e4a6c3b8 Olga Brani
  @module ember-data
9715 e4a6c3b8 Olga Brani
*/
9716 e4a6c3b8 Olga Brani
9717 e4a6c3b8 Olga Brani
})();
9718 e4a6c3b8 Olga Brani
9719 e4a6c3b8 Olga Brani
9720 e4a6c3b8 Olga Brani
9721 e4a6c3b8 Olga Brani
(function() {
9722 e4a6c3b8 Olga Brani
/**
9723 e4a6c3b8 Olga Brani
  Ember Data
9724 e4a6c3b8 Olga Brani

9725 e4a6c3b8 Olga Brani
  @module ember-data
9726 e4a6c3b8 Olga Brani
  @main ember-data
9727 e4a6c3b8 Olga Brani
*/
9728 e4a6c3b8 Olga Brani
9729 e4a6c3b8 Olga Brani
})();
9730 e4a6c3b8 Olga Brani
9731 e4a6c3b8 Olga Brani
(function() {
9732 e4a6c3b8 Olga Brani
Ember.String.pluralize = function(word) {
9733 e4a6c3b8 Olga Brani
  return Ember.Inflector.inflector.pluralize(word);
9734 e4a6c3b8 Olga Brani
};
9735 e4a6c3b8 Olga Brani
9736 e4a6c3b8 Olga Brani
Ember.String.singularize = function(word) {
9737 e4a6c3b8 Olga Brani
  return Ember.Inflector.inflector.singularize(word);
9738 e4a6c3b8 Olga Brani
};
9739 e4a6c3b8 Olga Brani
9740 e4a6c3b8 Olga Brani
})();
9741 e4a6c3b8 Olga Brani
9742 e4a6c3b8 Olga Brani
9743 e4a6c3b8 Olga Brani
9744 e4a6c3b8 Olga Brani
(function() {
9745 e4a6c3b8 Olga Brani
var BLANK_REGEX = /^\s*$/;
9746 e4a6c3b8 Olga Brani
9747 e4a6c3b8 Olga Brani
function loadUncountable(rules, uncountable) {
9748 e4a6c3b8 Olga Brani
  for (var i = 0, length = uncountable.length; i < length; i++) {
9749 e4a6c3b8 Olga Brani
    rules.uncountable[uncountable[i].toLowerCase()] = true;
9750 e4a6c3b8 Olga Brani
  }
9751 e4a6c3b8 Olga Brani
}
9752 e4a6c3b8 Olga Brani
9753 e4a6c3b8 Olga Brani
function loadIrregular(rules, irregularPairs) {
9754 e4a6c3b8 Olga Brani
  var pair;
9755 e4a6c3b8 Olga Brani
9756 e4a6c3b8 Olga Brani
  for (var i = 0, length = irregularPairs.length; i < length; i++) {
9757 e4a6c3b8 Olga Brani
    pair = irregularPairs[i];
9758 e4a6c3b8 Olga Brani
9759 e4a6c3b8 Olga Brani
    rules.irregular[pair[0].toLowerCase()] = pair[1];
9760 e4a6c3b8 Olga Brani
    rules.irregularInverse[pair[1].toLowerCase()] = pair[0];
9761 e4a6c3b8 Olga Brani
  }
9762 e4a6c3b8 Olga Brani
}
9763 e4a6c3b8 Olga Brani
9764 e4a6c3b8 Olga Brani
/**
9765 e4a6c3b8 Olga Brani
  Inflector.Ember provides a mechanism for supplying inflection rules for your
9766 e4a6c3b8 Olga Brani
  application. Ember includes a default set of inflection rules, and provides an
9767 e4a6c3b8 Olga Brani
  API for providing additional rules.
9768 e4a6c3b8 Olga Brani

9769 e4a6c3b8 Olga Brani
  Examples:
9770 e4a6c3b8 Olga Brani

9771 e4a6c3b8 Olga Brani
  Creating an inflector with no rules.
9772 e4a6c3b8 Olga Brani

9773 e4a6c3b8 Olga Brani
  ```js
9774 e4a6c3b8 Olga Brani
  var inflector = new Ember.Inflector();
9775 e4a6c3b8 Olga Brani
  ```
9776 e4a6c3b8 Olga Brani

9777 e4a6c3b8 Olga Brani
  Creating an inflector with the default ember ruleset.
9778 e4a6c3b8 Olga Brani

9779 e4a6c3b8 Olga Brani
  ```js
9780 e4a6c3b8 Olga Brani
  var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
9781 e4a6c3b8 Olga Brani

9782 e4a6c3b8 Olga Brani
  inflector.pluralize('cow') //=> 'kine'
9783 e4a6c3b8 Olga Brani
  inflector.singularize('kine') //=> 'cow'
9784 e4a6c3b8 Olga Brani
  ```
9785 e4a6c3b8 Olga Brani

9786 e4a6c3b8 Olga Brani
  Creating an inflector and adding rules later.
9787 e4a6c3b8 Olga Brani

9788 e4a6c3b8 Olga Brani
  ```javascript
9789 e4a6c3b8 Olga Brani
  var inflector = Ember.Inflector.inflector;
9790 e4a6c3b8 Olga Brani

9791 e4a6c3b8 Olga Brani
  inflector.pluralize('advice') // => 'advices'
9792 e4a6c3b8 Olga Brani
  inflector.uncountable('advice');
9793 e4a6c3b8 Olga Brani
  inflector.pluralize('advice') // => 'advice'
9794 e4a6c3b8 Olga Brani

9795 e4a6c3b8 Olga Brani
  inflector.pluralize('formula') // => 'formulas'
9796 e4a6c3b8 Olga Brani
  inflector.irregular('formula', 'formulae');
9797 e4a6c3b8 Olga Brani
  inflector.pluralize('formula') // => 'formulae'
9798 e4a6c3b8 Olga Brani

9799 e4a6c3b8 Olga Brani
  // you would not need to add these as they are the default rules
9800 e4a6c3b8 Olga Brani
  inflector.plural(/$/, 's');
9801 e4a6c3b8 Olga Brani
  inflector.singular(/s$/i, '');
9802 e4a6c3b8 Olga Brani
  ```
9803 e4a6c3b8 Olga Brani

9804 e4a6c3b8 Olga Brani
  Creating an inflector with a nondefault ruleset.
9805 e4a6c3b8 Olga Brani

9806 e4a6c3b8 Olga Brani
  ```javascript
9807 e4a6c3b8 Olga Brani
  var rules = {
9808 e4a6c3b8 Olga Brani
    plurals:  [ /$/, 's' ],
9809 e4a6c3b8 Olga Brani
    singular: [ /\s$/, '' ],
9810 e4a6c3b8 Olga Brani
    irregularPairs: [
9811 e4a6c3b8 Olga Brani
      [ 'cow', 'kine' ]
9812 e4a6c3b8 Olga Brani
    ],
9813 e4a6c3b8 Olga Brani
    uncountable: [ 'fish' ]
9814 e4a6c3b8 Olga Brani
  };
9815 e4a6c3b8 Olga Brani

9816 e4a6c3b8 Olga Brani
  var inflector = new Ember.Inflector(rules);
9817 e4a6c3b8 Olga Brani
  ```
9818 e4a6c3b8 Olga Brani

9819 e4a6c3b8 Olga Brani
  @class Inflector
9820 e4a6c3b8 Olga Brani
  @namespace Ember
9821 e4a6c3b8 Olga Brani
*/
9822 e4a6c3b8 Olga Brani
function Inflector(ruleSet) {
9823 e4a6c3b8 Olga Brani
  ruleSet = ruleSet || {};
9824 e4a6c3b8 Olga Brani
  ruleSet.uncountable = ruleSet.uncountable || {};
9825 e4a6c3b8 Olga Brani
  ruleSet.irregularPairs = ruleSet.irregularPairs || {};
9826 e4a6c3b8 Olga Brani
9827 e4a6c3b8 Olga Brani
  var rules = this.rules = {
9828 e4a6c3b8 Olga Brani
    plurals:  ruleSet.plurals || [],
9829 e4a6c3b8 Olga Brani
    singular: ruleSet.singular || [],
9830 e4a6c3b8 Olga Brani
    irregular: {},
9831 e4a6c3b8 Olga Brani
    irregularInverse: {},
9832 e4a6c3b8 Olga Brani
    uncountable: {}
9833 e4a6c3b8 Olga Brani
  };
9834 e4a6c3b8 Olga Brani
9835 e4a6c3b8 Olga Brani
  loadUncountable(rules, ruleSet.uncountable);
9836 e4a6c3b8 Olga Brani
  loadIrregular(rules, ruleSet.irregularPairs);
9837 e4a6c3b8 Olga Brani
}
9838 e4a6c3b8 Olga Brani
9839 e4a6c3b8 Olga Brani
Inflector.prototype = {
9840 e4a6c3b8 Olga Brani
  /**
9841 e4a6c3b8 Olga Brani
    @method plural
9842 e4a6c3b8 Olga Brani
    @param {RegExp} regex
9843 e4a6c3b8 Olga Brani
    @param {String} string
9844 e4a6c3b8 Olga Brani
  */
9845 e4a6c3b8 Olga Brani
  plural: function(regex, string) {
9846 e4a6c3b8 Olga Brani
    this.rules.plurals.push([regex, string.toLowerCase()]);
9847 e4a6c3b8 Olga Brani
  },
9848 e4a6c3b8 Olga Brani
9849 e4a6c3b8 Olga Brani
  /**
9850 e4a6c3b8 Olga Brani
    @method singular
9851 e4a6c3b8 Olga Brani
    @param {RegExp} regex
9852 e4a6c3b8 Olga Brani
    @param {String} string
9853 e4a6c3b8 Olga Brani
  */
9854 e4a6c3b8 Olga Brani
  singular: function(regex, string) {
9855 e4a6c3b8 Olga Brani
    this.rules.singular.push([regex, string.toLowerCase()]);
9856 e4a6c3b8 Olga Brani
  },
9857 e4a6c3b8 Olga Brani
9858 e4a6c3b8 Olga Brani
  /**
9859 e4a6c3b8 Olga Brani
    @method uncountable
9860 e4a6c3b8 Olga Brani
    @param {String} regex
9861 e4a6c3b8 Olga Brani
  */
9862 e4a6c3b8 Olga Brani
  uncountable: function(string) {
9863 e4a6c3b8 Olga Brani
    loadUncountable(this.rules, [string.toLowerCase()]);
9864 e4a6c3b8 Olga Brani
  },
9865 e4a6c3b8 Olga Brani
9866 e4a6c3b8 Olga Brani
  /**
9867 e4a6c3b8 Olga Brani
    @method irregular
9868 e4a6c3b8 Olga Brani
    @param {String} singular
9869 e4a6c3b8 Olga Brani
    @param {String} plural
9870 e4a6c3b8 Olga Brani
  */
9871 e4a6c3b8 Olga Brani
  irregular: function (singular, plural) {
9872 e4a6c3b8 Olga Brani
    loadIrregular(this.rules, [[singular, plural]]);
9873 e4a6c3b8 Olga Brani
  },
9874 e4a6c3b8 Olga Brani
9875 e4a6c3b8 Olga Brani
  /**
9876 e4a6c3b8 Olga Brani
    @method pluralize
9877 e4a6c3b8 Olga Brani
    @param {String} word
9878 e4a6c3b8 Olga Brani
  */
9879 e4a6c3b8 Olga Brani
  pluralize: function(word) {
9880 e4a6c3b8 Olga Brani
    return this.inflect(word, this.rules.plurals, this.rules.irregular);
9881 e4a6c3b8 Olga Brani
  },
9882 e4a6c3b8 Olga Brani
9883 e4a6c3b8 Olga Brani
  /**
9884 e4a6c3b8 Olga Brani
    @method singularize
9885 e4a6c3b8 Olga Brani
    @param {String} word
9886 e4a6c3b8 Olga Brani
  */
9887 e4a6c3b8 Olga Brani
  singularize: function(word) {
9888 e4a6c3b8 Olga Brani
    return this.inflect(word, this.rules.singular,  this.rules.irregularInverse);
9889 e4a6c3b8 Olga Brani
  },
9890 e4a6c3b8 Olga Brani
9891 e4a6c3b8 Olga Brani
  /**
9892 e4a6c3b8 Olga Brani
    @protected
9893 e4a6c3b8 Olga Brani

9894 e4a6c3b8 Olga Brani
    @method inflect
9895 e4a6c3b8 Olga Brani
    @param {String} word
9896 e4a6c3b8 Olga Brani
    @param {Object} typeRules
9897 e4a6c3b8 Olga Brani
    @param {Object} irregular
9898 e4a6c3b8 Olga Brani
  */
9899 e4a6c3b8 Olga Brani
  inflect: function(word, typeRules, irregular) {
9900 e4a6c3b8 Olga Brani
    var inflection, substitution, result, lowercase, isBlank,
9901 e4a6c3b8 Olga Brani
    isUncountable, isIrregular, isIrregularInverse, rule;
9902 e4a6c3b8 Olga Brani
9903 e4a6c3b8 Olga Brani
    isBlank = BLANK_REGEX.test(word);
9904 e4a6c3b8 Olga Brani
9905 e4a6c3b8 Olga Brani
    if (isBlank) {
9906 e4a6c3b8 Olga Brani
      return word;
9907 e4a6c3b8 Olga Brani
    }
9908 e4a6c3b8 Olga Brani
9909 e4a6c3b8 Olga Brani
    lowercase = word.toLowerCase();
9910 e4a6c3b8 Olga Brani
9911 e4a6c3b8 Olga Brani
    isUncountable = this.rules.uncountable[lowercase];
9912 e4a6c3b8 Olga Brani
9913 e4a6c3b8 Olga Brani
    if (isUncountable) {
9914 e4a6c3b8 Olga Brani
      return word;
9915 e4a6c3b8 Olga Brani
    }
9916 e4a6c3b8 Olga Brani
9917 e4a6c3b8 Olga Brani
    isIrregular = irregular && irregular[lowercase];
9918 e4a6c3b8 Olga Brani
9919 e4a6c3b8 Olga Brani
    if (isIrregular) {
9920 e4a6c3b8 Olga Brani
      return isIrregular;
9921 e4a6c3b8 Olga Brani
    }
9922 e4a6c3b8 Olga Brani
9923 e4a6c3b8 Olga Brani
    for (var i = typeRules.length, min = 0; i > min; i--) {
9924 e4a6c3b8 Olga Brani
       inflection = typeRules[i-1];
9925 e4a6c3b8 Olga Brani
       rule = inflection[0];
9926 e4a6c3b8 Olga Brani
9927 e4a6c3b8 Olga Brani
      if (rule.test(word)) {
9928 e4a6c3b8 Olga Brani
        break;
9929 e4a6c3b8 Olga Brani
      }
9930 e4a6c3b8 Olga Brani
    }
9931 e4a6c3b8 Olga Brani
9932 e4a6c3b8 Olga Brani
    inflection = inflection || [];
9933 e4a6c3b8 Olga Brani
9934 e4a6c3b8 Olga Brani
    rule = inflection[0];
9935 e4a6c3b8 Olga Brani
    substitution = inflection[1];
9936 e4a6c3b8 Olga Brani
9937 e4a6c3b8 Olga Brani
    result = word.replace(rule, substitution);
9938 e4a6c3b8 Olga Brani
9939 e4a6c3b8 Olga Brani
    return result;
9940 e4a6c3b8 Olga Brani
  }
9941 e4a6c3b8 Olga Brani
};
9942 e4a6c3b8 Olga Brani
9943 e4a6c3b8 Olga Brani
Ember.Inflector = Inflector;
9944 e4a6c3b8 Olga Brani
9945 e4a6c3b8 Olga Brani
})();
9946 e4a6c3b8 Olga Brani
9947 e4a6c3b8 Olga Brani
9948 e4a6c3b8 Olga Brani
9949 e4a6c3b8 Olga Brani
(function() {
9950 e4a6c3b8 Olga Brani
Ember.Inflector.defaultRules = {
9951 e4a6c3b8 Olga Brani
  plurals: [
9952 e4a6c3b8 Olga Brani
    [/$/, 's'],
9953 e4a6c3b8 Olga Brani
    [/s$/i, 's'],
9954 e4a6c3b8 Olga Brani
    [/^(ax|test)is$/i, '$1es'],
9955 e4a6c3b8 Olga Brani
    [/(octop|vir)us$/i, '$1i'],
9956 e4a6c3b8 Olga Brani
    [/(octop|vir)i$/i, '$1i'],
9957 e4a6c3b8 Olga Brani
    [/(alias|status)$/i, '$1es'],
9958 e4a6c3b8 Olga Brani
    [/(bu)s$/i, '$1ses'],
9959 e4a6c3b8 Olga Brani
    [/(buffal|tomat)o$/i, '$1oes'],
9960 e4a6c3b8 Olga Brani
    [/([ti])um$/i, '$1a'],
9961 e4a6c3b8 Olga Brani
    [/([ti])a$/i, '$1a'],
9962 e4a6c3b8 Olga Brani
    [/sis$/i, 'ses'],
9963 e4a6c3b8 Olga Brani
    [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
9964 e4a6c3b8 Olga Brani
    [/(hive)$/i, '$1s'],
9965 e4a6c3b8 Olga Brani
    [/([^aeiouy]|qu)y$/i, '$1ies'],
9966 e4a6c3b8 Olga Brani
    [/(x|ch|ss|sh)$/i, '$1es'],
9967 e4a6c3b8 Olga Brani
    [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
9968 e4a6c3b8 Olga Brani
    [/^(m|l)ouse$/i, '$1ice'],
9969 e4a6c3b8 Olga Brani
    [/^(m|l)ice$/i, '$1ice'],
9970 e4a6c3b8 Olga Brani
    [/^(ox)$/i, '$1en'],
9971 e4a6c3b8 Olga Brani
    [/^(oxen)$/i, '$1'],
9972 e4a6c3b8 Olga Brani
    [/(quiz)$/i, '$1zes']
9973 e4a6c3b8 Olga Brani
  ],
9974 e4a6c3b8 Olga Brani
9975 e4a6c3b8 Olga Brani
  singular: [
9976 e4a6c3b8 Olga Brani
    [/s$/i, ''],
9977 e4a6c3b8 Olga Brani
    [/(ss)$/i, '$1'],
9978 e4a6c3b8 Olga Brani
    [/(n)ews$/i, '$1ews'],
9979 e4a6c3b8 Olga Brani
    [/([ti])a$/i, '$1um'],
9980 e4a6c3b8 Olga Brani
    [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
9981 e4a6c3b8 Olga Brani
    [/(^analy)(sis|ses)$/i, '$1sis'],
9982 e4a6c3b8 Olga Brani
    [/([^f])ves$/i, '$1fe'],
9983 e4a6c3b8 Olga Brani
    [/(hive)s$/i, '$1'],
9984 e4a6c3b8 Olga Brani
    [/(tive)s$/i, '$1'],
9985 e4a6c3b8 Olga Brani
    [/([lr])ves$/i, '$1f'],
9986 e4a6c3b8 Olga Brani
    [/([^aeiouy]|qu)ies$/i, '$1y'],
9987 e4a6c3b8 Olga Brani
    [/(s)eries$/i, '$1eries'],
9988 e4a6c3b8 Olga Brani
    [/(m)ovies$/i, '$1ovie'],
9989 e4a6c3b8 Olga Brani
    [/(x|ch|ss|sh)es$/i, '$1'],
9990 e4a6c3b8 Olga Brani
    [/^(m|l)ice$/i, '$1ouse'],
9991 e4a6c3b8 Olga Brani
    [/(bus)(es)?$/i, '$1'],
9992 e4a6c3b8 Olga Brani
    [/(o)es$/i, '$1'],
9993 e4a6c3b8 Olga Brani
    [/(shoe)s$/i, '$1'],
9994 e4a6c3b8 Olga Brani
    [/(cris|test)(is|es)$/i, '$1is'],
9995 e4a6c3b8 Olga Brani
    [/^(a)x[ie]s$/i, '$1xis'],
9996 e4a6c3b8 Olga Brani
    [/(octop|vir)(us|i)$/i, '$1us'],
9997 e4a6c3b8 Olga Brani
    [/(alias|status)(es)?$/i, '$1'],
9998 e4a6c3b8 Olga Brani
    [/^(ox)en/i, '$1'],
9999 e4a6c3b8 Olga Brani
    [/(vert|ind)ices$/i, '$1ex'],
10000 e4a6c3b8 Olga Brani
    [/(matr)ices$/i, '$1ix'],
10001 e4a6c3b8 Olga Brani
    [/(quiz)zes$/i, '$1'],
10002 e4a6c3b8 Olga Brani
    [/(database)s$/i, '$1']
10003 e4a6c3b8 Olga Brani
  ],
10004 e4a6c3b8 Olga Brani
10005 e4a6c3b8 Olga Brani
  irregularPairs: [
10006 e4a6c3b8 Olga Brani
    ['person', 'people'],
10007 e4a6c3b8 Olga Brani
    ['man', 'men'],
10008 e4a6c3b8 Olga Brani
    ['child', 'children'],
10009 e4a6c3b8 Olga Brani
    ['sex', 'sexes'],
10010 e4a6c3b8 Olga Brani
    ['move', 'moves'],
10011 e4a6c3b8 Olga Brani
    ['cow', 'kine'],
10012 e4a6c3b8 Olga Brani
    ['zombie', 'zombies']
10013 e4a6c3b8 Olga Brani
  ],
10014 e4a6c3b8 Olga Brani
10015 e4a6c3b8 Olga Brani
  uncountable: [
10016 e4a6c3b8 Olga Brani
    'equipment',
10017 e4a6c3b8 Olga Brani
    'information',
10018 e4a6c3b8 Olga Brani
    'rice',
10019 e4a6c3b8 Olga Brani
    'money',
10020 e4a6c3b8 Olga Brani
    'species',
10021 e4a6c3b8 Olga Brani
    'series',
10022 e4a6c3b8 Olga Brani
    'fish',
10023 e4a6c3b8 Olga Brani
    'sheep',
10024 e4a6c3b8 Olga Brani
    'jeans',
10025 e4a6c3b8 Olga Brani
    'police'
10026 e4a6c3b8 Olga Brani
  ]
10027 e4a6c3b8 Olga Brani
};
10028 e4a6c3b8 Olga Brani
10029 e4a6c3b8 Olga Brani
})();
10030 e4a6c3b8 Olga Brani
10031 e4a6c3b8 Olga Brani
10032 e4a6c3b8 Olga Brani
10033 e4a6c3b8 Olga Brani
(function() {
10034 e4a6c3b8 Olga Brani
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
10035 e4a6c3b8 Olga Brani
  /**
10036 e4a6c3b8 Olga Brani
    See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
10037 e4a6c3b8 Olga Brani

10038 e4a6c3b8 Olga Brani
    @method pluralize
10039 e4a6c3b8 Olga Brani
    @for String
10040 e4a6c3b8 Olga Brani
  */
10041 e4a6c3b8 Olga Brani
  String.prototype.pluralize = function() {
10042 e4a6c3b8 Olga Brani
    return Ember.String.pluralize(this);
10043 e4a6c3b8 Olga Brani
  };
10044 e4a6c3b8 Olga Brani
10045 e4a6c3b8 Olga Brani
  /**
10046 e4a6c3b8 Olga Brani
    See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
10047 e4a6c3b8 Olga Brani

10048 e4a6c3b8 Olga Brani
    @method singularize
10049 e4a6c3b8 Olga Brani
    @for String
10050 e4a6c3b8 Olga Brani
  */
10051 e4a6c3b8 Olga Brani
  String.prototype.singularize = function() {
10052 e4a6c3b8 Olga Brani
    return Ember.String.singularize(this);
10053 e4a6c3b8 Olga Brani
  };
10054 e4a6c3b8 Olga Brani
}
10055 e4a6c3b8 Olga Brani
10056 e4a6c3b8 Olga Brani
})();
10057 e4a6c3b8 Olga Brani
10058 e4a6c3b8 Olga Brani
10059 e4a6c3b8 Olga Brani
10060 e4a6c3b8 Olga Brani
(function() {
10061 e4a6c3b8 Olga Brani
Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
10062 e4a6c3b8 Olga Brani
10063 e4a6c3b8 Olga Brani
})();
10064 e4a6c3b8 Olga Brani
10065 e4a6c3b8 Olga Brani
10066 e4a6c3b8 Olga Brani
10067 e4a6c3b8 Olga Brani
(function() {
10068 e4a6c3b8 Olga Brani
10069 e4a6c3b8 Olga Brani
})();
10070 e4a6c3b8 Olga Brani
10071 e4a6c3b8 Olga Brani
(function() {
10072 e4a6c3b8 Olga Brani
/**
10073 e4a6c3b8 Olga Brani
  @module ember-data
10074 e4a6c3b8 Olga Brani
*/
10075 e4a6c3b8 Olga Brani
10076 e4a6c3b8 Olga Brani
var get = Ember.get;
10077 e4a6c3b8 Olga Brani
var forEach = Ember.EnumerableUtils.forEach;
10078 e4a6c3b8 Olga Brani
10079 e4a6c3b8 Olga Brani
DS.ActiveModelSerializer = DS.RESTSerializer.extend({
10080 e4a6c3b8 Olga Brani
  // SERIALIZE
10081 e4a6c3b8 Olga Brani
10082 e4a6c3b8 Olga Brani
  /**
10083 e4a6c3b8 Olga Brani
    Converts camelcased attributes to underscored when serializing.
10084 e4a6c3b8 Olga Brani

10085 e4a6c3b8 Olga Brani
    @method keyForAttribute
10086 e4a6c3b8 Olga Brani
    @param {String} attribute
10087 e4a6c3b8 Olga Brani
    @returns String
10088 e4a6c3b8 Olga Brani
  */
10089 e4a6c3b8 Olga Brani
  keyForAttribute: function(attr) {
10090 e4a6c3b8 Olga Brani
    return Ember.String.decamelize(attr);
10091 e4a6c3b8 Olga Brani
  },
10092 e4a6c3b8 Olga Brani
10093 e4a6c3b8 Olga Brani
  /**
10094 e4a6c3b8 Olga Brani
    Underscores relationship names and appends "_id" or "_ids" when serializing
10095 e4a6c3b8 Olga Brani
    relationship keys.
10096 e4a6c3b8 Olga Brani

10097 e4a6c3b8 Olga Brani
    @method keyForRelationship
10098 e4a6c3b8 Olga Brani
    @param {String} key
10099 e4a6c3b8 Olga Brani
    @param {String} kind
10100 e4a6c3b8 Olga Brani
    @returns String
10101 e4a6c3b8 Olga Brani
  */
10102 e4a6c3b8 Olga Brani
  keyForRelationship: function(key, kind) {
10103 e4a6c3b8 Olga Brani
    key = Ember.String.decamelize(key);
10104 e4a6c3b8 Olga Brani
    if (kind === "belongsTo") {
10105 e4a6c3b8 Olga Brani
      return key + "_id";
10106 e4a6c3b8 Olga Brani
    } else if (kind === "hasMany") {
10107 e4a6c3b8 Olga Brani
      return Ember.String.singularize(key) + "_ids";
10108 e4a6c3b8 Olga Brani
    } else {
10109 e4a6c3b8 Olga Brani
      return key;
10110 e4a6c3b8 Olga Brani
    }
10111 e4a6c3b8 Olga Brani
  },
10112 e4a6c3b8 Olga Brani
10113 e4a6c3b8 Olga Brani
  /**
10114 e4a6c3b8 Olga Brani
    Does not serialize hasMany relationships by default.
10115 e4a6c3b8 Olga Brani
  */
10116 e4a6c3b8 Olga Brani
  serializeHasMany: Ember.K,
10117 e4a6c3b8 Olga Brani
10118 e4a6c3b8 Olga Brani
  /**
10119 e4a6c3b8 Olga Brani
    Underscores the JSON root keys when serializing.
10120 e4a6c3b8 Olga Brani

10121 e4a6c3b8 Olga Brani
    @method serializeIntoHash
10122 e4a6c3b8 Olga Brani
    @param {Object} hash
10123 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
10124 e4a6c3b8 Olga Brani
    @param {DS.Model} record
10125 e4a6c3b8 Olga Brani
    @param {Object} options
10126 e4a6c3b8 Olga Brani
  */
10127 e4a6c3b8 Olga Brani
  serializeIntoHash: function(data, type, record, options) {
10128 e4a6c3b8 Olga Brani
    var root = Ember.String.decamelize(type.typeKey);
10129 e4a6c3b8 Olga Brani
    data[root] = this.serialize(record, options);
10130 e4a6c3b8 Olga Brani
  },
10131 e4a6c3b8 Olga Brani
10132 e4a6c3b8 Olga Brani
  /**
10133 e4a6c3b8 Olga Brani
    Serializes a polymorphic type as a fully capitalized model name.
10134 e4a6c3b8 Olga Brani

10135 e4a6c3b8 Olga Brani
    @method serializePolymorphicType
10136 e4a6c3b8 Olga Brani
    @param {DS.Model} record
10137 e4a6c3b8 Olga Brani
    @param {Object} json
10138 e4a6c3b8 Olga Brani
    @param relationship
10139 e4a6c3b8 Olga Brani
  */
10140 e4a6c3b8 Olga Brani
  serializePolymorphicType: function(record, json, relationship) {
10141 e4a6c3b8 Olga Brani
    var key = relationship.key,
10142 e4a6c3b8 Olga Brani
        belongsTo = get(record, key);
10143 e4a6c3b8 Olga Brani
    key = this.keyForAttribute(key);
10144 e4a6c3b8 Olga Brani
    json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
10145 e4a6c3b8 Olga Brani
  },
10146 e4a6c3b8 Olga Brani
10147 e4a6c3b8 Olga Brani
  // EXTRACT
10148 e4a6c3b8 Olga Brani
10149 e4a6c3b8 Olga Brani
  /**
10150 e4a6c3b8 Olga Brani
    Extracts the model typeKey from underscored root objects.
10151 e4a6c3b8 Olga Brani

10152 e4a6c3b8 Olga Brani
    @method typeForRoot
10153 e4a6c3b8 Olga Brani
    @param {String} root
10154 e4a6c3b8 Olga Brani
    @returns String the model's typeKey
10155 e4a6c3b8 Olga Brani
  */
10156 e4a6c3b8 Olga Brani
  typeForRoot: function(root) {
10157 e4a6c3b8 Olga Brani
    var camelized = Ember.String.camelize(root);
10158 e4a6c3b8 Olga Brani
    return Ember.String.singularize(camelized);
10159 e4a6c3b8 Olga Brani
  },
10160 e4a6c3b8 Olga Brani
10161 e4a6c3b8 Olga Brani
  /**
10162 e4a6c3b8 Olga Brani
    Add extra step to `DS.RESTSerializer.normalize` so links are
10163 e4a6c3b8 Olga Brani
    normalized.
10164 e4a6c3b8 Olga Brani

10165 e4a6c3b8 Olga Brani
    If your payload looks like this
10166 e4a6c3b8 Olga Brani

10167 e4a6c3b8 Olga Brani
    ```js
10168 e4a6c3b8 Olga Brani
    {
10169 e4a6c3b8 Olga Brani
      "post": {
10170 e4a6c3b8 Olga Brani
        "id": 1,
10171 e4a6c3b8 Olga Brani
        "title": "Rails is omakase",
10172 e4a6c3b8 Olga Brani
        "links": { "flagged_comments": "api/comments/flagged" }
10173 e4a6c3b8 Olga Brani
      }
10174 e4a6c3b8 Olga Brani
    }
10175 e4a6c3b8 Olga Brani
    ```
10176 e4a6c3b8 Olga Brani
    The normalized version would look like this
10177 e4a6c3b8 Olga Brani

10178 e4a6c3b8 Olga Brani
    ```js
10179 e4a6c3b8 Olga Brani
    {
10180 e4a6c3b8 Olga Brani
      "post": {
10181 e4a6c3b8 Olga Brani
        "id": 1,
10182 e4a6c3b8 Olga Brani
        "title": "Rails is omakase",
10183 e4a6c3b8 Olga Brani
        "links": { "flaggedComments": "api/comments/flagged" }
10184 e4a6c3b8 Olga Brani
      }
10185 e4a6c3b8 Olga Brani
    }
10186 e4a6c3b8 Olga Brani
    ```
10187 e4a6c3b8 Olga Brani

10188 e4a6c3b8 Olga Brani
    @method normalize
10189 e4a6c3b8 Olga Brani
    @param {subclass of DS.Model} type
10190 e4a6c3b8 Olga Brani
    @param {Object} hash
10191 e4a6c3b8 Olga Brani
    @param {String} prop
10192 e4a6c3b8 Olga Brani
    @returns Object
10193 e4a6c3b8 Olga Brani
  */
10194 e4a6c3b8 Olga Brani
10195 e4a6c3b8 Olga Brani
  normalize: function(type, hash, prop) {
10196 e4a6c3b8 Olga Brani
    this.normalizeLinks(hash);
10197 e4a6c3b8 Olga Brani
10198 e4a6c3b8 Olga Brani
    return this._super(type, hash, prop);
10199 e4a6c3b8 Olga Brani
  },
10200 e4a6c3b8 Olga Brani
10201 e4a6c3b8 Olga Brani
  /**
10202 e4a6c3b8 Olga Brani
    Convert `snake_cased` links  to `camelCase`
10203 e4a6c3b8 Olga Brani

10204 e4a6c3b8 Olga Brani
    @method normalizeLinks
10205 e4a6c3b8 Olga Brani
    @param {Object} hash
10206 e4a6c3b8 Olga Brani
  */
10207 e4a6c3b8 Olga Brani
10208 e4a6c3b8 Olga Brani
  normalizeLinks: function(data){
10209 e4a6c3b8 Olga Brani
    if (data.links) {
10210 e4a6c3b8 Olga Brani
      var links = data.links;
10211 e4a6c3b8 Olga Brani
10212 e4a6c3b8 Olga Brani
      for (var link in links) {
10213 e4a6c3b8 Olga Brani
        var camelizedLink = Ember.String.camelize(link);
10214 e4a6c3b8 Olga Brani
10215 e4a6c3b8 Olga Brani
        if (camelizedLink !== link) {
10216 e4a6c3b8 Olga Brani
          links[camelizedLink] = links[link];
10217 e4a6c3b8 Olga Brani
          delete links[link];
10218 e4a6c3b8 Olga Brani
        }
10219 e4a6c3b8 Olga Brani
      }
10220 e4a6c3b8 Olga Brani
    }
10221 e4a6c3b8 Olga Brani
  },
10222 e4a6c3b8 Olga Brani
10223 e4a6c3b8 Olga Brani
  /**
10224 e4a6c3b8 Olga Brani
    Normalize the polymorphic type from the JSON.
10225 e4a6c3b8 Olga Brani

10226 e4a6c3b8 Olga Brani
    Normalize:
10227 e4a6c3b8 Olga Brani
    ```js
10228 e4a6c3b8 Olga Brani
      {
10229 e4a6c3b8 Olga Brani
        id: "1"
10230 e4a6c3b8 Olga Brani
        minion: { type: "evil_minion", id: "12"}
10231 e4a6c3b8 Olga Brani
      }
10232 e4a6c3b8 Olga Brani
    ```
10233 e4a6c3b8 Olga Brani

10234 e4a6c3b8 Olga Brani
    To:
10235 e4a6c3b8 Olga Brani
    ```js
10236 e4a6c3b8 Olga Brani
      {
10237 e4a6c3b8 Olga Brani
        id: "1"
10238 e4a6c3b8 Olga Brani
        minion: { type: "evilMinion", id: "12"}
10239 e4a6c3b8 Olga Brani
      }
10240 e4a6c3b8 Olga Brani
    ```
10241 e4a6c3b8 Olga Brani

10242 e4a6c3b8 Olga Brani
    @method normalizeRelationships
10243 e4a6c3b8 Olga Brani
    @private
10244 e4a6c3b8 Olga Brani
  */
10245 e4a6c3b8 Olga Brani
  normalizeRelationships: function(type, hash) {
10246 e4a6c3b8 Olga Brani
    var payloadKey, payload;
10247 e4a6c3b8 Olga Brani
10248 e4a6c3b8 Olga Brani
    if (this.keyForRelationship) {
10249 e4a6c3b8 Olga Brani
      type.eachRelationship(function(key, relationship) {
10250 e4a6c3b8 Olga Brani
        if (relationship.options.polymorphic) {
10251 e4a6c3b8 Olga Brani
          payloadKey = this.keyForAttribute(key);
10252 e4a6c3b8 Olga Brani
          payload = hash[payloadKey];
10253 e4a6c3b8 Olga Brani
          if (payload && payload.type) {
10254 e4a6c3b8 Olga Brani
            payload.type = this.typeForRoot(payload.type);
10255 e4a6c3b8 Olga Brani
          } else if (payload && relationship.kind === "hasMany") {
10256 e4a6c3b8 Olga Brani
            var self = this;
10257 e4a6c3b8 Olga Brani
            forEach(payload, function(single) {
10258 e4a6c3b8 Olga Brani
              single.type = self.typeForRoot(single.type);
10259 e4a6c3b8 Olga Brani
            });
10260 e4a6c3b8 Olga Brani
          }
10261 e4a6c3b8 Olga Brani
        } else {
10262 e4a6c3b8 Olga Brani
          payloadKey = this.keyForRelationship(key, relationship.kind);
10263 e4a6c3b8 Olga Brani
          payload = hash[payloadKey];
10264 e4a6c3b8 Olga Brani
        }
10265 e4a6c3b8 Olga Brani
10266 e4a6c3b8 Olga Brani
        hash[key] = payload;
10267 e4a6c3b8 Olga Brani
10268 e4a6c3b8 Olga Brani
        if (key !== payloadKey) {
10269 e4a6c3b8 Olga Brani
          delete hash[payloadKey];
10270 e4a6c3b8 Olga Brani
        }
10271 e4a6c3b8 Olga Brani
      }, this);
10272 e4a6c3b8 Olga Brani
    }
10273 e4a6c3b8 Olga Brani
  }
10274 e4a6c3b8 Olga Brani
});
10275 e4a6c3b8 Olga Brani
10276 e4a6c3b8 Olga Brani
})();
10277 e4a6c3b8 Olga Brani
10278 e4a6c3b8 Olga Brani
10279 e4a6c3b8 Olga Brani
10280 e4a6c3b8 Olga Brani
(function() {
10281 e4a6c3b8 Olga Brani
var get = Ember.get;
10282 e4a6c3b8 Olga Brani
var forEach = Ember.EnumerableUtils.forEach;
10283 e4a6c3b8 Olga Brani
10284 e4a6c3b8 Olga Brani
/**
10285 e4a6c3b8 Olga Brani
  The EmbeddedRecordsMixin allows you to add embedded record support to your
10286 e4a6c3b8 Olga Brani
  serializers.
10287 e4a6c3b8 Olga Brani
  To set up embedded records, you include the mixin into the serializer and then
10288 e4a6c3b8 Olga Brani
  define your embedded relations.
10289 e4a6c3b8 Olga Brani

10290 e4a6c3b8 Olga Brani
  ```js
10291 e4a6c3b8 Olga Brani
  App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
10292 e4a6c3b8 Olga Brani
    attrs: {
10293 e4a6c3b8 Olga Brani
      comments: {embedded: 'always'}
10294 e4a6c3b8 Olga Brani
    }
10295 e4a6c3b8 Olga Brani
  })
10296 e4a6c3b8 Olga Brani
  ```
10297 e4a6c3b8 Olga Brani

10298 e4a6c3b8 Olga Brani
  Currently only `{embedded: 'always'}` records are supported.
10299 e4a6c3b8 Olga Brani

10300 e4a6c3b8 Olga Brani
  @class EmbeddedRecordsMixin
10301 e4a6c3b8 Olga Brani
  @namespace DS
10302 e4a6c3b8 Olga Brani
*/
10303 e4a6c3b8 Olga Brani
DS.EmbeddedRecordsMixin = Ember.Mixin.create({
10304 e4a6c3b8 Olga Brani
10305 e4a6c3b8 Olga Brani
  /**
10306 e4a6c3b8 Olga Brani
    Serialize has-may relationship when it is configured as embedded objects.
10307 e4a6c3b8 Olga Brani

10308 e4a6c3b8 Olga Brani
    @method serializeHasMany
10309 e4a6c3b8 Olga Brani
  */
10310 e4a6c3b8 Olga Brani
  serializeHasMany: function(record, json, relationship) {
10311 e4a6c3b8 Olga Brani
    var key   = relationship.key,
10312 e4a6c3b8 Olga Brani
        attrs = get(this, 'attrs'),
10313 e4a6c3b8 Olga Brani
        embed = attrs && attrs[key] && attrs[key].embedded === 'always';
10314 e4a6c3b8 Olga Brani
10315 e4a6c3b8 Olga Brani
    if (embed) {
10316 e4a6c3b8 Olga Brani
      json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
10317 e4a6c3b8 Olga Brani
        var data = relation.serialize(),
10318 e4a6c3b8 Olga Brani
            primaryKey = get(this, 'primaryKey');
10319 e4a6c3b8 Olga Brani
10320 e4a6c3b8 Olga Brani
        data[primaryKey] = get(relation, primaryKey);
10321 e4a6c3b8 Olga Brani
10322 e4a6c3b8 Olga Brani
        return data;
10323 e4a6c3b8 Olga Brani
      }, this);
10324 e4a6c3b8 Olga Brani
    }
10325 e4a6c3b8 Olga Brani
  },
10326 e4a6c3b8 Olga Brani
10327 e4a6c3b8 Olga Brani
  /**
10328 e4a6c3b8 Olga Brani
    Extract embedded objects out of the payload for a single object
10329 e4a6c3b8 Olga Brani
    and add them as sideloaded objects instead.
10330 e4a6c3b8 Olga Brani

10331 e4a6c3b8 Olga Brani
    @method extractSingle
10332 e4a6c3b8 Olga Brani
  */
10333 e4a6c3b8 Olga Brani
  extractSingle: function(store, primaryType, payload, recordId, requestType) {
10334 e4a6c3b8 Olga Brani
    var root = this.keyForAttribute(primaryType.typeKey),
10335 e4a6c3b8 Olga Brani
        partial = payload[root];
10336 e4a6c3b8 Olga Brani
10337 e4a6c3b8 Olga Brani
    updatePayloadWithEmbedded(store, this, primaryType, partial, payload);
10338 e4a6c3b8 Olga Brani
10339 e4a6c3b8 Olga Brani
    return this._super(store, primaryType, payload, recordId, requestType);
10340 e4a6c3b8 Olga Brani
  },
10341 e4a6c3b8 Olga Brani
10342 e4a6c3b8 Olga Brani
  /**
10343 e4a6c3b8 Olga Brani
    Extract embedded objects out of a standard payload
10344 e4a6c3b8 Olga Brani
    and add them as sideloaded objects instead.
10345 e4a6c3b8 Olga Brani

10346 e4a6c3b8 Olga Brani
    @method extractArray
10347 e4a6c3b8 Olga Brani
  */
10348 e4a6c3b8 Olga Brani
  extractArray: function(store, type, payload) {
10349 e4a6c3b8 Olga Brani
    var root = this.keyForAttribute(type.typeKey),
10350 e4a6c3b8 Olga Brani
        partials = payload[Ember.String.pluralize(root)];
10351 e4a6c3b8 Olga Brani
10352 e4a6c3b8 Olga Brani
    forEach(partials, function(partial) {
10353 e4a6c3b8 Olga Brani
      updatePayloadWithEmbedded(store, this, type, partial, payload);
10354 e4a6c3b8 Olga Brani
    }, this);
10355 e4a6c3b8 Olga Brani
10356 e4a6c3b8 Olga Brani
    return this._super(store, type, payload);
10357 e4a6c3b8 Olga Brani
  }
10358 e4a6c3b8 Olga Brani
});
10359 e4a6c3b8 Olga Brani
10360 e4a6c3b8 Olga Brani
function updatePayloadWithEmbedded(store, serializer, type, partial, payload) {
10361 e4a6c3b8 Olga Brani
  var attrs = get(serializer, 'attrs');
10362 e4a6c3b8 Olga Brani
10363 e4a6c3b8 Olga Brani
  if (!attrs) {
10364 e4a6c3b8 Olga Brani
    return;
10365 e4a6c3b8 Olga Brani
  }
10366 e4a6c3b8 Olga Brani
10367 e4a6c3b8 Olga Brani
  type.eachRelationship(function(key, relationship) {
10368 e4a6c3b8 Olga Brani
    var expandedKey, embeddedTypeKey, attribute, ids,
10369 e4a6c3b8 Olga Brani
        config = attrs[key],
10370 e4a6c3b8 Olga Brani
        serializer = store.serializerFor(relationship.type.typeKey),
10371 e4a6c3b8 Olga Brani
        primaryKey = get(serializer, "primaryKey");
10372 e4a6c3b8 Olga Brani
10373 e4a6c3b8 Olga Brani
    if (relationship.kind !== "hasMany") {
10374 e4a6c3b8 Olga Brani
      return;
10375 e4a6c3b8 Olga Brani
    }
10376 e4a6c3b8 Olga Brani
10377 e4a6c3b8 Olga Brani
    if (config && (config.embedded === 'always' || config.embedded === 'load')) {
10378 e4a6c3b8 Olga Brani
      // underscore forces the embedded records to be side loaded.
10379 e4a6c3b8 Olga Brani
      // it is needed when main type === relationship.type
10380 e4a6c3b8 Olga Brani
      embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey);
10381 e4a6c3b8 Olga Brani
      expandedKey = this.keyForRelationship(key, relationship.kind);
10382 e4a6c3b8 Olga Brani
      attribute  = this.keyForAttribute(key);
10383 e4a6c3b8 Olga Brani
      ids = [];
10384 e4a6c3b8 Olga Brani
10385 e4a6c3b8 Olga Brani
      if (!partial[attribute]) {
10386 e4a6c3b8 Olga Brani
        return;
10387 e4a6c3b8 Olga Brani
      }
10388 e4a6c3b8 Olga Brani
10389 e4a6c3b8 Olga Brani
      payload[embeddedTypeKey] = payload[embeddedTypeKey] || [];
10390 e4a6c3b8 Olga Brani
10391 e4a6c3b8 Olga Brani
      forEach(partial[attribute], function(data) {
10392 e4a6c3b8 Olga Brani
        var embeddedType = store.modelFor(relationship.type.typeKey);
10393 e4a6c3b8 Olga Brani
        updatePayloadWithEmbedded(store, serializer, embeddedType, data, payload);
10394 e4a6c3b8 Olga Brani
        ids.push(data[primaryKey]);
10395 e4a6c3b8 Olga Brani
        payload[embeddedTypeKey].push(data);
10396 e4a6c3b8 Olga Brani
      });
10397 e4a6c3b8 Olga Brani
10398 e4a6c3b8 Olga Brani
      partial[expandedKey] = ids;
10399 e4a6c3b8 Olga Brani
      delete partial[attribute];
10400 e4a6c3b8 Olga Brani
    }
10401 e4a6c3b8 Olga Brani
  }, serializer);
10402 e4a6c3b8 Olga Brani
}
10403 e4a6c3b8 Olga Brani
})();
10404 e4a6c3b8 Olga Brani
10405 e4a6c3b8 Olga Brani
10406 e4a6c3b8 Olga Brani
10407 e4a6c3b8 Olga Brani
(function() {
10408 e4a6c3b8 Olga Brani
/**
10409 e4a6c3b8 Olga Brani
  @module ember-data
10410 e4a6c3b8 Olga Brani
*/
10411 e4a6c3b8 Olga Brani
10412 e4a6c3b8 Olga Brani
var forEach = Ember.EnumerableUtils.forEach;
10413 e4a6c3b8 Olga Brani
10414 e4a6c3b8 Olga Brani
/**
10415 e4a6c3b8 Olga Brani
  The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
10416 e4a6c3b8 Olga Brani
  with a JSON API that uses an underscored naming convention instead of camelcasing.
10417 e4a6c3b8 Olga Brani
  It has been designed to work out of the box with the
10418 e4a6c3b8 Olga Brani
  [active_model_serializers](http://github.com/rails-api/active_model_serializers)
10419 e4a6c3b8 Olga Brani
  Ruby gem.
10420 e4a6c3b8 Olga Brani

10421 e4a6c3b8 Olga Brani
  This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
10422 e4a6c3b8 Olga Brani
  decamelization and pluralization methods to normalize the serialized JSON into a
10423 e4a6c3b8 Olga Brani
  format that is compatible with a conventional Rails backend and Ember Data.
10424 e4a6c3b8 Olga Brani

10425 e4a6c3b8 Olga Brani
  ## JSON Structure
10426 e4a6c3b8 Olga Brani

10427 e4a6c3b8 Olga Brani
  The ActiveModelAdapter expects the JSON returned from your server to follow
10428 e4a6c3b8 Olga Brani
  the REST adapter conventions substituting underscored keys for camelcased ones.
10429 e4a6c3b8 Olga Brani

10430 e4a6c3b8 Olga Brani
  ### Conventional Names
10431 e4a6c3b8 Olga Brani

10432 e4a6c3b8 Olga Brani
  Attribute names in your JSON payload should be the underscored versions of
10433 e4a6c3b8 Olga Brani
  the attributes in your Ember.js models.
10434 e4a6c3b8 Olga Brani

10435 e4a6c3b8 Olga Brani
  For example, if you have a `Person` model:
10436 e4a6c3b8 Olga Brani

10437 e4a6c3b8 Olga Brani
  ```js
10438 e4a6c3b8 Olga Brani
  App.FamousPerson = DS.Model.extend({
10439 e4a6c3b8 Olga Brani
    firstName: DS.attr('string'),
10440 e4a6c3b8 Olga Brani
    lastName: DS.attr('string'),
10441 e4a6c3b8 Olga Brani
    occupation: DS.attr('string')
10442 e4a6c3b8 Olga Brani
  });
10443 e4a6c3b8 Olga Brani
  ```
10444 e4a6c3b8 Olga Brani

10445 e4a6c3b8 Olga Brani
  The JSON returned should look like this:
10446 e4a6c3b8 Olga Brani

10447 e4a6c3b8 Olga Brani
  ```js
10448 e4a6c3b8 Olga Brani
  {
10449 e4a6c3b8 Olga Brani
    "famous_person": {
10450 e4a6c3b8 Olga Brani
      "first_name": "Barack",
10451 e4a6c3b8 Olga Brani
      "last_name": "Obama",
10452 e4a6c3b8 Olga Brani
      "occupation": "President"
10453 e4a6c3b8 Olga Brani
    }
10454 e4a6c3b8 Olga Brani
  }
10455 e4a6c3b8 Olga Brani
  ```
10456 e4a6c3b8 Olga Brani

10457 e4a6c3b8 Olga Brani
  @class ActiveModelAdapter
10458 e4a6c3b8 Olga Brani
  @constructor
10459 e4a6c3b8 Olga Brani
  @namespace DS
10460 e4a6c3b8 Olga Brani
  @extends DS.Adapter
10461 e4a6c3b8 Olga Brani
**/
10462 e4a6c3b8 Olga Brani
10463 e4a6c3b8 Olga Brani
DS.ActiveModelAdapter = DS.RESTAdapter.extend({
10464 e4a6c3b8 Olga Brani
  defaultSerializer: '_ams',
10465 e4a6c3b8 Olga Brani
  /**
10466 e4a6c3b8 Olga Brani
    The ActiveModelAdapter overrides the `pathForType` method to build
10467 e4a6c3b8 Olga Brani
    underscored URLs by decamelizing and pluralizing the object type name.
10468 e4a6c3b8 Olga Brani

10469 e4a6c3b8 Olga Brani
    ```js
10470 e4a6c3b8 Olga Brani
      this.pathForType("famousPerson");
10471 e4a6c3b8 Olga Brani
      //=> "famous_people"
10472 e4a6c3b8 Olga Brani
    ```
10473 e4a6c3b8 Olga Brani

10474 e4a6c3b8 Olga Brani
    @method pathForType
10475 e4a6c3b8 Olga Brani
    @param {String} type
10476 e4a6c3b8 Olga Brani
    @returns String
10477 e4a6c3b8 Olga Brani
  */
10478 e4a6c3b8 Olga Brani
  pathForType: function(type) {
10479 e4a6c3b8 Olga Brani
    var decamelized = Ember.String.decamelize(type);
10480 e4a6c3b8 Olga Brani
    return Ember.String.pluralize(decamelized);
10481 e4a6c3b8 Olga Brani
  },
10482 e4a6c3b8 Olga Brani
10483 e4a6c3b8 Olga Brani
  /**
10484 e4a6c3b8 Olga Brani
    The ActiveModelAdapter overrides the `ajaxError` method
10485 e4a6c3b8 Olga Brani
    to return a DS.InvalidError for all 422 Unprocessable Entity
10486 e4a6c3b8 Olga Brani
    responses.
10487 e4a6c3b8 Olga Brani

10488 e4a6c3b8 Olga Brani
    A 422 HTTP response from the server generally implies that the request
10489 e4a6c3b8 Olga Brani
    was well formed but the API was unable to process it because the
10490 e4a6c3b8 Olga Brani
    content was not semantically correct or meaningful per the API.
10491 e4a6c3b8 Olga Brani

10492 e4a6c3b8 Olga Brani
    For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
10493 e4a6c3b8 Olga Brani
    https://tools.ietf.org/html/rfc4918#section-11.2
10494 e4a6c3b8 Olga Brani

10495 e4a6c3b8 Olga Brani
    @method ajaxError
10496 e4a6c3b8 Olga Brani
    @param jqXHR
10497 e4a6c3b8 Olga Brani
    @returns error
10498 e4a6c3b8 Olga Brani
  */
10499 e4a6c3b8 Olga Brani
  ajaxError: function(jqXHR) {
10500 e4a6c3b8 Olga Brani
    var error = this._super(jqXHR);
10501 e4a6c3b8 Olga Brani
10502 e4a6c3b8 Olga Brani
    if (jqXHR && jqXHR.status === 422) {
10503 e4a6c3b8 Olga Brani
      var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"],
10504 e4a6c3b8 Olga Brani
          errors = {};
10505 e4a6c3b8 Olga Brani
10506 e4a6c3b8 Olga Brani
      forEach(Ember.keys(jsonErrors), function(key) {
10507 e4a6c3b8 Olga Brani
        errors[Ember.String.camelize(key)] = jsonErrors[key];
10508 e4a6c3b8 Olga Brani
      });
10509 e4a6c3b8 Olga Brani
10510 e4a6c3b8 Olga Brani
      return new DS.InvalidError(errors);
10511 e4a6c3b8 Olga Brani
    } else {
10512 e4a6c3b8 Olga Brani
      return error;
10513 e4a6c3b8 Olga Brani
    }
10514 e4a6c3b8 Olga Brani
  }
10515 e4a6c3b8 Olga Brani
});
10516 e4a6c3b8 Olga Brani
10517 e4a6c3b8 Olga Brani
})();
10518 e4a6c3b8 Olga Brani
10519 e4a6c3b8 Olga Brani
10520 e4a6c3b8 Olga Brani
10521 e4a6c3b8 Olga Brani
(function() {
10522 e4a6c3b8 Olga Brani
10523 e4a6c3b8 Olga Brani
})();
10524 e4a6c3b8 Olga Brani
10525 e4a6c3b8 Olga Brani
10526 e4a6c3b8 Olga Brani
10527 e4a6c3b8 Olga Brani
(function() {
10528 e4a6c3b8 Olga Brani
Ember.onLoad('Ember.Application', function(Application) {
10529 e4a6c3b8 Olga Brani
  Application.initializer({
10530 e4a6c3b8 Olga Brani
    name: "activeModelAdapter",
10531 e4a6c3b8 Olga Brani
10532 e4a6c3b8 Olga Brani
    initialize: function(container, application) {
10533 e4a6c3b8 Olga Brani
      application.register('serializer:_ams', DS.ActiveModelSerializer);
10534 e4a6c3b8 Olga Brani
      application.register('adapter:_ams', DS.ActiveModelAdapter);
10535 e4a6c3b8 Olga Brani
    }
10536 e4a6c3b8 Olga Brani
  });
10537 e4a6c3b8 Olga Brani
});
10538 e4a6c3b8 Olga Brani
10539 e4a6c3b8 Olga Brani
})();
10540 e4a6c3b8 Olga Brani
10541 e4a6c3b8 Olga Brani
10542 e4a6c3b8 Olga Brani
10543 e4a6c3b8 Olga Brani
(function() {
10544 e4a6c3b8 Olga Brani
10545 e4a6c3b8 Olga Brani
})();
10546 e4a6c3b8 Olga Brani
10547 e4a6c3b8 Olga Brani
10548 e4a6c3b8 Olga Brani
})();