ember-data-source 1.0.0.beta.1 → 1.0.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 49a8abeac26b84118c0106e9e494759469b7fb6d
4
- data.tar.gz: d43a9c9df58de30ce5ae8a097d37f350aa9bc1c4
3
+ metadata.gz: 3ab50c82ea065fb1d9eb982e0fde381caa4184de
4
+ data.tar.gz: 4f52807164f630f58e6144e3a11bc41e09b2c26c
5
5
  SHA512:
6
- metadata.gz: a5ab28522f356d624f719a3d03a829963f546d7da1109be23e743df25ae6109f51ced2a374973da26bb8f74ae2a81d21ac3204f55dc0874bffe87d00259f8c29
7
- data.tar.gz: 718c1b74121a1362aeda3a351635a9c3ec5361ff5a212b8c110122f43c15f12ce5d96d230bc4c37ef2f9bf4c2a9b6c544ae109067d63b15d8f2f35b3227bd2d2
6
+ metadata.gz: 483401b7507bc40fc841672dbf04d3cad28886cae33f342957bb77bfe0ab654d53d0ec21869da0426aa15907c8a028a65e7aa7a14fcea999644287fb50ce3111
7
+ data.tar.gz: b4a40bc1be9f8fcc995b2e5a99710ce824dad66b345f9cfd5f16e82687f3a99bf84971817e88565ef7473843ac4e80066e0cbb1f89a968c00d2d5607f6e76dbf
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0-beta.1
1
+ 1.0.0-beta.2
@@ -1,5 +1,5 @@
1
- // Version: v1.0.0-beta.1
2
- // Last commit: e9489ab (2013-09-01 00:38:13 -0700)
1
+ // Version: v1.0.0-beta.2
2
+ // Last commit: d1158e2 (2013-09-04 16:03:06 -0700)
3
3
 
4
4
 
5
5
  (function() {
@@ -41,249 +41,6 @@ var define, requireModule;
41
41
  return seen[name] = exports || value;
42
42
  };
43
43
  })();
44
- (function() {
45
- Ember.String.pluralize = function(word) {
46
- return Ember.Inflector.inflector.pluralize(word);
47
- };
48
-
49
- Ember.String.singularize = function(word) {
50
- return Ember.Inflector.inflector.singularize(word);
51
- };
52
-
53
- })();
54
-
55
-
56
-
57
- (function() {
58
- var BLANK_REGEX = /^\s*$/;
59
-
60
- function loadUncountable(rules, uncountable) {
61
- for (var i = 0, length = uncountable.length; i < length; i++) {
62
- rules.uncountable[uncountable[i]] = true;
63
- }
64
- }
65
-
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
-
72
- rules.irregular[pair[0]] = pair[1];
73
- rules.irregularInverse[pair[1]] = pair[0];
74
- }
75
- }
76
-
77
- function Inflector(ruleSet) {
78
- ruleSet = ruleSet || {};
79
- ruleSet.uncountable = ruleSet.uncountable || {};
80
- ruleSet.irregularPairs= ruleSet.irregularPairs|| {};
81
-
82
- var rules = this.rules = {
83
- plurals: ruleSet.plurals || [],
84
- singular: ruleSet.singular || [],
85
- irregular: {},
86
- irregularInverse: {},
87
- uncountable: {}
88
- };
89
-
90
- loadUncountable(rules, ruleSet.uncountable);
91
- loadIrregular(rules, ruleSet.irregularPairs);
92
- }
93
-
94
- Inflector.prototype = {
95
- pluralize: function(word) {
96
- return this.inflect(word, this.rules.plurals);
97
- },
98
-
99
- singularize: function(word) {
100
- return this.inflect(word, this.rules.singular);
101
- },
102
-
103
- inflect: function(word, typeRules) {
104
- var inflection, substitution, result, lowercase, isBlank,
105
- isUncountable, isIrregular, isIrregularInverse, rule;
106
-
107
- isBlank = BLANK_REGEX.test(word);
108
-
109
- if (isBlank) {
110
- return word;
111
- }
112
-
113
- lowercase = word.toLowerCase();
114
-
115
- isUncountable = this.rules.uncountable[lowercase];
116
-
117
- if (isUncountable) {
118
- return word;
119
- }
120
-
121
- isIrregular = this.rules.irregular[lowercase];
122
-
123
- if (isIrregular) {
124
- return isIrregular;
125
- }
126
-
127
- isIrregularInverse = this.rules.irregularInverse[lowercase];
128
-
129
- if (isIrregularInverse) {
130
- return isIrregularInverse;
131
- }
132
-
133
- for (var i = typeRules.length, min = 0; i > min; i--) {
134
- inflection = typeRules[i-1];
135
- rule = inflection[0];
136
-
137
- if (rule.test(word)) {
138
- break;
139
- }
140
- }
141
-
142
- inflection = inflection || [];
143
-
144
- rule = inflection[0];
145
- substitution = inflection[1];
146
-
147
- result = word.replace(rule, substitution);
148
-
149
- return result;
150
- }
151
- };
152
-
153
- Ember.Inflector = Inflector;
154
-
155
- })();
156
-
157
-
158
-
159
- (function() {
160
- Ember.Inflector.defaultRules = {
161
- plurals: [
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
- ],
184
-
185
- singular: [
186
- [/s$/i, ''],
187
- [/(ss)$/i, '$1'],
188
- [/(n)ews$/i, '$1ews'],
189
- [/([ti])a$/i, '$1um'],
190
- [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
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
- ],
214
-
215
- irregularPairs: [
216
- ['person', 'people'],
217
- ['man', 'men'],
218
- ['child', 'children'],
219
- ['sex', 'sexes'],
220
- ['move', 'moves'],
221
- ['cow', 'kine'],
222
- ['zombie', 'zombies']
223
- ],
224
-
225
- uncountable: [
226
- 'equipment',
227
- 'information',
228
- 'rice',
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: e9489ab (2013-09-01 00:38:13 -0700)
285
-
286
-
287
44
  (function() {
288
45
  /**
289
46
  @module ember-data
@@ -309,120 +66,9 @@ if ('undefined' === typeof DS) {
309
66
 
310
67
 
311
68
 
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
69
  (function() {
422
70
  var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
423
71
 
424
- var transforms = DS.JSONTransforms;
425
-
426
72
  // Simple dispatcher to support overriding the aliased
427
73
  // method in subclasses.
428
74
  function aliasMethod(methodName) {
@@ -434,55 +80,20 @@ function aliasMethod(methodName) {
434
80
  DS.JSONSerializer = Ember.Object.extend({
435
81
  primaryKey: 'id',
436
82
 
437
- deserialize: function(type, data) {
438
- var store = get(this, 'store');
439
-
83
+ applyTransforms: function(type, data) {
440
84
  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
- }
85
+ var transform = this.transformFor(type);
86
+ data[key] = transform.deserialize(data[key]);
461
87
  }, this);
462
88
 
463
89
  return data;
464
90
  },
465
91
 
466
- deserializeRecordId: function(data, key, relationship, id) {
467
- if (isNone(id) || id instanceof DS.Model) {
468
- return;
469
- }
470
-
471
- var type;
92
+ normalize: function(type, hash) {
93
+ if (!hash) { return hash; }
472
94
 
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
- }
95
+ this.applyTransforms(type, hash);
96
+ return hash;
486
97
  },
487
98
 
488
99
  // SERIALIZE
@@ -500,20 +111,8 @@ DS.JSONSerializer = Ember.Object.extend({
500
111
  }
501
112
  }
502
113
 
503
- var attrs = get(this, 'attrs');
504
-
505
114
  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;
115
+ this.serializeAttribute(record, json, key, attribute);
517
116
  }, this);
518
117
 
519
118
  record.eachRelationship(function(key, relationship) {
@@ -527,6 +126,22 @@ DS.JSONSerializer = Ember.Object.extend({
527
126
  return json;
528
127
  },
529
128
 
129
+ serializeAttribute: function(record, json, key, attribute) {
130
+ var attrs = get(this, 'attrs');
131
+ var value = get(record, key), type = attribute.type;
132
+
133
+ if (type) {
134
+ var transform = this.transformFor(type);
135
+ value = transform.serialize(value);
136
+ }
137
+
138
+ // if provided, use the mapping provided by `attrs` in
139
+ // the serializer
140
+ key = attrs && attrs[key] || key;
141
+
142
+ json[key] = value;
143
+ },
144
+
530
145
  serializeBelongsTo: function(record, json, relationship) {
531
146
  var key = relationship.key;
532
147
 
@@ -541,11 +156,22 @@ DS.JSONSerializer = Ember.Object.extend({
541
156
  }
542
157
  },
543
158
 
544
- serializeHasMany: Ember.K,
159
+ serializeHasMany: function(record, json, relationship) {
160
+ var key = relationship.key;
161
+
162
+ var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
163
+
164
+ if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') {
165
+ json[key] = get(record, key).mapBy('id');
166
+ // TODO support for polymorphic manyToNone and manyToMany relationships
167
+ }
168
+ },
545
169
 
546
170
  // EXTRACT
547
171
 
548
172
  extract: function(store, type, payload, id, requestType) {
173
+ this.extractMeta(store, type, payload);
174
+
549
175
  var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1);
550
176
  return this[specificExtract](store, type, payload, id, requestType);
551
177
  },
@@ -563,12 +189,20 @@ DS.JSONSerializer = Ember.Object.extend({
563
189
  extractSave: aliasMethod('extractSingle'),
564
190
 
565
191
  extractSingle: function(store, type, payload) {
566
- return payload;
192
+ return this.normalize(type, payload);
567
193
  },
568
194
 
569
195
  extractArray: function(store, type, payload) {
570
- return payload;
196
+ return this.normalize(type, payload);
571
197
  },
198
+
199
+ extractMeta: function(store, type, payload) {
200
+ if (payload && payload.meta) {
201
+ store.metaForType(type, payload.meta);
202
+ delete payload.meta;
203
+ }
204
+ },
205
+
572
206
  // HELPERS
573
207
 
574
208
  typeFor: function(relationship, key, data) {
@@ -579,8 +213,8 @@ DS.JSONSerializer = Ember.Object.extend({
579
213
  }
580
214
  },
581
215
 
582
- eachEmbeddedRecord: function() {
583
- // this is used by transaction.add
216
+ transformFor: function(attributeType) {
217
+ return this.container.lookup('transform:' + attributeType);
584
218
  }
585
219
  });
586
220
 
@@ -698,6 +332,139 @@ DS.DebugAdapter = Ember.DataAdapter.extend({
698
332
 
699
333
 
700
334
 
335
+ (function() {
336
+ DS.Transform = Ember.Object.extend({
337
+
338
+ serialize: Ember.required(),
339
+
340
+ deserialize: Ember.required()
341
+
342
+ });
343
+ })();
344
+
345
+
346
+
347
+ (function() {
348
+
349
+ DS.BooleanTransform = DS.Transform.extend({
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
+ })();
370
+
371
+
372
+
373
+ (function() {
374
+ DS.DateTransform = DS.Transform.extend({
375
+
376
+ deserialize: function(serialized) {
377
+ var type = typeof serialized;
378
+
379
+ if (type === "string") {
380
+ return new Date(Ember.Date.parse(serialized));
381
+ } else if (type === "number") {
382
+ return new Date(serialized);
383
+ } else if (serialized === null || serialized === undefined) {
384
+ // if the value is not present in the data,
385
+ // return undefined, not null.
386
+ return serialized;
387
+ } else {
388
+ return null;
389
+ }
390
+ },
391
+
392
+ serialize: function(date) {
393
+ if (date instanceof Date) {
394
+ var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
395
+ var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
396
+
397
+ var pad = function(num) {
398
+ return num < 10 ? "0"+num : ""+num;
399
+ };
400
+
401
+ var utcYear = date.getUTCFullYear(),
402
+ utcMonth = date.getUTCMonth(),
403
+ utcDayOfMonth = date.getUTCDate(),
404
+ utcDay = date.getUTCDay(),
405
+ utcHours = date.getUTCHours(),
406
+ utcMinutes = date.getUTCMinutes(),
407
+ utcSeconds = date.getUTCSeconds();
408
+
409
+
410
+ var dayOfWeek = days[utcDay];
411
+ var dayOfMonth = pad(utcDayOfMonth);
412
+ var month = months[utcMonth];
413
+
414
+ return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
415
+ pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
416
+ } else {
417
+ return null;
418
+ }
419
+ }
420
+
421
+ });
422
+
423
+ })();
424
+
425
+
426
+
427
+ (function() {
428
+ var empty = Ember.isEmpty;
429
+
430
+ DS.NumberTransform = DS.Transform.extend({
431
+
432
+ deserialize: function(serialized) {
433
+ return empty(serialized) ? null : Number(serialized);
434
+ },
435
+
436
+ serialize: function(deserialized) {
437
+ return empty(deserialized) ? null : Number(deserialized);
438
+ }
439
+ });
440
+ })();
441
+
442
+
443
+
444
+ (function() {
445
+ var none = Ember.isNone, empty = Ember.isEmpty;
446
+
447
+ DS.StringTransform = DS.Transform.extend({
448
+
449
+ deserialize: function(serialized) {
450
+ return none(serialized) ? null : String(serialized);
451
+ },
452
+
453
+ serialize: function(deserialized) {
454
+ return none(deserialized) ? null : String(deserialized);
455
+ }
456
+
457
+ });
458
+ })();
459
+
460
+
461
+
462
+ (function() {
463
+
464
+ })();
465
+
466
+
467
+
701
468
  (function() {
702
469
  /**
703
470
  @module ember-data
@@ -718,7 +485,7 @@ var set = Ember.set;
718
485
  For example, imagine an Ember.js application with the following classes:
719
486
 
720
487
  App.Store = DS.Store.extend({
721
- adapter: 'App.MyCustomAdapter'
488
+ adapter: 'custom'
722
489
  });
723
490
 
724
491
  App.PostsController = Ember.ArrayController.extend({
@@ -751,17 +518,16 @@ Ember.onLoad('Ember.Application', function(Application) {
751
518
  }
752
519
  });
753
520
 
754
- // Keep ED compatible with previous versions of ember
755
- // TODO: Remove the if statement for Ember 1.0
756
- if (DS.DebugAdapter) {
757
- Application.initializer({
758
- name: "dataAdapter",
521
+ Application.initializer({
522
+ name: "transforms",
759
523
 
760
- initialize: function(container, application) {
761
- application.register('dataAdapter:main', DS.DebugAdapter);
762
- }
763
- });
764
- }
524
+ initialize: function(container, application) {
525
+ application.register('transform:boolean', DS.BooleanTransform);
526
+ application.register('transform:date', DS.DateTransform);
527
+ application.register('transform:number', DS.NumberTransform);
528
+ application.register('transform:string', DS.StringTransform);
529
+ }
530
+ });
765
531
 
766
532
  Application.initializer({
767
533
  name: "dataAdapter",
@@ -1209,6 +975,7 @@ DS.ManyArray = DS.RecordArray.extend({
1209
975
  */
1210
976
 
1211
977
  var get = Ember.get;
978
+ var forEach = Ember.ArrayPolyfills.forEach;
1212
979
 
1213
980
  var resolveMapConflict = function(oldValue, newValue) {
1214
981
  return oldValue;
@@ -1304,7 +1071,7 @@ DS._Mappable = Ember.Mixin.create({
1304
1071
 
1305
1072
  var classMap = classMeta[mapName];
1306
1073
  if (classMap) {
1307
- classMap.forEach(eachMap, this);
1074
+ forEach.call(classMap, eachMap, this);
1308
1075
  }
1309
1076
 
1310
1077
  function eachMap(key, value) {
@@ -1322,7 +1089,6 @@ DS._Mappable = Ember.Mixin.create({
1322
1089
  }
1323
1090
  }
1324
1091
 
1325
-
1326
1092
  });
1327
1093
 
1328
1094
  DS._Mappable.generateMapFunctionFor = function(mapName, transform) {
@@ -1501,12 +1267,15 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1501
1267
  This property is cacheable, so the same instance of a specified
1502
1268
  adapter class should be used for the lifetime of the store.
1503
1269
 
1504
- @property _adapter
1270
+ @property defaultAdapter
1505
1271
  @private
1506
1272
  @returns DS.Adapter
1507
1273
  */
1508
- _adapter: Ember.computed(function() {
1274
+ defaultAdapter: Ember.computed(function() {
1509
1275
  var adapter = get(this, 'adapter');
1276
+
1277
+ 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));
1278
+
1510
1279
  if (typeof adapter === 'string') {
1511
1280
  adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:_rest');
1512
1281
  }
@@ -1878,19 +1647,20 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
1878
1647
  var unloadedRecords = records.filterProperty('isEmpty', true),
1879
1648
  manyArray = this.recordArrayManager.createManyArray(type, records);
1880
1649
 
1881
- unloadedRecords.forEach(function(record) {
1650
+ forEach(unloadedRecords, function(record) {
1882
1651
  record.loadingData();
1883
1652
  });
1884
1653
 
1885
1654
  manyArray.loadingRecordsCount = unloadedRecords.length;
1886
1655
 
1887
1656
  if (unloadedRecords.length) {
1888
- unloadedRecords.forEach(function(record) {
1657
+ forEach(unloadedRecords, function(record) {
1889
1658
  this.recordArrayManager.registerWaitingRecordArray(record, manyArray);
1890
1659
  }, this);
1891
1660
 
1892
1661
  this.fetchMany(unloadedRecords, owner, resolver);
1893
1662
  } else {
1663
+ if (resolver) { resolver.resolve(); }
1894
1664
  manyArray.set('isLoaded', true);
1895
1665
  Ember.run.once(manyArray, 'trigger', 'didLoad');
1896
1666
  }
@@ -2027,6 +1797,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2027
1797
  @return {DS.RecordArray}
2028
1798
  */
2029
1799
  all: function(type) {
1800
+ type = this.modelFor(type);
1801
+
2030
1802
  var typeMap = this.typeMapFor(type),
2031
1803
  findAllCache = typeMap.findAllCache;
2032
1804
 
@@ -2308,14 +2080,15 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2308
2080
  @method _load
2309
2081
  @private
2310
2082
  @param {DS.Model} type
2311
- @param data
2312
- @param prematerialized
2083
+ @param {Object} data
2084
+ @param {Boolean} partial the data should be merged into
2085
+ the existing fata, not replace it.
2313
2086
  */
2314
- _load: function(type, data) {
2087
+ _load: function(type, data, partial) {
2315
2088
  var id = coerceId(data.id),
2316
2089
  record = this.recordForId(type, id);
2317
2090
 
2318
- record.setupData(data);
2091
+ record.setupData(data, partial);
2319
2092
  this.recordArrayManager.recordDidChange(record);
2320
2093
 
2321
2094
  return record;
@@ -2405,17 +2178,26 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2405
2178
  @returns DS.Model the record that was created or
2406
2179
  updated.
2407
2180
  */
2408
- push: function(type, data) {
2181
+ push: function(type, data, _partial) {
2182
+ // _partial is an internal param used by `update`.
2183
+ // If passed, it means that the data should be
2184
+ // merged into the existing data, not replace it.
2185
+
2409
2186
  var serializer = this.serializerFor(type);
2410
2187
  type = this.modelFor(type);
2411
2188
 
2412
- data = serializer.deserialize(type, data);
2189
+ // normalize relationship IDs into records
2190
+ data = normalizeRelationships(this, type, data);
2413
2191
 
2414
- this._load(type, data);
2192
+ this._load(type, data, _partial);
2415
2193
 
2416
2194
  return this.recordForId(type, data.id);
2417
2195
  },
2418
2196
 
2197
+ update: function(type, data) {
2198
+ return this.push(type, data, true);
2199
+ },
2200
+
2419
2201
  /**
2420
2202
  If you have an Array of normalized data to push,
2421
2203
  you can call `pushMany` with the Array, and it will
@@ -2432,6 +2214,20 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2432
2214
  }, this);
2433
2215
  },
2434
2216
 
2217
+ /**
2218
+ If you have some metadata to set for a type
2219
+ you can call `metaForType`.
2220
+
2221
+ @method metaForType
2222
+ @param {String} type
2223
+ @param {Object} metadata
2224
+ */
2225
+ metaForType: function(type, metadata) {
2226
+ type = this.modelFor(type);
2227
+
2228
+ Ember.merge(this.typeMapFor(type).metadata, metadata);
2229
+ },
2230
+
2435
2231
  /**
2436
2232
  Build a brand new record for a given type, ID, and
2437
2233
  initial data.
@@ -2566,7 +2362,7 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2566
2362
  adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application');
2567
2363
  }
2568
2364
 
2569
- return adapter || get(this, '_adapter');
2365
+ return adapter || get(this, 'defaultAdapter');
2570
2366
  },
2571
2367
 
2572
2368
  // ..............................
@@ -2590,19 +2386,65 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
2590
2386
  @param {String} type the record to serialize
2591
2387
  */
2592
2388
  serializerFor: function(type) {
2593
- var container = this.container;
2389
+ type = this.modelFor(type);
2390
+ var adapter = this.adapterForType(type);
2391
+
2392
+ return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer);
2393
+ }
2394
+ });
2395
+
2396
+ function normalizeRelationships(store, type, data) {
2397
+ type.eachRelationship(function(key, relationship) {
2398
+ // A link (usually a URL) was already provided in
2399
+ // normalized form
2400
+ if (data.links && data.links[key]) {
2401
+ return;
2402
+ }
2594
2403
 
2595
- // TODO: Make tests pass without this
2404
+ var type = relationship.type,
2405
+ value = data[key];
2596
2406
 
2597
- if (!container) {
2598
- return DS.JSONSerializer.create({ store: this });
2407
+ if (value == null) { return; }
2408
+
2409
+ if (relationship.kind === 'belongsTo') {
2410
+ deserializeRecordId(store, data, key, relationship, value);
2411
+ } else if (relationship.kind === 'hasMany') {
2412
+ deserializeRecordIds(store, data, key, relationship, value);
2599
2413
  }
2414
+ });
2600
2415
 
2601
- return container.lookup('serializer:'+type) ||
2602
- container.lookup('serializer:application') ||
2603
- container.lookup('serializer:_default');
2416
+ return data;
2417
+ }
2418
+
2419
+ function deserializeRecordId(store, data, key, relationship, id) {
2420
+ if (isNone(id) || id instanceof DS.Model) {
2421
+ return;
2604
2422
  }
2605
- });
2423
+
2424
+ var type;
2425
+
2426
+ if (typeof id === 'number' || typeof id === 'string') {
2427
+ type = typeFor(relationship, key, data);
2428
+ data[key] = store.recordForId(type, id);
2429
+ } else if (typeof id === 'object') {
2430
+ // polymorphic
2431
+ data[key] = store.recordForId(id.type, id.id);
2432
+ }
2433
+ }
2434
+
2435
+ function typeFor(relationship, key, data) {
2436
+ if (relationship.options.polymorphic) {
2437
+ return data[key + "_type"];
2438
+ } else {
2439
+ return relationship.type;
2440
+ }
2441
+ }
2442
+
2443
+ function deserializeRecordIds(store, data, key, relationship, ids) {
2444
+ for (var i=0, l=ids.length; i<l; i++) {
2445
+ deserializeRecordId(store, ids, i, relationship, ids[i]);
2446
+ }
2447
+ }
2606
2448
 
2607
2449
  // Delegation to the adapter and promise management
2608
2450
 
@@ -2621,15 +2463,20 @@ function isThenable(object) {
2621
2463
  return object && typeof object.then === 'function';
2622
2464
  }
2623
2465
 
2624
- function serializerFor(adapter, type) {
2466
+ function serializerFor(container, type, defaultSerializer) {
2467
+ return container.lookup('serializer:'+type) ||
2468
+ container.lookup('serializer:application') ||
2469
+ container.lookup('serializer:' + defaultSerializer) ||
2470
+ container.lookup('serializer:_default');
2471
+ }
2472
+
2473
+ function serializerForAdapter(adapter, type) {
2625
2474
  var serializer = adapter.serializer,
2626
2475
  defaultSerializer = adapter.defaultSerializer,
2627
2476
  container = adapter.container;
2628
2477
 
2629
2478
  if (container && serializer === undefined) {
2630
- serializer = container.lookup('serializer:'+type.typeKey) ||
2631
- container.lookup('serializer:application') ||
2632
- container.lookup('serializer:' + defaultSerializer || 'serializer:_default');
2479
+ serializer = serializerFor(container, type.typeKey, defaultSerializer);
2633
2480
  }
2634
2481
 
2635
2482
  if (serializer === null || serializer === undefined) {
@@ -2643,7 +2490,7 @@ function serializerFor(adapter, type) {
2643
2490
 
2644
2491
  function _find(adapter, store, type, id, resolver) {
2645
2492
  var promise = adapter.find(store, type, id),
2646
- serializer = serializerFor(adapter, type);
2493
+ serializer = serializerForAdapter(adapter, type);
2647
2494
 
2648
2495
  return resolve(promise).then(function(payload) {
2649
2496
  Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", payload);
@@ -2655,7 +2502,7 @@ function _find(adapter, store, type, id, resolver) {
2655
2502
 
2656
2503
  function _findMany(adapter, store, type, ids, owner, resolver) {
2657
2504
  var promise = adapter.findMany(store, type, ids, owner),
2658
- serializer = serializerFor(adapter, type);
2505
+ serializer = serializerForAdapter(adapter, type);
2659
2506
 
2660
2507
  return resolve(promise).then(function(payload) {
2661
2508
  payload = serializer.extract(store, type, payload, null, 'findMany');
@@ -2666,7 +2513,7 @@ function _findMany(adapter, store, type, ids, owner, resolver) {
2666
2513
 
2667
2514
  function _findHasMany(adapter, store, record, link, relationship, resolver) {
2668
2515
  var promise = adapter.findHasMany(store, record, link, relationship),
2669
- serializer = serializerFor(adapter, relationship.type);
2516
+ serializer = serializerForAdapter(adapter, relationship.type);
2670
2517
 
2671
2518
  return resolve(promise).then(function(payload) {
2672
2519
  payload = serializer.extract(store, relationship.type, payload, null, 'findHasMany');
@@ -2678,7 +2525,7 @@ function _findHasMany(adapter, store, record, link, relationship, resolver) {
2678
2525
 
2679
2526
  function _findAll(adapter, store, type, sinceToken, resolver) {
2680
2527
  var promise = adapter.findAll(store, type, sinceToken),
2681
- serializer = serializerFor(adapter, type);
2528
+ serializer = serializerForAdapter(adapter, type);
2682
2529
 
2683
2530
  return resolve(promise).then(function(payload) {
2684
2531
  payload = serializer.extract(store, type, payload, null, 'findAll');
@@ -2691,7 +2538,7 @@ function _findAll(adapter, store, type, sinceToken, resolver) {
2691
2538
 
2692
2539
  function _findQuery(adapter, store, type, query, recordArray, resolver) {
2693
2540
  var promise = adapter.findQuery(store, type, query, recordArray),
2694
- serializer = serializerFor(adapter, type);
2541
+ serializer = serializerForAdapter(adapter, type);
2695
2542
 
2696
2543
  return resolve(promise).then(function(payload) {
2697
2544
  payload = serializer.extract(store, type, payload, null, 'findAll');
@@ -2704,7 +2551,7 @@ function _findQuery(adapter, store, type, query, recordArray, resolver) {
2704
2551
  function _commit(adapter, store, operation, record, resolver) {
2705
2552
  var type = record.constructor,
2706
2553
  promise = adapter[operation](store, type, record),
2707
- serializer = serializerFor(adapter, type);
2554
+ serializer = serializerForAdapter(adapter, type);
2708
2555
 
2709
2556
  Ember.assert("Your adapter's '" + operation + "' method must return a promise, but it returned " + promise, isThenable(promise));
2710
2557
 
@@ -2978,7 +2825,7 @@ var DirtyState = {
2978
2825
  get(record, 'store').reloadRecord(record, resolver);
2979
2826
  },
2980
2827
 
2981
- becameClean: function(record) {
2828
+ rolledBack: function(record) {
2982
2829
  record.transitionTo('loaded.saved');
2983
2830
  },
2984
2831
 
@@ -3108,6 +2955,10 @@ var createdState = dirtyState({
3108
2955
  isNew: true
3109
2956
  });
3110
2957
 
2958
+ createdState.uncommitted.rolledBack = function(record) {
2959
+ record.transitionTo('deleted.saved');
2960
+ };
2961
+
3111
2962
  var updatedState = dirtyState({
3112
2963
  dirtyType: 'updated'
3113
2964
  });
@@ -3138,6 +2989,14 @@ var RootState = {
3138
2989
  isNew: false,
3139
2990
  isValid: true,
3140
2991
 
2992
+ // DEFAULT EVENTS
2993
+
2994
+ // Trying to roll back if you're not in the dirty state
2995
+ // doesn't change your state. For example, if you're in the
2996
+ // in-flight state, rolling back the record doesn't move
2997
+ // you out of the in-flight state.
2998
+ rolledBack: Ember.K,
2999
+
3141
3000
  // SUBSTATES
3142
3001
 
3143
3002
  // A record begins its lifecycle in the `empty` state.
@@ -3301,7 +3160,7 @@ var RootState = {
3301
3160
  becomeDirty: Ember.K,
3302
3161
  deleteRecord: Ember.K,
3303
3162
 
3304
- becameClean: function(record) {
3163
+ rolledBack: function(record) {
3305
3164
  record.transitionTo('loaded.saved');
3306
3165
  }
3307
3166
  },
@@ -3322,6 +3181,11 @@ var RootState = {
3322
3181
  record.transitionTo('saved');
3323
3182
 
3324
3183
  record.send('invokeLifecycleCallbacks');
3184
+ },
3185
+
3186
+ becameError: function(record) {
3187
+ record.transitionTo('uncommitted');
3188
+ record.triggerLater('becameError', record);
3325
3189
  }
3326
3190
  },
3327
3191
 
@@ -3701,8 +3565,18 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3701
3565
  Ember.run.once(this, this.updateRecordArrays);
3702
3566
  },
3703
3567
 
3704
- setupData: function(data) {
3705
- this._data = data;
3568
+ setupData: function(data, partial) {
3569
+ if (partial) {
3570
+ Ember.merge(this._data, data);
3571
+ } else {
3572
+ this._data = data;
3573
+ }
3574
+
3575
+ var relationships = this._relationships;
3576
+
3577
+ this.eachRelationship(function(name, rel) {
3578
+ if (rel.options.async) { relationships[name] = null; }
3579
+ });
3706
3580
 
3707
3581
  if (data) { this.pushedData(); }
3708
3582
 
@@ -3730,8 +3604,8 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
3730
3604
  },
3731
3605
 
3732
3606
  rollback: function() {
3733
- this._setup();
3734
- this.send('becameClean');
3607
+ this._attributes = {};
3608
+ this.send('rolledBack');
3735
3609
 
3736
3610
  this.suspendRelationshipObservers(function() {
3737
3611
  this.notifyPropertyChange('data');
@@ -3962,21 +3836,48 @@ DS.Model.reopen({
3962
3836
  }
3963
3837
  });
3964
3838
 
3965
- function getAttr(record, options, key) {
3966
- var attributes = get(record, 'data');
3967
- var value = attributes[key];
3968
-
3969
- if (value === undefined) {
3970
- if (typeof options.defaultValue === "function") {
3971
- value = options.defaultValue();
3972
- } else {
3973
- value = options.defaultValue;
3974
- }
3839
+ function getDefaultValue(record, options, key) {
3840
+ if (typeof options.defaultValue === "function") {
3841
+ return options.defaultValue();
3842
+ } else {
3843
+ return options.defaultValue;
3975
3844
  }
3845
+ }
3976
3846
 
3977
- return value;
3847
+ function hasValue(record, key) {
3848
+ return record._attributes.hasOwnProperty(key) ||
3849
+ record._inFlightAttributes.hasOwnProperty(key) ||
3850
+ record._data.hasOwnProperty(key);
3851
+ }
3852
+
3853
+ function getValue(record, key) {
3854
+ if (record._attributes.hasOwnProperty(key)) {
3855
+ return record._attributes[key];
3856
+ } else if (record._inFlightAttributes.hasOwnProperty(key)) {
3857
+ return record._inFlightAttributes[key];
3858
+ } else {
3859
+ return record._data[key];
3860
+ }
3978
3861
  }
3979
3862
 
3863
+ /**
3864
+ `DS.attr` defines an attribute on a DS.Model.
3865
+ By default, attributes are passed through as-is, however you can specify an
3866
+ optional type to have the value automatically transformed.
3867
+ Ember Data ships with four basic transform types:
3868
+ 'string', 'number', 'boolean' and 'date'.
3869
+ You can define your own transforms by subclassing DS.Transform.
3870
+
3871
+ DS.attr takes an optional hash as a second parameter, currently
3872
+ supported options are:
3873
+ 'defaultValue': Pass a string or a function to be called to set the attribute
3874
+ to a default value if none is supplied.
3875
+
3876
+ @method attr
3877
+ @param {String} type the attribute type
3878
+ @param {Object} options a hash of options
3879
+ */
3880
+
3980
3881
  DS.attr = function(type, options) {
3981
3882
  options = options || {};
3982
3883
 
@@ -3987,17 +3888,19 @@ DS.attr = function(type, options) {
3987
3888
  };
3988
3889
 
3989
3890
  return Ember.computed(function(key, value, oldValue) {
3891
+ var currentValue;
3892
+
3990
3893
  if (arguments.length > 1) {
3991
3894
  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
3895
  this.send('didSetProperty', { name: key, oldValue: this._attributes[key] || this._inFlightAttributes[key] || this._data[key], value: value });
3993
3896
  this._attributes[key] = value;
3994
- } else if (this._attributes[key]) {
3995
- return this._attributes[key];
3897
+ return value;
3898
+ } else if (hasValue(this, key)) {
3899
+ return getValue(this, key);
3996
3900
  } else {
3997
- value = getAttr(this, options, key);
3901
+ return getDefaultValue(this, options, key);
3998
3902
  }
3999
3903
 
4000
- return value;
4001
3904
  // `data` is never set directly. However, it may be
4002
3905
  // invalidated from the state manager's setData
4003
3906
  // event.
@@ -4542,14 +4445,21 @@ var get = Ember.get, set = Ember.set,
4542
4445
  function asyncBelongsTo(type, options, meta) {
4543
4446
  return Ember.computed(function(key, value) {
4544
4447
  var data = get(this, 'data'),
4545
- store = get(this, 'store');
4448
+ store = get(this, 'store'),
4449
+ belongsTo;
4546
4450
 
4547
4451
  if (arguments.length === 2) {
4548
- Ember.assert("You can only add a '" + type + "' record to this relationship", !value || store.modelFor(type).detectInstance(value));
4452
+ Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof store.modelFor(type));
4549
4453
  return value === undefined ? null : value;
4550
4454
  }
4551
4455
 
4552
- return store.fetchRecord(data[key]);
4456
+ belongsTo = data[key];
4457
+
4458
+ if(!isNone(belongsTo) && get(belongsTo, 'isEmpty')) {
4459
+ return store.fetchRecord(belongsTo);
4460
+ } else {
4461
+ return null;
4462
+ }
4553
4463
  }).property('data').meta(meta);
4554
4464
  }
4555
4465
 
@@ -4579,7 +4489,7 @@ DS.belongsTo = function(type, options) {
4579
4489
  }
4580
4490
 
4581
4491
  if (arguments.length === 2) {
4582
- Ember.assert("You can only add a '" + type + "' record to this relationship", !value || typeClass.detectInstance(value));
4492
+ Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof typeClass);
4583
4493
  return value === undefined ? null : value;
4584
4494
  }
4585
4495
 
@@ -5376,7 +5286,7 @@ DS.RecordArrayManager = Ember.Object.extend({
5376
5286
  */
5377
5287
 
5378
5288
  var get = Ember.get, set = Ember.set, merge = Ember.merge;
5379
- var forEach = Ember.EnumerableUtils.forEach;
5289
+ var map = Ember.ArrayPolyfills.map;
5380
5290
  var resolve = Ember.RSVP.resolve;
5381
5291
 
5382
5292
  var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
@@ -5441,7 +5351,7 @@ function aliasMethod(methodName) {
5441
5351
  * `deleteRecords()`
5442
5352
  * `commit()`
5443
5353
 
5444
- For an example implementation, see `DS.RestAdapter`, the
5354
+ For an example implementation, see `DS.RESTAdapter`, the
5445
5355
  included REST adapter.
5446
5356
 
5447
5357
  @class Adapter
@@ -5589,7 +5499,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
5589
5499
  @property {Array} ids
5590
5500
  */
5591
5501
  findMany: function(store, type, ids) {
5592
- var promises = ids.map(function(id) {
5502
+ var promises = map.call(ids, function(id) {
5593
5503
  return this.find(store, type, id);
5594
5504
  }, this);
5595
5505
 
@@ -5712,7 +5622,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
5712
5622
  var fixtures = this.fixturesForType(type),
5713
5623
  fixture;
5714
5624
 
5715
- Ember.warn("Unable to find fixtures for model type " + type.toString(), fixtures);
5625
+ Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
5716
5626
 
5717
5627
  if (fixtures) {
5718
5628
  fixture = Ember.A(fixtures).findProperty('id', id);
@@ -5734,7 +5644,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
5734
5644
  findMany: function(store, type, ids) {
5735
5645
  var fixtures = this.fixturesForType(type);
5736
5646
 
5737
- Ember.assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
5647
+ Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
5738
5648
 
5739
5649
  if (fixtures) {
5740
5650
  fixtures = fixtures.filter(function(item) {
@@ -5757,7 +5667,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
5757
5667
  findAll: function(store, type) {
5758
5668
  var fixtures = this.fixturesForType(type);
5759
5669
 
5760
- Ember.assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
5670
+ Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
5761
5671
 
5762
5672
  return this.simulateRemoteCall(function() {
5763
5673
  return fixtures;
@@ -5774,7 +5684,7 @@ DS.FixtureAdapter = DS.Adapter.extend({
5774
5684
  findQuery: function(store, type, query, array) {
5775
5685
  var fixtures = this.fixturesForType(type);
5776
5686
 
5777
- Ember.assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
5687
+ Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
5778
5688
 
5779
5689
  fixtures = this.queryFixtures(fixtures, query, type);
5780
5690
 
@@ -5914,17 +5824,48 @@ DS.FixtureAdapter = DS.Adapter.extend({
5914
5824
  */
5915
5825
 
5916
5826
  var get = Ember.get, set = Ember.set;
5917
-
5918
- DS.rejectionHandler = function(reason) {
5919
- Ember.Logger.assert([reason, reason.message, reason.stack]);
5920
-
5921
- throw reason;
5922
- };
5827
+ var forEach = Ember.ArrayPolyfills.forEach;
5923
5828
 
5924
5829
  function coerceId(id) {
5925
5830
  return id == null ? null : id+'';
5926
5831
  }
5927
5832
 
5833
+ /**
5834
+ Normally, applications will use the `RESTSerializer` by implementing
5835
+ the `normalize` method and individual normalizations under
5836
+ `normalizeHash`.
5837
+
5838
+ This allows you to do whatever kind of munging you need, and is
5839
+ especially useful if your server is inconsistent and you need to
5840
+ do munging differently for many different kinds of responses.
5841
+
5842
+ See the `normalize` documentation for more information.
5843
+
5844
+ ## Across the Board Normalization
5845
+
5846
+ There are also a number of hooks that you might find useful to defined
5847
+ across-the-board rules for your payload. These rules will be useful
5848
+ if your server is consistent, or if you're building an adapter for
5849
+ an infrastructure service, like Parse, and want to encode service
5850
+ conventions.
5851
+
5852
+ For example, if all of your keys are underscored and all-caps, but
5853
+ otherwise consistent with the names you use in your models, you
5854
+ can implement across-the-board rules for how to convert an attribute
5855
+ name in your model to a key in your JSON.
5856
+
5857
+ ```js
5858
+ App.ApplicationSerializer = DS.RESTSerializer.extend({
5859
+ keyForAttribute: function(attr) {
5860
+ return Ember.String.underscore(attr).toUpperCase();
5861
+ }
5862
+ });
5863
+ ```
5864
+
5865
+ You can also implement `keyForRelationship`, which takes the name
5866
+ of the relationship as the first parameter, and the kind of
5867
+ relationship (`hasMany` or `belongsTo`) as the second parameter.
5868
+ */
5928
5869
  DS.RESTSerializer = DS.JSONSerializer.extend({
5929
5870
  /**
5930
5871
  Normalizes a part of the JSON payload returned by
@@ -5990,15 +5931,17 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
5990
5931
  @param {Object} hash
5991
5932
  @returns Object
5992
5933
  */
5993
- normalize: function(type, prop, hash) {
5934
+ normalize: function(type, hash, prop) {
5994
5935
  this.normalizeId(hash);
5995
- this.normalizeAttributes(hash);
5936
+ this.normalizeUsingDeclaredMapping(type, hash);
5937
+ this.normalizeAttributes(type, hash);
5938
+ this.normalizeRelationships(type, hash);
5996
5939
 
5997
5940
  if (this.normalizeHash && this.normalizeHash[prop]) {
5998
5941
  return this.normalizeHash[prop](hash);
5999
5942
  }
6000
5943
 
6001
- return hash;
5944
+ return this._super(type, hash, prop);
6002
5945
  },
6003
5946
 
6004
5947
  /**
@@ -6014,20 +5957,56 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6014
5957
  delete hash[primaryKey];
6015
5958
  },
6016
5959
 
5960
+ /**
5961
+ @method normalizeUsingDeclaredMapping
5962
+ @private
5963
+ */
5964
+ normalizeUsingDeclaredMapping: function(type, hash) {
5965
+ var attrs = get(this, 'attrs'), payloadKey, key;
5966
+
5967
+ if (attrs) {
5968
+ for (key in attrs) {
5969
+ payloadKey = attrs[key];
5970
+
5971
+ hash[key] = hash[payloadKey];
5972
+ delete hash[payloadKey];
5973
+ }
5974
+ }
5975
+ },
5976
+
6017
5977
  /**
6018
5978
  @method normalizeAttributes
6019
5979
  @private
6020
5980
  */
6021
- normalizeAttributes: function(hash) {
6022
- var attrs = get(this, 'attrs');
5981
+ normalizeAttributes: function(type, hash) {
5982
+ var payloadKey, key;
5983
+
5984
+ if (this.keyForAttribute) {
5985
+ type.eachAttribute(function(key) {
5986
+ payloadKey = this.keyForAttribute(key);
5987
+ if (key === payloadKey) { return; }
5988
+
5989
+ hash[key] = hash[payloadKey];
5990
+ delete hash[payloadKey];
5991
+ }, this);
5992
+ }
5993
+ },
6023
5994
 
6024
- if (!attrs) { return; }
5995
+ /**
5996
+ @method normalizeRelationships
5997
+ @private
5998
+ */
5999
+ normalizeRelationships: function(type, hash) {
6000
+ var payloadKey, key;
6025
6001
 
6026
- for (var key in attrs) {
6027
- var payloadKey = attrs[key];
6002
+ if (this.keyForRelationship) {
6003
+ type.eachRelationship(function(key, relationship) {
6004
+ payloadKey = this.keyForRelationship(key, relationship.kind);
6005
+ if (key === payloadKey) { return; }
6028
6006
 
6029
- hash[key] = hash[payloadKey];
6030
- delete hash[payloadKey];
6007
+ hash[key] = hash[payloadKey];
6008
+ delete hash[payloadKey];
6009
+ }, this);
6031
6010
  }
6032
6011
  },
6033
6012
 
@@ -6114,7 +6093,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6114
6093
  for (var prop in payload) {
6115
6094
  // legacy support for singular names
6116
6095
  if (prop === primaryTypeName) {
6117
- primaryRecord = this.normalize(primaryType, prop, payload[prop]);
6096
+ primaryRecord = this.normalize(primaryType, payload[prop], prop);
6118
6097
  continue;
6119
6098
  }
6120
6099
 
@@ -6122,8 +6101,8 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6122
6101
  type = store.modelFor(typeName);
6123
6102
 
6124
6103
  /*jshint loopfunc:true*/
6125
- payload[prop].forEach(function(hash) {
6126
- hash = this.normalize(type, prop, hash);
6104
+ forEach.call(payload[prop], function(hash) {
6105
+ hash = this.normalize(type, hash, prop);
6127
6106
 
6128
6107
  var isFirstCreatedRecord = typeName === primaryTypeName && !recordId && !primaryRecord,
6129
6108
  isUpdatedRecord = typeName === primaryTypeName && coerceId(hash.id) === recordId;
@@ -6251,7 +6230,7 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6251
6230
 
6252
6231
  /*jshint loopfunc:true*/
6253
6232
  var normalizedArray = payload[prop].map(function(hash) {
6254
- return this.normalize(type, prop, hash);
6233
+ return this.normalize(type, hash, prop);
6255
6234
  }, this);
6256
6235
 
6257
6236
  if (isPrimary) {
@@ -6427,6 +6406,18 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6427
6406
  }
6428
6407
  });
6429
6408
 
6409
+ })();
6410
+
6411
+
6412
+
6413
+ (function() {
6414
+ /**
6415
+ @module ember-data
6416
+ */
6417
+
6418
+ var get = Ember.get, set = Ember.set;
6419
+ var forEach = Ember.ArrayPolyfills.forEach;
6420
+
6430
6421
  /**
6431
6422
  The REST adapter allows your store to communicate with an HTTP server by
6432
6423
  transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
@@ -6498,11 +6489,11 @@ DS.RESTSerializer = DS.JSONSerializer.extend({
6498
6489
 
6499
6490
  ### Host customization
6500
6491
 
6501
- An adapter can target other hosts by setting the `url` property.
6492
+ An adapter can target other hosts by setting the `host` property.
6502
6493
 
6503
6494
  ```js
6504
6495
  DS.RESTAdapter.reopen({
6505
- url: 'https://api.example.com'
6496
+ host: 'https://api.example.com'
6506
6497
  });
6507
6498
  ```
6508
6499
 
@@ -6544,7 +6535,7 @@ DS.RESTAdapter = DS.Adapter.extend({
6544
6535
  @returns Promise
6545
6536
  */
6546
6537
  find: function(store, type, id) {
6547
- return this.ajax(this.buildURL(type, id), 'GET');
6538
+ return this.ajax(this.buildURL(type.typeKey, id), 'GET');
6548
6539
  },
6549
6540
 
6550
6541
  /**
@@ -6559,10 +6550,17 @@ DS.RESTAdapter = DS.Adapter.extend({
6559
6550
  @see RESTAdapter/ajax
6560
6551
  @param {DS.Store} store
6561
6552
  @param {subclass of DS.Model} type
6553
+ @param {String} sinceToken
6562
6554
  @returns Promise
6563
6555
  */
6564
- findAll: function(store, type) {
6565
- return this.ajax(this.buildURL(type), 'GET');
6556
+ findAll: function(store, type, sinceToken) {
6557
+ var query;
6558
+
6559
+ if (sinceToken) {
6560
+ query = { since: sinceToken };
6561
+ }
6562
+
6563
+ return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
6566
6564
  },
6567
6565
 
6568
6566
  /**
@@ -6584,7 +6582,7 @@ DS.RESTAdapter = DS.Adapter.extend({
6584
6582
  @returns Promise
6585
6583
  */
6586
6584
  findQuery: function(store, type, query) {
6587
- return this.ajax(this.buildURL(type), 'GET', query);
6585
+ return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query });
6588
6586
  },
6589
6587
 
6590
6588
  /**
@@ -6623,8 +6621,8 @@ DS.RESTAdapter = DS.Adapter.extend({
6623
6621
  @param {Array<String>} ids
6624
6622
  @returns Promise
6625
6623
  */
6626
- findMany: function(store, type, ids) {
6627
- return this.ajax(this.buildURL(type), 'GET', { ids: ids });
6624
+ findMany: function(store, type, ids, owner) {
6625
+ return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } });
6628
6626
  },
6629
6627
 
6630
6628
  /**
@@ -6680,9 +6678,9 @@ DS.RESTAdapter = DS.Adapter.extend({
6680
6678
  */
6681
6679
  createRecord: function(store, type, record) {
6682
6680
  var data = {};
6683
- data[type.typeKey] = this.serializerFor(type.typeKey).serialize(record, { includeId: true });
6681
+ data[type.typeKey] = store.serializerFor(type.typeKey).serialize(record, { includeId: true });
6684
6682
 
6685
- return this.ajax(this.buildURL(type), "POST", { data: data });
6683
+ return this.ajax(this.buildURL(type.typeKey), "POST", { data: data });
6686
6684
  },
6687
6685
 
6688
6686
  /**
@@ -6704,11 +6702,11 @@ DS.RESTAdapter = DS.Adapter.extend({
6704
6702
  */
6705
6703
  updateRecord: function(store, type, record) {
6706
6704
  var data = {};
6707
- data[type.typeKey] = this.serializerFor(type.typeKey).serialize(record);
6705
+ data[type.typeKey] = store.serializerFor(type.typeKey).serialize(record);
6708
6706
 
6709
6707
  var id = get(record, 'id');
6710
6708
 
6711
- return this.ajax(this.buildURL(type, id), "PUT", { data: data });
6709
+ return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data });
6712
6710
  },
6713
6711
 
6714
6712
  /**
@@ -6728,38 +6726,65 @@ DS.RESTAdapter = DS.Adapter.extend({
6728
6726
  deleteRecord: function(store, type, record) {
6729
6727
  var id = get(record, 'id');
6730
6728
 
6731
- return this.ajax(this.buildURL(type, id), "DELETE");
6729
+ return this.ajax(this.buildURL(type.typeKey, id), "DELETE");
6730
+ },
6731
+
6732
+ /**
6733
+ Builds a URL for a given type and optional ID.
6734
+
6735
+ If an ID is specified, it adds the ID to the root generated
6736
+ for the type, separated by a `/`.
6737
+
6738
+ @method buildURL
6739
+ @param {String} type
6740
+ @param {String} id
6741
+ @returns String
6742
+ */
6743
+ buildURL: function(type, id) {
6744
+ var host = get(this, 'host'),
6745
+ namespace = get(this, 'namespace'),
6746
+ url = [];
6747
+
6748
+ if (host) { url.push(host); }
6749
+ if (namespace) { url.push(namespace); }
6750
+
6751
+ url.push(this.rootForType(type));
6752
+ if (id) { url.push(id); }
6753
+
6754
+ url = url.join('/');
6755
+ if (!host) { url = '/' + url; }
6756
+
6757
+ return url;
6732
6758
  },
6733
6759
 
6734
6760
  /**
6735
- Builds a URL for a given type and optional ID.
6761
+ Determines the pathname root for a given type.
6736
6762
 
6737
6763
  By default, it pluralizes the type's name (for example,
6738
6764
  'post' becomes 'posts' and 'person' becomes 'people').
6739
6765
 
6740
- If an ID is specified, it adds the ID to the plural form
6741
- of the type, separated by a `/`.
6766
+ ### Pathname root customization
6742
6767
 
6743
- @method buildURL
6744
- @param {subclass of DS.Model} type
6745
- @param {String} id
6746
- @returns String
6747
- */
6748
- buildURL: function(type, id) {
6749
- var url = "/" + Ember.String.pluralize(type.typeKey);
6750
- if (id) { url += "/" + id; }
6768
+ For example if you have an object LineItem with an
6769
+ endpoint of "/line_items/".
6751
6770
 
6752
- return url;
6753
- },
6771
+ ```js
6772
+ DS.RESTAdapter.reopen({
6773
+ rootForType: function(type) {
6774
+ var decamelized = Ember.String.decamelize(type);
6775
+ return Ember.String.pluralize(decamelized);
6776
+ };
6777
+ });
6778
+ ```
6754
6779
 
6755
- serializerFor: function(type) {
6756
- // This logic has to be kept in sync with DS.Store#serializerFor
6757
- return this.container.lookup('serializer:' + type) ||
6758
- this.container.lookup('serializer:application') ||
6759
- this.container.lookup('serializer:_rest');
6780
+ @method rootForType
6781
+ @param {String} type
6782
+ @returns String
6783
+ **/
6784
+ rootForType: function(type) {
6785
+ return Ember.String.pluralize(type);
6760
6786
  },
6761
6787
 
6762
-
6763
6788
  /**
6764
6789
  Takes a URL, an HTTP method and a hash of data, and makes an
6765
6790
  HTTP request.
@@ -6801,7 +6826,7 @@ DS.RESTAdapter = DS.Adapter.extend({
6801
6826
  if (adapter.headers !== undefined) {
6802
6827
  var headers = adapter.headers;
6803
6828
  hash.beforeSend = function (xhr) {
6804
- Ember.keys(headers).forEach(function(key) {
6829
+ forEach.call(Ember.keys(headers), function(key) {
6805
6830
  xhr.setRequestHeader(key, headers[key]);
6806
6831
  });
6807
6832
  };
@@ -6945,3 +6970,242 @@ DS.Model.reopen({
6945
6970
 
6946
6971
  })();
6947
6972
 
6973
+ (function() {
6974
+ Ember.String.pluralize = function(word) {
6975
+ return Ember.Inflector.inflector.pluralize(word);
6976
+ };
6977
+
6978
+ Ember.String.singularize = function(word) {
6979
+ return Ember.Inflector.inflector.singularize(word);
6980
+ };
6981
+
6982
+ })();
6983
+
6984
+
6985
+
6986
+ (function() {
6987
+ var BLANK_REGEX = /^\s*$/;
6988
+
6989
+ function loadUncountable(rules, uncountable) {
6990
+ for (var i = 0, length = uncountable.length; i < length; i++) {
6991
+ rules.uncountable[uncountable[i]] = true;
6992
+ }
6993
+ }
6994
+
6995
+ function loadIrregular(rules, irregularPairs) {
6996
+ var pair;
6997
+
6998
+ for (var i = 0, length = irregularPairs.length; i < length; i++) {
6999
+ pair = irregularPairs[i];
7000
+
7001
+ rules.irregular[pair[0]] = pair[1];
7002
+ rules.irregularInverse[pair[1]] = pair[0];
7003
+ }
7004
+ }
7005
+
7006
+ function Inflector(ruleSet) {
7007
+ ruleSet = ruleSet || {};
7008
+ ruleSet.uncountable = ruleSet.uncountable || {};
7009
+ ruleSet.irregularPairs= ruleSet.irregularPairs|| {};
7010
+
7011
+ var rules = this.rules = {
7012
+ plurals: ruleSet.plurals || [],
7013
+ singular: ruleSet.singular || [],
7014
+ irregular: {},
7015
+ irregularInverse: {},
7016
+ uncountable: {}
7017
+ };
7018
+
7019
+ loadUncountable(rules, ruleSet.uncountable);
7020
+ loadIrregular(rules, ruleSet.irregularPairs);
7021
+ }
7022
+
7023
+ Inflector.prototype = {
7024
+ pluralize: function(word) {
7025
+ return this.inflect(word, this.rules.plurals);
7026
+ },
7027
+
7028
+ singularize: function(word) {
7029
+ return this.inflect(word, this.rules.singular);
7030
+ },
7031
+
7032
+ inflect: function(word, typeRules) {
7033
+ var inflection, substitution, result, lowercase, isBlank,
7034
+ isUncountable, isIrregular, isIrregularInverse, rule;
7035
+
7036
+ isBlank = BLANK_REGEX.test(word);
7037
+
7038
+ if (isBlank) {
7039
+ return word;
7040
+ }
7041
+
7042
+ lowercase = word.toLowerCase();
7043
+
7044
+ isUncountable = this.rules.uncountable[lowercase];
7045
+
7046
+ if (isUncountable) {
7047
+ return word;
7048
+ }
7049
+
7050
+ isIrregular = this.rules.irregular[lowercase];
7051
+
7052
+ if (isIrregular) {
7053
+ return isIrregular;
7054
+ }
7055
+
7056
+ isIrregularInverse = this.rules.irregularInverse[lowercase];
7057
+
7058
+ if (isIrregularInverse) {
7059
+ return isIrregularInverse;
7060
+ }
7061
+
7062
+ for (var i = typeRules.length, min = 0; i > min; i--) {
7063
+ inflection = typeRules[i-1];
7064
+ rule = inflection[0];
7065
+
7066
+ if (rule.test(word)) {
7067
+ break;
7068
+ }
7069
+ }
7070
+
7071
+ inflection = inflection || [];
7072
+
7073
+ rule = inflection[0];
7074
+ substitution = inflection[1];
7075
+
7076
+ result = word.replace(rule, substitution);
7077
+
7078
+ return result;
7079
+ }
7080
+ };
7081
+
7082
+ Ember.Inflector = Inflector;
7083
+
7084
+ })();
7085
+
7086
+
7087
+
7088
+ (function() {
7089
+ Ember.Inflector.defaultRules = {
7090
+ plurals: [
7091
+ [/$/, 's'],
7092
+ [/s$/i, 's'],
7093
+ [/^(ax|test)is$/i, '$1es'],
7094
+ [/(octop|vir)us$/i, '$1i'],
7095
+ [/(octop|vir)i$/i, '$1i'],
7096
+ [/(alias|status)$/i, '$1es'],
7097
+ [/(bu)s$/i, '$1ses'],
7098
+ [/(buffal|tomat)o$/i, '$1oes'],
7099
+ [/([ti])um$/i, '$1a'],
7100
+ [/([ti])a$/i, '$1a'],
7101
+ [/sis$/i, 'ses'],
7102
+ [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
7103
+ [/(hive)$/i, '$1s'],
7104
+ [/([^aeiouy]|qu)y$/i, '$1ies'],
7105
+ [/(x|ch|ss|sh)$/i, '$1es'],
7106
+ [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
7107
+ [/^(m|l)ouse$/i, '$1ice'],
7108
+ [/^(m|l)ice$/i, '$1ice'],
7109
+ [/^(ox)$/i, '$1en'],
7110
+ [/^(oxen)$/i, '$1'],
7111
+ [/(quiz)$/i, '$1zes']
7112
+ ],
7113
+
7114
+ singular: [
7115
+ [/s$/i, ''],
7116
+ [/(ss)$/i, '$1'],
7117
+ [/(n)ews$/i, '$1ews'],
7118
+ [/([ti])a$/i, '$1um'],
7119
+ [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
7120
+ [/(^analy)(sis|ses)$/i, '$1sis'],
7121
+ [/([^f])ves$/i, '$1fe'],
7122
+ [/(hive)s$/i, '$1'],
7123
+ [/(tive)s$/i, '$1'],
7124
+ [/([lr])ves$/i, '$1f'],
7125
+ [/([^aeiouy]|qu)ies$/i, '$1y'],
7126
+ [/(s)eries$/i, '$1eries'],
7127
+ [/(m)ovies$/i, '$1ovie'],
7128
+ [/(x|ch|ss|sh)es$/i, '$1'],
7129
+ [/^(m|l)ice$/i, '$1ouse'],
7130
+ [/(bus)(es)?$/i, '$1'],
7131
+ [/(o)es$/i, '$1'],
7132
+ [/(shoe)s$/i, '$1'],
7133
+ [/(cris|test)(is|es)$/i, '$1is'],
7134
+ [/^(a)x[ie]s$/i, '$1xis'],
7135
+ [/(octop|vir)(us|i)$/i, '$1us'],
7136
+ [/(alias|status)(es)?$/i, '$1'],
7137
+ [/^(ox)en/i, '$1'],
7138
+ [/(vert|ind)ices$/i, '$1ex'],
7139
+ [/(matr)ices$/i, '$1ix'],
7140
+ [/(quiz)zes$/i, '$1'],
7141
+ [/(database)s$/i, '$1']
7142
+ ],
7143
+
7144
+ irregularPairs: [
7145
+ ['person', 'people'],
7146
+ ['man', 'men'],
7147
+ ['child', 'children'],
7148
+ ['sex', 'sexes'],
7149
+ ['move', 'moves'],
7150
+ ['cow', 'kine'],
7151
+ ['zombie', 'zombies']
7152
+ ],
7153
+
7154
+ uncountable: [
7155
+ 'equipment',
7156
+ 'information',
7157
+ 'rice',
7158
+ 'money',
7159
+ 'species',
7160
+ 'series',
7161
+ 'fish',
7162
+ 'sheep',
7163
+ 'jeans',
7164
+ 'police'
7165
+ ]
7166
+ };
7167
+
7168
+ })();
7169
+
7170
+
7171
+
7172
+ (function() {
7173
+ if (Ember.EXTEND_PROTOTYPES) {
7174
+ /**
7175
+ See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
7176
+
7177
+ @method pluralize
7178
+ @for String
7179
+ */
7180
+ String.prototype.pluralize = function() {
7181
+ return Ember.String.pluralize(this);
7182
+ };
7183
+
7184
+ /**
7185
+ See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
7186
+
7187
+ @method singularize
7188
+ @for String
7189
+ */
7190
+ String.prototype.singularize = function() {
7191
+ return Ember.String.singularize(this);
7192
+ };
7193
+ }
7194
+
7195
+ })();
7196
+
7197
+
7198
+
7199
+ (function() {
7200
+ Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
7201
+
7202
+ })();
7203
+
7204
+
7205
+
7206
+ (function() {
7207
+
7208
+ })();
7209
+
7210
+
7211
+ })();