pyro 1.0.0.rc2 → 1.0.0.rc3
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.
- checksums.yaml +4 -4
- data/bin/pyro +1 -1
- data/lib/pyro.rb +8 -3
- data/pyro.gemspec +3 -3
- data/templates/app/index.erb +11 -11
- data/templates/app/vendor/ember/ember-data.js +1965 -807
- data/templates/app/vendor/ember/ember-data.min.js +16 -0
- data/templates/app/vendor/ember/ember-data.prod.js +7840 -37
- data/templates/app/vendor/ember/ember.js +1049 -520
- data/templates/app/vendor/ember/ember.min.js +20 -0
- data/templates/app/vendor/ember/ember.prod.js +996 -496
- metadata +13 -11
@@ -1,5 +1,14 @@
|
|
1
|
-
//
|
2
|
-
//
|
1
|
+
// ==========================================================================
|
2
|
+
// Project: Ember Data
|
3
|
+
// Copyright: ©2011-2012 Tilde Inc. and contributors.
|
4
|
+
// Portions ©2011 Living Social Inc. and contributors.
|
5
|
+
// License: Licensed under MIT license (see license.js)
|
6
|
+
// ==========================================================================
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
// Version: v1.0.0-beta.3
|
11
|
+
// Last commit: 2259c27 (2013-09-28 19:24:07 -0700)
|
3
12
|
|
4
13
|
|
5
14
|
(function() {
|
@@ -42,547 +51,185 @@ var define, requireModule;
|
|
42
51
|
};
|
43
52
|
})();
|
44
53
|
(function() {
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
Ember.String.singularize = function(word) {
|
50
|
-
return Ember.Inflector.inflector.singularize(word);
|
51
|
-
};
|
54
|
+
/**
|
55
|
+
@module ember-data
|
56
|
+
*/
|
52
57
|
|
53
|
-
|
58
|
+
/**
|
59
|
+
All Ember Data methods and functions are defined inside of this namespace.
|
54
60
|
|
61
|
+
@class DS
|
62
|
+
@static
|
63
|
+
*/
|
55
64
|
|
65
|
+
if ('undefined' === typeof DS) {
|
66
|
+
DS = Ember.Namespace.create({
|
67
|
+
VERSION: '1.0.0-beta.2'
|
68
|
+
});
|
56
69
|
|
57
|
-
(
|
58
|
-
|
70
|
+
if ('undefined' !== typeof window) {
|
71
|
+
window.DS = DS;
|
72
|
+
}
|
59
73
|
|
60
|
-
|
61
|
-
|
62
|
-
rules.uncountable[uncountable[i]] = true;
|
74
|
+
if (Ember.libraries) {
|
75
|
+
Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
|
63
76
|
}
|
64
77
|
}
|
78
|
+
})();
|
65
79
|
|
66
|
-
function loadIrregular(rules, irregularPairs) {
|
67
|
-
var pair;
|
68
|
-
|
69
|
-
for (var i = 0, length = irregularPairs.length; i < length; i++) {
|
70
|
-
pair = irregularPairs[i];
|
71
80
|
|
72
|
-
rules.irregular[pair[0]] = pair[1];
|
73
|
-
rules.irregularInverse[pair[1]] = pair[0];
|
74
|
-
}
|
75
|
-
}
|
76
81
|
|
77
|
-
function
|
78
|
-
|
79
|
-
ruleSet.uncountable = ruleSet.uncountable || {};
|
80
|
-
ruleSet.irregularPairs= ruleSet.irregularPairs|| {};
|
82
|
+
(function() {
|
83
|
+
var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
uncountable: {}
|
85
|
+
// Simple dispatcher to support overriding the aliased
|
86
|
+
// method in subclasses.
|
87
|
+
function aliasMethod(methodName) {
|
88
|
+
return function() {
|
89
|
+
return this[methodName].apply(this, arguments);
|
88
90
|
};
|
89
|
-
|
90
|
-
loadUncountable(rules, ruleSet.uncountable);
|
91
|
-
loadIrregular(rules, ruleSet.irregularPairs);
|
92
91
|
}
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
|
93
|
+
DS.JSONSerializer = Ember.Object.extend({
|
94
|
+
primaryKey: 'id',
|
95
|
+
|
96
|
+
applyTransforms: function(type, data) {
|
97
|
+
type.eachTransformedAttribute(function(key, type) {
|
98
|
+
var transform = this.transformFor(type);
|
99
|
+
data[key] = transform.deserialize(data[key]);
|
100
|
+
}, this);
|
101
|
+
|
102
|
+
return data;
|
97
103
|
},
|
98
104
|
|
99
|
-
|
100
|
-
|
105
|
+
normalize: function(type, hash) {
|
106
|
+
if (!hash) { return hash; }
|
107
|
+
|
108
|
+
this.applyTransforms(type, hash);
|
109
|
+
return hash;
|
101
110
|
},
|
102
111
|
|
103
|
-
|
104
|
-
var inflection, substitution, result, lowercase, isBlank,
|
105
|
-
isUncountable, isIrregular, isIrregularInverse, rule;
|
112
|
+
// SERIALIZE
|
106
113
|
|
107
|
-
|
114
|
+
serialize: function(record, options) {
|
115
|
+
var json = {};
|
108
116
|
|
109
|
-
if (
|
110
|
-
|
117
|
+
if (options && options.includeId) {
|
118
|
+
var id = get(record, 'id');
|
119
|
+
|
120
|
+
if (id) {
|
121
|
+
json[get(this, 'primaryKey')] = get(record, 'id');
|
122
|
+
}
|
111
123
|
}
|
112
124
|
|
113
|
-
|
125
|
+
record.eachAttribute(function(key, attribute) {
|
126
|
+
this.serializeAttribute(record, json, key, attribute);
|
127
|
+
}, this);
|
114
128
|
|
115
|
-
|
129
|
+
record.eachRelationship(function(key, relationship) {
|
130
|
+
if (relationship.kind === 'belongsTo') {
|
131
|
+
this.serializeBelongsTo(record, json, relationship);
|
132
|
+
} else if (relationship.kind === 'hasMany') {
|
133
|
+
this.serializeHasMany(record, json, relationship);
|
134
|
+
}
|
135
|
+
}, this);
|
116
136
|
|
117
|
-
|
118
|
-
|
119
|
-
}
|
137
|
+
return json;
|
138
|
+
},
|
120
139
|
|
121
|
-
|
140
|
+
serializeAttribute: function(record, json, key, attribute) {
|
141
|
+
var attrs = get(this, 'attrs');
|
142
|
+
var value = get(record, key), type = attribute.type;
|
122
143
|
|
123
|
-
if (
|
124
|
-
|
144
|
+
if (type) {
|
145
|
+
var transform = this.transformFor(type);
|
146
|
+
value = transform.serialize(value);
|
125
147
|
}
|
126
148
|
|
127
|
-
|
149
|
+
// if provided, use the mapping provided by `attrs` in
|
150
|
+
// the serializer
|
151
|
+
key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key);
|
152
|
+
|
153
|
+
json[key] = value;
|
154
|
+
},
|
155
|
+
|
156
|
+
serializeBelongsTo: function(record, json, relationship) {
|
157
|
+
var key = relationship.key;
|
158
|
+
|
159
|
+
var belongsTo = get(record, key);
|
160
|
+
|
161
|
+
key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
|
128
162
|
|
129
|
-
if (
|
130
|
-
|
163
|
+
if (isNone(belongsTo)) {
|
164
|
+
json[key] = belongsTo;
|
165
|
+
} else {
|
166
|
+
json[key] = get(belongsTo, 'id');
|
131
167
|
}
|
132
168
|
|
133
|
-
|
134
|
-
|
135
|
-
|
169
|
+
if (relationship.options.polymorphic) {
|
170
|
+
this.serializePolymorphicType(record, json, relationship);
|
171
|
+
}
|
172
|
+
},
|
136
173
|
|
137
|
-
|
138
|
-
|
139
|
-
|
174
|
+
serializeHasMany: function(record, json, relationship) {
|
175
|
+
var key = relationship.key;
|
176
|
+
|
177
|
+
var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
|
178
|
+
|
179
|
+
if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') {
|
180
|
+
json[key] = get(record, key).mapBy('id');
|
181
|
+
// TODO support for polymorphic manyToNone and manyToMany relationships
|
140
182
|
}
|
183
|
+
},
|
141
184
|
|
142
|
-
|
185
|
+
/**
|
186
|
+
You can use this method to customize how polymorphic objects are serialized.
|
187
|
+
*/
|
188
|
+
serializePolymorphicType: Ember.K,
|
143
189
|
|
144
|
-
|
145
|
-
substitution = inflection[1];
|
190
|
+
// EXTRACT
|
146
191
|
|
147
|
-
|
192
|
+
extract: function(store, type, payload, id, requestType) {
|
193
|
+
this.extractMeta(store, type, payload);
|
148
194
|
|
149
|
-
|
150
|
-
|
151
|
-
}
|
195
|
+
var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
|
196
|
+
return this[specificExtract](store, type, payload, id, requestType);
|
197
|
+
},
|
152
198
|
|
153
|
-
|
199
|
+
extractFindAll: aliasMethod('extractArray'),
|
200
|
+
extractFindQuery: aliasMethod('extractArray'),
|
201
|
+
extractFindMany: aliasMethod('extractArray'),
|
202
|
+
extractFindHasMany: aliasMethod('extractArray'),
|
154
203
|
|
155
|
-
|
204
|
+
extractCreateRecord: aliasMethod('extractSave'),
|
205
|
+
extractUpdateRecord: aliasMethod('extractSave'),
|
206
|
+
extractDeleteRecord: aliasMethod('extractSave'),
|
156
207
|
|
208
|
+
extractFind: aliasMethod('extractSingle'),
|
209
|
+
extractFindBelongsTo: aliasMethod('extractSingle'),
|
210
|
+
extractSave: aliasMethod('extractSingle'),
|
157
211
|
|
212
|
+
extractSingle: function(store, type, payload) {
|
213
|
+
return this.normalize(type, payload);
|
214
|
+
},
|
158
215
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
[/$/, 's'],
|
163
|
-
[/s$/i, 's'],
|
164
|
-
[/^(ax|test)is$/i, '$1es'],
|
165
|
-
[/(octop|vir)us$/i, '$1i'],
|
166
|
-
[/(octop|vir)i$/i, '$1i'],
|
167
|
-
[/(alias|status)$/i, '$1es'],
|
168
|
-
[/(bu)s$/i, '$1ses'],
|
169
|
-
[/(buffal|tomat)o$/i, '$1oes'],
|
170
|
-
[/([ti])um$/i, '$1a'],
|
171
|
-
[/([ti])a$/i, '$1a'],
|
172
|
-
[/sis$/i, 'ses'],
|
173
|
-
[/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
|
174
|
-
[/(hive)$/i, '$1s'],
|
175
|
-
[/([^aeiouy]|qu)y$/i, '$1ies'],
|
176
|
-
[/(x|ch|ss|sh)$/i, '$1es'],
|
177
|
-
[/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
|
178
|
-
[/^(m|l)ouse$/i, '$1ice'],
|
179
|
-
[/^(m|l)ice$/i, '$1ice'],
|
180
|
-
[/^(ox)$/i, '$1en'],
|
181
|
-
[/^(oxen)$/i, '$1'],
|
182
|
-
[/(quiz)$/i, '$1zes']
|
183
|
-
],
|
216
|
+
extractArray: function(store, type, payload) {
|
217
|
+
return this.normalize(type, payload);
|
218
|
+
},
|
184
219
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
[/(^analy)(sis|ses)$/i, '$1sis'],
|
192
|
-
[/([^f])ves$/i, '$1fe'],
|
193
|
-
[/(hive)s$/i, '$1'],
|
194
|
-
[/(tive)s$/i, '$1'],
|
195
|
-
[/([lr])ves$/i, '$1f'],
|
196
|
-
[/([^aeiouy]|qu)ies$/i, '$1y'],
|
197
|
-
[/(s)eries$/i, '$1eries'],
|
198
|
-
[/(m)ovies$/i, '$1ovie'],
|
199
|
-
[/(x|ch|ss|sh)es$/i, '$1'],
|
200
|
-
[/^(m|l)ice$/i, '$1ouse'],
|
201
|
-
[/(bus)(es)?$/i, '$1'],
|
202
|
-
[/(o)es$/i, '$1'],
|
203
|
-
[/(shoe)s$/i, '$1'],
|
204
|
-
[/(cris|test)(is|es)$/i, '$1is'],
|
205
|
-
[/^(a)x[ie]s$/i, '$1xis'],
|
206
|
-
[/(octop|vir)(us|i)$/i, '$1us'],
|
207
|
-
[/(alias|status)(es)?$/i, '$1'],
|
208
|
-
[/^(ox)en/i, '$1'],
|
209
|
-
[/(vert|ind)ices$/i, '$1ex'],
|
210
|
-
[/(matr)ices$/i, '$1ix'],
|
211
|
-
[/(quiz)zes$/i, '$1'],
|
212
|
-
[/(database)s$/i, '$1']
|
213
|
-
],
|
220
|
+
extractMeta: function(store, type, payload) {
|
221
|
+
if (payload && payload.meta) {
|
222
|
+
store.metaForType(type, payload.meta);
|
223
|
+
delete payload.meta;
|
224
|
+
}
|
225
|
+
},
|
214
226
|
|
215
|
-
|
216
|
-
['person', 'people'],
|
217
|
-
['man', 'men'],
|
218
|
-
['child', 'children'],
|
219
|
-
['sex', 'sexes'],
|
220
|
-
['move', 'moves'],
|
221
|
-
['cow', 'kine'],
|
222
|
-
['zombie', 'zombies']
|
223
|
-
],
|
227
|
+
// HELPERS
|
224
228
|
|
225
|
-
|
226
|
-
'
|
227
|
-
|
228
|
-
|
229
|
-
'money',
|
230
|
-
'species',
|
231
|
-
'series',
|
232
|
-
'fish',
|
233
|
-
'sheep',
|
234
|
-
'jeans',
|
235
|
-
'police'
|
236
|
-
]
|
237
|
-
};
|
238
|
-
|
239
|
-
})();
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
(function() {
|
244
|
-
if (Ember.EXTEND_PROTOTYPES) {
|
245
|
-
/**
|
246
|
-
See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
|
247
|
-
|
248
|
-
@method pluralize
|
249
|
-
@for String
|
250
|
-
*/
|
251
|
-
String.prototype.pluralize = function() {
|
252
|
-
return Ember.String.pluralize(this);
|
253
|
-
};
|
254
|
-
|
255
|
-
/**
|
256
|
-
See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
|
257
|
-
|
258
|
-
@method singularize
|
259
|
-
@for String
|
260
|
-
*/
|
261
|
-
String.prototype.singularize = function() {
|
262
|
-
return Ember.String.singularize(this);
|
263
|
-
};
|
264
|
-
}
|
265
|
-
|
266
|
-
})();
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
(function() {
|
271
|
-
Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
|
272
|
-
|
273
|
-
})();
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
(function() {
|
278
|
-
|
279
|
-
})();
|
280
|
-
|
281
|
-
|
282
|
-
})();
|
283
|
-
// Version: v1.0.0-beta.1
|
284
|
-
// Last commit: cafab9d (2013-09-01 00:42:39 -0700)
|
285
|
-
|
286
|
-
|
287
|
-
(function() {
|
288
|
-
/**
|
289
|
-
@module ember-data
|
290
|
-
*/
|
291
|
-
|
292
|
-
/**
|
293
|
-
All Ember Data methods and functions are defined inside of this namespace.
|
294
|
-
|
295
|
-
@class DS
|
296
|
-
@static
|
297
|
-
*/
|
298
|
-
|
299
|
-
if ('undefined' === typeof DS) {
|
300
|
-
DS = Ember.Namespace.create({
|
301
|
-
VERSION: '1.0.0-beta.1'
|
302
|
-
});
|
303
|
-
|
304
|
-
if ('undefined' !== typeof window) {
|
305
|
-
window.DS = DS;
|
306
|
-
}
|
307
|
-
}
|
308
|
-
})();
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
(function() {
|
313
|
-
/**
|
314
|
-
@module ember-data
|
315
|
-
*/
|
316
|
-
|
317
|
-
var isNone = Ember.isNone, isEmpty = Ember.isEmpty;
|
318
|
-
|
319
|
-
/**
|
320
|
-
DS.JSONTransforms is a hash of transforms used by DS.Serializer.
|
321
|
-
|
322
|
-
@class JSONTransforms
|
323
|
-
@static
|
324
|
-
@namespace DS
|
325
|
-
*/
|
326
|
-
DS.JSONTransforms = {
|
327
|
-
string: {
|
328
|
-
deserialize: function(serialized) {
|
329
|
-
return isNone(serialized) ? null : String(serialized);
|
330
|
-
},
|
331
|
-
|
332
|
-
serialize: function(deserialized) {
|
333
|
-
return isNone(deserialized) ? null : String(deserialized);
|
334
|
-
}
|
335
|
-
},
|
336
|
-
|
337
|
-
number: {
|
338
|
-
deserialize: function(serialized) {
|
339
|
-
return isEmpty(serialized) ? null : Number(serialized);
|
340
|
-
},
|
341
|
-
|
342
|
-
serialize: function(deserialized) {
|
343
|
-
return isEmpty(deserialized) ? null : Number(deserialized);
|
344
|
-
}
|
345
|
-
},
|
346
|
-
|
347
|
-
// Handles the following boolean inputs:
|
348
|
-
// "TrUe", "t", "f", "FALSE", 0, (non-zero), or boolean true/false
|
349
|
-
'boolean': {
|
350
|
-
deserialize: function(serialized) {
|
351
|
-
var type = typeof serialized;
|
352
|
-
|
353
|
-
if (type === "boolean") {
|
354
|
-
return serialized;
|
355
|
-
} else if (type === "string") {
|
356
|
-
return serialized.match(/^true$|^t$|^1$/i) !== null;
|
357
|
-
} else if (type === "number") {
|
358
|
-
return serialized === 1;
|
359
|
-
} else {
|
360
|
-
return false;
|
361
|
-
}
|
362
|
-
},
|
363
|
-
|
364
|
-
serialize: function(deserialized) {
|
365
|
-
return Boolean(deserialized);
|
366
|
-
}
|
367
|
-
},
|
368
|
-
|
369
|
-
date: {
|
370
|
-
deserialize: function(serialized) {
|
371
|
-
var type = typeof serialized;
|
372
|
-
|
373
|
-
if (type === "string") {
|
374
|
-
return new Date(Ember.Date.parse(serialized));
|
375
|
-
} else if (type === "number") {
|
376
|
-
return new Date(serialized);
|
377
|
-
} else if (serialized === null || serialized === undefined) {
|
378
|
-
// if the value is not present in the data,
|
379
|
-
// return undefined, not null.
|
380
|
-
return serialized;
|
381
|
-
} else {
|
382
|
-
return null;
|
383
|
-
}
|
384
|
-
},
|
385
|
-
|
386
|
-
serialize: function(date) {
|
387
|
-
if (date instanceof Date) {
|
388
|
-
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
389
|
-
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
390
|
-
|
391
|
-
var pad = function(num) {
|
392
|
-
return num < 10 ? "0"+num : ""+num;
|
393
|
-
};
|
394
|
-
|
395
|
-
var utcYear = date.getUTCFullYear(),
|
396
|
-
utcMonth = date.getUTCMonth(),
|
397
|
-
utcDayOfMonth = date.getUTCDate(),
|
398
|
-
utcDay = date.getUTCDay(),
|
399
|
-
utcHours = date.getUTCHours(),
|
400
|
-
utcMinutes = date.getUTCMinutes(),
|
401
|
-
utcSeconds = date.getUTCSeconds();
|
402
|
-
|
403
|
-
|
404
|
-
var dayOfWeek = days[utcDay];
|
405
|
-
var dayOfMonth = pad(utcDayOfMonth);
|
406
|
-
var month = months[utcMonth];
|
407
|
-
|
408
|
-
return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
|
409
|
-
pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
|
410
|
-
} else {
|
411
|
-
return null;
|
412
|
-
}
|
413
|
-
}
|
414
|
-
}
|
415
|
-
};
|
416
|
-
|
417
|
-
})();
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
(function() {
|
422
|
-
var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
|
423
|
-
|
424
|
-
var transforms = DS.JSONTransforms;
|
425
|
-
|
426
|
-
// Simple dispatcher to support overriding the aliased
|
427
|
-
// method in subclasses.
|
428
|
-
function aliasMethod(methodName) {
|
429
|
-
return function() {
|
430
|
-
return this[methodName].apply(this, arguments);
|
431
|
-
};
|
432
|
-
}
|
433
|
-
|
434
|
-
DS.JSONSerializer = Ember.Object.extend({
|
435
|
-
primaryKey: 'id',
|
436
|
-
|
437
|
-
deserialize: function(type, data) {
|
438
|
-
var store = get(this, 'store');
|
439
|
-
|
440
|
-
type.eachTransformedAttribute(function(key, type) {
|
441
|
-
data[key] = transforms[type].deserialize(data[key]);
|
442
|
-
});
|
443
|
-
|
444
|
-
type.eachRelationship(function(key, relationship) {
|
445
|
-
// A link (usually a URL) was already provided in
|
446
|
-
// normalized form
|
447
|
-
if (data.links && data.links[key]) {
|
448
|
-
return;
|
449
|
-
}
|
450
|
-
|
451
|
-
var type = relationship.type,
|
452
|
-
value = data[key];
|
453
|
-
|
454
|
-
if (value == null) { return; }
|
455
|
-
|
456
|
-
if (relationship.kind === 'belongsTo') {
|
457
|
-
this.deserializeRecordId(data, key, relationship, value);
|
458
|
-
} else if (relationship.kind === 'hasMany') {
|
459
|
-
this.deserializeRecordIds(data, key, relationship, value);
|
460
|
-
}
|
461
|
-
}, this);
|
462
|
-
|
463
|
-
return data;
|
464
|
-
},
|
465
|
-
|
466
|
-
deserializeRecordId: function(data, key, relationship, id) {
|
467
|
-
if (isNone(id) || id instanceof DS.Model) {
|
468
|
-
return;
|
469
|
-
}
|
470
|
-
|
471
|
-
var type;
|
472
|
-
|
473
|
-
if (typeof id === 'number' || typeof id === 'string') {
|
474
|
-
type = this.typeFor(relationship, key, data);
|
475
|
-
data[key] = get(this, 'store').recordForId(type, id);
|
476
|
-
} else if (typeof id === 'object') {
|
477
|
-
// polymorphic
|
478
|
-
data[key] = get(this, 'store').recordForId(id.type, id.id);
|
479
|
-
}
|
480
|
-
},
|
481
|
-
|
482
|
-
deserializeRecordIds: function(data, key, relationship, ids) {
|
483
|
-
for (var i=0, l=ids.length; i<l; i++) {
|
484
|
-
this.deserializeRecordId(ids, i, relationship, ids[i]);
|
485
|
-
}
|
486
|
-
},
|
487
|
-
|
488
|
-
// SERIALIZE
|
489
|
-
|
490
|
-
serialize: function(record, options) {
|
491
|
-
var store = get(this, 'store');
|
492
|
-
|
493
|
-
var json = {};
|
494
|
-
|
495
|
-
if (options && options.includeId) {
|
496
|
-
var id = get(record, 'id');
|
497
|
-
|
498
|
-
if (id) {
|
499
|
-
json[get(this, 'primaryKey')] = get(record, 'id');
|
500
|
-
}
|
501
|
-
}
|
502
|
-
|
503
|
-
var attrs = get(this, 'attrs');
|
504
|
-
|
505
|
-
record.eachAttribute(function(key, attribute) {
|
506
|
-
var value = get(record, key), type = attribute.type;
|
507
|
-
|
508
|
-
if (type) {
|
509
|
-
value = transforms[type].serialize(value);
|
510
|
-
}
|
511
|
-
|
512
|
-
// if provided, use the mapping provided by `attrs` in
|
513
|
-
// the serializer
|
514
|
-
key = attrs && attrs[key] || key;
|
515
|
-
|
516
|
-
json[key] = value;
|
517
|
-
}, this);
|
518
|
-
|
519
|
-
record.eachRelationship(function(key, relationship) {
|
520
|
-
if (relationship.kind === 'belongsTo') {
|
521
|
-
this.serializeBelongsTo(record, json, relationship);
|
522
|
-
} else if (relationship.kind === 'hasMany') {
|
523
|
-
this.serializeHasMany(record, json, relationship);
|
524
|
-
}
|
525
|
-
}, this);
|
526
|
-
|
527
|
-
return json;
|
528
|
-
},
|
529
|
-
|
530
|
-
serializeBelongsTo: function(record, json, relationship) {
|
531
|
-
var key = relationship.key;
|
532
|
-
|
533
|
-
var belongsTo = get(record, key);
|
534
|
-
|
535
|
-
if (isNone(belongsTo)) { return; }
|
536
|
-
|
537
|
-
json[key] = get(belongsTo, 'id');
|
538
|
-
|
539
|
-
if (relationship.options.polymorphic) {
|
540
|
-
json[key + "_type"] = belongsTo.constructor.typeKey;
|
541
|
-
}
|
542
|
-
},
|
543
|
-
|
544
|
-
serializeHasMany: Ember.K,
|
545
|
-
|
546
|
-
// EXTRACT
|
547
|
-
|
548
|
-
extract: function(store, type, payload, id, requestType) {
|
549
|
-
var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
|
550
|
-
return this[specificExtract](store, type, payload, id, requestType);
|
551
|
-
},
|
552
|
-
|
553
|
-
extractFindAll: aliasMethod('extractArray'),
|
554
|
-
extractFindQuery: aliasMethod('extractArray'),
|
555
|
-
extractFindMany: aliasMethod('extractArray'),
|
556
|
-
extractFindHasMany: aliasMethod('extractArray'),
|
557
|
-
|
558
|
-
extractCreateRecord: aliasMethod('extractSave'),
|
559
|
-
extractUpdateRecord: aliasMethod('extractSave'),
|
560
|
-
extractDeleteRecord: aliasMethod('extractSave'),
|
561
|
-
|
562
|
-
extractFind: aliasMethod('extractSingle'),
|
563
|
-
extractSave: aliasMethod('extractSingle'),
|
564
|
-
|
565
|
-
extractSingle: function(store, type, payload) {
|
566
|
-
return payload;
|
567
|
-
},
|
568
|
-
|
569
|
-
extractArray: function(store, type, payload) {
|
570
|
-
return payload;
|
571
|
-
},
|
572
|
-
// HELPERS
|
573
|
-
|
574
|
-
typeFor: function(relationship, key, data) {
|
575
|
-
if (relationship.options.polymorphic) {
|
576
|
-
return data[key + "_type"];
|
577
|
-
} else {
|
578
|
-
return relationship.type;
|
579
|
-
}
|
580
|
-
},
|
581
|
-
|
582
|
-
eachEmbeddedRecord: function() {
|
583
|
-
// this is used by transaction.add
|
584
|
-
}
|
585
|
-
});
|
229
|
+
transformFor: function(attributeType) {
|
230
|
+
return this.container.lookup('transform:' + attributeType);
|
231
|
+
}
|
232
|
+
});
|
586
233
|
|
587
234
|
})();
|
588
235
|
|
@@ -596,6 +243,11 @@ var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.St
|
|
596
243
|
|
597
244
|
/**
|
598
245
|
Extend `Ember.DataAdapter` with ED specific code.
|
246
|
+
|
247
|
+
@class DebugAdapter
|
248
|
+
@namespace DS
|
249
|
+
@extends Ember.DataAdapter
|
250
|
+
@private
|
599
251
|
*/
|
600
252
|
DS.DebugAdapter = Ember.DataAdapter.extend({
|
601
253
|
getFilters: function() {
|
@@ -612,7 +264,7 @@ DS.DebugAdapter = Ember.DataAdapter.extend({
|
|
612
264
|
|
613
265
|
columnsForType: function(type) {
|
614
266
|
var columns = [{ name: 'id', desc: 'Id' }], count = 0, self = this;
|
615
|
-
|
267
|
+
get(type, 'attributes').forEach(function(name, meta) {
|
616
268
|
if (count++ > self.attributeLimit) { return false; }
|
617
269
|
var desc = capitalize(underscore(name).replace('_', ' '));
|
618
270
|
columns.push({ name: name, desc: desc });
|
@@ -699,35 +351,169 @@ DS.DebugAdapter = Ember.DataAdapter.extend({
|
|
699
351
|
|
700
352
|
|
701
353
|
(function() {
|
702
|
-
|
703
|
-
@module ember-data
|
704
|
-
*/
|
354
|
+
DS.Transform = Ember.Object.extend({
|
705
355
|
|
706
|
-
|
356
|
+
serialize: Ember.required(),
|
357
|
+
|
358
|
+
deserialize: Ember.required()
|
707
359
|
|
708
|
-
|
709
|
-
|
360
|
+
});
|
361
|
+
})();
|
710
362
|
|
711
|
-
If an Ember.js developer defines a subclass of DS.Store on their application,
|
712
|
-
this code will automatically instantiate it and make it available on the
|
713
|
-
router.
|
714
363
|
|
715
|
-
Additionally, after an application's controllers have been injected, they will
|
716
|
-
each have the store made available to them.
|
717
364
|
|
718
|
-
|
365
|
+
(function() {
|
719
366
|
|
720
|
-
|
721
|
-
|
722
|
-
|
367
|
+
DS.BooleanTransform = DS.Transform.extend({
|
368
|
+
deserialize: function(serialized) {
|
369
|
+
var type = typeof serialized;
|
723
370
|
|
724
|
-
|
725
|
-
|
726
|
-
|
371
|
+
if (type === "boolean") {
|
372
|
+
return serialized;
|
373
|
+
} else if (type === "string") {
|
374
|
+
return serialized.match(/^true$|^t$|^1$/i) !== null;
|
375
|
+
} else if (type === "number") {
|
376
|
+
return serialized === 1;
|
377
|
+
} else {
|
378
|
+
return false;
|
379
|
+
}
|
380
|
+
},
|
727
381
|
|
728
|
-
|
729
|
-
|
730
|
-
|
382
|
+
serialize: function(deserialized) {
|
383
|
+
return Boolean(deserialized);
|
384
|
+
}
|
385
|
+
});
|
386
|
+
|
387
|
+
})();
|
388
|
+
|
389
|
+
|
390
|
+
|
391
|
+
(function() {
|
392
|
+
DS.DateTransform = DS.Transform.extend({
|
393
|
+
|
394
|
+
deserialize: function(serialized) {
|
395
|
+
var type = typeof serialized;
|
396
|
+
|
397
|
+
if (type === "string") {
|
398
|
+
return new Date(Ember.Date.parse(serialized));
|
399
|
+
} else if (type === "number") {
|
400
|
+
return new Date(serialized);
|
401
|
+
} else if (serialized === null || serialized === undefined) {
|
402
|
+
// if the value is not present in the data,
|
403
|
+
// return undefined, not null.
|
404
|
+
return serialized;
|
405
|
+
} else {
|
406
|
+
return null;
|
407
|
+
}
|
408
|
+
},
|
409
|
+
|
410
|
+
serialize: function(date) {
|
411
|
+
if (date instanceof Date) {
|
412
|
+
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
413
|
+
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
414
|
+
|
415
|
+
var pad = function(num) {
|
416
|
+
return num < 10 ? "0"+num : ""+num;
|
417
|
+
};
|
418
|
+
|
419
|
+
var utcYear = date.getUTCFullYear(),
|
420
|
+
utcMonth = date.getUTCMonth(),
|
421
|
+
utcDayOfMonth = date.getUTCDate(),
|
422
|
+
utcDay = date.getUTCDay(),
|
423
|
+
utcHours = date.getUTCHours(),
|
424
|
+
utcMinutes = date.getUTCMinutes(),
|
425
|
+
utcSeconds = date.getUTCSeconds();
|
426
|
+
|
427
|
+
|
428
|
+
var dayOfWeek = days[utcDay];
|
429
|
+
var dayOfMonth = pad(utcDayOfMonth);
|
430
|
+
var month = months[utcMonth];
|
431
|
+
|
432
|
+
return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
|
433
|
+
pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
|
434
|
+
} else {
|
435
|
+
return null;
|
436
|
+
}
|
437
|
+
}
|
438
|
+
|
439
|
+
});
|
440
|
+
|
441
|
+
})();
|
442
|
+
|
443
|
+
|
444
|
+
|
445
|
+
(function() {
|
446
|
+
var empty = Ember.isEmpty;
|
447
|
+
|
448
|
+
DS.NumberTransform = DS.Transform.extend({
|
449
|
+
|
450
|
+
deserialize: function(serialized) {
|
451
|
+
return empty(serialized) ? null : Number(serialized);
|
452
|
+
},
|
453
|
+
|
454
|
+
serialize: function(deserialized) {
|
455
|
+
return empty(deserialized) ? null : Number(deserialized);
|
456
|
+
}
|
457
|
+
});
|
458
|
+
})();
|
459
|
+
|
460
|
+
|
461
|
+
|
462
|
+
(function() {
|
463
|
+
var none = Ember.isNone;
|
464
|
+
|
465
|
+
DS.StringTransform = DS.Transform.extend({
|
466
|
+
|
467
|
+
deserialize: function(serialized) {
|
468
|
+
return none(serialized) ? null : String(serialized);
|
469
|
+
},
|
470
|
+
|
471
|
+
serialize: function(deserialized) {
|
472
|
+
return none(deserialized) ? null : String(deserialized);
|
473
|
+
}
|
474
|
+
|
475
|
+
});
|
476
|
+
|
477
|
+
})();
|
478
|
+
|
479
|
+
|
480
|
+
|
481
|
+
(function() {
|
482
|
+
|
483
|
+
})();
|
484
|
+
|
485
|
+
|
486
|
+
|
487
|
+
(function() {
|
488
|
+
/**
|
489
|
+
@module ember-data
|
490
|
+
*/
|
491
|
+
|
492
|
+
var set = Ember.set;
|
493
|
+
|
494
|
+
/*
|
495
|
+
This code registers an injection for Ember.Application.
|
496
|
+
|
497
|
+
If an Ember.js developer defines a subclass of DS.Store on their application,
|
498
|
+
this code will automatically instantiate it and make it available on the
|
499
|
+
router.
|
500
|
+
|
501
|
+
Additionally, after an application's controllers have been injected, they will
|
502
|
+
each have the store made available to them.
|
503
|
+
|
504
|
+
For example, imagine an Ember.js application with the following classes:
|
505
|
+
|
506
|
+
App.Store = DS.Store.extend({
|
507
|
+
adapter: 'custom'
|
508
|
+
});
|
509
|
+
|
510
|
+
App.PostsController = Ember.ArrayController.extend({
|
511
|
+
// ...
|
512
|
+
});
|
513
|
+
|
514
|
+
When the application is initialized, `App.Store` will automatically be
|
515
|
+
instantiated, and the instance of `App.PostsController` will have its `store`
|
516
|
+
property set to that instance.
|
731
517
|
|
732
518
|
Note that this code will only be run if the `ember-application` package is
|
733
519
|
loaded. If Ember Data is being used in an environment other than a
|
@@ -751,17 +537,16 @@ Ember.onLoad('Ember.Application', function(Application) {
|
|
751
537
|
}
|
752
538
|
});
|
753
539
|
|
754
|
-
|
755
|
-
|
756
|
-
if (DS.DebugAdapter) {
|
757
|
-
Application.initializer({
|
758
|
-
name: "dataAdapter",
|
540
|
+
Application.initializer({
|
541
|
+
name: "transforms",
|
759
542
|
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
543
|
+
initialize: function(container, application) {
|
544
|
+
application.register('transform:boolean', DS.BooleanTransform);
|
545
|
+
application.register('transform:date', DS.DateTransform);
|
546
|
+
application.register('transform:number', DS.NumberTransform);
|
547
|
+
application.register('transform:string', DS.StringTransform);
|
548
|
+
}
|
549
|
+
});
|
765
550
|
|
766
551
|
Application.initializer({
|
767
552
|
name: "dataAdapter",
|
@@ -922,6 +707,14 @@ DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
|
|
922
707
|
|
923
708
|
removeRecord: function(record) {
|
924
709
|
get(this, 'content').removeObject(record);
|
710
|
+
},
|
711
|
+
|
712
|
+
save: function() {
|
713
|
+
var promise = Ember.RSVP.all(this.invoke("save")).then(function(array) {
|
714
|
+
return Ember.A(array);
|
715
|
+
});
|
716
|
+
|
717
|
+
return DS.PromiseArray.create({ promise: promise });
|
925
718
|
}
|
926
719
|
});
|
927
720
|
|
@@ -983,11 +776,13 @@ DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
|
|
983
776
|
load: function(data) {
|
984
777
|
var store = get(this, 'store'),
|
985
778
|
type = get(this, 'type'),
|
986
|
-
records = store.pushMany(type, data)
|
779
|
+
records = store.pushMany(type, data),
|
780
|
+
meta = store.metadataFor(type);
|
987
781
|
|
988
782
|
this.setProperties({
|
989
783
|
content: Ember.A(records),
|
990
|
-
isLoaded: true
|
784
|
+
isLoaded: true,
|
785
|
+
meta: meta
|
991
786
|
});
|
992
787
|
|
993
788
|
// TODO: does triggering didLoad event should be the last action of the runLoop?
|
@@ -1021,11 +816,11 @@ var map = Ember.EnumerableUtils.map;
|
|
1021
816
|
defined:
|
1022
817
|
|
1023
818
|
App.Post = DS.Model.extend({
|
1024
|
-
comments: DS.hasMany('
|
819
|
+
comments: DS.hasMany('comment')
|
1025
820
|
});
|
1026
821
|
|
1027
822
|
App.Comment = DS.Model.extend({
|
1028
|
-
post: DS.belongsTo('
|
823
|
+
post: DS.belongsTo('post')
|
1029
824
|
});
|
1030
825
|
|
1031
826
|
If you created a new instance of `App.Post` and added
|
@@ -1082,10 +877,11 @@ DS.ManyArray = DS.RecordArray.extend({
|
|
1082
877
|
fetch: function() {
|
1083
878
|
var records = get(this, 'content'),
|
1084
879
|
store = get(this, 'store'),
|
1085
|
-
owner = get(this, 'owner')
|
880
|
+
owner = get(this, 'owner'),
|
881
|
+
resolver = Ember.RSVP.defer();
|
1086
882
|
|
1087
883
|
var unloadedRecords = records.filterProperty('isEmpty', true);
|
1088
|
-
store.fetchMany(unloadedRecords, owner);
|
884
|
+
store.fetchMany(unloadedRecords, owner, resolver);
|
1089
885
|
},
|
1090
886
|
|
1091
887
|
// Overrides Ember.Array's replace method to implement
|
@@ -1100,7 +896,7 @@ DS.ManyArray = DS.RecordArray.extend({
|
|
1100
896
|
},
|
1101
897
|
|
1102
898
|
arrangedContentDidChange: function() {
|
1103
|
-
this
|
899
|
+
Ember.run.once(this, 'fetch');
|
1104
900
|
},
|
1105
901
|
|
1106
902
|
arrayContentWillChange: function(index, removed, added) {
|
@@ -1209,6 +1005,7 @@ DS.ManyArray = DS.RecordArray.extend({
|
|
1209
1005
|
*/
|
1210
1006
|
|
1211
1007
|
var get = Ember.get;
|
1008
|
+
var forEach = Ember.ArrayPolyfills.forEach;
|
1212
1009
|
|
1213
1010
|
var resolveMapConflict = function(oldValue, newValue) {
|
1214
1011
|
return oldValue;
|
@@ -1304,7 +1101,7 @@ DS._Mappable = Ember.Mixin.create({
|
|
1304
1101
|
|
1305
1102
|
var classMap = classMeta[mapName];
|
1306
1103
|
if (classMap) {
|
1307
|
-
|
1104
|
+
forEach.call(classMap, eachMap, this);
|
1308
1105
|
}
|
1309
1106
|
|
1310
1107
|
function eachMap(key, value) {
|
@@ -1322,7 +1119,6 @@ DS._Mappable = Ember.Mixin.create({
|
|
1322
1119
|
}
|
1323
1120
|
}
|
1324
1121
|
|
1325
|
-
|
1326
1122
|
});
|
1327
1123
|
|
1328
1124
|
DS._Mappable.generateMapFunctionFor = function(mapName, transform) {
|
@@ -1370,7 +1166,6 @@ var isNone = Ember.isNone;
|
|
1370
1166
|
var forEach = Ember.EnumerableUtils.forEach;
|
1371
1167
|
var indexOf = Ember.EnumerableUtils.indexOf;
|
1372
1168
|
var map = Ember.EnumerableUtils.map;
|
1373
|
-
var OrderedSet = Ember.OrderedSet;
|
1374
1169
|
var resolve = Ember.RSVP.resolve;
|
1375
1170
|
|
1376
1171
|
// Implementors Note:
|
@@ -1501,12 +1296,15 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1501
1296
|
This property is cacheable, so the same instance of a specified
|
1502
1297
|
adapter class should be used for the lifetime of the store.
|
1503
1298
|
|
1504
|
-
@property
|
1299
|
+
@property defaultAdapter
|
1505
1300
|
@private
|
1506
1301
|
@returns DS.Adapter
|
1507
1302
|
*/
|
1508
|
-
|
1303
|
+
defaultAdapter: Ember.computed(function() {
|
1509
1304
|
var adapter = get(this, 'adapter');
|
1305
|
+
|
1306
|
+
Ember.assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name or a factory', !(adapter instanceof DS.Adapter));
|
1307
|
+
|
1510
1308
|
if (typeof adapter === 'string') {
|
1511
1309
|
adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:_rest');
|
1512
1310
|
}
|
@@ -1578,7 +1376,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1578
1376
|
@returns String if the adapter can generate one, an ID
|
1579
1377
|
*/
|
1580
1378
|
_generateId: function(type) {
|
1581
|
-
var adapter = this.
|
1379
|
+
var adapter = this.adapterFor(type);
|
1582
1380
|
|
1583
1381
|
if (adapter && adapter.generateIdForRecord) {
|
1584
1382
|
return adapter.generateIdForRecord(this);
|
@@ -1682,12 +1480,10 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1682
1480
|
findById: function(type, id) {
|
1683
1481
|
type = this.modelFor(type);
|
1684
1482
|
|
1685
|
-
var record = this.
|
1686
|
-
|
1687
|
-
|
1688
|
-
|
1689
|
-
return promiseObject(resolve(record));
|
1690
|
-
}
|
1483
|
+
var record = this.recordForId(type, id);
|
1484
|
+
|
1485
|
+
var promise = this.fetchRecord(record) || resolve(record);
|
1486
|
+
return promiseObject(promise);
|
1691
1487
|
},
|
1692
1488
|
|
1693
1489
|
/**
|
@@ -1720,13 +1516,17 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1720
1516
|
@returns Promise
|
1721
1517
|
*/
|
1722
1518
|
fetchRecord: function(record) {
|
1519
|
+
if (isNone(record)) { return null; }
|
1520
|
+
if (record._loadingPromise) { return record._loadingPromise; }
|
1521
|
+
if (!get(record, 'isEmpty')) { return null; }
|
1522
|
+
|
1723
1523
|
var type = record.constructor,
|
1724
1524
|
id = get(record, 'id'),
|
1725
1525
|
resolver = Ember.RSVP.defer();
|
1726
1526
|
|
1727
|
-
record.loadingData();
|
1527
|
+
record.loadingData(resolver.promise);
|
1728
1528
|
|
1729
|
-
var adapter = this.
|
1529
|
+
var adapter = this.adapterFor(type);
|
1730
1530
|
|
1731
1531
|
Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter);
|
1732
1532
|
Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find);
|
@@ -1740,7 +1540,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1740
1540
|
Get a record by a given type and ID without triggering a fetch.
|
1741
1541
|
|
1742
1542
|
This method will synchronously return the record if it's available.
|
1743
|
-
Otherwise, it will return
|
1543
|
+
Otherwise, it will return null.
|
1744
1544
|
|
1745
1545
|
```js
|
1746
1546
|
var post = store.getById('post', 1);
|
@@ -1756,7 +1556,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1756
1556
|
if (this.hasRecordForId(type, id)) {
|
1757
1557
|
return this.recordForId(type, id);
|
1758
1558
|
} else {
|
1759
|
-
return
|
1559
|
+
return null;
|
1760
1560
|
}
|
1761
1561
|
},
|
1762
1562
|
|
@@ -1775,8 +1575,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1775
1575
|
*/
|
1776
1576
|
reloadRecord: function(record, resolver) {
|
1777
1577
|
var type = record.constructor,
|
1778
|
-
adapter = this.
|
1779
|
-
store = this,
|
1578
|
+
adapter = this.adapterFor(type),
|
1780
1579
|
id = get(record, 'id');
|
1781
1580
|
|
1782
1581
|
Ember.assert("You cannot reload a record without an ID", id);
|
@@ -1817,7 +1616,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1817
1616
|
|
1818
1617
|
forEach(recordsByTypeMap, function(type, records) {
|
1819
1618
|
var ids = records.mapProperty('id'),
|
1820
|
-
adapter = this.
|
1619
|
+
adapter = this.adapterFor(type);
|
1821
1620
|
|
1822
1621
|
Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter);
|
1823
1622
|
Ember.assert("You tried to load many records but your adapter does not implement `findMany`", adapter.findMany);
|
@@ -1829,6 +1628,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1829
1628
|
/**
|
1830
1629
|
Returns true if a record for a given type and ID is already loaded.
|
1831
1630
|
|
1631
|
+
@method hasRecordForId
|
1832
1632
|
@param {String} type
|
1833
1633
|
@param {String|Integer} id
|
1834
1634
|
@returns Boolean
|
@@ -1843,6 +1643,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1843
1643
|
Returns id record for a given type and ID. If one isn't already loaded,
|
1844
1644
|
it builds a new record and leaves it in the `empty` state.
|
1845
1645
|
|
1646
|
+
@method recordForId
|
1846
1647
|
@param {String} type
|
1847
1648
|
@param {String|Integer} id
|
1848
1649
|
@returns DS.Model
|
@@ -1878,19 +1679,20 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1878
1679
|
var unloadedRecords = records.filterProperty('isEmpty', true),
|
1879
1680
|
manyArray = this.recordArrayManager.createManyArray(type, records);
|
1880
1681
|
|
1881
|
-
|
1682
|
+
forEach(unloadedRecords, function(record) {
|
1882
1683
|
record.loadingData();
|
1883
1684
|
});
|
1884
1685
|
|
1885
1686
|
manyArray.loadingRecordsCount = unloadedRecords.length;
|
1886
1687
|
|
1887
1688
|
if (unloadedRecords.length) {
|
1888
|
-
|
1689
|
+
forEach(unloadedRecords, function(record) {
|
1889
1690
|
this.recordArrayManager.registerWaitingRecordArray(record, manyArray);
|
1890
1691
|
}, this);
|
1891
1692
|
|
1892
1693
|
this.fetchMany(unloadedRecords, owner, resolver);
|
1893
1694
|
} else {
|
1695
|
+
if (resolver) { resolver.resolve(); }
|
1894
1696
|
manyArray.set('isLoaded', true);
|
1895
1697
|
Ember.run.once(manyArray, 'trigger', 'didLoad');
|
1896
1698
|
}
|
@@ -1909,6 +1711,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1909
1711
|
The usual use-case is for the server to register a URL as a link, and
|
1910
1712
|
then use that URL in the future to make a request for the relationship.
|
1911
1713
|
|
1714
|
+
@method findHasMany
|
1912
1715
|
@private
|
1913
1716
|
@param {DS.Model} owner
|
1914
1717
|
@param {any} link
|
@@ -1917,7 +1720,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1917
1720
|
@return DS.ManyArray
|
1918
1721
|
*/
|
1919
1722
|
findHasMany: function(owner, link, relationship, resolver) {
|
1920
|
-
var adapter = this.
|
1723
|
+
var adapter = this.adapterFor(owner.constructor);
|
1921
1724
|
|
1922
1725
|
Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter);
|
1923
1726
|
Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", adapter.findHasMany);
|
@@ -1927,6 +1730,15 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1927
1730
|
return records;
|
1928
1731
|
},
|
1929
1732
|
|
1733
|
+
findBelongsTo: function(owner, link, relationship, resolver) {
|
1734
|
+
var adapter = this.adapterFor(owner.constructor);
|
1735
|
+
|
1736
|
+
Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter);
|
1737
|
+
Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", adapter.findBelongsTo);
|
1738
|
+
|
1739
|
+
_findBelongsTo(adapter, this, owner, link, relationship, resolver);
|
1740
|
+
},
|
1741
|
+
|
1930
1742
|
/**
|
1931
1743
|
This method delegates a query to the adapter. This is the one place where
|
1932
1744
|
adapter-level semantics are exposed to the application.
|
@@ -1954,7 +1766,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1954
1766
|
store: this
|
1955
1767
|
});
|
1956
1768
|
|
1957
|
-
var adapter = this.
|
1769
|
+
var adapter = this.adapterFor(type),
|
1958
1770
|
resolver = Ember.RSVP.defer();
|
1959
1771
|
|
1960
1772
|
Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter);
|
@@ -1989,7 +1801,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
1989
1801
|
@returns Promise
|
1990
1802
|
*/
|
1991
1803
|
fetchAll: function(type, array) {
|
1992
|
-
var adapter = this.
|
1804
|
+
var adapter = this.adapterFor(type),
|
1993
1805
|
sinceToken = this.typeMapFor(type).metadata.since,
|
1994
1806
|
resolver = Ember.RSVP.defer();
|
1995
1807
|
|
@@ -2027,6 +1839,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2027
1839
|
@return {DS.RecordArray}
|
2028
1840
|
*/
|
2029
1841
|
all: function(type) {
|
1842
|
+
type = this.modelFor(type);
|
1843
|
+
|
2030
1844
|
var typeMap = this.typeMapFor(type),
|
2031
1845
|
findAllCache = typeMap.findAllCache;
|
2032
1846
|
|
@@ -2045,6 +1859,24 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2045
1859
|
return array;
|
2046
1860
|
},
|
2047
1861
|
|
1862
|
+
|
1863
|
+
/**
|
1864
|
+
This method unloads all of the known records for a given type.
|
1865
|
+
|
1866
|
+
@method unloadAll
|
1867
|
+
@param {Class} type
|
1868
|
+
*/
|
1869
|
+
unloadAll: function(type) {
|
1870
|
+
type = this.modelFor(type);
|
1871
|
+
|
1872
|
+
var typeMap = this.typeMapFor(type),
|
1873
|
+
records = typeMap.records, record;
|
1874
|
+
|
1875
|
+
while(record = records.pop()) {
|
1876
|
+
record.unloadRecord();
|
1877
|
+
}
|
1878
|
+
},
|
1879
|
+
|
2048
1880
|
/**
|
2049
1881
|
Takes a type and filter function, and returns a live RecordArray that
|
2050
1882
|
remains up to date as new records are loaded into the store or created
|
@@ -2061,14 +1893,6 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2061
1893
|
filter function will be invoked again to determine whether it should
|
2062
1894
|
still be in the array.
|
2063
1895
|
|
2064
|
-
Note that the existence of a filter on a type will trigger immediate
|
2065
|
-
materialization of all loaded data for a given type, so you might
|
2066
|
-
not want to use filters for a type if you are loading many records
|
2067
|
-
into the store, many of which are not active at any given time.
|
2068
|
-
|
2069
|
-
In this scenario, you might want to consider filtering the raw
|
2070
|
-
data before loading it into the store.
|
2071
|
-
|
2072
1896
|
@method filter
|
2073
1897
|
@param {Class} type
|
2074
1898
|
@param {Function} filter
|
@@ -2118,6 +1942,18 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2118
1942
|
return !get(this.recordForId(type, id), 'isEmpty');
|
2119
1943
|
},
|
2120
1944
|
|
1945
|
+
/**
|
1946
|
+
This method returns the metadata for a specific type.
|
1947
|
+
|
1948
|
+
@method metadataFor
|
1949
|
+
@param {string} type
|
1950
|
+
@return {object}
|
1951
|
+
*/
|
1952
|
+
metadataFor: function(type) {
|
1953
|
+
type = this.modelFor(type);
|
1954
|
+
return this.typeMapFor(type).metadata;
|
1955
|
+
},
|
1956
|
+
|
2121
1957
|
// ............
|
2122
1958
|
// . UPDATING .
|
2123
1959
|
// ............
|
@@ -2187,7 +2023,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2187
2023
|
|
2188
2024
|
forEach(pending, function(tuple) {
|
2189
2025
|
var record = tuple[0], resolver = tuple[1],
|
2190
|
-
adapter = this.
|
2026
|
+
adapter = this.adapterFor(record.constructor),
|
2191
2027
|
operation;
|
2192
2028
|
|
2193
2029
|
if (get(record, 'isNew')) {
|
@@ -2217,6 +2053,9 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2217
2053
|
*/
|
2218
2054
|
didSaveRecord: function(record, data) {
|
2219
2055
|
if (data) {
|
2056
|
+
// normalize relationship IDs into records
|
2057
|
+
data = normalizeRelationships(this, record.constructor, data, record);
|
2058
|
+
|
2220
2059
|
this.updateId(record, data);
|
2221
2060
|
}
|
2222
2061
|
|
@@ -2308,14 +2147,15 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2308
2147
|
@method _load
|
2309
2148
|
@private
|
2310
2149
|
@param {DS.Model} type
|
2311
|
-
@param data
|
2312
|
-
@param
|
2150
|
+
@param {Object} data
|
2151
|
+
@param {Boolean} partial the data should be merged into
|
2152
|
+
the existing data, not replace it.
|
2313
2153
|
*/
|
2314
|
-
_load: function(type, data) {
|
2154
|
+
_load: function(type, data, partial) {
|
2315
2155
|
var id = coerceId(data.id),
|
2316
2156
|
record = this.recordForId(type, id);
|
2317
2157
|
|
2318
|
-
record.setupData(data);
|
2158
|
+
record.setupData(data, partial);
|
2319
2159
|
this.recordArrayManager.recordDidChange(record);
|
2320
2160
|
|
2321
2161
|
return record;
|
@@ -2326,6 +2166,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2326
2166
|
methods that take a type key (like `find`, `createRecord`,
|
2327
2167
|
etc.)
|
2328
2168
|
|
2169
|
+
@method modelFor
|
2329
2170
|
@param {String} key
|
2330
2171
|
@returns {subclass of DS.Model}
|
2331
2172
|
*/
|
@@ -2405,17 +2246,67 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2405
2246
|
@returns DS.Model the record that was created or
|
2406
2247
|
updated.
|
2407
2248
|
*/
|
2408
|
-
push: function(type, data) {
|
2409
|
-
|
2249
|
+
push: function(type, data, _partial) {
|
2250
|
+
// _partial is an internal param used by `update`.
|
2251
|
+
// If passed, it means that the data should be
|
2252
|
+
// merged into the existing data, not replace it.
|
2253
|
+
|
2254
|
+
Ember.assert("You must include an `id` in a hash passed to `push`", data.id != null);
|
2255
|
+
|
2410
2256
|
type = this.modelFor(type);
|
2411
2257
|
|
2412
|
-
|
2258
|
+
// normalize relationship IDs into records
|
2259
|
+
data = normalizeRelationships(this, type, data);
|
2413
2260
|
|
2414
|
-
this._load(type, data);
|
2261
|
+
this._load(type, data, _partial);
|
2415
2262
|
|
2416
2263
|
return this.recordForId(type, data.id);
|
2417
2264
|
},
|
2418
2265
|
|
2266
|
+
/**
|
2267
|
+
Push some raw data into the store.
|
2268
|
+
|
2269
|
+
The data will be automatically deserialized using the
|
2270
|
+
serializer for the `type` param.
|
2271
|
+
|
2272
|
+
This method can be used both to push in brand new
|
2273
|
+
records, as well as to update existing records.
|
2274
|
+
|
2275
|
+
You can push in more than one type of object at once.
|
2276
|
+
All objects should be in the format expected by the
|
2277
|
+
serializer.
|
2278
|
+
|
2279
|
+
```js
|
2280
|
+
App.ApplicationSerializer = DS.ActiveModelSerializer;
|
2281
|
+
|
2282
|
+
var pushData = {
|
2283
|
+
posts: [
|
2284
|
+
{id: 1, post_title: "Great post", comment_ids: [2]}
|
2285
|
+
],
|
2286
|
+
comments: [
|
2287
|
+
{id: 2, comment_body: "Insightful comment"}
|
2288
|
+
]
|
2289
|
+
}
|
2290
|
+
|
2291
|
+
store.pushPayload('post', pushData);
|
2292
|
+
```
|
2293
|
+
|
2294
|
+
@method push
|
2295
|
+
@param {String} type
|
2296
|
+
@param {Object} payload
|
2297
|
+
*/
|
2298
|
+
|
2299
|
+
pushPayload: function (type, payload) {
|
2300
|
+
var serializer = this.serializerFor(type);
|
2301
|
+
serializer.pushPayload(this, payload);
|
2302
|
+
},
|
2303
|
+
|
2304
|
+
update: function(type, data) {
|
2305
|
+
Ember.assert("You must include an `id` in a hash passed to `update`", data.id != null);
|
2306
|
+
|
2307
|
+
return this.push(type, data, true);
|
2308
|
+
},
|
2309
|
+
|
2419
2310
|
/**
|
2420
2311
|
If you have an Array of normalized data to push,
|
2421
2312
|
you can call `pushMany` with the Array, and it will
|
@@ -2432,6 +2323,20 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2432
2323
|
}, this);
|
2433
2324
|
},
|
2434
2325
|
|
2326
|
+
/**
|
2327
|
+
If you have some metadata to set for a type
|
2328
|
+
you can call `metaForType`.
|
2329
|
+
|
2330
|
+
@method metaForType
|
2331
|
+
@param {String} type
|
2332
|
+
@param {Object} metadata
|
2333
|
+
*/
|
2334
|
+
metaForType: function(type, metadata) {
|
2335
|
+
type = this.modelFor(type);
|
2336
|
+
|
2337
|
+
Ember.merge(this.typeMapFor(type).metadata, metadata);
|
2338
|
+
},
|
2339
|
+
|
2435
2340
|
/**
|
2436
2341
|
Build a brand new record for a given type, ID, and
|
2437
2342
|
initial data.
|
@@ -2449,9 +2354,12 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2449
2354
|
|
2450
2355
|
Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]);
|
2451
2356
|
|
2357
|
+
// lookupFactory should really return an object that creates
|
2358
|
+
// instances with the injections applied
|
2452
2359
|
var record = type._create({
|
2453
2360
|
id: id,
|
2454
2361
|
store: this,
|
2362
|
+
container: this.container
|
2455
2363
|
});
|
2456
2364
|
|
2457
2365
|
if (data) {
|
@@ -2554,19 +2462,19 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2554
2462
|
/**
|
2555
2463
|
Returns the adapter for a given type.
|
2556
2464
|
|
2557
|
-
@method
|
2465
|
+
@method adapterFor
|
2558
2466
|
@private
|
2559
2467
|
@param {subclass of DS.Model} type
|
2560
2468
|
@returns DS.Adapter
|
2561
2469
|
*/
|
2562
|
-
|
2470
|
+
adapterFor: function(type) {
|
2563
2471
|
var container = this.container, adapter;
|
2564
2472
|
|
2565
2473
|
if (container) {
|
2566
2474
|
adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application');
|
2567
2475
|
}
|
2568
2476
|
|
2569
|
-
return adapter || get(this, '
|
2477
|
+
return adapter || get(this, 'defaultAdapter');
|
2570
2478
|
},
|
2571
2479
|
|
2572
2480
|
// ..............................
|
@@ -2590,19 +2498,66 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
|
|
2590
2498
|
@param {String} type the record to serialize
|
2591
2499
|
*/
|
2592
2500
|
serializerFor: function(type) {
|
2593
|
-
|
2501
|
+
type = this.modelFor(type);
|
2502
|
+
var adapter = this.adapterFor(type);
|
2503
|
+
|
2504
|
+
return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer);
|
2505
|
+
}
|
2506
|
+
});
|
2507
|
+
|
2508
|
+
function normalizeRelationships(store, type, data, record) {
|
2509
|
+
type.eachRelationship(function(key, relationship) {
|
2510
|
+
// A link (usually a URL) was already provided in
|
2511
|
+
// normalized form
|
2512
|
+
if (data.links && data.links[key]) {
|
2513
|
+
if (record && relationship.options.async) { record._relationships[key] = null; }
|
2514
|
+
return;
|
2515
|
+
}
|
2594
2516
|
|
2595
|
-
|
2517
|
+
var kind = relationship.kind,
|
2518
|
+
value = data[key];
|
2596
2519
|
|
2597
|
-
if (
|
2598
|
-
|
2520
|
+
if (value == null) { return; }
|
2521
|
+
|
2522
|
+
if (kind === 'belongsTo') {
|
2523
|
+
deserializeRecordId(store, data, key, relationship, value);
|
2524
|
+
} else if (kind === 'hasMany') {
|
2525
|
+
deserializeRecordIds(store, data, key, relationship, value);
|
2599
2526
|
}
|
2527
|
+
});
|
2528
|
+
|
2529
|
+
return data;
|
2530
|
+
}
|
2600
2531
|
|
2601
|
-
|
2602
|
-
|
2603
|
-
|
2532
|
+
function deserializeRecordId(store, data, key, relationship, id) {
|
2533
|
+
if (isNone(id) || id instanceof DS.Model) {
|
2534
|
+
return;
|
2604
2535
|
}
|
2605
|
-
|
2536
|
+
|
2537
|
+
var type;
|
2538
|
+
|
2539
|
+
if (typeof id === 'number' || typeof id === 'string') {
|
2540
|
+
type = typeFor(relationship, key, data);
|
2541
|
+
data[key] = store.recordForId(type, id);
|
2542
|
+
} else if (typeof id === 'object') {
|
2543
|
+
// polymorphic
|
2544
|
+
data[key] = store.recordForId(id.type, id.id);
|
2545
|
+
}
|
2546
|
+
}
|
2547
|
+
|
2548
|
+
function typeFor(relationship, key, data) {
|
2549
|
+
if (relationship.options.polymorphic) {
|
2550
|
+
return data[key + "Type"];
|
2551
|
+
} else {
|
2552
|
+
return relationship.type;
|
2553
|
+
}
|
2554
|
+
}
|
2555
|
+
|
2556
|
+
function deserializeRecordIds(store, data, key, relationship, ids) {
|
2557
|
+
for (var i=0, l=ids.length; i<l; i++) {
|
2558
|
+
deserializeRecordId(store, ids, i, relationship, ids[i]);
|
2559
|
+
}
|
2560
|
+
}
|
2606
2561
|
|
2607
2562
|
// Delegation to the adapter and promise management
|
2608
2563
|
|
@@ -2621,15 +2576,20 @@ function isThenable(object) {
|
|
2621
2576
|
return object && typeof object.then === 'function';
|
2622
2577
|
}
|
2623
2578
|
|
2624
|
-
function serializerFor(
|
2579
|
+
function serializerFor(container, type, defaultSerializer) {
|
2580
|
+
return container.lookup('serializer:'+type) ||
|
2581
|
+
container.lookup('serializer:application') ||
|
2582
|
+
container.lookup('serializer:' + defaultSerializer) ||
|
2583
|
+
container.lookup('serializer:_default');
|
2584
|
+
}
|
2585
|
+
|
2586
|
+
function serializerForAdapter(adapter, type) {
|
2625
2587
|
var serializer = adapter.serializer,
|
2626
2588
|
defaultSerializer = adapter.defaultSerializer,
|
2627
2589
|
container = adapter.container;
|
2628
2590
|
|
2629
2591
|
if (container && serializer === undefined) {
|
2630
|
-
serializer = container
|
2631
|
-
container.lookup('serializer:application') ||
|
2632
|
-
container.lookup('serializer:' + defaultSerializer || 'serializer:_default');
|
2592
|
+
serializer = serializerFor(container, type.typeKey, defaultSerializer);
|
2633
2593
|
}
|
2634
2594
|
|
2635
2595
|
if (serializer === null || serializer === undefined) {
|
@@ -2643,46 +2603,68 @@ function serializerFor(adapter, type) {
|
|
2643
2603
|
|
2644
2604
|
function _find(adapter, store, type, id, resolver) {
|
2645
2605
|
var promise = adapter.find(store, type, id),
|
2646
|
-
serializer =
|
2606
|
+
serializer = serializerForAdapter(adapter, type);
|
2647
2607
|
|
2648
2608
|
return resolve(promise).then(function(payload) {
|
2649
2609
|
Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", payload);
|
2650
2610
|
payload = serializer.extract(store, type, payload, id, 'find');
|
2651
2611
|
|
2652
2612
|
return store.push(type, payload);
|
2613
|
+
}, function(error) {
|
2614
|
+
var record = store.getById(type, id);
|
2615
|
+
record.notFound();
|
2616
|
+
throw error;
|
2653
2617
|
}).then(resolver.resolve, resolver.reject);
|
2654
2618
|
}
|
2655
2619
|
|
2656
2620
|
function _findMany(adapter, store, type, ids, owner, resolver) {
|
2657
2621
|
var promise = adapter.findMany(store, type, ids, owner),
|
2658
|
-
serializer =
|
2622
|
+
serializer = serializerForAdapter(adapter, type);
|
2659
2623
|
|
2660
2624
|
return resolve(promise).then(function(payload) {
|
2661
2625
|
payload = serializer.extract(store, type, payload, null, 'findMany');
|
2662
2626
|
|
2627
|
+
Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
|
2628
|
+
|
2663
2629
|
store.pushMany(type, payload);
|
2664
2630
|
}).then(resolver.resolve, resolver.reject);
|
2665
2631
|
}
|
2666
2632
|
|
2667
2633
|
function _findHasMany(adapter, store, record, link, relationship, resolver) {
|
2668
2634
|
var promise = adapter.findHasMany(store, record, link, relationship),
|
2669
|
-
serializer =
|
2635
|
+
serializer = serializerForAdapter(adapter, relationship.type);
|
2670
2636
|
|
2671
2637
|
return resolve(promise).then(function(payload) {
|
2672
2638
|
payload = serializer.extract(store, relationship.type, payload, null, 'findHasMany');
|
2673
2639
|
|
2640
|
+
Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
|
2641
|
+
|
2674
2642
|
var records = store.pushMany(relationship.type, payload);
|
2675
2643
|
record.updateHasMany(relationship.key, records);
|
2676
2644
|
}).then(resolver.resolve, resolver.reject);
|
2677
2645
|
}
|
2678
2646
|
|
2647
|
+
function _findBelongsTo(adapter, store, record, link, relationship, resolver) {
|
2648
|
+
var promise = adapter.findBelongsTo(store, record, link, relationship),
|
2649
|
+
serializer = serializerForAdapter(adapter, relationship.type);
|
2650
|
+
|
2651
|
+
return resolve(promise).then(function(payload) {
|
2652
|
+
payload = serializer.extract(store, relationship.type, payload, null, 'findBelongsTo');
|
2653
|
+
|
2654
|
+
var record = store.push(relationship.type, payload);
|
2655
|
+
record.updateBelongsTo(relationship.key, record);
|
2656
|
+
}).then(resolver.resolve, resolver.reject);
|
2657
|
+
}
|
2658
|
+
|
2679
2659
|
function _findAll(adapter, store, type, sinceToken, resolver) {
|
2680
2660
|
var promise = adapter.findAll(store, type, sinceToken),
|
2681
|
-
serializer =
|
2661
|
+
serializer = serializerForAdapter(adapter, type);
|
2682
2662
|
|
2683
2663
|
return resolve(promise).then(function(payload) {
|
2684
2664
|
payload = serializer.extract(store, type, payload, null, 'findAll');
|
2685
2665
|
|
2666
|
+
Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
|
2667
|
+
|
2686
2668
|
store.pushMany(type, payload);
|
2687
2669
|
store.didUpdateAll(type);
|
2688
2670
|
return store.all(type);
|
@@ -2691,11 +2673,13 @@ function _findAll(adapter, store, type, sinceToken, resolver) {
|
|
2691
2673
|
|
2692
2674
|
function _findQuery(adapter, store, type, query, recordArray, resolver) {
|
2693
2675
|
var promise = adapter.findQuery(store, type, query, recordArray),
|
2694
|
-
serializer =
|
2676
|
+
serializer = serializerForAdapter(adapter, type);
|
2695
2677
|
|
2696
2678
|
return resolve(promise).then(function(payload) {
|
2697
2679
|
payload = serializer.extract(store, type, payload, null, 'findAll');
|
2698
2680
|
|
2681
|
+
Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array');
|
2682
|
+
|
2699
2683
|
recordArray.load(payload);
|
2700
2684
|
return recordArray;
|
2701
2685
|
}).then(resolver.resolve, resolver.reject);
|
@@ -2704,12 +2688,12 @@ function _findQuery(adapter, store, type, query, recordArray, resolver) {
|
|
2704
2688
|
function _commit(adapter, store, operation, record, resolver) {
|
2705
2689
|
var type = record.constructor,
|
2706
2690
|
promise = adapter[operation](store, type, record),
|
2707
|
-
serializer =
|
2691
|
+
serializer = serializerForAdapter(adapter, type);
|
2708
2692
|
|
2709
2693
|
Ember.assert("Your adapter's '" + operation + "' method must return a promise, but it returned " + promise, isThenable(promise));
|
2710
2694
|
|
2711
2695
|
return promise.then(function(payload) {
|
2712
|
-
payload = serializer.extract(store, type, payload, get(record, 'id'), operation);
|
2696
|
+
if (payload) { payload = serializer.extract(store, type, payload, get(record, 'id'), operation); }
|
2713
2697
|
store.didSaveRecord(record, payload);
|
2714
2698
|
return record;
|
2715
2699
|
}, function(reason) {
|
@@ -2732,8 +2716,7 @@ function _commit(adapter, store, operation, record, resolver) {
|
|
2732
2716
|
@module ember-data
|
2733
2717
|
*/
|
2734
2718
|
|
2735
|
-
var get = Ember.get, set = Ember.set
|
2736
|
-
once = Ember.run.once, arrayMap = Ember.ArrayPolyfills.map;
|
2719
|
+
var get = Ember.get, set = Ember.set;
|
2737
2720
|
|
2738
2721
|
/*
|
2739
2722
|
WARNING: Much of these docs are inaccurate as of bf8497.
|
@@ -2901,10 +2884,14 @@ var hasDefinedProperties = function(object) {
|
|
2901
2884
|
};
|
2902
2885
|
|
2903
2886
|
var didSetProperty = function(record, context) {
|
2904
|
-
if (context.value
|
2887
|
+
if (context.value === context.originalValue) {
|
2888
|
+
delete record._attributes[context.name];
|
2889
|
+
record.send('propertyWasReset', context.name);
|
2890
|
+
} else if (context.value !== context.oldValue) {
|
2905
2891
|
record.send('becomeDirty');
|
2906
|
-
record.updateRecordArraysLater();
|
2907
2892
|
}
|
2893
|
+
|
2894
|
+
record.updateRecordArraysLater();
|
2908
2895
|
};
|
2909
2896
|
|
2910
2897
|
// Implementation notes:
|
@@ -2962,10 +2949,20 @@ var DirtyState = {
|
|
2962
2949
|
// This means that there are local pending changes, but they
|
2963
2950
|
// have not yet begun to be saved, and are not invalid.
|
2964
2951
|
uncommitted: {
|
2965
|
-
|
2966
2952
|
// EVENTS
|
2967
2953
|
didSetProperty: didSetProperty,
|
2968
2954
|
|
2955
|
+
propertyWasReset: function(record, name) {
|
2956
|
+
var stillDirty = false;
|
2957
|
+
|
2958
|
+
for (var prop in record._attributes) {
|
2959
|
+
stillDirty = true;
|
2960
|
+
break;
|
2961
|
+
}
|
2962
|
+
|
2963
|
+
if (!stillDirty) { record.send('rolledBack'); }
|
2964
|
+
},
|
2965
|
+
|
2969
2966
|
pushedData: Ember.K,
|
2970
2967
|
|
2971
2968
|
becomeDirty: Ember.K,
|
@@ -2978,7 +2975,7 @@ var DirtyState = {
|
|
2978
2975
|
get(record, 'store').reloadRecord(record, resolver);
|
2979
2976
|
},
|
2980
2977
|
|
2981
|
-
|
2978
|
+
rolledBack: function(record) {
|
2982
2979
|
record.transitionTo('loaded.saved');
|
2983
2980
|
},
|
2984
2981
|
|
@@ -3108,6 +3105,10 @@ var createdState = dirtyState({
|
|
3108
3105
|
isNew: true
|
3109
3106
|
});
|
3110
3107
|
|
3108
|
+
createdState.uncommitted.rolledBack = function(record) {
|
3109
|
+
record.transitionTo('deleted.saved');
|
3110
|
+
};
|
3111
|
+
|
3111
3112
|
var updatedState = dirtyState({
|
3112
3113
|
dirtyType: 'updated'
|
3113
3114
|
});
|
@@ -3138,6 +3139,16 @@ var RootState = {
|
|
3138
3139
|
isNew: false,
|
3139
3140
|
isValid: true,
|
3140
3141
|
|
3142
|
+
// DEFAULT EVENTS
|
3143
|
+
|
3144
|
+
// Trying to roll back if you're not in the dirty state
|
3145
|
+
// doesn't change your state. For example, if you're in the
|
3146
|
+
// in-flight state, rolling back the record doesn't move
|
3147
|
+
// you out of the in-flight state.
|
3148
|
+
rolledBack: Ember.K,
|
3149
|
+
|
3150
|
+
propertyWasReset: Ember.K,
|
3151
|
+
|
3141
3152
|
// SUBSTATES
|
3142
3153
|
|
3143
3154
|
// A record begins its lifecycle in the `empty` state.
|
@@ -3149,7 +3160,8 @@ var RootState = {
|
|
3149
3160
|
isEmpty: true,
|
3150
3161
|
|
3151
3162
|
// EVENTS
|
3152
|
-
loadingData: function(record) {
|
3163
|
+
loadingData: function(record, promise) {
|
3164
|
+
record._loadingPromise = promise;
|
3153
3165
|
record.transitionTo('loading');
|
3154
3166
|
},
|
3155
3167
|
|
@@ -3163,6 +3175,7 @@ var RootState = {
|
|
3163
3175
|
|
3164
3176
|
pushedData: function(record) {
|
3165
3177
|
record.transitionTo('loaded.saved');
|
3178
|
+
record.triggerLater('didLoad');
|
3166
3179
|
}
|
3167
3180
|
},
|
3168
3181
|
|
@@ -3176,6 +3189,10 @@ var RootState = {
|
|
3176
3189
|
// FLAGS
|
3177
3190
|
isLoading: true,
|
3178
3191
|
|
3192
|
+
exit: function(record) {
|
3193
|
+
record._loadingPromise = null;
|
3194
|
+
},
|
3195
|
+
|
3179
3196
|
// EVENTS
|
3180
3197
|
pushedData: function(record) {
|
3181
3198
|
record.transitionTo('loaded.saved');
|
@@ -3185,6 +3202,10 @@ var RootState = {
|
|
3185
3202
|
|
3186
3203
|
becameError: function(record) {
|
3187
3204
|
record.triggerLater('becameError', record);
|
3205
|
+
},
|
3206
|
+
|
3207
|
+
notFound: function(record) {
|
3208
|
+
record.transitionTo('empty');
|
3188
3209
|
}
|
3189
3210
|
},
|
3190
3211
|
|
@@ -3301,7 +3322,7 @@ var RootState = {
|
|
3301
3322
|
becomeDirty: Ember.K,
|
3302
3323
|
deleteRecord: Ember.K,
|
3303
3324
|
|
3304
|
-
|
3325
|
+
rolledBack: function(record) {
|
3305
3326
|
record.transitionTo('loaded.saved');
|
3306
3327
|
}
|
3307
3328
|
},
|
@@ -3322,6 +3343,11 @@ var RootState = {
|
|
3322
3343
|
record.transitionTo('saved');
|
3323
3344
|
|
3324
3345
|
record.send('invokeLifecycleCallbacks');
|
3346
|
+
},
|
3347
|
+
|
3348
|
+
becameError: function(record) {
|
3349
|
+
record.transitionTo('uncommitted');
|
3350
|
+
record.triggerLater('becameError', record);
|
3325
3351
|
}
|
3326
3352
|
},
|
3327
3353
|
|
@@ -3355,8 +3381,6 @@ var RootState = {
|
|
3355
3381
|
}
|
3356
3382
|
};
|
3357
3383
|
|
3358
|
-
var hasOwnProp = {}.hasOwnProperty;
|
3359
|
-
|
3360
3384
|
function wireState(object, parent, name) {
|
3361
3385
|
/*jshint proto:true*/
|
3362
3386
|
// TODO: Use Object.create and copy instead
|
@@ -3387,11 +3411,9 @@ DS.RootState = RootState;
|
|
3387
3411
|
@module ember-data
|
3388
3412
|
*/
|
3389
3413
|
|
3390
|
-
var get = Ember.get, set = Ember.set,
|
3414
|
+
var get = Ember.get, set = Ember.set,
|
3391
3415
|
merge = Ember.merge, once = Ember.run.once;
|
3392
3416
|
|
3393
|
-
var arrayMap = Ember.ArrayPolyfills.map;
|
3394
|
-
|
3395
3417
|
var retrieveFromCurrentState = Ember.computed(function(key, value) {
|
3396
3418
|
return get(get(this, 'currentState'), key);
|
3397
3419
|
}).property('currentState').readOnly();
|
@@ -3455,7 +3477,8 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3455
3477
|
@returns {Object} A JSON representation of the object.
|
3456
3478
|
*/
|
3457
3479
|
toJSON: function(options) {
|
3458
|
-
|
3480
|
+
// container is for lazy transform lookups
|
3481
|
+
var serializer = DS.JSONSerializer.create({ container: this.container });
|
3459
3482
|
return serializer.serialize(this, options);
|
3460
3483
|
},
|
3461
3484
|
|
@@ -3592,14 +3615,18 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3592
3615
|
if (transaction) { fn(transaction); }
|
3593
3616
|
},
|
3594
3617
|
|
3595
|
-
loadingData: function() {
|
3596
|
-
this.send('loadingData');
|
3618
|
+
loadingData: function(promise) {
|
3619
|
+
this.send('loadingData', promise);
|
3597
3620
|
},
|
3598
3621
|
|
3599
3622
|
loadedData: function() {
|
3600
3623
|
this.send('loadedData');
|
3601
3624
|
},
|
3602
3625
|
|
3626
|
+
notFound: function() {
|
3627
|
+
this.send('notFound');
|
3628
|
+
},
|
3629
|
+
|
3603
3630
|
pushedData: function() {
|
3604
3631
|
this.send('pushedData');
|
3605
3632
|
},
|
@@ -3632,6 +3659,27 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3632
3659
|
}
|
3633
3660
|
},
|
3634
3661
|
|
3662
|
+
/**
|
3663
|
+
Gets the diff for the current model.
|
3664
|
+
|
3665
|
+
@method changedAttributes
|
3666
|
+
|
3667
|
+
@returns {Object} an object, whose keys are changed properties,
|
3668
|
+
and value is an [oldProp, newProp] array.
|
3669
|
+
*/
|
3670
|
+
changedAttributes: function() {
|
3671
|
+
var oldData = get(this, '_data'),
|
3672
|
+
newData = get(this, '_attributes'),
|
3673
|
+
diffData = {},
|
3674
|
+
prop;
|
3675
|
+
|
3676
|
+
for (prop in newData) {
|
3677
|
+
diffData[prop] = [oldData[prop], newData[prop]];
|
3678
|
+
}
|
3679
|
+
|
3680
|
+
return diffData;
|
3681
|
+
},
|
3682
|
+
|
3635
3683
|
adapterWillCommit: function() {
|
3636
3684
|
this.send('willCommit');
|
3637
3685
|
},
|
@@ -3677,6 +3725,7 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3677
3725
|
var relationships = get(this.constructor, 'relationshipsByName');
|
3678
3726
|
this.updateRecordArraysLater();
|
3679
3727
|
relationships.forEach(function(name, relationship) {
|
3728
|
+
if (this._data.links && this._data.links[name]) { return; }
|
3680
3729
|
if (relationship.kind === 'hasMany') {
|
3681
3730
|
this.hasManyDidChange(relationship.key);
|
3682
3731
|
}
|
@@ -3687,8 +3736,6 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3687
3736
|
var hasMany = this._relationships[key];
|
3688
3737
|
|
3689
3738
|
if (hasMany) {
|
3690
|
-
var type = get(this.constructor, 'relationshipsByName').get(key).type;
|
3691
|
-
var store = get(this, 'store');
|
3692
3739
|
var records = this._data[key] || [];
|
3693
3740
|
|
3694
3741
|
set(hasMany, 'content', Ember.A(records));
|
@@ -3701,8 +3748,19 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3701
3748
|
Ember.run.once(this, this.updateRecordArrays);
|
3702
3749
|
},
|
3703
3750
|
|
3704
|
-
setupData: function(data) {
|
3705
|
-
|
3751
|
+
setupData: function(data, partial) {
|
3752
|
+
if (partial) {
|
3753
|
+
Ember.merge(this._data, data);
|
3754
|
+
} else {
|
3755
|
+
this._data = data;
|
3756
|
+
}
|
3757
|
+
|
3758
|
+
var relationships = this._relationships;
|
3759
|
+
|
3760
|
+
this.eachRelationship(function(name, rel) {
|
3761
|
+
if (data.links && data.links[name]) { return; }
|
3762
|
+
if (rel.options.async) { relationships[name] = null; }
|
3763
|
+
});
|
3706
3764
|
|
3707
3765
|
if (data) { this.pushedData(); }
|
3708
3766
|
|
@@ -3729,9 +3787,19 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3729
3787
|
this.hasManyDidChange(name);
|
3730
3788
|
},
|
3731
3789
|
|
3790
|
+
updateBelongsTo: function(name, record) {
|
3791
|
+
this._data[name] = record;
|
3792
|
+
},
|
3793
|
+
|
3732
3794
|
rollback: function() {
|
3733
|
-
this.
|
3734
|
-
|
3795
|
+
this._attributes = {};
|
3796
|
+
|
3797
|
+
if (get(this, 'isError')) {
|
3798
|
+
this._inFlightAttributes = {};
|
3799
|
+
set(this, 'isError', false);
|
3800
|
+
}
|
3801
|
+
|
3802
|
+
this.send('rolledBack');
|
3735
3803
|
|
3736
3804
|
this.suspendRelationshipObservers(function() {
|
3737
3805
|
this.notifyPropertyChange('data');
|
@@ -3781,7 +3849,7 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
|
|
3781
3849
|
@method save
|
3782
3850
|
*/
|
3783
3851
|
save: function() {
|
3784
|
-
var resolver = Ember.RSVP.defer()
|
3852
|
+
var resolver = Ember.RSVP.defer();
|
3785
3853
|
|
3786
3854
|
this.get('store').scheduleSave(this, resolver);
|
3787
3855
|
this._inFlightAttributes = this._attributes;
|
@@ -3962,21 +4030,48 @@ DS.Model.reopen({
|
|
3962
4030
|
}
|
3963
4031
|
});
|
3964
4032
|
|
3965
|
-
function
|
3966
|
-
|
3967
|
-
|
3968
|
-
|
3969
|
-
|
3970
|
-
if (typeof options.defaultValue === "function") {
|
3971
|
-
value = options.defaultValue();
|
3972
|
-
} else {
|
3973
|
-
value = options.defaultValue;
|
3974
|
-
}
|
4033
|
+
function getDefaultValue(record, options, key) {
|
4034
|
+
if (typeof options.defaultValue === "function") {
|
4035
|
+
return options.defaultValue();
|
4036
|
+
} else {
|
4037
|
+
return options.defaultValue;
|
3975
4038
|
}
|
4039
|
+
}
|
3976
4040
|
|
3977
|
-
|
4041
|
+
function hasValue(record, key) {
|
4042
|
+
return record._attributes.hasOwnProperty(key) ||
|
4043
|
+
record._inFlightAttributes.hasOwnProperty(key) ||
|
4044
|
+
record._data.hasOwnProperty(key);
|
4045
|
+
}
|
4046
|
+
|
4047
|
+
function getValue(record, key) {
|
4048
|
+
if (record._attributes.hasOwnProperty(key)) {
|
4049
|
+
return record._attributes[key];
|
4050
|
+
} else if (record._inFlightAttributes.hasOwnProperty(key)) {
|
4051
|
+
return record._inFlightAttributes[key];
|
4052
|
+
} else {
|
4053
|
+
return record._data[key];
|
4054
|
+
}
|
3978
4055
|
}
|
3979
4056
|
|
4057
|
+
/**
|
4058
|
+
`DS.attr` defines an attribute on a DS.Model.
|
4059
|
+
By default, attributes are passed through as-is, however you can specify an
|
4060
|
+
optional type to have the value automatically transformed.
|
4061
|
+
Ember Data ships with four basic transform types:
|
4062
|
+
'string', 'number', 'boolean' and 'date'.
|
4063
|
+
You can define your own transforms by subclassing DS.Transform.
|
4064
|
+
|
4065
|
+
DS.attr takes an optional hash as a second parameter, currently
|
4066
|
+
supported options are:
|
4067
|
+
'defaultValue': Pass a string or a function to be called to set the attribute
|
4068
|
+
to a default value if none is supplied.
|
4069
|
+
|
4070
|
+
@method attr
|
4071
|
+
@param {String} type the attribute type
|
4072
|
+
@param {Object} options a hash of options
|
4073
|
+
*/
|
4074
|
+
|
3980
4075
|
DS.attr = function(type, options) {
|
3981
4076
|
options = options || {};
|
3982
4077
|
|
@@ -3986,18 +4081,19 @@ DS.attr = function(type, options) {
|
|
3986
4081
|
options: options
|
3987
4082
|
};
|
3988
4083
|
|
3989
|
-
return Ember.computed(function(key, value
|
4084
|
+
return Ember.computed(function(key, value) {
|
3990
4085
|
if (arguments.length > 1) {
|
3991
4086
|
Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id');
|
3992
|
-
|
4087
|
+
var oldValue = this._attributes[key] || this._inFlightAttributes[key] || this._data[key];
|
4088
|
+
this.send('didSetProperty', { name: key, oldValue: oldValue, originalValue: this._data[key], value: value });
|
3993
4089
|
this._attributes[key] = value;
|
3994
|
-
|
3995
|
-
|
4090
|
+
return value;
|
4091
|
+
} else if (hasValue(this, key)) {
|
4092
|
+
return getValue(this, key);
|
3996
4093
|
} else {
|
3997
|
-
|
4094
|
+
return getDefaultValue(this, options, key);
|
3998
4095
|
}
|
3999
4096
|
|
4000
|
-
return value;
|
4001
4097
|
// `data` is never set directly. However, it may be
|
4002
4098
|
// invalidated from the state manager's setData
|
4003
4099
|
// event.
|
@@ -4545,16 +4641,33 @@ function asyncBelongsTo(type, options, meta) {
|
|
4545
4641
|
store = get(this, 'store');
|
4546
4642
|
|
4547
4643
|
if (arguments.length === 2) {
|
4548
|
-
Ember.assert("You can only add a '" + type + "' record to this relationship", !value || store.modelFor(type)
|
4644
|
+
Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof store.modelFor(type));
|
4549
4645
|
return value === undefined ? null : value;
|
4550
4646
|
}
|
4551
4647
|
|
4552
|
-
|
4648
|
+
var link = data.links && data.links[key],
|
4649
|
+
belongsTo = data[key];
|
4650
|
+
|
4651
|
+
if(!isNone(belongsTo)) {
|
4652
|
+
var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo);
|
4653
|
+
return DS.PromiseObject.create({promise: promise});
|
4654
|
+
} else if (link) {
|
4655
|
+
var resolver = Ember.RSVP.defer();
|
4656
|
+
store.findBelongsTo(this, link, meta, resolver);
|
4657
|
+
return DS.PromiseObject.create({ promise: resolver.promise });
|
4658
|
+
} else {
|
4659
|
+
return null;
|
4660
|
+
}
|
4553
4661
|
}).property('data').meta(meta);
|
4554
4662
|
}
|
4555
4663
|
|
4556
4664
|
DS.belongsTo = function(type, options) {
|
4557
|
-
|
4665
|
+
if (typeof type === 'object') {
|
4666
|
+
options = type;
|
4667
|
+
type = undefined;
|
4668
|
+
} else {
|
4669
|
+
Ember.assert("The first argument DS.belongsTo must be a model type or string, like DS.belongsTo(App.Person)", !!type && (typeof type === 'string' || DS.Model.detect(type)));
|
4670
|
+
}
|
4558
4671
|
|
4559
4672
|
options = options || {};
|
4560
4673
|
|
@@ -4569,17 +4682,13 @@ DS.belongsTo = function(type, options) {
|
|
4569
4682
|
store = get(this, 'store'), belongsTo, typeClass;
|
4570
4683
|
|
4571
4684
|
if (typeof type === 'string') {
|
4572
|
-
|
4573
|
-
typeClass = store.modelFor(type);
|
4574
|
-
} else {
|
4575
|
-
typeClass = get(Ember.lookup, type);
|
4576
|
-
}
|
4685
|
+
typeClass = store.modelFor(type);
|
4577
4686
|
} else {
|
4578
4687
|
typeClass = type;
|
4579
4688
|
}
|
4580
4689
|
|
4581
4690
|
if (arguments.length === 2) {
|
4582
|
-
Ember.assert("You can only add a '" + type + "' record to this relationship", !value || typeClass
|
4691
|
+
Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof typeClass);
|
4583
4692
|
return value === undefined ? null : value;
|
4584
4693
|
}
|
4585
4694
|
|
@@ -4587,9 +4696,7 @@ DS.belongsTo = function(type, options) {
|
|
4587
4696
|
|
4588
4697
|
if (isNone(belongsTo)) { return null; }
|
4589
4698
|
|
4590
|
-
|
4591
|
-
store.fetchRecord(belongsTo);
|
4592
|
-
}
|
4699
|
+
store.fetchRecord(belongsTo);
|
4593
4700
|
|
4594
4701
|
return belongsTo;
|
4595
4702
|
}).property('data').meta(meta);
|
@@ -4613,11 +4720,12 @@ DS.Model.reopen({
|
|
4613
4720
|
*/
|
4614
4721
|
belongsToWillChange: Ember.beforeObserver(function(record, key) {
|
4615
4722
|
if (get(record, 'isLoaded')) {
|
4616
|
-
var oldParent = get(record, key)
|
4617
|
-
|
4723
|
+
var oldParent = get(record, key);
|
4724
|
+
|
4725
|
+
if (oldParent) {
|
4726
|
+
var store = get(record, 'store'),
|
4727
|
+
change = DS.RelationshipChange.createChange(record, oldParent, store, { key: key, kind: "belongsTo", changeType: "remove" });
|
4618
4728
|
|
4619
|
-
if (oldParent){
|
4620
|
-
var change = DS.RelationshipChange.createChange(record, oldParent, store, { key: key, kind: "belongsTo", changeType: "remove" });
|
4621
4729
|
change.sync();
|
4622
4730
|
this._changesToSync[key] = change;
|
4623
4731
|
}
|
@@ -4634,7 +4742,8 @@ DS.Model.reopen({
|
|
4634
4742
|
belongsToDidChange: Ember.immediateObserver(function(record, key) {
|
4635
4743
|
if (get(record, 'isLoaded')) {
|
4636
4744
|
var newParent = get(record, key);
|
4637
|
-
|
4745
|
+
|
4746
|
+
if (newParent) {
|
4638
4747
|
var store = get(record, 'store'),
|
4639
4748
|
change = DS.RelationshipChange.createChange(record, newParent, store, { key: key, kind: "belongsTo", changeType: "add" });
|
4640
4749
|
|
@@ -4656,10 +4765,11 @@ DS.Model.reopen({
|
|
4656
4765
|
*/
|
4657
4766
|
|
4658
4767
|
var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
|
4659
|
-
var forEach = Ember.EnumerableUtils.forEach;
|
4660
4768
|
|
4661
4769
|
function asyncHasMany(type, options, meta) {
|
4662
4770
|
return Ember.computed(function(key, value) {
|
4771
|
+
if (this._relationships[key]) { return this._relationships[key]; }
|
4772
|
+
|
4663
4773
|
var resolver = Ember.RSVP.defer();
|
4664
4774
|
|
4665
4775
|
var relationship = buildRelationship(this, key, options, function(store, data) {
|
@@ -4707,14 +4817,17 @@ function hasRelationship(type, options) {
|
|
4707
4817
|
return Ember.computed(function(key, value) {
|
4708
4818
|
return buildRelationship(this, key, options, function(store, data) {
|
4709
4819
|
var records = data[key];
|
4710
|
-
Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.
|
4820
|
+
Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", Ember.A(records).everyProperty('isEmpty', false));
|
4711
4821
|
return store.findMany(this, data[key], meta.type);
|
4712
4822
|
});
|
4713
4823
|
}).property('data').meta(meta);
|
4714
4824
|
}
|
4715
4825
|
|
4716
4826
|
DS.hasMany = function(type, options) {
|
4717
|
-
|
4827
|
+
if (typeof type === 'object') {
|
4828
|
+
options = type;
|
4829
|
+
type = undefined;
|
4830
|
+
}
|
4718
4831
|
return hasRelationship(type, options);
|
4719
4832
|
};
|
4720
4833
|
|
@@ -4753,7 +4866,7 @@ DS.Model.reopen({
|
|
4753
4866
|
being defined. So, for example, when the user does this:
|
4754
4867
|
|
4755
4868
|
DS.Model.extend({
|
4756
|
-
parent: DS.belongsTo(
|
4869
|
+
parent: DS.belongsTo('user')
|
4757
4870
|
});
|
4758
4871
|
|
4759
4872
|
This hook would be called with "parent" as the key and the computed
|
@@ -4807,7 +4920,7 @@ DS.Model.reopenClass({
|
|
4807
4920
|
For example, if you define a model like this:
|
4808
4921
|
|
4809
4922
|
App.Post = DS.Model.extend({
|
4810
|
-
comments: DS.hasMany(
|
4923
|
+
comments: DS.hasMany('comment')
|
4811
4924
|
});
|
4812
4925
|
|
4813
4926
|
Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
|
@@ -4881,9 +4994,9 @@ DS.Model.reopenClass({
|
|
4881
4994
|
For example, given the following model definition:
|
4882
4995
|
|
4883
4996
|
App.Blog = DS.Model.extend({
|
4884
|
-
users: DS.hasMany(
|
4885
|
-
owner: DS.belongsTo(
|
4886
|
-
posts: DS.hasMany(
|
4997
|
+
users: DS.hasMany('user'),
|
4998
|
+
owner: DS.belongsTo('user'),
|
4999
|
+
posts: DS.hasMany('post')
|
4887
5000
|
});
|
4888
5001
|
|
4889
5002
|
This computed property would return a map describing these
|
@@ -4913,7 +5026,7 @@ DS.Model.reopenClass({
|
|
4913
5026
|
// it to the map.
|
4914
5027
|
if (meta.isRelationship) {
|
4915
5028
|
if (typeof meta.type === 'string') {
|
4916
|
-
meta.type =
|
5029
|
+
meta.type = this.store.modelFor(meta.type);
|
4917
5030
|
}
|
4918
5031
|
|
4919
5032
|
var relationshipsForType = map.get(meta.type);
|
@@ -4931,10 +5044,10 @@ DS.Model.reopenClass({
|
|
4931
5044
|
definition:
|
4932
5045
|
|
4933
5046
|
App.Blog = DS.Model.extend({
|
4934
|
-
users: DS.hasMany(
|
4935
|
-
owner: DS.belongsTo(
|
5047
|
+
users: DS.hasMany('user'),
|
5048
|
+
owner: DS.belongsTo('user'),
|
4936
5049
|
|
4937
|
-
posts: DS.hasMany(
|
5050
|
+
posts: DS.hasMany('post')
|
4938
5051
|
});
|
4939
5052
|
|
4940
5053
|
This property would contain the following:
|
@@ -4970,9 +5083,10 @@ DS.Model.reopenClass({
|
|
4970
5083
|
For example, given a model with this definition:
|
4971
5084
|
|
4972
5085
|
App.Blog = DS.Model.extend({
|
4973
|
-
users: DS.hasMany(
|
4974
|
-
owner: DS.belongsTo(
|
4975
|
-
|
5086
|
+
users: DS.hasMany('user'),
|
5087
|
+
owner: DS.belongsTo('user'),
|
5088
|
+
|
5089
|
+
posts: DS.hasMany('post')
|
4976
5090
|
});
|
4977
5091
|
|
4978
5092
|
This property would contain the following:
|
@@ -4997,7 +5111,7 @@ DS.Model.reopenClass({
|
|
4997
5111
|
type = meta.type;
|
4998
5112
|
|
4999
5113
|
if (typeof type === 'string') {
|
5000
|
-
type = get(this, type, false) ||
|
5114
|
+
type = get(this, type, false) || this.store.modelFor(type);
|
5001
5115
|
}
|
5002
5116
|
|
5003
5117
|
Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type);
|
@@ -5020,10 +5134,10 @@ DS.Model.reopenClass({
|
|
5020
5134
|
definition:
|
5021
5135
|
|
5022
5136
|
App.Blog = DS.Model.extend({
|
5023
|
-
users: DS.hasMany(
|
5024
|
-
owner: DS.belongsTo(
|
5137
|
+
users: DS.hasMany('user'),
|
5138
|
+
owner: DS.belongsTo('user'),
|
5025
5139
|
|
5026
|
-
posts: DS.hasMany(
|
5140
|
+
posts: DS.hasMany('post')
|
5027
5141
|
});
|
5028
5142
|
|
5029
5143
|
This property would contain the following:
|
@@ -5047,6 +5161,12 @@ DS.Model.reopenClass({
|
|
5047
5161
|
meta.key = name;
|
5048
5162
|
type = meta.type;
|
5049
5163
|
|
5164
|
+
if (!type && meta.kind === 'hasMany') {
|
5165
|
+
type = Ember.String.singularize(name);
|
5166
|
+
} else if (!type) {
|
5167
|
+
type = name;
|
5168
|
+
}
|
5169
|
+
|
5050
5170
|
if (typeof type === 'string') {
|
5051
5171
|
meta.type = this.store.modelFor(type);
|
5052
5172
|
}
|
@@ -5066,10 +5186,10 @@ DS.Model.reopenClass({
|
|
5066
5186
|
For example:
|
5067
5187
|
|
5068
5188
|
App.Blog = DS.Model.extend({
|
5069
|
-
users: DS.hasMany(
|
5070
|
-
owner: DS.belongsTo(
|
5189
|
+
users: DS.hasMany('user'),
|
5190
|
+
owner: DS.belongsTo('user'),
|
5071
5191
|
|
5072
|
-
posts: DS.hasMany(
|
5192
|
+
posts: DS.hasMany('post'),
|
5073
5193
|
|
5074
5194
|
title: DS.attr('string')
|
5075
5195
|
});
|
@@ -5375,9 +5495,8 @@ DS.RecordArrayManager = Ember.Object.extend({
|
|
5375
5495
|
@module ember-data
|
5376
5496
|
*/
|
5377
5497
|
|
5378
|
-
var get = Ember.get, set = Ember.set
|
5379
|
-
var
|
5380
|
-
var resolve = Ember.RSVP.resolve;
|
5498
|
+
var get = Ember.get, set = Ember.set;
|
5499
|
+
var map = Ember.ArrayPolyfills.map;
|
5381
5500
|
|
5382
5501
|
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
|
5383
5502
|
|
@@ -5391,18 +5510,6 @@ DS.InvalidError = function(errors) {
|
|
5391
5510
|
};
|
5392
5511
|
DS.InvalidError.prototype = Ember.create(Error.prototype);
|
5393
5512
|
|
5394
|
-
function isThenable(object) {
|
5395
|
-
return object && typeof object.then === 'function';
|
5396
|
-
}
|
5397
|
-
|
5398
|
-
// Simple dispatcher to support overriding the aliased
|
5399
|
-
// method in subclasses.
|
5400
|
-
function aliasMethod(methodName) {
|
5401
|
-
return function() {
|
5402
|
-
return this[methodName].apply(this, arguments);
|
5403
|
-
};
|
5404
|
-
}
|
5405
|
-
|
5406
5513
|
/**
|
5407
5514
|
An adapter is an object that receives requests from a store and
|
5408
5515
|
translates them into the appropriate action to take against your
|
@@ -5441,7 +5548,7 @@ function aliasMethod(methodName) {
|
|
5441
5548
|
* `deleteRecords()`
|
5442
5549
|
* `commit()`
|
5443
5550
|
|
5444
|
-
For an example implementation, see `DS.
|
5551
|
+
For an example implementation, see `DS.RESTAdapter`, the
|
5445
5552
|
included REST adapter.
|
5446
5553
|
|
5447
5554
|
@class Adapter
|
@@ -5456,8 +5563,8 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
|
|
5456
5563
|
The `find()` method is invoked when the store is asked for a record that
|
5457
5564
|
has not previously been loaded. In response to `find()` being called, you
|
5458
5565
|
should query your persistence layer for a record with the given ID. Once
|
5459
|
-
found, you can asynchronously call the store's `
|
5460
|
-
the record.
|
5566
|
+
found, you can asynchronously call the store's `push()` method to push
|
5567
|
+
the record into the store.
|
5461
5568
|
|
5462
5569
|
Here is an example `find` implementation:
|
5463
5570
|
|
@@ -5468,8 +5575,8 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
|
|
5468
5575
|
jQuery.getJSON(url, function(data) {
|
5469
5576
|
// data is a hash of key/value pairs. If your server returns a
|
5470
5577
|
// root, simply do something like:
|
5471
|
-
// store.
|
5472
|
-
store.
|
5578
|
+
// store.push(type, id, data.person)
|
5579
|
+
store.push(type, id, data);
|
5473
5580
|
});
|
5474
5581
|
}
|
5475
5582
|
|
@@ -5544,9 +5651,9 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
|
|
5544
5651
|
method on success or `didError` method on failure.
|
5545
5652
|
|
5546
5653
|
@method createRecord
|
5547
|
-
@
|
5548
|
-
@
|
5549
|
-
@
|
5654
|
+
@param {DS.Store} store
|
5655
|
+
@param {subclass of DS.Model} type the DS.Model class of the record
|
5656
|
+
@param {DS.Model} record
|
5550
5657
|
*/
|
5551
5658
|
createRecord: Ember.required(Function),
|
5552
5659
|
|
@@ -5557,9 +5664,9 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
|
|
5557
5664
|
Serializes the record update and send it to the server.
|
5558
5665
|
|
5559
5666
|
@method updateRecord
|
5560
|
-
@
|
5561
|
-
@
|
5562
|
-
@
|
5667
|
+
@param {DS.Store} store
|
5668
|
+
@param {subclass of DS.Model} type the DS.Model class of the record
|
5669
|
+
@param {DS.Model} record
|
5563
5670
|
*/
|
5564
5671
|
updateRecord: Ember.required(Function),
|
5565
5672
|
|
@@ -5570,9 +5677,9 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
|
|
5570
5677
|
Sends a delete request for the record to the server.
|
5571
5678
|
|
5572
5679
|
@method deleteRecord
|
5573
|
-
@
|
5574
|
-
@
|
5575
|
-
@
|
5680
|
+
@param {DS.Store} store
|
5681
|
+
@param {subclass of DS.Model} type the DS.Model class of the record
|
5682
|
+
@param {DS.Model} record
|
5576
5683
|
*/
|
5577
5684
|
deleteRecord: Ember.required(Function),
|
5578
5685
|
|
@@ -5584,12 +5691,12 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
|
|
5584
5691
|
server requests.
|
5585
5692
|
|
5586
5693
|
@method findMany
|
5587
|
-
@
|
5588
|
-
@
|
5589
|
-
@
|
5694
|
+
@param {DS.Store} store
|
5695
|
+
@param {subclass of DS.Model} type the DS.Model class of the records
|
5696
|
+
@param {Array} ids
|
5590
5697
|
*/
|
5591
5698
|
findMany: function(store, type, ids) {
|
5592
|
-
var promises =
|
5699
|
+
var promises = map.call(ids, function(id) {
|
5593
5700
|
return this.find(store, type, id);
|
5594
5701
|
}, this);
|
5595
5702
|
|
@@ -5699,7 +5806,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
|
|
5699
5806
|
@param record
|
5700
5807
|
*/
|
5701
5808
|
generateIdForRecord: function(store) {
|
5702
|
-
return counter++;
|
5809
|
+
return "fixture-" + counter++;
|
5703
5810
|
},
|
5704
5811
|
|
5705
5812
|
/**
|
@@ -5712,7 +5819,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
|
|
5712
5819
|
var fixtures = this.fixturesForType(type),
|
5713
5820
|
fixture;
|
5714
5821
|
|
5715
|
-
Ember.
|
5822
|
+
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
5716
5823
|
|
5717
5824
|
if (fixtures) {
|
5718
5825
|
fixture = Ember.A(fixtures).findProperty('id', id);
|
@@ -5734,7 +5841,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
|
|
5734
5841
|
findMany: function(store, type, ids) {
|
5735
5842
|
var fixtures = this.fixturesForType(type);
|
5736
5843
|
|
5737
|
-
Ember.assert("Unable to find fixtures for model type "+type.toString(),
|
5844
|
+
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
5738
5845
|
|
5739
5846
|
if (fixtures) {
|
5740
5847
|
fixtures = fixtures.filter(function(item) {
|
@@ -5757,7 +5864,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
|
|
5757
5864
|
findAll: function(store, type) {
|
5758
5865
|
var fixtures = this.fixturesForType(type);
|
5759
5866
|
|
5760
|
-
Ember.assert("Unable to find fixtures for model type "+type.toString(),
|
5867
|
+
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
5761
5868
|
|
5762
5869
|
return this.simulateRemoteCall(function() {
|
5763
5870
|
return fixtures;
|
@@ -5774,7 +5881,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
|
|
5774
5881
|
findQuery: function(store, type, query, array) {
|
5775
5882
|
var fixtures = this.fixturesForType(type);
|
5776
5883
|
|
5777
|
-
Ember.assert("Unable to find fixtures for model type "+type.toString(),
|
5884
|
+
Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
5778
5885
|
|
5779
5886
|
fixtures = this.queryFixtures(fixtures, query, type);
|
5780
5887
|
|
@@ -5914,17 +6021,53 @@ DS.FixtureAdapter = DS.Adapter.extend({
|
|
5914
6021
|
*/
|
5915
6022
|
|
5916
6023
|
var get = Ember.get, set = Ember.set;
|
5917
|
-
|
5918
|
-
|
5919
|
-
Ember.Logger.assert([reason, reason.message, reason.stack]);
|
5920
|
-
|
5921
|
-
throw reason;
|
5922
|
-
};
|
6024
|
+
var forEach = Ember.ArrayPolyfills.forEach;
|
6025
|
+
var map = Ember.ArrayPolyfills.map;
|
5923
6026
|
|
5924
6027
|
function coerceId(id) {
|
5925
6028
|
return id == null ? null : id+'';
|
5926
6029
|
}
|
5927
6030
|
|
6031
|
+
/**
|
6032
|
+
Normally, applications will use the `RESTSerializer` by implementing
|
6033
|
+
the `normalize` method and individual normalizations under
|
6034
|
+
`normalizeHash`.
|
6035
|
+
|
6036
|
+
This allows you to do whatever kind of munging you need, and is
|
6037
|
+
especially useful if your server is inconsistent and you need to
|
6038
|
+
do munging differently for many different kinds of responses.
|
6039
|
+
|
6040
|
+
See the `normalize` documentation for more information.
|
6041
|
+
|
6042
|
+
## Across the Board Normalization
|
6043
|
+
|
6044
|
+
There are also a number of hooks that you might find useful to defined
|
6045
|
+
across-the-board rules for your payload. These rules will be useful
|
6046
|
+
if your server is consistent, or if you're building an adapter for
|
6047
|
+
an infrastructure service, like Parse, and want to encode service
|
6048
|
+
conventions.
|
6049
|
+
|
6050
|
+
For example, if all of your keys are underscored and all-caps, but
|
6051
|
+
otherwise consistent with the names you use in your models, you
|
6052
|
+
can implement across-the-board rules for how to convert an attribute
|
6053
|
+
name in your model to a key in your JSON.
|
6054
|
+
|
6055
|
+
```js
|
6056
|
+
App.ApplicationSerializer = DS.RESTSerializer.extend({
|
6057
|
+
keyForAttribute: function(attr) {
|
6058
|
+
return Ember.String.underscore(attr).toUpperCase();
|
6059
|
+
}
|
6060
|
+
});
|
6061
|
+
```
|
6062
|
+
|
6063
|
+
You can also implement `keyForRelationship`, which takes the name
|
6064
|
+
of the relationship as the first parameter, and the kind of
|
6065
|
+
relationship (`hasMany` or `belongsTo`) as the second parameter.
|
6066
|
+
|
6067
|
+
@class RESTSerializer
|
6068
|
+
@namespace DS
|
6069
|
+
@extends DS.JSONSerializer
|
6070
|
+
*/
|
5928
6071
|
DS.RESTSerializer = DS.JSONSerializer.extend({
|
5929
6072
|
/**
|
5930
6073
|
Normalizes a part of the JSON payload returned by
|
@@ -5990,15 +6133,42 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
5990
6133
|
@param {Object} hash
|
5991
6134
|
@returns Object
|
5992
6135
|
*/
|
5993
|
-
normalize: function(type,
|
6136
|
+
normalize: function(type, hash, prop) {
|
5994
6137
|
this.normalizeId(hash);
|
5995
|
-
this.
|
6138
|
+
this.normalizeUsingDeclaredMapping(type, hash);
|
6139
|
+
this.normalizeAttributes(type, hash);
|
6140
|
+
this.normalizeRelationships(type, hash);
|
5996
6141
|
|
5997
6142
|
if (this.normalizeHash && this.normalizeHash[prop]) {
|
5998
6143
|
return this.normalizeHash[prop](hash);
|
5999
6144
|
}
|
6000
6145
|
|
6001
|
-
return hash;
|
6146
|
+
return this._super(type, hash, prop);
|
6147
|
+
},
|
6148
|
+
|
6149
|
+
/**
|
6150
|
+
You can use this method to normalize all payloads, regardless of whether they
|
6151
|
+
represent single records or an array.
|
6152
|
+
|
6153
|
+
For example, you might want to remove some extraneous data from the payload:
|
6154
|
+
|
6155
|
+
```js
|
6156
|
+
App.ApplicationSerializer = DS.RESTSerializer.extend({
|
6157
|
+
normalizePayload: function(type, payload) {
|
6158
|
+
delete payload.version;
|
6159
|
+
delete payload.status;
|
6160
|
+
return payload;
|
6161
|
+
}
|
6162
|
+
});
|
6163
|
+
```
|
6164
|
+
|
6165
|
+
@method normalizePayload
|
6166
|
+
@param {subclass of DS.Model} type
|
6167
|
+
@param {Object} hash
|
6168
|
+
@returns Object the normalized payload
|
6169
|
+
*/
|
6170
|
+
normalizePayload: function(type, payload) {
|
6171
|
+
return payload;
|
6002
6172
|
},
|
6003
6173
|
|
6004
6174
|
/**
|
@@ -6014,20 +6184,56 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6014
6184
|
delete hash[primaryKey];
|
6015
6185
|
},
|
6016
6186
|
|
6187
|
+
/**
|
6188
|
+
@method normalizeUsingDeclaredMapping
|
6189
|
+
@private
|
6190
|
+
*/
|
6191
|
+
normalizeUsingDeclaredMapping: function(type, hash) {
|
6192
|
+
var attrs = get(this, 'attrs'), payloadKey, key;
|
6193
|
+
|
6194
|
+
if (attrs) {
|
6195
|
+
for (key in attrs) {
|
6196
|
+
payloadKey = attrs[key];
|
6197
|
+
|
6198
|
+
hash[key] = hash[payloadKey];
|
6199
|
+
delete hash[payloadKey];
|
6200
|
+
}
|
6201
|
+
}
|
6202
|
+
},
|
6203
|
+
|
6017
6204
|
/**
|
6018
6205
|
@method normalizeAttributes
|
6019
6206
|
@private
|
6020
6207
|
*/
|
6021
|
-
normalizeAttributes: function(hash) {
|
6022
|
-
var
|
6208
|
+
normalizeAttributes: function(type, hash) {
|
6209
|
+
var payloadKey, key;
|
6210
|
+
|
6211
|
+
if (this.keyForAttribute) {
|
6212
|
+
type.eachAttribute(function(key) {
|
6213
|
+
payloadKey = this.keyForAttribute(key);
|
6214
|
+
if (key === payloadKey) { return; }
|
6215
|
+
|
6216
|
+
hash[key] = hash[payloadKey];
|
6217
|
+
delete hash[payloadKey];
|
6218
|
+
}, this);
|
6219
|
+
}
|
6220
|
+
},
|
6023
6221
|
|
6024
|
-
|
6222
|
+
/**
|
6223
|
+
@method normalizeRelationships
|
6224
|
+
@private
|
6225
|
+
*/
|
6226
|
+
normalizeRelationships: function(type, hash) {
|
6227
|
+
var payloadKey, key;
|
6025
6228
|
|
6026
|
-
|
6027
|
-
|
6229
|
+
if (this.keyForRelationship) {
|
6230
|
+
type.eachRelationship(function(key, relationship) {
|
6231
|
+
payloadKey = this.keyForRelationship(key, relationship.kind);
|
6232
|
+
if (key === payloadKey) { return; }
|
6028
6233
|
|
6029
|
-
|
6030
|
-
|
6234
|
+
hash[key] = hash[payloadKey];
|
6235
|
+
delete hash[payloadKey];
|
6236
|
+
}, this);
|
6031
6237
|
}
|
6032
6238
|
},
|
6033
6239
|
|
@@ -6100,6 +6306,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6100
6306
|
for the first time or updated (`createRecord` or `updateRecord`). In
|
6101
6307
|
particular, it will update the properties of the record that was saved.
|
6102
6308
|
|
6309
|
+
@method extractSingle
|
6103
6310
|
@param {DS.Store} store
|
6104
6311
|
@param {subclass of DS.Model} type
|
6105
6312
|
@param {Object} payload
|
@@ -6108,25 +6315,33 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6108
6315
|
@returns Object the primary response to the original request
|
6109
6316
|
*/
|
6110
6317
|
extractSingle: function(store, primaryType, payload, recordId, requestType) {
|
6318
|
+
payload = this.normalizePayload(primaryType, payload);
|
6319
|
+
|
6111
6320
|
var primaryTypeName = primaryType.typeKey,
|
6112
6321
|
primaryRecord;
|
6113
6322
|
|
6114
6323
|
for (var prop in payload) {
|
6115
|
-
|
6116
|
-
|
6117
|
-
|
6324
|
+
var typeName = this.typeForRoot(prop),
|
6325
|
+
isPrimary = typeName === primaryTypeName;
|
6326
|
+
|
6327
|
+
// legacy support for singular resources
|
6328
|
+
if (isPrimary && Ember.typeOf(payload[prop]) !== "array" ) {
|
6329
|
+
primaryRecord = this.normalize(primaryType, payload[prop], prop);
|
6118
6330
|
continue;
|
6119
6331
|
}
|
6120
6332
|
|
6121
|
-
var
|
6122
|
-
type = store.modelFor(typeName);
|
6333
|
+
var type = store.modelFor(typeName);
|
6123
6334
|
|
6124
6335
|
/*jshint loopfunc:true*/
|
6125
|
-
payload[prop]
|
6126
|
-
|
6336
|
+
forEach.call(payload[prop], function(hash) {
|
6337
|
+
var typeName = this.typeForRoot(prop),
|
6338
|
+
type = store.modelFor(typeName),
|
6339
|
+
typeSerializer = store.serializerFor(type);
|
6340
|
+
|
6341
|
+
hash = typeSerializer.normalize(type, hash, prop);
|
6127
6342
|
|
6128
|
-
var isFirstCreatedRecord =
|
6129
|
-
isUpdatedRecord =
|
6343
|
+
var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord,
|
6344
|
+
isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId;
|
6130
6345
|
|
6131
6346
|
// find the primary record.
|
6132
6347
|
//
|
@@ -6233,6 +6448,12 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6233
6448
|
or `findHasMany`. In particular, the primary array will become the
|
6234
6449
|
list of records in the record array that kicked off the request.
|
6235
6450
|
|
6451
|
+
If your primary array contains secondary (embedded) records of the same type,
|
6452
|
+
you cannot place these into the primary array `posts`. Instead, place the
|
6453
|
+
secondary items into an underscore prefixed property `_posts`, which will
|
6454
|
+
push these items into the store and will not affect the resulting query.
|
6455
|
+
|
6456
|
+
@method extractArray
|
6236
6457
|
@param {DS.Store} store
|
6237
6458
|
@param {subclass of DS.Model} type
|
6238
6459
|
@param {Object} payload
|
@@ -6241,17 +6462,28 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6241
6462
|
to the original query.
|
6242
6463
|
*/
|
6243
6464
|
extractArray: function(store, primaryType, payload) {
|
6465
|
+
payload = this.normalizePayload(primaryType, payload);
|
6466
|
+
|
6244
6467
|
var primaryTypeName = primaryType.typeKey,
|
6245
6468
|
primaryArray;
|
6246
6469
|
|
6247
6470
|
for (var prop in payload) {
|
6248
|
-
var
|
6471
|
+
var typeKey = prop,
|
6472
|
+
forcedSecondary = false;
|
6473
|
+
|
6474
|
+
if (prop.charAt(0) === '_') {
|
6475
|
+
forcedSecondary = true;
|
6476
|
+
typeKey = prop.substr(1);
|
6477
|
+
}
|
6478
|
+
|
6479
|
+
var typeName = this.typeForRoot(typeKey),
|
6249
6480
|
type = store.modelFor(typeName),
|
6250
|
-
|
6481
|
+
typeSerializer = store.serializerFor(type),
|
6482
|
+
isPrimary = (!forcedSecondary && (typeName === primaryTypeName));
|
6251
6483
|
|
6252
6484
|
/*jshint loopfunc:true*/
|
6253
|
-
var normalizedArray = payload[prop]
|
6254
|
-
return
|
6485
|
+
var normalizedArray = map.call(payload[prop], function(hash) {
|
6486
|
+
return typeSerializer.normalize(type, hash, prop);
|
6255
6487
|
}, this);
|
6256
6488
|
|
6257
6489
|
if (isPrimary) {
|
@@ -6265,21 +6497,73 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6265
6497
|
},
|
6266
6498
|
|
6267
6499
|
/**
|
6268
|
-
|
6269
|
-
|
6270
|
-
|
6500
|
+
This method allows you to push a payload containing top-level
|
6501
|
+
collections of records organized per type.
|
6502
|
+
|
6503
|
+
```js
|
6504
|
+
{
|
6505
|
+
"posts": [{
|
6506
|
+
"id": "1",
|
6507
|
+
"title": "Rails is omakase",
|
6508
|
+
"author", "1",
|
6509
|
+
"comments": [ "1" ]
|
6510
|
+
}],
|
6511
|
+
"comments": [{
|
6512
|
+
"id": "1",
|
6513
|
+
"body": "FIRST
|
6514
|
+
}],
|
6515
|
+
"users": [{
|
6516
|
+
"id": "1",
|
6517
|
+
"name": "@d2h"
|
6518
|
+
}]
|
6519
|
+
}
|
6520
|
+
```
|
6521
|
+
|
6522
|
+
It will first normalize the payload, so you can use this to push
|
6523
|
+
in data streaming in from your server structured the same way
|
6524
|
+
that fetches and saves are structured.
|
6525
|
+
|
6526
|
+
@param {DS.Store} store
|
6527
|
+
@param {Object} payload
|
6271
6528
|
*/
|
6272
|
-
|
6273
|
-
|
6529
|
+
pushPayload: function(store, payload) {
|
6530
|
+
payload = this.normalizePayload(null, payload);
|
6531
|
+
|
6532
|
+
for (var prop in payload) {
|
6533
|
+
var typeName = this.typeForRoot(prop),
|
6534
|
+
type = store.modelFor(typeName);
|
6535
|
+
|
6536
|
+
/*jshint loopfunc:true*/
|
6537
|
+
var normalizedArray = map.call(payload[prop], function(hash) {
|
6538
|
+
return this.normalize(type, hash, prop);
|
6539
|
+
}, this);
|
6540
|
+
|
6541
|
+
store.pushMany(typeName, normalizedArray);
|
6542
|
+
}
|
6274
6543
|
},
|
6275
6544
|
|
6276
6545
|
/**
|
6277
|
-
|
6278
|
-
|
6279
|
-
|
6546
|
+
You can use this method to normalize the JSON root keys returned
|
6547
|
+
into the model type expected by your store.
|
6548
|
+
|
6549
|
+
For example, your server may return underscored root keys rather than
|
6550
|
+
the expected camelcased versions.
|
6551
|
+
|
6552
|
+
```js
|
6553
|
+
App.ApplicationSerializer = DS.RESTSerializer.extend({
|
6554
|
+
typeForRoot: function(root) {
|
6555
|
+
var camelized = Ember.String.camelize(root);
|
6556
|
+
return Ember.String.singularize(camelized);
|
6557
|
+
}
|
6558
|
+
});
|
6559
|
+
```
|
6560
|
+
|
6561
|
+
@method typeForRoot
|
6562
|
+
@param {String} root
|
6563
|
+
@returns String the model's typeKey
|
6280
6564
|
*/
|
6281
|
-
|
6282
|
-
return Ember.String.singularize(
|
6565
|
+
typeForRoot: function(root) {
|
6566
|
+
return Ember.String.singularize(root);
|
6283
6567
|
},
|
6284
6568
|
|
6285
6569
|
// SERIALIZE
|
@@ -6421,12 +6705,69 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6421
6705
|
}
|
6422
6706
|
});
|
6423
6707
|
```
|
6708
|
+
|
6709
|
+
@method serialize
|
6710
|
+
@param record
|
6711
|
+
@param options
|
6424
6712
|
*/
|
6425
6713
|
serialize: function(record, options) {
|
6426
6714
|
return this._super.apply(this, arguments);
|
6715
|
+
},
|
6716
|
+
|
6717
|
+
/**
|
6718
|
+
You can use this method to customize the root keys serialized into the JSON.
|
6719
|
+
By default the REST Serializer sends camelized root keys.
|
6720
|
+
For example, your server may expect underscored root objects.
|
6721
|
+
|
6722
|
+
```js
|
6723
|
+
App.ApplicationSerializer = DS.RESTSerializer.extend({
|
6724
|
+
serializeIntoHash: function(data, type, record, options) {
|
6725
|
+
var root = Ember.String.decamelize(type.typeKey);
|
6726
|
+
data[root] = this.serialize(record, options);
|
6727
|
+
}
|
6728
|
+
});
|
6729
|
+
```
|
6730
|
+
|
6731
|
+
@method serializeIntoHash
|
6732
|
+
@param {Object} hash
|
6733
|
+
@param {subclass of DS.Model} type
|
6734
|
+
@param {DS.Model} record
|
6735
|
+
@param {Object} options
|
6736
|
+
*/
|
6737
|
+
serializeIntoHash: function(hash, type, record, options) {
|
6738
|
+
hash[type.typeKey] = this.serialize(record, options);
|
6739
|
+
},
|
6740
|
+
|
6741
|
+
/**
|
6742
|
+
You can use this method to customize how polymorphic objects are serialized.
|
6743
|
+
By default the JSON Serializer creates the key by appending `Type` to
|
6744
|
+
the attribute and value from the model's camelcased model name.
|
6745
|
+
|
6746
|
+
@method serializePolymorphicType
|
6747
|
+
@param {DS.Model} record
|
6748
|
+
@param {Object} json
|
6749
|
+
@param relationship
|
6750
|
+
*/
|
6751
|
+
serializePolymorphicType: function(record, json, relationship) {
|
6752
|
+
var key = relationship.key,
|
6753
|
+
belongsTo = get(record, key);
|
6754
|
+
key = this.keyForAttribute ? this.keyForAttribute(key) : key;
|
6755
|
+
json[key + "Type"] = belongsTo.constructor.typeKey;
|
6427
6756
|
}
|
6428
6757
|
});
|
6429
6758
|
|
6759
|
+
})();
|
6760
|
+
|
6761
|
+
|
6762
|
+
|
6763
|
+
(function() {
|
6764
|
+
/**
|
6765
|
+
@module ember-data
|
6766
|
+
*/
|
6767
|
+
|
6768
|
+
var get = Ember.get, set = Ember.set;
|
6769
|
+
var forEach = Ember.ArrayPolyfills.forEach;
|
6770
|
+
|
6430
6771
|
/**
|
6431
6772
|
The REST adapter allows your store to communicate with an HTTP server by
|
6432
6773
|
transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
|
@@ -6457,7 +6798,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6457
6798
|
|
6458
6799
|
### Conventional Names
|
6459
6800
|
|
6460
|
-
Attribute names in your JSON payload should be the
|
6801
|
+
Attribute names in your JSON payload should be the camelcased versions of
|
6461
6802
|
the attributes in your Ember.js models.
|
6462
6803
|
|
6463
6804
|
For example, if you have a `Person` model:
|
@@ -6475,8 +6816,8 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6475
6816
|
```js
|
6476
6817
|
{
|
6477
6818
|
"person": {
|
6478
|
-
"
|
6479
|
-
"
|
6819
|
+
"firstName": "Barack",
|
6820
|
+
"lastName": "Obama",
|
6480
6821
|
"occupation": "President"
|
6481
6822
|
}
|
6482
6823
|
}
|
@@ -6498,11 +6839,11 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
|
|
6498
6839
|
|
6499
6840
|
### Host customization
|
6500
6841
|
|
6501
|
-
An adapter can target other hosts by setting the `
|
6842
|
+
An adapter can target other hosts by setting the `host` property.
|
6502
6843
|
|
6503
6844
|
```js
|
6504
6845
|
DS.RESTAdapter.reopen({
|
6505
|
-
|
6846
|
+
host: 'https://api.example.com'
|
6506
6847
|
});
|
6507
6848
|
```
|
6508
6849
|
|
@@ -6544,7 +6885,7 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6544
6885
|
@returns Promise
|
6545
6886
|
*/
|
6546
6887
|
find: function(store, type, id) {
|
6547
|
-
return this.ajax(this.buildURL(type, id), 'GET');
|
6888
|
+
return this.ajax(this.buildURL(type.typeKey, id), 'GET');
|
6548
6889
|
},
|
6549
6890
|
|
6550
6891
|
/**
|
@@ -6559,10 +6900,17 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6559
6900
|
@see RESTAdapter/ajax
|
6560
6901
|
@param {DS.Store} store
|
6561
6902
|
@param {subclass of DS.Model} type
|
6903
|
+
@param {String} sinceToken
|
6562
6904
|
@returns Promise
|
6563
6905
|
*/
|
6564
|
-
findAll: function(store, type) {
|
6565
|
-
|
6906
|
+
findAll: function(store, type, sinceToken) {
|
6907
|
+
var query;
|
6908
|
+
|
6909
|
+
if (sinceToken) {
|
6910
|
+
query = { since: sinceToken };
|
6911
|
+
}
|
6912
|
+
|
6913
|
+
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
|
6566
6914
|
},
|
6567
6915
|
|
6568
6916
|
/**
|
@@ -6584,7 +6932,7 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6584
6932
|
@returns Promise
|
6585
6933
|
*/
|
6586
6934
|
findQuery: function(store, type, query) {
|
6587
|
-
return this.ajax(this.buildURL(type), 'GET', query);
|
6935
|
+
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
|
6588
6936
|
},
|
6589
6937
|
|
6590
6938
|
/**
|
@@ -6623,8 +6971,8 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6623
6971
|
@param {Array<String>} ids
|
6624
6972
|
@returns Promise
|
6625
6973
|
*/
|
6626
|
-
findMany: function(store, type, ids) {
|
6627
|
-
return this.ajax(this.buildURL(type), 'GET', { ids: ids });
|
6974
|
+
findMany: function(store, type, ids, owner) {
|
6975
|
+
return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } });
|
6628
6976
|
},
|
6629
6977
|
|
6630
6978
|
/**
|
@@ -6657,7 +7005,46 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6657
7005
|
@returns Promise
|
6658
7006
|
*/
|
6659
7007
|
findHasMany: function(store, record, url) {
|
6660
|
-
|
7008
|
+
var id = get(record, 'id'),
|
7009
|
+
type = record.constructor.typeKey;
|
7010
|
+
|
7011
|
+
return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
|
7012
|
+
},
|
7013
|
+
|
7014
|
+
/**
|
7015
|
+
Called by the store in order to fetch a JSON array for
|
7016
|
+
the unloaded records in a belongs-to relationship that were originally
|
7017
|
+
specified as a URL (inside of `links`).
|
7018
|
+
|
7019
|
+
For example, if your original payload looks like this:
|
7020
|
+
|
7021
|
+
```js
|
7022
|
+
{
|
7023
|
+
"person": {
|
7024
|
+
"id": 1,
|
7025
|
+
"name": "Tom Dale",
|
7026
|
+
"links": { "group": "/people/1/group" }
|
7027
|
+
}
|
7028
|
+
}
|
7029
|
+
```
|
7030
|
+
|
7031
|
+
This method will be called with the parent record and `/people/1/group`.
|
7032
|
+
|
7033
|
+
It will make an Ajax request to the originally specified URL.
|
7034
|
+
|
7035
|
+
@method findBelongsTo
|
7036
|
+
@see RESTAdapter/buildURL
|
7037
|
+
@see RESTAdapter/ajax
|
7038
|
+
@param {DS.Store} store
|
7039
|
+
@param {DS.Model} record
|
7040
|
+
@param {String} url
|
7041
|
+
@returns Promise
|
7042
|
+
*/
|
7043
|
+
findBelongsTo: function(store, record, url) {
|
7044
|
+
var id = get(record, 'id'),
|
7045
|
+
type = record.constructor.typeKey;
|
7046
|
+
|
7047
|
+
return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET');
|
6661
7048
|
},
|
6662
7049
|
|
6663
7050
|
/**
|
@@ -6680,9 +7067,11 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6680
7067
|
*/
|
6681
7068
|
createRecord: function(store, type, record) {
|
6682
7069
|
var data = {};
|
6683
|
-
|
7070
|
+
var serializer = store.serializerFor(type.typeKey);
|
7071
|
+
|
7072
|
+
serializer.serializeIntoHash(data, type, record, { includeId: true });
|
6684
7073
|
|
6685
|
-
return this.ajax(this.buildURL(type), "POST", { data: data });
|
7074
|
+
return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
|
6686
7075
|
},
|
6687
7076
|
|
6688
7077
|
/**
|
@@ -6704,11 +7093,13 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6704
7093
|
*/
|
6705
7094
|
updateRecord: function(store, type, record) {
|
6706
7095
|
var data = {};
|
6707
|
-
|
7096
|
+
var serializer = store.serializerFor(type.typeKey);
|
7097
|
+
|
7098
|
+
serializer.serializeIntoHash(data, type, record);
|
6708
7099
|
|
6709
7100
|
var id = get(record, 'id');
|
6710
7101
|
|
6711
|
-
return this.ajax(this.buildURL(type, id), "PUT", { data: data });
|
7102
|
+
return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
|
6712
7103
|
},
|
6713
7104
|
|
6714
7105
|
/**
|
@@ -6728,7 +7119,7 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6728
7119
|
deleteRecord: function(store, type, record) {
|
6729
7120
|
var id = get(record, 'id');
|
6730
7121
|
|
6731
|
-
return this.ajax(this.buildURL(type, id), "DELETE");
|
7122
|
+
return this.ajax(this.buildURL(type.typeKey, id), "DELETE");
|
6732
7123
|
},
|
6733
7124
|
|
6734
7125
|
/**
|
@@ -6737,41 +7128,116 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6737
7128
|
By default, it pluralizes the type's name (for example,
|
6738
7129
|
'post' becomes 'posts' and 'person' becomes 'people').
|
6739
7130
|
|
6740
|
-
If an ID is specified, it adds the ID to the
|
6741
|
-
|
7131
|
+
If an ID is specified, it adds the ID to the path generated
|
7132
|
+
for the type, separated by a `/`.
|
6742
7133
|
|
6743
7134
|
@method buildURL
|
6744
|
-
@param {
|
7135
|
+
@param {String} type
|
6745
7136
|
@param {String} id
|
6746
7137
|
@returns String
|
6747
7138
|
*/
|
6748
7139
|
buildURL: function(type, id) {
|
6749
|
-
var url =
|
6750
|
-
|
7140
|
+
var url = [],
|
7141
|
+
host = get(this, 'host'),
|
7142
|
+
prefix = this.urlPrefix();
|
7143
|
+
|
7144
|
+
if (type) { url.push(this.pathForType(type)); }
|
7145
|
+
if (id) { url.push(id); }
|
7146
|
+
|
7147
|
+
if (prefix) { url.unshift(prefix); }
|
7148
|
+
|
7149
|
+
url = url.join('/');
|
7150
|
+
if (!host && url) { url = '/' + url; }
|
6751
7151
|
|
6752
7152
|
return url;
|
6753
7153
|
},
|
6754
7154
|
|
6755
|
-
|
6756
|
-
|
6757
|
-
|
6758
|
-
|
6759
|
-
|
6760
|
-
|
7155
|
+
urlPrefix: function(path, parentURL) {
|
7156
|
+
var host = get(this, 'host'),
|
7157
|
+
namespace = get(this, 'namespace'),
|
7158
|
+
url = [];
|
7159
|
+
|
7160
|
+
if (path) {
|
7161
|
+
// Absolute path
|
7162
|
+
if (path.charAt(0) === '/') {
|
7163
|
+
if (host) {
|
7164
|
+
path = path.slice(1);
|
7165
|
+
url.push(host);
|
7166
|
+
}
|
7167
|
+
// Relative path
|
7168
|
+
} else if (!/^http(s)?:\/\//.test(path)) {
|
7169
|
+
url.push(parentURL);
|
7170
|
+
}
|
7171
|
+
} else {
|
7172
|
+
if (host) { url.push(host); }
|
7173
|
+
if (namespace) { url.push(namespace); }
|
7174
|
+
}
|
7175
|
+
|
7176
|
+
if (path) {
|
7177
|
+
url.push(path);
|
7178
|
+
}
|
6761
7179
|
|
7180
|
+
return url.join('/');
|
7181
|
+
},
|
6762
7182
|
|
6763
7183
|
/**
|
6764
|
-
|
6765
|
-
HTTP request.
|
7184
|
+
Determines the pathname for a given type.
|
6766
7185
|
|
6767
|
-
|
6768
|
-
|
6769
|
-
many records).
|
7186
|
+
By default, it pluralizes the type's name (for example,
|
7187
|
+
'post' becomes 'posts' and 'person' becomes 'people').
|
6770
7188
|
|
6771
|
-
|
7189
|
+
### Pathname customization
|
6772
7190
|
|
6773
|
-
|
6774
|
-
|
7191
|
+
For example if you have an object LineItem with an
|
7192
|
+
endpoint of "/line_items/".
|
7193
|
+
|
7194
|
+
```js
|
7195
|
+
DS.RESTAdapter.reopen({
|
7196
|
+
pathForType: function(type) {
|
7197
|
+
var decamelized = Ember.String.decamelize(type);
|
7198
|
+
return Ember.String.pluralize(decamelized);
|
7199
|
+
};
|
7200
|
+
});
|
7201
|
+
```
|
7202
|
+
|
7203
|
+
@method pathForType
|
7204
|
+
@param {String} type
|
7205
|
+
@returns String
|
7206
|
+
**/
|
7207
|
+
pathForType: function(type) {
|
7208
|
+
return Ember.String.pluralize(type);
|
7209
|
+
},
|
7210
|
+
|
7211
|
+
/**
|
7212
|
+
Takes an ajax response, and returns a relavant error.
|
7213
|
+
|
7214
|
+
By default, it has the following behavior:
|
7215
|
+
|
7216
|
+
* It simply returns the ajax response.
|
7217
|
+
|
7218
|
+
@method ajaxError
|
7219
|
+
@param jqXHR
|
7220
|
+
*/
|
7221
|
+
ajaxError: function(jqXHR) {
|
7222
|
+
if (jqXHR) {
|
7223
|
+
jqXHR.then = null;
|
7224
|
+
}
|
7225
|
+
|
7226
|
+
return jqXHR;
|
7227
|
+
},
|
7228
|
+
|
7229
|
+
/**
|
7230
|
+
Takes a URL, an HTTP method and a hash of data, and makes an
|
7231
|
+
HTTP request.
|
7232
|
+
|
7233
|
+
When the server responds with a payload, Ember Data will call into `extractSingle`
|
7234
|
+
or `extractArray` (depending on whether the original query was for one record or
|
7235
|
+
many records).
|
7236
|
+
|
7237
|
+
By default, it has the following behavior:
|
7238
|
+
|
7239
|
+
* It sets the response `dataType` to `"json"`
|
7240
|
+
* If the HTTP method is not `"GET"`, it sets the `Content-Type` to be
|
6775
7241
|
`application/json; charset=utf-8`
|
6776
7242
|
* If the HTTP method is not `"GET"`, it stringifies the data passed in. The
|
6777
7243
|
data is the serialized record in the case of a save.
|
@@ -6801,7 +7267,7 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6801
7267
|
if (adapter.headers !== undefined) {
|
6802
7268
|
var headers = adapter.headers;
|
6803
7269
|
hash.beforeSend = function (xhr) {
|
6804
|
-
Ember.keys(headers)
|
7270
|
+
forEach.call(Ember.keys(headers), function(key) {
|
6805
7271
|
xhr.setRequestHeader(key, headers[key]);
|
6806
7272
|
});
|
6807
7273
|
};
|
@@ -6812,11 +7278,7 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6812
7278
|
};
|
6813
7279
|
|
6814
7280
|
hash.error = function(jqXHR, textStatus, errorThrown) {
|
6815
|
-
|
6816
|
-
jqXHR.then = null;
|
6817
|
-
}
|
6818
|
-
|
6819
|
-
Ember.run(null, reject, jqXHR);
|
7281
|
+
Ember.run(null, reject, adapter.ajaxError(jqXHR));
|
6820
7282
|
};
|
6821
7283
|
|
6822
7284
|
Ember.$.ajax(hash);
|
@@ -6842,16 +7304,20 @@ DS.RESTAdapter = DS.Adapter.extend({
|
|
6842
7304
|
DS.Model.reopen({
|
6843
7305
|
|
6844
7306
|
/**
|
6845
|
-
|
6846
|
-
|
7307
|
+
Provides info about the model for debugging purposes
|
7308
|
+
by grouping the properties into more semantic groups.
|
6847
7309
|
|
6848
|
-
|
7310
|
+
Meant to be used by debugging tools such as the Chrome Ember Extension.
|
6849
7311
|
|
6850
|
-
|
6851
|
-
|
6852
|
-
|
6853
|
-
|
6854
|
-
|
7312
|
+
- Groups all attributes in "Attributes" group.
|
7313
|
+
- Groups all belongsTo relationships in "Belongs To" group.
|
7314
|
+
- Groups all hasMany relationships in "Has Many" group.
|
7315
|
+
- Groups all flags in "Flags" group.
|
7316
|
+
- Flags relationship CPs as expensive properties.
|
7317
|
+
|
7318
|
+
@method _debugInfo
|
7319
|
+
@for DS.Model
|
7320
|
+
@private
|
6855
7321
|
*/
|
6856
7322
|
_debugInfo: function() {
|
6857
7323
|
var attributes = ['id'],
|
@@ -6871,7 +7337,7 @@ DS.Model.reopen({
|
|
6871
7337
|
{
|
6872
7338
|
name: 'Attributes',
|
6873
7339
|
properties: attributes,
|
6874
|
-
expand: true
|
7340
|
+
expand: true
|
6875
7341
|
},
|
6876
7342
|
{
|
6877
7343
|
name: 'Belongs To',
|
@@ -6945,3 +7411,695 @@ DS.Model.reopen({
|
|
6945
7411
|
|
6946
7412
|
})();
|
6947
7413
|
|
7414
|
+
(function() {
|
7415
|
+
Ember.String.pluralize = function(word) {
|
7416
|
+
return Ember.Inflector.inflector.pluralize(word);
|
7417
|
+
};
|
7418
|
+
|
7419
|
+
Ember.String.singularize = function(word) {
|
7420
|
+
return Ember.Inflector.inflector.singularize(word);
|
7421
|
+
};
|
7422
|
+
|
7423
|
+
})();
|
7424
|
+
|
7425
|
+
|
7426
|
+
|
7427
|
+
(function() {
|
7428
|
+
var BLANK_REGEX = /^\s*$/;
|
7429
|
+
|
7430
|
+
function loadUncountable(rules, uncountable) {
|
7431
|
+
for (var i = 0, length = uncountable.length; i < length; i++) {
|
7432
|
+
rules.uncountable[uncountable[i]] = true;
|
7433
|
+
}
|
7434
|
+
}
|
7435
|
+
|
7436
|
+
function loadIrregular(rules, irregularPairs) {
|
7437
|
+
var pair;
|
7438
|
+
|
7439
|
+
for (var i = 0, length = irregularPairs.length; i < length; i++) {
|
7440
|
+
pair = irregularPairs[i];
|
7441
|
+
|
7442
|
+
rules.irregular[pair[0]] = pair[1];
|
7443
|
+
rules.irregularInverse[pair[1]] = pair[0];
|
7444
|
+
}
|
7445
|
+
}
|
7446
|
+
|
7447
|
+
/**
|
7448
|
+
Inflector.Ember provides a mechanism for supplying inflection rules for your
|
7449
|
+
application. Ember includes a default set of inflection rules, and provides an
|
7450
|
+
API for providing additional rules.
|
7451
|
+
|
7452
|
+
Examples:
|
7453
|
+
|
7454
|
+
Creating an inflector with no rules.
|
7455
|
+
|
7456
|
+
```js
|
7457
|
+
var inflector = new Ember.Inflector();
|
7458
|
+
```
|
7459
|
+
|
7460
|
+
Creating an inflector with the default ember ruleset.
|
7461
|
+
|
7462
|
+
```js
|
7463
|
+
var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
|
7464
|
+
|
7465
|
+
inflector.pluralize('cow') //=> 'kine'
|
7466
|
+
inflector.singularize('kine') //=> 'cow'
|
7467
|
+
```
|
7468
|
+
|
7469
|
+
Creating an inflector and adding rules later.
|
7470
|
+
|
7471
|
+
```javascript
|
7472
|
+
var inflector = Ember.Inflector.inflector;
|
7473
|
+
|
7474
|
+
inflector.pluralize('advice') // => 'advices'
|
7475
|
+
inflector.uncountable('advice');
|
7476
|
+
inflector.pluralize('advice') // => 'advice'
|
7477
|
+
|
7478
|
+
inflector.pluralize('formula') // => 'formulas'
|
7479
|
+
inflector.irregular('formula', 'formulae');
|
7480
|
+
inflector.pluralize('formula') // => 'formulae'
|
7481
|
+
|
7482
|
+
// you would not need to add these as they are the default rules
|
7483
|
+
inflector.plural(/$/, 's');
|
7484
|
+
inflector.singular(/s$/i, '');
|
7485
|
+
```
|
7486
|
+
|
7487
|
+
Creating an inflector with a nondefault ruleset.
|
7488
|
+
|
7489
|
+
```javascript
|
7490
|
+
var rules = {
|
7491
|
+
plurals: [ /$/, 's' ],
|
7492
|
+
singular: [ /\s$/, '' ],
|
7493
|
+
irregularPairs: [
|
7494
|
+
[ 'cow', 'kine' ]
|
7495
|
+
],
|
7496
|
+
uncountable: [ 'fish' ]
|
7497
|
+
};
|
7498
|
+
|
7499
|
+
var inflector = new Ember.Inflector(rules);
|
7500
|
+
```
|
7501
|
+
|
7502
|
+
@class Inflector
|
7503
|
+
@namespace Ember
|
7504
|
+
*/
|
7505
|
+
function Inflector(ruleSet) {
|
7506
|
+
ruleSet = ruleSet || {};
|
7507
|
+
ruleSet.uncountable = ruleSet.uncountable || {};
|
7508
|
+
ruleSet.irregularPairs= ruleSet.irregularPairs|| {};
|
7509
|
+
|
7510
|
+
var rules = this.rules = {
|
7511
|
+
plurals: ruleSet.plurals || [],
|
7512
|
+
singular: ruleSet.singular || [],
|
7513
|
+
irregular: {},
|
7514
|
+
irregularInverse: {},
|
7515
|
+
uncountable: {}
|
7516
|
+
};
|
7517
|
+
|
7518
|
+
loadUncountable(rules, ruleSet.uncountable);
|
7519
|
+
loadIrregular(rules, ruleSet.irregularPairs);
|
7520
|
+
}
|
7521
|
+
|
7522
|
+
Inflector.prototype = {
|
7523
|
+
/**
|
7524
|
+
@method plural
|
7525
|
+
@param {RegExp} regex
|
7526
|
+
@param {String} string
|
7527
|
+
*/
|
7528
|
+
plural: function(regex, string) {
|
7529
|
+
this.rules.plurals.push([regex, string]);
|
7530
|
+
},
|
7531
|
+
|
7532
|
+
/**
|
7533
|
+
@method singular
|
7534
|
+
@param {RegExp} regex
|
7535
|
+
@param {String} string
|
7536
|
+
*/
|
7537
|
+
singular: function(regex, string) {
|
7538
|
+
this.rules.singular.push([regex, string]);
|
7539
|
+
},
|
7540
|
+
|
7541
|
+
/**
|
7542
|
+
@method uncountable
|
7543
|
+
@param {String} regex
|
7544
|
+
*/
|
7545
|
+
uncountable: function(string) {
|
7546
|
+
loadUncountable(this.rules, [string]);
|
7547
|
+
},
|
7548
|
+
|
7549
|
+
/**
|
7550
|
+
@method irregular
|
7551
|
+
@param {String} singular
|
7552
|
+
@param {String} plural
|
7553
|
+
*/
|
7554
|
+
irregular: function (singular, plural) {
|
7555
|
+
loadIrregular(this.rules, [[singular, plural]]);
|
7556
|
+
},
|
7557
|
+
|
7558
|
+
/**
|
7559
|
+
@method pluralize
|
7560
|
+
@param {String} word
|
7561
|
+
*/
|
7562
|
+
pluralize: function(word) {
|
7563
|
+
return this.inflect(word, this.rules.plurals, this.rules.irregular);
|
7564
|
+
},
|
7565
|
+
|
7566
|
+
/**
|
7567
|
+
@method singularize
|
7568
|
+
@param {String} word
|
7569
|
+
*/
|
7570
|
+
singularize: function(word) {
|
7571
|
+
return this.inflect(word, this.rules.singular, this.rules.irregularInverse);
|
7572
|
+
},
|
7573
|
+
|
7574
|
+
/**
|
7575
|
+
@protected
|
7576
|
+
|
7577
|
+
@method inflect
|
7578
|
+
@param {String} word
|
7579
|
+
@param {Object} typeRules
|
7580
|
+
@param {Object} irregular
|
7581
|
+
*/
|
7582
|
+
inflect: function(word, typeRules, irregular) {
|
7583
|
+
var inflection, substitution, result, lowercase, isBlank,
|
7584
|
+
isUncountable, isIrregular, isIrregularInverse, rule;
|
7585
|
+
|
7586
|
+
isBlank = BLANK_REGEX.test(word);
|
7587
|
+
|
7588
|
+
if (isBlank) {
|
7589
|
+
return word;
|
7590
|
+
}
|
7591
|
+
|
7592
|
+
lowercase = word.toLowerCase();
|
7593
|
+
|
7594
|
+
isUncountable = this.rules.uncountable[lowercase];
|
7595
|
+
|
7596
|
+
if (isUncountable) {
|
7597
|
+
return word;
|
7598
|
+
}
|
7599
|
+
|
7600
|
+
isIrregular = irregular && irregular[lowercase];
|
7601
|
+
|
7602
|
+
if (isIrregular) {
|
7603
|
+
return isIrregular;
|
7604
|
+
}
|
7605
|
+
|
7606
|
+
for (var i = typeRules.length, min = 0; i > min; i--) {
|
7607
|
+
inflection = typeRules[i-1];
|
7608
|
+
rule = inflection[0];
|
7609
|
+
|
7610
|
+
if (rule.test(word)) {
|
7611
|
+
break;
|
7612
|
+
}
|
7613
|
+
}
|
7614
|
+
|
7615
|
+
inflection = inflection || [];
|
7616
|
+
|
7617
|
+
rule = inflection[0];
|
7618
|
+
substitution = inflection[1];
|
7619
|
+
|
7620
|
+
result = word.replace(rule, substitution);
|
7621
|
+
|
7622
|
+
return result;
|
7623
|
+
}
|
7624
|
+
};
|
7625
|
+
|
7626
|
+
Ember.Inflector = Inflector;
|
7627
|
+
|
7628
|
+
})();
|
7629
|
+
|
7630
|
+
|
7631
|
+
|
7632
|
+
(function() {
|
7633
|
+
Ember.Inflector.defaultRules = {
|
7634
|
+
plurals: [
|
7635
|
+
[/$/, 's'],
|
7636
|
+
[/s$/i, 's'],
|
7637
|
+
[/^(ax|test)is$/i, '$1es'],
|
7638
|
+
[/(octop|vir)us$/i, '$1i'],
|
7639
|
+
[/(octop|vir)i$/i, '$1i'],
|
7640
|
+
[/(alias|status)$/i, '$1es'],
|
7641
|
+
[/(bu)s$/i, '$1ses'],
|
7642
|
+
[/(buffal|tomat)o$/i, '$1oes'],
|
7643
|
+
[/([ti])um$/i, '$1a'],
|
7644
|
+
[/([ti])a$/i, '$1a'],
|
7645
|
+
[/sis$/i, 'ses'],
|
7646
|
+
[/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
|
7647
|
+
[/(hive)$/i, '$1s'],
|
7648
|
+
[/([^aeiouy]|qu)y$/i, '$1ies'],
|
7649
|
+
[/(x|ch|ss|sh)$/i, '$1es'],
|
7650
|
+
[/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
|
7651
|
+
[/^(m|l)ouse$/i, '$1ice'],
|
7652
|
+
[/^(m|l)ice$/i, '$1ice'],
|
7653
|
+
[/^(ox)$/i, '$1en'],
|
7654
|
+
[/^(oxen)$/i, '$1'],
|
7655
|
+
[/(quiz)$/i, '$1zes']
|
7656
|
+
],
|
7657
|
+
|
7658
|
+
singular: [
|
7659
|
+
[/s$/i, ''],
|
7660
|
+
[/(ss)$/i, '$1'],
|
7661
|
+
[/(n)ews$/i, '$1ews'],
|
7662
|
+
[/([ti])a$/i, '$1um'],
|
7663
|
+
[/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
|
7664
|
+
[/(^analy)(sis|ses)$/i, '$1sis'],
|
7665
|
+
[/([^f])ves$/i, '$1fe'],
|
7666
|
+
[/(hive)s$/i, '$1'],
|
7667
|
+
[/(tive)s$/i, '$1'],
|
7668
|
+
[/([lr])ves$/i, '$1f'],
|
7669
|
+
[/([^aeiouy]|qu)ies$/i, '$1y'],
|
7670
|
+
[/(s)eries$/i, '$1eries'],
|
7671
|
+
[/(m)ovies$/i, '$1ovie'],
|
7672
|
+
[/(x|ch|ss|sh)es$/i, '$1'],
|
7673
|
+
[/^(m|l)ice$/i, '$1ouse'],
|
7674
|
+
[/(bus)(es)?$/i, '$1'],
|
7675
|
+
[/(o)es$/i, '$1'],
|
7676
|
+
[/(shoe)s$/i, '$1'],
|
7677
|
+
[/(cris|test)(is|es)$/i, '$1is'],
|
7678
|
+
[/^(a)x[ie]s$/i, '$1xis'],
|
7679
|
+
[/(octop|vir)(us|i)$/i, '$1us'],
|
7680
|
+
[/(alias|status)(es)?$/i, '$1'],
|
7681
|
+
[/^(ox)en/i, '$1'],
|
7682
|
+
[/(vert|ind)ices$/i, '$1ex'],
|
7683
|
+
[/(matr)ices$/i, '$1ix'],
|
7684
|
+
[/(quiz)zes$/i, '$1'],
|
7685
|
+
[/(database)s$/i, '$1']
|
7686
|
+
],
|
7687
|
+
|
7688
|
+
irregularPairs: [
|
7689
|
+
['person', 'people'],
|
7690
|
+
['man', 'men'],
|
7691
|
+
['child', 'children'],
|
7692
|
+
['sex', 'sexes'],
|
7693
|
+
['move', 'moves'],
|
7694
|
+
['cow', 'kine'],
|
7695
|
+
['zombie', 'zombies']
|
7696
|
+
],
|
7697
|
+
|
7698
|
+
uncountable: [
|
7699
|
+
'equipment',
|
7700
|
+
'information',
|
7701
|
+
'rice',
|
7702
|
+
'money',
|
7703
|
+
'species',
|
7704
|
+
'series',
|
7705
|
+
'fish',
|
7706
|
+
'sheep',
|
7707
|
+
'jeans',
|
7708
|
+
'police'
|
7709
|
+
]
|
7710
|
+
};
|
7711
|
+
|
7712
|
+
})();
|
7713
|
+
|
7714
|
+
|
7715
|
+
|
7716
|
+
(function() {
|
7717
|
+
if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
|
7718
|
+
/**
|
7719
|
+
See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
|
7720
|
+
|
7721
|
+
@method pluralize
|
7722
|
+
@for String
|
7723
|
+
*/
|
7724
|
+
String.prototype.pluralize = function() {
|
7725
|
+
return Ember.String.pluralize(this);
|
7726
|
+
};
|
7727
|
+
|
7728
|
+
/**
|
7729
|
+
See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
|
7730
|
+
|
7731
|
+
@method singularize
|
7732
|
+
@for String
|
7733
|
+
*/
|
7734
|
+
String.prototype.singularize = function() {
|
7735
|
+
return Ember.String.singularize(this);
|
7736
|
+
};
|
7737
|
+
}
|
7738
|
+
|
7739
|
+
})();
|
7740
|
+
|
7741
|
+
|
7742
|
+
|
7743
|
+
(function() {
|
7744
|
+
Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
|
7745
|
+
|
7746
|
+
})();
|
7747
|
+
|
7748
|
+
|
7749
|
+
|
7750
|
+
(function() {
|
7751
|
+
|
7752
|
+
})();
|
7753
|
+
|
7754
|
+
(function() {
|
7755
|
+
/**
|
7756
|
+
@module ember-data
|
7757
|
+
*/
|
7758
|
+
|
7759
|
+
var get = Ember.get;
|
7760
|
+
var forEach = Ember.EnumerableUtils.forEach;
|
7761
|
+
|
7762
|
+
DS.ActiveModelSerializer = DS.RESTSerializer.extend({
|
7763
|
+
// SERIALIZE
|
7764
|
+
|
7765
|
+
/**
|
7766
|
+
Converts camelcased attributes to underscored when serializing.
|
7767
|
+
|
7768
|
+
@method keyForAttribute
|
7769
|
+
@param {String} attribute
|
7770
|
+
@returns String
|
7771
|
+
*/
|
7772
|
+
keyForAttribute: function(attr) {
|
7773
|
+
return Ember.String.decamelize(attr);
|
7774
|
+
},
|
7775
|
+
|
7776
|
+
/**
|
7777
|
+
Underscores relationship names and appends "_id" or "_ids" when serializing
|
7778
|
+
relationship keys.
|
7779
|
+
|
7780
|
+
@method keyForRelationship
|
7781
|
+
@param {String} key
|
7782
|
+
@param {String} kind
|
7783
|
+
@returns String
|
7784
|
+
*/
|
7785
|
+
keyForRelationship: function(key, kind) {
|
7786
|
+
key = Ember.String.decamelize(key);
|
7787
|
+
if (kind === "belongsTo") {
|
7788
|
+
return key + "_id";
|
7789
|
+
} else if (kind === "hasMany") {
|
7790
|
+
return Ember.String.singularize(key) + "_ids";
|
7791
|
+
} else {
|
7792
|
+
return key;
|
7793
|
+
}
|
7794
|
+
},
|
7795
|
+
|
7796
|
+
/**
|
7797
|
+
Serialize has-may relationship when it is configured as embedded objects.
|
7798
|
+
|
7799
|
+
@method serializeHasMany
|
7800
|
+
*/
|
7801
|
+
serializeHasMany: function(record, json, relationship) {
|
7802
|
+
var key = relationship.key,
|
7803
|
+
attrs = get(this, 'attrs'),
|
7804
|
+
embed = attrs && attrs[key] && attrs[key].embedded === 'always';
|
7805
|
+
|
7806
|
+
if (embed) {
|
7807
|
+
json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
|
7808
|
+
var data = relation.serialize(),
|
7809
|
+
primaryKey = get(this, 'primaryKey');
|
7810
|
+
|
7811
|
+
data[primaryKey] = get(relation, primaryKey);
|
7812
|
+
|
7813
|
+
return data;
|
7814
|
+
}, this);
|
7815
|
+
}
|
7816
|
+
},
|
7817
|
+
|
7818
|
+
/**
|
7819
|
+
Underscores the JSON root keys when serializing.
|
7820
|
+
|
7821
|
+
@method serializeIntoHash
|
7822
|
+
@param {Object} hash
|
7823
|
+
@param {subclass of DS.Model} type
|
7824
|
+
@param {DS.Model} record
|
7825
|
+
@param {Object} options
|
7826
|
+
*/
|
7827
|
+
serializeIntoHash: function(data, type, record, options) {
|
7828
|
+
var root = Ember.String.decamelize(type.typeKey);
|
7829
|
+
data[root] = this.serialize(record, options);
|
7830
|
+
},
|
7831
|
+
|
7832
|
+
/**
|
7833
|
+
Serializes a polymorphic type as a fully capitalized model name.
|
7834
|
+
|
7835
|
+
@method serializePolymorphicType
|
7836
|
+
@param {DS.Model} record
|
7837
|
+
@param {Object} json
|
7838
|
+
@param relationship
|
7839
|
+
*/
|
7840
|
+
serializePolymorphicType: function(record, json, relationship) {
|
7841
|
+
var key = relationship.key,
|
7842
|
+
belongsTo = get(record, key);
|
7843
|
+
key = this.keyForAttribute(key);
|
7844
|
+
json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
|
7845
|
+
},
|
7846
|
+
|
7847
|
+
// EXTRACT
|
7848
|
+
|
7849
|
+
/**
|
7850
|
+
Extracts the model typeKey from underscored root objects.
|
7851
|
+
|
7852
|
+
@method typeForRoot
|
7853
|
+
@param {String} root
|
7854
|
+
@returns String the model's typeKey
|
7855
|
+
*/
|
7856
|
+
typeForRoot: function(root) {
|
7857
|
+
var camelized = Ember.String.camelize(root);
|
7858
|
+
return Ember.String.singularize(camelized);
|
7859
|
+
},
|
7860
|
+
|
7861
|
+
/**
|
7862
|
+
Normalize the polymorphic type from the JSON.
|
7863
|
+
|
7864
|
+
Normalize:
|
7865
|
+
```js
|
7866
|
+
{
|
7867
|
+
id: "1"
|
7868
|
+
minion: { type: "evil_minion", id: "12"}
|
7869
|
+
}
|
7870
|
+
```
|
7871
|
+
|
7872
|
+
To:
|
7873
|
+
```js
|
7874
|
+
{
|
7875
|
+
id: "1"
|
7876
|
+
minion: { type: "evilMinion", id: "12"}
|
7877
|
+
}
|
7878
|
+
```
|
7879
|
+
|
7880
|
+
@method normalizeRelationships
|
7881
|
+
@private
|
7882
|
+
*/
|
7883
|
+
normalizeRelationships: function(type, hash) {
|
7884
|
+
var payloadKey, payload;
|
7885
|
+
|
7886
|
+
if (this.keyForRelationship) {
|
7887
|
+
type.eachRelationship(function(key, relationship) {
|
7888
|
+
if (relationship.options.polymorphic) {
|
7889
|
+
payloadKey = this.keyForAttribute(key);
|
7890
|
+
payload = hash[payloadKey];
|
7891
|
+
if (payload && payload.type) {
|
7892
|
+
payload.type = this.typeForRoot(payload.type);
|
7893
|
+
}
|
7894
|
+
} else {
|
7895
|
+
payloadKey = this.keyForRelationship(key, relationship.kind);
|
7896
|
+
payload = hash[payloadKey];
|
7897
|
+
}
|
7898
|
+
|
7899
|
+
hash[key] = payload;
|
7900
|
+
|
7901
|
+
if (key !== payloadKey) {
|
7902
|
+
delete hash[payloadKey];
|
7903
|
+
}
|
7904
|
+
}, this);
|
7905
|
+
}
|
7906
|
+
},
|
7907
|
+
|
7908
|
+
extractSingle: function(store, primaryType, payload, recordId, requestType) {
|
7909
|
+
var root = this.keyForAttribute(primaryType.typeKey),
|
7910
|
+
partial = payload[root];
|
7911
|
+
|
7912
|
+
updatePayloadWithEmbedded(store, this, primaryType, partial, payload);
|
7913
|
+
|
7914
|
+
return this._super(store, primaryType, payload, recordId, requestType);
|
7915
|
+
},
|
7916
|
+
|
7917
|
+
extractArray: function(store, type, payload) {
|
7918
|
+
var root = this.keyForAttribute(type.typeKey),
|
7919
|
+
partials = payload[Ember.String.pluralize(root)];
|
7920
|
+
|
7921
|
+
forEach(partials, function(partial) {
|
7922
|
+
updatePayloadWithEmbedded(store, this, type, partial, payload);
|
7923
|
+
}, this);
|
7924
|
+
|
7925
|
+
return this._super(store, type, payload);
|
7926
|
+
}
|
7927
|
+
});
|
7928
|
+
|
7929
|
+
function updatePayloadWithEmbedded(store, serializer, type, partial, payload) {
|
7930
|
+
var attrs = get(serializer, 'attrs');
|
7931
|
+
|
7932
|
+
if (!attrs) {
|
7933
|
+
return;
|
7934
|
+
}
|
7935
|
+
|
7936
|
+
type.eachRelationship(function(key, relationship) {
|
7937
|
+
var expandedKey, embeddedTypeKey, attribute, ids,
|
7938
|
+
config = attrs[key],
|
7939
|
+
serializer = store.serializerFor(relationship.type.typeKey),
|
7940
|
+
primaryKey = get(serializer, "primaryKey");
|
7941
|
+
|
7942
|
+
if (relationship.kind !== "hasMany") {
|
7943
|
+
return;
|
7944
|
+
}
|
7945
|
+
|
7946
|
+
if (config && (config.embedded === 'always' || config.embedded === 'load')) {
|
7947
|
+
// underscore forces the embedded records to be side loaded.
|
7948
|
+
// it is needed when main type === relationship.type
|
7949
|
+
embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey);
|
7950
|
+
expandedKey = this.keyForRelationship(key, relationship.kind);
|
7951
|
+
attribute = this.keyForAttribute(key);
|
7952
|
+
ids = [];
|
7953
|
+
|
7954
|
+
if (!partial[attribute]) {
|
7955
|
+
return;
|
7956
|
+
}
|
7957
|
+
|
7958
|
+
payload[embeddedTypeKey] = payload[embeddedTypeKey] || [];
|
7959
|
+
|
7960
|
+
forEach(partial[attribute], function(data) {
|
7961
|
+
ids.push(data[primaryKey]);
|
7962
|
+
payload[embeddedTypeKey].push(data);
|
7963
|
+
});
|
7964
|
+
|
7965
|
+
partial[expandedKey] = ids;
|
7966
|
+
delete partial[attribute];
|
7967
|
+
}
|
7968
|
+
}, serializer);
|
7969
|
+
}
|
7970
|
+
|
7971
|
+
})();
|
7972
|
+
|
7973
|
+
|
7974
|
+
|
7975
|
+
(function() {
|
7976
|
+
/**
|
7977
|
+
@module ember-data
|
7978
|
+
*/
|
7979
|
+
|
7980
|
+
var forEach = Ember.EnumerableUtils.forEach;
|
7981
|
+
|
7982
|
+
/**
|
7983
|
+
The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
|
7984
|
+
with a JSON API that uses an underscored naming convention instead of camelcasing.
|
7985
|
+
It has been designed to work out of the box with the
|
7986
|
+
[active_model_serializers](http://github.com/rails-api/active_model_serializers)
|
7987
|
+
Ruby gem.
|
7988
|
+
|
7989
|
+
## JSON Structure
|
7990
|
+
|
7991
|
+
The ActiveModelAdapter expects the JSON returned from your server to follow
|
7992
|
+
the REST adapter conventions substituting underscored keys for camelcased ones.
|
7993
|
+
|
7994
|
+
### Conventional Names
|
7995
|
+
|
7996
|
+
Attribute names in your JSON payload should be the underscored versions of
|
7997
|
+
the attributes in your Ember.js models.
|
7998
|
+
|
7999
|
+
For example, if you have a `Person` model:
|
8000
|
+
|
8001
|
+
```js
|
8002
|
+
App.FamousPerson = DS.Model.extend({
|
8003
|
+
firstName: DS.attr('string'),
|
8004
|
+
lastName: DS.attr('string'),
|
8005
|
+
occupation: DS.attr('string')
|
8006
|
+
});
|
8007
|
+
```
|
8008
|
+
|
8009
|
+
The JSON returned should look like this:
|
8010
|
+
|
8011
|
+
```js
|
8012
|
+
{
|
8013
|
+
"famous_person": {
|
8014
|
+
"first_name": "Barack",
|
8015
|
+
"last_name": "Obama",
|
8016
|
+
"occupation": "President"
|
8017
|
+
}
|
8018
|
+
}
|
8019
|
+
```
|
8020
|
+
|
8021
|
+
@class ActiveModelAdapter
|
8022
|
+
@constructor
|
8023
|
+
@namespace DS
|
8024
|
+
@extends DS.Adapter
|
8025
|
+
**/
|
8026
|
+
|
8027
|
+
DS.ActiveModelAdapter = DS.RESTAdapter.extend({
|
8028
|
+
defaultSerializer: '_ams',
|
8029
|
+
/**
|
8030
|
+
The ActiveModelAdapter overrides the `pathForType` method
|
8031
|
+
to build underscored URLs.
|
8032
|
+
|
8033
|
+
```js
|
8034
|
+
this.pathForType("famousPerson");
|
8035
|
+
//=> "famous_people"
|
8036
|
+
```
|
8037
|
+
|
8038
|
+
@method pathForType
|
8039
|
+
@param {String} type
|
8040
|
+
@returns String
|
8041
|
+
*/
|
8042
|
+
pathForType: function(type) {
|
8043
|
+
var decamelized = Ember.String.decamelize(type);
|
8044
|
+
return Ember.String.pluralize(decamelized);
|
8045
|
+
},
|
8046
|
+
|
8047
|
+
/**
|
8048
|
+
The ActiveModelAdapter overrides the `ajaxError` method
|
8049
|
+
to return a DS.InvalidError for all 422 Unprocessable Entity
|
8050
|
+
responses.
|
8051
|
+
|
8052
|
+
@method ajaxError
|
8053
|
+
@param jqXHR
|
8054
|
+
@returns error
|
8055
|
+
*/
|
8056
|
+
ajaxError: function(jqXHR) {
|
8057
|
+
var error = this._super(jqXHR);
|
8058
|
+
|
8059
|
+
if (jqXHR && jqXHR.status === 422) {
|
8060
|
+
var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"],
|
8061
|
+
errors = {};
|
8062
|
+
|
8063
|
+
forEach(Ember.keys(jsonErrors), function(key) {
|
8064
|
+
errors[Ember.String.camelize(key)] = jsonErrors[key];
|
8065
|
+
});
|
8066
|
+
|
8067
|
+
return new DS.InvalidError(errors);
|
8068
|
+
} else {
|
8069
|
+
return error;
|
8070
|
+
}
|
8071
|
+
}
|
8072
|
+
});
|
8073
|
+
|
8074
|
+
})();
|
8075
|
+
|
8076
|
+
|
8077
|
+
|
8078
|
+
(function() {
|
8079
|
+
|
8080
|
+
})();
|
8081
|
+
|
8082
|
+
|
8083
|
+
|
8084
|
+
(function() {
|
8085
|
+
Ember.onLoad('Ember.Application', function(Application) {
|
8086
|
+
Application.initializer({
|
8087
|
+
name: "activeModelAdapter",
|
8088
|
+
|
8089
|
+
initialize: function(container, application) {
|
8090
|
+
application.register('serializer:_ams', DS.ActiveModelSerializer);
|
8091
|
+
application.register('adapter:_ams', DS.ActiveModelAdapter);
|
8092
|
+
}
|
8093
|
+
});
|
8094
|
+
});
|
8095
|
+
|
8096
|
+
})();
|
8097
|
+
|
8098
|
+
|
8099
|
+
|
8100
|
+
(function() {
|
8101
|
+
|
8102
|
+
})();
|
8103
|
+
|
8104
|
+
|
8105
|
+
})();
|