embient 0.0.5 → 0.0.6
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.
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
embient (0.0.
|
4
|
+
embient (0.0.6)
|
5
5
|
emberjs-rails
|
6
6
|
rails (>= 3.1.0)
|
7
7
|
|
@@ -50,7 +50,7 @@ GEM
|
|
50
50
|
i18n (0.6.0)
|
51
51
|
journey (1.0.3)
|
52
52
|
json (1.6.5)
|
53
|
-
mail (2.4.
|
53
|
+
mail (2.4.3)
|
54
54
|
i18n (>= 0.4.0)
|
55
55
|
mime-types (~> 1.16)
|
56
56
|
treetop (~> 1.4.8)
|
@@ -58,7 +58,7 @@ GEM
|
|
58
58
|
multi_json (1.1.0)
|
59
59
|
polyglot (0.3.3)
|
60
60
|
rack (1.4.1)
|
61
|
-
rack-cache (1.
|
61
|
+
rack-cache (1.2)
|
62
62
|
rack (>= 0.4)
|
63
63
|
rack-ssl (1.3.2)
|
64
64
|
rack
|
@@ -91,7 +91,7 @@ GEM
|
|
91
91
|
treetop (1.4.10)
|
92
92
|
polyglot
|
93
93
|
polyglot (>= 0.3.1)
|
94
|
-
tzinfo (0.3.
|
94
|
+
tzinfo (0.3.32)
|
95
95
|
|
96
96
|
PLATFORMS
|
97
97
|
ruby
|
data/lib/embient/version.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
(function(exports) {
|
3
|
-
window.DS = Ember.Namespace.create(
|
3
|
+
window.DS = Ember.Namespace.create({
|
4
|
+
CURRENT_API_REVISION: 3
|
5
|
+
});
|
4
6
|
|
5
7
|
})({});
|
6
8
|
|
@@ -89,11 +91,12 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
89
91
|
var root = this.rootForType(type);
|
90
92
|
|
91
93
|
var data = {};
|
92
|
-
data[root] =
|
94
|
+
data[root] = model.toJSON();
|
93
95
|
|
94
|
-
this.ajax(
|
96
|
+
this.ajax(this.buildURL(root), "POST", {
|
95
97
|
data: data,
|
96
98
|
success: function(json) {
|
99
|
+
this.sideload(store, type, json, root);
|
97
100
|
store.didCreateRecord(model, json[root]);
|
98
101
|
}
|
99
102
|
});
|
@@ -109,12 +112,14 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
109
112
|
|
110
113
|
var data = {};
|
111
114
|
data[plural] = models.map(function(model) {
|
112
|
-
return
|
115
|
+
return model.toJSON();
|
113
116
|
});
|
114
117
|
|
115
|
-
this.ajax(
|
118
|
+
this.ajax(this.buildURL(root), "POST", {
|
116
119
|
data: data,
|
120
|
+
|
117
121
|
success: function(json) {
|
122
|
+
this.sideload(store, type, json, plural);
|
118
123
|
store.didCreateRecords(type, models, json[plural]);
|
119
124
|
}
|
120
125
|
});
|
@@ -125,14 +130,13 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
125
130
|
var root = this.rootForType(type);
|
126
131
|
|
127
132
|
var data = {};
|
128
|
-
data[root] =
|
133
|
+
data[root] = model.toJSON();
|
129
134
|
|
130
|
-
|
131
|
-
|
132
|
-
this.ajax(url, "PUT", {
|
135
|
+
this.ajax(this.buildURL(root, id), "PUT", {
|
133
136
|
data: data,
|
134
137
|
success: function(json) {
|
135
|
-
|
138
|
+
this.sideload(store, type, json, root);
|
139
|
+
store.didUpdateRecord(model, json && json[root]);
|
136
140
|
}
|
137
141
|
});
|
138
142
|
},
|
@@ -147,12 +151,13 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
147
151
|
|
148
152
|
var data = {};
|
149
153
|
data[plural] = models.map(function(model) {
|
150
|
-
return
|
154
|
+
return model.toJSON();
|
151
155
|
});
|
152
156
|
|
153
|
-
this.ajax(
|
157
|
+
this.ajax(this.buildURL(root, "bulk"), "PUT", {
|
154
158
|
data: data,
|
155
159
|
success: function(json) {
|
160
|
+
this.sideload(store, type, json, plural);
|
156
161
|
store.didUpdateRecords(models, json[plural]);
|
157
162
|
}
|
158
163
|
});
|
@@ -162,10 +167,9 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
162
167
|
var id = get(model, 'id');
|
163
168
|
var root = this.rootForType(type);
|
164
169
|
|
165
|
-
|
166
|
-
|
167
|
-
this.ajax(url, "DELETE", {
|
170
|
+
this.ajax(this.buildURL(root, id), "DELETE", {
|
168
171
|
success: function(json) {
|
172
|
+
if (json) { this.sideload(store, type, json); }
|
169
173
|
store.didDeleteRecord(model);
|
170
174
|
}
|
171
175
|
});
|
@@ -184,9 +188,10 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
184
188
|
return get(model, 'id');
|
185
189
|
});
|
186
190
|
|
187
|
-
this.ajax(
|
191
|
+
this.ajax(this.buildURL(root, 'bulk'), "DELETE", {
|
188
192
|
data: data,
|
189
193
|
success: function(json) {
|
194
|
+
if (json) { this.sideload(store, type, json); }
|
190
195
|
store.didDeleteRecords(models);
|
191
196
|
}
|
192
197
|
});
|
@@ -195,11 +200,10 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
195
200
|
find: function(store, type, id) {
|
196
201
|
var root = this.rootForType(type);
|
197
202
|
|
198
|
-
|
199
|
-
|
200
|
-
this.ajax(url, "GET", {
|
203
|
+
this.ajax(this.buildURL(root, id), "GET", {
|
201
204
|
success: function(json) {
|
202
205
|
store.load(type, json[root]);
|
206
|
+
this.sideload(store, type, json, root);
|
203
207
|
}
|
204
208
|
});
|
205
209
|
},
|
@@ -207,10 +211,11 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
207
211
|
findMany: function(store, type, ids) {
|
208
212
|
var root = this.rootForType(type), plural = this.pluralize(root);
|
209
213
|
|
210
|
-
this.ajax(
|
214
|
+
this.ajax(this.buildURL(root), "GET", {
|
211
215
|
data: { ids: ids },
|
212
216
|
success: function(json) {
|
213
217
|
store.loadMany(type, ids, json[plural]);
|
218
|
+
this.sideload(store, type, json, plural);
|
214
219
|
}
|
215
220
|
});
|
216
221
|
},
|
@@ -218,9 +223,10 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
218
223
|
findAll: function(store, type) {
|
219
224
|
var root = this.rootForType(type), plural = this.pluralize(root);
|
220
225
|
|
221
|
-
this.ajax(
|
226
|
+
this.ajax(this.buildURL(root), "GET", {
|
222
227
|
success: function(json) {
|
223
228
|
store.loadMany(type, json[plural]);
|
229
|
+
this.sideload(store, type, json, plural);
|
224
230
|
}
|
225
231
|
});
|
226
232
|
},
|
@@ -228,10 +234,11 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
228
234
|
findQuery: function(store, type, query, modelArray) {
|
229
235
|
var root = this.rootForType(type), plural = this.pluralize(root);
|
230
236
|
|
231
|
-
this.ajax(
|
237
|
+
this.ajax(this.buildURL(root), "GET", {
|
232
238
|
data: query,
|
233
239
|
success: function(json) {
|
234
240
|
modelArray.load(json[plural]);
|
241
|
+
this.sideload(store, type, json, plural);
|
235
242
|
}
|
236
243
|
});
|
237
244
|
},
|
@@ -258,9 +265,61 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
258
265
|
ajax: function(url, type, hash) {
|
259
266
|
hash.url = url;
|
260
267
|
hash.type = type;
|
261
|
-
hash.dataType =
|
268
|
+
hash.dataType = 'json';
|
269
|
+
hash.contentType = 'application/json';
|
270
|
+
hash.context = this;
|
271
|
+
|
272
|
+
if (hash.data && type !== 'GET') {
|
273
|
+
hash.data = JSON.stringify(hash.data);
|
274
|
+
}
|
262
275
|
|
263
276
|
jQuery.ajax(hash);
|
277
|
+
},
|
278
|
+
|
279
|
+
sideload: function(store, type, json, root) {
|
280
|
+
var sideloadedType, mappings;
|
281
|
+
|
282
|
+
for (var prop in json) {
|
283
|
+
if (!json.hasOwnProperty(prop)) { continue; }
|
284
|
+
if (prop === root) { continue; }
|
285
|
+
|
286
|
+
sideloadedType = type.typeForAssociation(prop);
|
287
|
+
|
288
|
+
if (!sideloadedType) {
|
289
|
+
mappings = get(this, 'mappings');
|
290
|
+
|
291
|
+
ember_assert("Your server returned a hash with the key " + prop + " but you have no mappings", !!mappings);
|
292
|
+
|
293
|
+
sideloadedType = get(get(this, 'mappings'), prop);
|
294
|
+
|
295
|
+
ember_assert("Your server returned a hash with the key " + prop + " but you have no mapping for it", !!sideloadedType);
|
296
|
+
}
|
297
|
+
|
298
|
+
this.loadValue(store, sideloadedType, json[prop]);
|
299
|
+
}
|
300
|
+
},
|
301
|
+
|
302
|
+
loadValue: function(store, type, value) {
|
303
|
+
if (value instanceof Array) {
|
304
|
+
store.loadMany(type, value);
|
305
|
+
} else {
|
306
|
+
store.load(type, value);
|
307
|
+
}
|
308
|
+
},
|
309
|
+
|
310
|
+
buildURL: function(model, suffix) {
|
311
|
+
var url = [""];
|
312
|
+
|
313
|
+
if (this.namespace !== undefined) {
|
314
|
+
url.push(this.namespace);
|
315
|
+
}
|
316
|
+
|
317
|
+
url.push(this.pluralize(model));
|
318
|
+
if (suffix !== undefined) {
|
319
|
+
url.push(suffix);
|
320
|
+
}
|
321
|
+
|
322
|
+
return url.join("/");
|
264
323
|
}
|
265
324
|
});
|
266
325
|
|
@@ -297,29 +356,11 @@ DS.ModelArray = Ember.ArrayProxy.extend({
|
|
297
356
|
// The store that created this model array.
|
298
357
|
store: null,
|
299
358
|
|
300
|
-
// for associations, the model that this association belongs to.
|
301
|
-
parentModel: null,
|
302
|
-
|
303
359
|
init: function() {
|
304
360
|
set(this, 'modelCache', Ember.A([]));
|
305
361
|
this._super();
|
306
362
|
},
|
307
363
|
|
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
|
-
|
323
364
|
arrayDidChange: function(array, index, removed, added) {
|
324
365
|
var modelCache = get(this, 'modelCache');
|
325
366
|
modelCache.replace(index, 0, new Array(added));
|
@@ -363,6 +404,11 @@ var get = Ember.get;
|
|
363
404
|
DS.FilteredModelArray = DS.ModelArray.extend({
|
364
405
|
filterFunction: null,
|
365
406
|
|
407
|
+
replace: function() {
|
408
|
+
var type = get(this, 'type').toString();
|
409
|
+
throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
|
410
|
+
},
|
411
|
+
|
366
412
|
updateFilter: Ember.observer(function() {
|
367
413
|
var store = get(this, 'store');
|
368
414
|
store.updateModelArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
|
@@ -379,6 +425,11 @@ DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
|
|
379
425
|
query: null,
|
380
426
|
isLoaded: false,
|
381
427
|
|
428
|
+
replace: function() {
|
429
|
+
var type = get(this, 'type').toString();
|
430
|
+
throw new Error("The result of a server query (on " + type + ") is immutable.");
|
431
|
+
},
|
432
|
+
|
382
433
|
load: function(array) {
|
383
434
|
var store = get(this, 'store'), type = get(this, 'type');
|
384
435
|
|
@@ -396,168 +447,161 @@ DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
|
|
396
447
|
|
397
448
|
|
398
449
|
(function(exports) {
|
399
|
-
|
450
|
+
var get = Ember.get, set = Ember.set;
|
400
451
|
|
452
|
+
DS.ManyArray = DS.ModelArray.extend({
|
453
|
+
parentRecord: null,
|
401
454
|
|
402
|
-
|
403
|
-
|
455
|
+
// Overrides Ember.Array's replace method to implement
|
456
|
+
replace: function(index, removed, added) {
|
457
|
+
var parentRecord = get(this, 'parentRecord');
|
458
|
+
var pendingParent = parentRecord && !get(parentRecord, 'id');
|
404
459
|
|
405
|
-
|
406
|
-
|
407
|
-
this.clear();
|
408
|
-
},
|
460
|
+
added = added.map(function(record) {
|
461
|
+
ember_assert("You can only add records of " + (get(this, 'type') && get(this, 'type').toString()) + " to this association.", !get(this, 'type') || (get(this, 'type') === record.constructor));
|
409
462
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
},
|
463
|
+
if (pendingParent) {
|
464
|
+
record.send('waitingOn', parentRecord);
|
465
|
+
}
|
414
466
|
|
415
|
-
|
416
|
-
var guid = Ember.guidFor(obj),
|
417
|
-
presenceSet = get(this, 'presenceSet'),
|
418
|
-
list = get(this, 'list');
|
467
|
+
this.assignInverse(record, parentRecord);
|
419
468
|
|
420
|
-
|
469
|
+
return record.get('clientId');
|
470
|
+
}, this);
|
421
471
|
|
422
|
-
|
423
|
-
list.pushObject(obj);
|
472
|
+
this._super(index, removed, added);
|
424
473
|
},
|
425
474
|
|
426
|
-
|
427
|
-
var
|
428
|
-
|
429
|
-
|
475
|
+
assignInverse: function(record, parentRecord) {
|
476
|
+
var associationMap = get(record.constructor, 'associations'),
|
477
|
+
possibleAssociations = associationMap.get(record.constructor),
|
478
|
+
possible, actual;
|
430
479
|
|
431
|
-
|
432
|
-
list.removeObject(obj);
|
433
|
-
},
|
480
|
+
if (!possibleAssociations) { return; }
|
434
481
|
|
435
|
-
|
436
|
-
|
437
|
-
},
|
482
|
+
for (var i = 0, l = possibleAssociations.length; i < l; i++) {
|
483
|
+
possible = possibleAssociations[i];
|
438
484
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
}
|
444
|
-
},
|
485
|
+
if (possible.kind === 'belongsTo') {
|
486
|
+
actual = possible;
|
487
|
+
break;
|
488
|
+
}
|
489
|
+
}
|
445
490
|
|
446
|
-
|
447
|
-
|
491
|
+
if (actual) {
|
492
|
+
set(record, actual.name, parentRecord);
|
493
|
+
}
|
448
494
|
}
|
449
495
|
});
|
450
496
|
|
451
|
-
|
452
|
-
A Hash stores values indexed by keys. Unlike JavaScript's
|
453
|
-
default Objects, the keys of a Hash can be any JavaScript
|
454
|
-
object.
|
497
|
+
})({});
|
455
498
|
|
456
|
-
Internally, a Hash has two data structures:
|
457
499
|
|
458
|
-
|
459
|
-
|
460
|
-
Ember.guidFor(key)
|
500
|
+
(function(exports) {
|
501
|
+
})({});
|
461
502
|
|
462
|
-
When a key/value pair is added for the first time, we
|
463
|
-
add the key to the `keys` OrderedSet, and create or
|
464
|
-
replace an entry in `values`. When an entry is deleted,
|
465
|
-
we delete its entry in `keys` and `values`.
|
466
|
-
*/
|
467
503
|
|
468
|
-
|
504
|
+
(function(exports) {
|
505
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
|
506
|
+
|
507
|
+
DS.Transaction = Ember.Object.extend({
|
469
508
|
init: function() {
|
470
|
-
set(this, '
|
471
|
-
|
509
|
+
set(this, 'buckets', {
|
510
|
+
clean: Ember.Map.create(),
|
511
|
+
created: Ember.Map.create(),
|
512
|
+
updated: Ember.Map.create(),
|
513
|
+
deleted: Ember.Map.create()
|
514
|
+
});
|
472
515
|
},
|
473
516
|
|
474
|
-
|
475
|
-
var
|
476
|
-
var guid = Ember.guidFor(key);
|
477
|
-
|
478
|
-
keys.add(key);
|
479
|
-
values[guid] = value;
|
517
|
+
createRecord: function(type, hash) {
|
518
|
+
var store = get(this, 'store');
|
480
519
|
|
481
|
-
return
|
520
|
+
return store.createRecord(type, hash, this);
|
482
521
|
},
|
483
522
|
|
484
|
-
|
485
|
-
|
486
|
-
|
523
|
+
add: function(record) {
|
524
|
+
// we could probably make this work if someone has a valid use case. Do you?
|
525
|
+
ember_assert("Once a record has changed, you cannot move it into a different transaction", !get(record, 'isDirty'));
|
487
526
|
|
488
|
-
|
527
|
+
var modelTransaction = get(record, 'transaction'),
|
528
|
+
defaultTransaction = getPath(this, 'store.defaultTransaction');
|
489
529
|
|
490
|
-
|
491
|
-
delete values[guid];
|
530
|
+
ember_assert("Models cannot belong to more than one transaction at a time.", modelTransaction === defaultTransaction);
|
492
531
|
|
493
|
-
|
532
|
+
this.adoptRecord(record);
|
494
533
|
},
|
495
534
|
|
496
|
-
|
497
|
-
var
|
498
|
-
var guid = Ember.guidFor(key);
|
535
|
+
remove: function(record) {
|
536
|
+
var defaultTransaction = getPath(this, 'store.defaultTransaction');
|
499
537
|
|
500
|
-
|
538
|
+
defaultTransaction.adoptRecord(record);
|
501
539
|
},
|
502
540
|
|
503
|
-
|
504
|
-
|
505
|
-
values = get(this, 'values');
|
541
|
+
/**
|
542
|
+
@private
|
506
543
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
});
|
511
|
-
}
|
512
|
-
});
|
544
|
+
This method moves a record into a different transaction without the normal
|
545
|
+
checks that ensure that the user is not doing something weird, like moving
|
546
|
+
a dirty record into a new transaction.
|
513
547
|
|
514
|
-
|
515
|
-
|
516
|
-
set(this, 'dirty', {
|
517
|
-
created: Hash.create(),
|
518
|
-
updated: Hash.create(),
|
519
|
-
deleted: Hash.create()
|
520
|
-
});
|
521
|
-
},
|
548
|
+
It is designed for internal use, such as when we are moving a clean record
|
549
|
+
into a new transaction when the transaction is committed.
|
522
550
|
|
523
|
-
|
524
|
-
|
551
|
+
This method must not be called unless the record is clean.
|
552
|
+
*/
|
553
|
+
adoptRecord: function(record) {
|
554
|
+
var oldTransaction = get(record, 'transaction');
|
525
555
|
|
526
|
-
|
527
|
-
|
556
|
+
if (oldTransaction) {
|
557
|
+
oldTransaction.removeFromBucket('clean', record);
|
558
|
+
}
|
528
559
|
|
529
|
-
|
530
|
-
|
531
|
-
|
560
|
+
this.addToBucket('clean', record);
|
561
|
+
set(record, 'transaction', this);
|
562
|
+
},
|
532
563
|
|
533
|
-
|
564
|
+
modelBecameDirty: function(kind, record) {
|
565
|
+
this.removeFromBucket('clean', record);
|
566
|
+
this.addToBucket(kind, record);
|
534
567
|
},
|
535
568
|
|
536
|
-
|
537
|
-
|
538
|
-
|
569
|
+
/** @private */
|
570
|
+
addToBucket: function(kind, record) {
|
571
|
+
var bucket = get(get(this, 'buckets'), kind),
|
572
|
+
type = record.constructor;
|
573
|
+
|
574
|
+
var records = bucket.get(type);
|
539
575
|
|
540
|
-
|
576
|
+
if (!records) {
|
577
|
+
records = Ember.OrderedSet.create();
|
578
|
+
bucket.set(type, records);
|
579
|
+
}
|
541
580
|
|
542
|
-
|
543
|
-
models.add(model);
|
581
|
+
records.add(record);
|
544
582
|
},
|
545
583
|
|
546
|
-
|
547
|
-
|
548
|
-
|
584
|
+
/** @private */
|
585
|
+
removeFromBucket: function(kind, record) {
|
586
|
+
var bucket = get(get(this, 'buckets'), kind),
|
587
|
+
type = record.constructor;
|
588
|
+
|
589
|
+
var records = bucket.get(type);
|
590
|
+
records.remove(record);
|
591
|
+
},
|
549
592
|
|
550
|
-
|
551
|
-
|
593
|
+
modelBecameClean: function(kind, record) {
|
594
|
+
this.removeFromBucket(kind, record);
|
552
595
|
|
553
|
-
|
596
|
+
var defaultTransaction = getPath(this, 'store.defaultTransaction');
|
597
|
+
defaultTransaction.adoptRecord(record);
|
554
598
|
},
|
555
599
|
|
556
600
|
commit: function() {
|
557
|
-
var
|
601
|
+
var buckets = get(this, 'buckets');
|
558
602
|
|
559
603
|
var iterate = function(kind, fn, binding) {
|
560
|
-
var dirty = get(
|
604
|
+
var dirty = get(buckets, kind);
|
561
605
|
|
562
606
|
dirty.forEach(function(type, models) {
|
563
607
|
if (models.isEmpty()) { return; }
|
@@ -592,6 +636,16 @@ DS.Transaction = Ember.Object.extend({
|
|
592
636
|
|
593
637
|
var store = get(this, 'store');
|
594
638
|
var adapter = get(store, '_adapter');
|
639
|
+
|
640
|
+
var clean = get(buckets, 'clean');
|
641
|
+
var defaultTransaction = get(store, 'defaultTransaction');
|
642
|
+
|
643
|
+
clean.forEach(function(type, records) {
|
644
|
+
records.forEach(function(record) {
|
645
|
+
this.remove(record);
|
646
|
+
}, this);
|
647
|
+
}, this);
|
648
|
+
|
595
649
|
if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
|
596
650
|
else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
|
597
651
|
}
|
@@ -603,46 +657,12 @@ DS.Transaction = Ember.Object.extend({
|
|
603
657
|
(function(exports) {
|
604
658
|
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
|
605
659
|
|
606
|
-
var
|
607
|
-
|
608
|
-
this.
|
609
|
-
},
|
610
|
-
|
611
|
-
clear: function() {
|
612
|
-
this.set('presenceSet', {});
|
613
|
-
this.set('list', Ember.NativeArray.apply([]));
|
614
|
-
},
|
615
|
-
|
616
|
-
add: function(obj) {
|
617
|
-
var guid = Ember.guidFor(obj),
|
618
|
-
presenceSet = get(this, 'presenceSet'),
|
619
|
-
list = get(this, 'list');
|
620
|
-
|
621
|
-
if (guid in presenceSet) { return; }
|
622
|
-
|
623
|
-
presenceSet[guid] = true;
|
624
|
-
list.pushObject(obj);
|
625
|
-
},
|
626
|
-
|
627
|
-
remove: function(obj) {
|
628
|
-
var guid = Ember.guidFor(obj),
|
629
|
-
presenceSet = get(this, 'presenceSet'),
|
630
|
-
list = get(this, 'list');
|
631
|
-
|
632
|
-
delete presenceSet[guid];
|
633
|
-
list.removeObject(obj);
|
634
|
-
},
|
635
|
-
|
636
|
-
isEmpty: function() {
|
637
|
-
return getPath(this, 'list.length') === 0;
|
638
|
-
},
|
639
|
-
|
640
|
-
forEach: function(fn, self) {
|
641
|
-
get(this, 'list').forEach(function(item) {
|
642
|
-
fn.call(self, item);
|
643
|
-
});
|
660
|
+
var DATA_PROXY = {
|
661
|
+
get: function(name) {
|
662
|
+
return this.savedData[name];
|
644
663
|
}
|
645
|
-
}
|
664
|
+
};
|
665
|
+
|
646
666
|
|
647
667
|
// Implementors Note:
|
648
668
|
//
|
@@ -691,33 +711,49 @@ DS.Store = Ember.Object.extend({
|
|
691
711
|
The init method registers this store as the default if none is specified.
|
692
712
|
*/
|
693
713
|
init: function() {
|
714
|
+
// Enforce API revisioning. See BREAKING_CHANGES.md for more.
|
715
|
+
var revision = get(this, 'revision');
|
716
|
+
|
717
|
+
if (revision !== DS.CURRENT_API_REVISION && !Ember.ENV.TESTING) {
|
718
|
+
throw new Error("Error: The Ember Data library has had breaking API changes since the last time you updated the library. Please review the list of breaking changes at https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md, then update your store's `revision` property to " + DS.CURRENT_API_REVISION);
|
719
|
+
}
|
720
|
+
|
694
721
|
if (!get(DS, 'defaultStore') || get(this, 'isDefaultStore')) {
|
695
722
|
set(DS, 'defaultStore', this);
|
696
723
|
}
|
697
724
|
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
725
|
+
// internal bookkeeping; not observable
|
726
|
+
this.typeMaps = {};
|
727
|
+
this.recordCache = [];
|
728
|
+
this.clientIdToId = {};
|
729
|
+
this.modelArraysByClientId = {};
|
730
|
+
|
731
|
+
set(this, 'defaultTransaction', this.transaction());
|
704
732
|
|
705
733
|
return this._super();
|
706
734
|
},
|
707
735
|
|
736
|
+
/**
|
737
|
+
Returns a new transaction scoped to this store.
|
738
|
+
|
739
|
+
@see {DS.Transaction}
|
740
|
+
@returns DS.Transaction
|
741
|
+
*/
|
708
742
|
transaction: function() {
|
709
743
|
return DS.Transaction.create({ store: this });
|
710
744
|
},
|
711
745
|
|
712
|
-
|
713
|
-
|
714
|
-
var ret = modelArrays[clientId];
|
746
|
+
/**
|
747
|
+
@private
|
715
748
|
|
716
|
-
|
717
|
-
|
718
|
-
|
749
|
+
This is used only by the model's DataProxy. Do not use this directly.
|
750
|
+
*/
|
751
|
+
dataForRecord: function(record) {
|
752
|
+
var type = record.constructor,
|
753
|
+
clientId = get(record, 'clientId'),
|
754
|
+
typeMap = this.typeMapFor(type);
|
719
755
|
|
720
|
-
return
|
756
|
+
return typeMap.cidToHash[clientId];
|
721
757
|
},
|
722
758
|
|
723
759
|
/**
|
@@ -730,6 +766,13 @@ DS.Store = Ember.Object.extend({
|
|
730
766
|
*/
|
731
767
|
adapter: null,
|
732
768
|
|
769
|
+
/**
|
770
|
+
@private
|
771
|
+
|
772
|
+
This property returns the adapter, after resolving a possible String.
|
773
|
+
|
774
|
+
@returns DS.Adapter
|
775
|
+
*/
|
733
776
|
_adapter: Ember.computed(function() {
|
734
777
|
var adapter = get(this, 'adapter');
|
735
778
|
if (typeof adapter === 'string') {
|
@@ -738,44 +781,80 @@ DS.Store = Ember.Object.extend({
|
|
738
781
|
return adapter;
|
739
782
|
}).property('adapter').cacheable(),
|
740
783
|
|
784
|
+
// A monotonically increasing number to be used to uniquely identify
|
785
|
+
// data hashes and records.
|
741
786
|
clientIdCounter: -1,
|
742
787
|
|
743
788
|
// ....................
|
744
789
|
// . CREATE NEW MODEL .
|
745
790
|
// ....................
|
746
791
|
|
792
|
+
/**
|
793
|
+
Create a new record in the current store. The properties passed
|
794
|
+
to this method are set on the newly created record.
|
795
|
+
|
796
|
+
@param {subclass of DS.Model} type
|
797
|
+
@param {Object} properties a hash of properties to set on the
|
798
|
+
newly created record.
|
799
|
+
@returns DS.Model
|
800
|
+
*/
|
747
801
|
createRecord: function(type, properties, transaction) {
|
748
802
|
properties = properties || {};
|
749
803
|
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
804
|
+
// Create a new instance of the model `type` and put it
|
805
|
+
// into the specified `transaction`. If no transaction is
|
806
|
+
// specified, the default transaction will be used.
|
807
|
+
//
|
808
|
+
// NOTE: A `transaction` is specified when the
|
809
|
+
// `transaction.createRecord` API is used.
|
810
|
+
var record = type._create({
|
811
|
+
store: this
|
755
812
|
});
|
756
813
|
|
814
|
+
transaction = transaction || get(this, 'defaultTransaction');
|
815
|
+
transaction.adoptRecord(record);
|
816
|
+
|
817
|
+
// Extract the primary key from the `properties` hash,
|
818
|
+
// based on the `primaryKey` for the model type.
|
819
|
+
var id = properties[get(record, 'primaryKey')] || null;
|
820
|
+
|
757
821
|
var hash = {}, clientId;
|
758
822
|
|
823
|
+
// Push the hash into the store. If present, associate the
|
824
|
+
// extracted `id` with the hash.
|
759
825
|
clientId = this.pushHash(hash, id, type);
|
760
|
-
model.send('setData', hash);
|
761
826
|
|
762
|
-
|
827
|
+
record.send('didChangeData');
|
763
828
|
|
764
|
-
|
765
|
-
models[clientId] = model;
|
829
|
+
var recordCache = get(this, 'recordCache');
|
766
830
|
|
767
|
-
|
768
|
-
|
831
|
+
// Now that we have a clientId, attach it to the record we
|
832
|
+
// just created.
|
833
|
+
set(record, 'clientId', clientId);
|
769
834
|
|
770
|
-
|
835
|
+
// Store the record we just created in the record cache for
|
836
|
+
// this clientId.
|
837
|
+
recordCache[clientId] = record;
|
838
|
+
|
839
|
+
// Set the properties specified on the record.
|
840
|
+
record.setProperties(properties);
|
841
|
+
|
842
|
+
this.updateModelArrays(type, clientId, get(record, 'data'));
|
843
|
+
|
844
|
+
return record;
|
771
845
|
},
|
772
846
|
|
773
847
|
// ................
|
774
848
|
// . DELETE MODEL .
|
775
849
|
// ................
|
776
850
|
|
777
|
-
|
778
|
-
|
851
|
+
/**
|
852
|
+
For symmetry, a record can be deleted via the store.
|
853
|
+
|
854
|
+
@param {DS.Model} record
|
855
|
+
*/
|
856
|
+
deleteRecord: function(record) {
|
857
|
+
record.send('deleteRecord');
|
779
858
|
},
|
780
859
|
|
781
860
|
// ...............
|
@@ -783,22 +862,60 @@ DS.Store = Ember.Object.extend({
|
|
783
862
|
// ...............
|
784
863
|
|
785
864
|
/**
|
786
|
-
|
787
|
-
|
788
|
-
immediately. Otherwise, an empty DS.Model instance will be returned in
|
789
|
-
the loading state. As soon as the requested data is available, the model
|
790
|
-
will be moved into the loaded state and all of the information will be
|
791
|
-
available.
|
865
|
+
This is the main entry point into finding records. The first
|
866
|
+
parameter to this method is always a subclass of `DS.Model`.
|
792
867
|
|
793
|
-
|
794
|
-
|
868
|
+
You can use the `find` method on a subclass of `DS.Model`
|
869
|
+
directly if your application only has one store. For
|
870
|
+
example, instead of `store.find(App.Person, 1)`, you could
|
871
|
+
say `App.Person.find(1)`.
|
795
872
|
|
796
|
-
|
873
|
+
---
|
797
874
|
|
798
|
-
|
875
|
+
To find a record by ID, pass the `id` as the second parameter:
|
799
876
|
|
800
|
-
|
801
|
-
|
877
|
+
store.find(App.Person, 1);
|
878
|
+
App.Person.find(1);
|
879
|
+
|
880
|
+
If the record with that `id` had not previously been loaded,
|
881
|
+
the store will return an empty record immediately and ask
|
882
|
+
the adapter to find the data by calling its `find` method.
|
883
|
+
|
884
|
+
The `find` method will always return the same object for a
|
885
|
+
given type and `id`. To check whether the adapter has populated
|
886
|
+
a record, you can check its `isLoaded` property.
|
887
|
+
|
888
|
+
---
|
889
|
+
|
890
|
+
To find all records for a type, call `find` with no additional
|
891
|
+
parameters:
|
892
|
+
|
893
|
+
store.find(App.Person);
|
894
|
+
App.Person.find();
|
895
|
+
|
896
|
+
This will return a `ModelArray` representing all known records
|
897
|
+
for the given type and kick off a request to the adapter's
|
898
|
+
`findAll` method to load any additional records for the type.
|
899
|
+
|
900
|
+
The `ModelArray` returned by `find()` is live. If any more
|
901
|
+
records for the type are added at a later time through any
|
902
|
+
mechanism, it will automatically update to reflect the change.
|
903
|
+
|
904
|
+
---
|
905
|
+
|
906
|
+
To find a record by a query, call `find` with a hash as the
|
907
|
+
second parameter:
|
908
|
+
|
909
|
+
store.find(App.Person, { page: 1 });
|
910
|
+
App.Person.find({ page: 1 });
|
911
|
+
|
912
|
+
This will return a `ModelArray` immediately, but it will always
|
913
|
+
be an empty `ModelArray` at first. It will call the adapter's
|
914
|
+
`findQuery` method, which will populate the `ModelArray` once
|
915
|
+
the server has returned results.
|
916
|
+
|
917
|
+
You can check whether a query results `ModelArray` has loaded
|
918
|
+
by checking its `isLoaded` property.
|
802
919
|
*/
|
803
920
|
find: function(type, id, query) {
|
804
921
|
if (id === undefined) {
|
@@ -821,10 +938,9 @@ DS.Store = Ember.Object.extend({
|
|
821
938
|
},
|
822
939
|
|
823
940
|
findByClientId: function(type, clientId, id) {
|
824
|
-
var
|
825
|
-
|
826
|
-
|
827
|
-
var data = this.clientIdToHashMap(type);
|
941
|
+
var recordCache = get(this, 'recordCache'),
|
942
|
+
dataCache = this.typeMapFor(type).cidToHash,
|
943
|
+
model;
|
828
944
|
|
829
945
|
// If there is already a clientId assigned for this
|
830
946
|
// type/id combination, try to find an existing
|
@@ -832,15 +948,16 @@ DS.Store = Ember.Object.extend({
|
|
832
948
|
// materialize a new model and set its data to the
|
833
949
|
// value we already have.
|
834
950
|
if (clientId !== undefined) {
|
835
|
-
model =
|
951
|
+
model = recordCache[clientId];
|
836
952
|
|
837
953
|
if (!model) {
|
838
954
|
// create a new instance of the model in the
|
839
955
|
// 'isLoading' state
|
840
956
|
model = this.materializeRecord(type, clientId);
|
841
957
|
|
842
|
-
|
843
|
-
|
958
|
+
if (dataCache[clientId]) {
|
959
|
+
model.send('didChangeData');
|
960
|
+
}
|
844
961
|
}
|
845
962
|
} else {
|
846
963
|
clientId = this.pushHash(null, id, type);
|
@@ -861,8 +978,10 @@ DS.Store = Ember.Object.extend({
|
|
861
978
|
/** @private
|
862
979
|
*/
|
863
980
|
findMany: function(type, ids, query) {
|
864
|
-
var
|
865
|
-
|
981
|
+
var typeMap = this.typeMapFor(type),
|
982
|
+
idToClientIdMap = typeMap.idToCid,
|
983
|
+
data = typeMap.cidToHash,
|
984
|
+
needed;
|
866
985
|
|
867
986
|
var clientIds = Ember.A([]);
|
868
987
|
|
@@ -888,7 +1007,7 @@ DS.Store = Ember.Object.extend({
|
|
888
1007
|
else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }
|
889
1008
|
}
|
890
1009
|
|
891
|
-
return this.
|
1010
|
+
return this.createManyArray(type, clientIds);
|
892
1011
|
},
|
893
1012
|
|
894
1013
|
findQuery: function(type, query) {
|
@@ -916,7 +1035,14 @@ DS.Store = Ember.Object.extend({
|
|
916
1035
|
return array;
|
917
1036
|
},
|
918
1037
|
|
919
|
-
filter: function(type, filter) {
|
1038
|
+
filter: function(type, query, filter) {
|
1039
|
+
// allow an optional server query
|
1040
|
+
if (arguments.length === 3) {
|
1041
|
+
this.findQuery(type, query);
|
1042
|
+
} else if (arguments.length === 2) {
|
1043
|
+
filter = query;
|
1044
|
+
}
|
1045
|
+
|
920
1046
|
var array = DS.FilteredModelArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
|
921
1047
|
|
922
1048
|
this.registerModelArray(array, type, filter);
|
@@ -928,11 +1054,8 @@ DS.Store = Ember.Object.extend({
|
|
928
1054
|
// . UPDATING .
|
929
1055
|
// ............
|
930
1056
|
|
931
|
-
hashWasUpdated: function(type, clientId) {
|
932
|
-
|
933
|
-
var hash = clientIdToHashMap[clientId];
|
934
|
-
|
935
|
-
this.updateModelArrays(type, clientId, hash);
|
1057
|
+
hashWasUpdated: function(type, clientId, record) {
|
1058
|
+
this.updateModelArrays(type, clientId, get(record, 'data'));
|
936
1059
|
},
|
937
1060
|
|
938
1061
|
// ..............
|
@@ -940,11 +1063,14 @@ DS.Store = Ember.Object.extend({
|
|
940
1063
|
// ..............
|
941
1064
|
|
942
1065
|
commit: function() {
|
943
|
-
get(this, 'defaultTransaction')
|
1066
|
+
var defaultTransaction = get(this, 'defaultTransaction');
|
1067
|
+
set(this, 'defaultTransaction', this.transaction());
|
1068
|
+
|
1069
|
+
defaultTransaction.commit();
|
944
1070
|
},
|
945
1071
|
|
946
1072
|
didUpdateRecords: function(array, hashes) {
|
947
|
-
if (
|
1073
|
+
if (hashes) {
|
948
1074
|
array.forEach(function(model, idx) {
|
949
1075
|
this.didUpdateRecord(model, hashes[idx]);
|
950
1076
|
}, this);
|
@@ -956,12 +1082,13 @@ DS.Store = Ember.Object.extend({
|
|
956
1082
|
},
|
957
1083
|
|
958
1084
|
didUpdateRecord: function(model, hash) {
|
959
|
-
if (
|
960
|
-
var clientId = get(model, 'clientId')
|
961
|
-
|
1085
|
+
if (hash) {
|
1086
|
+
var clientId = get(model, 'clientId'),
|
1087
|
+
dataCache = this.typeMapFor(model.constructor).cidToHash;
|
962
1088
|
|
963
|
-
|
964
|
-
model.send('
|
1089
|
+
dataCache[clientId] = hash;
|
1090
|
+
model.send('didChangeData');
|
1091
|
+
model.hashWasUpdated();
|
965
1092
|
}
|
966
1093
|
|
967
1094
|
model.send('didCommit');
|
@@ -977,41 +1104,54 @@ DS.Store = Ember.Object.extend({
|
|
977
1104
|
model.send('didCommit');
|
978
1105
|
},
|
979
1106
|
|
980
|
-
|
981
|
-
var
|
1107
|
+
_didCreateRecord: function(record, hash, typeMap, clientId, primaryKey) {
|
1108
|
+
var recordData = get(record, 'data'), id, changes;
|
982
1109
|
|
983
|
-
|
984
|
-
|
985
|
-
|
1110
|
+
if (hash) {
|
1111
|
+
typeMap.cidToHash[clientId] = hash;
|
1112
|
+
|
1113
|
+
// If the server returns a hash, we assume that the server's version
|
1114
|
+
// of the data supercedes the local changes.
|
1115
|
+
record.beginPropertyChanges();
|
1116
|
+
record.send('didChangeData');
|
1117
|
+
recordData.adapterDidUpdate(hash);
|
1118
|
+
record.hashWasUpdated();
|
1119
|
+
record.endPropertyChanges();
|
986
1120
|
|
987
|
-
for (var i=0, l=get(array, 'length'); i<l; i++) {
|
988
|
-
var model = array[i], hash = hashes[i];
|
989
1121
|
id = hash[primaryKey];
|
990
|
-
clientId = get(model, 'clientId');
|
991
1122
|
|
992
|
-
|
993
|
-
|
1123
|
+
typeMap.idToCid[id] = clientId;
|
1124
|
+
this.clientIdToId[clientId] = id;
|
1125
|
+
} else {
|
1126
|
+
recordData.commit();
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
record.send('didCommit');
|
1130
|
+
},
|
994
1131
|
|
995
|
-
idToClientIdMap[id] = clientId;
|
996
|
-
idList.push(id);
|
997
1132
|
|
998
|
-
|
1133
|
+
didCreateRecords: function(type, array, hashes) {
|
1134
|
+
var primaryKey = type.proto().primaryKey,
|
1135
|
+
typeMap = this.typeMapFor(type),
|
1136
|
+
id, clientId;
|
1137
|
+
|
1138
|
+
for (var i=0, l=get(array, 'length'); i<l; i++) {
|
1139
|
+
var model = array[i], hash = hashes[i];
|
1140
|
+
clientId = get(model, 'clientId');
|
1141
|
+
|
1142
|
+
this._didCreateRecord(model, hash, typeMap, clientId, primaryKey);
|
999
1143
|
}
|
1000
1144
|
},
|
1001
1145
|
|
1002
1146
|
didCreateRecord: function(model, hash) {
|
1003
|
-
var type = model.constructor
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
var idToClientIdMap = this.idToClientIdMap(type);
|
1008
|
-
var data = this.clientIdToHashMap(type);
|
1009
|
-
var idList = this.idList(type);
|
1147
|
+
var type = model.constructor,
|
1148
|
+
typeMap = this.typeMapFor(type),
|
1149
|
+
id, clientId, primaryKey;
|
1010
1150
|
|
1011
1151
|
// The hash is optional, but if it is not provided, the client must have
|
1012
1152
|
// provided a primary key.
|
1013
1153
|
|
1014
|
-
primaryKey =
|
1154
|
+
primaryKey = type.proto().primaryKey;
|
1015
1155
|
|
1016
1156
|
// TODO: Make ember_assert more flexible and convert this into an ember_assert
|
1017
1157
|
if (hash) {
|
@@ -1020,21 +1160,9 @@ DS.Store = Ember.Object.extend({
|
|
1020
1160
|
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
1161
|
}
|
1022
1162
|
|
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
|
-
}
|
1031
|
-
|
1032
1163
|
clientId = get(model, 'clientId');
|
1033
1164
|
|
1034
|
-
|
1035
|
-
idList.push(id);
|
1036
|
-
|
1037
|
-
model.send('didCommit');
|
1165
|
+
this._didCreateRecord(model, hash, typeMap, clientId, primaryKey);
|
1038
1166
|
},
|
1039
1167
|
|
1040
1168
|
recordWasInvalid: function(record, errors) {
|
@@ -1046,15 +1174,15 @@ DS.Store = Ember.Object.extend({
|
|
1046
1174
|
// ................
|
1047
1175
|
|
1048
1176
|
registerModelArray: function(array, type, filter) {
|
1049
|
-
var modelArrays =
|
1177
|
+
var modelArrays = this.typeMapFor(type).modelArrays;
|
1050
1178
|
|
1051
1179
|
modelArrays.push(array);
|
1052
1180
|
|
1053
1181
|
this.updateModelArrayFilter(array, type, filter);
|
1054
1182
|
},
|
1055
1183
|
|
1056
|
-
|
1057
|
-
var array = DS.
|
1184
|
+
createManyArray: function(type, clientIds) {
|
1185
|
+
var array = DS.ManyArray.create({ type: type, content: clientIds, store: this });
|
1058
1186
|
|
1059
1187
|
clientIds.forEach(function(clientId) {
|
1060
1188
|
var modelArrays = this.modelArraysForClientId(clientId);
|
@@ -1065,41 +1193,46 @@ DS.Store = Ember.Object.extend({
|
|
1065
1193
|
},
|
1066
1194
|
|
1067
1195
|
updateModelArrayFilter: function(array, type, filter) {
|
1068
|
-
var
|
1069
|
-
|
1196
|
+
var typeMap = this.typeMapFor(type),
|
1197
|
+
dataCache = typeMap.cidToHash,
|
1198
|
+
clientIds = typeMap.clientIds,
|
1199
|
+
clientId, hash, proxy;
|
1200
|
+
|
1201
|
+
var recordCache = get(this, 'recordCache'), record;
|
1070
1202
|
|
1071
|
-
for (var i=0, l=
|
1072
|
-
clientId =
|
1203
|
+
for (var i=0, l=clientIds.length; i<l; i++) {
|
1204
|
+
clientId = clientIds[i];
|
1073
1205
|
|
1074
|
-
hash =
|
1206
|
+
if (hash = dataCache[clientId]) {
|
1207
|
+
if (record = recordCache[clientId]) {
|
1208
|
+
proxy = get(record, 'data');
|
1209
|
+
} else {
|
1210
|
+
DATA_PROXY.savedData = hash;
|
1211
|
+
proxy = DATA_PROXY;
|
1212
|
+
}
|
1075
1213
|
|
1076
|
-
|
1077
|
-
this.updateModelArray(array, filter, type, clientId, hash);
|
1214
|
+
this.updateModelArray(array, filter, type, clientId, proxy);
|
1078
1215
|
}
|
1079
1216
|
}
|
1080
1217
|
},
|
1081
1218
|
|
1082
|
-
updateModelArrays: function(type, clientId,
|
1083
|
-
var modelArrays =
|
1219
|
+
updateModelArrays: function(type, clientId, dataProxy) {
|
1220
|
+
var modelArrays = this.typeMapFor(type).modelArrays,
|
1084
1221
|
modelArrayType, filter;
|
1085
1222
|
|
1086
1223
|
modelArrays.forEach(function(array) {
|
1087
|
-
modelArrayType = get(array, 'type');
|
1088
1224
|
filter = get(array, 'filterFunction');
|
1089
|
-
|
1090
|
-
if (type !== modelArrayType) { return; }
|
1091
|
-
|
1092
|
-
this.updateModelArray(array, filter, type, clientId, hash);
|
1225
|
+
this.updateModelArray(array, filter, type, clientId, dataProxy);
|
1093
1226
|
}, this);
|
1094
1227
|
},
|
1095
1228
|
|
1096
|
-
updateModelArray: function(array, filter, type, clientId,
|
1229
|
+
updateModelArray: function(array, filter, type, clientId, dataProxy) {
|
1097
1230
|
var shouldBeInArray;
|
1098
1231
|
|
1099
1232
|
if (!filter) {
|
1100
1233
|
shouldBeInArray = true;
|
1101
1234
|
} else {
|
1102
|
-
shouldBeInArray = filter(
|
1235
|
+
shouldBeInArray = filter(dataProxy);
|
1103
1236
|
}
|
1104
1237
|
|
1105
1238
|
var content = get(array, 'content');
|
@@ -1127,44 +1260,39 @@ DS.Store = Ember.Object.extend({
|
|
1127
1260
|
},
|
1128
1261
|
|
1129
1262
|
// ............
|
1130
|
-
// .
|
1263
|
+
// . INDEXING .
|
1131
1264
|
// ............
|
1132
1265
|
|
1266
|
+
modelArraysForClientId: function(clientId) {
|
1267
|
+
var modelArrays = get(this, 'modelArraysByClientId');
|
1268
|
+
var ret = modelArrays[clientId];
|
1269
|
+
|
1270
|
+
if (!ret) {
|
1271
|
+
ret = modelArrays[clientId] = Ember.OrderedSet.create();
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
return ret;
|
1275
|
+
},
|
1276
|
+
|
1133
1277
|
typeMapFor: function(type) {
|
1134
|
-
var
|
1278
|
+
var typeMaps = get(this, 'typeMaps');
|
1135
1279
|
var guidForType = Ember.guidFor(type);
|
1136
1280
|
|
1137
|
-
var typeMap =
|
1281
|
+
var typeMap = typeMaps[guidForType];
|
1138
1282
|
|
1139
1283
|
if (typeMap) {
|
1140
1284
|
return typeMap;
|
1141
1285
|
} else {
|
1142
|
-
return (
|
1286
|
+
return (typeMaps[guidForType] =
|
1143
1287
|
{
|
1144
1288
|
idToCid: {},
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1289
|
+
clientIds: [],
|
1290
|
+
cidToHash: {},
|
1291
|
+
modelArrays: []
|
1148
1292
|
});
|
1149
1293
|
}
|
1150
1294
|
},
|
1151
1295
|
|
1152
|
-
idToClientIdMap: function(type) {
|
1153
|
-
return this.typeMapFor(type).idToCid;
|
1154
|
-
},
|
1155
|
-
|
1156
|
-
idList: function(type) {
|
1157
|
-
return this.typeMapFor(type).idList;
|
1158
|
-
},
|
1159
|
-
|
1160
|
-
clientIdList: function(type) {
|
1161
|
-
return this.typeMapFor(type).cidList;
|
1162
|
-
},
|
1163
|
-
|
1164
|
-
clientIdToHashMap: function(type) {
|
1165
|
-
return this.typeMapFor(type).cidToHash;
|
1166
|
-
},
|
1167
|
-
|
1168
1296
|
/** @private
|
1169
1297
|
|
1170
1298
|
For a given type and id combination, returns the client id used by the store.
|
@@ -1177,13 +1305,6 @@ DS.Store = Ember.Object.extend({
|
|
1177
1305
|
return this.typeMapFor(type).idToCid[id];
|
1178
1306
|
},
|
1179
1307
|
|
1180
|
-
idForHash: function(type, hash) {
|
1181
|
-
var primaryKey = getPath(type, 'proto.primaryKey');
|
1182
|
-
|
1183
|
-
ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
|
1184
|
-
return hash[primaryKey];
|
1185
|
-
},
|
1186
|
-
|
1187
1308
|
// ................
|
1188
1309
|
// . LOADING DATA .
|
1189
1310
|
// ................
|
@@ -1203,28 +1324,29 @@ DS.Store = Ember.Object.extend({
|
|
1203
1324
|
load: function(type, id, hash) {
|
1204
1325
|
if (hash === undefined) {
|
1205
1326
|
hash = id;
|
1206
|
-
var primaryKey =
|
1207
|
-
ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.",
|
1327
|
+
var primaryKey = type.proto().primaryKey;
|
1328
|
+
ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", primaryKey in hash);
|
1208
1329
|
id = hash[primaryKey];
|
1209
1330
|
}
|
1210
1331
|
|
1211
|
-
var
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1332
|
+
var typeMap = this.typeMapFor(type),
|
1333
|
+
dataCache = typeMap.cidToHash,
|
1334
|
+
clientId = typeMap.idToCid[id],
|
1335
|
+
recordCache = get(this, 'recordCache');
|
1215
1336
|
|
1216
1337
|
if (clientId !== undefined) {
|
1217
|
-
|
1338
|
+
dataCache[clientId] = hash;
|
1218
1339
|
|
1219
|
-
var model =
|
1340
|
+
var model = recordCache[clientId];
|
1220
1341
|
if (model) {
|
1221
|
-
model.send('
|
1342
|
+
model.send('didChangeData');
|
1222
1343
|
}
|
1223
1344
|
} else {
|
1224
1345
|
clientId = this.pushHash(hash, id, type);
|
1225
1346
|
}
|
1226
1347
|
|
1227
|
-
|
1348
|
+
DATA_PROXY.savedData = hash;
|
1349
|
+
this.updateModelArrays(type, clientId, DATA_PROXY);
|
1228
1350
|
|
1229
1351
|
return { id: id, clientId: clientId };
|
1230
1352
|
},
|
@@ -1235,10 +1357,9 @@ DS.Store = Ember.Object.extend({
|
|
1235
1357
|
if (hashes === undefined) {
|
1236
1358
|
hashes = ids;
|
1237
1359
|
ids = [];
|
1238
|
-
var primaryKey =
|
1360
|
+
var primaryKey = type.proto().primaryKey;
|
1239
1361
|
|
1240
|
-
ids =
|
1241
|
-
ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
|
1362
|
+
ids = Ember.ArrayUtils.map(hashes, function(hash) {
|
1242
1363
|
return hash[primaryKey];
|
1243
1364
|
});
|
1244
1365
|
}
|
@@ -1262,23 +1383,25 @@ DS.Store = Ember.Object.extend({
|
|
1262
1383
|
@returns {Number}
|
1263
1384
|
*/
|
1264
1385
|
pushHash: function(hash, id, type) {
|
1265
|
-
var
|
1266
|
-
|
1267
|
-
var
|
1268
|
-
|
1386
|
+
var typeMap = this.typeMapFor(type);
|
1387
|
+
|
1388
|
+
var idToClientIdMap = typeMap.idToCid,
|
1389
|
+
clientIdToIdMap = this.clientIdToId,
|
1390
|
+
clientIds = typeMap.clientIds,
|
1391
|
+
dataCache = typeMap.cidToHash;
|
1269
1392
|
|
1270
|
-
var clientId = this.
|
1393
|
+
var clientId = ++this.clientIdCounter;
|
1271
1394
|
|
1272
|
-
|
1395
|
+
dataCache[clientId] = hash;
|
1273
1396
|
|
1274
1397
|
// if we're creating an item, this process will be done
|
1275
1398
|
// later, once the object has been persisted.
|
1276
1399
|
if (id) {
|
1277
1400
|
idToClientIdMap[id] = clientId;
|
1278
|
-
|
1401
|
+
clientIdToIdMap[clientId] = id;
|
1279
1402
|
}
|
1280
1403
|
|
1281
|
-
|
1404
|
+
clientIds.push(clientId);
|
1282
1405
|
|
1283
1406
|
return clientId;
|
1284
1407
|
},
|
@@ -1290,8 +1413,13 @@ DS.Store = Ember.Object.extend({
|
|
1290
1413
|
materializeRecord: function(type, clientId) {
|
1291
1414
|
var model;
|
1292
1415
|
|
1293
|
-
get(this, '
|
1294
|
-
|
1416
|
+
get(this, 'recordCache')[clientId] = model = type._create({
|
1417
|
+
store: this,
|
1418
|
+
clientId: clientId
|
1419
|
+
});
|
1420
|
+
|
1421
|
+
get(this, 'defaultTransaction').adoptRecord(model);
|
1422
|
+
|
1295
1423
|
model.send('loadingData');
|
1296
1424
|
return model;
|
1297
1425
|
},
|
@@ -1305,7 +1433,6 @@ DS.Store = Ember.Object.extend({
|
|
1305
1433
|
}
|
1306
1434
|
});
|
1307
1435
|
|
1308
|
-
|
1309
1436
|
})({});
|
1310
1437
|
|
1311
1438
|
|
@@ -1327,6 +1454,14 @@ var isEmptyObject = function(object) {
|
|
1327
1454
|
return true;
|
1328
1455
|
};
|
1329
1456
|
|
1457
|
+
var hasDefinedProperties = function(object) {
|
1458
|
+
for (var name in object) {
|
1459
|
+
if (object.hasOwnProperty(name) && object[name]) { return true; }
|
1460
|
+
}
|
1461
|
+
|
1462
|
+
return false;
|
1463
|
+
};
|
1464
|
+
|
1330
1465
|
DS.State = Ember.State.extend({
|
1331
1466
|
isLoaded: stateProperty,
|
1332
1467
|
isDirty: stateProperty,
|
@@ -1344,27 +1479,21 @@ DS.State = Ember.State.extend({
|
|
1344
1479
|
dirtyType: stateProperty
|
1345
1480
|
});
|
1346
1481
|
|
1347
|
-
var isEmptyObject = function(obj) {
|
1348
|
-
for (var prop in obj) {
|
1349
|
-
if (!obj.hasOwnProperty(prop)) { continue; }
|
1350
|
-
return false;
|
1351
|
-
}
|
1352
|
-
|
1353
|
-
return true;
|
1354
|
-
};
|
1355
|
-
|
1356
1482
|
var setProperty = function(manager, context) {
|
1357
1483
|
var key = context.key, value = context.value;
|
1358
1484
|
|
1359
1485
|
var model = get(manager, 'model'),
|
1360
1486
|
data = get(model, 'data');
|
1361
1487
|
|
1362
|
-
data
|
1488
|
+
set(data, key, value);
|
1489
|
+
};
|
1490
|
+
|
1491
|
+
var didChangeData = function(manager) {
|
1492
|
+
var model = get(manager, 'model'),
|
1493
|
+
data = get(model, 'data');
|
1363
1494
|
|
1364
|
-
|
1365
|
-
|
1366
|
-
// to determine membership.
|
1367
|
-
Ember.run.once(model, model.notifyHashWasUpdated);
|
1495
|
+
data._savedData = null;
|
1496
|
+
model.notifyPropertyChange('data');
|
1368
1497
|
};
|
1369
1498
|
|
1370
1499
|
// The waitingOn event shares common functionality
|
@@ -1425,6 +1554,48 @@ var waitingOn = function(manager, object) {
|
|
1425
1554
|
// `isPending` property on all children will become `false`
|
1426
1555
|
// and the transaction will try to commit the records.
|
1427
1556
|
|
1557
|
+
// This mixin is mixed into various uncommitted states. Make
|
1558
|
+
// sure to mix it in *after* the class definition, so its
|
1559
|
+
// super points to the class definition.
|
1560
|
+
var Uncommitted = Ember.Mixin.create({
|
1561
|
+
setProperty: setProperty,
|
1562
|
+
|
1563
|
+
deleteRecord: function(manager) {
|
1564
|
+
this._super(manager);
|
1565
|
+
|
1566
|
+
var model = get(manager, 'model'),
|
1567
|
+
dirtyType = get(this, 'dirtyType');
|
1568
|
+
|
1569
|
+
model.withTransaction(function(t) {
|
1570
|
+
t.modelBecameClean(dirtyType, model);
|
1571
|
+
});
|
1572
|
+
}
|
1573
|
+
});
|
1574
|
+
|
1575
|
+
// These mixins are mixed into substates of the concrete
|
1576
|
+
// subclasses of DirtyState.
|
1577
|
+
|
1578
|
+
var CreatedUncommitted = Ember.Mixin.create({
|
1579
|
+
deleteRecord: function(manager) {
|
1580
|
+
this._super(manager);
|
1581
|
+
|
1582
|
+
manager.goToState('deleted.saved');
|
1583
|
+
}
|
1584
|
+
});
|
1585
|
+
|
1586
|
+
var UpdatedUncommitted = Ember.Mixin.create({
|
1587
|
+
deleteRecord: function(manager) {
|
1588
|
+
this._super(manager);
|
1589
|
+
|
1590
|
+
var model = get(manager, 'model');
|
1591
|
+
|
1592
|
+
model.withTransaction(function(t) {
|
1593
|
+
t.modelBecameClean('created', model);
|
1594
|
+
});
|
1595
|
+
|
1596
|
+
manager.goToState('deleted');
|
1597
|
+
}
|
1598
|
+
});
|
1428
1599
|
|
1429
1600
|
// The dirty state is a abstract state whose functionality is
|
1430
1601
|
// shared between the `created` and `updated` states.
|
@@ -1460,11 +1631,7 @@ var DirtyState = DS.State.extend({
|
|
1460
1631
|
},
|
1461
1632
|
|
1462
1633
|
// EVENTS
|
1463
|
-
|
1464
|
-
|
1465
|
-
deleteRecord: function(manager) {
|
1466
|
-
manager.goToState('deleted');
|
1467
|
-
},
|
1634
|
+
deleteRecord: Ember.K,
|
1468
1635
|
|
1469
1636
|
waitingOn: function(manager, object) {
|
1470
1637
|
waitingOn(manager, object);
|
@@ -1474,7 +1641,7 @@ var DirtyState = DS.State.extend({
|
|
1474
1641
|
willCommit: function(manager) {
|
1475
1642
|
manager.goToState('inFlight');
|
1476
1643
|
}
|
1477
|
-
}),
|
1644
|
+
}, Uncommitted),
|
1478
1645
|
|
1479
1646
|
// Once a record has been handed off to the adapter to be
|
1480
1647
|
// saved, it is in the 'in flight' state. Changes to the
|
@@ -1505,10 +1672,7 @@ var DirtyState = DS.State.extend({
|
|
1505
1672
|
manager.goToState('invalid');
|
1506
1673
|
},
|
1507
1674
|
|
1508
|
-
|
1509
|
-
var model = get(manager, 'model');
|
1510
|
-
set(model, 'data', hash);
|
1511
|
-
}
|
1675
|
+
didChangeData: didChangeData
|
1512
1676
|
}),
|
1513
1677
|
|
1514
1678
|
// If a record becomes associated with a newly created
|
@@ -1533,8 +1697,6 @@ var DirtyState = DS.State.extend({
|
|
1533
1697
|
// started to commit is in this state.
|
1534
1698
|
uncommitted: DS.State.extend({
|
1535
1699
|
// EVENTS
|
1536
|
-
setProperty: setProperty,
|
1537
|
-
|
1538
1700
|
deleteRecord: function(manager) {
|
1539
1701
|
var model = get(manager, 'model'),
|
1540
1702
|
pendingQueue = get(model, 'pendingQueue'),
|
@@ -1548,8 +1710,6 @@ var DirtyState = DS.State.extend({
|
|
1548
1710
|
tuple = pendingQueue[prop];
|
1549
1711
|
Ember.removeObserver(tuple[0], 'id', tuple[1]);
|
1550
1712
|
}
|
1551
|
-
|
1552
|
-
manager.goToState('deleted');
|
1553
1713
|
},
|
1554
1714
|
|
1555
1715
|
willCommit: function(manager) {
|
@@ -1572,7 +1732,7 @@ var DirtyState = DS.State.extend({
|
|
1572
1732
|
var dirtyType = get(this, 'dirtyType');
|
1573
1733
|
manager.goToState(dirtyType + '.uncommitted');
|
1574
1734
|
}
|
1575
|
-
}),
|
1735
|
+
}, Uncommitted),
|
1576
1736
|
|
1577
1737
|
// A pending record whose transaction has started
|
1578
1738
|
// to commit is in this state. Since it has not yet
|
@@ -1596,6 +1756,15 @@ var DirtyState = DS.State.extend({
|
|
1596
1756
|
},
|
1597
1757
|
|
1598
1758
|
doneWaiting: function(manager) {
|
1759
|
+
var model = get(manager, 'model'),
|
1760
|
+
transaction = get(model, 'transaction');
|
1761
|
+
|
1762
|
+
// Now that the model is no longer pending, schedule
|
1763
|
+
// the transaction to commit.
|
1764
|
+
Ember.run.once(transaction, transaction.commit);
|
1765
|
+
},
|
1766
|
+
|
1767
|
+
willCommit: function(manager) {
|
1599
1768
|
var dirtyType = get(this, 'dirtyType');
|
1600
1769
|
manager.goToState(dirtyType + '.inFlight');
|
1601
1770
|
}
|
@@ -1623,7 +1792,7 @@ var DirtyState = DS.State.extend({
|
|
1623
1792
|
|
1624
1793
|
delete errors[key];
|
1625
1794
|
|
1626
|
-
if (
|
1795
|
+
if (!hasDefinedProperties(errors)) {
|
1627
1796
|
manager.send('becameValid');
|
1628
1797
|
}
|
1629
1798
|
},
|
@@ -1634,6 +1803,41 @@ var DirtyState = DS.State.extend({
|
|
1634
1803
|
})
|
1635
1804
|
});
|
1636
1805
|
|
1806
|
+
// The created and updated states are created outside the state
|
1807
|
+
// chart so we can reopen their substates and add mixins as
|
1808
|
+
// necessary.
|
1809
|
+
|
1810
|
+
var createdState = DirtyState.create({
|
1811
|
+
dirtyType: 'created',
|
1812
|
+
|
1813
|
+
// FLAGS
|
1814
|
+
isNew: true,
|
1815
|
+
|
1816
|
+
// EVENTS
|
1817
|
+
invokeLifecycleCallbacks: function(manager, model) {
|
1818
|
+
model.didCreate();
|
1819
|
+
}
|
1820
|
+
});
|
1821
|
+
|
1822
|
+
var updatedState = DirtyState.create({
|
1823
|
+
dirtyType: 'updated',
|
1824
|
+
|
1825
|
+
// EVENTS
|
1826
|
+
invokeLifecycleCallbacks: function(manager, model) {
|
1827
|
+
model.didUpdate();
|
1828
|
+
}
|
1829
|
+
});
|
1830
|
+
|
1831
|
+
// The created.uncommitted state and created.pending.uncommitted share
|
1832
|
+
// some logic defined in CreatedUncommitted.
|
1833
|
+
createdState.states.uncommitted.reopen(CreatedUncommitted);
|
1834
|
+
createdState.states.pending.states.uncommitted.reopen(CreatedUncommitted);
|
1835
|
+
|
1836
|
+
// The updated.uncommitted state and updated.pending.uncommitted share
|
1837
|
+
// some logic defined in UpdatedUncommitted.
|
1838
|
+
updatedState.states.uncommitted.reopen(UpdatedUncommitted);
|
1839
|
+
updatedState.states.pending.states.uncommitted.reopen(UpdatedUncommitted);
|
1840
|
+
|
1637
1841
|
var states = {
|
1638
1842
|
rootState: Ember.State.create({
|
1639
1843
|
// FLAGS
|
@@ -1659,9 +1863,9 @@ var states = {
|
|
1659
1863
|
manager.goToState('loading');
|
1660
1864
|
},
|
1661
1865
|
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1866
|
+
didChangeData: function(manager) {
|
1867
|
+
didChangeData(manager);
|
1868
|
+
|
1665
1869
|
manager.goToState('loaded.created');
|
1666
1870
|
}
|
1667
1871
|
}),
|
@@ -1680,17 +1884,9 @@ var states = {
|
|
1680
1884
|
},
|
1681
1885
|
|
1682
1886
|
// EVENTS
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
model.beginPropertyChanges();
|
1687
|
-
set(model, 'data', data);
|
1688
|
-
|
1689
|
-
if (data !== null) {
|
1690
|
-
manager.send('loadedData');
|
1691
|
-
}
|
1692
|
-
|
1693
|
-
model.endPropertyChanges();
|
1887
|
+
didChangeData: function(manager, data) {
|
1888
|
+
didChangeData(manager);
|
1889
|
+
manager.send('loadedData');
|
1694
1890
|
},
|
1695
1891
|
|
1696
1892
|
loadedData: function(manager) {
|
@@ -1718,6 +1914,8 @@ var states = {
|
|
1718
1914
|
manager.goToState('updated');
|
1719
1915
|
},
|
1720
1916
|
|
1917
|
+
didChangeData: didChangeData,
|
1918
|
+
|
1721
1919
|
deleteRecord: function(manager) {
|
1722
1920
|
manager.goToState('deleted');
|
1723
1921
|
},
|
@@ -1731,29 +1929,12 @@ var states = {
|
|
1731
1929
|
// A record is in this state after it has been locally
|
1732
1930
|
// created but before the adapter has indicated that
|
1733
1931
|
// it has been saved.
|
1734
|
-
created:
|
1735
|
-
dirtyType: 'created',
|
1736
|
-
|
1737
|
-
// FLAGS
|
1738
|
-
isNew: true,
|
1739
|
-
|
1740
|
-
// EVENTS
|
1741
|
-
invokeLifecycleCallbacks: function(manager, model) {
|
1742
|
-
model.didCreate();
|
1743
|
-
}
|
1744
|
-
}),
|
1932
|
+
created: createdState,
|
1745
1933
|
|
1746
1934
|
// A record is in this state if it has already been
|
1747
1935
|
// saved to the server, but there are new local changes
|
1748
1936
|
// that have not yet been saved.
|
1749
|
-
updated:
|
1750
|
-
dirtyType: 'updated',
|
1751
|
-
|
1752
|
-
// EVENTS
|
1753
|
-
invokeLifecycleCallbacks: function(manager, model) {
|
1754
|
-
model.didUpdate();
|
1755
|
-
}
|
1756
|
-
})
|
1937
|
+
updated: updatedState
|
1757
1938
|
}),
|
1758
1939
|
|
1759
1940
|
// A record is in this state if it was deleted from the store.
|
@@ -1763,26 +1944,27 @@ var states = {
|
|
1763
1944
|
isLoaded: true,
|
1764
1945
|
isDirty: true,
|
1765
1946
|
|
1766
|
-
// TRANSITIONS
|
1767
|
-
enter: function(manager) {
|
1768
|
-
var model = get(manager, 'model');
|
1769
|
-
var store = get(model, 'store');
|
1770
|
-
|
1771
|
-
if (store) {
|
1772
|
-
store.removeFromModelArrays(model);
|
1773
|
-
}
|
1774
|
-
|
1775
|
-
model.withTransaction(function(t) {
|
1776
|
-
t.modelBecameDirty('deleted', model);
|
1777
|
-
});
|
1778
|
-
},
|
1779
|
-
|
1780
1947
|
// SUBSTATES
|
1781
1948
|
|
1782
1949
|
// When a record is deleted, it enters the `start`
|
1783
1950
|
// state. It will exit this state when the record's
|
1784
1951
|
// transaction starts to commit.
|
1785
1952
|
start: DS.State.create({
|
1953
|
+
// TRANSITIONS
|
1954
|
+
enter: function(manager) {
|
1955
|
+
var model = get(manager, 'model');
|
1956
|
+
var store = get(model, 'store');
|
1957
|
+
|
1958
|
+
if (store) {
|
1959
|
+
store.removeFromModelArrays(model);
|
1960
|
+
}
|
1961
|
+
|
1962
|
+
model.withTransaction(function(t) {
|
1963
|
+
t.modelBecameDirty('deleted', model);
|
1964
|
+
});
|
1965
|
+
},
|
1966
|
+
|
1967
|
+
// EVENTS
|
1786
1968
|
willCommit: function(manager) {
|
1787
1969
|
manager.goToState('inFlight');
|
1788
1970
|
}
|
@@ -1815,6 +1997,7 @@ var states = {
|
|
1815
1997
|
// been saved, the record enters the `saved` substate
|
1816
1998
|
// of `deleted`.
|
1817
1999
|
saved: DS.State.create({
|
2000
|
+
// FLAGS
|
1818
2001
|
isDirty: false
|
1819
2002
|
})
|
1820
2003
|
}),
|
@@ -1838,12 +2021,103 @@ DS.StateManager = Ember.StateManager.extend({
|
|
1838
2021
|
|
1839
2022
|
|
1840
2023
|
(function(exports) {
|
1841
|
-
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
2024
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, none = Ember.none;
|
1842
2025
|
|
1843
2026
|
var retrieveFromCurrentState = Ember.computed(function(key) {
|
1844
2027
|
return get(getPath(this, 'stateManager.currentState'), key);
|
1845
2028
|
}).property('stateManager.currentState').cacheable();
|
1846
2029
|
|
2030
|
+
// This object is a regular JS object for performance. It is only
|
2031
|
+
// used internally for bookkeeping purposes.
|
2032
|
+
var DataProxy = function(record) {
|
2033
|
+
this.record = record;
|
2034
|
+
this.unsavedData = {};
|
2035
|
+
this.associations = {};
|
2036
|
+
};
|
2037
|
+
|
2038
|
+
DataProxy.prototype = {
|
2039
|
+
get: function(key) { return Ember.get(this, key); },
|
2040
|
+
set: function(key, value) { return Ember.set(this, key, value); },
|
2041
|
+
|
2042
|
+
setAssociation: function(key, value) {
|
2043
|
+
this.associations[key] = value;
|
2044
|
+
},
|
2045
|
+
|
2046
|
+
savedData: function() {
|
2047
|
+
var savedData = this._savedData;
|
2048
|
+
if (savedData) { return savedData; }
|
2049
|
+
|
2050
|
+
var record = this.record,
|
2051
|
+
clientId = get(record, 'clientId'),
|
2052
|
+
store = get(record, 'store');
|
2053
|
+
|
2054
|
+
if (store) {
|
2055
|
+
savedData = store.dataForRecord(record);
|
2056
|
+
this._savedData = savedData;
|
2057
|
+
return savedData;
|
2058
|
+
}
|
2059
|
+
},
|
2060
|
+
|
2061
|
+
unknownProperty: function(key) {
|
2062
|
+
var unsavedData = this.unsavedData,
|
2063
|
+
associations = this.associations,
|
2064
|
+
savedData = this.savedData(),
|
2065
|
+
store;
|
2066
|
+
|
2067
|
+
var value = unsavedData[key], association;
|
2068
|
+
|
2069
|
+
// if this is a belongsTo association, this will
|
2070
|
+
// be a clientId.
|
2071
|
+
association = associations[key];
|
2072
|
+
|
2073
|
+
if (association !== undefined) {
|
2074
|
+
store = get(this.record, 'store');
|
2075
|
+
return store.clientIdToId[association];
|
2076
|
+
}
|
2077
|
+
|
2078
|
+
if (savedData && value === undefined) {
|
2079
|
+
value = savedData[key];
|
2080
|
+
}
|
2081
|
+
|
2082
|
+
return value;
|
2083
|
+
},
|
2084
|
+
|
2085
|
+
setUnknownProperty: function(key, value) {
|
2086
|
+
var record = this.record,
|
2087
|
+
unsavedData = this.unsavedData;
|
2088
|
+
|
2089
|
+
unsavedData[key] = value;
|
2090
|
+
|
2091
|
+
record.hashWasUpdated();
|
2092
|
+
|
2093
|
+
return value;
|
2094
|
+
},
|
2095
|
+
|
2096
|
+
commit: function() {
|
2097
|
+
var record = this.record;
|
2098
|
+
|
2099
|
+
var unsavedData = this.unsavedData;
|
2100
|
+
var savedData = this.savedData();
|
2101
|
+
|
2102
|
+
for (var prop in unsavedData) {
|
2103
|
+
if (unsavedData.hasOwnProperty(prop)) {
|
2104
|
+
savedData[prop] = unsavedData[prop];
|
2105
|
+
delete unsavedData[prop];
|
2106
|
+
}
|
2107
|
+
}
|
2108
|
+
|
2109
|
+
record.notifyPropertyChange('data');
|
2110
|
+
},
|
2111
|
+
|
2112
|
+
rollback: function() {
|
2113
|
+
this.unsavedData = {};
|
2114
|
+
},
|
2115
|
+
|
2116
|
+
adapterDidUpdate: function(data) {
|
2117
|
+
this.unsavedData = {};
|
2118
|
+
}
|
2119
|
+
};
|
2120
|
+
|
1847
2121
|
DS.Model = Ember.Object.extend({
|
1848
2122
|
isLoaded: retrieveFromCurrentState,
|
1849
2123
|
isDirty: retrieveFromCurrentState,
|
@@ -1855,6 +2129,10 @@ DS.Model = Ember.Object.extend({
|
|
1855
2129
|
isValid: retrieveFromCurrentState,
|
1856
2130
|
|
1857
2131
|
clientId: null,
|
2132
|
+
transaction: null,
|
2133
|
+
stateManager: null,
|
2134
|
+
pendingQueue: null,
|
2135
|
+
errors: null,
|
1858
2136
|
|
1859
2137
|
// because unknownProperty is used, any internal property
|
1860
2138
|
// must be initialized here.
|
@@ -1871,10 +2149,159 @@ DS.Model = Ember.Object.extend({
|
|
1871
2149
|
return data && get(data, primaryKey);
|
1872
2150
|
}).property('primaryKey', 'data'),
|
1873
2151
|
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
2152
|
+
// The following methods are callbacks invoked by `toJSON`. You
|
2153
|
+
// can override one of the callbacks to override specific behavior,
|
2154
|
+
// or toJSON itself.
|
2155
|
+
//
|
2156
|
+
// If you override toJSON, you can invoke these callbacks manually
|
2157
|
+
// to get the default behavior.
|
2158
|
+
|
2159
|
+
/**
|
2160
|
+
Add the record's primary key to the JSON hash.
|
2161
|
+
|
2162
|
+
The default implementation uses the record's specified `primaryKey`
|
2163
|
+
and the `id` computed property, which are passed in as parameters.
|
2164
|
+
|
2165
|
+
@param {Object} json the JSON hash being built
|
2166
|
+
@param {Number|String} id the record's id
|
2167
|
+
@param {String} key the primaryKey for the record
|
2168
|
+
*/
|
2169
|
+
addIdToJSON: function(json, id, key) {
|
2170
|
+
if (id) { json[key] = id; }
|
2171
|
+
},
|
2172
|
+
|
2173
|
+
/**
|
2174
|
+
Add the attributes' current values to the JSON hash.
|
2175
|
+
|
2176
|
+
The default implementation gets the current value of each
|
2177
|
+
attribute from the `data`, and uses a `defaultValue` if
|
2178
|
+
specified in the `DS.attr` definition.
|
2179
|
+
|
2180
|
+
@param {Object} json the JSON hash being build
|
2181
|
+
@param {Ember.Map} attributes a Map of attributes
|
2182
|
+
@param {DataProxy} data the record's data, accessed with `get` and `set`.
|
2183
|
+
*/
|
2184
|
+
addAttributesToJSON: function(json, attributes, data) {
|
2185
|
+
attributes.forEach(function(name, meta) {
|
2186
|
+
var key = meta.key(this.constructor),
|
2187
|
+
value = get(data, key);
|
2188
|
+
|
2189
|
+
if (value === undefined) {
|
2190
|
+
value = meta.options.defaultValue;
|
2191
|
+
}
|
2192
|
+
|
2193
|
+
json[key] = value;
|
2194
|
+
}, this);
|
2195
|
+
},
|
2196
|
+
|
2197
|
+
/**
|
2198
|
+
Add the value of a `hasMany` association to the JSON hash.
|
2199
|
+
|
2200
|
+
The default implementation honors the `embedded` option
|
2201
|
+
passed to `DS.hasMany`. If embedded, `toJSON` is recursively
|
2202
|
+
called on the child records. If not, the `id` of each
|
2203
|
+
record is added.
|
2204
|
+
|
2205
|
+
Note that if a record is not embedded and does not
|
2206
|
+
yet have an `id` (usually provided by the server), it
|
2207
|
+
will not be included in the output.
|
2208
|
+
|
2209
|
+
@param {Object} json the JSON hash being built
|
2210
|
+
@param {DataProxy} data the record's data, accessed with `get` and `set`.
|
2211
|
+
@param {Object} meta information about the association
|
2212
|
+
@param {Object} options options passed to `toJSON`
|
2213
|
+
*/
|
2214
|
+
addHasManyToJSON: function(json, data, meta, options) {
|
2215
|
+
var key = meta.key,
|
2216
|
+
manyArray = get(this, key),
|
2217
|
+
records = [],
|
2218
|
+
clientId, id;
|
2219
|
+
|
2220
|
+
if (meta.options.embedded) {
|
2221
|
+
// TODO: Avoid materializing embedded hashes if possible
|
2222
|
+
manyArray.forEach(function(record) {
|
2223
|
+
records.push(record.toJSON(options));
|
2224
|
+
});
|
2225
|
+
} else {
|
2226
|
+
var clientIds = get(manyArray, 'content');
|
2227
|
+
|
2228
|
+
for (var i=0, l=clientIds.length; i<l; i++) {
|
2229
|
+
clientId = clientIds[i];
|
2230
|
+
id = get(this, 'store').clientIdToId[clientId];
|
2231
|
+
|
2232
|
+
if (id !== undefined) {
|
2233
|
+
records.push(id);
|
2234
|
+
}
|
2235
|
+
}
|
2236
|
+
}
|
2237
|
+
|
2238
|
+
json[key] = records;
|
2239
|
+
},
|
2240
|
+
|
2241
|
+
/**
|
2242
|
+
Add the value of a `belongsTo` association to the JSON hash.
|
2243
|
+
|
2244
|
+
The default implementation always includes the `id`.
|
2245
|
+
|
2246
|
+
@param {Object} json the JSON hash being built
|
2247
|
+
@param {DataProxy} data the record's data, accessed with `get` and `set`.
|
2248
|
+
@param {Object} meta information about the association
|
2249
|
+
@param {Object} options options passed to `toJSON`
|
2250
|
+
*/
|
2251
|
+
addBelongsToToJSON: function(json, data, meta, options) {
|
2252
|
+
var key = meta.key, value, id;
|
2253
|
+
|
2254
|
+
if (options.embedded) {
|
2255
|
+
key = options.key || get(this, 'namingConvention').keyToJSONKey(key);
|
2256
|
+
value = get(data.record, key);
|
2257
|
+
json[key] = value ? value.toJSON(options) : null;
|
2258
|
+
} else {
|
2259
|
+
key = options.key || get(this, 'namingConvention').foreignKey(key);
|
2260
|
+
id = data.get(key);
|
2261
|
+
json[key] = none(id) ? null : id;
|
2262
|
+
}
|
2263
|
+
},
|
2264
|
+
/**
|
2265
|
+
Create a JSON representation of the record, including its `id`,
|
2266
|
+
attributes and associations. Honor any settings defined on the
|
2267
|
+
attributes or associations (such as `embedded` or `key`).
|
2268
|
+
*/
|
2269
|
+
toJSON: function(options) {
|
2270
|
+
var data = get(this, 'data'),
|
2271
|
+
result = {},
|
2272
|
+
type = this.constructor,
|
2273
|
+
attributes = get(type, 'attributes'),
|
2274
|
+
primaryKey = get(this, 'primaryKey'),
|
2275
|
+
id = get(this, 'id'),
|
2276
|
+
store = get(this, 'store'),
|
2277
|
+
associations;
|
2278
|
+
|
2279
|
+
options = options || {};
|
2280
|
+
|
2281
|
+
// delegate to `addIdToJSON` callback
|
2282
|
+
this.addIdToJSON(result, id, primaryKey);
|
2283
|
+
|
2284
|
+
// delegate to `addAttributesToJSON` callback
|
2285
|
+
this.addAttributesToJSON(result, attributes, data);
|
2286
|
+
|
2287
|
+
associations = get(type, 'associationsByName');
|
2288
|
+
|
2289
|
+
// add associations, delegating to `addHasManyToJSON` and
|
2290
|
+
// `addBelongsToToJSON`.
|
2291
|
+
associations.forEach(function(key, meta) {
|
2292
|
+
if (options.associations && meta.kind === 'hasMany') {
|
2293
|
+
this.addHasManyToJSON(result, data, meta, options);
|
2294
|
+
} else if (meta.kind === 'belongsTo') {
|
2295
|
+
this.addBelongsToToJSON(result, data, meta, options);
|
2296
|
+
}
|
2297
|
+
}, this);
|
2298
|
+
|
2299
|
+
return result;
|
2300
|
+
},
|
2301
|
+
|
2302
|
+
data: Ember.computed(function() {
|
2303
|
+
return new DataProxy(this);
|
2304
|
+
}).cacheable(),
|
1878
2305
|
|
1879
2306
|
didLoad: Ember.K,
|
1880
2307
|
didUpdate: Ember.K,
|
@@ -1886,6 +2313,7 @@ DS.Model = Ember.Object.extend({
|
|
1886
2313
|
});
|
1887
2314
|
|
1888
2315
|
set(this, 'pendingQueue', {});
|
2316
|
+
|
1889
2317
|
set(this, 'stateManager', stateManager);
|
1890
2318
|
stateManager.goToState('empty');
|
1891
2319
|
},
|
@@ -1902,7 +2330,7 @@ DS.Model = Ember.Object.extend({
|
|
1902
2330
|
},
|
1903
2331
|
|
1904
2332
|
withTransaction: function(fn) {
|
1905
|
-
var transaction = get(this, 'transaction')
|
2333
|
+
var transaction = get(this, 'transaction');
|
1906
2334
|
if (transaction) { fn(transaction); }
|
1907
2335
|
},
|
1908
2336
|
|
@@ -1921,7 +2349,7 @@ DS.Model = Ember.Object.extend({
|
|
1921
2349
|
notifyHashWasUpdated: function() {
|
1922
2350
|
var store = get(this, 'store');
|
1923
2351
|
if (store) {
|
1924
|
-
store.hashWasUpdated(this.constructor, get(this, 'clientId'));
|
2352
|
+
store.hashWasUpdated(this.constructor, get(this, 'clientId'), this);
|
1925
2353
|
}
|
1926
2354
|
},
|
1927
2355
|
|
@@ -1941,6 +2369,25 @@ DS.Model = Ember.Object.extend({
|
|
1941
2369
|
} else {
|
1942
2370
|
return this._super(key, value);
|
1943
2371
|
}
|
2372
|
+
},
|
2373
|
+
|
2374
|
+
namingConvention: {
|
2375
|
+
keyToJSONKey: function(key) {
|
2376
|
+
// TODO: Strip off `is` from the front. Example: `isHipster` becomes `hipster`
|
2377
|
+
return Ember.String.decamelize(key);
|
2378
|
+
},
|
2379
|
+
|
2380
|
+
foreignKey: function(key) {
|
2381
|
+
return Ember.String.decamelize(key) + '_id';
|
2382
|
+
}
|
2383
|
+
},
|
2384
|
+
|
2385
|
+
/** @private */
|
2386
|
+
hashWasUpdated: function() {
|
2387
|
+
// At the end of the run loop, notify model arrays that
|
2388
|
+
// this record has changed so they can re-evaluate its contents
|
2389
|
+
// to determine membership.
|
2390
|
+
Ember.run.once(this, this.notifyHashWasUpdated);
|
1944
2391
|
}
|
1945
2392
|
});
|
1946
2393
|
|
@@ -1976,6 +2423,30 @@ DS.Model.reopenClass({
|
|
1976
2423
|
|
1977
2424
|
(function(exports) {
|
1978
2425
|
var get = Ember.get, getPath = Ember.getPath;
|
2426
|
+
DS.Model.reopenClass({
|
2427
|
+
attributes: Ember.computed(function() {
|
2428
|
+
var map = Ember.Map.create();
|
2429
|
+
|
2430
|
+
this.eachComputedProperty(function(name, meta) {
|
2431
|
+
if (meta.isAttribute) { map.set(name, meta); }
|
2432
|
+
});
|
2433
|
+
|
2434
|
+
return map;
|
2435
|
+
}).cacheable(),
|
2436
|
+
|
2437
|
+
processAttributeKeys: function() {
|
2438
|
+
if (this.processedAttributeKeys) { return; }
|
2439
|
+
|
2440
|
+
var namingConvention = this.proto().namingConvention;
|
2441
|
+
|
2442
|
+
this.eachComputedProperty(function(name, meta) {
|
2443
|
+
if (meta.isAttribute && !meta.options.key) {
|
2444
|
+
meta.options.key = namingConvention.keyToJSONKey(name, this);
|
2445
|
+
}
|
2446
|
+
}, this);
|
2447
|
+
}
|
2448
|
+
});
|
2449
|
+
|
1979
2450
|
DS.attr = function(type, options) {
|
1980
2451
|
var transform = DS.attr.transforms[type];
|
1981
2452
|
ember_assert("Could not find model attribute of type " + type, !!transform);
|
@@ -1983,24 +2454,45 @@ DS.attr = function(type, options) {
|
|
1983
2454
|
var transformFrom = transform.from;
|
1984
2455
|
var transformTo = transform.to;
|
1985
2456
|
|
1986
|
-
|
1987
|
-
var data = get(this, 'data');
|
2457
|
+
options = options || {};
|
1988
2458
|
|
1989
|
-
|
2459
|
+
var meta = {
|
2460
|
+
type: type,
|
2461
|
+
isAttribute: true,
|
2462
|
+
options: options,
|
2463
|
+
|
2464
|
+
// this will ensure that the key always takes naming
|
2465
|
+
// conventions into consideration.
|
2466
|
+
key: function(recordType) {
|
2467
|
+
recordType.processAttributeKeys();
|
2468
|
+
return options.key;
|
2469
|
+
}
|
2470
|
+
};
|
1990
2471
|
|
1991
|
-
|
1992
|
-
|
2472
|
+
return Ember.computed(function(key, value) {
|
2473
|
+
var data;
|
1993
2474
|
|
1994
|
-
|
1995
|
-
} else {
|
1996
|
-
ember_assert("You cannot set a model attribute before its data is loaded.", !!data);
|
2475
|
+
key = meta.key(this.constructor);
|
1997
2476
|
|
2477
|
+
if (arguments.length === 2) {
|
1998
2478
|
value = transformTo(value);
|
1999
2479
|
this.setProperty(key, value);
|
2000
|
-
|
2480
|
+
} else {
|
2481
|
+
data = get(this, 'data');
|
2482
|
+
value = get(data, key);
|
2483
|
+
|
2484
|
+
if (value === undefined) {
|
2485
|
+
value = options.defaultValue;
|
2486
|
+
}
|
2001
2487
|
}
|
2002
|
-
|
2488
|
+
|
2489
|
+
return transformFrom(value);
|
2490
|
+
// `data` is never set directly. However, it may be
|
2491
|
+
// invalidated from the state manager's setData
|
2492
|
+
// event.
|
2493
|
+
}).property('data').cacheable().meta(meta);
|
2003
2494
|
};
|
2495
|
+
|
2004
2496
|
DS.attr.transforms = {
|
2005
2497
|
string: {
|
2006
2498
|
from: function(serialized) {
|
@@ -2012,7 +2504,7 @@ DS.attr.transforms = {
|
|
2012
2504
|
}
|
2013
2505
|
},
|
2014
2506
|
|
2015
|
-
|
2507
|
+
number: {
|
2016
2508
|
from: function(serialized) {
|
2017
2509
|
return Ember.none(serialized) ? null : Number(serialized);
|
2018
2510
|
},
|
@@ -2022,7 +2514,7 @@ DS.attr.transforms = {
|
|
2022
2514
|
}
|
2023
2515
|
},
|
2024
2516
|
|
2025
|
-
boolean: {
|
2517
|
+
'boolean': {
|
2026
2518
|
from: function(serialized) {
|
2027
2519
|
return Boolean(serialized);
|
2028
2520
|
},
|
@@ -2087,13 +2579,55 @@ DS.attr.transforms = {
|
|
2087
2579
|
(function(exports) {
|
2088
2580
|
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
2089
2581
|
DS.Model.reopenClass({
|
2090
|
-
typeForAssociation: function(
|
2091
|
-
var
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2095
|
-
|
2096
|
-
|
2582
|
+
typeForAssociation: function(name) {
|
2583
|
+
var association = get(this, 'associationsByName').get(name);
|
2584
|
+
return association && association.type;
|
2585
|
+
},
|
2586
|
+
|
2587
|
+
associations: Ember.computed(function() {
|
2588
|
+
var map = Ember.Map.create();
|
2589
|
+
|
2590
|
+
this.eachComputedProperty(function(name, meta) {
|
2591
|
+
if (meta.isAssociation) {
|
2592
|
+
var type = meta.type,
|
2593
|
+
typeList = map.get(type);
|
2594
|
+
|
2595
|
+
if (typeof type === 'string') {
|
2596
|
+
type = getPath(this, type, false) || getPath(window, type);
|
2597
|
+
meta.type = type;
|
2598
|
+
}
|
2599
|
+
|
2600
|
+
if (!typeList) {
|
2601
|
+
typeList = [];
|
2602
|
+
map.set(type, typeList);
|
2603
|
+
}
|
2604
|
+
|
2605
|
+
typeList.push({ name: name, kind: meta.kind });
|
2606
|
+
}
|
2607
|
+
});
|
2608
|
+
|
2609
|
+
return map;
|
2610
|
+
}).cacheable(),
|
2611
|
+
|
2612
|
+
associationsByName: Ember.computed(function() {
|
2613
|
+
var map = Ember.Map.create(), type;
|
2614
|
+
|
2615
|
+
this.eachComputedProperty(function(name, meta) {
|
2616
|
+
if (meta.isAssociation) {
|
2617
|
+
meta.key = name;
|
2618
|
+
type = meta.type;
|
2619
|
+
|
2620
|
+
if (typeof type === 'string') {
|
2621
|
+
type = getPath(this, type, false) || getPath(window, type);
|
2622
|
+
meta.type = type;
|
2623
|
+
}
|
2624
|
+
|
2625
|
+
map.set(name, meta);
|
2626
|
+
}
|
2627
|
+
});
|
2628
|
+
|
2629
|
+
return map;
|
2630
|
+
}).cacheable()
|
2097
2631
|
});
|
2098
2632
|
|
2099
2633
|
|
@@ -2111,29 +2645,55 @@ var referencedFindRecord = function(store, type, data, key, one) {
|
|
2111
2645
|
};
|
2112
2646
|
|
2113
2647
|
var hasAssociation = function(type, options, one) {
|
2114
|
-
|
2115
|
-
|
2648
|
+
options = options || {};
|
2649
|
+
|
2650
|
+
var embedded = options.embedded,
|
2651
|
+
findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
|
2116
2652
|
|
2117
|
-
|
2653
|
+
var meta = { type: type, isAssociation: true, options: options };
|
2654
|
+
if (one) {
|
2655
|
+
meta.kind = 'belongsTo';
|
2656
|
+
} else {
|
2657
|
+
meta.kind = 'hasMany';
|
2658
|
+
}
|
2659
|
+
|
2660
|
+
return Ember.computed(function(key, value) {
|
2118
2661
|
var data = get(this, 'data'), ids, id, association,
|
2119
|
-
|
2662
|
+
store = get(this, 'store');
|
2120
2663
|
|
2121
2664
|
if (typeof type === 'string') {
|
2122
2665
|
type = getPath(this, type, false) || getPath(window, type);
|
2123
2666
|
}
|
2124
2667
|
|
2125
|
-
key = (options && options.key) ? options.key : key;
|
2126
2668
|
if (one) {
|
2127
|
-
|
2128
|
-
|
2669
|
+
if (arguments.length === 2) {
|
2670
|
+
key = options.key || get(this, 'namingConvention').foreignKey(key);
|
2671
|
+
data.setAssociation(key, get(value, 'clientId'));
|
2672
|
+
// put the client id in `key` in the data hash
|
2673
|
+
return value;
|
2674
|
+
} else {
|
2675
|
+
// Embedded belongsTo associations should not look for
|
2676
|
+
// a foreign key.
|
2677
|
+
if (embedded) {
|
2678
|
+
key = options.key || key;
|
2679
|
+
|
2680
|
+
// Non-embedded associations should look for a foreign key.
|
2681
|
+
// For example, instead of person, we might look for person_id
|
2682
|
+
} else {
|
2683
|
+
key = options.key || get(this, 'namingConvention').foreignKey(key);
|
2684
|
+
}
|
2685
|
+
id = findRecord(store, type, data, key, true);
|
2686
|
+
association = id ? store.find(type, id) : null;
|
2687
|
+
}
|
2129
2688
|
} else {
|
2689
|
+
key = options.key || key;
|
2130
2690
|
ids = findRecord(store, type, data, key);
|
2131
2691
|
association = store.findMany(type, ids);
|
2132
2692
|
set(association, 'parentRecord', this);
|
2133
2693
|
}
|
2134
2694
|
|
2135
2695
|
return association;
|
2136
|
-
}).property('data').cacheable().meta(
|
2696
|
+
}).property('data').cacheable().meta(meta);
|
2137
2697
|
};
|
2138
2698
|
|
2139
2699
|
DS.hasMany = function(type, options) {
|
@@ -2142,10 +2702,12 @@ DS.hasMany = function(type, options) {
|
|
2142
2702
|
};
|
2143
2703
|
|
2144
2704
|
DS.hasOne = function(type, options) {
|
2145
|
-
ember_assert("The type passed to DS.
|
2705
|
+
ember_assert("The type passed to DS.belongsTo must be defined", !!type);
|
2146
2706
|
return hasAssociation(type, options, true);
|
2147
2707
|
};
|
2148
2708
|
|
2709
|
+
DS.belongsTo = DS.hasOne;
|
2710
|
+
|
2149
2711
|
})({});
|
2150
2712
|
|
2151
2713
|
|