rightnow_oms 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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