rightnow_oms 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/CHANGELOG +8 -1
  2. data/README.md +10 -0
  3. data/app/assets/javascripts/rightnow_oms/app/app.js.coffee +2 -0
  4. data/app/assets/javascripts/rightnow_oms/app/controllers/cart.js.coffee +3 -3
  5. data/app/assets/javascripts/rightnow_oms/app/models/cart.js.coffee +14 -4
  6. data/app/assets/javascripts/rightnow_oms/app/models/cart_item.js.coffee +25 -15
  7. data/app/assets/javascripts/rightnow_oms/app/templates/cart_items/show.hjs +23 -9
  8. data/app/assets/javascripts/rightnow_oms/app/templates/cart_items/show_in_detail.hjs +45 -18
  9. data/app/assets/javascripts/rightnow_oms/app/templates/carts/show.hjs +0 -2
  10. data/app/assets/javascripts/rightnow_oms/app/templates/carts/show_in_detail.hjs +15 -23
  11. data/app/assets/javascripts/rightnow_oms/app/views/cart_items/show.js.coffee +5 -1
  12. data/app/assets/javascripts/rightnow_oms/app/views/cart_items/show_in_detail.js.coffee +9 -6
  13. data/app/assets/javascripts/rightnow_oms/app/views/carts/show_in_detail.js.coffee +1 -0
  14. data/app/assets/javascripts/rightnow_oms/vendor/ember-data.js +640 -236
  15. data/app/assets/javascripts/rightnow_oms/vendor/ember-data.min.js +1 -1
  16. data/app/assets/javascripts/rightnow_oms/vendor/ember.js +61 -37
  17. data/app/assets/javascripts/rightnow_oms/vendor/ember.min.js +2 -2
  18. data/app/assets/stylesheets/rightnow_oms/application.css.scss +17 -0
  19. data/app/assets/stylesheets/rightnow_oms/carts.css.scss +99 -72
  20. data/app/assets/stylesheets/rightnow_oms/orders.css.scss +50 -0
  21. data/app/controllers/rightnow_oms/cart_items_controller.rb +5 -5
  22. data/app/controllers/rightnow_oms/orders_controller.rb +30 -0
  23. data/app/models/rightnow_oms/cart.rb +22 -13
  24. data/app/models/rightnow_oms/cart_item.rb +20 -2
  25. data/app/models/rightnow_oms/order.rb +43 -0
  26. data/app/models/rightnow_oms/order_item.rb +16 -0
  27. data/app/views/rightnow_oms/carts/show.html.haml +2 -0
  28. data/app/views/rightnow_oms/orders/show.html.haml +45 -0
  29. data/config/routes.rb +1 -0
  30. data/db/migrate/20120214074943_create_rightnow_oms_orders.rb +20 -0
  31. data/db/migrate/20120214081113_create_rightnow_oms_order_items.rb +15 -0
  32. data/db/migrate/20120215064659_add_user_id_to_rightnow_oms_orders.rb +7 -0
  33. data/db/migrate/20120217081138_add_base_quantity_to_rightnow_oms_cart_items.rb +6 -0
  34. data/lib/rightnow_oms/order_no_generator.rb +13 -0
  35. data/lib/rightnow_oms/version.rb +1 -1
  36. data/lib/rightnow_oms.rb +19 -5
  37. metadata +41 -20
  38. data/app/assets/stylesheets/rightnow_oms/application.css +0 -7
@@ -71,7 +71,7 @@ DS.fixtureAdapter = DS.Adapter.create({
71
71
 
72
72
  ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
73
73
 
74
- var ids = fixtures.map(function(item, index, self){ return item.id });
74
+ var ids = fixtures.map(function(item, index, self){ return item.id; });
75
75
  store.loadMany(type, ids, fixtures);
76
76
  }
77
77
 
@@ -81,6 +81,7 @@ DS.fixtureAdapter = DS.Adapter.create({
81
81
 
82
82
 
83
83
  (function(exports) {
84
+ /*global jQuery*/
84
85
  var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
85
86
 
86
87
  DS.RESTAdapter = DS.Adapter.extend({
@@ -120,8 +121,7 @@ DS.RESTAdapter = DS.Adapter.extend({
120
121
  },
121
122
 
122
123
  updateRecord: function(store, type, model) {
123
- var primaryKey = getPath(type, 'proto.primaryKey'),
124
- id = get(model, primaryKey);
124
+ var id = get(model, 'id');
125
125
  var root = this.rootForType(type);
126
126
 
127
127
  var data = {};
@@ -159,8 +159,7 @@ DS.RESTAdapter = DS.Adapter.extend({
159
159
  },
160
160
 
161
161
  deleteRecord: function(store, type, model) {
162
- var primaryKey = getPath(type, 'proto.primaryKey'),
163
- id = get(model, primaryKey);
162
+ var id = get(model, 'id');
164
163
  var root = this.rootForType(type);
165
164
 
166
165
  var url = ["", this.pluralize(root), id].join("/");
@@ -178,12 +177,11 @@ DS.RESTAdapter = DS.Adapter.extend({
178
177
  }
179
178
 
180
179
  var root = this.rootForType(type),
181
- plural = this.pluralize(root),
182
- primaryKey = getPath(type, 'proto.primaryKey');
180
+ plural = this.pluralize(root);
183
181
 
184
182
  var data = {};
185
183
  data[plural] = models.map(function(model) {
186
- return get(model, primaryKey);
184
+ return get(model, 'id');
187
185
  });
188
186
 
189
187
  this.ajax("/" + this.pluralize(root) + "/delete", "POST", {
@@ -215,7 +213,6 @@ DS.RESTAdapter = DS.Adapter.extend({
215
213
  store.loadMany(type, ids, json[plural]);
216
214
  }
217
215
  });
218
- var url = "/" + plural;
219
216
  },
220
217
 
221
218
  findAll: function(store, type) {
@@ -274,19 +271,58 @@ DS.RESTAdapter = DS.Adapter.extend({
274
271
  (function(exports) {
275
272
  var get = Ember.get, set = Ember.set;
276
273
 
274
+ /**
275
+ A model array is an array that contains records of a certain type. The model
276
+ array materializes records as needed when they are retrieved for the first
277
+ time. You should not create model arrays yourself. Instead, an instance of
278
+ DS.ModelArray or its subclasses will be returned by your application's store
279
+ in response to queries.
280
+ */
281
+
277
282
  DS.ModelArray = Ember.ArrayProxy.extend({
283
+
284
+ /**
285
+ The model type contained by this model array.
286
+
287
+ @type DS.Model
288
+ */
278
289
  type: null,
290
+
291
+ // The array of client ids backing the model array. When a
292
+ // record is requested from the model array, the record
293
+ // for the client id at the same index is materialized, if
294
+ // necessary, by the store.
279
295
  content: null,
296
+
297
+ // The store that created this model array.
280
298
  store: null,
281
299
 
300
+ // for associations, the model that this association belongs to.
301
+ parentModel: null,
302
+
282
303
  init: function() {
283
304
  set(this, 'modelCache', Ember.A([]));
284
305
  this._super();
285
306
  },
286
307
 
308
+ // Overrides Ember.Array's replace method to implement
309
+ replace: function(index, removed, added) {
310
+ var parentRecord = get(this, 'parentRecord');
311
+ var pendingParent = parentRecord && !get(parentRecord, 'id');
312
+
313
+ added = added.map(function(item) {
314
+ ember_assert("You can only add items of " + (get(this, 'type') && get(this, 'type').toString()) + " to this association.", !get(this, 'type') || (get(this, 'type') === item.constructor));
315
+
316
+ if (pendingParent) { item.send('waitingOn', parentRecord); }
317
+ return item.get('clientId');
318
+ });
319
+
320
+ this._super(index, removed, added);
321
+ },
322
+
287
323
  arrayDidChange: function(array, index, removed, added) {
288
324
  var modelCache = get(this, 'modelCache');
289
- modelCache.replace(index, 0, Array(added));
325
+ modelCache.replace(index, 0, new Array(added));
290
326
 
291
327
  this._super(array, index, removed, added);
292
328
  },
@@ -318,6 +354,12 @@ DS.ModelArray = Ember.ArrayProxy.extend({
318
354
  }
319
355
  });
320
356
 
357
+ })({});
358
+
359
+
360
+ (function(exports) {
361
+ var get = Ember.get;
362
+
321
363
  DS.FilteredModelArray = DS.ModelArray.extend({
322
364
  filterFunction: null,
323
365
 
@@ -327,6 +369,12 @@ DS.FilteredModelArray = DS.ModelArray.extend({
327
369
  }, 'filterFunction')
328
370
  });
329
371
 
372
+ })({});
373
+
374
+
375
+ (function(exports) {
376
+ var get = Ember.get, set = Ember.set;
377
+
330
378
  DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
331
379
  query: null,
332
380
  isLoaded: false,
@@ -343,6 +391,11 @@ DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
343
391
  }
344
392
  });
345
393
 
394
+
395
+ })({});
396
+
397
+
398
+ (function(exports) {
346
399
  })({});
347
400
 
348
401
 
@@ -384,7 +437,8 @@ var OrderedSet = Ember.Object.extend({
384
437
  },
385
438
 
386
439
  forEach: function(fn, self) {
387
- get(this, 'list').forEach(function(item) {
440
+ // allow mutation during iteration
441
+ get(this, 'list').slice().forEach(function(item) {
388
442
  fn.call(self, item);
389
443
  });
390
444
  },
@@ -447,7 +501,8 @@ var Hash = Ember.Object.extend({
447
501
  },
448
502
 
449
503
  forEach: function(fn, binding) {
450
- var keys = get(this, 'keys'), values = get(this, 'values');
504
+ var keys = get(this, 'keys'),
505
+ values = get(this, 'values');
451
506
 
452
507
  keys.forEach(function(key) {
453
508
  var guid = Ember.guidFor(key);
@@ -507,8 +562,17 @@ DS.Transaction = Ember.Object.extend({
507
562
  dirty.forEach(function(type, models) {
508
563
  if (models.isEmpty()) { return; }
509
564
 
510
- models.forEach(function(model) { model.willCommit(); });
511
- fn.call(binding, type, models.toArray());
565
+ var array = [];
566
+
567
+ models.forEach(function(model) {
568
+ model.send('willCommit');
569
+
570
+ if (get(model, 'isPending') === false) {
571
+ array.push(model);
572
+ }
573
+ });
574
+
575
+ fn.call(binding, type, array);
512
576
  });
513
577
  };
514
578
 
@@ -680,28 +744,27 @@ DS.Store = Ember.Object.extend({
680
744
  // . CREATE NEW MODEL .
681
745
  // ....................
682
746
 
683
- createRecord: function(type, hash, transaction) {
684
- hash = hash || {};
747
+ createRecord: function(type, properties, transaction) {
748
+ properties = properties || {};
685
749
 
686
- var id = hash[getPath(type, 'proto.primaryKey')] || null;
750
+ var id = properties[getPath(type, 'proto.primaryKey')] || null;
687
751
 
688
- var model = type.create({
689
- data: hash || {},
752
+ var model = type._create({
690
753
  store: this,
691
754
  transaction: transaction
692
755
  });
693
756
 
694
- model.adapterDidCreate();
757
+ var hash = {}, clientId;
695
758
 
696
- var data = this.clientIdToHashMap(type);
697
- var models = get(this, 'models');
759
+ clientId = this.pushHash(hash, id, type);
760
+ model.send('setData', hash);
698
761
 
699
- var clientId = this.pushHash(hash, id, type);
762
+ var models = get(this, 'models');
700
763
 
701
764
  set(model, 'clientId', clientId);
702
-
703
765
  models[clientId] = model;
704
766
 
767
+ model.setProperties(properties);
705
768
  this.updateModelArrays(type, clientId, hash);
706
769
 
707
770
  return model;
@@ -712,7 +775,7 @@ DS.Store = Ember.Object.extend({
712
775
  // ................
713
776
 
714
777
  deleteRecord: function(model) {
715
- model.deleteRecord();
778
+ model.send('deleteRecord');
716
779
  },
717
780
 
718
781
  // ...............
@@ -739,7 +802,7 @@ DS.Store = Ember.Object.extend({
739
802
  */
740
803
  find: function(type, id, query) {
741
804
  if (id === undefined) {
742
- return this.findMany(type, null, null);
805
+ return this.findAll(type);
743
806
  }
744
807
 
745
808
  if (query !== undefined) {
@@ -774,17 +837,17 @@ DS.Store = Ember.Object.extend({
774
837
  if (!model) {
775
838
  // create a new instance of the model in the
776
839
  // 'isLoading' state
777
- model = this.createModel(type, clientId);
840
+ model = this.materializeRecord(type, clientId);
778
841
 
779
842
  // immediately set its data
780
- model.setData(data[clientId] || null);
843
+ model.send('setData', data[clientId] || null);
781
844
  }
782
845
  } else {
783
846
  clientId = this.pushHash(null, id, type);
784
847
 
785
848
  // create a new instance of the model in the
786
849
  // 'isLoading' state
787
- model = this.createModel(type, clientId);
850
+ model = this.materializeRecord(type, clientId);
788
851
 
789
852
  // let the adapter set the data, possibly async
790
853
  var adapter = get(this, '_adapter');
@@ -898,20 +961,20 @@ DS.Store = Ember.Object.extend({
898
961
  var data = this.clientIdToHashMap(model.constructor);
899
962
 
900
963
  data[clientId] = hash;
901
- model.set('data', hash);
964
+ model.send('setData', hash);
902
965
  }
903
966
 
904
- model.adapterDidUpdate();
967
+ model.send('didCommit');
905
968
  },
906
969
 
907
970
  didDeleteRecords: function(array) {
908
971
  array.forEach(function(model) {
909
- model.adapterDidDelete();
972
+ model.send('didCommit');
910
973
  });
911
974
  },
912
975
 
913
976
  didDeleteRecord: function(model) {
914
- model.adapterDidDelete();
977
+ model.send('didCommit');
915
978
  },
916
979
 
917
980
  didCreateRecords: function(type, array, hashes) {
@@ -927,38 +990,55 @@ DS.Store = Ember.Object.extend({
927
990
  clientId = get(model, 'clientId');
928
991
 
929
992
  data[clientId] = hash;
930
- set(model, 'data', hash);
993
+ model.send('setData', hash);
931
994
 
932
995
  idToClientIdMap[id] = clientId;
933
996
  idList.push(id);
934
997
 
935
- model.adapterDidUpdate();
998
+ model.send('didCommit');
936
999
  }
937
1000
  },
938
1001
 
939
1002
  didCreateRecord: function(model, hash) {
940
1003
  var type = model.constructor;
941
1004
 
942
- var id, clientId, primaryKey = getPath(type, 'proto.primaryKey');
1005
+ var id, clientId, primaryKey;
943
1006
 
944
1007
  var idToClientIdMap = this.idToClientIdMap(type);
945
1008
  var data = this.clientIdToHashMap(type);
946
1009
  var idList = this.idList(type);
947
1010
 
948
- id = hash[primaryKey];
1011
+ // The hash is optional, but if it is not provided, the client must have
1012
+ // provided a primary key.
1013
+
1014
+ primaryKey = getPath(type, 'proto.primaryKey');
1015
+
1016
+ // TODO: Make ember_assert more flexible and convert this into an ember_assert
1017
+ if (hash) {
1018
+ ember_assert("The server must provide a primary key: " + primaryKey, get(hash, primaryKey));
1019
+ } else {
1020
+ ember_assert("The server did not return data, and you did not create a primary key (" + primaryKey + ") on the client", get(get(model, 'data'), primaryKey));
1021
+ }
1022
+
1023
+ // If a hash was provided, index it under the model's client ID
1024
+ // and update the model.
1025
+ if (arguments.length === 2) {
1026
+ id = hash[primaryKey];
1027
+
1028
+ data[clientId] = hash;
1029
+ set(model, 'data', hash);
1030
+ }
949
1031
 
950
1032
  clientId = get(model, 'clientId');
951
- data[clientId] = hash;
952
- set(model, 'data', hash);
953
1033
 
954
1034
  idToClientIdMap[id] = clientId;
955
1035
  idList.push(id);
956
1036
 
957
- model.adapterDidUpdate();
1037
+ model.send('didCommit');
958
1038
  },
959
1039
 
960
1040
  recordWasInvalid: function(record, errors) {
961
- record.wasInvalid(errors);
1041
+ record.send('becameInvalid', errors);
962
1042
  },
963
1043
 
964
1044
  // ................
@@ -967,7 +1047,6 @@ DS.Store = Ember.Object.extend({
967
1047
 
968
1048
  registerModelArray: function(array, type, filter) {
969
1049
  var modelArrays = get(this, 'modelArrays');
970
- var idToClientIdMap = this.idToClientIdMap(type);
971
1050
 
972
1051
  modelArrays.push(array);
973
1052
 
@@ -987,7 +1066,7 @@ DS.Store = Ember.Object.extend({
987
1066
 
988
1067
  updateModelArrayFilter: function(array, type, filter) {
989
1068
  var data = this.clientIdToHashMap(type);
990
- var allClientIds = this.clientIdList(type);
1069
+ var allClientIds = this.clientIdList(type), clientId, hash;
991
1070
 
992
1071
  for (var i=0, l=allClientIds.length; i<l; i++) {
993
1072
  clientId = allClientIds[i];
@@ -1001,11 +1080,12 @@ DS.Store = Ember.Object.extend({
1001
1080
  },
1002
1081
 
1003
1082
  updateModelArrays: function(type, clientId, hash) {
1004
- var modelArrays = get(this, 'modelArrays');
1083
+ var modelArrays = get(this, 'modelArrays'),
1084
+ modelArrayType, filter;
1005
1085
 
1006
1086
  modelArrays.forEach(function(array) {
1007
- modelArrayType = get(array, 'type');
1008
- filter = get(array, 'filterFunction');
1087
+ modelArrayType = get(array, 'type');
1088
+ filter = get(array, 'filterFunction');
1009
1089
 
1010
1090
  if (type !== modelArrayType) { return; }
1011
1091
 
@@ -1138,8 +1218,7 @@ DS.Store = Ember.Object.extend({
1138
1218
 
1139
1219
  var model = models[clientId];
1140
1220
  if (model) {
1141
- model.willLoadData();
1142
- model.setData(hash);
1221
+ model.send('setData', hash);
1143
1222
  }
1144
1223
  } else {
1145
1224
  clientId = this.pushHash(hash, id, type);
@@ -1208,13 +1287,21 @@ DS.Store = Ember.Object.extend({
1208
1287
  // . MODEL MATERIALIZATION .
1209
1288
  // .........................
1210
1289
 
1211
- createModel: function(type, clientId) {
1290
+ materializeRecord: function(type, clientId) {
1212
1291
  var model;
1213
1292
 
1214
- get(this, 'models')[clientId] = model = type.create({ store: this, clientId: clientId });
1293
+ get(this, 'models')[clientId] = model = type._create({ store: this, clientId: clientId });
1215
1294
  set(model, 'clientId', clientId);
1216
- model.loadingData();
1295
+ model.send('loadingData');
1217
1296
  return model;
1297
+ },
1298
+
1299
+ destroy: function() {
1300
+ if (get(DS, 'defaultStore') === this) {
1301
+ set(DS, 'defaultStore', null);
1302
+ }
1303
+
1304
+ return this._super();
1218
1305
  }
1219
1306
  });
1220
1307
 
@@ -1223,7 +1310,7 @@ DS.Store = Ember.Object.extend({
1223
1310
 
1224
1311
 
1225
1312
  (function(exports) {
1226
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
1313
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath, guidFor = Ember.guidFor;
1227
1314
 
1228
1315
  var stateProperty = Ember.computed(function(key) {
1229
1316
  var parent = get(this, 'parentState');
@@ -1232,6 +1319,14 @@ var stateProperty = Ember.computed(function(key) {
1232
1319
  }
1233
1320
  }).property();
1234
1321
 
1322
+ var isEmptyObject = function(object) {
1323
+ for (var name in object) {
1324
+ if (object.hasOwnProperty(name)) { return false; }
1325
+ }
1326
+
1327
+ return true;
1328
+ };
1329
+
1235
1330
  DS.State = Ember.State.extend({
1236
1331
  isLoaded: stateProperty,
1237
1332
  isDirty: stateProperty,
@@ -1239,14 +1334,16 @@ DS.State = Ember.State.extend({
1239
1334
  isDeleted: stateProperty,
1240
1335
  isError: stateProperty,
1241
1336
  isNew: stateProperty,
1242
- isValid: stateProperty
1337
+ isValid: stateProperty,
1338
+ isPending: stateProperty,
1339
+
1340
+ // For states that are substates of a
1341
+ // DirtyState (updated or created), it is
1342
+ // useful to be able to determine which
1343
+ // type of dirty state it is.
1344
+ dirtyType: stateProperty
1243
1345
  });
1244
1346
 
1245
- var cantLoadData = function() {
1246
- // TODO: get the current state name
1247
- throw "You cannot load data into the store when its associated model is in its current state";
1248
- };
1249
-
1250
1347
  var isEmptyObject = function(obj) {
1251
1348
  for (var prop in obj) {
1252
1349
  if (!obj.hasOwnProperty(prop)) { continue; }
@@ -1259,89 +1356,287 @@ var isEmptyObject = function(obj) {
1259
1356
  var setProperty = function(manager, context) {
1260
1357
  var key = context.key, value = context.value;
1261
1358
 
1262
- var model = get(manager, 'model'), type = model.constructor;
1263
- var store = get(model, 'store');
1264
- var data = get(model, 'data');
1359
+ var model = get(manager, 'model'),
1360
+ data = get(model, 'data');
1265
1361
 
1266
1362
  data[key] = value;
1267
1363
 
1268
- if (store) { store.hashWasUpdated(type, get(model, 'clientId')); }
1364
+ // At the end of the run loop, notify model arrays that
1365
+ // this record has changed so they can re-evaluate its contents
1366
+ // to determine membership.
1367
+ Ember.run.once(model, model.notifyHashWasUpdated);
1269
1368
  };
1270
1369
 
1271
- // several states share extremely common functionality, so we are factoring
1272
- // them out into a common class.
1370
+ // The waitingOn event shares common functionality
1371
+ // between the different dirty states, but each is
1372
+ // treated slightly differently. This method is exposed
1373
+ // so that each implementation can invoke the common
1374
+ // behavior, and then implement the behavior specific
1375
+ // to the state.
1376
+ var waitingOn = function(manager, object) {
1377
+ var model = get(manager, 'model'),
1378
+ pendingQueue = get(model, 'pendingQueue'),
1379
+ objectGuid = guidFor(object);
1380
+
1381
+ var observer = function() {
1382
+ if (get(object, 'id')) {
1383
+ manager.send('doneWaitingOn', object);
1384
+ Ember.removeObserver(object, 'id', observer);
1385
+ }
1386
+ };
1387
+
1388
+ pendingQueue[objectGuid] = [object, observer];
1389
+ Ember.addObserver(object, 'id', observer);
1390
+ };
1391
+
1392
+ // Implementation notes:
1393
+ //
1394
+ // Each state has a boolean value for all of the following flags:
1395
+ //
1396
+ // * isLoaded: The record has a populated `data` property. When a
1397
+ // record is loaded via `store.find`, `isLoaded` is false
1398
+ // until the adapter sets it. When a record is created locally,
1399
+ // its `isLoaded` property is always true.
1400
+ // * isDirty: The record has local changes that have not yet been
1401
+ // saved by the adapter. This includes records that have been
1402
+ // created (but not yet saved) or deleted.
1403
+ // * isSaving: The record's transaction has been committed, but
1404
+ // the adapter has not yet acknowledged that the changes have
1405
+ // been persisted to the backend.
1406
+ // * isDeleted: The record was marked for deletion. When `isDeleted`
1407
+ // is true and `isDirty` is true, the record is deleted locally
1408
+ // but the deletion was not yet persisted. When `isSaving` is
1409
+ // true, the change is in-flight. When both `isDirty` and
1410
+ // `isSaving` are false, the change has persisted.
1411
+ // * isError: The adapter reported that it was unable to save
1412
+ // local changes to the backend. This may also result in the
1413
+ // record having its `isValid` property become false if the
1414
+ // adapter reported that server-side validations failed.
1415
+ // * isNew: The record was created on the client and the adapter
1416
+ // did not yet report that it was successfully saved.
1417
+ // * isValid: No client-side validations have failed and the
1418
+ // adapter did not report any server-side validation failures.
1419
+ // * isPending: A record `isPending` when it belongs to an
1420
+ // association on another record and that record has not been
1421
+ // saved. A record in this state cannot be saved because it
1422
+ // lacks a "foreign key" that will be supplied by its parent
1423
+ // association when the parent record has been created. When
1424
+ // the adapter reports that the parent has saved, the
1425
+ // `isPending` property on all children will become `false`
1426
+ // and the transaction will try to commit the records.
1427
+
1428
+
1429
+ // The dirty state is a abstract state whose functionality is
1430
+ // shared between the `created` and `updated` states.
1431
+ //
1432
+ // The deleted state shares the `isDirty` flag with the
1433
+ // subclasses of `DirtyState`, but with a very different
1434
+ // implementation.
1273
1435
  var DirtyState = DS.State.extend({
1274
- // these states are virtually identical except that
1275
- // they (thrice) use their states name explicitly.
1276
- //
1277
- // child classes implement stateName.
1278
- stateName: null,
1436
+ initialState: 'uncommitted',
1437
+
1438
+ // FLAGS
1279
1439
  isDirty: true,
1280
- willLoadData: cantLoadData,
1281
1440
 
1282
- enter: function(manager) {
1283
- var stateName = get(this, 'stateName'),
1284
- model = get(manager, 'model');
1441
+ // SUBSTATES
1285
1442
 
1286
- model.withTransaction(function (t) {
1287
- t.modelBecameDirty(stateName, model);
1288
- });
1289
- },
1443
+ // When a record first becomes dirty, it is `uncommitted`.
1444
+ // This means that there are local pending changes,
1445
+ // but they have not yet begun to be saved.
1446
+ uncommitted: DS.State.extend({
1447
+ // TRANSITIONS
1448
+ enter: function(manager) {
1449
+ var dirtyType = get(this, 'dirtyType'),
1450
+ model = get(manager, 'model');
1290
1451
 
1291
- exit: function(manager) {
1292
- var stateName = get(this, 'stateName'),
1293
- model = get(manager, 'model');
1452
+ model.withTransaction(function (t) {
1453
+ t.modelBecameDirty(dirtyType, model);
1454
+ });
1455
+ },
1294
1456
 
1295
- this.notifyModel(model);
1457
+ exit: function(manager) {
1458
+ var model = get(manager, 'model');
1459
+ manager.send('invokeLifecycleCallbacks', model);
1460
+ },
1296
1461
 
1297
- model.withTransaction(function (t) {
1298
- t.modelBecameClean(stateName, model);
1299
- });
1300
- },
1462
+ // EVENTS
1463
+ setProperty: setProperty,
1301
1464
 
1302
- setProperty: setProperty,
1465
+ deleteRecord: function(manager) {
1466
+ manager.goToState('deleted');
1467
+ },
1303
1468
 
1304
- willCommit: function(manager) {
1305
- manager.goToState('saving');
1306
- },
1469
+ waitingOn: function(manager, object) {
1470
+ waitingOn(manager, object);
1471
+ manager.goToState('pending');
1472
+ },
1307
1473
 
1308
- saving: DS.State.extend({
1474
+ willCommit: function(manager) {
1475
+ manager.goToState('inFlight');
1476
+ }
1477
+ }),
1478
+
1479
+ // Once a record has been handed off to the adapter to be
1480
+ // saved, it is in the 'in flight' state. Changes to the
1481
+ // record cannot be made during this window.
1482
+ inFlight: DS.State.extend({
1483
+ // FLAGS
1309
1484
  isSaving: true,
1310
1485
 
1311
- didUpdate: function(manager) {
1486
+ // TRANSITIONS
1487
+ enter: function(manager) {
1488
+ var dirtyType = get(this, 'dirtyType'),
1489
+ model = get(manager, 'model');
1490
+
1491
+ model.withTransaction(function (t) {
1492
+ t.modelBecameClean(dirtyType, model);
1493
+ });
1494
+ },
1495
+
1496
+ // EVENTS
1497
+ didCommit: function(manager) {
1312
1498
  manager.goToState('loaded');
1313
1499
  },
1314
1500
 
1315
- wasInvalid: function(manager, errors) {
1501
+ becameInvalid: function(manager, errors) {
1316
1502
  var model = get(manager, 'model');
1317
1503
 
1318
1504
  set(model, 'errors', errors);
1319
1505
  manager.goToState('invalid');
1506
+ },
1507
+
1508
+ setData: function(manager, hash) {
1509
+ var model = get(manager, 'model');
1510
+ set(model, 'data', hash);
1320
1511
  }
1321
1512
  }),
1322
1513
 
1514
+ // If a record becomes associated with a newly created
1515
+ // parent record, it will be `pending` until the parent
1516
+ // record has successfully persisted. Once this happens,
1517
+ // this record can use the parent's primary key as its
1518
+ // foreign key.
1519
+ //
1520
+ // If the record's transaction had already started to
1521
+ // commit, the record will transition to the `inFlight`
1522
+ // state. If it had not, the record will transition to
1523
+ // the `uncommitted` state.
1524
+ pending: DS.State.extend({
1525
+ initialState: 'uncommitted',
1526
+
1527
+ // FLAGS
1528
+ isPending: true,
1529
+
1530
+ // SUBSTATES
1531
+
1532
+ // A pending record whose transaction has not yet
1533
+ // started to commit is in this state.
1534
+ uncommitted: DS.State.extend({
1535
+ // EVENTS
1536
+ setProperty: setProperty,
1537
+
1538
+ deleteRecord: function(manager) {
1539
+ var model = get(manager, 'model'),
1540
+ pendingQueue = get(model, 'pendingQueue'),
1541
+ tuple;
1542
+
1543
+ // since we are leaving the pending state, remove any
1544
+ // observers we have registered on other records.
1545
+ for (var prop in pendingQueue) {
1546
+ if (!pendingQueue.hasOwnProperty(prop)) { continue; }
1547
+
1548
+ tuple = pendingQueue[prop];
1549
+ Ember.removeObserver(tuple[0], 'id', tuple[1]);
1550
+ }
1551
+
1552
+ manager.goToState('deleted');
1553
+ },
1554
+
1555
+ willCommit: function(manager) {
1556
+ manager.goToState('committing');
1557
+ },
1558
+
1559
+ doneWaitingOn: function(manager, object) {
1560
+ var model = get(manager, 'model'),
1561
+ pendingQueue = get(model, 'pendingQueue'),
1562
+ objectGuid = guidFor(object);
1563
+
1564
+ delete pendingQueue[objectGuid];
1565
+
1566
+ if (isEmptyObject(pendingQueue)) {
1567
+ manager.send('doneWaiting');
1568
+ }
1569
+ },
1570
+
1571
+ doneWaiting: function(manager) {
1572
+ var dirtyType = get(this, 'dirtyType');
1573
+ manager.goToState(dirtyType + '.uncommitted');
1574
+ }
1575
+ }),
1576
+
1577
+ // A pending record whose transaction has started
1578
+ // to commit is in this state. Since it has not yet
1579
+ // been sent to the adapter, it is not `inFlight`
1580
+ // until all of its dependencies have been committed.
1581
+ committing: DS.State.extend({
1582
+ // FLAGS
1583
+ isSaving: true,
1584
+
1585
+ // EVENTS
1586
+ doneWaitingOn: function(manager, object) {
1587
+ var model = get(manager, 'model'),
1588
+ pendingQueue = get(model, 'pendingQueue'),
1589
+ objectGuid = guidFor(object);
1590
+
1591
+ delete pendingQueue[objectGuid];
1592
+
1593
+ if (isEmptyObject(pendingQueue)) {
1594
+ manager.send('doneWaiting');
1595
+ }
1596
+ },
1597
+
1598
+ doneWaiting: function(manager) {
1599
+ var dirtyType = get(this, 'dirtyType');
1600
+ manager.goToState(dirtyType + '.inFlight');
1601
+ }
1602
+ })
1603
+ }),
1604
+
1605
+ // A record is in the `invalid` state when its client-side
1606
+ // invalidations have failed, or if the adapter has indicated
1607
+ // the the record failed server-side invalidations.
1323
1608
  invalid: DS.State.extend({
1609
+ // FLAGS
1324
1610
  isValid: false,
1325
1611
 
1612
+ // EVENTS
1613
+ deleteRecord: function(manager) {
1614
+ manager.goToState('deleted');
1615
+ },
1616
+
1326
1617
  setProperty: function(manager, context) {
1327
1618
  setProperty(manager, context);
1328
1619
 
1329
- var stateName = getPath(this, 'parentState.stateName'),
1330
- model = get(manager, 'model'),
1620
+ var model = get(manager, 'model'),
1331
1621
  errors = get(model, 'errors'),
1332
1622
  key = context.key;
1333
1623
 
1334
1624
  delete errors[key];
1335
1625
 
1336
1626
  if (isEmptyObject(errors)) {
1337
- manager.goToState(stateName);
1627
+ manager.send('becameValid');
1338
1628
  }
1629
+ },
1630
+
1631
+ becameValid: function(manager) {
1632
+ manager.goToState('uncommitted');
1339
1633
  }
1340
1634
  })
1341
1635
  });
1342
1636
 
1343
1637
  var states = {
1344
1638
  rootState: Ember.State.create({
1639
+ // FLAGS
1345
1640
  isLoaded: false,
1346
1641
  isDirty: false,
1347
1642
  isSaving: false,
@@ -1349,80 +1644,126 @@ var states = {
1349
1644
  isError: false,
1350
1645
  isNew: false,
1351
1646
  isValid: true,
1647
+ isPending: false,
1352
1648
 
1353
- willLoadData: cantLoadData,
1354
-
1355
- didCreate: function(manager) {
1356
- manager.goToState('loaded.created');
1357
- },
1649
+ // SUBSTATES
1358
1650
 
1651
+ // A record begins its lifecycle in the `empty` state.
1652
+ // If its data will come from the adapter, it will
1653
+ // transition into the `loading` state. Otherwise, if
1654
+ // the record is being created on the client, it will
1655
+ // transition into the `created` state.
1359
1656
  empty: DS.State.create({
1657
+ // EVENTS
1360
1658
  loadingData: function(manager) {
1361
1659
  manager.goToState('loading');
1660
+ },
1661
+
1662
+ setData: function(manager, hash) {
1663
+ var model = get(manager, 'model');
1664
+ set(model, 'data', hash);
1665
+ manager.goToState('loaded.created');
1362
1666
  }
1363
1667
  }),
1364
1668
 
1669
+ // A record enters this state when the store askes
1670
+ // the adapter for its data. It remains in this state
1671
+ // until the adapter provides the requested data.
1672
+ //
1673
+ // Usually, this process is asynchronous, using an
1674
+ // XHR to retrieve the data.
1365
1675
  loading: DS.State.create({
1366
- willLoadData: Ember.K,
1367
-
1676
+ // TRANSITIONS
1368
1677
  exit: function(manager) {
1369
1678
  var model = get(manager, 'model');
1370
1679
  model.didLoad();
1371
1680
  },
1372
1681
 
1682
+ // EVENTS
1373
1683
  setData: function(manager, data) {
1374
1684
  var model = get(manager, 'model');
1375
1685
 
1376
1686
  model.beginPropertyChanges();
1377
- model.set('data', data);
1687
+ set(model, 'data', data);
1378
1688
 
1379
1689
  if (data !== null) {
1380
- manager.goToState('loaded');
1690
+ manager.send('loadedData');
1381
1691
  }
1382
1692
 
1383
1693
  model.endPropertyChanges();
1694
+ },
1695
+
1696
+ loadedData: function(manager) {
1697
+ manager.goToState('loaded');
1384
1698
  }
1385
1699
  }),
1386
1700
 
1701
+ // A record enters this state when its data is populated.
1702
+ // Most of a record's lifecycle is spent inside substates
1703
+ // of the `loaded` state.
1387
1704
  loaded: DS.State.create({
1705
+ initialState: 'saved',
1706
+
1707
+ // FLAGS
1388
1708
  isLoaded: true,
1389
1709
 
1390
- willLoadData: Ember.K,
1710
+ // SUBSTATES
1391
1711
 
1392
- setProperty: function(manager, context) {
1393
- setProperty(manager, context);
1394
- manager.goToState('updated');
1395
- },
1712
+ // If there are no local changes to a record, it remains
1713
+ // in the `saved` state.
1714
+ saved: DS.State.create({
1715
+ // EVENTS
1716
+ setProperty: function(manager, context) {
1717
+ setProperty(manager, context);
1718
+ manager.goToState('updated');
1719
+ },
1396
1720
 
1397
- 'delete': function(manager) {
1398
- manager.goToState('deleted');
1399
- },
1721
+ deleteRecord: function(manager) {
1722
+ manager.goToState('deleted');
1723
+ },
1400
1724
 
1725
+ waitingOn: function(manager, object) {
1726
+ waitingOn(manager, object);
1727
+ manager.goToState('updated.pending');
1728
+ }
1729
+ }),
1730
+
1731
+ // A record is in this state after it has been locally
1732
+ // created but before the adapter has indicated that
1733
+ // it has been saved.
1401
1734
  created: DirtyState.create({
1402
- stateName: 'created',
1735
+ dirtyType: 'created',
1736
+
1737
+ // FLAGS
1403
1738
  isNew: true,
1404
1739
 
1405
- notifyModel: function(model) {
1740
+ // EVENTS
1741
+ invokeLifecycleCallbacks: function(manager, model) {
1406
1742
  model.didCreate();
1407
1743
  }
1408
1744
  }),
1409
1745
 
1746
+ // A record is in this state if it has already been
1747
+ // saved to the server, but there are new local changes
1748
+ // that have not yet been saved.
1410
1749
  updated: DirtyState.create({
1411
- stateName: 'updated',
1750
+ dirtyType: 'updated',
1412
1751
 
1413
- notifyModel: function(model) {
1752
+ // EVENTS
1753
+ invokeLifecycleCallbacks: function(manager, model) {
1414
1754
  model.didUpdate();
1415
1755
  }
1416
1756
  })
1417
1757
  }),
1418
1758
 
1759
+ // A record is in this state if it was deleted from the store.
1419
1760
  deleted: DS.State.create({
1761
+ // FLAGS
1420
1762
  isDeleted: true,
1421
1763
  isLoaded: true,
1422
1764
  isDirty: true,
1423
1765
 
1424
- willLoadData: cantLoadData,
1425
-
1766
+ // TRANSITIONS
1426
1767
  enter: function(manager) {
1427
1768
  var model = get(manager, 'model');
1428
1769
  var store = get(model, 'store');
@@ -1436,31 +1777,51 @@ var states = {
1436
1777
  });
1437
1778
  },
1438
1779
 
1439
- willCommit: function(manager) {
1440
- manager.goToState('saving');
1441
- },
1780
+ // SUBSTATES
1442
1781
 
1443
- saving: DS.State.create({
1444
- isSaving: true,
1782
+ // When a record is deleted, it enters the `start`
1783
+ // state. It will exit this state when the record's
1784
+ // transaction starts to commit.
1785
+ start: DS.State.create({
1786
+ willCommit: function(manager) {
1787
+ manager.goToState('inFlight');
1788
+ }
1789
+ }),
1445
1790
 
1446
- didDelete: function(manager) {
1447
- manager.goToState('saved');
1448
- },
1791
+ // After a record's transaction is committing, but
1792
+ // before the adapter indicates that the deletion
1793
+ // has saved to the server, a record is in the
1794
+ // `inFlight` substate of `deleted`.
1795
+ inFlight: DS.State.create({
1796
+ // FLAGS
1797
+ isSaving: true,
1449
1798
 
1799
+ // TRANSITIONS
1450
1800
  exit: function(stateManager) {
1451
1801
  var model = get(stateManager, 'model');
1452
1802
 
1453
1803
  model.withTransaction(function(t) {
1454
1804
  t.modelBecameClean('deleted', model);
1455
1805
  });
1806
+ },
1807
+
1808
+ // EVENTS
1809
+ didCommit: function(manager) {
1810
+ manager.goToState('saved');
1456
1811
  }
1457
1812
  }),
1458
1813
 
1814
+ // Once the adapter indicates that the deletion has
1815
+ // been saved, the record enters the `saved` substate
1816
+ // of `deleted`.
1459
1817
  saved: DS.State.create({
1460
1818
  isDirty: false
1461
1819
  })
1462
1820
  }),
1463
1821
 
1822
+ // If the adapter indicates that there was an unknown
1823
+ // error saving a record, the record enters the `error`
1824
+ // state.
1464
1825
  error: DS.State.create({
1465
1826
  isError: true
1466
1827
  })
@@ -1473,6 +1834,12 @@ DS.StateManager = Ember.StateManager.extend({
1473
1834
  states: states
1474
1835
  });
1475
1836
 
1837
+ })({});
1838
+
1839
+
1840
+ (function(exports) {
1841
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
1842
+
1476
1843
  var retrieveFromCurrentState = Ember.computed(function(key) {
1477
1844
  return get(getPath(this, 'stateManager.currentState'), key);
1478
1845
  }).property('stateManager.currentState').cacheable();
@@ -1484,6 +1851,7 @@ DS.Model = Ember.Object.extend({
1484
1851
  isDeleted: retrieveFromCurrentState,
1485
1852
  isError: retrieveFromCurrentState,
1486
1853
  isNew: retrieveFromCurrentState,
1854
+ isPending: retrieveFromCurrentState,
1487
1855
  isValid: retrieveFromCurrentState,
1488
1856
 
1489
1857
  clientId: null,
@@ -1491,8 +1859,22 @@ DS.Model = Ember.Object.extend({
1491
1859
  // because unknownProperty is used, any internal property
1492
1860
  // must be initialized here.
1493
1861
  primaryKey: 'id',
1862
+ id: Ember.computed(function(key, value) {
1863
+ var primaryKey = get(this, 'primaryKey'),
1864
+ data = get(this, 'data');
1865
+
1866
+ if (arguments.length === 2) {
1867
+ set(data, primaryKey, value);
1868
+ return value;
1869
+ }
1870
+
1871
+ return data && get(data, primaryKey);
1872
+ }).property('primaryKey', 'data'),
1873
+
1494
1874
  data: null,
1875
+ pendingQueue: null,
1495
1876
  transaction: null,
1877
+ errors: null,
1496
1878
 
1497
1879
  didLoad: Ember.K,
1498
1880
  didUpdate: Ember.K,
@@ -1503,100 +1885,101 @@ DS.Model = Ember.Object.extend({
1503
1885
  model: this
1504
1886
  });
1505
1887
 
1888
+ set(this, 'pendingQueue', {});
1506
1889
  set(this, 'stateManager', stateManager);
1507
1890
  stateManager.goToState('empty');
1508
1891
  },
1509
1892
 
1510
- withTransaction: function(fn) {
1511
- var transaction = get(this, 'transaction') || getPath(this, 'store.defaultTransaction');
1512
-
1513
- if (transaction) { fn(transaction); }
1514
- },
1515
-
1516
- setData: function(data) {
1517
- var stateManager = get(this, 'stateManager');
1518
- stateManager.send('setData', data);
1519
- },
1520
-
1521
- setProperty: function(key, value) {
1522
- var stateManager = get(this, 'stateManager');
1523
- stateManager.send('setProperty', { key: key, value: value });
1524
- },
1525
-
1526
- deleteRecord: function() {
1527
- var stateManager = get(this, 'stateManager');
1528
- stateManager.send('delete');
1529
- },
1530
-
1531
1893
  destroy: function() {
1532
- this.deleteRecord();
1894
+ if (!get(this, 'isDeleted')) {
1895
+ this.deleteRecord();
1896
+ }
1533
1897
  this._super();
1534
1898
  },
1535
1899
 
1536
- loadingData: function() {
1537
- var stateManager = get(this, 'stateManager');
1538
- stateManager.send('loadingData');
1900
+ send: function(name, context) {
1901
+ return get(this, 'stateManager').send(name, context);
1539
1902
  },
1540
1903
 
1541
- willLoadData: function() {
1542
- var stateManager = get(this, 'stateManager');
1543
- stateManager.send('willLoadData');
1544
- },
1545
-
1546
- willCommit: function() {
1547
- var stateManager = get(this, 'stateManager');
1548
- stateManager.send('willCommit');
1904
+ withTransaction: function(fn) {
1905
+ var transaction = get(this, 'transaction') || getPath(this, 'store.defaultTransaction');
1906
+ if (transaction) { fn(transaction); }
1549
1907
  },
1550
1908
 
1551
- adapterDidUpdate: function() {
1552
- var stateManager = get(this, 'stateManager');
1553
- stateManager.send('didUpdate');
1909
+ setProperty: function(key, value) {
1910
+ this.send('setProperty', { key: key, value: value });
1554
1911
  },
1555
1912
 
1556
- adapterDidCreate: function() {
1557
- var stateManager = get(this, 'stateManager');
1558
- stateManager.send('didCreate');
1913
+ deleteRecord: function() {
1914
+ this.send('deleteRecord');
1559
1915
  },
1560
1916
 
1561
- adapterDidDelete: function() {
1562
- var stateManager = get(this, 'stateManager');
1563
- stateManager.send('didDelete');
1917
+ waitingOn: function(record) {
1918
+ this.send('waitingOn', record);
1564
1919
  },
1565
1920
 
1566
- wasInvalid: function(errors) {
1567
- var stateManager = get(this, 'stateManager');
1568
- stateManager.send('wasInvalid', errors);
1921
+ notifyHashWasUpdated: function() {
1922
+ var store = get(this, 'store');
1923
+ if (store) {
1924
+ store.hashWasUpdated(this.constructor, get(this, 'clientId'));
1925
+ }
1569
1926
  },
1570
1927
 
1571
1928
  unknownProperty: function(key) {
1572
1929
  var data = get(this, 'data');
1573
1930
 
1574
- if (data) {
1575
- return get(data, key);
1931
+ if (data && key in data) {
1932
+ ember_assert("You attempted to access the " + key + " property on a model without defining an attribute.", false);
1576
1933
  }
1577
1934
  },
1578
1935
 
1579
1936
  setUnknownProperty: function(key, value) {
1580
1937
  var data = get(this, 'data');
1581
- ember_assert("You cannot set a model attribute before its data is loaded.", !!data);
1582
1938
 
1583
- this.setProperty(key, value);
1584
- return value;
1939
+ if (data && key in data) {
1940
+ ember_assert("You attempted to set the " + key + " property on a model without defining an attribute.", false);
1941
+ } else {
1942
+ return this._super(key, value);
1943
+ }
1585
1944
  }
1586
1945
  });
1587
1946
 
1947
+ // Helper function to generate store aliases.
1948
+ // This returns a function that invokes the named alias
1949
+ // on the default store, but injects the class as the
1950
+ // first parameter.
1951
+ var storeAlias = function(methodName) {
1952
+ return function() {
1953
+ var store = get(DS, 'defaultStore'),
1954
+ args = [].slice.call(arguments);
1955
+
1956
+ args.unshift(this);
1957
+ return store[methodName].apply(store, args);
1958
+ };
1959
+ };
1960
+
1588
1961
  DS.Model.reopenClass({
1589
- typeForAssociation: function(association) {
1590
- var type = this.metaForProperty(association).type;
1591
- if (typeof type === 'string') {
1592
- type = getPath(this, type, false) || getPath(window, type);
1593
- }
1594
- return type;
1595
- }
1962
+ find: storeAlias('find'),
1963
+ filter: storeAlias('filter'),
1964
+
1965
+ _create: DS.Model.create,
1966
+
1967
+ create: function() {
1968
+ throw new Ember.Error("You should not call `create` on a model. Instead, call `createRecord` with the attributes you would like to set.");
1969
+ },
1970
+
1971
+ createRecord: storeAlias('createRecord')
1596
1972
  });
1597
1973
 
1974
+ })({});
1975
+
1976
+
1977
+ (function(exports) {
1978
+ var get = Ember.get, getPath = Ember.getPath;
1598
1979
  DS.attr = function(type, options) {
1599
1980
  var transform = DS.attr.transforms[type];
1981
+ ember_assert("Could not find model attribute of type " + type, !!transform);
1982
+
1600
1983
  var transformFrom = transform.from;
1601
1984
  var transformTo = transform.to;
1602
1985
 
@@ -1618,73 +2001,24 @@ DS.attr = function(type, options) {
1618
2001
  }
1619
2002
  }).property('data');
1620
2003
  };
1621
-
1622
- var embeddedFindRecord = function(store, type, data, key, one) {
1623
- var association = data ? get(data, key) : one ? null : [];
1624
- if (one) {
1625
- return association ? store.load(type, association).id : null;
1626
- } else {
1627
- return association ? store.loadMany(type, association).ids : [];
1628
- }
1629
- };
1630
-
1631
- var referencedFindRecord = function(store, type, data, key, one) {
1632
- return data ? get(data, key) : one ? null : [];
1633
- };
1634
-
1635
- var hasAssociation = function(type, options, one) {
1636
- var embedded = options && options.embedded,
1637
- findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
1638
-
1639
- return Ember.computed(function(key) {
1640
- var data = get(this, 'data'), ids, id, association,
1641
- store = get(this, 'store');
1642
-
1643
- if (typeof type === 'string') {
1644
- type = getPath(this, type, false) || getPath(window, type);
1645
- }
1646
-
1647
- key = (options && options.key) ? options.key : key;
1648
- if (one) {
1649
- id = findRecord(store, type, data, key, true);
1650
- association = id ? store.find(type, id) : null;
1651
- } else {
1652
- ids = findRecord(store, type, data, key);
1653
- association = store.findMany(type, ids);
1654
- }
1655
-
1656
- return association;
1657
- }).property('data').cacheable().meta({ type: type });
1658
- };
1659
-
1660
- DS.hasMany = function(type, options) {
1661
- ember_assert("The type passed to DS.hasMany must be defined", !!type);
1662
- return hasAssociation(type, options);
1663
- };
1664
-
1665
- DS.hasOne = function(type, options) {
1666
- ember_assert("The type passed to DS.hasOne must be defined", !!type);
1667
- return hasAssociation(type, options, true);
1668
- };
1669
-
1670
2004
  DS.attr.transforms = {
1671
2005
  string: {
1672
2006
  from: function(serialized) {
1673
- return Em.none(serialized) ? null : String(serialized);
2007
+ return Ember.none(serialized) ? null : String(serialized);
1674
2008
  },
1675
2009
 
1676
2010
  to: function(deserialized) {
1677
- return Em.none(deserialized) ? null : String(deserialized);
2011
+ return Ember.none(deserialized) ? null : String(deserialized);
1678
2012
  }
1679
2013
  },
1680
2014
 
1681
2015
  integer: {
1682
2016
  from: function(serialized) {
1683
- return Em.none(serialized) ? null : Number(serialized);
2017
+ return Ember.none(serialized) ? null : Number(serialized);
1684
2018
  },
1685
2019
 
1686
2020
  to: function(deserialized) {
1687
- return Em.none(deserialized) ? null : Number(deserialized);
2021
+ return Ember.none(deserialized) ? null : Number(deserialized);
1688
2022
  }
1689
2023
  },
1690
2024
 
@@ -1746,6 +2080,76 @@ DS.attr.transforms = {
1746
2080
  }
1747
2081
  };
1748
2082
 
2083
+
2084
+ })({});
2085
+
2086
+
2087
+ (function(exports) {
2088
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
2089
+ DS.Model.reopenClass({
2090
+ typeForAssociation: function(association) {
2091
+ var type = this.metaForProperty(association).type;
2092
+ if (typeof type === 'string') {
2093
+ type = getPath(this, type, false) || getPath(window, type);
2094
+ }
2095
+ return type;
2096
+ }
2097
+ });
2098
+
2099
+
2100
+ var embeddedFindRecord = function(store, type, data, key, one) {
2101
+ var association = data ? get(data, key) : one ? null : [];
2102
+ if (one) {
2103
+ return association ? store.load(type, association).id : null;
2104
+ } else {
2105
+ return association ? store.loadMany(type, association).ids : [];
2106
+ }
2107
+ };
2108
+
2109
+ var referencedFindRecord = function(store, type, data, key, one) {
2110
+ return data ? get(data, key) : one ? null : [];
2111
+ };
2112
+
2113
+ var hasAssociation = function(type, options, one) {
2114
+ var embedded = options && options.embedded,
2115
+ findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
2116
+
2117
+ return Ember.computed(function(key) {
2118
+ var data = get(this, 'data'), ids, id, association,
2119
+ store = get(this, 'store');
2120
+
2121
+ if (typeof type === 'string') {
2122
+ type = getPath(this, type, false) || getPath(window, type);
2123
+ }
2124
+
2125
+ key = (options && options.key) ? options.key : key;
2126
+ if (one) {
2127
+ id = findRecord(store, type, data, key, true);
2128
+ association = id ? store.find(type, id) : null;
2129
+ } else {
2130
+ ids = findRecord(store, type, data, key);
2131
+ association = store.findMany(type, ids);
2132
+ set(association, 'parentRecord', this);
2133
+ }
2134
+
2135
+ return association;
2136
+ }).property('data').cacheable().meta({ type: type });
2137
+ };
2138
+
2139
+ DS.hasMany = function(type, options) {
2140
+ ember_assert("The type passed to DS.hasMany must be defined", !!type);
2141
+ return hasAssociation(type, options);
2142
+ };
2143
+
2144
+ DS.hasOne = function(type, options) {
2145
+ ember_assert("The type passed to DS.hasOne must be defined", !!type);
2146
+ return hasAssociation(type, options, true);
2147
+ };
2148
+
2149
+ })({});
2150
+
2151
+
2152
+ (function(exports) {
1749
2153
  })({});
1750
2154
 
1751
2155