Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (272.4 kB)

1
/*!
2
 * @overview  Ember Data
3
 * @copyright Copyright 2011-2014 Tilde Inc. and contributors.
4
 *            Portions Copyright 2011 LivingSocial Inc.
5
 * @license   Licensed under MIT license (see license.js)
6
 * @version   1.0.0-beta.6+canary.bf00e754
7
 */
8

    
9

    
10
(function() {
11
var define, requireModule;
12

    
13
(function() {
14
  var registry = {}, seen = {};
15

    
16
  define = function(name, deps, callback) {
17
    registry[name] = { deps: deps, callback: callback };
18
  };
19

    
20
  requireModule = function(name) {
21
    if (seen[name]) { return seen[name]; }
22
    seen[name] = {};
23

    
24
    var mod, deps, callback, reified , exports;
25

    
26
    mod = registry[name];
27

    
28
    if (!mod) {
29
      throw new Error("Module '" + name + "' not found.");
30
    }
31

    
32
    deps = mod.deps;
33
    callback = mod.callback;
34
    reified = [];
35
    exports;
36

    
37
    for (var i=0, l=deps.length; i<l; i++) {
38
      if (deps[i] === 'exports') {
39
        reified.push(exports = {});
40
      } else {
41
        reified.push(requireModule(deps[i]));
42
      }
43
    }
44

    
45
    var value = callback.apply(this, reified);
46
    return seen[name] = exports || value;
47
  };
48
})();
49
(function() {
50
/**
51
  @module ember-data
52
*/
53

    
54
/**
55
  All Ember Data methods and functions are defined inside of this namespace.
56

57
  @class DS
58
  @static
59
*/
60
var DS;
61
if ('undefined' === typeof DS) {
62
  /**
63
    @property VERSION
64
    @type String
65
    @default '1.0.0-beta.6+canary.bf00e754'
66
    @static
67
  */
68
  DS = Ember.Namespace.create({
69
    VERSION: '1.0.0-beta.6+canary.bf00e754'
70
  });
71

    
72
  if ('undefined' !== typeof window) {
73
    window.DS = DS;
74
  }
75

    
76
  if (Ember.libraries) {
77
    Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
78
  }
79
}
80

    
81
})();
82

    
83

    
84

    
85
(function() {
86
var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
87

    
88
// Simple dispatcher to support overriding the aliased
89
// method in subclasses.
90
function aliasMethod(methodName) {
91
  return function() {
92
    return this[methodName].apply(this, arguments);
93
  };
94
}
95

    
96
/**
97
  In Ember Data a Serializer is used to serialize and deserialize
98
  records when they are transfered in and out of an external source.
99
  This process involves normalizing property names, transforming
100
  attribute values and serializeing relationships.
101

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

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

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

120
    Example
121

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

128
    @property primaryKey
129
    @type {String}
130
    @default 'id'
131
  */
132
  primaryKey: 'id',
133

    
134
  /**
135
   Given a subclass of `DS.Model` and a JSON object this method will
136
   iterate through each attribute of the `DS.Model` and invoke the
137
   `DS.Transform#deserialize` method on the matching property of the
138
   JSON object.  This method is typically called after the
139
   serializer's `normalize` method.
140

141
   @method applyTransforms
142
   @private
143
   @param {subclass of DS.Model} type
144
   @param {Object} data The data to transform
145
   @return {Object} data The transformed data object
146
  */
147
  applyTransforms: function(type, data) {
148
    type.eachTransformedAttribute(function(key, type) {
149
      var transform = this.transformFor(type);
150
      data[key] = transform.deserialize(data[key]);
151
    }, this);
152

    
153
    return data;
154
  },
155

    
156
  /**
157
    Normalizes a part of the JSON payload returned by
158
    the server. You should override this method, munge the hash
159
    and call super if you have generic normalization to do.
160

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

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

168
    Example
169

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

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

186
    @method normalize
187
    @param {subclass of DS.Model} type
188
    @param {Object} hash
189
    @return {Object}
190
  */
191
  normalize: function(type, hash) {
192
    if (!hash) { return hash; }
193

    
194
    this.applyTransforms(type, hash);
195
    return hash;
196
  },
197

    
198
  // SERIALIZE
199
  /**
200
    Called when a record is saved in order to convert the
201
    record into JSON.
202

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

206
    For example, consider this model:
207

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

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

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

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

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

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

235
    ## IDs
236

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

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

244
    ## Customization
245

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

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

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

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

265
        return json;
266
      }
267
    });
268
    ```
269

270
    ## Customizing an App-Wide Serializer
271

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

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

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

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

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

295
        return json;
296
      }
297
    });
298

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

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

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

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

318
    ## Tweaking the Default JSON
319

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

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

329
        json.subject = json.title;
330
        delete json.title;
331

332
        return json;
333
      }
334
    });
335
    ```
336

337
    @method serialize
338
    @param {subclass of DS.Model} record
339
    @param {Object} options
340
    @return {Object} json
341
  */
342
  serialize: function(record, options) {
343
    var json = {};
344

    
345
    if (options && options.includeId) {
346
      var id = get(record, 'id');
347

    
348
      if (id) {
349
        json[get(this, 'primaryKey')] = id;
350
      }
351
    }
352

    
353
    record.eachAttribute(function(key, attribute) {
354
      this.serializeAttribute(record, json, key, attribute);
355
    }, this);
356

    
357
    record.eachRelationship(function(key, relationship) {
358
      if (relationship.kind === 'belongsTo') {
359
        this.serializeBelongsTo(record, json, relationship);
360
      } else if (relationship.kind === 'hasMany') {
361
        this.serializeHasMany(record, json, relationship);
362
      }
363
    }, this);
364

    
365
    return json;
366
  },
367

    
368
  /**
369
   `serializeAttribute` can be used to customize how `DS.attr`
370
   properties are serialized
371

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

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

385
   @method serializeAttribute
386
   @param {DS.Model} record
387
   @param {Object} json
388
   @param {String} key
389
   @param {Object} attribute
390
  */
391
  serializeAttribute: function(record, json, key, attribute) {
392
    var attrs = get(this, 'attrs');
393
    var value = get(record, key), type = attribute.type;
394

    
395
    if (type) {
396
      var transform = this.transformFor(type);
397
      value = transform.serialize(value);
398
    }
399

    
400
    // if provided, use the mapping provided by `attrs` in
401
    // the serializer
402
    key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key);
403

    
404
    json[key] = value;
405
  },
406

    
407
  /**
408
   `serializeBelongsTo` can be used to customize how `DS.belongsTo`
409
   properties are serialized.
410

411
   Example
412

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

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

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

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

427
   @method serializeBelongsTo
428
   @param {DS.Model} record
429
   @param {Object} json
430
   @param {Object} relationship
431
  */
432
  serializeBelongsTo: function(record, json, relationship) {
433
    var key = relationship.key;
434

    
435
    var belongsTo = get(record, key);
436

    
437
    key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
438

    
439
    if (isNone(belongsTo)) {
440
      json[key] = belongsTo;
441
    } else {
442
      json[key] = get(belongsTo, 'id');
443
    }
444

    
445
    if (relationship.options.polymorphic) {
446
      this.serializePolymorphicType(record, json, relationship);
447
    }
448
  },
449

    
450
  /**
451
   `serializeHasMany` can be used to customize how `DS.hasMany`
452
   properties are serialized.
453

454
   Example
455

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

469
   @method serializeHasMany
470
   @param {DS.Model} record
471
   @param {Object} json
472
   @param {Object} relationship
473
  */
474
  serializeHasMany: function(record, json, relationship) {
475
    var key = relationship.key;
476

    
477
    var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
478

    
479
    if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') {
480
      json[key] = get(record, key).mapBy('id');
481
      // TODO support for polymorphic manyToNone and manyToMany relationships
482
    }
483
  },
484

    
485
  /**
486
    You can use this method to customize how polymorphic objects are
487
    serialized. Objects are considered to be polymorphic if
488
    `{polymorphic: true}` is pass as the second argument to the
489
    `DS.belongsTo` function.
490

491
    Example
492

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

504
    @method serializePolymorphicType
505
    @param {DS.Model} record
506
    @param {Object} json
507
    @param {Object} relationship
508
  */
509
  serializePolymorphicType: Ember.K,
510

    
511
  // EXTRACT
512

    
513
  /**
514
    The `extract` method is used to deserialize payload data from the
515
    server. By default the `JSONSerializer` does not push the records
516
    into the store. However records that subclass `JSONSerializer`
517
    such as the `RESTSerializer` may push records into the store as
518
    part of the extract call.
519

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

523
    Example
524

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

537
    @method extract
538
    @param {DS.Store} store
539
    @param {subclass of DS.Model} type
540
    @param {Object} payload
541
    @param {String or Number} id
542
    @param {String} requestType
543
    @return {Object} json The deserialized payload
544
  */
545
  extract: function(store, type, payload, id, requestType) {
546
    this.extractMeta(store, type, payload);
547

    
548
    var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
549
    return this[specificExtract](store, type, payload, id, requestType);
550
  },
551

    
552
  /**
553
    `extractFindAll` is a hook into the extract method used when a
554
    call is made to `DS.Store#findAll`. By default this method is an
555
    alias for [extractArray](#method_extractArray).
556

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

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

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

593
    @method extractFindHasMany
594
    @param {DS.Store} store
595
    @param {subclass of DS.Model} type
596
    @param {Object} payload
597
    @return {Array} array An array of deserialized objects
598
  */
599
  extractFindHasMany: aliasMethod('extractArray'),
600

    
601
  /**
602
    `extractCreateRecord` is a hook into the extract method used when a
603
    call is made to `DS.Store#createRecord`. By default this method is
604
    alias for [extractSave](#method_extractSave).
605

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

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

630
    @method extractDeleteRecord
631
    @param {DS.Store} store
632
    @param {subclass of DS.Model} type
633
    @param {Object} payload
634
    @return {Object} json The deserialized payload
635
  */
636
  extractDeleteRecord: aliasMethod('extractSave'),
637

    
638
  /**
639
    `extractFind` is a hook into the extract method used when
640
    a call is made to `DS.Store#find`. By default this method is
641
    alias for [extractSingle](#method_extractSingle).
642

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

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

667
    @method extractSave
668
    @param {DS.Store} store
669
    @param {subclass of DS.Model} type
670
    @param {Object} payload
671
    @return {Object} json The deserialized payload
672
  */
673
  extractSave: aliasMethod('extractSingle'),
674

    
675
  /**
676
    `extractSingle` is used to deserialize a single record returned
677
    from the adapter.
678

679
    Example
680

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

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

692
    @method extractSingle
693
    @param {DS.Store} store
694
    @param {subclass of DS.Model} type
695
    @param {Object} payload
696
    @return {Object} json The deserialized payload
697
  */
698
  extractSingle: function(store, type, payload) {
699
    return this.normalize(type, payload);
700
  },
701

    
702
  /**
703
    `extractArray` is used to deserialize an array of records
704
    returned from the adapter.
705

706
    Example
707

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

718
    @method extractArray
719
    @param {DS.Store} store
720
    @param {subclass of DS.Model} type
721
    @param {Object} payload
722
    @return {Array} array An array of deserialized objects
723
  */
724
  extractArray: function(store, type, payload) {
725
    return this.normalize(type, payload);
726
  },
727

    
728
  /**
729
    `extractMeta` is used to deserialize any meta information in the
730
    adapter payload. By default Ember Data expects meta information to
731
    be located on the `meta` property of the payload object.
732

733
    Example
734

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

746
    @method extractMeta
747
    @param {DS.Store} store
748
    @param {subclass of DS.Model} type
749
    @param {Object} payload
750
  */
751
  extractMeta: function(store, type, payload) {
752
    if (payload && payload.meta) {
753
      store.metaForType(type, payload.meta);
754
      delete payload.meta;
755
    }
756
  },
757

    
758
  /**
759
   `keyForAttribute` can be used to define rules for how to convert an
760
   attribute name in your model to a key in your JSON.
761

762
   Example
763

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

772
   @method keyForAttribute
773
   @param {String} key
774
   @return {String} normalized key
775
  */
776

    
777

    
778
  /**
779
   `keyForRelationship` can be used to define a custom key when
780
   serializeing relationship properties. By default `JSONSerializer`
781
   does not provide an implementation of this method.
782

783
   Example
784

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

793
   @method keyForRelationship
794
   @param {String} key
795
   @param {String} relationship type
796
   @return {String} normalized key
797
  */
798

    
799
  // HELPERS
800

    
801
  /**
802
   @method transformFor
803
   @private
804
   @param {String} attributeType
805
   @param {Boolean} skipAssertion
806
   @return {DS.Transform} transform
807
  */
808
  transformFor: function(attributeType, skipAssertion) {
809
    var transform = this.container.lookup('transform:' + attributeType);
810
    Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform);
811
    return transform;
812
  }
813
});
814

    
815
})();
816

    
817

    
818

    
819
(function() {
820
/**
821
  @module ember-data
822
*/
823
var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore, DS = window.DS ;
824

    
825
/**
826
  Extend `Ember.DataAdapter` with ED specific code.
827

828
  @class DebugAdapter
829
  @namespace DS
830
  @extends Ember.DataAdapter
831
  @private
832
*/
833
DS.DebugAdapter = Ember.DataAdapter.extend({
834
  getFilters: function() {
835
    return [
836
      { name: 'isNew', desc: 'New' },
837
      { name: 'isModified', desc: 'Modified' },
838
      { name: 'isClean', desc: 'Clean' }
839
    ];
840
  },
841

    
842
  detect: function(klass) {
843
    return klass !== DS.Model && DS.Model.detect(klass);
844
  },
845

    
846
  columnsForType: function(type) {
847
    var columns = [{ name: 'id', desc: 'Id' }], count = 0, self = this;
848
    get(type, 'attributes').forEach(function(name, meta) {
849
        if (count++ > self.attributeLimit) { return false; }
850
        var desc = capitalize(underscore(name).replace('_', ' '));
851
        columns.push({ name: name, desc: desc });
852
    });
853
    return columns;
854
  },
855

    
856
  getRecords: function(type) {
857
    return this.get('store').all(type);
858
  },
859

    
860
  getRecordColumnValues: function(record) {
861
    var self = this, count = 0,
862
        columnValues = { id: get(record, 'id') };
863

    
864
    record.eachAttribute(function(key) {
865
      if (count++ > self.attributeLimit) {
866
        return false;
867
      }
868
      var value = get(record, key);
869
      columnValues[key] = value;
870
    });
871
    return columnValues;
872
  },
873

    
874
  getRecordKeywords: function(record) {
875
    var keywords = [], keys = Ember.A(['id']);
876
    record.eachAttribute(function(key) {
877
      keys.push(key);
878
    });
879
    keys.forEach(function(key) {
880
      keywords.push(get(record, key));
881
    });
882
    return keywords;
883
  },
884

    
885
  getRecordFilterValues: function(record) {
886
    return {
887
      isNew: record.get('isNew'),
888
      isModified: record.get('isDirty') && !record.get('isNew'),
889
      isClean: !record.get('isDirty')
890
    };
891
  },
892

    
893
  getRecordColor: function(record) {
894
    var color = 'black';
895
    if (record.get('isNew')) {
896
      color = 'green';
897
    } else if (record.get('isDirty')) {
898
      color = 'blue';
899
    }
900
    return color;
901
  },
902

    
903
  observeRecord: function(record, recordUpdated) {
904
    var releaseMethods = Ember.A(), self = this,
905
        keysToObserve = Ember.A(['id', 'isNew', 'isDirty']);
906

    
907
    record.eachAttribute(function(key) {
908
      keysToObserve.push(key);
909
    });
910

    
911
    keysToObserve.forEach(function(key) {
912
      var handler = function() {
913
        recordUpdated(self.wrapRecord(record));
914
      };
915
      Ember.addObserver(record, key, handler);
916
      releaseMethods.push(function() {
917
        Ember.removeObserver(record, key, handler);
918
      });
919
    });
920

    
921
    var release = function() {
922
      releaseMethods.forEach(function(fn) { fn(); } );
923
    };
924

    
925
    return release;
926
  }
927

    
928
});
929

    
930
})();
931

    
932

    
933

    
934
(function() {
935
/**
936
  The `DS.Transform` class is used to serialize and deserialize model
937
  attributes when they are saved or loaded from an
938
  adapter. Subclassing `DS.Transform` is useful for creating custom
939
  attributes. All subclasses of `DS.Transform` must implement a
940
  `serialize` and a `deserialize` method.
941

942
  Example
943

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

955
  Usage
956

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

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

973
    Example
974

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

981
    @method serialize
982
    @param deserialized The deserialized value
983
    @return The serialized value
984
  */
985
  serialize: Ember.required(),
986

    
987
  /**
988
    When given a serialize value from a JSON object this method must
989
    return the deserialized value for the record attribute.
990

991
    Example
992

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

999
    @method deserialize
1000
    @param serialized The serialized value
1001
    @return The deserialized value
1002
  */
1003
  deserialize: Ember.required()
1004

    
1005
});
1006

    
1007
})();
1008

    
1009

    
1010

    
1011
(function() {
1012

    
1013
/**
1014
  The `DS.BooleanTransform` class is used to serialize and deserialize
1015
  boolean attributes on Ember Data record objects. This transform is
1016
  used when `boolean` is passed as the type parameter to the
1017
  [DS.attr](../../data#method_attr) function.
1018

1019
  Usage
1020

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

1030
  @class BooleanTransform
1031
  @extends DS.Transform
1032
  @namespace DS
1033
 */
1034
DS.BooleanTransform = DS.Transform.extend({
1035
  deserialize: function(serialized) {
1036
    var type = typeof serialized;
1037

    
1038
    if (type === "boolean") {
1039
      return serialized;
1040
    } else if (type === "string") {
1041
      return serialized.match(/^true$|^t$|^1$/i) !== null;
1042
    } else if (type === "number") {
1043
      return serialized === 1;
1044
    } else {
1045
      return false;
1046
    }
1047
  },
1048

    
1049
  serialize: function(deserialized) {
1050
    return Boolean(deserialized);
1051
  }
1052
});
1053

    
1054
})();
1055

    
1056

    
1057

    
1058
(function() {
1059
/**
1060
  The `DS.DateTransform` class is used to serialize and deserialize
1061
  date attributes on Ember Data record objects. This transform is used
1062
  when `date` is passed as the type parameter to the
1063
  [DS.attr](../../data#method_attr) function.
1064

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

1074
  @class DateTransform
1075
  @extends DS.Transform
1076
  @namespace DS
1077
 */
1078
DS.DateTransform = DS.Transform.extend({
1079

    
1080
  deserialize: function(serialized) {
1081
    var type = typeof serialized;
1082

    
1083
    if (type === "string") {
1084
      return new Date(Ember.Date.parse(serialized));
1085
    } else if (type === "number") {
1086
      return new Date(serialized);
1087
    } else if (serialized === null || serialized === undefined) {
1088
      // if the value is not present in the data,
1089
      // return undefined, not null.
1090
      return serialized;
1091
    } else {
1092
      return null;
1093
    }
1094
  },
1095

    
1096
  serialize: function(date) {
1097
    if (date instanceof Date) {
1098
      var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1099
      var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1100

    
1101
      var pad = function(num) {
1102
        return num < 10 ? "0"+num : ""+num;
1103
      };
1104

    
1105
      var utcYear = date.getUTCFullYear(),
1106
          utcMonth = date.getUTCMonth(),
1107
          utcDayOfMonth = date.getUTCDate(),
1108
          utcDay = date.getUTCDay(),
1109
          utcHours = date.getUTCHours(),
1110
          utcMinutes = date.getUTCMinutes(),
1111
          utcSeconds = date.getUTCSeconds();
1112

    
1113

    
1114
      var dayOfWeek = days[utcDay];
1115
      var dayOfMonth = pad(utcDayOfMonth);
1116
      var month = months[utcMonth];
1117

    
1118
      return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
1119
             pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
1120
    } else {
1121
      return null;
1122
    }
1123
  } 
1124

    
1125
});
1126

    
1127
})();
1128

    
1129

    
1130

    
1131
(function() {
1132
var empty = Ember.isEmpty;
1133
/**
1134
  The `DS.NumberTransform` class is used to serialize and deserialize
1135
  numeric attributes on Ember Data record objects. This transform is
1136
  used when `number` is passed as the type parameter to the
1137
  [DS.attr](../../data#method_attr) function.
1138

1139
  Usage
1140

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

1150
  @class NumberTransform
1151
  @extends DS.Transform
1152
  @namespace DS
1153
 */
1154
DS.NumberTransform = DS.Transform.extend({
1155

    
1156
  deserialize: function(serialized) {
1157
    return empty(serialized) ? null : Number(serialized);
1158
  },
1159

    
1160
  serialize: function(deserialized) {
1161
    return empty(deserialized) ? null : Number(deserialized);
1162
  }
1163
});
1164

    
1165
})();
1166

    
1167

    
1168

    
1169
(function() {
1170
var none = Ember.isNone;
1171

    
1172
/**
1173
  The `DS.StringTransform` class is used to serialize and deserialize
1174
  string attributes on Ember Data record objects. This transform is
1175
  used when `string` is passed as the type parameter to the
1176
  [DS.attr](../../data#method_attr) function.
1177

1178
  Usage
1179

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

1189
  @class StringTransform
1190
  @extends DS.Transform
1191
  @namespace DS
1192
 */
1193
DS.StringTransform = DS.Transform.extend({
1194

    
1195
  deserialize: function(serialized) {
1196
    return none(serialized) ? null : String(serialized);
1197
  },
1198

    
1199
  serialize: function(deserialized) {
1200
    return none(deserialized) ? null : String(deserialized);
1201
  }
1202

    
1203
});
1204

    
1205
})();
1206

    
1207

    
1208

    
1209
(function() {
1210

    
1211
})();
1212

    
1213

    
1214

    
1215
(function() {
1216
/**
1217
  @module ember-data
1218
*/
1219

    
1220
var set = Ember.set;
1221

    
1222
/*
1223
  This code registers an injection for Ember.Application.
1224

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

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

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

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

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

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

1246
  Note that this code will only be run if the `ember-application` package is
1247
  loaded. If Ember Data is being used in an environment other than a
1248
  typical application (e.g., node.js where only `ember-runtime` is available),
1249
  this code will be ignored.
1250
*/
1251

    
1252
Ember.onLoad('Ember.Application', function(Application) {
1253
  Application.initializer({
1254
    name: "store",
1255

    
1256
    initialize: function(container, application) {
1257
      application.register('store:main', application.Store || DS.Store);
1258
      application.register('serializer:_default', DS.JSONSerializer);
1259
      application.register('serializer:_rest', DS.RESTSerializer);
1260
      application.register('adapter:_rest', DS.RESTAdapter);
1261

    
1262
      // Eagerly generate the store so defaultStore is populated.
1263
      // TODO: Do this in a finisher hook
1264
      container.lookup('store:main');
1265
    }
1266
  });
1267

    
1268
  Application.initializer({
1269
    name: "transforms",
1270
    before: "store",
1271

    
1272
    initialize: function(container, application) {
1273
      application.register('transform:boolean', DS.BooleanTransform);
1274
      application.register('transform:date', DS.DateTransform);
1275
      application.register('transform:number', DS.NumberTransform);
1276
      application.register('transform:string', DS.StringTransform);
1277
    }
1278
  });
1279

    
1280
  Application.initializer({
1281
    name: "data-adapter",
1282
    before: "store",
1283

    
1284
    initialize: function(container, application) {
1285
      application.register('data-adapter:main', DS.DebugAdapter);
1286
    }
1287
  });
1288

    
1289
  Application.initializer({
1290
    name: "injectStore",
1291
    before: "store",
1292

    
1293
    initialize: function(container, application) {
1294
      application.inject('controller', 'store', 'store:main');
1295
      application.inject('route', 'store', 'store:main');
1296
      application.inject('serializer', 'store', 'store:main');
1297
      application.inject('data-adapter', 'store', 'store:main');
1298
    }
1299
  });
1300

    
1301
});
1302

    
1303
})();
1304

    
1305

    
1306

    
1307
(function() {
1308
/**
1309
  @module ember-data
1310
*/
1311

    
1312
/**
1313
  Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
1314

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

1317
  Released under MIT license.
1318

1319
  @class Date
1320
  @namespace Ember
1321
  @static
1322
*/
1323
Ember.Date = Ember.Date || {};
1324

    
1325
var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
1326

    
1327
/**
1328
  @method parse
1329
  @param date
1330
*/
1331
Ember.Date.parse = function (date) {
1332
    var timestamp, struct, minutesOffset = 0;
1333

    
1334
    // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
1335
    // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
1336
    // implementations could be faster
1337
    //              1 YYYY                2 MM       3 DD           4 HH    5 mm       6 ss        7 msec        8 Z 9 ±    10 tzHH    11 tzmm
1338
    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
        // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
1340
        for (var i = 0, k; (k = numericKeys[i]); ++i) {
1341
            struct[k] = +struct[k] || 0;
1342
        }
1343

    
1344
        // allow undefined days and months
1345
        struct[2] = (+struct[2] || 1) - 1;
1346
        struct[3] = +struct[3] || 1;
1347

    
1348
        if (struct[8] !== 'Z' && struct[9] !== undefined) {
1349
            minutesOffset = struct[10] * 60 + struct[11];
1350

    
1351
            if (struct[9] === '+') {
1352
                minutesOffset = 0 - minutesOffset;
1353
            }
1354
        }
1355

    
1356
        timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
1357
    }
1358
    else {
1359
        timestamp = origParse ? origParse(date) : NaN;
1360
    }
1361

    
1362
    return timestamp;
1363
};
1364

    
1365
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) {
1366
  Date.parse = Ember.Date.parse;
1367
}
1368

    
1369
})();
1370

    
1371

    
1372

    
1373
(function() {
1374

    
1375
})();
1376

    
1377

    
1378

    
1379
(function() {
1380
/**
1381
  @module ember-data
1382
*/
1383

    
1384
var get = Ember.get, set = Ember.set;
1385

    
1386
/**
1387
  A record array is an array that contains records of a certain type. The record
1388
  array materializes records as needed when they are retrieved for the first
1389
  time. You should not create record arrays yourself. Instead, an instance of
1390
  `DS.RecordArray` or its subclasses will be returned by your application's store
1391
  in response to queries.
1392

1393
  @class RecordArray
1394
  @namespace DS
1395
  @extends Ember.ArrayProxy
1396
  @uses Ember.Evented
1397
*/
1398

    
1399
DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
1400
  /**
1401
    The model type contained by this record array.
1402

1403
    @property type
1404
    @type DS.Model
1405
  */
1406
  type: null,
1407

    
1408
  /**
1409
    The array of client ids backing the record array. When a
1410
    record is requested from the record array, the record
1411
    for the client id at the same index is materialized, if
1412
    necessary, by the store.
1413

1414
    @property content
1415
    @private
1416
    @type Ember.Array
1417
  */
1418
  content: null,
1419

    
1420
  /**
1421
    The flag to signal a `RecordArray` is currently loading data.
1422

1423
    Example
1424

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

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

1437
    Example
1438

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

1446
    @property isUpdating
1447
    @type Boolean
1448
  */
1449
  isUpdating: false,
1450

    
1451
  /**
1452
    The store that created this record array.
1453

1454
    @property store
1455
    @private
1456
    @type DS.Store
1457
  */
1458
  store: null,
1459

    
1460
  /**
1461
    Retrieves an object from the content by index.
1462

1463
    @method objectAtContent
1464
    @private
1465
    @param {Number} index
1466
    @return {DS.Model} record
1467
  */
1468
  objectAtContent: function(index) {
1469
    var content = get(this, 'content');
1470

    
1471
    return content.objectAt(index);
1472
  },
1473

    
1474
  /**
1475
    Used to get the latest version of all of the records in this array
1476
    from the adapter.
1477

1478
    Example
1479

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

1487
    @method update
1488
  */
1489
  update: function() {
1490
    if (get(this, 'isUpdating')) { return; }
1491

    
1492
    var store = get(this, 'store'),
1493
        type = get(this, 'type');
1494

    
1495
    store.fetchAll(type, this);
1496
  },
1497

    
1498
  /**
1499
    Adds a record to the `RecordArray`.
1500

1501
    @method addRecord
1502
    @private
1503
    @param {DS.Model} record
1504
  */
1505
  addRecord: function(record) {
1506
    get(this, 'content').addObject(record);
1507
  },
1508

    
1509
  /**
1510
    Removes a record to the `RecordArray`.
1511

1512
    @method removeRecord
1513
    @private
1514
    @param {DS.Model} record
1515
  */
1516
  removeRecord: function(record) {
1517
    get(this, 'content').removeObject(record);
1518
  },
1519

    
1520
  /**
1521
    Saves all of the records in the `RecordArray`.
1522

1523
    Example
1524

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

1533
    @method save
1534
    @return {DS.PromiseArray} promise
1535
  */
1536
  save: function() {
1537
    var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
1538
    var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) {
1539
      return Ember.A(array);
1540
    }, null, "DS: RecordArray#save apply Ember.NativeArray");
1541

    
1542
    return DS.PromiseArray.create({ promise: promise });
1543
  }
1544
});
1545

    
1546
})();
1547

    
1548

    
1549

    
1550
(function() {
1551
/**
1552
  @module ember-data
1553
*/
1554

    
1555
var get = Ember.get;
1556

    
1557
/**
1558
  Represents a list of records whose membership is determined by the
1559
  store. As records are created, loaded, or modified, the store
1560
  evaluates them to determine if they should be part of the record
1561
  array.
1562

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

1572
    Example
1573

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

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

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

1590
    @method filterFunction
1591
    @param {DS.Model} record
1592
    @return {Boolean} `true` if the record should be in the array
1593
  */
1594
  filterFunction: null,
1595
  isLoaded: true,
1596

    
1597
  replace: function() {
1598
    var type = get(this, 'type').toString();
1599
    throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
1600
  },
1601

    
1602
  /**
1603
    @method updateFilter
1604
    @private
1605
  */
1606
  updateFilter: Ember.observer(function() {
1607
    var manager = get(this, 'manager');
1608
    manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
1609
  }, 'filterFunction')
1610
});
1611

    
1612
})();
1613

    
1614

    
1615

    
1616
(function() {
1617
/**
1618
  @module ember-data
1619
*/
1620

    
1621
var get = Ember.get, set = Ember.set;
1622

    
1623
/**
1624
  Represents an ordered list of records whose order and membership is
1625
  determined by the adapter. For example, a query sent to the adapter
1626
  may trigger a search on the server, whose results would be loaded
1627
  into an instance of the `AdapterPopulatedRecordArray`.
1628

1629
  @class AdapterPopulatedRecordArray
1630
  @namespace DS
1631
  @extends DS.RecordArray
1632
*/
1633
DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
1634
  query: null,
1635

    
1636
  replace: function() {
1637
    var type = get(this, 'type').toString();
1638
    throw new Error("The result of a server query (on " + type + ") is immutable.");
1639
  },
1640

    
1641
  /**
1642
    @method load
1643
    @private
1644
    @param {Array} data
1645
  */
1646
  load: function(data) {
1647
    var store = get(this, 'store'),
1648
        type = get(this, 'type'),
1649
        records = store.pushMany(type, data),
1650
        meta = store.metadataFor(type);
1651

    
1652
    this.setProperties({
1653
      content: Ember.A(records),
1654
      isLoaded: true,
1655
      meta: meta
1656
    });
1657

    
1658
    // TODO: does triggering didLoad event should be the last action of the runLoop?
1659
    Ember.run.once(this, 'trigger', 'didLoad');
1660
  }
1661
});
1662

    
1663
})();
1664

    
1665

    
1666

    
1667
(function() {
1668
/**
1669
  @module ember-data
1670
*/
1671

    
1672
var get = Ember.get, set = Ember.set;
1673
var map = Ember.EnumerableUtils.map;
1674

    
1675
/**
1676
  A `ManyArray` is a `RecordArray` that represents the contents of a has-many
1677
  relationship.
1678

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

1682
  ### Inverses
1683

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

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

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

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

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

1707
  @class ManyArray
1708
  @namespace DS
1709
  @extends DS.RecordArray
1710
*/
1711
DS.ManyArray = DS.RecordArray.extend({
1712
  init: function() {
1713
    this._super.apply(this, arguments);
1714
    this._changesToSync = Ember.OrderedSet.create();
1715
  },
1716

    
1717
  /**
1718
    The property name of the relationship
1719

1720
    @property {String} name
1721
    @private
1722
  */
1723
  name: null,
1724

    
1725
  /**
1726
    The record to which this relationship belongs.
1727

1728
    @property {DS.Model} owner
1729
    @private
1730
  */
1731
  owner: null,
1732

    
1733
  /**
1734
    `true` if the relationship is polymorphic, `false` otherwise.
1735

1736
    @property {Boolean} isPolymorphic
1737
    @private
1738
  */
1739
  isPolymorphic: false,
1740

    
1741
  // LOADING STATE
1742

    
1743
  isLoaded: false,
1744

    
1745
  /**
1746
    Used for async `hasMany` arrays
1747
    to keep track of when they will resolve.
1748

1749
    @property {Ember.RSVP.Promise} promise
1750
    @private
1751
  */
1752
  promise: null,
1753

    
1754
  /**
1755
    @method loadingRecordsCount
1756
    @param {Number} count
1757
    @private
1758
  */
1759
  loadingRecordsCount: function(count) {
1760
    this.loadingRecordsCount = count;
1761
  },
1762

    
1763
  /**
1764
    @method loadedRecord
1765
    @private
1766
  */
1767
  loadedRecord: function() {
1768
    this.loadingRecordsCount--;
1769
    if (this.loadingRecordsCount === 0) {
1770
      set(this, 'isLoaded', true);
1771
      this.trigger('didLoad');
1772
    }
1773
  },
1774

    
1775
  /**
1776
    @method fetch
1777
    @private
1778
  */
1779
  fetch: function() {
1780
    var records = get(this, 'content'),
1781
        store = get(this, 'store'),
1782
        owner = get(this, 'owner'),
1783
        resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type'));
1784

    
1785
    var unloadedRecords = records.filterProperty('isEmpty', true);
1786
    store.fetchMany(unloadedRecords, owner, resolver);
1787
  },
1788

    
1789
  // Overrides Ember.Array's replace method to implement
1790
  replaceContent: function(index, removed, added) {
1791
    // Map the array of record objects into an array of  client ids.
1792
    added = map(added, function(record) {
1793
      Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.type.typeKey + "' allowed)", !this.type || record instanceof this.type);
1794
      return record;
1795
    }, this);
1796

    
1797
    this._super(index, removed, added);
1798
  },
1799

    
1800
  arrangedContentDidChange: function() {
1801
    Ember.run.once(this, 'fetch');
1802
  },
1803

    
1804
  arrayContentWillChange: function(index, removed, added) {
1805
    var owner = get(this, 'owner'),
1806
        name = get(this, 'name');
1807

    
1808
    if (!owner._suspendedRelationships) {
1809
      // This code is the first half of code that continues inside
1810
      // of arrayContentDidChange. It gets or creates a change from
1811
      // the child object, adds the current owner as the old
1812
      // parent if this is the first time the object was removed
1813
      // from a ManyArray, and sets `newParent` to null.
1814
      //
1815
      // Later, if the object is added to another ManyArray,
1816
      // the `arrayContentDidChange` will set `newParent` on
1817
      // the change.
1818
      for (var i=index; i<index+removed; i++) {
1819
        var record = get(this, 'content').objectAt(i);
1820

    
1821
        var change = DS.RelationshipChange.createChange(owner, record, get(this, 'store'), {
1822
          parentType: owner.constructor,
1823
          changeType: "remove",
1824
          kind: "hasMany",
1825
          key: name
1826
        });
1827

    
1828
        this._changesToSync.add(change);
1829
      }
1830
    }
1831

    
1832
    return this._super.apply(this, arguments);
1833
  },
1834

    
1835
  arrayContentDidChange: function(index, removed, added) {
1836
    this._super.apply(this, arguments);
1837

    
1838
    var owner = get(this, 'owner'),
1839
        name = get(this, 'name'),
1840
        store = get(this, 'store');
1841

    
1842
    if (!owner._suspendedRelationships) {
1843
      // This code is the second half of code that started in
1844
      // `arrayContentWillChange`. It gets or creates a change
1845
      // from the child object, and adds the current owner as
1846
      // the new parent.
1847
      for (var i=index; i<index+added; i++) {
1848
        var record = get(this, 'content').objectAt(i);
1849

    
1850
        var change = DS.RelationshipChange.createChange(owner, record, store, {
1851
          parentType: owner.constructor,
1852
          changeType: "add",
1853
          kind:"hasMany",
1854
          key: name
1855
        });
1856
        change.hasManyName = name;
1857

    
1858
        this._changesToSync.add(change);
1859
      }
1860

    
1861
      // We wait until the array has finished being
1862
      // mutated before syncing the OneToManyChanges created
1863
      // in arrayContentWillChange, so that the array
1864
      // membership test in the sync() logic operates
1865
      // on the final results.
1866
      this._changesToSync.forEach(function(change) {
1867
        change.sync();
1868
      });
1869

    
1870
      this._changesToSync.clear();
1871
    }
1872
  },
1873

    
1874
  /**
1875
    Create a child record within the owner
1876

1877
    @method createRecord
1878
    @private
1879
    @param {Object} hash
1880
    @return {DS.Model} record
1881
  */
1882
  createRecord: function(hash) {
1883
    var owner = get(this, 'owner'),
1884
        store = get(owner, 'store'),
1885
        type = get(this, 'type'),
1886
        record;
1887

    
1888
    Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic'));
1889

    
1890
    record = store.createRecord.call(store, type, hash);
1891
    this.pushObject(record);
1892

    
1893
    return record;
1894
  }
1895

    
1896
});
1897

    
1898
})();
1899

    
1900

    
1901

    
1902
(function() {
1903
/**
1904
  @module ember-data
1905
*/
1906

    
1907
})();
1908

    
1909

    
1910

    
1911
(function() {
1912
/*globals Ember*/
1913
/*jshint eqnull:true*/
1914
/**
1915
  @module ember-data
1916
*/
1917

    
1918
var get = Ember.get, set = Ember.set;
1919
var once = Ember.run.once;
1920
var isNone = Ember.isNone;
1921
var forEach = Ember.EnumerableUtils.forEach;
1922
var indexOf = Ember.EnumerableUtils.indexOf;
1923
var map = Ember.EnumerableUtils.map;
1924
var resolve = Ember.RSVP.resolve;
1925
var copy = Ember.copy;
1926

    
1927
// Implementors Note:
1928
//
1929
//   The variables in this file are consistently named according to the following
1930
//   scheme:
1931
//
1932
//   * +id+ means an identifier managed by an external source, provided inside
1933
//     the data provided by that source. These are always coerced to be strings
1934
//     before being used internally.
1935
//   * +clientId+ means a transient numerical identifier generated at runtime by
1936
//     the data store. It is important primarily because newly created objects may
1937
//     not yet have an externally generated id.
1938
//   * +reference+ means a record reference object, which holds metadata about a
1939
//     record, even if it has not yet been fully materialized.
1940
//   * +type+ means a subclass of DS.Model.
1941

    
1942
// Used by the store to normalize IDs entering the store.  Despite the fact
1943
// that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`),
1944
// it is important that internally we use strings, since IDs may be serialized
1945
// and lose type information.  For example, Ember's router may put a record's
1946
// ID into the URL, and if we later try to deserialize that URL and find the
1947
// corresponding record, we will not know if it is a string or a number.
1948
var coerceId = function(id) {
1949
  return id == null ? null : id+'';
1950
};
1951

    
1952
/**
1953
  The store contains all of the data for records loaded from the server.
1954
  It is also responsible for creating instances of `DS.Model` that wrap
1955
  the individual data for a record, so that they can be bound to in your
1956
  Handlebars templates.
1957

1958
  Define your application's store like this:
1959

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

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

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

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

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

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

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

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

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

1994
  @class Store
1995
  @namespace DS
1996
  @extends Ember.Object
1997
*/
1998
DS.Store = Ember.Object.extend({
1999

    
2000
  /**
2001
    @method init
2002
    @private
2003
  */
2004
  init: function() {
2005
    // internal bookkeeping; not observable
2006
    this.typeMaps = {};
2007
    this.recordArrayManager = DS.RecordArrayManager.create({
2008
      store: this
2009
    });
2010
    this._relationshipChanges = {};
2011
    this._pendingSave = [];
2012
  },
2013

    
2014
  /**
2015
    The adapter to use to communicate to a backend server or other persistence layer.
2016

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

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

2021
    ```js
2022
    adapter: 'custom'
2023
    ```
2024

2025
    @property adapter
2026
    @default DS.RESTAdapter
2027
    @type {DS.Adapter|String}
2028
  */
2029
  adapter: '_rest',
2030

    
2031
  /**
2032
    Returns a JSON representation of the record using a custom
2033
    type-specific serializer, if one exists.
2034

2035
    The available options are:
2036

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

2040
    @method serialize
2041
    @private
2042
    @param {DS.Model} record the record to serialize
2043
    @param {Object} options an options hash
2044
  */
2045
  serialize: function(record, options) {
2046
    return this.serializerFor(record.constructor.typeKey).serialize(record, options);
2047
  },
2048

    
2049
  /**
2050
    This property returns the adapter, after resolving a possible
2051
    string key.
2052

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

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

2060
    @property defaultAdapter
2061
    @private
2062
    @returns DS.Adapter
2063
  */
2064
  defaultAdapter: Ember.computed('adapter', function() {
2065
    var adapter = get(this, 'adapter');
2066

    
2067
    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

    
2069
    if (typeof adapter === 'string') {
2070
      adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:_rest');
2071
    }
2072

    
2073
    if (DS.Adapter.detect(adapter)) {
2074
      adapter = adapter.create({ container: this.container });
2075
    }
2076

    
2077
    return adapter;
2078
  }),
2079

    
2080
  // .....................
2081
  // . CREATE NEW RECORD .
2082
  // .....................
2083

    
2084
  /**
2085
    Create a new record in the current store. The properties passed
2086
    to this method are set on the newly created record.
2087

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

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

2096
    @method createRecord
2097
    @param {String} type
2098
    @param {Object} properties a hash of properties to set on the
2099
      newly created record.
2100
    @returns {DS.Model} record
2101
  */
2102
  createRecord: function(type, properties) {
2103
    type = this.modelFor(type);
2104

    
2105
    properties = copy(properties) || {};
2106

    
2107
    // If the passed properties do not include a primary key,
2108
    // give the adapter an opportunity to generate one. Typically,
2109
    // client-side ID generators will use something like uuid.js
2110
    // to avoid conflicts.
2111

    
2112
    if (isNone(properties.id)) {
2113
      properties.id = this._generateId(type);
2114
    }
2115

    
2116
    // Coerce ID to a string
2117
    properties.id = coerceId(properties.id);
2118

    
2119
    var record = this.buildRecord(type, properties.id);
2120

    
2121
    // Move the record out of its initial `empty` state into
2122
    // the `loaded` state.
2123
    record.loadedData();
2124

    
2125
    // Set the properties specified on the record.
2126
    record.setProperties(properties);
2127

    
2128
    return record;
2129
  },
2130

    
2131
  /**
2132
    If possible, this method asks the adapter to generate an ID for
2133
    a newly created record.
2134

2135
    @method _generateId
2136
    @private
2137
    @param {String} type
2138
    @returns {String} if the adapter can generate one, an ID
2139
  */
2140
  _generateId: function(type) {
2141
    var adapter = this.adapterFor(type);
2142

    
2143
    if (adapter && adapter.generateIdForRecord) {
2144
      return adapter.generateIdForRecord(this);
2145
    }
2146

    
2147
    return null;
2148
  },
2149

    
2150
  // .................
2151
  // . DELETE RECORD .
2152
  // .................
2153

    
2154
  /**
2155
    For symmetry, a record can be deleted via the store.
2156

2157
    Example
2158

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

2164
    store.deleteRecord(post);
2165
    ```
2166

2167
    @method deleteRecord
2168
    @param {DS.Model} record
2169
  */
2170
  deleteRecord: function(record) {
2171
    record.deleteRecord();
2172
  },
2173

    
2174
  /**
2175
    For symmetry, a record can be unloaded via the store. Only
2176
    non-dirty records can be unloaded.
2177

2178
    Example
2179

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

2186
    @method unloadRecord
2187
    @param {DS.Model} record
2188
  */
2189
  unloadRecord: function(record) {
2190
    record.unloadRecord();
2191
  },
2192

    
2193
  // ................
2194
  // . FIND RECORDS .
2195
  // ................
2196

    
2197
  /**
2198
    This is the main entry point into finding records. The first parameter to
2199
    this method is the model's name as a string.
2200

2201
    ---
2202

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

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

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

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

2217
    ---
2218

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

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

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

2229
    ---
2230

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

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

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

2242
    @method find
2243
    @param {String or subclass of DS.Model} type
2244
    @param {Object|String|Integer|null} id
2245
    @return {Promise} promise
2246
  */
2247
  find: function(type, id) {
2248
    if (id === undefined) {
2249
      return this.findAll(type);
2250
    }
2251

    
2252
    // We are passed a query instead of an id.
2253
    if (Ember.typeOf(id) === 'object') {
2254
      return this.findQuery(type, id);
2255
    }
2256

    
2257
    return this.findById(type, coerceId(id));
2258
  },
2259

    
2260
  /**
2261
    This method returns a record for a given type and id combination.
2262

2263
    @method findById
2264
    @private
2265
    @param {String or subclass of DS.Model} type
2266
    @param {String|Integer} id
2267
    @return {Promise} promise
2268
  */
2269
  findById: function(type, id) {
2270
    type = this.modelFor(type);
2271

    
2272
    var record = this.recordForId(type, id);
2273

    
2274
    var promise = this.fetchRecord(record) || resolve(record, "DS: Store#findById " + type + " with id: " + id);
2275
    return promiseObject(promise);
2276
  },
2277

    
2278
  /**
2279
    This method makes a series of requests to the adapter's `find` method
2280
    and returns a promise that resolves once they are all loaded.
2281

2282
    @private
2283
    @method findByIds
2284
    @param {String} type
2285
    @param {Array} ids
2286
    @returns {Promise} promise
2287
  */
2288
  findByIds: function(type, ids) {
2289
    var store = this;
2290
    var promiseLabel = "DS: Store#findByIds " + type;
2291
    return promiseArray(Ember.RSVP.all(map(ids, function(id) {
2292
      return store.findById(type, id);
2293
    })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete"));
2294
  },
2295

    
2296
  /**
2297
    This method is called by `findById` if it discovers that a particular
2298
    type/id pair hasn't been loaded yet to kick off a request to the
2299
    adapter.
2300

2301
    @method fetchRecord
2302
    @private
2303
    @param {DS.Model} record
2304
    @returns {Promise} promise
2305
  */
2306
  fetchRecord: function(record) {
2307
    if (isNone(record)) { return null; }
2308
    if (record._loadingPromise) { return record._loadingPromise; }
2309
    if (!get(record, 'isEmpty')) { return null; }
2310

    
2311
    var type = record.constructor,
2312
        id = get(record, 'id');
2313

    
2314
    var adapter = this.adapterFor(type);
2315

    
2316
    Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter);
2317
    Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find);
2318

    
2319
    var promise = _find(adapter, this, type, id);
2320
    record.loadingData(promise);
2321
    return promise;
2322
  },
2323

    
2324
  /**
2325
    Get a record by a given type and ID without triggering a fetch.
2326

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

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

2334
    @method getById
2335
    @param {String or subclass of DS.Model} type
2336
    @param {String|Integer} id
2337
    @param {DS.Model} record
2338
  */
2339
  getById: function(type, id) {
2340
    if (this.hasRecordForId(type, id)) {
2341
      return this.recordForId(type, id);
2342
    } else {
2343
      return null;
2344
    }
2345
  },
2346

    
2347
  /**
2348
    This method is called by the record's `reload` method.
2349

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

2354
    @method reloadRecord
2355
    @private
2356
    @param {DS.Model} record
2357
    @return {Promise} promise
2358
  */
2359
  reloadRecord: function(record) {
2360
    var type = record.constructor,
2361
        adapter = this.adapterFor(type),
2362
        id = get(record, 'id');
2363

    
2364
    Ember.assert("You cannot reload a record without an ID", id);
2365
    Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter);
2366
    Ember.assert("You tried to reload a record but your adapter does not implement `find`", adapter.find);
2367

    
2368
    return _find(adapter, this, type, id);
2369
  },
2370

    
2371
  /**
2372
    This method takes a list of records, groups the records by type,
2373
    converts the records into IDs, and then invokes the adapter's `findMany`
2374
    method.
2375

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

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

2383
    @method fetchMany
2384
    @private
2385
    @param {Array} records
2386
    @param {DS.Model} owner
2387
    @param {Resolver} resolver
2388
  */
2389
  fetchMany: function(records, owner, resolver) {
2390
    if (!records.length) { return; }
2391

    
2392
    // Group By Type
2393
    var recordsByTypeMap = Ember.MapWithDefault.create({
2394
      defaultValue: function() { return Ember.A(); }
2395
    });
2396

    
2397
    forEach(records, function(record) {
2398
      recordsByTypeMap.get(record.constructor).push(record);
2399
    });
2400

    
2401
    forEach(recordsByTypeMap, function(type, records) {
2402
      var ids = records.mapProperty('id'),
2403
          adapter = this.adapterFor(type);
2404

    
2405
      Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter);
2406
      Ember.assert("You tried to load many records but your adapter does not implement `findMany`", adapter.findMany);
2407

    
2408
      resolver.resolve(_findMany(adapter, this, type, ids, owner));
2409
    }, this);
2410
  },
2411

    
2412
  /**
2413
    Returns true if a record for a given type and ID is already loaded.
2414

2415
    @method hasRecordForId
2416
    @param {String or subclass of DS.Model} type
2417
    @param {String|Integer} id
2418
    @returns {Boolean}
2419
  */
2420
  hasRecordForId: function(type, id) {
2421
    id = coerceId(id);
2422
    type = this.modelFor(type);
2423
    return !!this.typeMapFor(type).idToRecord[id];
2424
  },
2425

    
2426
  /**
2427
    Returns id record for a given type and ID. If one isn't already loaded,
2428
    it builds a new record and leaves it in the `empty` state.
2429

2430
    @method recordForId
2431
    @private
2432
    @param {String or subclass of DS.Model} type
2433
    @param {String|Integer} id
2434
    @returns {DS.Model} record
2435
  */
2436
  recordForId: function(type, id) {
2437
    type = this.modelFor(type);
2438

    
2439
    id = coerceId(id);
2440

    
2441
    var record = this.typeMapFor(type).idToRecord[id];
2442

    
2443
    if (!record) {
2444
      record = this.buildRecord(type, id);
2445
    }
2446

    
2447
    return record;
2448
  },
2449

    
2450
  /**
2451
    @method findMany
2452
    @private
2453
    @param {DS.Model} owner
2454
    @param {Array} records
2455
    @param {String or subclass of DS.Model} type
2456
    @param {Resolver} resolver
2457
    @return {DS.ManyArray} records
2458
  */
2459
  findMany: function(owner, records, type, resolver) {
2460
    type = this.modelFor(type);
2461

    
2462
    records = Ember.A(records);
2463

    
2464
    var unloadedRecords = records.filterProperty('isEmpty', true),
2465
        manyArray = this.recordArrayManager.createManyArray(type, records);
2466

    
2467
    forEach(unloadedRecords, function(record) {
2468
      record.loadingData();
2469
    });
2470

    
2471
    manyArray.loadingRecordsCount = unloadedRecords.length;
2472

    
2473
    if (unloadedRecords.length) {
2474
      forEach(unloadedRecords, function(record) {
2475
        this.recordArrayManager.registerWaitingRecordArray(record, manyArray);
2476
      }, this);
2477

    
2478
      this.fetchMany(unloadedRecords, owner, resolver);
2479
    } else {
2480
      if (resolver) { resolver.resolve(); }
2481
      manyArray.set('isLoaded', true);
2482
      Ember.run.once(manyArray, 'trigger', 'didLoad');
2483
    }
2484

    
2485
    return manyArray;
2486
  },
2487

    
2488
  /**
2489
    If a relationship was originally populated by the adapter as a link
2490
    (as opposed to a list of IDs), this method is called when the
2491
    relationship is fetched.
2492

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

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

2499
    @method findHasMany
2500
    @private
2501
    @param {DS.Model} owner
2502
    @param {any} link
2503
    @param {String or subclass of DS.Model} type
2504
    @param {Resolver} resolver
2505
    @return {DS.ManyArray}
2506
  */
2507
  findHasMany: function(owner, link, relationship, resolver) {
2508
    var adapter = this.adapterFor(owner.constructor);
2509

    
2510
    Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter);
2511
    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

    
2513
    var records = this.recordArrayManager.createManyArray(relationship.type, Ember.A([]));
2514
    resolver.resolve(_findHasMany(adapter, this, owner, link, relationship));
2515
    return records;
2516
  },
2517

    
2518
  /**
2519
    @method findBelongsTo
2520
    @private
2521
    @param {DS.Model} owner
2522
    @param {any} link
2523
    @param {Relationship} relationship
2524
    @param {Resolver} resolver
2525
  */
2526
  findBelongsTo: function(owner, link, relationship, resolver) {
2527
    var adapter = this.adapterFor(owner.constructor);
2528

    
2529
    Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter);
2530
    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

    
2532
    resolver.resolve(_findBelongsTo(adapter, this, owner, link, relationship));
2533
  },
2534

    
2535
  /**
2536
    This method delegates a query to the adapter. This is the one place where
2537
    adapter-level semantics are exposed to the application.
2538

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

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

2546
    @method findQuery
2547
    @private
2548
    @param {String or subclass of DS.Model} type
2549
    @param {any} query an opaque query to be used by the adapter
2550
    @return {Promise} promise
2551
  */
2552
  findQuery: function(type, query) {
2553
    type = this.modelFor(type);
2554

    
2555
    var array = this.recordArrayManager
2556
      .createAdapterPopulatedRecordArray(type, query);
2557

    
2558
    var adapter = this.adapterFor(type),
2559
        promiseLabel = "DS: Store#findQuery " + type,
2560
        resolver = Ember.RSVP.defer(promiseLabel);
2561

    
2562
    Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter);
2563
    Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", adapter.findQuery);
2564

    
2565
    resolver.resolve(_findQuery(adapter, this, type, query, array));
2566

    
2567
    return promiseArray(resolver.promise);
2568
  },
2569

    
2570
  /**
2571
    This method returns an array of all records adapter can find.
2572
    It triggers the adapter's `findAll` method to give it an opportunity to populate
2573
    the array with records of that type.
2574

2575
    @method findAll
2576
    @private
2577
    @param {String or subclass of DS.Model} type
2578
    @return {DS.AdapterPopulatedRecordArray}
2579
  */
2580
  findAll: function(type) {
2581
    type = this.modelFor(type);
2582

    
2583
    return this.fetchAll(type, this.all(type));
2584
  },
2585

    
2586
  /**
2587
    @method fetchAll
2588
    @private
2589
    @param {DS.Model} type
2590
    @param {DS.RecordArray} array
2591
    @returns {Promise} promise
2592
  */
2593
  fetchAll: function(type, array) {
2594
    var adapter = this.adapterFor(type),
2595
        sinceToken = this.typeMapFor(type).metadata.since;
2596

    
2597
    set(array, 'isUpdating', true);
2598

    
2599
    Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter);
2600
    Ember.assert("You tried to load all records but your adapter does not implement `findAll`", adapter.findAll);
2601

    
2602
    return promiseArray(_findAll(adapter, this, type, sinceToken));
2603
  },
2604

    
2605
  /**
2606
    @method didUpdateAll
2607
    @param {DS.Model} type
2608
  */
2609
  didUpdateAll: function(type) {
2610
    var findAllCache = this.typeMapFor(type).findAllCache;
2611
    set(findAllCache, 'isUpdating', false);
2612
  },
2613

    
2614
  /**
2615
    This method returns a filtered array that contains all of the known records
2616
    for a given type.
2617

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

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

2624
    Example
2625

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

2630
    @method all
2631
    @param {String or subclass of DS.Model} type
2632
    @return {DS.RecordArray}
2633
  */
2634
  all: function(type) {
2635
    type = this.modelFor(type);
2636

    
2637
    var typeMap = this.typeMapFor(type),
2638
        findAllCache = typeMap.findAllCache;
2639

    
2640
    if (findAllCache) { return findAllCache; }
2641

    
2642
    var array = this.recordArrayManager.createRecordArray(type);
2643

    
2644
    typeMap.findAllCache = array;
2645
    return array;
2646
  },
2647

    
2648

    
2649
  /**
2650
    This method unloads all of the known records for a given type.
2651

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

2656
    @method unloadAll
2657
    @param {String or subclass of DS.Model} type
2658
  */
2659
  unloadAll: function(type) {
2660
    type = this.modelFor(type);
2661

    
2662
    var typeMap = this.typeMapFor(type),
2663
        records = typeMap.records.splice(0), record;
2664

    
2665
    while(record = records.pop()) {
2666
      record.unloadRecord();
2667
    }
2668

    
2669
    typeMap.findAllCache = null;
2670
  },
2671

    
2672
  /**
2673
    Takes a type and filter function, and returns a live RecordArray that
2674
    remains up to date as new records are loaded into the store or created
2675
    locally.
2676

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

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

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

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

2692
    Example
2693

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

2705
    @method filter
2706
    @param {String or subclass of DS.Model} type
2707
    @param {Object} query optional query
2708
    @param {Function} filter
2709
    @return {DS.PromiseArray}
2710
  */
2711
  filter: function(type, query, filter) {
2712
    var promise;
2713

    
2714
    // allow an optional server query
2715
    if (arguments.length === 3) {
2716
      promise = this.findQuery(type, query);
2717
    } else if (arguments.length === 2) {
2718
      filter = query;
2719
    }
2720

    
2721
    type = this.modelFor(type);
2722

    
2723
    var array = this.recordArrayManager
2724
      .createFilteredRecordArray(type, filter);
2725
    promise = promise || resolve(array);
2726

    
2727
    return promiseArray(promise.then(function() {
2728
      return array;
2729
    }, null, "DS: Store#filter of " + type));
2730
  },
2731

    
2732
  /**
2733
    This method returns if a certain record is already loaded
2734
    in the store. Use this function to know beforehand if a find()
2735
    will result in a request or that it will be a cache hit.
2736

2737
     Example
2738

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

2746
    @method recordIsLoaded
2747
    @param {String or subclass of DS.Model} type
2748
    @param {string} id
2749
    @return {boolean}
2750
  */
2751
  recordIsLoaded: function(type, id) {
2752
    if (!this.hasRecordForId(type, id)) { return false; }
2753
    return !get(this.recordForId(type, id), 'isEmpty');
2754
  },
2755

    
2756
  /**
2757
    This method returns the metadata for a specific type.
2758

2759
    @method metadataFor
2760
    @param {String or subclass of DS.Model} type
2761
    @return {object}
2762
  */
2763
  metadataFor: function(type) {
2764
    type = this.modelFor(type);
2765
    return this.typeMapFor(type).metadata;
2766
  },
2767

    
2768
  // ............
2769
  // . UPDATING .
2770
  // ............
2771

    
2772
  /**
2773
    If the adapter updates attributes or acknowledges creation
2774
    or deletion, the record will notify the store to update its
2775
    membership in any filters.
2776
    To avoid thrashing, this method is invoked only once per
2777

2778
    run loop per record.
2779

2780
    @method dataWasUpdated
2781
    @private
2782
    @param {Class} type
2783
    @param {DS.Model} record
2784
  */
2785
  dataWasUpdated: function(type, record) {
2786
    this.recordArrayManager.recordDidChange(record);
2787
  },
2788

    
2789
  // ..............
2790
  // . PERSISTING .
2791
  // ..............
2792

    
2793
  /**
2794
    This method is called by `record.save`, and gets passed a
2795
    resolver for the promise that `record.save` returns.
2796

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

2799
    @method scheduleSave
2800
    @private
2801
    @param {DS.Model} record
2802
    @param {Resolver} resolver
2803
  */
2804
  scheduleSave: function(record, resolver) {
2805
    record.adapterWillCommit();
2806
    this._pendingSave.push([record, resolver]);
2807
    once(this, 'flushPendingSave');
2808
  },
2809

    
2810
  /**
2811
    This method is called at the end of the run loop, and
2812
    flushes any records passed into `scheduleSave`
2813

2814
    @method flushPendingSave
2815
    @private
2816
  */
2817
  flushPendingSave: function() {
2818
    var pending = this._pendingSave.slice();
2819
    this._pendingSave = [];
2820

    
2821
    forEach(pending, function(tuple) {
2822
      var record = tuple[0], resolver = tuple[1],
2823
          adapter = this.adapterFor(record.constructor),
2824
          operation;
2825

    
2826
      if (get(record, 'isNew')) {
2827
        operation = 'createRecord';
2828
      } else if (get(record, 'isDeleted')) {
2829
        operation = 'deleteRecord';
2830
      } else {
2831
        operation = 'updateRecord';
2832
      }
2833

    
2834
      resolver.resolve(_commit(adapter, this, operation, record));
2835
    }, this);
2836
  },
2837

    
2838
  /**
2839
    This method is called once the promise returned by an
2840
    adapter's `createRecord`, `updateRecord` or `deleteRecord`
2841
    is resolved.
2842

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

2846
    @method didSaveRecord
2847
    @private
2848
    @param {DS.Model} record the in-flight record
2849
    @param {Object} data optional data (see above)
2850
  */
2851
  didSaveRecord: function(record, data) {
2852
    if (data) {
2853
      // normalize relationship IDs into records
2854
      data = normalizeRelationships(this, record.constructor, data, record);
2855

    
2856
      this.updateId(record, data);
2857
    }
2858

    
2859
    record.adapterDidCommit(data);
2860
  },
2861

    
2862
  /**
2863
    This method is called once the promise returned by an
2864
    adapter's `createRecord`, `updateRecord` or `deleteRecord`
2865
    is rejected with a `DS.InvalidError`.
2866

2867
    @method recordWasInvalid
2868
    @private
2869
    @param {DS.Model} record
2870
    @param {Object} errors
2871
  */
2872
  recordWasInvalid: function(record, errors) {
2873
    record.adapterDidInvalidate(errors);
2874
  },
2875

    
2876
  /**
2877
    This method is called once the promise returned by an
2878
    adapter's `createRecord`, `updateRecord` or `deleteRecord`
2879
    is rejected (with anything other than a `DS.InvalidError`).
2880

2881
    @method recordWasError
2882
    @private
2883
    @param {DS.Model} record
2884
  */
2885
  recordWasError: function(record) {
2886
    record.adapterDidError();
2887
  },
2888

    
2889
  /**
2890
    When an adapter's `createRecord`, `updateRecord` or `deleteRecord`
2891
    resolves with data, this method extracts the ID from the supplied
2892
    data.
2893

2894
    @method updateId
2895
    @private
2896
    @param {DS.Model} record
2897
    @param {Object} data
2898
  */
2899
  updateId: function(record, data) {
2900
    var oldId = get(record, 'id'),
2901
        id = coerceId(data.id);
2902

    
2903
    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

    
2905
    this.typeMapFor(record.constructor).idToRecord[id] = record;
2906

    
2907
    set(record, 'id', id);
2908
  },
2909

    
2910
  /**
2911
    Returns a map of IDs to client IDs for a given type.
2912

2913
    @method typeMapFor
2914
    @private
2915
    @param type
2916
    @return {Object} typeMap
2917
  */
2918
  typeMapFor: function(type) {
2919
    var typeMaps = get(this, 'typeMaps'),
2920
        guid = Ember.guidFor(type),
2921
        typeMap;
2922

    
2923
    typeMap = typeMaps[guid];
2924

    
2925
    if (typeMap) { return typeMap; }
2926

    
2927
    typeMap = {
2928
      idToRecord: {},
2929
      records: [],
2930
      metadata: {}
2931
    };
2932

    
2933
    typeMaps[guid] = typeMap;
2934

    
2935
    return typeMap;
2936
  },
2937

    
2938
  // ................
2939
  // . LOADING DATA .
2940
  // ................
2941

    
2942
  /**
2943
    This internal method is used by `push`.
2944

2945
    @method _load
2946
    @private
2947
    @param {String or subclass of DS.Model} type
2948
    @param {Object} data
2949
    @param {Boolean} partial the data should be merged into
2950
      the existing data, not replace it.
2951
  */
2952
  _load: function(type, data, partial) {
2953
    var id = coerceId(data.id),
2954
        record = this.recordForId(type, id);
2955

    
2956
    record.setupData(data, partial);
2957
    this.recordArrayManager.recordDidChange(record);
2958

    
2959
    return record;
2960
  },
2961

    
2962
  /**
2963
    Returns a model class for a particular key. Used by
2964
    methods that take a type key (like `find`, `createRecord`,
2965
    etc.)
2966

2967
    @method modelFor
2968
    @param {String or subclass of DS.Model} key
2969
    @returns {subclass of DS.Model}
2970
  */
2971
  modelFor: function(key) {
2972
    var factory;
2973

    
2974

    
2975
    if (typeof key === 'string') {
2976
      var normalizedKey = this.container.normalize('model:' + key);
2977

    
2978
      factory = this.container.lookupFactory(normalizedKey);
2979
      if (!factory) { throw new Ember.Error("No model was found for '" + key + "'"); }
2980
      factory.typeKey = normalizedKey.split(':', 2)[1];
2981
    } else {
2982
      // A factory already supplied.
2983
      factory = key;
2984
    }
2985

    
2986
    factory.store = this;
2987
    return factory;
2988
  },
2989

    
2990
  /**
2991
    Push some data for a given type into the store.
2992

2993
    This method expects normalized data:
2994

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

3003
    For this model:
3004

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

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

3014
    To represent the children as IDs:
3015

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

3025
    To represent the children relationship as a URL:
3026

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

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

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

3045
    @method push
3046
    @param {String or subclass of DS.Model} type
3047
    @param {Object} data
3048
    @returns {DS.Model} the record that was created or
3049
      updated.
3050
  */
3051
  push: function(type, data, _partial) {
3052
    // _partial is an internal param used by `update`.
3053
    // If passed, it means that the data should be
3054
    // merged into the existing data, not replace it.
3055

    
3056
    Ember.assert("You must include an `id` in a hash passed to `push`", data.id != null);
3057

    
3058
    type = this.modelFor(type);
3059

    
3060
    // normalize relationship IDs into records
3061
    data = normalizeRelationships(this, type, data);
3062

    
3063
    this._load(type, data, _partial);
3064

    
3065
    return this.recordForId(type, data.id);
3066
  },
3067

    
3068
  /**
3069
    Push some raw data into the store.
3070

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

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

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

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

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

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

3096
    @method pushPayload
3097
    @param {String} type
3098
    @param {Object} payload
3099
    @return {DS.Model} the record that was created or updated.
3100
  */
3101
  pushPayload: function (type, payload) {
3102
    var serializer;
3103
    if (!payload) {
3104
      payload = type;
3105
      serializer = defaultSerializer(this.container);
3106
      Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", serializer.pushPayload);
3107
    } else {
3108
      serializer = this.serializerFor(type);
3109
    }
3110
    serializer.pushPayload(this, payload);
3111
  },
3112

    
3113
  update: function(type, data) {
3114
    Ember.assert("You must include an `id` in a hash passed to `update`", data.id != null);
3115

    
3116
    return this.push(type, data, true);
3117
  },
3118

    
3119
  /**
3120
    If you have an Array of normalized data to push,
3121
    you can call `pushMany` with the Array, and it will
3122
    call `push` repeatedly for you.
3123

3124
    @method pushMany
3125
    @param {String or subclass of DS.Model} type
3126
    @param {Array} datas
3127
    @return {Array}
3128
  */
3129
  pushMany: function(type, datas) {
3130
    return map(datas, function(data) {
3131
      return this.push(type, data);
3132
    }, this);
3133
  },
3134

    
3135
  /**
3136
    If you have some metadata to set for a type
3137
    you can call `metaForType`.
3138

3139
    @method metaForType
3140
    @param {String or subclass of DS.Model} type
3141
    @param {Object} metadata
3142
  */
3143
  metaForType: function(type, metadata) {
3144
    type = this.modelFor(type);
3145

    
3146
    Ember.merge(this.typeMapFor(type).metadata, metadata);
3147
  },
3148

    
3149
  /**
3150
    Build a brand new record for a given type, ID, and
3151
    initial data.
3152

3153
    @method buildRecord
3154
    @private
3155
    @param {subclass of DS.Model} type
3156
    @param {String} id
3157
    @param {Object} data
3158
    @returns {DS.Model} record
3159
  */
3160
  buildRecord: function(type, id, data) {
3161
    var typeMap = this.typeMapFor(type),
3162
        idToRecord = typeMap.idToRecord;
3163

    
3164
    Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]);
3165

    
3166
    // lookupFactory should really return an object that creates
3167
    // instances with the injections applied
3168
    var record = type._create({
3169
      id: id,
3170
      store: this,
3171
      container: this.container
3172
    });
3173

    
3174
    if (data) {
3175
      record.setupData(data);
3176
    }
3177

    
3178
    // if we're creating an item, this process will be done
3179
    // later, once the object has been persisted.
3180
    if (id) {
3181
      idToRecord[id] = record;
3182
    }
3183

    
3184
    typeMap.records.push(record);
3185

    
3186
    return record;
3187
  },
3188

    
3189
  // ...............
3190
  // . DESTRUCTION .
3191
  // ...............
3192

    
3193
  /**
3194
    When a record is destroyed, this un-indexes it and
3195
    removes it from any record arrays so it can be GCed.
3196

3197
    @method dematerializeRecord
3198
    @private
3199
    @param {DS.Model} record
3200
  */
3201
  dematerializeRecord: function(record) {
3202
    var type = record.constructor,
3203
        typeMap = this.typeMapFor(type),
3204
        id = get(record, 'id');
3205

    
3206
    record.updateRecordArrays();
3207

    
3208
    if (id) {
3209
      delete typeMap.idToRecord[id];
3210
    }
3211

    
3212
    var loc = indexOf(typeMap.records, record);
3213
    typeMap.records.splice(loc, 1);
3214
  },
3215

    
3216
  // ........................
3217
  // . RELATIONSHIP CHANGES .
3218
  // ........................
3219

    
3220
  addRelationshipChangeFor: function(childRecord, childKey, parentRecord, parentKey, change) {
3221
    var clientId = childRecord.clientId,
3222
        parentClientId = parentRecord ? parentRecord : parentRecord;
3223
    var key = childKey + parentKey;
3224
    var changes = this._relationshipChanges;
3225
    if (!(clientId in changes)) {
3226
      changes[clientId] = {};
3227
    }
3228
    if (!(parentClientId in changes[clientId])) {
3229
      changes[clientId][parentClientId] = {};
3230
    }
3231
    if (!(key in changes[clientId][parentClientId])) {
3232
      changes[clientId][parentClientId][key] = {};
3233
    }
3234
    changes[clientId][parentClientId][key][change.changeType] = change;
3235
  },
3236

    
3237
  removeRelationshipChangeFor: function(clientRecord, childKey, parentRecord, parentKey, type) {
3238
    var clientId = clientRecord.clientId,
3239
        parentClientId = parentRecord ? parentRecord.clientId : parentRecord;
3240
    var changes = this._relationshipChanges;
3241
    var key = childKey + parentKey;
3242
    if (!(clientId in changes) || !(parentClientId in changes[clientId]) || !(key in changes[clientId][parentClientId])){
3243
      return;
3244
    }
3245
    delete changes[clientId][parentClientId][key][type];
3246
  },
3247

    
3248
  relationshipChangePairsFor: function(record){
3249
    var toReturn = [];
3250

    
3251
    if( !record ) { return toReturn; }
3252

    
3253
    //TODO(Igor) What about the other side
3254
    var changesObject = this._relationshipChanges[record.clientId];
3255
    for (var objKey in changesObject){
3256
      if(changesObject.hasOwnProperty(objKey)){
3257
        for (var changeKey in changesObject[objKey]){
3258
          if(changesObject[objKey].hasOwnProperty(changeKey)){
3259
            toReturn.push(changesObject[objKey][changeKey]);
3260
          }
3261
        }
3262
      }
3263
    }
3264
    return toReturn;
3265
  },
3266

    
3267
  // ......................
3268
  // . PER-TYPE ADAPTERS
3269
  // ......................
3270

    
3271
  /**
3272
    Returns the adapter for a given type.
3273

3274
    @method adapterFor
3275
    @private
3276
    @param {subclass of DS.Model} type
3277
    @returns DS.Adapter
3278
  */
3279
  adapterFor: function(type) {
3280
    var container = this.container, adapter;
3281

    
3282
    if (container) {
3283
      adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application');
3284
    }
3285

    
3286
    return adapter || get(this, 'defaultAdapter');
3287
  },
3288

    
3289
  // ..............................
3290
  // . RECORD CHANGE NOTIFICATION .
3291
  // ..............................
3292

    
3293
  /**
3294
    Returns an instance of the serializer for a given type. For
3295
    example, `serializerFor('person')` will return an instance of
3296
    `App.PersonSerializer`.
3297

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

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

3305
    @method serializerFor
3306
    @private
3307
    @param {String} type the record to serialize
3308
    @return {DS.Serializer}
3309
  */
3310
  serializerFor: function(type) {
3311
    type = this.modelFor(type);
3312
    var adapter = this.adapterFor(type);
3313

    
3314
    return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer);
3315
  }
3316
});
3317

    
3318
function normalizeRelationships(store, type, data, record) {
3319
  type.eachRelationship(function(key, relationship) {
3320
    // A link (usually a URL) was already provided in
3321
    // normalized form
3322
    if (data.links && data.links[key]) {
3323
      if (record && relationship.options.async) { record._relationships[key] = null; }
3324
      return;
3325
    }
3326

    
3327
    var kind = relationship.kind,
3328
        value = data[key];
3329

    
3330
    if (value == null) { return; }
3331

    
3332
    if (kind === 'belongsTo') {
3333
      deserializeRecordId(store, data, key, relationship, value);
3334
    } else if (kind === 'hasMany') {
3335
      deserializeRecordIds(store, data, key, relationship, value);
3336
      addUnsavedRecords(record, key, value);
3337
    }
3338
  });
3339

    
3340
  return data;
3341
}
3342

    
3343
function deserializeRecordId(store, data, key, relationship, id) {
3344
  if (isNone(id) || id instanceof DS.Model) {
3345
    return;
3346
  }
3347

    
3348
  var type;
3349

    
3350
  if (typeof id === 'number' || typeof id === 'string') {
3351
    type = typeFor(relationship, key, data);
3352
    data[key] = store.recordForId(type, id);
3353
  } else if (typeof id === 'object') {
3354
    // polymorphic
3355
    data[key] = store.recordForId(id.type, id.id);
3356
  }
3357
}
3358

    
3359
function typeFor(relationship, key, data) {
3360
  if (relationship.options.polymorphic) {
3361
    return data[key + "Type"];
3362
  } else {
3363
    return relationship.type;
3364
  }
3365
}
3366

    
3367
function deserializeRecordIds(store, data, key, relationship, ids) {
3368
  for (var i=0, l=ids.length; i<l; i++) {
3369
    deserializeRecordId(store, ids, i, relationship, ids[i]);
3370
  }
3371
}
3372

    
3373
// If there are any unsaved records that are in a hasMany they won't be
3374
// in the payload, so add them back in manually.
3375
function addUnsavedRecords(record, key, data) {
3376
  if(record) {
3377
    data.pushObjects(record.get(key).filterBy('isNew'));
3378
  }
3379
}
3380

    
3381
// Delegation to the adapter and promise management
3382
/**
3383
  A `PromiseArray` is an object that acts like both an `Ember.Array`
3384
  and a promise. When the promise is resolved the the resulting value
3385
  will be set to the `PromiseArray`'s `content` property. This makes
3386
  it easy to create data bindings with the `PromiseArray` that will be
3387
  updated when the promise resolves.
3388

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

3392
  Example
3393

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

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

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

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

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

3422
  Example
3423

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

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

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

3436
  @class PromiseObject
3437
  @namespace DS
3438
  @extends Ember.ObjectProxy
3439
  @uses Ember.PromiseProxyMixin
3440
*/
3441
DS.PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin);
3442

    
3443
function promiseObject(promise) {
3444
  return DS.PromiseObject.create({ promise: promise });
3445
}
3446

    
3447
function promiseArray(promise) {
3448
  return DS.PromiseArray.create({ promise: promise });
3449
}
3450

    
3451
function isThenable(object) {
3452
  return object && typeof object.then === 'function';
3453
}
3454

    
3455
function serializerFor(container, type, defaultSerializer) {
3456
  return container.lookup('serializer:'+type) ||
3457
                 container.lookup('serializer:application') ||
3458
                 container.lookup('serializer:' + defaultSerializer) ||
3459
                 container.lookup('serializer:_default');
3460
}
3461

    
3462
function defaultSerializer(container) {
3463
  return container.lookup('serializer:application') ||
3464
         container.lookup('serializer:_default');
3465
}
3466

    
3467
function serializerForAdapter(adapter, type) {
3468
  var serializer = adapter.serializer,
3469
      defaultSerializer = adapter.defaultSerializer,
3470
      container = adapter.container;
3471

    
3472
  if (container && serializer === undefined) {
3473
    serializer = serializerFor(container, type.typeKey, defaultSerializer);
3474
  }
3475

    
3476
  if (serializer === null || serializer === undefined) {
3477
    serializer = {
3478
      extract: function(store, type, payload) { return payload; }
3479
    };
3480
  }
3481

    
3482
  return serializer;
3483
}
3484

    
3485
function _find(adapter, store, type, id) {
3486
  var promise = adapter.find(store, type, id),
3487
      serializer = serializerForAdapter(adapter, type);
3488

    
3489
  return resolve(promise, "DS: Handle Adapter#find of " + type + " with id: " + id).then(function(payload) {
3490
    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
    payload = serializer.extract(store, type, payload, id, 'find');
3492

    
3493
    return store.push(type, payload);
3494
  }, function(error) {
3495
    var record = store.getById(type, id);
3496
    record.notFound();
3497
    throw error;
3498
  }, "DS: Extract payload of '" + type + "'");
3499
}
3500

    
3501
function _findMany(adapter, store, type, ids, owner) {
3502
  var promise = adapter.findMany(store, type, ids, owner),
3503
      serializer = serializerForAdapter(adapter, type);
3504

    
3505
  return resolve(promise, "DS: Handle Adapter#findMany of " + type).then(function(payload) {
3506
    payload = serializer.extract(store, type, payload, null, 'findMany');
3507

    
3508
    Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3509

    
3510
    store.pushMany(type, payload);
3511
  }, null, "DS: Extract payload of " + type);
3512
}
3513

    
3514
function _findHasMany(adapter, store, record, link, relationship) {
3515
  var promise = adapter.findHasMany(store, record, link, relationship),
3516
      serializer = serializerForAdapter(adapter, relationship.type);
3517

    
3518
  return resolve(promise, "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type).then(function(payload) {
3519
    payload = serializer.extract(store, relationship.type, payload, null, 'findHasMany');
3520

    
3521
    Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3522

    
3523
    var records = store.pushMany(relationship.type, payload);
3524
    record.updateHasMany(relationship.key, records);
3525
  }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type);
3526
}
3527

    
3528
function _findBelongsTo(adapter, store, record, link, relationship) {
3529
  var promise = adapter.findBelongsTo(store, record, link, relationship),
3530
      serializer = serializerForAdapter(adapter, relationship.type);
3531

    
3532
  return resolve(promise, "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type).then(function(payload) {
3533
    payload = serializer.extract(store, relationship.type, payload, null, 'findBelongsTo');
3534

    
3535
    var record = store.push(relationship.type, payload);
3536
    record.updateBelongsTo(relationship.key, record);
3537
    return record;
3538
  }, null, "DS: Extract payload of " + record + " : " + relationship.type);
3539
}
3540

    
3541
function _findAll(adapter, store, type, sinceToken) {
3542
  var promise = adapter.findAll(store, type, sinceToken),
3543
      serializer = serializerForAdapter(adapter, type);
3544

    
3545
  return resolve(promise, "DS: Handle Adapter#findAll of " + type).then(function(payload) {
3546
    payload = serializer.extract(store, type, payload, null, 'findAll');
3547

    
3548
    Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3549

    
3550
    store.pushMany(type, payload);
3551
    store.didUpdateAll(type);
3552
    return store.all(type);
3553
  }, null, "DS: Extract payload of findAll " + type);
3554
}
3555

    
3556
function _findQuery(adapter, store, type, query, recordArray) {
3557
  var promise = adapter.findQuery(store, type, query, recordArray),
3558
      serializer = serializerForAdapter(adapter, type);
3559

    
3560
  return resolve(promise, "DS: Handle Adapter#findQuery of " + type).then(function(payload) {
3561
    payload = serializer.extract(store, type, payload, null, 'findQuery');
3562

    
3563
    Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
3564

    
3565
    recordArray.load(payload);
3566
    return recordArray;
3567
  }, null, "DS: Extract payload of findQuery " + type);
3568
}
3569

    
3570
function _commit(adapter, store, operation, record) {
3571
  var type = record.constructor,
3572
      promise = adapter[operation](store, type, record),
3573
      serializer = serializerForAdapter(adapter, type);
3574

    
3575
  Ember.assert("Your adapter's '" + operation + "' method must return a promise, but it returned " + promise, isThenable(promise));
3576

    
3577
  return promise.then(function(payload) {
3578
    if (payload) { payload = serializer.extract(store, type, payload, get(record, 'id'), operation); }
3579
    store.didSaveRecord(record, payload);
3580
    return record;
3581
  }, function(reason) {
3582
    if (reason instanceof DS.InvalidError) {
3583
      store.recordWasInvalid(record, reason.errors);
3584
    } else {
3585
      store.recordWasError(record, reason);
3586
    }
3587

    
3588
    throw reason;
3589
  }, "DS: Extract and notify about " + operation + " completion of " + record);
3590
}
3591

    
3592
})();
3593

    
3594

    
3595

    
3596
(function() {
3597
/**
3598
  @module ember-data
3599
*/
3600

    
3601
var get = Ember.get, set = Ember.set;
3602
/*
3603
  This file encapsulates the various states that a record can transition
3604
  through during its lifecycle.
3605
*/
3606
/**
3607
  ### State
3608

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

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

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

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

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

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

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

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

3671
  ### Events and Flags
3672

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

3675
  #### Events
3676

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

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

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

3695
  To trigger this event:
3696

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

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

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

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

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

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

3728
  #### Flags
3729

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

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

3741
  You can say:
3742

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

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

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

3757

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

3767
  @namespace DS
3768
  @class RootState
3769
*/
3770

    
3771
var hasDefinedProperties = function(object) {
3772
  // Ignore internal property defined by simulated `Ember.create`.
3773
  var names = Ember.keys(object);
3774
  var i, l, name;
3775
  for (i = 0, l = names.length; i < l; i++ ) {
3776
    name = names[i];
3777
    if (object.hasOwnProperty(name) && object[name]) { return true; }
3778
  }
3779

    
3780
  return false;
3781
};
3782

    
3783
var didSetProperty = function(record, context) {
3784
  if (context.value === context.originalValue) {
3785
    delete record._attributes[context.name];
3786
    record.send('propertyWasReset', context.name);
3787
  } else if (context.value !== context.oldValue) {
3788
    record.send('becomeDirty');
3789
  }
3790

    
3791
  record.updateRecordArraysLater();
3792
};
3793

    
3794
// Implementation notes:
3795
//
3796
// Each state has a boolean value for all of the following flags:
3797
//
3798
// * isLoaded: The record has a populated `data` property. When a
3799
//   record is loaded via `store.find`, `isLoaded` is false
3800
//   until the adapter sets it. When a record is created locally,
3801
//   its `isLoaded` property is always true.
3802
// * isDirty: The record has local changes that have not yet been
3803
//   saved by the adapter. This includes records that have been
3804
//   created (but not yet saved) or deleted.
3805
// * isSaving: The record has been committed, but
3806
//   the adapter has not yet acknowledged that the changes have
3807
//   been persisted to the backend.
3808
// * isDeleted: The record was marked for deletion. When `isDeleted`
3809
//   is true and `isDirty` is true, the record is deleted locally
3810
//   but the deletion was not yet persisted. When `isSaving` is
3811
//   true, the change is in-flight. When both `isDirty` and
3812
//   `isSaving` are false, the change has persisted.
3813
// * isError: The adapter reported that it was unable to save
3814
//   local changes to the backend. This may also result in the
3815
//   record having its `isValid` property become false if the
3816
//   adapter reported that server-side validations failed.
3817
// * isNew: The record was created on the client and the adapter
3818
//   did not yet report that it was successfully saved.
3819
// * isValid: No client-side validations have failed and the
3820
//   adapter did not report any server-side validation failures.
3821

    
3822
// The dirty state is a abstract state whose functionality is
3823
// shared between the `created` and `updated` states.
3824
//
3825
// The deleted state shares the `isDirty` flag with the
3826
// subclasses of `DirtyState`, but with a very different
3827
// implementation.
3828
//
3829
// Dirty states have three child states:
3830
//
3831
// `uncommitted`: the store has not yet handed off the record
3832
//   to be saved.
3833
// `inFlight`: the store has handed off the record to be saved,
3834
//   but the adapter has not yet acknowledged success.
3835
// `invalid`: the record has invalid information and cannot be
3836
//   send to the adapter yet.
3837
var DirtyState = {
3838
  initialState: 'uncommitted',
3839

    
3840
  // FLAGS
3841
  isDirty: true,
3842

    
3843
  // SUBSTATES
3844

    
3845
  // When a record first becomes dirty, it is `uncommitted`.
3846
  // This means that there are local pending changes, but they
3847
  // have not yet begun to be saved, and are not invalid.
3848
  uncommitted: {
3849
    // EVENTS
3850
    didSetProperty: didSetProperty,
3851

    
3852
    propertyWasReset: function(record, name) {
3853
      var stillDirty = false;
3854

    
3855
      for (var prop in record._attributes) {
3856
        stillDirty = true;
3857
        break;
3858
      }
3859

    
3860
      if (!stillDirty) { record.send('rolledBack'); }
3861
    },
3862

    
3863
    pushedData: Ember.K,
3864

    
3865
    becomeDirty: Ember.K,
3866

    
3867
    willCommit: function(record) {
3868
      record.transitionTo('inFlight');
3869
    },
3870

    
3871
    reloadRecord: function(record, resolve) {
3872
      resolve(get(record, 'store').reloadRecord(record));
3873
    },
3874

    
3875
    rolledBack: function(record) {
3876
      record.transitionTo('loaded.saved');
3877
    },
3878

    
3879
    becameInvalid: function(record) {
3880
      record.transitionTo('invalid');
3881
    },
3882

    
3883
    rollback: function(record) {
3884
      record.rollback();
3885
    }
3886
  },
3887

    
3888
  // Once a record has been handed off to the adapter to be
3889
  // saved, it is in the 'in flight' state. Changes to the
3890
  // record cannot be made during this window.
3891
  inFlight: {
3892
    // FLAGS
3893
    isSaving: true,
3894

    
3895
    // EVENTS
3896
    didSetProperty: didSetProperty,
3897
    becomeDirty: Ember.K,
3898
    pushedData: Ember.K,
3899

    
3900
    // TODO: More robust semantics around save-while-in-flight
3901
    willCommit: Ember.K,
3902

    
3903
    didCommit: function(record) {
3904
      var dirtyType = get(this, 'dirtyType');
3905

    
3906
      record.transitionTo('saved');
3907
      record.send('invokeLifecycleCallbacks', dirtyType);
3908
    },
3909

    
3910
    becameInvalid: function(record) {
3911
      record.transitionTo('invalid');
3912
      record.send('invokeLifecycleCallbacks');
3913
    },
3914

    
3915
    becameError: function(record) {
3916
      record.transitionTo('uncommitted');
3917
      record.triggerLater('becameError', record);
3918
    }
3919
  },
3920

    
3921
  // A record is in the `invalid` state when its client-side
3922
  // invalidations have failed, or if the adapter has indicated
3923
  // the the record failed server-side invalidations.
3924
  invalid: {
3925
    // FLAGS
3926
    isValid: false,
3927

    
3928
    // EVENTS
3929
    deleteRecord: function(record) {
3930
      record.transitionTo('deleted.uncommitted');
3931
      record.clearRelationships();
3932
    },
3933

    
3934
    didSetProperty: function(record, context) {
3935
      get(record, 'errors').remove(context.name);
3936

    
3937
      didSetProperty(record, context);
3938
    },
3939

    
3940
    becomeDirty: Ember.K,
3941

    
3942
    rolledBack: function(record) {
3943
      get(record, 'errors').clear();
3944
    },
3945

    
3946
    becameValid: function(record) {
3947
      record.transitionTo('uncommitted');
3948
    },
3949

    
3950
    invokeLifecycleCallbacks: function(record) {
3951
      record.triggerLater('becameInvalid', record);
3952
    }
3953
  }
3954
};
3955

    
3956
// The created and updated states are created outside the state
3957
// chart so we can reopen their substates and add mixins as
3958
// necessary.
3959

    
3960
function deepClone(object) {
3961
  var clone = {}, value;
3962

    
3963
  for (var prop in object) {
3964
    value = object[prop];
3965
    if (value && typeof value === 'object') {
3966
      clone[prop] = deepClone(value);
3967
    } else {
3968
      clone[prop] = value;
3969
    }
3970
  }
3971

    
3972
  return clone;
3973
}
3974

    
3975
function mixin(original, hash) {
3976
  for (var prop in hash) {
3977
    original[prop] = hash[prop];
3978
  }
3979

    
3980
  return original;
3981
}
3982

    
3983
function dirtyState(options) {
3984
  var newState = deepClone(DirtyState);
3985
  return mixin(newState, options);
3986
}
3987

    
3988
var createdState = dirtyState({
3989
  dirtyType: 'created',
3990

    
3991
  // FLAGS
3992
  isNew: true
3993
});
3994

    
3995
createdState.uncommitted.rolledBack = function(record) {
3996
  record.transitionTo('deleted.saved');
3997
};
3998

    
3999
var updatedState = dirtyState({
4000
  dirtyType: 'updated'
4001
});
4002

    
4003
createdState.uncommitted.deleteRecord = function(record) {
4004
  record.clearRelationships();
4005
  record.transitionTo('deleted.saved');
4006
};
4007

    
4008
createdState.uncommitted.rollback = function(record) {
4009
  DirtyState.uncommitted.rollback.apply(this, arguments);
4010
  record.transitionTo('deleted.saved');
4011
};
4012

    
4013
updatedState.uncommitted.deleteRecord = function(record) {
4014
  record.transitionTo('deleted.uncommitted');
4015
  record.clearRelationships();
4016
};
4017

    
4018
var RootState = {
4019
  // FLAGS
4020
  isEmpty: false,
4021
  isLoading: false,
4022
  isLoaded: false,
4023
  isDirty: false,
4024
  isSaving: false,
4025
  isDeleted: false,
4026
  isNew: false,
4027
  isValid: true,
4028

    
4029
  // DEFAULT EVENTS
4030

    
4031
  // Trying to roll back if you're not in the dirty state
4032
  // doesn't change your state. For example, if you're in the
4033
  // in-flight state, rolling back the record doesn't move
4034
  // you out of the in-flight state.
4035
  rolledBack: Ember.K,
4036

    
4037
  propertyWasReset: Ember.K,
4038

    
4039
  // SUBSTATES
4040

    
4041
  // A record begins its lifecycle in the `empty` state.
4042
  // If its data will come from the adapter, it will
4043
  // transition into the `loading` state. Otherwise, if
4044
  // the record is being created on the client, it will
4045
  // transition into the `created` state.
4046
  empty: {
4047
    isEmpty: true,
4048

    
4049
    // EVENTS
4050
    loadingData: function(record, promise) {
4051
      record._loadingPromise = promise;
4052
      record.transitionTo('loading');
4053
    },
4054

    
4055
    loadedData: function(record) {
4056
      record.transitionTo('loaded.created.uncommitted');
4057

    
4058
      record.suspendRelationshipObservers(function() {
4059
        record.notifyPropertyChange('data');
4060
      });
4061
    },
4062

    
4063
    pushedData: function(record) {
4064
      record.transitionTo('loaded.saved');
4065
      record.triggerLater('didLoad');
4066
    }
4067
  },
4068

    
4069
  // A record enters this state when the store askes
4070
  // the adapter for its data. It remains in this state
4071
  // until the adapter provides the requested data.
4072
  //
4073
  // Usually, this process is asynchronous, using an
4074
  // XHR to retrieve the data.
4075
  loading: {
4076
    // FLAGS
4077
    isLoading: true,
4078

    
4079
    exit: function(record) {
4080
      record._loadingPromise = null;
4081
    },
4082

    
4083
    // EVENTS
4084
    pushedData: function(record) {
4085
      record.transitionTo('loaded.saved');
4086
      record.triggerLater('didLoad');
4087
      set(record, 'isError', false);
4088
    },
4089

    
4090
    becameError: function(record) {
4091
      record.triggerLater('becameError', record);
4092
    },
4093

    
4094
    notFound: function(record) {
4095
      record.transitionTo('empty');
4096
    }
4097
  },
4098

    
4099
  // A record enters this state when its data is populated.
4100
  // Most of a record's lifecycle is spent inside substates
4101
  // of the `loaded` state.
4102
  loaded: {
4103
    initialState: 'saved',
4104

    
4105
    // FLAGS
4106
    isLoaded: true,
4107

    
4108
    // SUBSTATES
4109

    
4110
    // If there are no local changes to a record, it remains
4111
    // in the `saved` state.
4112
    saved: {
4113
      setup: function(record) {
4114
        var attrs = record._attributes,
4115
            isDirty = false;
4116

    
4117
        for (var prop in attrs) {
4118
          if (attrs.hasOwnProperty(prop)) {
4119
            isDirty = true;
4120
            break;
4121
          }
4122
        }
4123

    
4124
        if (isDirty) {
4125
          record.adapterDidDirty();
4126
        }
4127
      },
4128

    
4129
      // EVENTS
4130
      didSetProperty: didSetProperty,
4131

    
4132
      pushedData: Ember.K,
4133

    
4134
      becomeDirty: function(record) {
4135
        record.transitionTo('updated.uncommitted');
4136
      },
4137

    
4138
      willCommit: function(record) {
4139
        record.transitionTo('updated.inFlight');
4140
      },
4141

    
4142
      reloadRecord: function(record, resolve) {
4143
        resolve(get(record, 'store').reloadRecord(record));
4144
      },
4145

    
4146
      deleteRecord: function(record) {
4147
        record.transitionTo('deleted.uncommitted');
4148
        record.clearRelationships();
4149
      },
4150

    
4151
      unloadRecord: function(record) {
4152
        // clear relationships before moving to deleted state
4153
        // otherwise it fails
4154
        record.clearRelationships();
4155
        record.transitionTo('deleted.saved');
4156
      },
4157

    
4158
      didCommit: function(record) {
4159
        record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType'));
4160
      },
4161

    
4162
      // loaded.saved.notFound would be triggered by a failed
4163
      // `reload()` on an unchanged record
4164
      notFound: Ember.K
4165

    
4166
    },
4167

    
4168
    // A record is in this state after it has been locally
4169
    // created but before the adapter has indicated that
4170
    // it has been saved.
4171
    created: createdState,
4172

    
4173
    // A record is in this state if it has already been
4174
    // saved to the server, but there are new local changes
4175
    // that have not yet been saved.
4176
    updated: updatedState
4177
  },
4178

    
4179
  // A record is in this state if it was deleted from the store.
4180
  deleted: {
4181
    initialState: 'uncommitted',
4182
    dirtyType: 'deleted',
4183

    
4184
    // FLAGS
4185
    isDeleted: true,
4186
    isLoaded: true,
4187
    isDirty: true,
4188

    
4189
    // TRANSITIONS
4190
    setup: function(record) {
4191
      record.updateRecordArrays();
4192
    },
4193

    
4194
    // SUBSTATES
4195

    
4196
    // When a record is deleted, it enters the `start`
4197
    // state. It will exit this state when the record
4198
    // starts to commit.
4199
    uncommitted: {
4200

    
4201
      // EVENTS
4202

    
4203
      willCommit: function(record) {
4204
        record.transitionTo('inFlight');
4205
      },
4206

    
4207
      rollback: function(record) {
4208
        record.rollback();
4209
      },
4210

    
4211
      becomeDirty: Ember.K,
4212
      deleteRecord: Ember.K,
4213

    
4214
      rolledBack: function(record) {
4215
        record.transitionTo('loaded.saved');
4216
      }
4217
    },
4218

    
4219
    // After a record starts committing, but
4220
    // before the adapter indicates that the deletion
4221
    // has saved to the server, a record is in the
4222
    // `inFlight` substate of `deleted`.
4223
    inFlight: {
4224
      // FLAGS
4225
      isSaving: true,
4226

    
4227
      // EVENTS
4228

    
4229
      // TODO: More robust semantics around save-while-in-flight
4230
      willCommit: Ember.K,
4231
      didCommit: function(record) {
4232
        record.transitionTo('saved');
4233

    
4234
        record.send('invokeLifecycleCallbacks');
4235
      },
4236

    
4237
      becameError: function(record) {
4238
        record.transitionTo('uncommitted');
4239
        record.triggerLater('becameError', record);
4240
      }
4241
    },
4242

    
4243
    // Once the adapter indicates that the deletion has
4244
    // been saved, the record enters the `saved` substate
4245
    // of `deleted`.
4246
    saved: {
4247
      // FLAGS
4248
      isDirty: false,
4249

    
4250
      setup: function(record) {
4251
        var store = get(record, 'store');
4252
        store.dematerializeRecord(record);
4253
      },
4254

    
4255
      invokeLifecycleCallbacks: function(record) {
4256
        record.triggerLater('didDelete', record);
4257
        record.triggerLater('didCommit', record);
4258
      }
4259
    }
4260
  },
4261

    
4262
  invokeLifecycleCallbacks: function(record, dirtyType) {
4263
    if (dirtyType === 'created') {
4264
      record.triggerLater('didCreate', record);
4265
    } else {
4266
      record.triggerLater('didUpdate', record);
4267
    }
4268

    
4269
    record.triggerLater('didCommit', record);
4270
  }
4271
};
4272

    
4273
function wireState(object, parent, name) {
4274
  /*jshint proto:true*/
4275
  // TODO: Use Object.create and copy instead
4276
  object = mixin(parent ? Ember.create(parent) : {}, object);
4277
  object.parentState = parent;
4278
  object.stateName = name;
4279

    
4280
  for (var prop in object) {
4281
    if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; }
4282
    if (typeof object[prop] === 'object') {
4283
      object[prop] = wireState(object[prop], object, name + "." + prop);
4284
    }
4285
  }
4286

    
4287
  return object;
4288
}
4289

    
4290
RootState = wireState(RootState, null, "root");
4291

    
4292
DS.RootState = RootState;
4293

    
4294
})();
4295

    
4296

    
4297

    
4298
(function() {
4299
var get = Ember.get, isEmpty = Ember.isEmpty;
4300

    
4301
/**
4302
@module ember-data
4303
*/
4304

    
4305
/**
4306
  Holds validation errors for a given record organized by attribute names.
4307

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

4318
    @method registerHandlers
4319
    @param {Object} target
4320
    @param {Function} becameInvalid
4321
    @param {Function} becameValid
4322
  */
4323
  registerHandlers: function(target, becameInvalid, becameValid) {
4324
    this.on('becameInvalid', target, becameInvalid);
4325
    this.on('becameValid', target, becameValid);
4326
  },
4327

    
4328
  /**
4329
    @property errorsByAttributeName
4330
    @type {Ember.MapWithDefault}
4331
    @private
4332
  */
4333
  errorsByAttributeName: Ember.reduceComputed("content", {
4334
    initialValue: function() {
4335
      return Ember.MapWithDefault.create({
4336
        defaultValue: function() {
4337
          return Ember.A();
4338
        }
4339
      });
4340
    },
4341

    
4342
    addedItem: function(errors, error) {
4343
      errors.get(error.attribute).pushObject(error);
4344

    
4345
      return errors;
4346
    },
4347

    
4348
    removedItem: function(errors, error) {
4349
      errors.get(error.attribute).removeObject(error);
4350

    
4351
      return errors;
4352
    }
4353
  }),
4354

    
4355
  /**
4356
    Returns errors for a given attribute
4357

4358
    @method errorsFor
4359
    @param {String} attribute
4360
    @returns {Array}
4361
  */
4362
  errorsFor: function(attribute) {
4363
    return get(this, 'errorsByAttributeName').get(attribute);
4364
  },
4365

    
4366
  /**
4367
  */
4368
  messages: Ember.computed.mapBy('content', 'message'),
4369

    
4370
  /**
4371
    @property content
4372
    @type {Array}
4373
    @private
4374
  */
4375
  content: Ember.computed(function() {
4376
    return Ember.A();
4377
  }),
4378

    
4379
  /**
4380
    @method unknownProperty
4381
    @private
4382
  */
4383
  unknownProperty: function(attribute) {
4384
    var errors = this.errorsFor(attribute);
4385
    if (isEmpty(errors)) { return null; }
4386
    return errors;
4387
  },
4388

    
4389
  /**
4390
    @method nextObject
4391
    @private
4392
  */
4393
  nextObject: function(index, previousObject, context) {
4394
    return get(this, 'content').objectAt(index);
4395
  },
4396

    
4397
  /**
4398
    Total number of errors.
4399

4400
    @property length
4401
    @type {Number}
4402
    @readOnly
4403
  */
4404
  length: Ember.computed.oneWay('content.length').readOnly(),
4405

    
4406
  /**
4407
    @property isEmpty
4408
    @type {Boolean}
4409
    @readOnly
4410
  */
4411
  isEmpty: Ember.computed.not('length').readOnly(),
4412

    
4413
  /**
4414
    Adds error messages to a given attribute and sends
4415
    `becameInvalid` event to the record.
4416

4417
    @method add
4418
    @param {String} attribute
4419
    @param {Array|String} messages
4420
  */
4421
  add: function(attribute, messages) {
4422
    var wasEmpty = get(this, 'isEmpty');
4423

    
4424
    messages = this._findOrCreateMessages(attribute, messages);
4425
    get(this, 'content').addObjects(messages);
4426

    
4427
    this.notifyPropertyChange(attribute);
4428
    this.enumerableContentDidChange();
4429

    
4430
    if (wasEmpty && !get(this, 'isEmpty')) {
4431
      this.trigger('becameInvalid');
4432
    }
4433
  },
4434

    
4435
  /**
4436
    @method _findOrCreateMessages
4437
    @private
4438
  */
4439
  _findOrCreateMessages: function(attribute, messages) {
4440
    var errors = this.errorsFor(attribute);
4441

    
4442
    return Ember.makeArray(messages).map(function(message) {
4443
      return errors.findBy('message', message) || {
4444
        attribute: attribute,
4445
        message: message
4446
      };
4447
    });
4448
  },
4449

    
4450
  /**
4451
    Removes all error messages from the given attribute and sends
4452
    `becameValid` event to the record if there no more errors left.
4453

4454
    @method remove
4455
    @param {String} attribute
4456
  */
4457
  remove: function(attribute) {
4458
    if (get(this, 'isEmpty')) { return; }
4459

    
4460
    var content = get(this, 'content').rejectBy('attribute', attribute);
4461
    get(this, 'content').setObjects(content);
4462

    
4463
    this.notifyPropertyChange(attribute);
4464
    this.enumerableContentDidChange();
4465

    
4466
    if (get(this, 'isEmpty')) {
4467
      this.trigger('becameValid');
4468
    }
4469
  },
4470

    
4471
  /**
4472
    Removes all error messages and sends `becameValid` event
4473
    to the record.
4474

4475
    @method clear
4476
  */
4477
  clear: function() {
4478
    if (get(this, 'isEmpty')) { return; }
4479

    
4480
    get(this, 'content').clear();
4481
    this.enumerableContentDidChange();
4482

    
4483
    this.trigger('becameValid');
4484
  },
4485

    
4486
  /**
4487
    Checks if there is error messages for the given attribute.
4488

4489
    @method has
4490
    @param {String} attribute
4491
    @returns {Boolean} true if there some errors on given attribute
4492
  */
4493
  has: function(attribute) {
4494
    return !isEmpty(this.errorsFor(attribute));
4495
  }
4496
});
4497

    
4498
})();
4499

    
4500

    
4501

    
4502
(function() {
4503
/**
4504
  @module ember-data
4505
*/
4506

    
4507
var get = Ember.get, set = Ember.set,
4508
    merge = Ember.merge, once = Ember.run.once;
4509

    
4510
var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) {
4511
  return get(get(this, 'currentState'), key);
4512
}).readOnly();
4513

    
4514
/**
4515

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

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

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

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

4555
    Example
4556

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

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

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

4577
    Example
4578

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

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

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

4601
    Example
4602

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

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

4626
    Example
4627

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

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

4646
    Example
4647

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

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

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

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

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

4682
    Example
4683

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

4689
    @property dirtyType
4690
    @type {String}
4691
    @readOnly
4692
  */
4693
  dirtyType: retrieveFromCurrentState,
4694

    
4695
  /**
4696
    If `true` the adapter reported that it was unable to save local
4697
    changes to the backend. This may also result in the record having
4698
    its `isValid` property become false if the adapter reported that
4699
    server-side validations failed.
4700

4701
    Example
4702

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

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

4719
    Example
4720

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

4727
    @property isReloading
4728
    @type {Boolean}
4729
    @readOnly
4730
  */
4731
  isReloading: false,
4732

    
4733
  /**
4734
    The `clientId` property is a transient numerical identifier
4735
    generated at runtime by the data store. It is important
4736
    primarily because newly created objects may not yet have an
4737
    externally generated id.
4738

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

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

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

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

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

4785
    @property errors
4786
    @type {Object}
4787
  */
4788
  errors: null,
4789

    
4790
  /**
4791
    Create a JSON representation of the record, using the serialization
4792
    strategy of the store's adapter.
4793

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

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

4800
    @method serialize
4801
    @param {Object} options
4802
    @returns {Object} an object whose values are primitive JSON values only
4803
  */
4804
  serialize: function(options) {
4805
    var store = get(this, 'store');
4806
    return store.serialize(this, options);
4807
  },
4808

    
4809
  /**
4810
    Use [DS.JSONSerializer](DS.JSONSerializer.html) to
4811
    get the JSON representation of a record.
4812

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

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

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

    
4829
  /**
4830
    Fired when the record is loaded from the server.
4831

4832
    @event didLoad
4833
  */
4834
  didLoad: Ember.K,
4835

    
4836
  /**
4837
    Fired when the record is updated.
4838

4839
    @event didUpdate
4840
  */
4841
  didUpdate: Ember.K,
4842

    
4843
  /**
4844
    Fired when the record is created.
4845

4846
    @event didCreate
4847
  */
4848
  didCreate: Ember.K,
4849

    
4850
  /**
4851
    Fired when the record is deleted.
4852

4853
    @event didDelete
4854
  */
4855
  didDelete: Ember.K,
4856

    
4857
  /**
4858
    Fired when the record becomes invalid.
4859

4860
    @event becameInvalid
4861
  */
4862
  becameInvalid: Ember.K,
4863

    
4864
  /**
4865
    Fired when the record enters the error state.
4866

4867
    @event becameError
4868
  */
4869
  becameError: Ember.K,
4870

    
4871
  /**
4872
    @property data
4873
    @private
4874
    @type {Object}
4875
  */
4876
  data: Ember.computed(function() {
4877
    this._data = this._data || {};
4878
    return this._data;
4879
  }).property(),
4880

    
4881
  _data: null,
4882

    
4883
  init: function() {
4884
    set(this, 'currentState', DS.RootState.empty);
4885
    var errors = DS.Errors.create();
4886
    errors.registerHandlers(this, function() {
4887
      this.send('becameInvalid');
4888
    }, function() {
4889
      this.send('becameValid');
4890
    });
4891
    set(this, 'errors', errors);
4892
    this._super();
4893
    this._setup();
4894
  },
4895

    
4896
  _setup: function() {
4897
    this._changesToSync = {};
4898
    this._deferredTriggers = [];
4899
    this._data = {};
4900
    this._attributes = {};
4901
    this._inFlightAttributes = {};
4902
    this._relationships = {};
4903
  },
4904

    
4905
  /**
4906
    @method send
4907
    @private
4908
    @param {String} name
4909
    @param {Object} context
4910
  */
4911
  send: function(name, context) {
4912
    var currentState = get(this, 'currentState');
4913

    
4914
    if (!currentState[name]) {
4915
      this._unhandledEvent(currentState, name, context);
4916
    }
4917

    
4918
    return currentState[name](this, context);
4919
  },
4920

    
4921
  /**
4922
    @method transitionTo
4923
    @private
4924
    @param {String} name
4925
  */
4926
  transitionTo: function(name) {
4927
    // POSSIBLE TODO: Remove this code and replace with
4928
    // always having direct references to state objects
4929

    
4930
    var pivotName = name.split(".", 1),
4931
        currentState = get(this, 'currentState'),
4932
        state = currentState;
4933

    
4934
    do {
4935
      if (state.exit) { state.exit(this); }
4936
      state = state.parentState;
4937
    } while (!state.hasOwnProperty(pivotName));
4938

    
4939
    var path = name.split(".");
4940

    
4941
    var setups = [], enters = [], i, l;
4942

    
4943
    for (i=0, l=path.length; i<l; i++) {
4944
      state = state[path[i]];
4945

    
4946
      if (state.enter) { enters.push(state); }
4947
      if (state.setup) { setups.push(state); }
4948
    }
4949

    
4950
    for (i=0, l=enters.length; i<l; i++) {
4951
      enters[i].enter(this);
4952
    }
4953

    
4954
    set(this, 'currentState', state);
4955

    
4956
    for (i=0, l=setups.length; i<l; i++) {
4957
      setups[i].setup(this);
4958
    }
4959

    
4960
    this.updateRecordArraysLater();
4961
  },
4962

    
4963
  _unhandledEvent: function(state, name, context) {
4964
    var errorMessage = "Attempted to handle event `" + name + "` ";
4965
    errorMessage    += "on " + String(this) + " while in state ";
4966
    errorMessage    += state.stateName + ". ";
4967

    
4968
    if (context !== undefined) {
4969
      errorMessage  += "Called with " + Ember.inspect(context) + ".";
4970
    }
4971

    
4972
    throw new Ember.Error(errorMessage);
4973
  },
4974

    
4975
  withTransaction: function(fn) {
4976
    var transaction = get(this, 'transaction');
4977
    if (transaction) { fn(transaction); }
4978
  },
4979

    
4980
  /**
4981
    @method loadingData
4982
    @private
4983
    @param {Promise} promise
4984
  */
4985
  loadingData: function(promise) {
4986
    this.send('loadingData', promise);
4987
  },
4988

    
4989
  /**
4990
    @method loadedData
4991
    @private
4992
  */
4993
  loadedData: function() {
4994
    this.send('loadedData');
4995
  },
4996

    
4997
  /**
4998
    @method notFound
4999
    @private
5000
  */
5001
  notFound: function() {
5002
    this.send('notFound');
5003
  },
5004

    
5005
  /**
5006
    @method pushedData
5007
    @private
5008
  */
5009
  pushedData: function() {
5010
    this.send('pushedData');
5011
  },
5012

    
5013
  /**
5014
    Marks the record as deleted but does not save it. You must call
5015
    `save` afterwards if you want to persist it. You might use this
5016
    method if you want to allow the user to still `rollback()` a
5017
    delete after it was made.
5018

5019
    Example
5020

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

5037
    @method deleteRecord
5038
  */
5039
  deleteRecord: function() {
5040
    this.send('deleteRecord');
5041
  },
5042

    
5043
  /**
5044
    Same as `deleteRecord`, but saves the record immediately.
5045

5046
    Example
5047

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

5061
    @method destroyRecord
5062
    @return {Promise} a promise that will be resolved when the adapter returns
5063
    successfully or rejected if the adapter returns with an error.
5064
  */
5065
  destroyRecord: function() {
5066
    this.deleteRecord();
5067
    return this.save();
5068
  },
5069

    
5070
  /**
5071
    @method unloadRecord
5072
    @private
5073
  */
5074
  unloadRecord: function() {
5075
    Ember.assert("You can only unload a loaded, non-dirty record.", !get(this, 'isDirty'));
5076

    
5077
    this.send('unloadRecord');
5078
  },
5079

    
5080
  /**
5081
    @method clearRelationships
5082
    @private
5083
  */
5084
  clearRelationships: function() {
5085
    this.eachRelationship(function(name, relationship) {
5086
      if (relationship.kind === 'belongsTo') {
5087
        set(this, name, null);
5088
      } else if (relationship.kind === 'hasMany') {
5089
        var hasMany = this._relationships[relationship.name];
5090
        if (hasMany) { hasMany.clear(); }
5091
      }
5092
    }, this);
5093
  },
5094

    
5095
  /**
5096
    @method updateRecordArrays
5097
    @private
5098
  */
5099
  updateRecordArrays: function() {
5100
    get(this, 'store').dataWasUpdated(this.constructor, this);
5101
  },
5102

    
5103
  /**
5104
    Returns an object, whose keys are changed properties, and value is
5105
    an [oldProp, newProp] array.
5106

5107
    Example
5108

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

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

5120
    @method changedAttributes
5121
    @return {Object} an object, whose keys are changed properties,
5122
      and value is an [oldProp, newProp] array.
5123
  */
5124
  changedAttributes: function() {
5125
    var oldData = get(this, '_data'),
5126
        newData = get(this, '_attributes'),
5127
        diffData = {},
5128
        prop;
5129

    
5130
    for (prop in newData) {
5131
      diffData[prop] = [oldData[prop], newData[prop]];
5132
    }
5133

    
5134
    return diffData;
5135
  },
5136

    
5137
  /**
5138
    @method adapterWillCommit
5139
    @private
5140
  */
5141
  adapterWillCommit: function() {
5142
    this.send('willCommit');
5143
  },
5144

    
5145
  /**
5146
    If the adapter did not return a hash in response to a commit,
5147
    merge the changed attributes and relationships into the existing
5148
    saved data.
5149

5150
    @method adapterDidCommit
5151
  */
5152
  adapterDidCommit: function(data) {
5153
    set(this, 'isError', false);
5154

    
5155
    if (data) {
5156
      this._data = data;
5157
    } else {
5158
      Ember.mixin(this._data, this._inFlightAttributes);
5159
    }
5160

    
5161
    this._inFlightAttributes = {};
5162

    
5163
    this.send('didCommit');
5164
    this.updateRecordArraysLater();
5165

    
5166
    if (!data) { return; }
5167

    
5168
    this.suspendRelationshipObservers(function() {
5169
      this.notifyPropertyChange('data');
5170
    });
5171
  },
5172

    
5173
  /**
5174
    @method adapterDidDirty
5175
    @private
5176
  */
5177
  adapterDidDirty: function() {
5178
    this.send('becomeDirty');
5179
    this.updateRecordArraysLater();
5180
  },
5181

    
5182
  dataDidChange: Ember.observer(function() {
5183
    this.reloadHasManys();
5184
  }, 'data'),
5185

    
5186
  reloadHasManys: function() {
5187
    var relationships = get(this.constructor, 'relationshipsByName');
5188
    this.updateRecordArraysLater();
5189
    relationships.forEach(function(name, relationship) {
5190
      if (this._data.links && this._data.links[name]) { return; }
5191
      if (relationship.kind === 'hasMany') {
5192
        this.hasManyDidChange(relationship.key);
5193
      }
5194
    }, this);
5195
  },
5196

    
5197
  hasManyDidChange: function(key) {
5198
    var hasMany = this._relationships[key];
5199

    
5200
    if (hasMany) {
5201
      var records = this._data[key] || [];
5202

    
5203
      set(hasMany, 'content', Ember.A(records));
5204
      set(hasMany, 'isLoaded', true);
5205
      hasMany.trigger('didLoad');
5206
    }
5207
  },
5208

    
5209
  /**
5210
    @method updateRecordArraysLater
5211
    @private
5212
  */
5213
  updateRecordArraysLater: function() {
5214
    Ember.run.once(this, this.updateRecordArrays);
5215
  },
5216

    
5217
  /**
5218
    @method setupData
5219
    @private
5220
    @param {Object} data
5221
    @param {Boolean} partial the data should be merged into
5222
      the existing data, not replace it.
5223
  */
5224
  setupData: function(data, partial) {
5225
    if (partial) {
5226
      Ember.merge(this._data, data);
5227
    } else {
5228
      this._data = data;
5229
    }
5230

    
5231
    var relationships = this._relationships;
5232

    
5233
    this.eachRelationship(function(name, rel) {
5234
      if (data.links && data.links[name]) { return; }
5235
      if (rel.options.async) { relationships[name] = null; }
5236
    });
5237

    
5238
    if (data) { this.pushedData(); }
5239

    
5240
    this.suspendRelationshipObservers(function() {
5241
      this.notifyPropertyChange('data');
5242
    });
5243
  },
5244

    
5245
  materializeId: function(id) {
5246
    set(this, 'id', id);
5247
  },
5248

    
5249
  materializeAttributes: function(attributes) {
5250
    Ember.assert("Must pass a hash of attributes to materializeAttributes", !!attributes);
5251
    merge(this._data, attributes);
5252
  },
5253

    
5254
  materializeAttribute: function(name, value) {
5255
    this._data[name] = value;
5256
  },
5257

    
5258
  /**
5259
    @method updateHasMany
5260
    @private
5261
    @param {String} name
5262
    @param {Array} records
5263
  */
5264
  updateHasMany: function(name, records) {
5265
    this._data[name] = records;
5266
    this.hasManyDidChange(name);
5267
  },
5268

    
5269
  /**
5270
    @method updateBelongsTo
5271
    @private
5272
    @param {String} name
5273
    @param {DS.Model} record
5274
  */
5275
  updateBelongsTo: function(name, record) {
5276
    this._data[name] = record;
5277
  },
5278

    
5279
  /**
5280
    If the model `isDirty` this function will which discard any unsaved
5281
    changes
5282

5283
    Example
5284

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

5293
    @method rollback
5294
  */
5295
  rollback: function() {
5296
    this._attributes = {};
5297

    
5298
    if (get(this, 'isError')) {
5299
      this._inFlightAttributes = {};
5300
      set(this, 'isError', false);
5301
    }
5302

    
5303
    if (!get(this, 'isValid')) {
5304
      this._inFlightAttributes = {};
5305
    }
5306

    
5307
    this.send('rolledBack');
5308

    
5309
    this.suspendRelationshipObservers(function() {
5310
      this.notifyPropertyChange('data');
5311
    });
5312
  },
5313

    
5314
  toStringExtension: function() {
5315
    return get(this, 'id');
5316
  },
5317

    
5318
  /**
5319
    The goal of this method is to temporarily disable specific observers
5320
    that take action in response to application changes.
5321

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

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

5330
    @method suspendRelationshipObservers
5331
    @private
5332
    @param callback
5333
    @param binding
5334
  */
5335
  suspendRelationshipObservers: function(callback, binding) {
5336
    var observers = get(this.constructor, 'relationshipNames').belongsTo;
5337
    var self = this;
5338

    
5339
    try {
5340
      this._suspendedRelationships = true;
5341
      Ember._suspendObservers(self, observers, null, 'belongsToDidChange', function() {
5342
        Ember._suspendBeforeObservers(self, observers, null, 'belongsToWillChange', function() {
5343
          callback.call(binding || self);
5344
        });
5345
      });
5346
    } finally {
5347
      this._suspendedRelationships = false;
5348
    }
5349
  },
5350

    
5351
  /**
5352
    Save the record and persist any changes to the record to an
5353
    extenal source via the adapter.
5354

5355
    Example
5356

5357
    ```javascript
5358
    record.set('name', 'Tomster');
5359
    record.save().then(function(){
5360
      // Success callback
5361
    }, function() {
5362
      // Error callback
5363
    });
5364
    ```
5365
    @method save
5366
    @return {Promise} a promise that will be resolved when the adapter returns
5367
    successfully or rejected if the adapter returns with an error.
5368
  */
5369
  save: function() {
5370
    var promiseLabel = "DS: Model#save " + this;
5371
    var resolver = Ember.RSVP.defer(promiseLabel);
5372

    
5373
    this.get('store').scheduleSave(this, resolver);
5374
    this._inFlightAttributes = this._attributes;
5375
    this._attributes = {};
5376

    
5377
    return DS.PromiseObject.create({ promise: resolver.promise });
5378
  },
5379

    
5380
  /**
5381
    Reload the record from the adapter.
5382

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

5387
    Example
5388

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

5399
    @method reload
5400
    @return {Promise} a promise that will be resolved with the record when the
5401
    adapter returns successfully or rejected if the adapter returns
5402
    with an error.
5403
  */
5404
  reload: function() {
5405
    set(this, 'isReloading', true);
5406

    
5407
    var  record = this;
5408

    
5409
    var promiseLabel = "DS: Model#reload of " + this;
5410
    var promise = new Ember.RSVP.Promise(function(resolve){
5411
       record.send('reloadRecord', resolve);
5412
    }, promiseLabel).then(function() {
5413
      record.set('isReloading', false);
5414
      record.set('isError', false);
5415
      return record;
5416
    }, function(reason) {
5417
      record.set('isError', true);
5418
      throw reason;
5419
    }, "DS: Model#reload complete, update flags");
5420

    
5421
    return DS.PromiseObject.create({ promise: promise });
5422
  },
5423

    
5424
  // FOR USE DURING COMMIT PROCESS
5425

    
5426
  adapterDidUpdateAttribute: function(attributeName, value) {
5427

    
5428
    // If a value is passed in, update the internal attributes and clear
5429
    // the attribute cache so it picks up the new value. Otherwise,
5430
    // collapse the current value into the internal attributes because
5431
    // the adapter has acknowledged it.
5432
    if (value !== undefined) {
5433
      this._data[attributeName] = value;
5434
      this.notifyPropertyChange(attributeName);
5435
    } else {
5436
      this._data[attributeName] = this._inFlightAttributes[attributeName];
5437
    }
5438

    
5439
    this.updateRecordArraysLater();
5440
  },
5441

    
5442
  /**
5443
    @method adapterDidInvalidate
5444
    @private
5445
  */
5446
  adapterDidInvalidate: function(errors) {
5447
    var recordErrors = get(this, 'errors');
5448
    function addError(name) {
5449
      if (errors[name]) {
5450
        recordErrors.add(name, errors[name]);
5451
      }
5452
    }
5453

    
5454
    this.eachAttribute(addError);
5455
    this.eachRelationship(addError);
5456
  },
5457

    
5458
  /**
5459
    @method adapterDidError
5460
    @private
5461
  */
5462
  adapterDidError: function() {
5463
    this.send('becameError');
5464
    set(this, 'isError', true);
5465
  },
5466

    
5467
  /**
5468
    Override the default event firing from Ember.Evented to
5469
    also call methods with the given name.
5470

5471
    @method trigger
5472
    @private
5473
    @param name
5474
  */
5475
  trigger: function(name) {
5476
    Ember.tryInvoke(this, name, [].slice.call(arguments, 1));
5477
    this._super.apply(this, arguments);
5478
  },
5479

    
5480
  triggerLater: function() {
5481
    this._deferredTriggers.push(arguments);
5482
    once(this, '_triggerDeferredTriggers');
5483
  },
5484

    
5485
  _triggerDeferredTriggers: function() {
5486
    for (var i=0, l=this._deferredTriggers.length; i<l; i++) {
5487
      this.trigger.apply(this, this._deferredTriggers[i]);
5488
    }
5489

    
5490
    this._deferredTriggers = [];
5491
  }
5492
});
5493

    
5494
DS.Model.reopenClass({
5495

    
5496
  /**
5497
    Alias DS.Model's `create` method to `_create`. This allows us to create DS.Model
5498
    instances from within the store, but if end users accidentally call `create()`
5499
    (instead of `createRecord()`), we can raise an error.
5500

5501
    @method _create
5502
    @private
5503
    @static
5504
  */
5505
  _create: DS.Model.create,
5506

    
5507
  /**
5508
    Override the class' `create()` method to raise an error. This
5509
    prevents end users from inadvertently calling `create()` instead
5510
    of `createRecord()`. The store is still able to create instances
5511
    by calling the `_create()` method. To create an instance of a
5512
    `DS.Model` use [store.createRecord](DS.Store.html#method_createRecord).
5513

5514
    @method create
5515
    @private
5516
    @static
5517
  */
5518
  create: function() {
5519
    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
  }
5521
});
5522

    
5523
})();
5524

    
5525

    
5526

    
5527
(function() {
5528
/**
5529
  @module ember-data
5530
*/
5531

    
5532
var get = Ember.get;
5533

    
5534
/**
5535
  @class Model
5536
  @namespace DS
5537
*/
5538
DS.Model.reopenClass({
5539
  /**
5540
    A map whose keys are the attributes of the model (properties
5541
    described by DS.attr) and whose values are the meta object for the
5542
    property.
5543

5544
    Example
5545

5546
    ```javascript
5547

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

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

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

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

5566
    @property attributes
5567
    @static
5568
    @type {Ember.Map}
5569
    @readOnly
5570
  */
5571
  attributes: Ember.computed(function() {
5572
    var map = Ember.Map.create();
5573

    
5574
    this.eachComputedProperty(function(name, meta) {
5575
      if (meta.isAttribute) {
5576
        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

    
5578
        meta.name = name;
5579
        map.set(name, meta);
5580
      }
5581
    });
5582

    
5583
    return map;
5584
  }),
5585

    
5586
  /**
5587
    A map whose keys are the attributes of the model (properties
5588
    described by DS.attr) and whose values are type of transformation
5589
    applied to each attribute. This map does not include any
5590
    attributes that do not have an transformation type.
5591

5592
    Example
5593

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

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

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

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

5612
    @property transformedAttributes
5613
    @static
5614
    @type {Ember.Map}
5615
    @readOnly
5616
  */
5617
  transformedAttributes: Ember.computed(function() {
5618
    var map = Ember.Map.create();
5619

    
5620
    this.eachAttribute(function(key, meta) {
5621
      if (meta.type) {
5622
        map.set(key, meta.type);
5623
      }
5624
    });
5625

    
5626
    return map;
5627
  }),
5628

    
5629
  /**
5630
    Iterates through the attributes of the model, calling the passed function on each
5631
    attribute.
5632

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

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

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

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

5646
    Example
5647

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

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

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

5665
    @method eachAttribute
5666
    @param {Function} callback The callback to execute
5667
    @param {Object} [target] The target object to use
5668
    @static
5669
  */
5670
  eachAttribute: function(callback, binding) {
5671
    get(this, 'attributes').forEach(function(name, meta) {
5672
      callback.call(binding, name, meta);
5673
    }, binding);
5674
  },
5675

    
5676
  /**
5677
    Iterates through the transformedAttributes of the model, calling
5678
    the passed function on each attribute. Note the callback will not be
5679
    called for any attributes that do not have an transformation type.
5680

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

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

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

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

5695
    Example
5696

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

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

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

5713
    @method eachTransformedAttribute
5714
    @param {Function} callback The callback to execute
5715
    @param {Object} [target] The target object to use
5716
    @static
5717
  */
5718
  eachTransformedAttribute: function(callback, binding) {
5719
    get(this, 'transformedAttributes').forEach(function(name, type) {
5720
      callback.call(binding, name, type);
5721
    });
5722
  }
5723
});
5724

    
5725

    
5726
DS.Model.reopen({
5727
  eachAttribute: function(callback, binding) {
5728
    this.constructor.eachAttribute(callback, binding);
5729
  }
5730
});
5731

    
5732
function getDefaultValue(record, options, key) {
5733
  if (typeof options.defaultValue === "function") {
5734
    return options.defaultValue();
5735
  } else {
5736
    return options.defaultValue;
5737
  }
5738
}
5739

    
5740
function hasValue(record, key) {
5741
  return record._attributes.hasOwnProperty(key) ||
5742
         record._inFlightAttributes.hasOwnProperty(key) ||
5743
         record._data.hasOwnProperty(key);
5744
}
5745

    
5746
function getValue(record, key) {
5747
  if (record._attributes.hasOwnProperty(key)) {
5748
    return record._attributes[key];
5749
  } else if (record._inFlightAttributes.hasOwnProperty(key)) {
5750
    return record._inFlightAttributes[key];
5751
  } else {
5752
    return record._data[key];
5753
  }
5754
}
5755

    
5756
/**
5757
  `DS.attr` defines an attribute on a [DS.Model](DS.Model.html).
5758
  By default, attributes are passed through as-is, however you can specify an
5759
  optional type to have the value automatically transformed.
5760
  Ember Data ships with four basic transform types: `string`, `number`,
5761
  `boolean` and `date`. You can define your own transforms by subclassing
5762
  [DS.Transform](DS.Transform.html).
5763

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

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

5770
  Example
5771

5772
  ```javascript
5773
  var attr = DS.attr;
5774

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

5782
  @namespace
5783
  @method attr
5784
  @for DS
5785
  @param {String} type the attribute type
5786
  @param {Object} options a hash of options
5787
  @return {Attribute}
5788
*/
5789

    
5790
DS.attr = function(type, options) {
5791
  options = options || {};
5792

    
5793
  var meta = {
5794
    type: type,
5795
    isAttribute: true,
5796
    options: options
5797
  };
5798

    
5799
  return Ember.computed(function(key, value) {
5800
    if (arguments.length > 1) {
5801
      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
      var oldValue = this._attributes[key] || this._inFlightAttributes[key] || this._data[key];
5803

    
5804
      this.send('didSetProperty', {
5805
        name: key,
5806
        oldValue: oldValue,
5807
        originalValue: this._data[key],
5808
        value: value
5809
      });
5810

    
5811
      this._attributes[key] = value;
5812
      return value;
5813
    } else if (hasValue(this, key)) {
5814
      return getValue(this, key);
5815
    } else {
5816
      return getDefaultValue(this, options, key);
5817
    }
5818

    
5819
  // `data` is never set directly. However, it may be
5820
  // invalidated from the state manager's setData
5821
  // event.
5822
  }).property('data').meta(meta);
5823
};
5824

    
5825

    
5826
})();
5827

    
5828

    
5829

    
5830
(function() {
5831
/**
5832
  @module ember-data
5833
*/
5834

    
5835
})();
5836

    
5837

    
5838

    
5839
(function() {
5840
/**
5841
  @module ember-data
5842
*/
5843

    
5844
/**
5845
  An AttributeChange object is created whenever a record's
5846
  attribute changes value. It is used to track changes to a
5847
  record between transaction commits.
5848

5849
  @class AttributeChange
5850
  @namespace DS
5851
  @private
5852
  @constructor
5853
*/
5854
var AttributeChange = DS.AttributeChange = function(options) {
5855
  this.record = options.record;
5856
  this.store = options.store;
5857
  this.name = options.name;
5858
  this.value = options.value;
5859
  this.oldValue = options.oldValue;
5860
};
5861

    
5862
AttributeChange.createChange = function(options) {
5863
  return new AttributeChange(options);
5864
};
5865

    
5866
AttributeChange.prototype = {
5867
  sync: function() {
5868
    if (this.value !== this.oldValue) {
5869
      this.record.send('becomeDirty');
5870
      this.record.updateRecordArraysLater();
5871
    }
5872

    
5873
    // TODO: Use this object in the commit process
5874
    this.destroy();
5875
  },
5876

    
5877
  /**
5878
    If the AttributeChange is destroyed (either by being rolled back
5879
    or being committed), remove it from the list of pending changes
5880
    on the record.
5881

5882
    @method destroy
5883
  */
5884
  destroy: function() {
5885
    delete this.record._changesToSync[this.name];
5886
  }
5887
};
5888

    
5889
})();
5890

    
5891

    
5892

    
5893
(function() {
5894
/**
5895
  @module ember-data
5896
*/
5897

    
5898
var get = Ember.get, set = Ember.set;
5899
var forEach = Ember.EnumerableUtils.forEach;
5900

    
5901
/**
5902
  @class RelationshipChange
5903
  @namespace DS
5904
  @private
5905
  @construtor
5906
*/
5907
DS.RelationshipChange = function(options) {
5908
  this.parentRecord = options.parentRecord;
5909
  this.childRecord = options.childRecord;
5910
  this.firstRecord = options.firstRecord;
5911
  this.firstRecordKind = options.firstRecordKind;
5912
  this.firstRecordName = options.firstRecordName;
5913
  this.secondRecord = options.secondRecord;
5914
  this.secondRecordKind = options.secondRecordKind;
5915
  this.secondRecordName = options.secondRecordName;
5916
  this.changeType = options.changeType;
5917
  this.store = options.store;
5918

    
5919
  this.committed = {};
5920
};
5921

    
5922
/**
5923
  @class RelationshipChangeAdd
5924
  @namespace DS
5925
  @private
5926
  @construtor
5927
*/
5928
DS.RelationshipChangeAdd = function(options){
5929
  DS.RelationshipChange.call(this, options);
5930
};
5931

    
5932
/**
5933
  @class RelationshipChangeRemove
5934
  @namespace DS
5935
  @private
5936
  @construtor
5937
*/
5938
DS.RelationshipChangeRemove = function(options){
5939
  DS.RelationshipChange.call(this, options);
5940
};
5941

    
5942
DS.RelationshipChange.create = function(options) {
5943
  return new DS.RelationshipChange(options);
5944
};
5945

    
5946
DS.RelationshipChangeAdd.create = function(options) {
5947
  return new DS.RelationshipChangeAdd(options);
5948
};
5949

    
5950
DS.RelationshipChangeRemove.create = function(options) {
5951
  return new DS.RelationshipChangeRemove(options);
5952
};
5953

    
5954
DS.OneToManyChange = {};
5955
DS.OneToNoneChange = {};
5956
DS.ManyToNoneChange = {};
5957
DS.OneToOneChange = {};
5958
DS.ManyToManyChange = {};
5959

    
5960
DS.RelationshipChange._createChange = function(options){
5961
  if(options.changeType === "add"){
5962
    return DS.RelationshipChangeAdd.create(options);
5963
  }
5964
  if(options.changeType === "remove"){
5965
    return DS.RelationshipChangeRemove.create(options);
5966
  }
5967
};
5968

    
5969

    
5970
DS.RelationshipChange.determineRelationshipType = function(recordType, knownSide){
5971
  var knownKey = knownSide.key, key, otherKind;
5972
  var knownKind = knownSide.kind;
5973

    
5974
  var inverse = recordType.inverseFor(knownKey);
5975

    
5976
  if (inverse){
5977
    key = inverse.name;
5978
    otherKind = inverse.kind;
5979
  }
5980

    
5981
  if (!inverse){
5982
    return knownKind === "belongsTo" ? "oneToNone" : "manyToNone";
5983
  }
5984
  else{
5985
    if(otherKind === "belongsTo"){
5986
      return knownKind === "belongsTo" ? "oneToOne" : "manyToOne";
5987
    }
5988
    else{
5989
      return knownKind === "belongsTo" ? "oneToMany" : "manyToMany";
5990
    }
5991
  }
5992

    
5993
};
5994

    
5995
DS.RelationshipChange.createChange = function(firstRecord, secondRecord, store, options){
5996
  // Get the type of the child based on the child's client ID
5997
  var firstRecordType = firstRecord.constructor, changeType;
5998
  changeType = DS.RelationshipChange.determineRelationshipType(firstRecordType, options);
5999
  if (changeType === "oneToMany"){
6000
    return DS.OneToManyChange.createChange(firstRecord, secondRecord, store, options);
6001
  }
6002
  else if (changeType === "manyToOne"){
6003
    return DS.OneToManyChange.createChange(secondRecord, firstRecord, store, options);
6004
  }
6005
  else if (changeType === "oneToNone"){
6006
    return DS.OneToNoneChange.createChange(firstRecord, secondRecord, store, options);
6007
  }
6008
  else if (changeType === "manyToNone"){
6009
    return DS.ManyToNoneChange.createChange(firstRecord, secondRecord, store, options);
6010
  }
6011
  else if (changeType === "oneToOne"){
6012
    return DS.OneToOneChange.createChange(firstRecord, secondRecord, store, options);
6013
  }
6014
  else if (changeType === "manyToMany"){
6015
    return DS.ManyToManyChange.createChange(firstRecord, secondRecord, store, options);
6016
  }
6017
};
6018

    
6019
DS.OneToNoneChange.createChange = function(childRecord, parentRecord, store, options) {
6020
  var key = options.key;
6021
  var change = DS.RelationshipChange._createChange({
6022
      parentRecord: parentRecord,
6023
      childRecord: childRecord,
6024
      firstRecord: childRecord,
6025
      store: store,
6026
      changeType: options.changeType,
6027
      firstRecordName: key,
6028
      firstRecordKind: "belongsTo"
6029
  });
6030

    
6031
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6032

    
6033
  return change;
6034
};
6035

    
6036
DS.ManyToNoneChange.createChange = function(childRecord, parentRecord, store, options) {
6037
  var key = options.key;
6038
  var change = DS.RelationshipChange._createChange({
6039
      parentRecord: childRecord,
6040
      childRecord: parentRecord,
6041
      secondRecord: childRecord,
6042
      store: store,
6043
      changeType: options.changeType,
6044
      secondRecordName: options.key,
6045
      secondRecordKind: "hasMany"
6046
  });
6047

    
6048
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6049
  return change;
6050
};
6051

    
6052

    
6053
DS.ManyToManyChange.createChange = function(childRecord, parentRecord, store, options) {
6054
  // If the name of the belongsTo side of the relationship is specified,
6055
  // use that
6056
  // If the type of the parent is specified, look it up on the child's type
6057
  // definition.
6058
  var key = options.key;
6059

    
6060
  var change = DS.RelationshipChange._createChange({
6061
      parentRecord: parentRecord,
6062
      childRecord: childRecord,
6063
      firstRecord: childRecord,
6064
      secondRecord: parentRecord,
6065
      firstRecordKind: "hasMany",
6066
      secondRecordKind: "hasMany",
6067
      store: store,
6068
      changeType: options.changeType,
6069
      firstRecordName:  key
6070
  });
6071

    
6072
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6073

    
6074

    
6075
  return change;
6076
};
6077

    
6078
DS.OneToOneChange.createChange = function(childRecord, parentRecord, store, options) {
6079
  var key;
6080

    
6081
  // If the name of the belongsTo side of the relationship is specified,
6082
  // use that
6083
  // If the type of the parent is specified, look it up on the child's type
6084
  // definition.
6085
  if (options.parentType) {
6086
    key = options.parentType.inverseFor(options.key).name;
6087
  } else if (options.key) {
6088
    key = options.key;
6089
  } else {
6090
    Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false);
6091
  }
6092

    
6093
  var change = DS.RelationshipChange._createChange({
6094
      parentRecord: parentRecord,
6095
      childRecord: childRecord,
6096
      firstRecord: childRecord,
6097
      secondRecord: parentRecord,
6098
      firstRecordKind: "belongsTo",
6099
      secondRecordKind: "belongsTo",
6100
      store: store,
6101
      changeType: options.changeType,
6102
      firstRecordName:  key
6103
  });
6104

    
6105
  store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
6106

    
6107

    
6108
  return change;
6109
};
6110

    
6111
DS.OneToOneChange.maintainInvariant = function(options, store, childRecord, key){
6112
  if (options.changeType === "add" && store.recordIsMaterialized(childRecord)) {
6113
    var oldParent = get(childRecord, key);
6114
    if (oldParent){
6115
      var correspondingChange = DS.OneToOneChange.createChange(childRecord, oldParent, store, {
6116
          parentType: options.parentType,
6117
          hasManyName: options.hasManyName,
6118
          changeType: "remove",
6119
          key: options.key
6120
        });
6121
      store.addRelationshipChangeFor(childRecord, key, options.parentRecord , null, correspondingChange);
6122
     correspondingChange.sync();
6123
    }
6124
  }
6125
};
6126

    
6127
DS.OneToManyChange.createChange = function(childRecord, parentRecord, store, options) {
6128
  var key;
6129

    
6130
  // If the name of the belongsTo side of the relationship is specified,
6131
  // use that
6132
  // If the type of the parent is specified, look it up on the child's type
6133
  // definition.
6134
  if (options.parentType) {
6135
    key = options.parentType.inverseFor(options.key).name;
6136
    DS.OneToManyChange.maintainInvariant( options, store, childRecord, key );
6137
  } else if (options.key) {
6138
    key = options.key;
6139
  } else {
6140
    Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false);
6141
  }
6142

    
6143
  var change = DS.RelationshipChange._createChange({
6144
      parentRecord: parentRecord,
6145
      childRecord: childRecord,
6146
      firstRecord: childRecord,
6147
      secondRecord: parentRecord,
6148
      firstRecordKind: "belongsTo",
6149
      secondRecordKind: "hasMany",
6150
      store: store,
6151
      changeType: options.changeType,
6152
      firstRecordName:  key
6153
  });
6154

    
6155
  store.addRelationshipChangeFor(childRecord, key, parentRecord, change.getSecondRecordName(), change);
6156

    
6157

    
6158
  return change;
6159
};
6160

    
6161

    
6162
DS.OneToManyChange.maintainInvariant = function(options, store, childRecord, key){
6163
  if (options.changeType === "add" && childRecord) {
6164
    var oldParent = get(childRecord, key);
6165
    if (oldParent){
6166
      var correspondingChange = DS.OneToManyChange.createChange(childRecord, oldParent, store, {
6167
          parentType: options.parentType,
6168
          hasManyName: options.hasManyName,
6169
          changeType: "remove",
6170
          key: options.key
6171
        });
6172
      store.addRelationshipChangeFor(childRecord, key, options.parentRecord, correspondingChange.getSecondRecordName(), correspondingChange);
6173
      correspondingChange.sync();
6174
    }
6175
  }
6176
};
6177

    
6178
/**
6179
  @class RelationshipChange
6180
  @namespace DS
6181
*/
6182
DS.RelationshipChange.prototype = {
6183

    
6184
  getSecondRecordName: function() {
6185
    var name = this.secondRecordName, parent;
6186

    
6187
    if (!name) {
6188
      parent = this.secondRecord;
6189
      if (!parent) { return; }
6190

    
6191
      var childType = this.firstRecord.constructor;
6192
      var inverse = childType.inverseFor(this.firstRecordName);
6193
      this.secondRecordName = inverse.name;
6194
    }
6195

    
6196
    return this.secondRecordName;
6197
  },
6198

    
6199
  /**
6200
    Get the name of the relationship on the belongsTo side.
6201

6202
    @method getFirstRecordName
6203
    @return {String}
6204
  */
6205
  getFirstRecordName: function() {
6206
    var name = this.firstRecordName;
6207
    return name;
6208
  },
6209

    
6210
  /**
6211
    @method destroy
6212
    @private
6213
  */
6214
  destroy: function() {
6215
    var childRecord = this.childRecord,
6216
        belongsToName = this.getFirstRecordName(),
6217
        hasManyName = this.getSecondRecordName(),
6218
        store = this.store;
6219

    
6220
    store.removeRelationshipChangeFor(childRecord, belongsToName, this.parentRecord, hasManyName, this.changeType);
6221
  },
6222

    
6223
  getSecondRecord: function(){
6224
    return this.secondRecord;
6225
  },
6226

    
6227
  /**
6228
    @method getFirstRecord
6229
    @private
6230
  */
6231
  getFirstRecord: function() {
6232
    return this.firstRecord;
6233
  },
6234

    
6235
  coalesce: function(){
6236
    var relationshipPairs = this.store.relationshipChangePairsFor(this.firstRecord);
6237
    forEach(relationshipPairs, function(pair){
6238
      var addedChange = pair["add"];
6239
      var removedChange = pair["remove"];
6240
      if(addedChange && removedChange) {
6241
        addedChange.destroy();
6242
        removedChange.destroy();
6243
      }
6244
    });
6245
  }
6246
};
6247

    
6248
DS.RelationshipChangeAdd.prototype = Ember.create(DS.RelationshipChange.create({}));
6249
DS.RelationshipChangeRemove.prototype = Ember.create(DS.RelationshipChange.create({}));
6250

    
6251
// the object is a value, and not a promise
6252
function isValue(object) {
6253
  return typeof object === 'object' && (!object.then || typeof object.then !== 'function');
6254
}
6255

    
6256
DS.RelationshipChangeAdd.prototype.changeType = "add";
6257
DS.RelationshipChangeAdd.prototype.sync = function() {
6258
  var secondRecordName = this.getSecondRecordName(),
6259
      firstRecordName = this.getFirstRecordName(),
6260
      firstRecord = this.getFirstRecord(),
6261
      secondRecord = this.getSecondRecord();
6262

    
6263
  //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
  //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

    
6266
  if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) {
6267
    if(this.secondRecordKind === "belongsTo"){
6268
      secondRecord.suspendRelationshipObservers(function(){
6269
        set(secondRecord, secondRecordName, firstRecord);
6270
      });
6271

    
6272
     }
6273
     else if(this.secondRecordKind === "hasMany"){
6274
      secondRecord.suspendRelationshipObservers(function(){
6275
        var relationship = get(secondRecord, secondRecordName);
6276
        if (isValue(relationship)) { relationship.addObject(firstRecord); }
6277
      });
6278
    }
6279
  }
6280

    
6281
  if (firstRecord instanceof DS.Model && secondRecord instanceof DS.Model && get(firstRecord, firstRecordName) !== secondRecord) {
6282
    if(this.firstRecordKind === "belongsTo"){
6283
      firstRecord.suspendRelationshipObservers(function(){
6284
        set(firstRecord, firstRecordName, secondRecord);
6285
      });
6286
    }
6287
    else if(this.firstRecordKind === "hasMany"){
6288
      firstRecord.suspendRelationshipObservers(function(){
6289
        var relationship = get(firstRecord, firstRecordName);
6290
        if (isValue(relationship)) { relationship.addObject(secondRecord); }
6291
      });
6292
    }
6293
  }
6294

    
6295
  this.coalesce();
6296
};
6297

    
6298
DS.RelationshipChangeRemove.prototype.changeType = "remove";
6299
DS.RelationshipChangeRemove.prototype.sync = function() {
6300
  var secondRecordName = this.getSecondRecordName(),
6301
      firstRecordName = this.getFirstRecordName(),
6302
      firstRecord = this.getFirstRecord(),
6303
      secondRecord = this.getSecondRecord();
6304

    
6305
  //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
  //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

    
6308
  if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) {
6309
    if(this.secondRecordKind === "belongsTo"){
6310
      secondRecord.suspendRelationshipObservers(function(){
6311
        set(secondRecord, secondRecordName, null);
6312
      });
6313
    }
6314
    else if(this.secondRecordKind === "hasMany"){
6315
      secondRecord.suspendRelationshipObservers(function(){
6316
        var relationship = get(secondRecord, secondRecordName);
6317
        if (isValue(relationship)) { relationship.removeObject(firstRecord); }
6318
      });
6319
    }
6320
  }
6321

    
6322
  if (firstRecord instanceof DS.Model && get(firstRecord, firstRecordName)) {
6323
    if(this.firstRecordKind === "belongsTo"){
6324
      firstRecord.suspendRelationshipObservers(function(){
6325
        set(firstRecord, firstRecordName, null);
6326
      });
6327
     }
6328
     else if(this.firstRecordKind === "hasMany"){
6329
       firstRecord.suspendRelationshipObservers(function(){
6330
         var relationship = get(firstRecord, firstRecordName);
6331
         if (isValue(relationship)) { relationship.removeObject(secondRecord); }
6332
      });
6333
    }
6334
  }
6335

    
6336
  this.coalesce();
6337
};
6338

    
6339
})();
6340

    
6341

    
6342

    
6343
(function() {
6344
/**
6345
  @module ember-data
6346
*/
6347

    
6348
})();
6349

    
6350

    
6351

    
6352
(function() {
6353
var get = Ember.get, set = Ember.set,
6354
    isNone = Ember.isNone;
6355

    
6356
/**
6357
  @module ember-data
6358
*/
6359

    
6360
function asyncBelongsTo(type, options, meta) {
6361
  return Ember.computed(function(key, value) {
6362
    var data = get(this, 'data'),
6363
        store = get(this, 'store'),
6364
        promiseLabel = "DS: Async belongsTo " + this + " : " + key;
6365

    
6366
    if (arguments.length === 2) {
6367
      Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof store.modelFor(type));
6368
      return value === undefined ? null : DS.PromiseObject.create({ promise: Ember.RSVP.resolve(value, promiseLabel) });
6369
    }
6370

    
6371
    var link = data.links && data.links[key],
6372
        belongsTo = data[key];
6373

    
6374
    if(!isNone(belongsTo)) {
6375
      var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo, promiseLabel);
6376
      return DS.PromiseObject.create({ promise: promise});
6377
    } else if (link) {
6378
      var resolver = Ember.RSVP.defer("DS: Async belongsTo (link) " + this + " : " + key);
6379
      store.findBelongsTo(this, link, meta, resolver);
6380
      return DS.PromiseObject.create({ promise: resolver.promise });
6381
    } else {
6382
      return null;
6383
    }
6384
  }).property('data').meta(meta);
6385
}
6386

    
6387
/**
6388
  `DS.belongsTo` is used to define One-To-One and One-To-Many
6389
  relationships on a [DS.Model](DS.Model.html).
6390

6391

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

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

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

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

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

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

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

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

6427
  @namespace
6428
  @method belongsTo
6429
  @for DS
6430
  @param {String or DS.Model} type the model type of the relationship
6431
  @param {Object} options a hash of options
6432
  @return {Ember.computed} relationship
6433
*/
6434
DS.belongsTo = function(type, options) {
6435
  if (typeof type === 'object') {
6436
    options = type;
6437
    type = undefined;
6438
  } else {
6439
    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
  }
6441

    
6442
  options = options || {};
6443

    
6444
  var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo' };
6445

    
6446
  if (options.async) {
6447
    return asyncBelongsTo(type, options, meta);
6448
  }
6449

    
6450
  return Ember.computed(function(key, value) {
6451
    var data = get(this, 'data'),
6452
        store = get(this, 'store'), belongsTo, typeClass;
6453

    
6454
    if (typeof type === 'string') {
6455
      typeClass = store.modelFor(type);
6456
    } else {
6457
      typeClass = type;
6458
    }
6459

    
6460
    if (arguments.length === 2) {
6461
      Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof typeClass);
6462
      return value === undefined ? null : value;
6463
    }
6464

    
6465
    belongsTo = data[key];
6466

    
6467
    if (isNone(belongsTo)) { return null; }
6468

    
6469
    store.fetchRecord(belongsTo);
6470

    
6471
    return belongsTo;
6472
  }).property('data').meta(meta);
6473
};
6474

    
6475
/**
6476
  These observers observe all `belongsTo` relationships on the record. See
6477
  `relationships/ext` to see how these observers get their dependencies.
6478

6479
  @class Model
6480
  @namespace DS
6481
*/
6482
DS.Model.reopen({
6483

    
6484
  /**
6485
    @method belongsToWillChange
6486
    @private
6487
    @static
6488
    @param record
6489
    @param key
6490
  */
6491
  belongsToWillChange: Ember.beforeObserver(function(record, key) {
6492
    if (get(record, 'isLoaded')) {
6493
      var oldParent = get(record, key);
6494

    
6495
      if (oldParent) {
6496
        var store = get(record, 'store'),
6497
            change = DS.RelationshipChange.createChange(record, oldParent, store, { key: key, kind: "belongsTo", changeType: "remove" });
6498

    
6499
        change.sync();
6500
        this._changesToSync[key] = change;
6501
      }
6502
    }
6503
  }),
6504

    
6505
  /**
6506
    @method belongsToDidChange
6507
    @private
6508
    @static
6509
    @param record
6510
    @param key
6511
  */
6512
  belongsToDidChange: Ember.immediateObserver(function(record, key) {
6513
    if (get(record, 'isLoaded')) {
6514
      var newParent = get(record, key);
6515

    
6516
      if (newParent) {
6517
        var store = get(record, 'store'),
6518
            change = DS.RelationshipChange.createChange(record, newParent, store, { key: key, kind: "belongsTo", changeType: "add" });
6519

    
6520
        change.sync();
6521
      }
6522
    }
6523

    
6524
    delete this._changesToSync[key];
6525
  })
6526
});
6527

    
6528
})();
6529

    
6530

    
6531

    
6532
(function() {
6533
/**
6534
  @module ember-data
6535
*/
6536

    
6537
var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
6538

    
6539
function asyncHasMany(type, options, meta) {
6540
  return Ember.computed(function(key, value) {
6541
    var relationship = this._relationships[key],
6542
        promiseLabel = "DS: Async hasMany " + this + " : " + key;
6543

    
6544
    if (!relationship) {
6545
      var resolver = Ember.RSVP.defer(promiseLabel);
6546
      relationship = buildRelationship(this, key, options, function(store, data) {
6547
        var link = data.links && data.links[key];
6548
        var rel;
6549
        if (link) {
6550
          rel = store.findHasMany(this, link, meta, resolver);
6551
        } else {
6552
          rel = store.findMany(this, data[key], meta.type, resolver);
6553
        }
6554
        // cache the promise so we can use it
6555
        // when we come back and don't need to rebuild
6556
        // the relationship.
6557
        set(rel, 'promise', resolver.promise);
6558
        return rel;
6559
      });
6560
    }
6561

    
6562
    var promise = relationship.get('promise').then(function() {
6563
      return relationship;
6564
    }, null, "DS: Async hasMany records received");
6565

    
6566
    return DS.PromiseArray.create({ promise: promise });
6567
  }).property('data').meta(meta);
6568
}
6569

    
6570
function buildRelationship(record, key, options, callback) {
6571
  var rels = record._relationships;
6572

    
6573
  if (rels[key]) { return rels[key]; }
6574

    
6575
  var data = get(record, 'data'),
6576
      store = get(record, 'store');
6577

    
6578
  var relationship = rels[key] = callback.call(record, store, data);
6579

    
6580
  return setProperties(relationship, {
6581
    owner: record, name: key, isPolymorphic: options.polymorphic
6582
  });
6583
}
6584

    
6585
function hasRelationship(type, options) {
6586
  options = options || {};
6587

    
6588
  var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' };
6589

    
6590
  if (options.async) {
6591
    return asyncHasMany(type, options, meta);
6592
  }
6593

    
6594
  return Ember.computed(function(key, value) {
6595
    return buildRelationship(this, key, options, function(store, data) {
6596
      var records = data[key];
6597
      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
      return store.findMany(this, data[key], meta.type);
6599
    });
6600
  }).property('data').meta(meta);
6601
}
6602

    
6603
/**
6604
  `DS.hasMany` is used to define One-To-Many and Many-To-Many
6605
  relationships on a [DS.Model](DS.Model.html).
6606

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

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

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

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

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

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

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

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

6641
  #### Explicit Inverses
6642

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

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

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

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

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

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

6674
  @namespace
6675
  @method hasMany
6676
  @for DS
6677
  @param {String or DS.Model} type the model type of the relationship
6678
  @param {Object} options a hash of options
6679
  @return {Ember.computed} relationship
6680
*/
6681
DS.hasMany = function(type, options) {
6682
  if (typeof type === 'object') {
6683
    options = type;
6684
    type = undefined;
6685
  }
6686
  return hasRelationship(type, options);
6687
};
6688

    
6689
})();
6690

    
6691

    
6692

    
6693
(function() {
6694
var get = Ember.get, set = Ember.set;
6695

    
6696
/**
6697
  @module ember-data
6698
*/
6699

    
6700
/*
6701
  This file defines several extensions to the base `DS.Model` class that
6702
  add support for one-to-many relationships.
6703
*/
6704

    
6705
/**
6706
  @class Model
6707
  @namespace DS
6708
*/
6709
DS.Model.reopen({
6710

    
6711
  /**
6712
    This Ember.js hook allows an object to be notified when a property
6713
    is defined.
6714

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

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

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

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

6732
    @method didDefineProperty
6733
    @param proto
6734
    @param key
6735
    @param value
6736
  */
6737
  didDefineProperty: function(proto, key, value) {
6738
    // Check if the value being set is a computed property.
6739
    if (value instanceof Ember.Descriptor) {
6740

    
6741
      // If it is, get the metadata for the relationship. This is
6742
      // populated by the `DS.belongsTo` helper when it is creating
6743
      // the computed property.
6744
      var meta = value.meta();
6745

    
6746
      if (meta.isRelationship && meta.kind === 'belongsTo') {
6747
        Ember.addObserver(proto, key, null, 'belongsToDidChange');
6748
        Ember.addBeforeObserver(proto, key, null, 'belongsToWillChange');
6749
      }
6750

    
6751
      meta.parentType = proto.constructor;
6752
    }
6753
  }
6754
});
6755

    
6756
/*
6757
  These DS.Model extensions add class methods that provide relationship
6758
  introspection abilities about relationships.
6759

6760
  A note about the computed properties contained here:
6761

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

6767
  If your application needs to modify a class after its initial definition
6768
  (for example, using `reopen()` to add additional attributes), make sure you
6769
  do it before using your model with the store, which uses these properties
6770
  extensively.
6771
*/
6772

    
6773
DS.Model.reopenClass({
6774
  /**
6775
    For a given relationship name, returns the model type of the relationship.
6776

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

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

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

6787
    @method typeForRelationship
6788
    @static
6789
    @param {String} name the name of the relationship
6790
    @return {subclass of DS.Model} the type of the relationship, or undefined
6791
  */
6792
  typeForRelationship: function(name) {
6793
    var relationship = get(this, 'relationshipsByName').get(name);
6794
    return relationship && relationship.type;
6795
  },
6796

    
6797
  inverseFor: function(name) {
6798
    var inverseType = this.typeForRelationship(name);
6799

    
6800
    if (!inverseType) { return null; }
6801

    
6802
    var options = this.metaForProperty(name).options;
6803

    
6804
    if (options.inverse === null) { return null; }
6805
    
6806
    var inverseName, inverseKind;
6807

    
6808
    if (options.inverse) {
6809
      inverseName = options.inverse;
6810
      inverseKind = Ember.get(inverseType, 'relationshipsByName').get(inverseName).kind;
6811
    } else {
6812
      var possibleRelationships = findPossibleInverses(this, inverseType);
6813

    
6814
      if (possibleRelationships.length === 0) { return null; }
6815

    
6816
      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

    
6818
      inverseName = possibleRelationships[0].name;
6819
      inverseKind = possibleRelationships[0].kind;
6820
    }
6821

    
6822
    function findPossibleInverses(type, inverseType, possibleRelationships) {
6823
      possibleRelationships = possibleRelationships || [];
6824

    
6825
      var relationshipMap = get(inverseType, 'relationships');
6826
      if (!relationshipMap) { return; }
6827

    
6828
      var relationships = relationshipMap.get(type);
6829
      if (relationships) {
6830
        possibleRelationships.push.apply(possibleRelationships, relationshipMap.get(type));
6831
      }
6832

    
6833
      if (type.superclass) {
6834
        findPossibleInverses(type.superclass, inverseType, possibleRelationships);
6835
      }
6836

    
6837
      return possibleRelationships;
6838
    }
6839

    
6840
    return {
6841
      type: inverseType,
6842
      name: inverseName,
6843
      kind: inverseKind
6844
    };
6845
  },
6846

    
6847
  /**
6848
    The model's relationships as a map, keyed on the type of the
6849
    relationship. The value of each entry is an array containing a descriptor
6850
    for each relationship with that type, describing the name of the relationship
6851
    as well as the type.
6852

6853
    For example, given the following model definition:
6854

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

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

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

6875
    @property relationships
6876
    @static
6877
    @type Ember.Map
6878
    @readOnly
6879
  */
6880
  relationships: Ember.computed(function() {
6881
    var map = new Ember.MapWithDefault({
6882
      defaultValue: function() { return []; }
6883
    });
6884

    
6885
    // Loop through each computed property on the class
6886
    this.eachComputedProperty(function(name, meta) {
6887

    
6888
      // If the computed property is a relationship, add
6889
      // it to the map.
6890
      if (meta.isRelationship) {
6891
        if (typeof meta.type === 'string') {
6892
          meta.type = this.store.modelFor(meta.type);
6893
        }
6894

    
6895
        var relationshipsForType = map.get(meta.type);
6896

    
6897
        relationshipsForType.push({ name: name, kind: meta.kind });
6898
      }
6899
    });
6900

    
6901
    return map;
6902
  }),
6903

    
6904
  /**
6905
    A hash containing lists of the model's relationships, grouped
6906
    by the relationship kind. For example, given a model with this
6907
    definition:
6908

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

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

6918
    This property would contain the following:
6919

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

6928
    @property relationshipNames
6929
    @static
6930
    @type Object
6931
    @readOnly
6932
  */
6933
  relationshipNames: Ember.computed(function() {
6934
    var names = { hasMany: [], belongsTo: [] };
6935

    
6936
    this.eachComputedProperty(function(name, meta) {
6937
      if (meta.isRelationship) {
6938
        names[meta.kind].push(name);
6939
      }
6940
    });
6941

    
6942
    return names;
6943
  }),
6944

    
6945
  /**
6946
    An array of types directly related to a model. Each type will be
6947
    included once, regardless of the number of relationships it has with
6948
    the model.
6949

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

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

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

6961
    This property would contain the following:
6962

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

6968
    @property relatedTypes
6969
    @static
6970
    @type Ember.Array
6971
    @readOnly
6972
  */
6973
  relatedTypes: Ember.computed(function() {
6974
    var type,
6975
        types = Ember.A();
6976

    
6977
    // Loop through each computed property on the class,
6978
    // and create an array of the unique types involved
6979
    // in relationships
6980
    this.eachComputedProperty(function(name, meta) {
6981
      if (meta.isRelationship) {
6982
        type = meta.type;
6983

    
6984
        if (typeof type === 'string') {
6985
          type = get(this, type, false) || this.store.modelFor(type);
6986
        }
6987

    
6988
        Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.",  type);
6989

    
6990
        if (!types.contains(type)) {
6991
          Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type);
6992
          types.push(type);
6993
        }
6994
      }
6995
    });
6996

    
6997
    return types;
6998
  }),
6999

    
7000
  /**
7001
    A map whose keys are the relationships of a model and whose values are
7002
    relationship descriptors.
7003

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

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

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

7016
    This property would contain the following:
7017

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

7026
    @property relationshipsByName
7027
    @static
7028
    @type Ember.Map
7029
    @readOnly
7030
  */
7031
  relationshipsByName: Ember.computed(function() {
7032
    var map = Ember.Map.create(), type;
7033

    
7034
    this.eachComputedProperty(function(name, meta) {
7035
      if (meta.isRelationship) {
7036
        meta.key = name;
7037
        type = meta.type;
7038

    
7039
        if (!type && meta.kind === 'hasMany') {
7040
          type = Ember.String.singularize(name);
7041
        } else if (!type) {
7042
          type = name;
7043
        }
7044

    
7045
        if (typeof type === 'string') {
7046
          meta.type = this.store.modelFor(type);
7047
        }
7048

    
7049
        map.set(name, meta);
7050
      }
7051
    });
7052

    
7053
    return map;
7054
  }),
7055

    
7056
  /**
7057
    A map whose keys are the fields of the model and whose values are strings
7058
    describing the kind of the field. A model's fields are the union of all of its
7059
    attributes and relationships.
7060

7061
    For example:
7062

7063
    ```javascript
7064

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

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

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

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

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

7086
    @property fields
7087
    @static
7088
    @type Ember.Map
7089
    @readOnly
7090
  */
7091
  fields: Ember.computed(function() {
7092
    var map = Ember.Map.create();
7093

    
7094
    this.eachComputedProperty(function(name, meta) {
7095
      if (meta.isRelationship) {
7096
        map.set(name, meta.kind);
7097
      } else if (meta.isAttribute) {
7098
        map.set(name, 'attribute');
7099
      }
7100
    });
7101

    
7102
    return map;
7103
  }),
7104

    
7105
  /**
7106
    Given a callback, iterates over each of the relationships in the model,
7107
    invoking the callback with the name of each relationship and its relationship
7108
    descriptor.
7109

7110
    @method eachRelationship
7111
    @static
7112
    @param {Function} callback the callback to invoke
7113
    @param {any} binding the value to which the callback's `this` should be bound
7114
  */
7115
  eachRelationship: function(callback, binding) {
7116
    get(this, 'relationshipsByName').forEach(function(name, relationship) {
7117
      callback.call(binding, name, relationship);
7118
    });
7119
  },
7120

    
7121
  /**
7122
    Given a callback, iterates over each of the types related to a model,
7123
    invoking the callback with the related type's class. Each type will be
7124
    returned just once, regardless of how many different relationships it has
7125
    with a model.
7126

7127
    @method eachRelatedType
7128
    @static
7129
    @param {Function} callback the callback to invoke
7130
    @param {any} binding the value to which the callback's `this` should be bound
7131
  */
7132
  eachRelatedType: function(callback, binding) {
7133
    get(this, 'relatedTypes').forEach(function(type) {
7134
      callback.call(binding, type);
7135
    });
7136
  }
7137
});
7138

    
7139
DS.Model.reopen({
7140
  /**
7141
    Given a callback, iterates over each of the relationships in the model,
7142
    invoking the callback with the name of each relationship and its relationship
7143
    descriptor.
7144

7145
    @method eachRelationship
7146
    @param {Function} callback the callback to invoke
7147
    @param {any} binding the value to which the callback's `this` should be bound
7148
  */
7149
  eachRelationship: function(callback, binding) {
7150
    this.constructor.eachRelationship(callback, binding);
7151
  }
7152
});
7153

    
7154
})();
7155

    
7156

    
7157

    
7158
(function() {
7159
/**
7160
  @module ember-data
7161
*/
7162

    
7163
})();
7164

    
7165

    
7166

    
7167
(function() {
7168
/**
7169
  @module ember-data
7170
*/
7171

    
7172
var get = Ember.get, set = Ember.set;
7173
var once = Ember.run.once;
7174
var forEach = Ember.EnumerableUtils.forEach;
7175

    
7176
/**
7177
  @class RecordArrayManager
7178
  @namespace DS
7179
  @private
7180
  @extends Ember.Object
7181
*/
7182
DS.RecordArrayManager = Ember.Object.extend({
7183
  init: function() {
7184
    this.filteredRecordArrays = Ember.MapWithDefault.create({
7185
      defaultValue: function() { return []; }
7186
    });
7187

    
7188
    this.changedRecords = [];
7189
  },
7190

    
7191
  recordDidChange: function(record) {
7192
    this.changedRecords.push(record);
7193
    once(this, this.updateRecordArrays);
7194
  },
7195

    
7196
  recordArraysForRecord: function(record) {
7197
    record._recordArrays = record._recordArrays || Ember.OrderedSet.create();
7198
    return record._recordArrays;
7199
  },
7200

    
7201
  /**
7202
    This method is invoked whenever data is loaded into the store by the
7203
    adapter or updated by the adapter, or when a record has changed.
7204

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

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

7209
    @method updateRecordArrays
7210
    @param {Class} type
7211
    @param {Number|String} clientId
7212
  */
7213
  updateRecordArrays: function() {
7214
    forEach(this.changedRecords, function(record) {
7215
      if (get(record, 'isDeleted')) {
7216
        this._recordWasDeleted(record);
7217
      } else {
7218
        this._recordWasChanged(record);
7219
      }
7220
    }, this);
7221

    
7222
    this.changedRecords = [];
7223
  },
7224

    
7225
  _recordWasDeleted: function (record) {
7226
    var recordArrays = record._recordArrays;
7227

    
7228
    if (!recordArrays) { return; }
7229

    
7230
    forEach(recordArrays, function(array) {
7231
      array.removeRecord(record);
7232
    });
7233
  },
7234

    
7235
  _recordWasChanged: function (record) {
7236
    var type = record.constructor,
7237
        recordArrays = this.filteredRecordArrays.get(type),
7238
        filter;
7239

    
7240
    forEach(recordArrays, function(array) {
7241
      filter = get(array, 'filterFunction');
7242
      this.updateRecordArray(array, filter, type, record);
7243
    }, this);
7244

    
7245
    // loop through all manyArrays containing an unloaded copy of this
7246
    // clientId and notify them that the record was loaded.
7247
    var manyArrays = record._loadingRecordArrays;
7248

    
7249
    if (manyArrays) {
7250
      for (var i=0, l=manyArrays.length; i<l; i++) {
7251
        manyArrays[i].loadedRecord();
7252
      }
7253

    
7254
      record._loadingRecordArrays = [];
7255
    }
7256
  },
7257

    
7258
  /**
7259
    Update an individual filter.
7260

7261
    @method updateRecordArray
7262
    @param {DS.FilteredRecordArray} array
7263
    @param {Function} filter
7264
    @param {Class} type
7265
    @param {Number|String} clientId
7266
  */
7267
  updateRecordArray: function(array, filter, type, record) {
7268
    var shouldBeInArray;
7269

    
7270
    if (!filter) {
7271
      shouldBeInArray = true;
7272
    } else {
7273
      shouldBeInArray = filter(record);
7274
    }
7275

    
7276
    var recordArrays = this.recordArraysForRecord(record);
7277

    
7278
    if (shouldBeInArray) {
7279
      recordArrays.add(array);
7280
      array.addRecord(record);
7281
    } else if (!shouldBeInArray) {
7282
      recordArrays.remove(array);
7283
      array.removeRecord(record);
7284
    }
7285
  },
7286

    
7287
  /**
7288
    This method is invoked if the `filterFunction` property is
7289
    changed on a `DS.FilteredRecordArray`.
7290

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

7294
    @method updateFilter
7295
    @param array
7296
    @param type
7297
    @param filter
7298
  */
7299
  updateFilter: function(array, type, filter) {
7300
    var typeMap = this.store.typeMapFor(type),
7301
        records = typeMap.records, record;
7302

    
7303
    for (var i=0, l=records.length; i<l; i++) {
7304
      record = records[i];
7305

    
7306
      if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) {
7307
        this.updateRecordArray(array, filter, type, record);
7308
      }
7309
    }
7310
  },
7311

    
7312
  /**
7313
    Create a `DS.ManyArray` for a type and list of record references, and index
7314
    the `ManyArray` under each reference. This allows us to efficiently remove
7315
    records from `ManyArray`s when they are deleted.
7316

7317
    @method createManyArray
7318
    @param {Class} type
7319
    @param {Array} references
7320
    @return {DS.ManyArray}
7321
  */
7322
  createManyArray: function(type, records) {
7323
    var manyArray = DS.ManyArray.create({
7324
      type: type,
7325
      content: records,
7326
      store: this.store
7327
    });
7328

    
7329
    forEach(records, function(record) {
7330
      var arrays = this.recordArraysForRecord(record);
7331
      arrays.add(manyArray);
7332
    }, this);
7333

    
7334
    return manyArray;
7335
  },
7336

    
7337
  /**
7338
    Create a `DS.RecordArray` for a type and register it for updates.
7339

7340
    @method createRecordArray
7341
    @param {Class} type
7342
    @return {DS.RecordArray}
7343
  */
7344
  createRecordArray: function(type) {
7345
    var array = DS.RecordArray.create({
7346
      type: type,
7347
      content: Ember.A(),
7348
      store: this.store,
7349
      isLoaded: true
7350
    });
7351

    
7352
    this.registerFilteredRecordArray(array, type);
7353

    
7354
    return array;
7355
  },
7356

    
7357
  /**
7358
    Create a `DS.FilteredRecordArray` for a type and register it for updates.
7359

7360
    @method createFilteredRecordArray
7361
    @param {Class} type
7362
    @param {Function} filter
7363
    @return {DS.FilteredRecordArray}
7364
  */
7365
  createFilteredRecordArray: function(type, filter) {
7366
    var array = DS.FilteredRecordArray.create({
7367
      type: type,
7368
      content: Ember.A(),
7369
      store: this.store,
7370
      manager: this,
7371
      filterFunction: filter
7372
    });
7373

    
7374
    this.registerFilteredRecordArray(array, type, filter);
7375

    
7376
    return array;
7377
  },
7378

    
7379
  /**
7380
    Create a `DS.AdapterPopulatedRecordArray` for a type with given query.
7381

7382
    @method createAdapterPopulatedRecordArray
7383
    @param {Class} type
7384
    @param {Object} query
7385
    @return {DS.AdapterPopulatedRecordArray}
7386
  */
7387
  createAdapterPopulatedRecordArray: function(type, query) {
7388
    return DS.AdapterPopulatedRecordArray.create({
7389
      type: type,
7390
      query: query,
7391
      content: Ember.A(),
7392
      store: this.store
7393
    });
7394
  },
7395

    
7396
  /**
7397
    Register a RecordArray for a given type to be backed by
7398
    a filter function. This will cause the array to update
7399
    automatically when records of that type change attribute
7400
    values or states.
7401

7402
    @method registerFilteredRecordArray
7403
    @param {DS.RecordArray} array
7404
    @param {Class} type
7405
    @param {Function} filter
7406
  */
7407
  registerFilteredRecordArray: function(array, type, filter) {
7408
    var recordArrays = this.filteredRecordArrays.get(type);
7409
    recordArrays.push(array);
7410

    
7411
    this.updateFilter(array, type, filter);
7412
  },
7413

    
7414
  // Internally, we maintain a map of all unloaded IDs requested by
7415
  // a ManyArray. As the adapter loads data into the store, the
7416
  // store notifies any interested ManyArrays. When the ManyArray's
7417
  // total number of loading records drops to zero, it becomes
7418
  // `isLoaded` and fires a `didLoad` event.
7419
  registerWaitingRecordArray: function(record, array) {
7420
    var loadingRecordArrays = record._loadingRecordArrays || [];
7421
    loadingRecordArrays.push(array);
7422
    record._loadingRecordArrays = loadingRecordArrays;
7423
  }
7424
});
7425

    
7426
})();
7427

    
7428

    
7429

    
7430
(function() {
7431
/**
7432
  @module ember-data
7433
*/
7434

    
7435
var get = Ember.get, set = Ember.set;
7436
var map = Ember.ArrayPolyfills.map;
7437

    
7438
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
7439

    
7440
/**
7441
  A `DS.InvalidError` is used by an adapter to signal the external API
7442
  was unable to process a request because the content was not
7443
  semantically correct or meaningful per the API. Usually this means a
7444
  record failed some form of server side validation. When a promise
7445
  from an adapter is rejected with a `DS.InvalidError` the record will
7446
  transition to the `invalid` state and the errors will be set to the
7447
  `errors` property on the record.
7448

7449
  Example
7450

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

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

7466
  @class InvalidError
7467
  @namespace DS
7468
*/
7469
DS.InvalidError = function(errors) {
7470
  var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors));
7471
  this.errors = errors;
7472

    
7473
  for (var i=0, l=errorProps.length; i<l; i++) {
7474
    this[errorProps[i]] = tmp[errorProps[i]];
7475
  }
7476
};
7477
DS.InvalidError.prototype = Ember.create(Error.prototype);
7478

    
7479
/**
7480
  An adapter is an object that receives requests from a store and
7481
  translates them into the appropriate action to take against your
7482
  persistence layer. The persistence layer is usually an HTTP API, but
7483
  may be anything, such as the browser's local storage. Typically the
7484
  adapter is not invoked directly instead its functionality is accessed
7485
  through the `store`.
7486

7487
  ### Creating an Adapter
7488

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

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

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

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

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

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

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

7519
    * `findMany()`
7520

7521

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

7525
  @class Adapter
7526
  @namespace DS
7527
  @extends Ember.Object
7528
*/
7529

    
7530
DS.Adapter = Ember.Object.extend({
7531

    
7532
  /**
7533
    If you would like your adapter to use a custom serializer you can
7534
    set the `defaultSerializer` property to be the name of the custom
7535
    serializer.
7536

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

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

7547
    @property defaultSerializer
7548
    @type {String}
7549
  */
7550

    
7551
  /**
7552
    The `find()` method is invoked when the store is asked for a record that
7553
    has not previously been loaded. In response to `find()` being called, you
7554
    should query your persistence layer for a record with the given ID. Once
7555
    found, you can asynchronously call the store's `push()` method to push
7556
    the record into the store.
7557

7558
    Here is an example `find` implementation:
7559

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

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

7577
    @method find
7578
    @param {DS.Store} store
7579
    @param {subclass of DS.Model} type
7580
    @param {String} id
7581
    @return {Promise} promise
7582
  */
7583
  find: Ember.required(Function),
7584

    
7585
  /**
7586
    The `findAll()` method is called when you call `find` on the store
7587
    without an ID (i.e. `store.find('post')`).
7588

7589
    Example
7590

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

7608
    @private
7609
    @method findAll
7610
    @param {DS.Store} store
7611
    @param {subclass of DS.Model} type
7612
    @param {String} sinceToken
7613
    @return {Promise} promise
7614
  */
7615
  findAll: null,
7616

    
7617
  /**
7618
    This method is called when you call `find` on the store with a
7619
    query object as the second parameter (i.e. `store.find('person', {
7620
    page: 1 })`).
7621

7622
    Example
7623

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

7640
    @private
7641
    @method findQuery
7642
    @param {DS.Store} store
7643
    @param {subclass of DS.Model} type
7644
    @param {Object} query
7645
    @param {DS.AdapterPopulatedRecordArray} recordArray
7646
    @return {Promise} promise
7647
  */
7648
  findQuery: null,
7649

    
7650
  /**
7651
    If the globally unique IDs for your records should be generated on the client,
7652
    implement the `generateIdForRecord()` method. This method will be invoked
7653
    each time you create a new record, and the value returned from it will be
7654
    assigned to the record's `primaryKey`.
7655

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

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

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

7671
    @method generateIdForRecord
7672
    @param {DS.Store} store
7673
    @param {DS.Model} record
7674
    @return {String|Number} id
7675
  */
7676
  generateIdForRecord: null,
7677

    
7678
  /**
7679
    Proxies to the serializer's `serialize` method.
7680

7681
    Example
7682

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

7689
        // ...
7690
      }
7691
    });
7692
    ```
7693

7694
    @method serialize
7695
    @param {DS.Model} record
7696
    @param {Object}   options
7697
    @return {Object} serialized record
7698
  */
7699
  serialize: function(record, options) {
7700
    return get(record, 'store').serializerFor(record.constructor.typeKey).serialize(record, options);
7701
  },
7702

    
7703
  /**
7704
    Implement this method in a subclass to handle the creation of
7705
    new records.
7706

7707
    Serializes the record and send it to the server.
7708

7709
    Example
7710

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

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

7734
    @method createRecord
7735
    @param {DS.Store} store
7736
    @param {subclass of DS.Model} type   the DS.Model class of the record
7737
    @param {DS.Model} record
7738
    @return {Promise} promise
7739
  */
7740
  createRecord: Ember.required(Function),
7741

    
7742
  /**
7743
    Implement this method in a subclass to handle the updating of
7744
    a record.
7745

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

7748
    Example
7749

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

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

7774
    @method updateRecord
7775
    @param {DS.Store} store
7776
    @param {subclass of DS.Model} type   the DS.Model class of the record
7777
    @param {DS.Model} record
7778
    @return {Promise} promise
7779
  */
7780
  updateRecord: Ember.required(Function),
7781

    
7782
  /**
7783
    Implement this method in a subclass to handle the deletion of
7784
    a record.
7785

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

7788
    Example
7789

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

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

7814
    @method deleteRecord
7815
    @param {DS.Store} store
7816
    @param {subclass of DS.Model} type   the DS.Model class of the record
7817
    @param {DS.Model} record
7818
    @return {Promise} promise
7819
  */
7820
  deleteRecord: Ember.required(Function),
7821

    
7822
  /**
7823
    Find multiple records at once.
7824

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

7829
    Example
7830

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

7847
    @method findMany
7848
    @param {DS.Store} store
7849
    @param {subclass of DS.Model} type   the DS.Model class of the records
7850
    @param {Array}    ids
7851
    @return {Promise} promise
7852
  */
7853
  findMany: function(store, type, ids) {
7854
    var promises = map.call(ids, function(id) {
7855
      return this.find(store, type, id);
7856
    }, this);
7857

    
7858
    return Ember.RSVP.all(promises);
7859
  }
7860
});
7861

    
7862
})();
7863

    
7864

    
7865

    
7866
(function() {
7867
/**
7868
  @module ember-data
7869
*/
7870

    
7871
var get = Ember.get, fmt = Ember.String.fmt,
7872
    indexOf = Ember.EnumerableUtils.indexOf;
7873

    
7874
var counter = 0;
7875

    
7876
/**
7877
  `DS.FixtureAdapter` is an adapter that loads records from memory.
7878
  Its primarily used for development and testing. You can also use
7879
  `DS.FixtureAdapter` while working on the API but are not ready to
7880
  integrate yet. It is a fully functioning adapter. All CRUD methods
7881
  are implemented. You can also implement query logic that a remote
7882
  system would do. Its possible to do develop your entire application
7883
  with `DS.FixtureAdapter`.
7884

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

7889
  @class FixtureAdapter
7890
  @namespace DS
7891
  @extends DS.Adapter
7892
*/
7893
DS.FixtureAdapter = DS.Adapter.extend({
7894
  // by default, fixtures are already in normalized form
7895
  serializer: null,
7896

    
7897
  /**
7898
    If `simulateRemoteResponse` is `true` the `FixtureAdapter` will
7899
    wait a number of milliseconds before resolving promises with the
7900
    fixture values. The wait time can be configured via the `latency`
7901
    property.
7902

7903
    @property simulateRemoteResponse
7904
    @type {Boolean}
7905
    @default true
7906
  */
7907
  simulateRemoteResponse: true,
7908

    
7909
  /**
7910
    By default the `FixtureAdapter` will simulate a wait of the
7911
    `latency` milliseconds before resolving promises with the fixture
7912
    values. This behavior can be turned off via the
7913
    `simulateRemoteResponse` property.
7914

7915
    @property latency
7916
    @type {Number}
7917
    @default 50
7918
  */
7919
  latency: 50,
7920

    
7921
  /**
7922
    Implement this method in order to provide data associated with a type
7923

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

    
7943
  /**
7944
    Implement this method in order to query fixtures data
7945

7946
    @method queryFixtures
7947
    @param {Array} fixture
7948
    @param {Object} query
7949
    @param {Subclass of DS.Model} type
7950
    @return {Promise|Array}
7951
  */
7952
  queryFixtures: function(fixtures, query, type) {
7953
    Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
7954
  },
7955

    
7956
  /**
7957
    @method updateFixtures
7958
    @param {Subclass of DS.Model} type
7959
    @param {Array} fixture
7960
  */
7961
  updateFixtures: function(type, fixture) {
7962
    if(!type.FIXTURES) {
7963
      type.FIXTURES = [];
7964
    }
7965

    
7966
    var fixtures = type.FIXTURES;
7967

    
7968
    this.deleteLoadedFixture(type, fixture);
7969

    
7970
    fixtures.push(fixture);
7971
  },
7972

    
7973
  /**
7974
    Implement this method in order to provide json for CRUD methods
7975

7976
    @method mockJSON
7977
    @param {Subclass of DS.Model} type
7978
    @param {DS.Model} record
7979
  */
7980
  mockJSON: function(store, type, record) {
7981
    return store.serializerFor(type).serialize(record, { includeId: true });
7982
  },
7983

    
7984
  /**
7985
    @method generateIdForRecord
7986
    @param {DS.Store} store
7987
    @param {DS.Model} record
7988
    @return {String} id
7989
  */
7990
  generateIdForRecord: function(store) {
7991
    return "fixture-" + counter++;
7992
  },
7993

    
7994
  /**
7995
    @method find
7996
    @param {DS.Store} store
7997
    @param {subclass of DS.Model} type
7998
    @param {String} id
7999
    @return {Promise} promise
8000
  */
8001
  find: function(store, type, id) {
8002
    var fixtures = this.fixturesForType(type),
8003
        fixture;
8004

    
8005
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8006

    
8007
    if (fixtures) {
8008
      fixture = Ember.A(fixtures).findProperty('id', id);
8009
    }
8010

    
8011
    if (fixture) {
8012
      return this.simulateRemoteCall(function() {
8013
        return fixture;
8014
      }, this);
8015
    }
8016
  },
8017

    
8018
  /**
8019
    @method findMany
8020
    @param {DS.Store} store
8021
    @param {subclass of DS.Model} type
8022
    @param {Array} ids
8023
    @return {Promise} promise
8024
  */
8025
  findMany: function(store, type, ids) {
8026
    var fixtures = this.fixturesForType(type);
8027

    
8028
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8029

    
8030
    if (fixtures) {
8031
      fixtures = fixtures.filter(function(item) {
8032
        return indexOf(ids, item.id) !== -1;
8033
      });
8034
    }
8035

    
8036
    if (fixtures) {
8037
      return this.simulateRemoteCall(function() {
8038
        return fixtures;
8039
      }, this);
8040
    }
8041
  },
8042

    
8043
  /**
8044
    @private
8045
    @method findAll
8046
    @param {DS.Store} store
8047
    @param {subclass of DS.Model} type
8048
    @param {String} sinceToken
8049
    @return {Promise} promise
8050
  */
8051
  findAll: function(store, type) {
8052
    var fixtures = this.fixturesForType(type);
8053

    
8054
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8055

    
8056
    return this.simulateRemoteCall(function() {
8057
      return fixtures;
8058
    }, this);
8059
  },
8060

    
8061
  /**
8062
    @private
8063
    @method findQuery
8064
    @param {DS.Store} store
8065
    @param {subclass of DS.Model} type
8066
    @param {Object} query
8067
    @param {DS.AdapterPopulatedRecordArray} recordArray
8068
    @return {Promise} promise
8069
  */
8070
  findQuery: function(store, type, query, array) {
8071
    var fixtures = this.fixturesForType(type);
8072

    
8073
    Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
8074

    
8075
    fixtures = this.queryFixtures(fixtures, query, type);
8076

    
8077
    if (fixtures) {
8078
      return this.simulateRemoteCall(function() {
8079
        return fixtures;
8080
      }, this);
8081
    }
8082
  },
8083

    
8084
  /**
8085
    @method createRecord
8086
    @param {DS.Store} store
8087
    @param {subclass of DS.Model} type
8088
    @param {DS.Model} record
8089
    @return {Promise} promise
8090
  */
8091
  createRecord: function(store, type, record) {
8092
    var fixture = this.mockJSON(store, type, record);
8093

    
8094
    this.updateFixtures(type, fixture);
8095

    
8096
    return this.simulateRemoteCall(function() {
8097
      return fixture;
8098
    }, this);
8099
  },
8100

    
8101
  /**
8102
    @method updateRecord
8103
    @param {DS.Store} store
8104
    @param {subclass of DS.Model} type
8105
    @param {DS.Model} record
8106
    @return {Promise} promise
8107
  */
8108
  updateRecord: function(store, type, record) {
8109
    var fixture = this.mockJSON(store, type, record);
8110

    
8111
    this.updateFixtures(type, fixture);
8112

    
8113
    return this.simulateRemoteCall(function() {
8114
      return fixture;
8115
    }, this);
8116
  },
8117

    
8118
  /**
8119
    @method deleteRecord
8120
    @param {DS.Store} store
8121
    @param {subclass of DS.Model} type
8122
    @param {DS.Model} record
8123
    @return {Promise} promise
8124
  */
8125
  deleteRecord: function(store, type, record) {
8126
    var fixture = this.mockJSON(store, type, record);
8127

    
8128
    this.deleteLoadedFixture(type, fixture);
8129

    
8130
    return this.simulateRemoteCall(function() {
8131
      // no payload in a deletion
8132
      return null;
8133
    });
8134
  },
8135

    
8136
  /*
8137
    @method deleteLoadedFixture
8138
    @private
8139
    @param type
8140
    @param record
8141
  */
8142
  deleteLoadedFixture: function(type, record) {
8143
    var existingFixture = this.findExistingFixture(type, record);
8144

    
8145
    if(existingFixture) {
8146
      var index = indexOf(type.FIXTURES, existingFixture);
8147
      type.FIXTURES.splice(index, 1);
8148
      return true;
8149
    }
8150
  },
8151

    
8152
  /*
8153
    @method findExistingFixture
8154
    @private
8155
    @param type
8156
    @param record
8157
  */
8158
  findExistingFixture: function(type, record) {
8159
    var fixtures = this.fixturesForType(type);
8160
    var id = get(record, 'id');
8161

    
8162
    return this.findFixtureById(fixtures, id);
8163
  },
8164

    
8165
  /*
8166
    @method findFixtureById
8167
    @private
8168
    @param fixtures
8169
    @param id
8170
  */
8171
  findFixtureById: function(fixtures, id) {
8172
    return Ember.A(fixtures).find(function(r) {
8173
      if(''+get(r, 'id') === ''+id) {
8174
        return true;
8175
      } else {
8176
        return false;
8177
      }
8178
    });
8179
  },
8180

    
8181
  /*
8182
    @method simulateRemoteCall
8183
    @private
8184
    @param callback
8185
    @param context
8186
  */
8187
  simulateRemoteCall: function(callback, context) {
8188
    var adapter = this;
8189

    
8190
    return new Ember.RSVP.Promise(function(resolve) {
8191
      if (get(adapter, 'simulateRemoteResponse')) {
8192
        // Schedule with setTimeout
8193
        Ember.run.later(function() {
8194
          resolve(callback.call(context));
8195
        }, get(adapter, 'latency'));
8196
      } else {
8197
        // Asynchronous, but at the of the runloop with zero latency
8198
        Ember.run.schedule('actions', null, function() {
8199
          resolve(callback.call(context));
8200
        });
8201
      }
8202
    }, "DS: FixtureAdapter#simulateRemoteCall");
8203
  }
8204
});
8205

    
8206
})();
8207

    
8208

    
8209

    
8210
(function() {
8211
/**
8212
  @module ember-data
8213
*/
8214

    
8215
var get = Ember.get, set = Ember.set;
8216
var forEach = Ember.ArrayPolyfills.forEach;
8217
var map = Ember.ArrayPolyfills.map;
8218

    
8219
function coerceId(id) {
8220
  return id == null ? null : id+'';
8221
}
8222

    
8223
/**
8224
  Normally, applications will use the `RESTSerializer` by implementing
8225
  the `normalize` method and individual normalizations under
8226
  `normalizeHash`.
8227

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

8232
  See the `normalize` documentation for more information.
8233

8234
  ## Across the Board Normalization
8235

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

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

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

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

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

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

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

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

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

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

8308
    @property normalizeHash
8309
    @type {Object}
8310
    @default undefined
8311
  */
8312

    
8313
  /**
8314
    Normalizes a part of the JSON payload returned by
8315
    the server. You should override this method, munge the hash
8316
    and call super if you have generic normalization to do.
8317

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

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

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

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

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

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

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

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

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

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

8371
    @method normalize
8372
    @param {subclass of DS.Model} type
8373
    @param {Object} hash
8374
    @param {String} prop
8375
    @returns {Object}
8376
  */
8377
  normalize: function(type, hash, prop) {
8378
    this.normalizeId(hash);
8379
    this.normalizeAttributes(type, hash);
8380
    this.normalizeRelationships(type, hash);
8381

    
8382
    this.normalizeUsingDeclaredMapping(type, hash);
8383

    
8384
    if (this.normalizeHash && this.normalizeHash[prop]) {
8385
      this.normalizeHash[prop](hash);
8386
    }
8387

    
8388
    return this._super(type, hash, prop);
8389
  },
8390

    
8391
  /**
8392
    You can use this method to normalize all payloads, regardless of whether they
8393
    represent single records or an array.
8394

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

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

8407
    @method normalizePayload
8408
    @param {subclass of DS.Model} type
8409
    @param {Object} hash
8410
    @returns {Object} the normalized payload
8411
  */
8412
  normalizePayload: function(type, payload) {
8413
    return payload;
8414
  },
8415

    
8416
  /**
8417
    @method normalizeId
8418
    @private
8419
  */
8420
  normalizeId: function(hash) {
8421
    var primaryKey = get(this, 'primaryKey');
8422

    
8423
    if (primaryKey === 'id') { return; }
8424

    
8425
    hash.id = hash[primaryKey];
8426
    delete hash[primaryKey];
8427
  },
8428

    
8429
  /**
8430
    @method normalizeUsingDeclaredMapping
8431
    @private
8432
  */
8433
  normalizeUsingDeclaredMapping: function(type, hash) {
8434
    var attrs = get(this, 'attrs'), payloadKey, key;
8435

    
8436
    if (attrs) {
8437
      for (key in attrs) {
8438
        payloadKey = attrs[key];
8439
        if (payloadKey && payloadKey.key) {
8440
          payloadKey = payloadKey.key;
8441
        }
8442
        if (typeof payloadKey === 'string') {
8443
          hash[key] = hash[payloadKey];
8444
          delete hash[payloadKey];
8445
        }
8446
      }
8447
    }
8448
  },
8449

    
8450
  /**
8451
    @method normalizeAttributes
8452
    @private
8453
  */
8454
  normalizeAttributes: function(type, hash) {
8455
    var payloadKey, key;
8456

    
8457
    if (this.keyForAttribute) {
8458
      type.eachAttribute(function(key) {
8459
        payloadKey = this.keyForAttribute(key);
8460
        if (key === payloadKey) { return; }
8461

    
8462
        hash[key] = hash[payloadKey];
8463
        delete hash[payloadKey];
8464
      }, this);
8465
    }
8466
  },
8467

    
8468
  /**
8469
    @method normalizeRelationships
8470
    @private
8471
  */
8472
  normalizeRelationships: function(type, hash) {
8473
    var payloadKey, key;
8474

    
8475
    if (this.keyForRelationship) {
8476
      type.eachRelationship(function(key, relationship) {
8477
        payloadKey = this.keyForRelationship(key, relationship.kind);
8478
        if (key === payloadKey) { return; }
8479

    
8480
        hash[key] = hash[payloadKey];
8481
        delete hash[payloadKey];
8482
      }, this);
8483
    }
8484
  },
8485

    
8486
  /**
8487
    Called when the server has returned a payload representing
8488
    a single record, such as in response to a `find` or `save`.
8489

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

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

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

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

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

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

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

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

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

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

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

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

8555
    @method extractSingle
8556
    @param {DS.Store} store
8557
    @param {subclass of DS.Model} type
8558
    @param {Object} payload
8559
    @param {String} id
8560
    @param {'find'|'createRecord'|'updateRecord'|'deleteRecord'} requestType
8561
    @returns {Object} the primary response to the original request
8562
  */
8563
  extractSingle: function(store, primaryType, payload, recordId, requestType) {
8564
    payload = this.normalizePayload(primaryType, payload);
8565

    
8566
    var primaryTypeName = primaryType.typeKey,
8567
        primaryRecord;
8568

    
8569
    for (var prop in payload) {
8570
      var typeName  = this.typeForRoot(prop),
8571
          isPrimary = typeName === primaryTypeName;
8572

    
8573
      // legacy support for singular resources
8574
      if (isPrimary && Ember.typeOf(payload[prop]) !== "array" ) {
8575
        primaryRecord = this.normalize(primaryType, payload[prop], prop);
8576
        continue;
8577
      }
8578

    
8579
      var type = store.modelFor(typeName);
8580

    
8581
      /*jshint loopfunc:true*/
8582
      forEach.call(payload[prop], function(hash) {
8583
        var typeName = this.typeForRoot(prop),
8584
            type = store.modelFor(typeName),
8585
            typeSerializer = store.serializerFor(type);
8586

    
8587
        hash = typeSerializer.normalize(type, hash, prop);
8588

    
8589
        var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord,
8590
            isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId;
8591

    
8592
        // find the primary record.
8593
        //
8594
        // It's either:
8595
        // * the record with the same ID as the original request
8596
        // * in the case of a newly created record that didn't have an ID, the first
8597
        //   record in the Array
8598
        if (isFirstCreatedRecord || isUpdatedRecord) {
8599
          primaryRecord = hash;
8600
        } else {
8601
          store.push(typeName, hash);
8602
        }
8603
      }, this);
8604
    }
8605

    
8606
    return primaryRecord;
8607
  },
8608

    
8609
  /**
8610
    Called when the server has returned a payload representing
8611
    multiple records, such as in response to a `findAll` or `findQuery`.
8612

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

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

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

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

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

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

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

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

8668
        payload = { comments: comments, posts: payload };
8669

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

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

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

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

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

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

8702
    @method extractArray
8703
    @param {DS.Store} store
8704
    @param {subclass of DS.Model} type
8705
    @param {Object} payload
8706
    @param {'findAll'|'findMany'|'findHasMany'|'findQuery'} requestType
8707
    @returns {Array} The primary array that was returned in response
8708
      to the original query.
8709
  */
8710
  extractArray: function(store, primaryType, payload) {
8711
    payload = this.normalizePayload(primaryType, payload);
8712

    
8713
    var primaryTypeName = primaryType.typeKey,
8714
        primaryArray;
8715

    
8716
    for (var prop in payload) {
8717
      var typeKey = prop,
8718
          forcedSecondary = false;
8719

    
8720
      if (prop.charAt(0) === '_') {
8721
        forcedSecondary = true;
8722
        typeKey = prop.substr(1);
8723
      }
8724

    
8725
      var typeName = this.typeForRoot(typeKey),
8726
          type = store.modelFor(typeName),
8727
          typeSerializer = store.serializerFor(type),
8728
          isPrimary = (!forcedSecondary && (typeName === primaryTypeName));
8729

    
8730
      /*jshint loopfunc:true*/
8731
      var normalizedArray = map.call(payload[prop], function(hash) {
8732
        return typeSerializer.normalize(type, hash, prop);
8733
      }, this);
8734

    
8735
      if (isPrimary) {
8736
        primaryArray = normalizedArray;
8737
      } else {
8738
        store.pushMany(typeName, normalizedArray);
8739
      }
8740
    }
8741

    
8742
    return primaryArray;
8743
  },
8744

    
8745
  /**
8746
    This method allows you to push a payload containing top-level
8747
    collections of records organized per type.
8748

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

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

8772
    @method pushPayload
8773
    @param {DS.Store} store
8774
    @param {Object} payload
8775
  */
8776
  pushPayload: function(store, payload) {
8777
    payload = this.normalizePayload(null, payload);
8778

    
8779
    for (var prop in payload) {
8780
      var typeName = this.typeForRoot(prop),
8781
          type = store.modelFor(typeName);
8782

    
8783
      /*jshint loopfunc:true*/
8784
      var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) {
8785
        return this.normalize(type, hash, prop);
8786
      }, this);
8787

    
8788
      store.pushMany(typeName, normalizedArray);
8789
    }
8790
  },
8791

    
8792
  /**
8793
    You can use this method to normalize the JSON root keys returned
8794
    into the model type expected by your store.
8795

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

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

8808
    @method typeForRoot
8809
    @param {String} root
8810
    @returns {String} the model's typeKey
8811
  */
8812
  typeForRoot: function(root) {
8813
    return Ember.String.singularize(root);
8814
  },
8815

    
8816
  // SERIALIZE
8817

    
8818
  /**
8819
    Called when a record is saved in order to convert the
8820
    record into JSON.
8821

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

8825
    For example, consider this model:
8826

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

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

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

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

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

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

8854
    ## IDs
8855

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

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

8863
    ## Customization
8864

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

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

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

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

8884
        return json;
8885
      }
8886
    });
8887
    ```
8888

8889
    ## Customizing an App-Wide Serializer
8890

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

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

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

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

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

8914
        return json;
8915
      }
8916
    });
8917

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

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

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

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

8937
    ## Tweaking the Default JSON
8938

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

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

8948
        json.subject = json.title;
8949
        delete json.title;
8950

8951
        return json;
8952
      }
8953
    });
8954
    ```
8955

8956
    @method serialize
8957
    @param record
8958
    @param options
8959
  */
8960
  serialize: function(record, options) {
8961
    return this._super.apply(this, arguments);
8962
  },
8963

    
8964
  /**
8965
    You can use this method to customize the root keys serialized into the JSON.
8966
    By default the REST Serializer sends camelized root keys.
8967
    For example, your server may expect underscored root objects.
8968

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

8978
    @method serializeIntoHash
8979
    @param {Object} hash
8980
    @param {subclass of DS.Model} type
8981
    @param {DS.Model} record
8982
    @param {Object} options
8983
  */
8984
  serializeIntoHash: function(hash, type, record, options) {
8985
    hash[type.typeKey] = this.serialize(record, options);
8986
  },
8987

    
8988
  /**
8989
    You can use this method to customize how polymorphic objects are serialized.
8990
    By default the JSON Serializer creates the key by appending `Type` to
8991
    the attribute and value from the model's camelcased model name.
8992

8993
    @method serializePolymorphicType
8994
    @param {DS.Model} record
8995
    @param {Object} json
8996
    @param {Object} relationship
8997
  */
8998
  serializePolymorphicType: function(record, json, relationship) {
8999
    var key = relationship.key,
9000
        belongsTo = get(record, key);
9001
    key = this.keyForAttribute ? this.keyForAttribute(key) : key;
9002
    json[key + "Type"] = belongsTo.constructor.typeKey;
9003
  }
9004
});
9005

    
9006
})();
9007

    
9008

    
9009

    
9010
(function() {
9011
/**
9012
  @module ember-data
9013
*/
9014

    
9015
var get = Ember.get, set = Ember.set;
9016
var forEach = Ember.ArrayPolyfills.forEach;
9017

    
9018
/**
9019
  The REST adapter allows your store to communicate with an HTTP server by
9020
  transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
9021
  should use the REST adapter.
9022

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

9026
  ## JSON Structure
9027

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

9031
  ### Object Root
9032

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

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

9046
  ### Conventional Names
9047

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

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

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

9061
  The JSON returned should look like this:
9062

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

9073
  ## Customization
9074

9075
  ### Endpoint path customization
9076

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

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

9087
  ### Host customization
9088

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

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

9097
  ### Headers customization
9098

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

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

9111
  @class RESTAdapter
9112
  @constructor
9113
  @namespace DS
9114
  @extends DS.Adapter
9115
*/
9116
DS.RESTAdapter = DS.Adapter.extend({
9117
  defaultSerializer: '_rest',
9118

    
9119

    
9120
  /**
9121
    Endpoint paths can be prefixed with a `namespace` by setting the namespace
9122
    property on the adapter:
9123

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

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

9132
    @property namespace
9133
    @type {String}
9134
  */
9135

    
9136
  /**
9137
    An adapter can target other hosts by setting the `host` property.
9138

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

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

9147
    @property host
9148
    @type {String}
9149
  */
9150

    
9151
  /**
9152
    Some APIs require HTTP headers, e.g. to provide an API key. An array of
9153
    headers can be added to the adapter which are passed with every request:
9154

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

9164
    @property headers
9165
    @type {Object}
9166
  */
9167

    
9168
  /**
9169
    Called by the store in order to fetch the JSON for a given
9170
    type and ID.
9171

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

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

9177
    @method find
9178
    @param {DS.Store} store
9179
    @param {subclass of DS.Model} type
9180
    @param {String} id
9181
    @returns {Promise} promise
9182
  */
9183
  find: function(store, type, id) {
9184
    return this.ajax(this.buildURL(type.typeKey, id), 'GET');
9185
  },
9186

    
9187
  /**
9188
    Called by the store in order to fetch a JSON array for all
9189
    of the records for a given type.
9190

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

9194
    @private
9195
    @method findAll
9196
    @param {DS.Store} store
9197
    @param {subclass of DS.Model} type
9198
    @param {String} sinceToken
9199
    @returns {Promise} promise
9200
  */
9201
  findAll: function(store, type, sinceToken) {
9202
    var query;
9203

    
9204
    if (sinceToken) {
9205
      query = { since: sinceToken };
9206
    }
9207

    
9208
    return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
9209
  },
9210

    
9211
  /**
9212
    Called by the store in order to fetch a JSON array for
9213
    the records that match a particular query.
9214

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

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

9221
    @private
9222
    @method findQuery
9223
    @param {DS.Store} store
9224
    @param {subclass of DS.Model} type
9225
    @param {Object} query
9226
    @returns {Promise} promise
9227
  */
9228
  findQuery: function(store, type, query) {
9229
    return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
9230
  },
9231

    
9232
  /**
9233
    Called by the store in order to fetch a JSON array for
9234
    the unloaded records in a has-many relationship that were originally
9235
    specified as IDs.
9236

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

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

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

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

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

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

9260
    @method findMany
9261
    @param {DS.Store} store
9262
    @param {subclass of DS.Model} type
9263
    @param {Array} ids
9264
    @returns {Promise} promise
9265
  */
9266
  findMany: function(store, type, ids) {
9267
    return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } });
9268
  },
9269

    
9270
  /**
9271
    Called by the store in order to fetch a JSON array for
9272
    the unloaded records in a has-many relationship that were originally
9273
    specified as a URL (inside of `links`).
9274

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

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

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

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

9293
    @method findHasMany
9294
    @param {DS.Store} store
9295
    @param {DS.Model} record
9296
    @param {String} url
9297
    @returns {Promise} promise
9298
  */
9299
  findHasMany: function(store, record, url) {
9300
    var host = get(this, 'host'),
9301
        id   = get(record, 'id'),
9302
        type = record.constructor.typeKey;
9303

    
9304
    if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') {
9305
      url = host + url;
9306
    }
9307

    
9308
    return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
9309
  },
9310

    
9311
  /**
9312
    Called by the store in order to fetch a JSON array for
9313
    the unloaded records in a belongs-to relationship that were originally
9314
    specified as a URL (inside of `links`).
9315

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

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

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

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

9332
    @method findBelongsTo
9333
    @param {DS.Store} store
9334
    @param {DS.Model} record
9335
    @param {String} url
9336
    @returns {Promise} promise
9337
  */
9338
  findBelongsTo: function(store, record, url) {
9339
    var id   = get(record, 'id'),
9340
        type = record.constructor.typeKey;
9341

    
9342
    return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
9343
  },
9344

    
9345
  /**
9346
    Called by the store when a newly created record is
9347
    saved via the `save` method on a model record instance.
9348

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

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

9355
    @method createRecord
9356
    @param {DS.Store} store
9357
    @param {subclass of DS.Model} type
9358
    @param {DS.Model} record
9359
    @returns {Promise} promise
9360
  */
9361
  createRecord: function(store, type, record) {
9362
    var data = {};
9363
    var serializer = store.serializerFor(type.typeKey);
9364

    
9365
    serializer.serializeIntoHash(data, type, record, { includeId: true });
9366

    
9367
    return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
9368
  },
9369

    
9370
  /**
9371
    Called by the store when an existing record is saved
9372
    via the `save` method on a model record instance.
9373

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

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

9380
    @method updateRecord
9381
    @param {DS.Store} store
9382
    @param {subclass of DS.Model} type
9383
    @param {DS.Model} record
9384
    @returns {Promise} promise
9385
  */
9386
  updateRecord: function(store, type, record) {
9387
    var data = {};
9388
    var serializer = store.serializerFor(type.typeKey);
9389

    
9390
    serializer.serializeIntoHash(data, type, record);
9391

    
9392
    var id = get(record, 'id');
9393

    
9394
    return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
9395
  },
9396

    
9397
  /**
9398
    Called by the store when a record is deleted.
9399

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

9402
    @method deleteRecord
9403
    @param {DS.Store} store
9404
    @param {subclass of DS.Model} type
9405
    @param {DS.Model} record
9406
    @returns {Promise} promise
9407
  */
9408
  deleteRecord: function(store, type, record) {
9409
    var id = get(record, 'id');
9410

    
9411
    return this.ajax(this.buildURL(type.typeKey, id), "DELETE");
9412
  },
9413

    
9414
  /**
9415
    Builds a URL for a given type and optional ID.
9416

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

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

9424
    @method buildURL
9425
    @param {String} type
9426
    @param {String} id
9427
    @returns {String} url
9428
  */
9429
  buildURL: function(type, id) {
9430
    var url = [],
9431
        host = get(this, 'host'),
9432
        prefix = this.urlPrefix();
9433

    
9434
    if (type) { url.push(this.pathForType(type)); }
9435
    if (id) { url.push(id); }
9436

    
9437
    if (prefix) { url.unshift(prefix); }
9438

    
9439
    url = url.join('/');
9440
    if (!host && url) { url = '/' + url; }
9441

    
9442
    return url;
9443
  },
9444

    
9445
  /**
9446
    @method urlPrefix
9447
    @private
9448
    @param {String} path
9449
    @param {String} parentUrl
9450
    @return {String} urlPrefix
9451
  */
9452
  urlPrefix: function(path, parentURL) {
9453
    var host = get(this, 'host'),
9454
        namespace = get(this, 'namespace'),
9455
        url = [];
9456

    
9457
    if (path) {
9458
      // Absolute path
9459
      if (path.charAt(0) === '/') {
9460
        if (host) {
9461
          path = path.slice(1);
9462
          url.push(host);
9463
        }
9464
      // Relative path
9465
      } else if (!/^http(s)?:\/\//.test(path)) {
9466
        url.push(parentURL);
9467
      }
9468
    } else {
9469
      if (host) { url.push(host); }
9470
      if (namespace) { url.push(namespace); }
9471
    }
9472

    
9473
    if (path) {
9474
      url.push(path);
9475
    }
9476

    
9477
    return url.join('/');
9478
  },
9479

    
9480
  /**
9481
    Determines the pathname for a given type.
9482

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

9486
    ### Pathname customization
9487

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

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

9500
    @method pathForType
9501
    @param {String} type
9502
    @returns {String} path
9503
  **/
9504
  pathForType: function(type) {
9505
    return Ember.String.pluralize(type);
9506
  },
9507

    
9508
  /**
9509
    Takes an ajax response, and returns a relavant error.
9510

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

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

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

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

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

9538
    @method ajaxError
9539
    @param  {Object} jqXHR
9540
    @return {Object} jqXHR
9541
  */
9542
  ajaxError: function(jqXHR) {
9543
    if (jqXHR) {
9544
      jqXHR.then = null;
9545
    }
9546

    
9547
    return jqXHR;
9548
  },
9549

    
9550
  /**
9551
    Takes a URL, an HTTP method and a hash of data, and makes an
9552
    HTTP request.
9553

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

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

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

9567
    @method ajax
9568
    @private
9569
    @param {String} url
9570
    @param {String} type The request type GET, POST, PUT, DELETE ect.
9571
    @param {Object} hash
9572
    @return {Promise} promise
9573
  */
9574
  ajax: function(url, type, hash) {
9575
    var adapter = this;
9576

    
9577
    return new Ember.RSVP.Promise(function(resolve, reject) {
9578
      hash = adapter.ajaxOptions(url, type, hash);
9579

    
9580
      hash.success = function(json) {
9581
        Ember.run(null, resolve, json);
9582
      };
9583

    
9584
      hash.error = function(jqXHR, textStatus, errorThrown) {
9585
        Ember.run(null, reject, adapter.ajaxError(jqXHR));
9586
      };
9587

    
9588
      Ember.$.ajax(hash);
9589
    }, "DS: RestAdapter#ajax " + type + " to " + url);
9590
  },
9591

    
9592
  /**
9593
    @method ajaxOptions
9594
    @private
9595
    @param {String} url
9596
    @param {String} type The request type GET, POST, PUT, DELETE ect.
9597
    @param {Object} hash
9598
    @return {Object} hash
9599
  */
9600
  ajaxOptions: function(url, type, hash) {
9601
    hash = hash || {};
9602
    hash.url = url;
9603
    hash.type = type;
9604
    hash.dataType = 'json';
9605
    hash.context = this;
9606

    
9607
    if (hash.data && type !== 'GET') {
9608
      hash.contentType = 'application/json; charset=utf-8';
9609
      hash.data = JSON.stringify(hash.data);
9610
    }
9611

    
9612
    if (this.headers !== undefined) {
9613
      var headers = this.headers;
9614
      hash.beforeSend = function (xhr) {
9615
        forEach.call(Ember.keys(headers), function(key) {
9616
          xhr.setRequestHeader(key, headers[key]);
9617
        });
9618
      };
9619
    }
9620

    
9621

    
9622
    return hash;
9623
  }
9624

    
9625
});
9626

    
9627
})();
9628

    
9629

    
9630

    
9631
(function() {
9632
/**
9633
  @module ember-data
9634
*/
9635

    
9636
})();
9637

    
9638

    
9639

    
9640
(function() {
9641
DS.Model.reopen({
9642

    
9643
  /**
9644
    Provides info about the model for debugging purposes
9645
    by grouping the properties into more semantic groups.
9646

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

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

9655
    @method _debugInfo
9656
    @for DS.Model
9657
    @private
9658
  */
9659
  _debugInfo: function() {
9660
    var attributes = ['id'],
9661
        relationships = { belongsTo: [], hasMany: [] },
9662
        expensiveProperties = [];
9663

    
9664
    this.eachAttribute(function(name, meta) {
9665
      attributes.push(name);
9666
    }, this);
9667

    
9668
    this.eachRelationship(function(name, relationship) {
9669
      relationships[relationship.kind].push(name);
9670
      expensiveProperties.push(name);
9671
    });
9672

    
9673
    var groups = [
9674
      {
9675
        name: 'Attributes',
9676
        properties: attributes,
9677
        expand: true
9678
      },
9679
      {
9680
        name: 'Belongs To',
9681
        properties: relationships.belongsTo,
9682
        expand: true
9683
      },
9684
      {
9685
        name: 'Has Many',
9686
        properties: relationships.hasMany,
9687
        expand: true
9688
      },
9689
      {
9690
        name: 'Flags',
9691
        properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid']
9692
      }
9693
    ];
9694

    
9695
    return {
9696
      propertyInfo: {
9697
        // include all other mixins / properties (not just the grouped ones)
9698
        includeOtherProperties: true,
9699
        groups: groups,
9700
        // don't pre-calculate unless cached
9701
        expensiveProperties: expensiveProperties
9702
      }
9703
    };
9704
  }
9705

    
9706
});
9707

    
9708
})();
9709

    
9710

    
9711

    
9712
(function() {
9713
/**
9714
  @module ember-data
9715
*/
9716

    
9717
})();
9718

    
9719

    
9720

    
9721
(function() {
9722
/**
9723
  Ember Data
9724

9725
  @module ember-data
9726
  @main ember-data
9727
*/
9728

    
9729
})();
9730

    
9731
(function() {
9732
Ember.String.pluralize = function(word) {
9733
  return Ember.Inflector.inflector.pluralize(word);
9734
};
9735

    
9736
Ember.String.singularize = function(word) {
9737
  return Ember.Inflector.inflector.singularize(word);
9738
};
9739

    
9740
})();
9741

    
9742

    
9743

    
9744
(function() {
9745
var BLANK_REGEX = /^\s*$/;
9746

    
9747
function loadUncountable(rules, uncountable) {
9748
  for (var i = 0, length = uncountable.length; i < length; i++) {
9749
    rules.uncountable[uncountable[i].toLowerCase()] = true;
9750
  }
9751
}
9752

    
9753
function loadIrregular(rules, irregularPairs) {
9754
  var pair;
9755

    
9756
  for (var i = 0, length = irregularPairs.length; i < length; i++) {
9757
    pair = irregularPairs[i];
9758

    
9759
    rules.irregular[pair[0].toLowerCase()] = pair[1];
9760
    rules.irregularInverse[pair[1].toLowerCase()] = pair[0];
9761
  }
9762
}
9763

    
9764
/**
9765
  Inflector.Ember provides a mechanism for supplying inflection rules for your
9766
  application. Ember includes a default set of inflection rules, and provides an
9767
  API for providing additional rules.
9768

9769
  Examples:
9770

9771
  Creating an inflector with no rules.
9772

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

9777
  Creating an inflector with the default ember ruleset.
9778

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

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

9786
  Creating an inflector and adding rules later.
9787

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

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

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

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

9804
  Creating an inflector with a nondefault ruleset.
9805

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

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

9819
  @class Inflector
9820
  @namespace Ember
9821
*/
9822
function Inflector(ruleSet) {
9823
  ruleSet = ruleSet || {};
9824
  ruleSet.uncountable = ruleSet.uncountable || {};
9825
  ruleSet.irregularPairs = ruleSet.irregularPairs || {};
9826

    
9827
  var rules = this.rules = {
9828
    plurals:  ruleSet.plurals || [],
9829
    singular: ruleSet.singular || [],
9830
    irregular: {},
9831
    irregularInverse: {},
9832
    uncountable: {}
9833
  };
9834

    
9835
  loadUncountable(rules, ruleSet.uncountable);
9836
  loadIrregular(rules, ruleSet.irregularPairs);
9837
}
9838

    
9839
Inflector.prototype = {
9840
  /**
9841
    @method plural
9842
    @param {RegExp} regex
9843
    @param {String} string
9844
  */
9845
  plural: function(regex, string) {
9846
    this.rules.plurals.push([regex, string.toLowerCase()]);
9847
  },
9848

    
9849
  /**
9850
    @method singular
9851
    @param {RegExp} regex
9852
    @param {String} string
9853
  */
9854
  singular: function(regex, string) {
9855
    this.rules.singular.push([regex, string.toLowerCase()]);
9856
  },
9857

    
9858
  /**
9859
    @method uncountable
9860
    @param {String} regex
9861
  */
9862
  uncountable: function(string) {
9863
    loadUncountable(this.rules, [string.toLowerCase()]);
9864
  },
9865

    
9866
  /**
9867
    @method irregular
9868
    @param {String} singular
9869
    @param {String} plural
9870
  */
9871
  irregular: function (singular, plural) {
9872
    loadIrregular(this.rules, [[singular, plural]]);
9873
  },
9874

    
9875
  /**
9876
    @method pluralize
9877
    @param {String} word
9878
  */
9879
  pluralize: function(word) {
9880
    return this.inflect(word, this.rules.plurals, this.rules.irregular);
9881
  },
9882

    
9883
  /**
9884
    @method singularize
9885
    @param {String} word
9886
  */
9887
  singularize: function(word) {
9888
    return this.inflect(word, this.rules.singular,  this.rules.irregularInverse);
9889
  },
9890

    
9891
  /**
9892
    @protected
9893

9894
    @method inflect
9895
    @param {String} word
9896
    @param {Object} typeRules
9897
    @param {Object} irregular
9898
  */
9899
  inflect: function(word, typeRules, irregular) {
9900
    var inflection, substitution, result, lowercase, isBlank,
9901
    isUncountable, isIrregular, isIrregularInverse, rule;
9902

    
9903
    isBlank = BLANK_REGEX.test(word);
9904

    
9905
    if (isBlank) {
9906
      return word;
9907
    }
9908

    
9909
    lowercase = word.toLowerCase();
9910

    
9911
    isUncountable = this.rules.uncountable[lowercase];
9912

    
9913
    if (isUncountable) {
9914
      return word;
9915
    }
9916

    
9917
    isIrregular = irregular && irregular[lowercase];
9918

    
9919
    if (isIrregular) {
9920
      return isIrregular;
9921
    }
9922

    
9923
    for (var i = typeRules.length, min = 0; i > min; i--) {
9924
       inflection = typeRules[i-1];
9925
       rule = inflection[0];
9926

    
9927
      if (rule.test(word)) {
9928
        break;
9929
      }
9930
    }
9931

    
9932
    inflection = inflection || [];
9933

    
9934
    rule = inflection[0];
9935
    substitution = inflection[1];
9936

    
9937
    result = word.replace(rule, substitution);
9938

    
9939
    return result;
9940
  }
9941
};
9942

    
9943
Ember.Inflector = Inflector;
9944

    
9945
})();
9946

    
9947

    
9948

    
9949
(function() {
9950
Ember.Inflector.defaultRules = {
9951
  plurals: [
9952
    [/$/, 's'],
9953
    [/s$/i, 's'],
9954
    [/^(ax|test)is$/i, '$1es'],
9955
    [/(octop|vir)us$/i, '$1i'],
9956
    [/(octop|vir)i$/i, '$1i'],
9957
    [/(alias|status)$/i, '$1es'],
9958
    [/(bu)s$/i, '$1ses'],
9959
    [/(buffal|tomat)o$/i, '$1oes'],
9960
    [/([ti])um$/i, '$1a'],
9961
    [/([ti])a$/i, '$1a'],
9962
    [/sis$/i, 'ses'],
9963
    [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
9964
    [/(hive)$/i, '$1s'],
9965
    [/([^aeiouy]|qu)y$/i, '$1ies'],
9966
    [/(x|ch|ss|sh)$/i, '$1es'],
9967
    [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
9968
    [/^(m|l)ouse$/i, '$1ice'],
9969
    [/^(m|l)ice$/i, '$1ice'],
9970
    [/^(ox)$/i, '$1en'],
9971
    [/^(oxen)$/i, '$1'],
9972
    [/(quiz)$/i, '$1zes']
9973
  ],
9974

    
9975
  singular: [
9976
    [/s$/i, ''],
9977
    [/(ss)$/i, '$1'],
9978
    [/(n)ews$/i, '$1ews'],
9979
    [/([ti])a$/i, '$1um'],
9980
    [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
9981
    [/(^analy)(sis|ses)$/i, '$1sis'],
9982
    [/([^f])ves$/i, '$1fe'],
9983
    [/(hive)s$/i, '$1'],
9984
    [/(tive)s$/i, '$1'],
9985
    [/([lr])ves$/i, '$1f'],
9986
    [/([^aeiouy]|qu)ies$/i, '$1y'],
9987
    [/(s)eries$/i, '$1eries'],
9988
    [/(m)ovies$/i, '$1ovie'],
9989
    [/(x|ch|ss|sh)es$/i, '$1'],
9990
    [/^(m|l)ice$/i, '$1ouse'],
9991
    [/(bus)(es)?$/i, '$1'],
9992
    [/(o)es$/i, '$1'],
9993
    [/(shoe)s$/i, '$1'],
9994
    [/(cris|test)(is|es)$/i, '$1is'],
9995
    [/^(a)x[ie]s$/i, '$1xis'],
9996
    [/(octop|vir)(us|i)$/i, '$1us'],
9997
    [/(alias|status)(es)?$/i, '$1'],
9998
    [/^(ox)en/i, '$1'],
9999
    [/(vert|ind)ices$/i, '$1ex'],
10000
    [/(matr)ices$/i, '$1ix'],
10001
    [/(quiz)zes$/i, '$1'],
10002
    [/(database)s$/i, '$1']
10003
  ],
10004

    
10005
  irregularPairs: [
10006
    ['person', 'people'],
10007
    ['man', 'men'],
10008
    ['child', 'children'],
10009
    ['sex', 'sexes'],
10010
    ['move', 'moves'],
10011
    ['cow', 'kine'],
10012
    ['zombie', 'zombies']
10013
  ],
10014

    
10015
  uncountable: [
10016
    'equipment',
10017
    'information',
10018
    'rice',
10019
    'money',
10020
    'species',
10021
    'series',
10022
    'fish',
10023
    'sheep',
10024
    'jeans',
10025
    'police'
10026
  ]
10027
};
10028

    
10029
})();
10030

    
10031

    
10032

    
10033
(function() {
10034
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
10035
  /**
10036
    See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
10037

10038
    @method pluralize
10039
    @for String
10040
  */
10041
  String.prototype.pluralize = function() {
10042
    return Ember.String.pluralize(this);
10043
  };
10044

    
10045
  /**
10046
    See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
10047

10048
    @method singularize
10049
    @for String
10050
  */
10051
  String.prototype.singularize = function() {
10052
    return Ember.String.singularize(this);
10053
  };
10054
}
10055

    
10056
})();
10057

    
10058

    
10059

    
10060
(function() {
10061
Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
10062

    
10063
})();
10064

    
10065

    
10066

    
10067
(function() {
10068

    
10069
})();
10070

    
10071
(function() {
10072
/**
10073
  @module ember-data
10074
*/
10075

    
10076
var get = Ember.get;
10077
var forEach = Ember.EnumerableUtils.forEach;
10078

    
10079
DS.ActiveModelSerializer = DS.RESTSerializer.extend({
10080
  // SERIALIZE
10081

    
10082
  /**
10083
    Converts camelcased attributes to underscored when serializing.
10084

10085
    @method keyForAttribute
10086
    @param {String} attribute
10087
    @returns String
10088
  */
10089
  keyForAttribute: function(attr) {
10090
    return Ember.String.decamelize(attr);
10091
  },
10092

    
10093
  /**
10094
    Underscores relationship names and appends "_id" or "_ids" when serializing
10095
    relationship keys.
10096

10097
    @method keyForRelationship
10098
    @param {String} key
10099
    @param {String} kind
10100
    @returns String
10101
  */
10102
  keyForRelationship: function(key, kind) {
10103
    key = Ember.String.decamelize(key);
10104
    if (kind === "belongsTo") {
10105
      return key + "_id";
10106
    } else if (kind === "hasMany") {
10107
      return Ember.String.singularize(key) + "_ids";
10108
    } else {
10109
      return key;
10110
    }
10111
  },
10112

    
10113
  /**
10114
    Does not serialize hasMany relationships by default.
10115
  */
10116
  serializeHasMany: Ember.K,
10117

    
10118
  /**
10119
    Underscores the JSON root keys when serializing.
10120

10121
    @method serializeIntoHash
10122
    @param {Object} hash
10123
    @param {subclass of DS.Model} type
10124
    @param {DS.Model} record
10125
    @param {Object} options
10126
  */
10127
  serializeIntoHash: function(data, type, record, options) {
10128
    var root = Ember.String.decamelize(type.typeKey);
10129
    data[root] = this.serialize(record, options);
10130
  },
10131

    
10132
  /**
10133
    Serializes a polymorphic type as a fully capitalized model name.
10134

10135
    @method serializePolymorphicType
10136
    @param {DS.Model} record
10137
    @param {Object} json
10138
    @param relationship
10139
  */
10140
  serializePolymorphicType: function(record, json, relationship) {
10141
    var key = relationship.key,
10142
        belongsTo = get(record, key);
10143
    key = this.keyForAttribute(key);
10144
    json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
10145
  },
10146

    
10147
  // EXTRACT
10148

    
10149
  /**
10150
    Extracts the model typeKey from underscored root objects.
10151

10152
    @method typeForRoot
10153
    @param {String} root
10154
    @returns String the model's typeKey
10155
  */
10156
  typeForRoot: function(root) {
10157
    var camelized = Ember.String.camelize(root);
10158
    return Ember.String.singularize(camelized);
10159
  },
10160

    
10161
  /**
10162
    Add extra step to `DS.RESTSerializer.normalize` so links are
10163
    normalized.
10164

10165
    If your payload looks like this
10166

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

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

10188
    @method normalize
10189
    @param {subclass of DS.Model} type
10190
    @param {Object} hash
10191
    @param {String} prop
10192
    @returns Object
10193
  */
10194

    
10195
  normalize: function(type, hash, prop) {
10196
    this.normalizeLinks(hash);
10197

    
10198
    return this._super(type, hash, prop);
10199
  },
10200

    
10201
  /**
10202
    Convert `snake_cased` links  to `camelCase`
10203

10204
    @method normalizeLinks
10205
    @param {Object} hash
10206
  */
10207

    
10208
  normalizeLinks: function(data){
10209
    if (data.links) {
10210
      var links = data.links;
10211

    
10212
      for (var link in links) {
10213
        var camelizedLink = Ember.String.camelize(link);
10214

    
10215
        if (camelizedLink !== link) {
10216
          links[camelizedLink] = links[link];
10217
          delete links[link];
10218
        }
10219
      }
10220
    }
10221
  },
10222

    
10223
  /**
10224
    Normalize the polymorphic type from the JSON.
10225

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

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

10242
    @method normalizeRelationships
10243
    @private
10244
  */
10245
  normalizeRelationships: function(type, hash) {
10246
    var payloadKey, payload;
10247

    
10248
    if (this.keyForRelationship) {
10249
      type.eachRelationship(function(key, relationship) {
10250
        if (relationship.options.polymorphic) {
10251
          payloadKey = this.keyForAttribute(key);
10252
          payload = hash[payloadKey];
10253
          if (payload && payload.type) {
10254
            payload.type = this.typeForRoot(payload.type);
10255
          } else if (payload && relationship.kind === "hasMany") {
10256
            var self = this;
10257
            forEach(payload, function(single) {
10258
              single.type = self.typeForRoot(single.type);
10259
            });
10260
          }
10261
        } else {
10262
          payloadKey = this.keyForRelationship(key, relationship.kind);
10263
          payload = hash[payloadKey];
10264
        }
10265

    
10266
        hash[key] = payload;
10267

    
10268
        if (key !== payloadKey) {
10269
          delete hash[payloadKey];
10270
        }
10271
      }, this);
10272
    }
10273
  }
10274
});
10275

    
10276
})();
10277

    
10278

    
10279

    
10280
(function() {
10281
var get = Ember.get;
10282
var forEach = Ember.EnumerableUtils.forEach;
10283

    
10284
/**
10285
  The EmbeddedRecordsMixin allows you to add embedded record support to your
10286
  serializers.
10287
  To set up embedded records, you include the mixin into the serializer and then
10288
  define your embedded relations.
10289

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

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

10300
  @class EmbeddedRecordsMixin
10301
  @namespace DS
10302
*/
10303
DS.EmbeddedRecordsMixin = Ember.Mixin.create({
10304

    
10305
  /**
10306
    Serialize has-may relationship when it is configured as embedded objects.
10307

10308
    @method serializeHasMany
10309
  */
10310
  serializeHasMany: function(record, json, relationship) {
10311
    var key   = relationship.key,
10312
        attrs = get(this, 'attrs'),
10313
        embed = attrs && attrs[key] && attrs[key].embedded === 'always';
10314

    
10315
    if (embed) {
10316
      json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
10317
        var data = relation.serialize(),
10318
            primaryKey = get(this, 'primaryKey');
10319

    
10320
        data[primaryKey] = get(relation, primaryKey);
10321

    
10322
        return data;
10323
      }, this);
10324
    }
10325
  },
10326

    
10327
  /**
10328
    Extract embedded objects out of the payload for a single object
10329
    and add them as sideloaded objects instead.
10330

10331
    @method extractSingle
10332
  */
10333
  extractSingle: function(store, primaryType, payload, recordId, requestType) {
10334
    var root = this.keyForAttribute(primaryType.typeKey),
10335
        partial = payload[root];
10336

    
10337
    updatePayloadWithEmbedded(store, this, primaryType, partial, payload);
10338

    
10339
    return this._super(store, primaryType, payload, recordId, requestType);
10340
  },
10341

    
10342
  /**
10343
    Extract embedded objects out of a standard payload
10344
    and add them as sideloaded objects instead.
10345

10346
    @method extractArray
10347
  */
10348
  extractArray: function(store, type, payload) {
10349
    var root = this.keyForAttribute(type.typeKey),
10350
        partials = payload[Ember.String.pluralize(root)];
10351

    
10352
    forEach(partials, function(partial) {
10353
      updatePayloadWithEmbedded(store, this, type, partial, payload);
10354
    }, this);
10355

    
10356
    return this._super(store, type, payload);
10357
  }
10358
});
10359

    
10360
function updatePayloadWithEmbedded(store, serializer, type, partial, payload) {
10361
  var attrs = get(serializer, 'attrs');
10362

    
10363
  if (!attrs) {
10364
    return;
10365
  }
10366

    
10367
  type.eachRelationship(function(key, relationship) {
10368
    var expandedKey, embeddedTypeKey, attribute, ids,
10369
        config = attrs[key],
10370
        serializer = store.serializerFor(relationship.type.typeKey),
10371
        primaryKey = get(serializer, "primaryKey");
10372

    
10373
    if (relationship.kind !== "hasMany") {
10374
      return;
10375
    }
10376

    
10377
    if (config && (config.embedded === 'always' || config.embedded === 'load')) {
10378
      // underscore forces the embedded records to be side loaded.
10379
      // it is needed when main type === relationship.type
10380
      embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey);
10381
      expandedKey = this.keyForRelationship(key, relationship.kind);
10382
      attribute  = this.keyForAttribute(key);
10383
      ids = [];
10384

    
10385
      if (!partial[attribute]) {
10386
        return;
10387
      }
10388

    
10389
      payload[embeddedTypeKey] = payload[embeddedTypeKey] || [];
10390

    
10391
      forEach(partial[attribute], function(data) {
10392
        var embeddedType = store.modelFor(relationship.type.typeKey);
10393
        updatePayloadWithEmbedded(store, serializer, embeddedType, data, payload);
10394
        ids.push(data[primaryKey]);
10395
        payload[embeddedTypeKey].push(data);
10396
      });
10397

    
10398
      partial[expandedKey] = ids;
10399
      delete partial[attribute];
10400
    }
10401
  }, serializer);
10402
}
10403
})();
10404

    
10405

    
10406

    
10407
(function() {
10408
/**
10409
  @module ember-data
10410
*/
10411

    
10412
var forEach = Ember.EnumerableUtils.forEach;
10413

    
10414
/**
10415
  The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
10416
  with a JSON API that uses an underscored naming convention instead of camelcasing.
10417
  It has been designed to work out of the box with the
10418
  [active_model_serializers](http://github.com/rails-api/active_model_serializers)
10419
  Ruby gem.
10420

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

10425
  ## JSON Structure
10426

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

10430
  ### Conventional Names
10431

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

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

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

10445
  The JSON returned should look like this:
10446

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

10457
  @class ActiveModelAdapter
10458
  @constructor
10459
  @namespace DS
10460
  @extends DS.Adapter
10461
**/
10462

    
10463
DS.ActiveModelAdapter = DS.RESTAdapter.extend({
10464
  defaultSerializer: '_ams',
10465
  /**
10466
    The ActiveModelAdapter overrides the `pathForType` method to build
10467
    underscored URLs by decamelizing and pluralizing the object type name.
10468

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

10474
    @method pathForType
10475
    @param {String} type
10476
    @returns String
10477
  */
10478
  pathForType: function(type) {
10479
    var decamelized = Ember.String.decamelize(type);
10480
    return Ember.String.pluralize(decamelized);
10481
  },
10482

    
10483
  /**
10484
    The ActiveModelAdapter overrides the `ajaxError` method
10485
    to return a DS.InvalidError for all 422 Unprocessable Entity
10486
    responses.
10487

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

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

10495
    @method ajaxError
10496
    @param jqXHR
10497
    @returns error
10498
  */
10499
  ajaxError: function(jqXHR) {
10500
    var error = this._super(jqXHR);
10501

    
10502
    if (jqXHR && jqXHR.status === 422) {
10503
      var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"],
10504
          errors = {};
10505

    
10506
      forEach(Ember.keys(jsonErrors), function(key) {
10507
        errors[Ember.String.camelize(key)] = jsonErrors[key];
10508
      });
10509

    
10510
      return new DS.InvalidError(errors);
10511
    } else {
10512
      return error;
10513
    }
10514
  }
10515
});
10516

    
10517
})();
10518

    
10519

    
10520

    
10521
(function() {
10522

    
10523
})();
10524

    
10525

    
10526

    
10527
(function() {
10528
Ember.onLoad('Ember.Application', function(Application) {
10529
  Application.initializer({
10530
    name: "activeModelAdapter",
10531

    
10532
    initialize: function(container, application) {
10533
      application.register('serializer:_ams', DS.ActiveModelSerializer);
10534
      application.register('adapter:_ams', DS.ActiveModelAdapter);
10535
    }
10536
  });
10537
});
10538

    
10539
})();
10540

    
10541

    
10542

    
10543
(function() {
10544

    
10545
})();
10546

    
10547

    
10548
})();