Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / new_ui / ui / javascripts / ember-data.js @ 0467b6cd

History | View | Annotate | Download (272.3 kB)

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

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

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

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

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

120 d0fe8c12 Athina Bekakou
    Example
121 d0fe8c12 Athina Bekakou

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

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

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

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

165 d0fe8c12 Athina Bekakou
    You can use this method, for example, to normalize underscored keys to camelized
166 d0fe8c12 Athina Bekakou
    or other general-purpose normalizations.
167 d0fe8c12 Athina Bekakou

168 d0fe8c12 Athina Bekakou
    Example
169 d0fe8c12 Athina Bekakou

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

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

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

203 d0fe8c12 Athina Bekakou
    By default, it creates a JSON object with a key for
204 d0fe8c12 Athina Bekakou
    each attribute and belongsTo relationship.
205 d0fe8c12 Athina Bekakou

206 d0fe8c12 Athina Bekakou
    For example, consider this model:
207 d0fe8c12 Athina Bekakou

208 d0fe8c12 Athina Bekakou
    ```javascript
209 d0fe8c12 Athina Bekakou
    App.Comment = DS.Model.extend({
210 d0fe8c12 Athina Bekakou
      title: DS.attr(),
211 d0fe8c12 Athina Bekakou
      body: DS.attr(),
212 d0fe8c12 Athina Bekakou

213 d0fe8c12 Athina Bekakou
      author: DS.belongsTo('user')
214 d0fe8c12 Athina Bekakou
    });
215 d0fe8c12 Athina Bekakou
    ```
216 d0fe8c12 Athina Bekakou

217 d0fe8c12 Athina Bekakou
    The default serialization would create a JSON object like:
218 d0fe8c12 Athina Bekakou

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

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

232 d0fe8c12 Athina Bekakou
    By default, belongs-to relationships are converted into
233 d0fe8c12 Athina Bekakou
    IDs when inserted into the JSON hash.
234 d0fe8c12 Athina Bekakou

235 d0fe8c12 Athina Bekakou
    ## IDs
236 d0fe8c12 Athina Bekakou

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

241 d0fe8c12 Athina Bekakou
    The adapter passes in `includeId: true` when serializing
242 d0fe8c12 Athina Bekakou
    a record for `createRecord`, but not for `updateRecord`.
243 d0fe8c12 Athina Bekakou

244 d0fe8c12 Athina Bekakou
    ## Customization
245 d0fe8c12 Athina Bekakou

246 d0fe8c12 Athina Bekakou
    Your server may expect a different JSON format than the
247 d0fe8c12 Athina Bekakou
    built-in serialization format.
248 d0fe8c12 Athina Bekakou

249 d0fe8c12 Athina Bekakou
    In that case, you can implement `serialize` yourself and
250 d0fe8c12 Athina Bekakou
    return a JSON hash of your choosing.
251 d0fe8c12 Athina Bekakou

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

261 d0fe8c12 Athina Bekakou
        if (options.includeId) {
262 d0fe8c12 Athina Bekakou
          json.POST_ID_ = post.get('id');
263 d0fe8c12 Athina Bekakou
        }
264 d0fe8c12 Athina Bekakou

265 d0fe8c12 Athina Bekakou
        return json;
266 d0fe8c12 Athina Bekakou
      }
267 d0fe8c12 Athina Bekakou
    });
268 d0fe8c12 Athina Bekakou
    ```
269 d0fe8c12 Athina Bekakou

270 d0fe8c12 Athina Bekakou
    ## Customizing an App-Wide Serializer
271 d0fe8c12 Athina Bekakou

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

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

281 d0fe8c12 Athina Bekakou
        record.eachAttribute(function(name) {
282 d0fe8c12 Athina Bekakou
          json[serverAttributeName(name)] = record.get(name);
283 d0fe8c12 Athina Bekakou
        })
284 d0fe8c12 Athina Bekakou

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

291 d0fe8c12 Athina Bekakou
        if (options.includeId) {
292 d0fe8c12 Athina Bekakou
          json.ID_ = record.get('id');
293 d0fe8c12 Athina Bekakou
        }
294 d0fe8c12 Athina Bekakou

295 d0fe8c12 Athina Bekakou
        return json;
296 d0fe8c12 Athina Bekakou
      }
297 d0fe8c12 Athina Bekakou
    });
298 d0fe8c12 Athina Bekakou

299 d0fe8c12 Athina Bekakou
    function serverAttributeName(attribute) {
300 d0fe8c12 Athina Bekakou
      return attribute.underscore().toUpperCase();
301 d0fe8c12 Athina Bekakou
    }
302 d0fe8c12 Athina Bekakou

303 d0fe8c12 Athina Bekakou
    function serverHasManyName(name) {
304 d0fe8c12 Athina Bekakou
      return serverAttributeName(name.singularize()) + "_IDS";
305 d0fe8c12 Athina Bekakou
    }
306 d0fe8c12 Athina Bekakou
    ```
307 d0fe8c12 Athina Bekakou

308 d0fe8c12 Athina Bekakou
    This serializer will generate JSON that looks like this:
309 d0fe8c12 Athina Bekakou

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

318 d0fe8c12 Athina Bekakou
    ## Tweaking the Default JSON
319 d0fe8c12 Athina Bekakou

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

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

329 d0fe8c12 Athina Bekakou
        json.subject = json.title;
330 d0fe8c12 Athina Bekakou
        delete json.title;
331 d0fe8c12 Athina Bekakou

332 d0fe8c12 Athina Bekakou
        return json;
333 d0fe8c12 Athina Bekakou
      }
334 d0fe8c12 Athina Bekakou
    });
335 d0fe8c12 Athina Bekakou
    ```
336 d0fe8c12 Athina Bekakou

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

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

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

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

411 d0fe8c12 Athina Bekakou
   Example
412 d0fe8c12 Athina Bekakou

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

418 d0fe8c12 Athina Bekakou
       var belongsTo = get(record, key);
419 d0fe8c12 Athina Bekakou

420 d0fe8c12 Athina Bekakou
       key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
421 d0fe8c12 Athina Bekakou

422 d0fe8c12 Athina Bekakou
       json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON();
423 d0fe8c12 Athina Bekakou
     }
424 d0fe8c12 Athina Bekakou
   });
425 d0fe8c12 Athina Bekakou
   ```
426 d0fe8c12 Athina Bekakou

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

454 d0fe8c12 Athina Bekakou
   Example
455 d0fe8c12 Athina Bekakou

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

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

491 d0fe8c12 Athina Bekakou
    Example
492 d0fe8c12 Athina Bekakou

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

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

520 d0fe8c12 Athina Bekakou
    This method deletegates to a more specific extract method based on
521 d0fe8c12 Athina Bekakou
    the `requestType`.
522 d0fe8c12 Athina Bekakou

523 d0fe8c12 Athina Bekakou
    Example
524 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

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

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

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

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

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

679 d0fe8c12 Athina Bekakou
    Example
680 d0fe8c12 Athina Bekakou

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

687 d0fe8c12 Athina Bekakou
        return this._super(store, type, payload);
688 d0fe8c12 Athina Bekakou
      },
689 d0fe8c12 Athina Bekakou
    });
690 d0fe8c12 Athina Bekakou
    ```
691 d0fe8c12 Athina Bekakou

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

706 d0fe8c12 Athina Bekakou
    Example
707 d0fe8c12 Athina Bekakou

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

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

733 d0fe8c12 Athina Bekakou
    Example
734 d0fe8c12 Athina Bekakou

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

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

762 d0fe8c12 Athina Bekakou
   Example
763 d0fe8c12 Athina Bekakou

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

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

783 d0fe8c12 Athina Bekakou
   Example
784 d0fe8c12 Athina Bekakou

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

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

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

942 d0fe8c12 Athina Bekakou
  Example
943 d0fe8c12 Athina Bekakou

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

955 d0fe8c12 Athina Bekakou
  Usage
956 d0fe8c12 Athina Bekakou

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

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

973 d0fe8c12 Athina Bekakou
    Example
974 d0fe8c12 Athina Bekakou

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

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

991 d0fe8c12 Athina Bekakou
    Example
992 d0fe8c12 Athina Bekakou

993 d0fe8c12 Athina Bekakou
    ```javascript
994 d0fe8c12 Athina Bekakou
    deserialize: function(serialized) {
995 d0fe8c12 Athina Bekakou
      return empty(serialized) ? null : Number(serialized);
996 d0fe8c12 Athina Bekakou
    }
997 d0fe8c12 Athina Bekakou
    ```
998 d0fe8c12 Athina Bekakou

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

1019 d0fe8c12 Athina Bekakou
  Usage
1020 d0fe8c12 Athina Bekakou

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

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

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

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

1139 d0fe8c12 Athina Bekakou
  Usage
1140 d0fe8c12 Athina Bekakou

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

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

1178 d0fe8c12 Athina Bekakou
  Usage
1179 d0fe8c12 Athina Bekakou

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

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

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

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

1232 d0fe8c12 Athina Bekakou
  For example, imagine an Ember.js application with the following classes:
1233 d0fe8c12 Athina Bekakou

1234 d0fe8c12 Athina Bekakou
  App.Store = DS.Store.extend({
1235 d0fe8c12 Athina Bekakou
    adapter: 'custom'
1236 d0fe8c12 Athina Bekakou
  });
1237 d0fe8c12 Athina Bekakou

1238 d0fe8c12 Athina Bekakou
  App.PostsController = Ember.ArrayController.extend({
1239 d0fe8c12 Athina Bekakou
    // ...
1240 d0fe8c12 Athina Bekakou
  });
1241 d0fe8c12 Athina Bekakou

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

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

1315 d0fe8c12 Athina Bekakou
  © 2011 Colin Snover <http://zetafleet.com>
1316 d0fe8c12 Athina Bekakou

1317 d0fe8c12 Athina Bekakou
  Released under MIT license.
1318 d0fe8c12 Athina Bekakou

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

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

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

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

1423 d0fe8c12 Athina Bekakou
    Example
1424 d0fe8c12 Athina Bekakou

1425 d0fe8c12 Athina Bekakou
    ```javascript
1426 d0fe8c12 Athina Bekakou
    var people = store.all(App.Person);
1427 d0fe8c12 Athina Bekakou
    people.get('isLoaded'); // true
1428 d0fe8c12 Athina Bekakou
    ```
1429 d0fe8c12 Athina Bekakou

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

1437 d0fe8c12 Athina Bekakou
    Example
1438 d0fe8c12 Athina Bekakou

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

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

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

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

1478 d0fe8c12 Athina Bekakou
    Example
1479 d0fe8c12 Athina Bekakou

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

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

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

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

1523 d0fe8c12 Athina Bekakou
    Example
1524 d0fe8c12 Athina Bekakou

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

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

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

1572 d0fe8c12 Athina Bekakou
    Example
1573 d0fe8c12 Athina Bekakou

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

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

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

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

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

1679 d0fe8c12 Athina Bekakou
  The `ManyArray` is instantiated lazily the first time the relationship is
1680 d0fe8c12 Athina Bekakou
  requested.
1681 d0fe8c12 Athina Bekakou

1682 d0fe8c12 Athina Bekakou
  ### Inverses
1683 d0fe8c12 Athina Bekakou

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

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

1693 d0fe8c12 Athina Bekakou
  App.Comment = DS.Model.extend({
1694 d0fe8c12 Athina Bekakou
    post: DS.belongsTo('post')
1695 d0fe8c12 Athina Bekakou
  });
1696 d0fe8c12 Athina Bekakou
  ```
1697 d0fe8c12 Athina Bekakou

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

1704 d0fe8c12 Athina Bekakou
  We call the record to which a relationship belongs the
1705 d0fe8c12 Athina Bekakou
  relationship's _owner_.
1706 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

1958 d0fe8c12 Athina Bekakou
  Define your application's store like this:
1959 d0fe8c12 Athina Bekakou

1960 d0fe8c12 Athina Bekakou
  ```javascript
1961 d0fe8c12 Athina Bekakou
  MyApp.Store = DS.Store.extend();
1962 d0fe8c12 Athina Bekakou
  ```
1963 d0fe8c12 Athina Bekakou

1964 d0fe8c12 Athina Bekakou
  Most Ember.js applications will only have a single `DS.Store` that is
1965 d0fe8c12 Athina Bekakou
  automatically created by their `Ember.Application`.
1966 d0fe8c12 Athina Bekakou

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

1970 d0fe8c12 Athina Bekakou
  ```javascript
1971 d0fe8c12 Athina Bekakou
  var person = store.find('person', 123);
1972 d0fe8c12 Athina Bekakou
  ```
1973 d0fe8c12 Athina Bekakou

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

1977 d0fe8c12 Athina Bekakou
  ```javascript
1978 d0fe8c12 Athina Bekakou
  var person = store.find(App.Person, 123);
1979 d0fe8c12 Athina Bekakou
  ```
1980 d0fe8c12 Athina Bekakou

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

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

1991 d0fe8c12 Athina Bekakou
  You can learn more about writing a custom adapter by reading the `DS.Adapter`
1992 d0fe8c12 Athina Bekakou
  documentation.
1993 d0fe8c12 Athina Bekakou

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

2017 d0fe8c12 Athina Bekakou
    This can be specified as an instance, class, or string.
2018 d0fe8c12 Athina Bekakou

2019 d0fe8c12 Athina Bekakou
    If you want to specify `App.CustomAdapter` as a string, do:
2020 d0fe8c12 Athina Bekakou

2021 d0fe8c12 Athina Bekakou
    ```js
2022 d0fe8c12 Athina Bekakou
    adapter: 'custom'
2023 d0fe8c12 Athina Bekakou
    ```
2024 d0fe8c12 Athina Bekakou

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

2035 d0fe8c12 Athina Bekakou
    The available options are:
2036 d0fe8c12 Athina Bekakou

2037 d0fe8c12 Athina Bekakou
    * `includeId`: `true` if the record's ID should be included in
2038 d0fe8c12 Athina Bekakou
      the JSON representation
2039 d0fe8c12 Athina Bekakou

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

2053 d0fe8c12 Athina Bekakou
    If the supplied `adapter` was a class, or a String property
2054 d0fe8c12 Athina Bekakou
    path resolved to a class, this property will instantiate the
2055 d0fe8c12 Athina Bekakou
    class.
2056 d0fe8c12 Athina Bekakou

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

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

2088 d0fe8c12 Athina Bekakou
    To create a new instance of `App.Post`:
2089 d0fe8c12 Athina Bekakou

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

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

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

2157 d0fe8c12 Athina Bekakou
    Example
2158 d0fe8c12 Athina Bekakou

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

2164 d0fe8c12 Athina Bekakou
    store.deletedRecord(post);
2165 d0fe8c12 Athina Bekakou
    ```
2166 d0fe8c12 Athina Bekakou

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

2178 d0fe8c12 Athina Bekakou
    Example
2179 d0fe8c12 Athina Bekakou

2180 d0fe8c12 Athina Bekakou
    ```javascript
2181 d0fe8c12 Athina Bekakou
    store.find('post', 1).then(function(post) {
2182 d0fe8c12 Athina Bekakou
      store.unloadRecord(post);
2183 d0fe8c12 Athina Bekakou
    });
2184 d0fe8c12 Athina Bekakou
    ```
2185 d0fe8c12 Athina Bekakou

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

2201 d0fe8c12 Athina Bekakou
    ---
2202 d0fe8c12 Athina Bekakou

2203 d0fe8c12 Athina Bekakou
    To find a record by ID, pass the `id` as the second parameter:
2204 d0fe8c12 Athina Bekakou

2205 d0fe8c12 Athina Bekakou
    ```javascript
2206 d0fe8c12 Athina Bekakou
    store.find('person', 1);
2207 d0fe8c12 Athina Bekakou
    ```
2208 d0fe8c12 Athina Bekakou

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

2214 d0fe8c12 Athina Bekakou
    The `find` method will always resolve its promise with the same object for
2215 d0fe8c12 Athina Bekakou
    a given type and `id`.
2216 d0fe8c12 Athina Bekakou

2217 d0fe8c12 Athina Bekakou
    ---
2218 d0fe8c12 Athina Bekakou

2219 d0fe8c12 Athina Bekakou
    To find all records for a type, call `find` with no additional parameters:
2220 d0fe8c12 Athina Bekakou

2221 d0fe8c12 Athina Bekakou
    ```javascript
2222 d0fe8c12 Athina Bekakou
    store.find('person');
2223 d0fe8c12 Athina Bekakou
    ```
2224 d0fe8c12 Athina Bekakou

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

2229 d0fe8c12 Athina Bekakou
    ---
2230 d0fe8c12 Athina Bekakou

2231 d0fe8c12 Athina Bekakou
    To find a record by a query, call `find` with a hash as the second
2232 d0fe8c12 Athina Bekakou
    parameter:
2233 d0fe8c12 Athina Bekakou

2234 d0fe8c12 Athina Bekakou
    ```javascript
2235 d0fe8c12 Athina Bekakou
    store.find(App.Person, { page: 1 });
2236 d0fe8c12 Athina Bekakou
    ```
2237 d0fe8c12 Athina Bekakou

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

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

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

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

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

2327 d0fe8c12 Athina Bekakou
    This method will synchronously return the record if it's available.
2328 d0fe8c12 Athina Bekakou
    Otherwise, it will return null.
2329 d0fe8c12 Athina Bekakou

2330 d0fe8c12 Athina Bekakou
    ```js
2331 d0fe8c12 Athina Bekakou
    var post = store.getById('post', 1);
2332 d0fe8c12 Athina Bekakou
    ```
2333 d0fe8c12 Athina Bekakou

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

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

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

2376 d0fe8c12 Athina Bekakou
    The records are grouped by type to invoke `findMany` on adapters
2377 d0fe8c12 Athina Bekakou
    for each unique type in records.
2378 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

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

2543 d0fe8c12 Athina Bekakou
    This method returns a promise, which is resolved with a `RecordArray`
2544 d0fe8c12 Athina Bekakou
    once the server returns.
2545 d0fe8c12 Athina Bekakou

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

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

2618 d0fe8c12 Athina Bekakou
    Note that because it's just a filter, it will have any locally
2619 d0fe8c12 Athina Bekakou
    created records of the type.
2620 d0fe8c12 Athina Bekakou

2621 d0fe8c12 Athina Bekakou
    Also note that multiple calls to `all` for a given type will always
2622 d0fe8c12 Athina Bekakou
    return the same RecordArray.
2623 d0fe8c12 Athina Bekakou

2624 d0fe8c12 Athina Bekakou
    Example
2625 d0fe8c12 Athina Bekakou

2626 d0fe8c12 Athina Bekakou
    ```javascript
2627 d0fe8c12 Athina Bekakou
    var local_posts = store.all(App.Post);
2628 d0fe8c12 Athina Bekakou
    ```
2629 d0fe8c12 Athina Bekakou

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

2652 d0fe8c12 Athina Bekakou
    ```javascript
2653 d0fe8c12 Athina Bekakou
    store.unloadAll(App.Post);
2654 d0fe8c12 Athina Bekakou
    ```
2655 d0fe8c12 Athina Bekakou

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

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

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

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

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

2692 d0fe8c12 Athina Bekakou
    Example
2693 d0fe8c12 Athina Bekakou

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

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

2737 d0fe8c12 Athina Bekakou
     Example
2738 d0fe8c12 Athina Bekakou

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

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

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

2778 d0fe8c12 Athina Bekakou
    run loop per record.
2779 d0fe8c12 Athina Bekakou

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

2797 d0fe8c12 Athina Bekakou
    It schedules saving to happen at the end of the run loop.
2798 d0fe8c12 Athina Bekakou

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

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

2843 d0fe8c12 Athina Bekakou
    If the data provides a server-generated ID, it will
2844 d0fe8c12 Athina Bekakou
    update the record and the store's indexes.
2845 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

2993 d0fe8c12 Athina Bekakou
    This method expects normalized data:
2994 d0fe8c12 Athina Bekakou

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

3003 d0fe8c12 Athina Bekakou
    For this model:
3004 d0fe8c12 Athina Bekakou

3005 d0fe8c12 Athina Bekakou
    ```js
3006 d0fe8c12 Athina Bekakou
    App.Person = DS.Model.extend({
3007 d0fe8c12 Athina Bekakou
      firstName: DS.attr(),
3008 d0fe8c12 Athina Bekakou
      lastName: DS.attr(),
3009 d0fe8c12 Athina Bekakou

3010 d0fe8c12 Athina Bekakou
      children: DS.hasMany('person')
3011 d0fe8c12 Athina Bekakou
    });
3012 d0fe8c12 Athina Bekakou
    ```
3013 d0fe8c12 Athina Bekakou

3014 d0fe8c12 Athina Bekakou
    To represent the children as IDs:
3015 d0fe8c12 Athina Bekakou

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

3025 d0fe8c12 Athina Bekakou
    To represent the children relationship as a URL:
3026 d0fe8c12 Athina Bekakou

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

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

3042 d0fe8c12 Athina Bekakou
    This method can be used both to push in brand new
3043 d0fe8c12 Athina Bekakou
    records, as well as to update existing records.
3044 d0fe8c12 Athina Bekakou

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

3071 d0fe8c12 Athina Bekakou
    The data will be automatically deserialized using the
3072 d0fe8c12 Athina Bekakou
    serializer for the `type` param.
3073 d0fe8c12 Athina Bekakou

3074 d0fe8c12 Athina Bekakou
    This method can be used both to push in brand new
3075 d0fe8c12 Athina Bekakou
    records, as well as to update existing records.
3076 d0fe8c12 Athina Bekakou

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

3081 d0fe8c12 Athina Bekakou
    ```js
3082 d0fe8c12 Athina Bekakou
    App.ApplicationSerializer = DS.ActiveModelSerializer;
3083 d0fe8c12 Athina Bekakou

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

3093 d0fe8c12 Athina Bekakou
    store.pushPayload('post', pushData);
3094 d0fe8c12 Athina Bekakou
    ```
3095 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

3302 d0fe8c12 Athina Bekakou
    If no `App.ApplicationSerializer` is found, it will fall back
3303 d0fe8c12 Athina Bekakou
    to an instance of `DS.JSONSerializer`.
3304 d0fe8c12 Athina Bekakou

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

3389 d0fe8c12 Athina Bekakou
  For more information see the [Ember.PromiseProxyMixin
3390 d0fe8c12 Athina Bekakou
  documentation](/api/classes/Ember.PromiseProxyMixin.html).
3391 d0fe8c12 Athina Bekakou

3392 d0fe8c12 Athina Bekakou
  Example
3393 d0fe8c12 Athina Bekakou

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

3399 d0fe8c12 Athina Bekakou
  promiseArray.get('length'); // 0
3400 d0fe8c12 Athina Bekakou

3401 d0fe8c12 Athina Bekakou
  promiseArray.then(function() {
3402 d0fe8c12 Athina Bekakou
    promiseArray.get('length'); // 100
3403 d0fe8c12 Athina Bekakou
  });
3404 d0fe8c12 Athina Bekakou
  ```
3405 d0fe8c12 Athina Bekakou

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

3419 d0fe8c12 Athina Bekakou
  For more information see the [Ember.PromiseProxyMixin
3420 d0fe8c12 Athina Bekakou
  documentation](/api/classes/Ember.PromiseProxyMixin.html).
3421 d0fe8c12 Athina Bekakou

3422 d0fe8c12 Athina Bekakou
  Example
3423 d0fe8c12 Athina Bekakou

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

3429 d0fe8c12 Athina Bekakou
  promiseObject.get('name'); // null
3430 d0fe8c12 Athina Bekakou

3431 d0fe8c12 Athina Bekakou
  promiseObject.then(function() {
3432 d0fe8c12 Athina Bekakou
    promiseObject.get('name'); // 'Tomster'
3433 d0fe8c12 Athina Bekakou
  });
3434 d0fe8c12 Athina Bekakou
  ```
3435 d0fe8c12 Athina Bekakou

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

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

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

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

3633 d0fe8c12 Athina Bekakou
  ```javascript
3634 d0fe8c12 Athina Bekakou
  record.get('currentState.stateName');
3635 d0fe8c12 Athina Bekakou
  //=> "root.created.uncommitted"
3636 d0fe8c12 Athina Bekakou
   ```
3637 d0fe8c12 Athina Bekakou

3638 d0fe8c12 Athina Bekakou
  The hierarchy of valid states that ship with ember data looks like
3639 d0fe8c12 Athina Bekakou
  this:
3640 d0fe8c12 Athina Bekakou

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

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

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

3671 d0fe8c12 Athina Bekakou
  ### Events and Flags
3672 d0fe8c12 Athina Bekakou

3673 d0fe8c12 Athina Bekakou
  A state may implement zero or more events and flags.
3674 d0fe8c12 Athina Bekakou

3675 d0fe8c12 Athina Bekakou
  #### Events
3676 d0fe8c12 Athina Bekakou

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

3685 d0fe8c12 Athina Bekakou
  Here's an example implementation of a state with a `myEvent` event handler:
3686 d0fe8c12 Athina Bekakou

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

3695 d0fe8c12 Athina Bekakou
  To trigger this event:
3696 d0fe8c12 Athina Bekakou

3697 d0fe8c12 Athina Bekakou
  ```javascript
3698 d0fe8c12 Athina Bekakou
  record.send('myEvent', 'foo');
3699 d0fe8c12 Athina Bekakou
  //=> "Received myEvent with foo"
3700 d0fe8c12 Athina Bekakou
  ```
3701 d0fe8c12 Athina Bekakou

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

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

3713 d0fe8c12 Athina Bekakou
      * created
3714 d0fe8c12 Athina Bekakou
        * uncommitted <-- currentState
3715 d0fe8c12 Athina Bekakou
        * inFlight
3716 d0fe8c12 Athina Bekakou
      * updated
3717 d0fe8c12 Athina Bekakou
        * inFlight
3718 d0fe8c12 Athina Bekakou

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

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

3728 d0fe8c12 Athina Bekakou
  #### Flags
3729 d0fe8c12 Athina Bekakou

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

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

3741 d0fe8c12 Athina Bekakou
  You can say:
3742 d0fe8c12 Athina Bekakou

3743 d0fe8c12 Athina Bekakou
  ```javascript
3744 d0fe8c12 Athina Bekakou
  if (record.get('isNew') && record.get('isSaving')) {
3745 d0fe8c12 Athina Bekakou
    doSomething();
3746 d0fe8c12 Athina Bekakou
  }
3747 d0fe8c12 Athina Bekakou
  ```
3748 d0fe8c12 Athina Bekakou

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

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

3757 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

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

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

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

4516 d0fe8c12 Athina Bekakou
  The model class that all Ember Data records descend from.
4517 d0fe8c12 Athina Bekakou

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

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

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

4555 d0fe8c12 Athina Bekakou
    Example
4556 d0fe8c12 Athina Bekakou

4557 d0fe8c12 Athina Bekakou
    ```javascript
4558 d0fe8c12 Athina Bekakou
    var record = store.createRecord(App.Model);
4559 d0fe8c12 Athina Bekakou
    record.get('isLoaded'); // true
4560 d0fe8c12 Athina Bekakou

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

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

4577 d0fe8c12 Athina Bekakou
    Example
4578 d0fe8c12 Athina Bekakou

4579 d0fe8c12 Athina Bekakou
    ```javascript
4580 d0fe8c12 Athina Bekakou
    var record = store.createRecord(App.Model);
4581 d0fe8c12 Athina Bekakou
    record.get('isDirty'); // true
4582 d0fe8c12 Athina Bekakou

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

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

4601 d0fe8c12 Athina Bekakou
    Example
4602 d0fe8c12 Athina Bekakou

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

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

4626 d0fe8c12 Athina Bekakou
    Example
4627 d0fe8c12 Athina Bekakou

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

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

4646 d0fe8c12 Athina Bekakou
    Example
4647 d0fe8c12 Athina Bekakou

4648 d0fe8c12 Athina Bekakou
    ```javascript
4649 d0fe8c12 Athina Bekakou
    var record = store.createRecord(App.Model);
4650 d0fe8c12 Athina Bekakou
    record.get('isNew'); // true
4651 d0fe8c12 Athina Bekakou

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

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

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

4678 d0fe8c12 Athina Bekakou
    - `created` The record has been created by the client and not yet saved to the adapter.
4679 d0fe8c12 Athina Bekakou
    - `updated` The record has been updated by the client and not yet saved to the adapter.
4680 d0fe8c12 Athina Bekakou
    - `deleted` The record has been deleted by the client and not yet saved to the adapter.
4681 d0fe8c12 Athina Bekakou

4682 d0fe8c12 Athina Bekakou
    Example
4683 d0fe8c12 Athina Bekakou

4684 d0fe8c12 Athina Bekakou
    ```javascript
4685 d0fe8c12 Athina Bekakou
    var record = store.createRecord(App.Model);
4686 d0fe8c12 Athina Bekakou
    record.get('dirtyType'); // 'created'
4687 d0fe8c12 Athina Bekakou
    ```
4688 d0fe8c12 Athina Bekakou

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

4701 d0fe8c12 Athina Bekakou
    Example
4702 d0fe8c12 Athina Bekakou

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

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

4719 d0fe8c12 Athina Bekakou
    Example
4720 d0fe8c12 Athina Bekakou

4721 d0fe8c12 Athina Bekakou
    ```javascript
4722 d0fe8c12 Athina Bekakou
    record.get('isReloading'); // false
4723 d0fe8c12 Athina Bekakou
    record.reload();
4724 d0fe8c12 Athina Bekakou
    record.get('isReloading'); // true
4725 d0fe8c12 Athina Bekakou
    ```
4726 d0fe8c12 Athina Bekakou

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

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

4751 d0fe8c12 Athina Bekakou
    ```javascript
4752 d0fe8c12 Athina Bekakou
    var record = store.createRecord(App.Model);
4753 d0fe8c12 Athina Bekakou
    record.get('id'); // null
4754 d0fe8c12 Athina Bekakou

4755 d0fe8c12 Athina Bekakou
    store.find('model', 1).then(function(model) {
4756 d0fe8c12 Athina Bekakou
      model.get('id'); // '1'
4757 d0fe8c12 Athina Bekakou
    });
4758 d0fe8c12 Athina Bekakou
    ```
4759 d0fe8c12 Athina Bekakou

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

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

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

4794 d0fe8c12 Athina Bekakou
   `serialize` takes an optional hash as a parameter, currently
4795 d0fe8c12 Athina Bekakou
    supported options are:
4796 d0fe8c12 Athina Bekakou

4797 d0fe8c12 Athina Bekakou
   - `includeId`: `true` if the record's ID should be included in the
4798 d0fe8c12 Athina Bekakou
      JSON representation.
4799 d0fe8c12 Athina Bekakou

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

4813 d0fe8c12 Athina Bekakou
    `toJSON` takes an optional hash as a parameter, currently
4814 d0fe8c12 Athina Bekakou
    supported options are:
4815 d0fe8c12 Athina Bekakou

4816 d0fe8c12 Athina Bekakou
    - `includeId`: `true` if the record's ID should be included in the
4817 d0fe8c12 Athina Bekakou
      JSON representation.
4818 d0fe8c12 Athina Bekakou

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

4832 d0fe8c12 Athina Bekakou
    @event didLoad
4833 d0fe8c12 Athina Bekakou
  */
4834 d0fe8c12 Athina Bekakou
  didLoad: Ember.K,
4835 d0fe8c12 Athina Bekakou
4836 d0fe8c12 Athina Bekakou
  /**
4837 d0fe8c12 Athina Bekakou
    Fired when the record is updated.
4838 d0fe8c12 Athina Bekakou

4839 d0fe8c12 Athina Bekakou
    @event didUpdate
4840 d0fe8c12 Athina Bekakou
  */
4841 d0fe8c12 Athina Bekakou
  didUpdate: Ember.K,
4842 d0fe8c12 Athina Bekakou
4843 d0fe8c12 Athina Bekakou
  /**
4844 d0fe8c12 Athina Bekakou
    Fired when the record is created.
4845 d0fe8c12 Athina Bekakou

4846 d0fe8c12 Athina Bekakou
    @event didCreate
4847 d0fe8c12 Athina Bekakou
  */
4848 d0fe8c12 Athina Bekakou
  didCreate: Ember.K,
4849 d0fe8c12 Athina Bekakou
4850 d0fe8c12 Athina Bekakou
  /**
4851 d0fe8c12 Athina Bekakou
    Fired when the record is deleted.
4852 d0fe8c12 Athina Bekakou

4853 d0fe8c12 Athina Bekakou
    @event didDelete
4854 d0fe8c12 Athina Bekakou
  */
4855 d0fe8c12 Athina Bekakou
  didDelete: Ember.K,
4856 d0fe8c12 Athina Bekakou
4857 d0fe8c12 Athina Bekakou
  /**
4858 d0fe8c12 Athina Bekakou
    Fired when the record becomes invalid.
4859 d0fe8c12 Athina Bekakou

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

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

5019 d0fe8c12 Athina Bekakou
    Example
5020 d0fe8c12 Athina Bekakou

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

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

5046 d0fe8c12 Athina Bekakou
    Example
5047 d0fe8c12 Athina Bekakou

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

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

5107 d0fe8c12 Athina Bekakou
    Example
5108 d0fe8c12 Athina Bekakou

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

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

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

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

5283 d0fe8c12 Athina Bekakou
    Example
5284 d0fe8c12 Athina Bekakou

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

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

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

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

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

5355 d0fe8c12 Athina Bekakou
    Example
5356 d0fe8c12 Athina Bekakou

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

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

5387 d0fe8c12 Athina Bekakou
    Example
5388 d0fe8c12 Athina Bekakou

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

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

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

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

5514 d0fe8c12 Athina Bekakou
    @method create
5515 d0fe8c12 Athina Bekakou
    @private
5516 d0fe8c12 Athina Bekakou
    @static
5517 d0fe8c12 Athina Bekakou
  */
5518 d0fe8c12 Athina Bekakou
  create: function() {
5519 d0fe8c12 Athina Bekakou
    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 d0fe8c12 Athina Bekakou
  }
5521 d0fe8c12 Athina Bekakou
});
5522 d0fe8c12 Athina Bekakou
5523 d0fe8c12 Athina Bekakou
})();
5524 d0fe8c12 Athina Bekakou
5525 d0fe8c12 Athina Bekakou
5526 d0fe8c12 Athina Bekakou
5527 d0fe8c12 Athina Bekakou
(function() {
5528 d0fe8c12 Athina Bekakou
/**
5529 d0fe8c12 Athina Bekakou
  @module ember-data
5530 d0fe8c12 Athina Bekakou
*/
5531 d0fe8c12 Athina Bekakou
5532 d0fe8c12 Athina Bekakou
var get = Ember.get;
5533 d0fe8c12 Athina Bekakou
5534 d0fe8c12 Athina Bekakou
/**
5535 d0fe8c12 Athina Bekakou
  @class Model
5536 d0fe8c12 Athina Bekakou
  @namespace DS
5537 d0fe8c12 Athina Bekakou
*/
5538 d0fe8c12 Athina Bekakou
DS.Model.reopenClass({
5539 d0fe8c12 Athina Bekakou
  /**
5540 d0fe8c12 Athina Bekakou
    A map whose keys are the attributes of the model (properties
5541 d0fe8c12 Athina Bekakou
    described by DS.attr) and whose values are the meta object for the
5542 d0fe8c12 Athina Bekakou
    property.
5543 d0fe8c12 Athina Bekakou

5544 d0fe8c12 Athina Bekakou
    Example
5545 d0fe8c12 Athina Bekakou

5546 d0fe8c12 Athina Bekakou
    ```javascript
5547 d0fe8c12 Athina Bekakou

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

5554 d0fe8c12 Athina Bekakou
    var attributes = Ember.get(App.Person, 'attributes')
5555 d0fe8c12 Athina Bekakou

5556 d0fe8c12 Athina Bekakou
    attributes.forEach(function(name, meta) {
5557 d0fe8c12 Athina Bekakou
      console.log(name, meta);
5558 d0fe8c12 Athina Bekakou
    });
5559 d0fe8c12 Athina Bekakou

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

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

5592 d0fe8c12 Athina Bekakou
    Example
5593 d0fe8c12 Athina Bekakou

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

5601 d0fe8c12 Athina Bekakou
    var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')
5602 d0fe8c12 Athina Bekakou

5603 d0fe8c12 Athina Bekakou
    transformedAttributes.forEach(function(field, type) {
5604 d0fe8c12 Athina Bekakou
      console.log(field, type);
5605 d0fe8c12 Athina Bekakou
    });
5606 d0fe8c12 Athina Bekakou

5607 d0fe8c12 Athina Bekakou
    // prints:
5608 d0fe8c12 Athina Bekakou
    // lastName string
5609 d0fe8c12 Athina Bekakou
    // birthday date
5610 d0fe8c12 Athina Bekakou
    ```
5611 d0fe8c12 Athina Bekakou

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

5633 d0fe8c12 Athina Bekakou
    The callback method you provide should have the following signature (all
5634 d0fe8c12 Athina Bekakou
    parameters are optional):
5635 d0fe8c12 Athina Bekakou

5636 d0fe8c12 Athina Bekakou
    ```javascript
5637 d0fe8c12 Athina Bekakou
    function(name, meta);
5638 d0fe8c12 Athina Bekakou
    ```
5639 d0fe8c12 Athina Bekakou

5640 d0fe8c12 Athina Bekakou
    - `name` the name of the current property in the iteration
5641 d0fe8c12 Athina Bekakou
    - `meta` the meta object for the attribute property in the iteration
5642 d0fe8c12 Athina Bekakou

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

5646 d0fe8c12 Athina Bekakou
    Example
5647 d0fe8c12 Athina Bekakou

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

5655 d0fe8c12 Athina Bekakou
    App.Person.eachAttribute(function(name, meta) {
5656 d0fe8c12 Athina Bekakou
      console.log(name, meta);
5657 d0fe8c12 Athina Bekakou
    });
5658 d0fe8c12 Athina Bekakou

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

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

5681 d0fe8c12 Athina Bekakou
    The callback method you provide should have the following signature (all
5682 d0fe8c12 Athina Bekakou
    parameters are optional):
5683 d0fe8c12 Athina Bekakou

5684 d0fe8c12 Athina Bekakou
    ```javascript
5685 d0fe8c12 Athina Bekakou
    function(name, type);
5686 d0fe8c12 Athina Bekakou
    ```
5687 d0fe8c12 Athina Bekakou

5688 d0fe8c12 Athina Bekakou
    - `name` the name of the current property in the iteration
5689 d0fe8c12 Athina Bekakou
    - `type` a tring contrining the name of the type of transformed
5690 d0fe8c12 Athina Bekakou
      applied to the attribute
5691 d0fe8c12 Athina Bekakou

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

5695 d0fe8c12 Athina Bekakou
    Example
5696 d0fe8c12 Athina Bekakou

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

5704 d0fe8c12 Athina Bekakou
    App.Person.eachTransformedAttribute(function(name, type) {
5705 d0fe8c12 Athina Bekakou
      console.log(name, type);
5706 d0fe8c12 Athina Bekakou
    });
5707 d0fe8c12 Athina Bekakou

5708 d0fe8c12 Athina Bekakou
    // prints:
5709 d0fe8c12 Athina Bekakou
    // lastName string
5710 d0fe8c12 Athina Bekakou
    // birthday date
5711 d0fe8c12 Athina Bekakou
   ```
5712 d0fe8c12 Athina Bekakou

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

5764 d0fe8c12 Athina Bekakou
  `DS.attr` takes an optional hash as a second parameter, currently
5765 d0fe8c12 Athina Bekakou
  supported options are:
5766 d0fe8c12 Athina Bekakou

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

5770 d0fe8c12 Athina Bekakou
  Example
5771 d0fe8c12 Athina Bekakou

5772 d0fe8c12 Athina Bekakou
  ```javascript
5773 d0fe8c12 Athina Bekakou
  var attr = DS.attr;
5774 d0fe8c12 Athina Bekakou

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

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

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

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

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

6391 d0fe8c12 Athina Bekakou

6392 d0fe8c12 Athina Bekakou
  `DS.belongsTo` takes an optional hash as a second parameter, currently
6393 d0fe8c12 Athina Bekakou
  supported options are:
6394 d0fe8c12 Athina Bekakou

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

6399 d0fe8c12 Athina Bekakou
  #### One-To-One
6400 d0fe8c12 Athina Bekakou
  To declare a one-to-one relationship between two models, use
6401 d0fe8c12 Athina Bekakou
  `DS.belongsTo`:
6402 d0fe8c12 Athina Bekakou

6403 d0fe8c12 Athina Bekakou
  ```javascript
6404 d0fe8c12 Athina Bekakou
  App.User = DS.Model.extend({
6405 d0fe8c12 Athina Bekakou
    profile: DS.belongsTo('profile')
6406 d0fe8c12 Athina Bekakou
  });
6407 d0fe8c12 Athina Bekakou

6408 d0fe8c12 Athina Bekakou
  App.Profile = DS.Model.extend({
6409 d0fe8c12 Athina Bekakou
    user: DS.belongsTo('user')
6410 d0fe8c12 Athina Bekakou
  });
6411 d0fe8c12 Athina Bekakou
  ```
6412 d0fe8c12 Athina Bekakou

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

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

6422 d0fe8c12 Athina Bekakou
  App.Comment = DS.Model.extend({
6423 d0fe8c12 Athina Bekakou
    post: DS.belongsTo('post')
6424 d0fe8c12 Athina Bekakou
  });
6425 d0fe8c12 Athina Bekakou
  ```
6426 d0fe8c12 Athina Bekakou

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

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

6607 d0fe8c12 Athina Bekakou
  `DS.hasMany` takes an optional hash as a second parameter, currently
6608 d0fe8c12 Athina Bekakou
  supported options are:
6609 d0fe8c12 Athina Bekakou

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

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

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

6622 d0fe8c12 Athina Bekakou
  App.Comment = DS.Model.extend({
6623 d0fe8c12 Athina Bekakou
    post: DS.belongsTo('post')
6624 d0fe8c12 Athina Bekakou
  });
6625 d0fe8c12 Athina Bekakou
  ```
6626 d0fe8c12 Athina Bekakou

6627 d0fe8c12 Athina Bekakou
  #### Many-To-Many
6628 d0fe8c12 Athina Bekakou
  To declare a many-to-many relationship between two models, use
6629 d0fe8c12 Athina Bekakou
  `DS.hasMany`:
6630 d0fe8c12 Athina Bekakou

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

6636 d0fe8c12 Athina Bekakou
  App.Tag = DS.Model.extend({
6637 d0fe8c12 Athina Bekakou
    posts: DS.hasMany('post')
6638 d0fe8c12 Athina Bekakou
  });
6639 d0fe8c12 Athina Bekakou
  ```
6640 d0fe8c12 Athina Bekakou

6641 d0fe8c12 Athina Bekakou
  #### Explicit Inverses
6642 d0fe8c12 Athina Bekakou

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

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

6653 d0fe8c12 Athina Bekakou
  ```javascript
6654 d0fe8c12 Athina Bekakou
  var belongsTo = DS.belongsTo,
6655 d0fe8c12 Athina Bekakou
      hasMany = DS.hasMany;
6656 d0fe8c12 Athina Bekakou

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

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

6671 d0fe8c12 Athina Bekakou
  You can also specify an inverse on a `belongsTo`, which works how
6672 d0fe8c12 Athina Bekakou
  you'd expect.
6673 d0fe8c12 Athina Bekakou

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

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

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

6723 d0fe8c12 Athina Bekakou
    ```javascript
6724 d0fe8c12 Athina Bekakou
    DS.Model.extend({
6725 d0fe8c12 Athina Bekakou
      parent: DS.belongsTo('user')
6726 d0fe8c12 Athina Bekakou
    });
6727 d0fe8c12 Athina Bekakou
    ```
6728 d0fe8c12 Athina Bekakou

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

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

6760 d0fe8c12 Athina Bekakou
  A note about the computed properties contained here:
6761 d0fe8c12 Athina Bekakou

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

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

6777 d0fe8c12 Athina Bekakou
    For example, if you define a model like this:
6778 d0fe8c12 Athina Bekakou

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

6785 d0fe8c12 Athina Bekakou
    Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
6786 d0fe8c12 Athina Bekakou

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

6853 d0fe8c12 Athina Bekakou
    For example, given the following model definition:
6854 d0fe8c12 Athina Bekakou

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

6863 d0fe8c12 Athina Bekakou
    This computed property would return a map describing these
6864 d0fe8c12 Athina Bekakou
    relationships, like this:
6865 d0fe8c12 Athina Bekakou

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

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

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

6914 d0fe8c12 Athina Bekakou
      posts: DS.hasMany('post')
6915 d0fe8c12 Athina Bekakou
    });
6916 d0fe8c12 Athina Bekakou
    ```
6917 d0fe8c12 Athina Bekakou

6918 d0fe8c12 Athina Bekakou
    This property would contain the following:
6919 d0fe8c12 Athina Bekakou

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

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

6950 d0fe8c12 Athina Bekakou
    For example, given a model with this definition:
6951 d0fe8c12 Athina Bekakou

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

6957 d0fe8c12 Athina Bekakou
      posts: DS.hasMany('post')
6958 d0fe8c12 Athina Bekakou
    });
6959 d0fe8c12 Athina Bekakou
    ```
6960 d0fe8c12 Athina Bekakou

6961 d0fe8c12 Athina Bekakou
    This property would contain the following:
6962 d0fe8c12 Athina Bekakou

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

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

7004 d0fe8c12 Athina Bekakou
    For example, given a model with this
7005 d0fe8c12 Athina Bekakou
    definition:
7006 d0fe8c12 Athina Bekakou

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

7012 d0fe8c12 Athina Bekakou
      posts: DS.hasMany('post')
7013 d0fe8c12 Athina Bekakou
    });
7014 d0fe8c12 Athina Bekakou
    ```
7015 d0fe8c12 Athina Bekakou

7016 d0fe8c12 Athina Bekakou
    This property would contain the following:
7017 d0fe8c12 Athina Bekakou

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

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

7061 d0fe8c12 Athina Bekakou
    For example:
7062 d0fe8c12 Athina Bekakou

7063 d0fe8c12 Athina Bekakou
    ```javascript
7064 d0fe8c12 Athina Bekakou

7065 d0fe8c12 Athina Bekakou
    App.Blog = DS.Model.extend({
7066 d0fe8c12 Athina Bekakou
      users: DS.hasMany('user'),
7067 d0fe8c12 Athina Bekakou
      owner: DS.belongsTo('user'),
7068 d0fe8c12 Athina Bekakou

7069 d0fe8c12 Athina Bekakou
      posts: DS.hasMany('post'),
7070 d0fe8c12 Athina Bekakou

7071 d0fe8c12 Athina Bekakou
      title: DS.attr('string')
7072 d0fe8c12 Athina Bekakou
    });
7073 d0fe8c12 Athina Bekakou

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

7079 d0fe8c12 Athina Bekakou
    // prints:
7080 d0fe8c12 Athina Bekakou
    // users, hasMany
7081 d0fe8c12 Athina Bekakou
    // owner, belongsTo
7082 d0fe8c12 Athina Bekakou
    // posts, hasMany
7083 d0fe8c12 Athina Bekakou
    // title, attribute
7084 d0fe8c12 Athina Bekakou
    ```
7085 d0fe8c12 Athina Bekakou

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

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

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

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

7205 d0fe8c12 Athina Bekakou
    It updates all record arrays that a record belongs to.
7206 d0fe8c12 Athina Bekakou

7207 d0fe8c12 Athina Bekakou
    To avoid thrashing, it only runs at most once per run loop.
7208 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

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

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

7449 d0fe8c12 Athina Bekakou
  Example
7450 d0fe8c12 Athina Bekakou

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

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

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

7487 d0fe8c12 Athina Bekakou
  ### Creating an Adapter
7488 d0fe8c12 Athina Bekakou

7489 d0fe8c12 Athina Bekakou
  First, create a new subclass of `DS.Adapter`:
7490 d0fe8c12 Athina Bekakou

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

7497 d0fe8c12 Athina Bekakou
  To tell your store which adapter to use, set its `adapter` property:
7498 d0fe8c12 Athina Bekakou

7499 d0fe8c12 Athina Bekakou
  ```javascript
7500 d0fe8c12 Athina Bekakou
  App.store = DS.Store.create({
7501 d0fe8c12 Athina Bekakou
    adapter: App.MyAdapter.create()
7502 d0fe8c12 Athina Bekakou
  });
7503 d0fe8c12 Athina Bekakou
  ```
7504 d0fe8c12 Athina Bekakou

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

7509 d0fe8c12 Athina Bekakou
    * `find()`
7510 d0fe8c12 Athina Bekakou
    * `createRecord()`
7511 d0fe8c12 Athina Bekakou
    * `updateRecord()`
7512 d0fe8c12 Athina Bekakou
    * `deleteRecord()`
7513 d0fe8c12 Athina Bekakou
    * `findAll()`
7514 d0fe8c12 Athina Bekakou
    * `findQuery()`
7515 d0fe8c12 Athina Bekakou

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

7519 d0fe8c12 Athina Bekakou
    * `findMany()`
7520 d0fe8c12 Athina Bekakou

7521 d0fe8c12 Athina Bekakou

7522 d0fe8c12 Athina Bekakou
  For an example implementation, see `DS.RESTAdapter`, the
7523 d0fe8c12 Athina Bekakou
  included REST adapter.
7524 d0fe8c12 Athina Bekakou

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

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

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

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

7558 d0fe8c12 Athina Bekakou
    Here is an example `find` implementation:
7559 d0fe8c12 Athina Bekakou

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

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

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

7589 d0fe8c12 Athina Bekakou
    Example
7590 d0fe8c12 Athina Bekakou

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

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

7622 d0fe8c12 Athina Bekakou
    Example
7623 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

7681 d0fe8c12 Athina Bekakou
    Example
7682 d0fe8c12 Athina Bekakou

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

7689 d0fe8c12 Athina Bekakou
        // ...
7690 d0fe8c12 Athina Bekakou
      }
7691 d0fe8c12 Athina Bekakou
    });
7692 d0fe8c12 Athina Bekakou
    ```
7693 d0fe8c12 Athina Bekakou

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

7707 d0fe8c12 Athina Bekakou
    Serializes the record and send it to the server.
7708 d0fe8c12 Athina Bekakou

7709 d0fe8c12 Athina Bekakou
    Example
7710 d0fe8c12 Athina Bekakou

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

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

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

7746 d0fe8c12 Athina Bekakou
    Serializes the record update and send it to the server.
7747 d0fe8c12 Athina Bekakou

7748 d0fe8c12 Athina Bekakou
    Example
7749 d0fe8c12 Athina Bekakou

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

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

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

7786 d0fe8c12 Athina Bekakou
    Sends a delete request for the record to the server.
7787 d0fe8c12 Athina Bekakou

7788 d0fe8c12 Athina Bekakou
    Example
7789 d0fe8c12 Athina Bekakou

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

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

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

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

7829 d0fe8c12 Athina Bekakou
    Example
7830 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

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

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

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

8232 d0fe8c12 Athina Bekakou
  See the `normalize` documentation for more information.
8233 d0fe8c12 Athina Bekakou

8234 d0fe8c12 Athina Bekakou
  ## Across the Board Normalization
8235 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

8288 d0fe8c12 Athina Bekakou
    You use `normalizeHash` to normalize just the comments:
8289 d0fe8c12 Athina Bekakou

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

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

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

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

8322 d0fe8c12 Athina Bekakou
    For example, if you have a payload that looks like this:
8323 d0fe8c12 Athina Bekakou

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

8341 d0fe8c12 Athina Bekakou
    The `normalize` method will be called three times:
8342 d0fe8c12 Athina Bekakou

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

8347 d0fe8c12 Athina Bekakou
    You can use this method, for example, to normalize underscored keys to camelized
8348 d0fe8c12 Athina Bekakou
    or other general-purpose normalizations.
8349 d0fe8c12 Athina Bekakou

8350 d0fe8c12 Athina Bekakou
    If you want to do normalizations specific to some part of the payload, you
8351 d0fe8c12 Athina Bekakou
    can specify those under `normalizeHash`.
8352 d0fe8c12 Athina Bekakou

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

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

8368 d0fe8c12 Athina Bekakou
    The key under `normalizeHash` is just the original key that was in the original
8369 d0fe8c12 Athina Bekakou
    payload.
8370 d0fe8c12 Athina Bekakou

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

8395 d0fe8c12 Athina Bekakou
    For example, you might want to remove some extraneous data from the payload:
8396 d0fe8c12 Athina Bekakou

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

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

8490 d0fe8c12 Athina Bekakou
    It is your opportunity to clean up the server's response into the normalized
8491 d0fe8c12 Athina Bekakou
    form expected by Ember Data.
8492 d0fe8c12 Athina Bekakou

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

8496 d0fe8c12 Athina Bekakou
    For example, if you have a payload like this in response to a request for
8497 d0fe8c12 Athina Bekakou
    post 1:
8498 d0fe8c12 Athina Bekakou

8499 d0fe8c12 Athina Bekakou
    ```js
8500 d0fe8c12 Athina Bekakou
    {
8501 d0fe8c12 Athina Bekakou
      "id": 1,
8502 d0fe8c12 Athina Bekakou
      "title": "Rails is omakase",
8503 d0fe8c12 Athina Bekakou

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

8516 d0fe8c12 Athina Bekakou
    You could implement a serializer that looks like this to get your payload
8517 d0fe8c12 Athina Bekakou
    into shape:
8518 d0fe8c12 Athina Bekakou

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

8526 d0fe8c12 Athina Bekakou
        payload = { comments: comments, post: payload };
8527 d0fe8c12 Athina Bekakou
        return this._super(store, type, payload, id, requestType);
8528 d0fe8c12 Athina Bekakou
      },
8529 d0fe8c12 Athina Bekakou

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

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

8548 d0fe8c12 Athina Bekakou
    The primary record is the single hash found under `post` or the first
8549 d0fe8c12 Athina Bekakou
    element of the `posts` array.
8550 d0fe8c12 Athina Bekakou

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

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

8613 d0fe8c12 Athina Bekakou
    It is your opportunity to clean up the server's response into the normalized
8614 d0fe8c12 Athina Bekakou
    form expected by Ember Data.
8615 d0fe8c12 Athina Bekakou

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

8619 d0fe8c12 Athina Bekakou
    For example, if you have a payload like this in response to a request for
8620 d0fe8c12 Athina Bekakou
    all posts:
8621 d0fe8c12 Athina Bekakou

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

8645 d0fe8c12 Athina Bekakou
    You could implement a serializer that looks like this to get your payload
8646 d0fe8c12 Athina Bekakou
    into shape:
8647 d0fe8c12 Athina Bekakou

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

8657 d0fe8c12 Athina Bekakou
        posts.forEach(function(post) {
8658 d0fe8c12 Athina Bekakou
          post.comments = [];
8659 d0fe8c12 Athina Bekakou
          postCache[post.id] = post;
8660 d0fe8c12 Athina Bekakou
        });
8661 d0fe8c12 Athina Bekakou

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

8668 d0fe8c12 Athina Bekakou
        payload = { comments: comments, posts: payload };
8669 d0fe8c12 Athina Bekakou

8670 d0fe8c12 Athina Bekakou
        return this._super(store, type, payload, id, requestType);
8671 d0fe8c12 Athina Bekakou
      },
8672 d0fe8c12 Athina Bekakou

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

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

8691 d0fe8c12 Athina Bekakou
    The primary array is the array found under `posts`.
8692 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

8796 d0fe8c12 Athina Bekakou
    For example, your server may return underscored root keys rather than
8797 d0fe8c12 Athina Bekakou
    the expected camelcased versions.
8798 d0fe8c12 Athina Bekakou

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

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

8822 d0fe8c12 Athina Bekakou
    By default, it creates a JSON object with a key for
8823 d0fe8c12 Athina Bekakou
    each attribute and belongsTo relationship.
8824 d0fe8c12 Athina Bekakou

8825 d0fe8c12 Athina Bekakou
    For example, consider this model:
8826 d0fe8c12 Athina Bekakou

8827 d0fe8c12 Athina Bekakou
    ```js
8828 d0fe8c12 Athina Bekakou
    App.Comment = DS.Model.extend({
8829 d0fe8c12 Athina Bekakou
      title: DS.attr(),
8830 d0fe8c12 Athina Bekakou
      body: DS.attr(),
8831 d0fe8c12 Athina Bekakou

8832 d0fe8c12 Athina Bekakou
      author: DS.belongsTo('user')
8833 d0fe8c12 Athina Bekakou
    });
8834 d0fe8c12 Athina Bekakou
    ```
8835 d0fe8c12 Athina Bekakou

8836 d0fe8c12 Athina Bekakou
    The default serialization would create a JSON object like:
8837 d0fe8c12 Athina Bekakou

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

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

8851 d0fe8c12 Athina Bekakou
    By default, belongs-to relationships are converted into
8852 d0fe8c12 Athina Bekakou
    IDs when inserted into the JSON hash.
8853 d0fe8c12 Athina Bekakou

8854 d0fe8c12 Athina Bekakou
    ## IDs
8855 d0fe8c12 Athina Bekakou

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

8860 d0fe8c12 Athina Bekakou
    The adapter passes in `includeId: true` when serializing
8861 d0fe8c12 Athina Bekakou
    a record for `createRecord`, but not for `updateRecord`.
8862 d0fe8c12 Athina Bekakou

8863 d0fe8c12 Athina Bekakou
    ## Customization
8864 d0fe8c12 Athina Bekakou

8865 d0fe8c12 Athina Bekakou
    Your server may expect a different JSON format than the
8866 d0fe8c12 Athina Bekakou
    built-in serialization format.
8867 d0fe8c12 Athina Bekakou

8868 d0fe8c12 Athina Bekakou
    In that case, you can implement `serialize` yourself and
8869 d0fe8c12 Athina Bekakou
    return a JSON hash of your choosing.
8870 d0fe8c12 Athina Bekakou

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

8880 d0fe8c12 Athina Bekakou
        if (options.includeId) {
8881 d0fe8c12 Athina Bekakou
          json.POST_ID_ = post.get('id');
8882 d0fe8c12 Athina Bekakou
        }
8883 d0fe8c12 Athina Bekakou

8884 d0fe8c12 Athina Bekakou
        return json;
8885 d0fe8c12 Athina Bekakou
      }
8886 d0fe8c12 Athina Bekakou
    });
8887 d0fe8c12 Athina Bekakou
    ```
8888 d0fe8c12 Athina Bekakou

8889 d0fe8c12 Athina Bekakou
    ## Customizing an App-Wide Serializer
8890 d0fe8c12 Athina Bekakou

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

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

8900 d0fe8c12 Athina Bekakou
        record.eachAttribute(function(name) {
8901 d0fe8c12 Athina Bekakou
          json[serverAttributeName(name)] = record.get(name);
8902 d0fe8c12 Athina Bekakou
        })
8903 d0fe8c12 Athina Bekakou

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

8910 d0fe8c12 Athina Bekakou
        if (options.includeId) {
8911 d0fe8c12 Athina Bekakou
          json.ID_ = record.get('id');
8912 d0fe8c12 Athina Bekakou
        }
8913 d0fe8c12 Athina Bekakou

8914 d0fe8c12 Athina Bekakou
        return json;
8915 d0fe8c12 Athina Bekakou
      }
8916 d0fe8c12 Athina Bekakou
    });
8917 d0fe8c12 Athina Bekakou

8918 d0fe8c12 Athina Bekakou
    function serverAttributeName(attribute) {
8919 d0fe8c12 Athina Bekakou
      return attribute.underscore().toUpperCase();
8920 d0fe8c12 Athina Bekakou
    }
8921 d0fe8c12 Athina Bekakou

8922 d0fe8c12 Athina Bekakou
    function serverHasManyName(name) {
8923 d0fe8c12 Athina Bekakou
      return serverAttributeName(name.singularize()) + "_IDS";
8924 d0fe8c12 Athina Bekakou
    }
8925 d0fe8c12 Athina Bekakou
    ```
8926 d0fe8c12 Athina Bekakou

8927 d0fe8c12 Athina Bekakou
    This serializer will generate JSON that looks like this:
8928 d0fe8c12 Athina Bekakou

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

8937 d0fe8c12 Athina Bekakou
    ## Tweaking the Default JSON
8938 d0fe8c12 Athina Bekakou

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

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

8948 d0fe8c12 Athina Bekakou
        json.subject = json.title;
8949 d0fe8c12 Athina Bekakou
        delete json.title;
8950 d0fe8c12 Athina Bekakou

8951 d0fe8c12 Athina Bekakou
        return json;
8952 d0fe8c12 Athina Bekakou
      }
8953 d0fe8c12 Athina Bekakou
    });
8954 d0fe8c12 Athina Bekakou
    ```
8955 d0fe8c12 Athina Bekakou

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

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

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

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

9023 d0fe8c12 Athina Bekakou
  This adapter is designed around the idea that the JSON exchanged with
9024 d0fe8c12 Athina Bekakou
  the server should be conventional.
9025 d0fe8c12 Athina Bekakou

9026 d0fe8c12 Athina Bekakou
  ## JSON Structure
9027 d0fe8c12 Athina Bekakou

9028 d0fe8c12 Athina Bekakou
  The REST adapter expects the JSON returned from your server to follow
9029 d0fe8c12 Athina Bekakou
  these conventions.
9030 d0fe8c12 Athina Bekakou

9031 d0fe8c12 Athina Bekakou
  ### Object Root
9032 d0fe8c12 Athina Bekakou

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

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

9046 d0fe8c12 Athina Bekakou
  ### Conventional Names
9047 d0fe8c12 Athina Bekakou

9048 d0fe8c12 Athina Bekakou
  Attribute names in your JSON payload should be the camelCased versions of
9049 d0fe8c12 Athina Bekakou
  the attributes in your Ember.js models.
9050 d0fe8c12 Athina Bekakou

9051 d0fe8c12 Athina Bekakou
  For example, if you have a `Person` model:
9052 d0fe8c12 Athina Bekakou

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

9061 d0fe8c12 Athina Bekakou
  The JSON returned should look like this:
9062 d0fe8c12 Athina Bekakou

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

9073 d0fe8c12 Athina Bekakou
  ## Customization
9074 d0fe8c12 Athina Bekakou

9075 d0fe8c12 Athina Bekakou
  ### Endpoint path customization
9076 d0fe8c12 Athina Bekakou

9077 d0fe8c12 Athina Bekakou
  Endpoint paths can be prefixed with a `namespace` by setting the namespace
9078 d0fe8c12 Athina Bekakou
  property on the adapter:
9079 d0fe8c12 Athina Bekakou

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

9087 d0fe8c12 Athina Bekakou
  ### Host customization
9088 d0fe8c12 Athina Bekakou

9089 d0fe8c12 Athina Bekakou
  An adapter can target other hosts by setting the `host` property.
9090 d0fe8c12 Athina Bekakou

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

9097 d0fe8c12 Athina Bekakou
  ### Headers customization
9098 d0fe8c12 Athina Bekakou

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

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

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

9124 d0fe8c12 Athina Bekakou
    ```javascript
9125 d0fe8c12 Athina Bekakou
    DS.RESTAdapter.reopen({
9126 d0fe8c12 Athina Bekakou
      namespace: 'api/1'
9127 d0fe8c12 Athina Bekakou
    });
9128 d0fe8c12 Athina Bekakou
    ```
9129 d0fe8c12 Athina Bekakou

9130 d0fe8c12 Athina Bekakou
    Requests for `App.Post` would now target `/api/1/post/`.
9131 d0fe8c12 Athina Bekakou

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

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

9145 d0fe8c12 Athina Bekakou
    Requests for `App.Post` would now target `https://api.example.com/post/`.
9146 d0fe8c12 Athina Bekakou

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

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

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

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

9175 d0fe8c12 Athina Bekakou
    This method performs an HTTP `GET` request with the id provided as part of the querystring.
9176 d0fe8c12 Athina Bekakou

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

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

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

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

9218 d0fe8c12 Athina Bekakou
    The `query` argument is a simple JavaScript object that will be passed directly
9219 d0fe8c12 Athina Bekakou
    to the server as parameters.
9220 d0fe8c12 Athina Bekakou

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

9237 d0fe8c12 Athina Bekakou
    For example, if the original payload looks like:
9238 d0fe8c12 Athina Bekakou

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

9247 d0fe8c12 Athina Bekakou
    The IDs will be passed as a URL-encoded Array of IDs, in this form:
9248 d0fe8c12 Athina Bekakou

9249 d0fe8c12 Athina Bekakou
    ```
9250 d0fe8c12 Athina Bekakou
    ids[]=1&ids[]=2&ids[]=3
9251 d0fe8c12 Athina Bekakou
    ```
9252 d0fe8c12 Athina Bekakou

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

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

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

9275 d0fe8c12 Athina Bekakou
    For example, if your original payload looks like this:
9276 d0fe8c12 Athina Bekakou

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

9287 d0fe8c12 Athina Bekakou
    This method will be called with the parent record and `/posts/1/comments`.
9288 d0fe8c12 Athina Bekakou

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

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

9316 d0fe8c12 Athina Bekakou
    For example, if your original payload looks like this:
9317 d0fe8c12 Athina Bekakou

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

9328 d0fe8c12 Athina Bekakou
    This method will be called with the parent record and `/people/1/group`.
9329 d0fe8c12 Athina Bekakou

9330 d0fe8c12 Athina Bekakou
    The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL.
9331 d0fe8c12 Athina Bekakou

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

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

9352 d0fe8c12 Athina Bekakou
    See `serialize` for information on how to customize the serialized form
9353 d0fe8c12 Athina Bekakou
    of a record.
9354 d0fe8c12 Athina Bekakou

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

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

9377 d0fe8c12 Athina Bekakou
    See `serialize` for information on how to customize the serialized form
9378 d0fe8c12 Athina Bekakou
    of a record.
9379 d0fe8c12 Athina Bekakou

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

9400 d0fe8c12 Athina Bekakou
    The `deleteRecord` method  makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`.
9401 d0fe8c12 Athina Bekakou

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

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

9421 d0fe8c12 Athina Bekakou
    If an ID is specified, it adds the ID to the path generated
9422 d0fe8c12 Athina Bekakou
    for the type, separated by a `/`.
9423 d0fe8c12 Athina Bekakou

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

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

9486 d0fe8c12 Athina Bekakou
    ### Pathname customization
9487 d0fe8c12 Athina Bekakou

9488 d0fe8c12 Athina Bekakou
    For example if you have an object LineItem with an
9489 d0fe8c12 Athina Bekakou
    endpoint of "/line_items/".
9490 d0fe8c12 Athina Bekakou

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

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

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

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

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

9523 d0fe8c12 Athina Bekakou
          return new DS.InvalidError(jsonErrors);
9524 d0fe8c12 Athina Bekakou
        } else {
9525 d0fe8c12 Athina Bekakou
          return error;
9526 d0fe8c12 Athina Bekakou
        }
9527 d0fe8c12 Athina Bekakou
      }
9528 d0fe8c12 Athina Bekakou
    });
9529 d0fe8c12 Athina Bekakou
    ```
9530 d0fe8c12 Athina Bekakou

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

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

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

9558 d0fe8c12 Athina Bekakou
    By default, `ajax` method has the following behavior:
9559 d0fe8c12 Athina Bekakou

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

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

9647 d0fe8c12 Athina Bekakou
    Meant to be used by debugging tools such as the Chrome Ember Extension.
9648 d0fe8c12 Athina Bekakou

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

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

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

9769 d0fe8c12 Athina Bekakou
  Examples:
9770 d0fe8c12 Athina Bekakou

9771 d0fe8c12 Athina Bekakou
  Creating an inflector with no rules.
9772 d0fe8c12 Athina Bekakou

9773 d0fe8c12 Athina Bekakou
  ```js
9774 d0fe8c12 Athina Bekakou
  var inflector = new Ember.Inflector();
9775 d0fe8c12 Athina Bekakou
  ```
9776 d0fe8c12 Athina Bekakou

9777 d0fe8c12 Athina Bekakou
  Creating an inflector with the default ember ruleset.
9778 d0fe8c12 Athina Bekakou

9779 d0fe8c12 Athina Bekakou
  ```js
9780 d0fe8c12 Athina Bekakou
  var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
9781 d0fe8c12 Athina Bekakou

9782 d0fe8c12 Athina Bekakou
  inflector.pluralize('cow') //=> 'kine'
9783 d0fe8c12 Athina Bekakou
  inflector.singularize('kine') //=> 'cow'
9784 d0fe8c12 Athina Bekakou
  ```
9785 d0fe8c12 Athina Bekakou

9786 d0fe8c12 Athina Bekakou
  Creating an inflector and adding rules later.
9787 d0fe8c12 Athina Bekakou

9788 d0fe8c12 Athina Bekakou
  ```javascript
9789 d0fe8c12 Athina Bekakou
  var inflector = Ember.Inflector.inflector;
9790 d0fe8c12 Athina Bekakou

9791 d0fe8c12 Athina Bekakou
  inflector.pluralize('advice') // => 'advices'
9792 d0fe8c12 Athina Bekakou
  inflector.uncountable('advice');
9793 d0fe8c12 Athina Bekakou
  inflector.pluralize('advice') // => 'advice'
9794 d0fe8c12 Athina Bekakou

9795 d0fe8c12 Athina Bekakou
  inflector.pluralize('formula') // => 'formulas'
9796 d0fe8c12 Athina Bekakou
  inflector.irregular('formula', 'formulae');
9797 d0fe8c12 Athina Bekakou
  inflector.pluralize('formula') // => 'formulae'
9798 d0fe8c12 Athina Bekakou

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

9804 d0fe8c12 Athina Bekakou
  Creating an inflector with a nondefault ruleset.
9805 d0fe8c12 Athina Bekakou

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

9816 d0fe8c12 Athina Bekakou
  var inflector = new Ember.Inflector(rules);
9817 d0fe8c12 Athina Bekakou
  ```
9818 d0fe8c12 Athina Bekakou

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

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

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

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

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

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

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

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

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

10165 d0fe8c12 Athina Bekakou
    If your payload looks like this
10166 d0fe8c12 Athina Bekakou

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

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

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

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

10226 d0fe8c12 Athina Bekakou
    Normalize:
10227 d0fe8c12 Athina Bekakou
    ```js
10228 d0fe8c12 Athina Bekakou
      {
10229 d0fe8c12 Athina Bekakou
        id: "1"
10230 d0fe8c12 Athina Bekakou
        minion: { type: "evil_minion", id: "12"}
10231 d0fe8c12 Athina Bekakou
      }
10232 d0fe8c12 Athina Bekakou
    ```
10233 d0fe8c12 Athina Bekakou

10234 d0fe8c12 Athina Bekakou
    To:
10235 d0fe8c12 Athina Bekakou
    ```js
10236 d0fe8c12 Athina Bekakou
      {
10237 d0fe8c12 Athina Bekakou
        id: "1"
10238 d0fe8c12 Athina Bekakou
        minion: { type: "evilMinion", id: "12"}
10239 d0fe8c12 Athina Bekakou
      }
10240 d0fe8c12 Athina Bekakou
    ```
10241 d0fe8c12 Athina Bekakou

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

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

10298 d0fe8c12 Athina Bekakou
  Currently only `{embedded: 'always'}` records are supported.
10299 d0fe8c12 Athina Bekakou

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

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

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

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

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

10425 d0fe8c12 Athina Bekakou
  ## JSON Structure
10426 d0fe8c12 Athina Bekakou

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

10430 d0fe8c12 Athina Bekakou
  ### Conventional Names
10431 d0fe8c12 Athina Bekakou

10432 d0fe8c12 Athina Bekakou
  Attribute names in your JSON payload should be the underscored versions of
10433 d0fe8c12 Athina Bekakou
  the attributes in your Ember.js models.
10434 d0fe8c12 Athina Bekakou

10435 d0fe8c12 Athina Bekakou
  For example, if you have a `Person` model:
10436 d0fe8c12 Athina Bekakou

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

10445 d0fe8c12 Athina Bekakou
  The JSON returned should look like this:
10446 d0fe8c12 Athina Bekakou

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

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

10469 d0fe8c12 Athina Bekakou
    ```js
10470 d0fe8c12 Athina Bekakou
      this.pathForType("famousPerson");
10471 d0fe8c12 Athina Bekakou
      //=> "famous_people"
10472 d0fe8c12 Athina Bekakou
    ```
10473 d0fe8c12 Athina Bekakou

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

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

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

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