rightnow_oms 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/README.rdoc +83 -0
  2. data/Rakefile +29 -0
  3. data/app/assets/javascripts/rightnow_oms/app/app.js.coffee +8 -0
  4. data/app/assets/javascripts/rightnow_oms/app/controllers/cart.js.coffee +28 -0
  5. data/app/assets/javascripts/rightnow_oms/app/models/cart.js.coffee +53 -0
  6. data/app/assets/javascripts/rightnow_oms/app/models/cart_item.js.coffee +60 -0
  7. data/app/assets/javascripts/rightnow_oms/app/templates/cart_items/show.hjs +9 -0
  8. data/app/assets/javascripts/rightnow_oms/app/templates/cart_items/show_in_detail.hjs +15 -0
  9. data/app/assets/javascripts/rightnow_oms/app/templates/carts/show.hjs +14 -0
  10. data/app/assets/javascripts/rightnow_oms/app/templates/carts/show_in_detail.hjs +21 -0
  11. data/app/assets/javascripts/rightnow_oms/app/views/cart_items/show.js.coffee +7 -0
  12. data/app/assets/javascripts/rightnow_oms/app/views/cart_items/show_in_detail.js.coffee +15 -0
  13. data/app/assets/javascripts/rightnow_oms/app/views/carts/show.js.coffee +4 -0
  14. data/app/assets/javascripts/rightnow_oms/app/views/carts/show_in_detail.js.coffee +5 -0
  15. data/app/assets/javascripts/rightnow_oms/application.js +8 -0
  16. data/app/assets/javascripts/rightnow_oms/lib/ember/data/adapters/my_rest_adapter.js.coffee +183 -0
  17. data/app/assets/javascripts/rightnow_oms/lib/ember/data/extensions.js +19 -0
  18. data/app/assets/javascripts/rightnow_oms/lib/utils.js.coffee +2 -0
  19. data/app/assets/javascripts/rightnow_oms/vendor/ember-data.js +1772 -0
  20. data/app/assets/javascripts/rightnow_oms/vendor/ember-data.min.js +8 -0
  21. data/app/assets/javascripts/rightnow_oms/vendor/ember.js +15833 -0
  22. data/app/assets/javascripts/rightnow_oms/vendor/ember.min.js +13 -0
  23. data/app/assets/stylesheets/rightnow_oms/application.css +7 -0
  24. data/app/assets/stylesheets/rightnow_oms/cart_items.css +12 -0
  25. data/app/assets/stylesheets/rightnow_oms/carts.css.scss +21 -0
  26. data/app/controllers/rightnow_oms/application_controller.rb +17 -0
  27. data/app/controllers/rightnow_oms/cart_items_controller.rb +59 -0
  28. data/app/controllers/rightnow_oms/carts_controller.rb +18 -0
  29. data/app/helpers/rightnow_oms/application_helper.rb +4 -0
  30. data/app/helpers/rightnow_oms/cart_items_helper.rb +4 -0
  31. data/app/helpers/rightnow_oms/carts_helper.rb +4 -0
  32. data/app/models/rightnow_oms/cart.rb +42 -0
  33. data/app/models/rightnow_oms/cart_item.rb +34 -0
  34. data/app/views/rightnow_oms/carts/show.html.haml +8 -0
  35. data/config/routes.rb +4 -0
  36. data/db/migrate/20120202104431_create_rightnow_oms_carts.rb +14 -0
  37. data/db/migrate/20120203045059_create_rightnow_oms_cart_items.rb +18 -0
  38. data/db/migrate/20120207085551_add_group_to_rightnow_oms_cart_items.rb +5 -0
  39. data/db/migrate/20120207095146_add_parent_id_to_rightnow_oms_cart_items.rb +5 -0
  40. data/lib/rightnow_oms.rb +6 -0
  41. data/lib/rightnow_oms/engine.rb +5 -0
  42. data/lib/rightnow_oms/version.rb +3 -0
  43. data/lib/tasks/rightnow_oms_tasks.rake +4 -0
  44. metadata +171 -0
@@ -0,0 +1,19 @@
1
+ DS.Store.reopen({
2
+ loadAll: function(type, data) {
3
+ var array = DS.ModelArray.create({ type: type, content: Em.A([]), store: this });
4
+ this.registerModelArray(array, type);
5
+
6
+ this.loadArray(type, data);
7
+
8
+ return array;
9
+ },
10
+
11
+ loadArray: function(type, array) {
12
+ // TODO: Why is it necessary to build a separate array of ids? Perhaps this logic could be included in loadMany()?
13
+ var ids = [];
14
+ for (var i = 0; i < array.length; i++) {
15
+ ids.push(array[i].id);
16
+ }
17
+ this.loadMany(type, ids, array);
18
+ }
19
+ });
@@ -0,0 +1,2 @@
1
+ window.round = (number, fractionDigits) ->
2
+ return (Math.round(number * Math.pow(10, fractionDigits)) / Math.pow(10, fractionDigits)).toFixed(fractionDigits);
@@ -0,0 +1,1772 @@
1
+
2
+ (function(exports) {
3
+ window.DS = Ember.Namespace.create();
4
+
5
+ })({});
6
+
7
+
8
+ (function(exports) {
9
+ DS.Adapter = Ember.Object.extend({
10
+ commit: function(store, commitDetails) {
11
+ commitDetails.updated.eachType(function(type, array) {
12
+ this.updateRecords(store, type, array.slice());
13
+ }, this);
14
+
15
+ commitDetails.created.eachType(function(type, array) {
16
+ this.createRecords(store, type, array.slice());
17
+ }, this);
18
+
19
+ commitDetails.deleted.eachType(function(type, array) {
20
+ this.deleteRecords(store, type, array.slice());
21
+ }, this);
22
+ },
23
+
24
+ createRecords: function(store, type, models) {
25
+ models.forEach(function(model) {
26
+ this.createRecord(store, type, model);
27
+ }, this);
28
+ },
29
+
30
+ updateRecords: function(store, type, models) {
31
+ models.forEach(function(model) {
32
+ this.updateRecord(store, type, model);
33
+ }, this);
34
+ },
35
+
36
+ deleteRecords: function(store, type, models) {
37
+ models.forEach(function(model) {
38
+ this.deleteRecord(store, type, model);
39
+ }, this);
40
+ },
41
+
42
+ findMany: function(store, type, ids) {
43
+ ids.forEach(function(id) {
44
+ this.find(store, type, id);
45
+ }, this);
46
+ }
47
+ });
48
+ })({});
49
+
50
+
51
+ (function(exports) {
52
+ DS.fixtureAdapter = DS.Adapter.create({
53
+ find: function(store, type, id) {
54
+ var fixtures = type.FIXTURES;
55
+
56
+ ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
57
+ if (fixtures.hasLoaded) { return; }
58
+
59
+ setTimeout(function() {
60
+ store.loadMany(type, fixtures);
61
+ fixtures.hasLoaded = true;
62
+ }, 300);
63
+ },
64
+
65
+ findMany: function() {
66
+ this.find.apply(this, arguments);
67
+ },
68
+
69
+ findAll: function(store, type) {
70
+ var fixtures = type.FIXTURES;
71
+
72
+ ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
73
+
74
+ var ids = fixtures.map(function(item, index, self){ return item.id });
75
+ store.loadMany(type, ids, fixtures);
76
+ }
77
+
78
+ });
79
+
80
+ })({});
81
+
82
+
83
+ (function(exports) {
84
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
85
+
86
+ DS.RESTAdapter = DS.Adapter.extend({
87
+ createRecord: function(store, type, model) {
88
+ var root = this.rootForType(type);
89
+
90
+ var data = {};
91
+ data[root] = get(model, 'data');
92
+
93
+ this.ajax("/" + this.pluralize(root), "POST", {
94
+ data: data,
95
+ success: function(json) {
96
+ store.didCreateRecord(model, json[root]);
97
+ }
98
+ });
99
+ },
100
+
101
+ createRecords: function(store, type, models) {
102
+ if (get(this, 'bulkCommit') === false) {
103
+ return this._super(store, type, models);
104
+ }
105
+
106
+ var root = this.rootForType(type),
107
+ plural = this.pluralize(root);
108
+
109
+ var data = {};
110
+ data[plural] = models.map(function(model) {
111
+ return get(model, 'data');
112
+ });
113
+
114
+ this.ajax("/" + this.pluralize(root), "POST", {
115
+ data: data,
116
+ success: function(json) {
117
+ store.didCreateRecords(type, models, json[plural]);
118
+ }
119
+ });
120
+ },
121
+
122
+ updateRecord: function(store, type, model) {
123
+ var primaryKey = getPath(type, 'proto.primaryKey'),
124
+ id = get(model, primaryKey);
125
+ var root = this.rootForType(type);
126
+
127
+ var data = {};
128
+ data[root] = get(model, 'data');
129
+
130
+ var url = ["", this.pluralize(root), id].join("/");
131
+
132
+ this.ajax(url, "PUT", {
133
+ data: data,
134
+ success: function(json) {
135
+ store.didUpdateRecord(model, json[root]);
136
+ }
137
+ });
138
+ },
139
+
140
+ updateRecords: function(store, type, models) {
141
+ if (get(this, 'bulkCommit') === false) {
142
+ return this._super(store, type, models);
143
+ }
144
+
145
+ var root = this.rootForType(type),
146
+ plural = this.pluralize(root);
147
+
148
+ var data = {};
149
+ data[plural] = models.map(function(model) {
150
+ return get(model, 'data');
151
+ });
152
+
153
+ this.ajax("/" + this.pluralize(root), "POST", {
154
+ data: data,
155
+ success: function(json) {
156
+ store.didUpdateRecords(models, json[plural]);
157
+ }
158
+ });
159
+ },
160
+
161
+ deleteRecord: function(store, type, model) {
162
+ var primaryKey = getPath(type, 'proto.primaryKey'),
163
+ id = get(model, primaryKey);
164
+ var root = this.rootForType(type);
165
+
166
+ var url = ["", this.pluralize(root), id].join("/");
167
+
168
+ this.ajax(url, "DELETE", {
169
+ success: function(json) {
170
+ store.didDeleteRecord(model);
171
+ }
172
+ });
173
+ },
174
+
175
+ deleteRecords: function(store, type, models) {
176
+ if (get(this, 'bulkCommit') === false) {
177
+ return this._super(store, type, models);
178
+ }
179
+
180
+ var root = this.rootForType(type),
181
+ plural = this.pluralize(root),
182
+ primaryKey = getPath(type, 'proto.primaryKey');
183
+
184
+ var data = {};
185
+ data[plural] = models.map(function(model) {
186
+ return get(model, primaryKey);
187
+ });
188
+
189
+ this.ajax("/" + this.pluralize(root) + "/delete", "POST", {
190
+ data: data,
191
+ success: function(json) {
192
+ store.didDeleteRecords(models);
193
+ }
194
+ });
195
+ },
196
+
197
+ find: function(store, type, id) {
198
+ var root = this.rootForType(type);
199
+
200
+ var url = ["", this.pluralize(root), id].join("/");
201
+
202
+ this.ajax(url, "GET", {
203
+ success: function(json) {
204
+ store.load(type, json[root]);
205
+ }
206
+ });
207
+ },
208
+
209
+ findMany: function(store, type, ids) {
210
+ var root = this.rootForType(type), plural = this.pluralize(root);
211
+
212
+ this.ajax("/" + plural, "GET", {
213
+ data: { ids: ids },
214
+ success: function(json) {
215
+ store.loadMany(type, ids, json[plural]);
216
+ }
217
+ });
218
+ var url = "/" + plural;
219
+ },
220
+
221
+ findAll: function(store, type) {
222
+ var root = this.rootForType(type), plural = this.pluralize(root);
223
+
224
+ this.ajax("/" + plural, "GET", {
225
+ success: function(json) {
226
+ store.loadMany(type, json[plural]);
227
+ }
228
+ });
229
+ },
230
+
231
+ findQuery: function(store, type, query, modelArray) {
232
+ var root = this.rootForType(type), plural = this.pluralize(root);
233
+
234
+ this.ajax("/" + plural, "GET", {
235
+ data: query,
236
+ success: function(json) {
237
+ modelArray.load(json[plural]);
238
+ }
239
+ });
240
+ },
241
+
242
+ // HELPERS
243
+
244
+ plurals: {},
245
+
246
+ // define a plurals hash in your subclass to define
247
+ // special-case pluralization
248
+ pluralize: function(name) {
249
+ return this.plurals[name] || name + "s";
250
+ },
251
+
252
+ rootForType: function(type) {
253
+ if (type.url) { return type.url; }
254
+
255
+ // use the last part of the name as the URL
256
+ var parts = type.toString().split(".");
257
+ var name = parts[parts.length - 1];
258
+ return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
259
+ },
260
+
261
+ ajax: function(url, type, hash) {
262
+ hash.url = url;
263
+ hash.type = type;
264
+ hash.dataType = "json";
265
+
266
+ jQuery.ajax(hash);
267
+ }
268
+ });
269
+
270
+
271
+ })({});
272
+
273
+
274
+ (function(exports) {
275
+ var get = Ember.get, set = Ember.set;
276
+
277
+ DS.ModelArray = Ember.ArrayProxy.extend({
278
+ type: null,
279
+ content: null,
280
+ store: null,
281
+
282
+ init: function() {
283
+ set(this, 'modelCache', Ember.A([]));
284
+ this._super();
285
+ },
286
+
287
+ arrayDidChange: function(array, index, removed, added) {
288
+ var modelCache = get(this, 'modelCache');
289
+ modelCache.replace(index, 0, Array(added));
290
+
291
+ this._super(array, index, removed, added);
292
+ },
293
+
294
+ arrayWillChange: function(array, index, removed, added) {
295
+ this._super(array, index, removed, added);
296
+
297
+ var modelCache = get(this, 'modelCache');
298
+ modelCache.replace(index, removed);
299
+ },
300
+
301
+ objectAtContent: function(index) {
302
+ var modelCache = get(this, 'modelCache');
303
+ var model = modelCache.objectAt(index);
304
+
305
+ if (!model) {
306
+ var store = get(this, 'store');
307
+ var content = get(this, 'content');
308
+
309
+ var contentObject = content.objectAt(index);
310
+
311
+ if (contentObject !== undefined) {
312
+ model = store.findByClientId(get(this, 'type'), contentObject);
313
+ modelCache.replace(index, 1, [model]);
314
+ }
315
+ }
316
+
317
+ return model;
318
+ }
319
+ });
320
+
321
+ DS.FilteredModelArray = DS.ModelArray.extend({
322
+ filterFunction: null,
323
+
324
+ updateFilter: Ember.observer(function() {
325
+ var store = get(this, 'store');
326
+ store.updateModelArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
327
+ }, 'filterFunction')
328
+ });
329
+
330
+ DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
331
+ query: null,
332
+ isLoaded: false,
333
+
334
+ load: function(array) {
335
+ var store = get(this, 'store'), type = get(this, 'type');
336
+
337
+ var clientIds = store.loadMany(type, array).clientIds;
338
+
339
+ this.beginPropertyChanges();
340
+ set(this, 'content', Ember.A(clientIds));
341
+ set(this, 'isLoaded', true);
342
+ this.endPropertyChanges();
343
+ }
344
+ });
345
+
346
+ })({});
347
+
348
+
349
+ (function(exports) {
350
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
351
+
352
+ var OrderedSet = Ember.Object.extend({
353
+ init: function() {
354
+ this.clear();
355
+ },
356
+
357
+ clear: function() {
358
+ this.set('presenceSet', {});
359
+ this.set('list', Ember.NativeArray.apply([]));
360
+ },
361
+
362
+ add: function(obj) {
363
+ var guid = Ember.guidFor(obj),
364
+ presenceSet = get(this, 'presenceSet'),
365
+ list = get(this, 'list');
366
+
367
+ if (guid in presenceSet) { return; }
368
+
369
+ presenceSet[guid] = true;
370
+ list.pushObject(obj);
371
+ },
372
+
373
+ remove: function(obj) {
374
+ var guid = Ember.guidFor(obj),
375
+ presenceSet = get(this, 'presenceSet'),
376
+ list = get(this, 'list');
377
+
378
+ delete presenceSet[guid];
379
+ list.removeObject(obj);
380
+ },
381
+
382
+ isEmpty: function() {
383
+ return getPath(this, 'list.length') === 0;
384
+ },
385
+
386
+ forEach: function(fn, self) {
387
+ get(this, 'list').forEach(function(item) {
388
+ fn.call(self, item);
389
+ });
390
+ },
391
+
392
+ toArray: function() {
393
+ return get(this, 'list').slice();
394
+ }
395
+ });
396
+
397
+ /**
398
+ A Hash stores values indexed by keys. Unlike JavaScript's
399
+ default Objects, the keys of a Hash can be any JavaScript
400
+ object.
401
+
402
+ Internally, a Hash has two data structures:
403
+
404
+ `keys`: an OrderedSet of all of the existing keys
405
+ `values`: a JavaScript Object indexed by the
406
+ Ember.guidFor(key)
407
+
408
+ When a key/value pair is added for the first time, we
409
+ add the key to the `keys` OrderedSet, and create or
410
+ replace an entry in `values`. When an entry is deleted,
411
+ we delete its entry in `keys` and `values`.
412
+ */
413
+
414
+ var Hash = Ember.Object.extend({
415
+ init: function() {
416
+ set(this, 'keys', OrderedSet.create());
417
+ set(this, 'values', {});
418
+ },
419
+
420
+ add: function(key, value) {
421
+ var keys = get(this, 'keys'), values = get(this, 'values');
422
+ var guid = Ember.guidFor(key);
423
+
424
+ keys.add(key);
425
+ values[guid] = value;
426
+
427
+ return value;
428
+ },
429
+
430
+ remove: function(key) {
431
+ var keys = get(this, 'keys'), values = get(this, 'values');
432
+ var guid = Ember.guidFor(key), value;
433
+
434
+ keys.remove(key);
435
+
436
+ value = values[guid];
437
+ delete values[guid];
438
+
439
+ return value;
440
+ },
441
+
442
+ fetch: function(key) {
443
+ var values = get(this, 'values');
444
+ var guid = Ember.guidFor(key);
445
+
446
+ return values[guid];
447
+ },
448
+
449
+ forEach: function(fn, binding) {
450
+ var keys = get(this, 'keys'), values = get(this, 'values');
451
+
452
+ keys.forEach(function(key) {
453
+ var guid = Ember.guidFor(key);
454
+ fn.call(binding, key, values[guid]);
455
+ });
456
+ }
457
+ });
458
+
459
+ DS.Transaction = Ember.Object.extend({
460
+ init: function() {
461
+ set(this, 'dirty', {
462
+ created: Hash.create(),
463
+ updated: Hash.create(),
464
+ deleted: Hash.create()
465
+ });
466
+ },
467
+
468
+ createRecord: function(type, hash) {
469
+ var store = get(this, 'store');
470
+
471
+ return store.createRecord(type, hash, this);
472
+ },
473
+
474
+ add: function(model) {
475
+ var modelTransaction = get(model, 'transaction');
476
+ ember_assert("Models cannot belong to more than one transaction at a time.", !modelTransaction);
477
+
478
+ set(model, 'transaction', this);
479
+ },
480
+
481
+ modelBecameDirty: function(kind, model) {
482
+ var dirty = get(get(this, 'dirty'), kind),
483
+ type = model.constructor;
484
+
485
+ var models = dirty.fetch(type);
486
+
487
+ models = models || dirty.add(type, OrderedSet.create());
488
+ models.add(model);
489
+ },
490
+
491
+ modelBecameClean: function(kind, model) {
492
+ var dirty = get(get(this, 'dirty'), kind),
493
+ type = model.constructor;
494
+
495
+ var models = dirty.fetch(type);
496
+ models.remove(model);
497
+
498
+ set(model, 'transaction', null);
499
+ },
500
+
501
+ commit: function() {
502
+ var dirtyMap = get(this, 'dirty');
503
+
504
+ var iterate = function(kind, fn, binding) {
505
+ var dirty = get(dirtyMap, kind);
506
+
507
+ dirty.forEach(function(type, models) {
508
+ if (models.isEmpty()) { return; }
509
+
510
+ models.forEach(function(model) { model.willCommit(); });
511
+ fn.call(binding, type, models.toArray());
512
+ });
513
+ };
514
+
515
+ var commitDetails = {
516
+ updated: {
517
+ eachType: function(fn, binding) { iterate('updated', fn, binding); }
518
+ },
519
+
520
+ created: {
521
+ eachType: function(fn, binding) { iterate('created', fn, binding); }
522
+ },
523
+
524
+ deleted: {
525
+ eachType: function(fn, binding) { iterate('deleted', fn, binding); }
526
+ }
527
+ };
528
+
529
+ var store = get(this, 'store');
530
+ var adapter = get(store, '_adapter');
531
+ if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
532
+ else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
533
+ }
534
+ });
535
+
536
+ })({});
537
+
538
+
539
+ (function(exports) {
540
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
541
+
542
+ var OrderedSet = Ember.Object.extend({
543
+ init: function() {
544
+ this.clear();
545
+ },
546
+
547
+ clear: function() {
548
+ this.set('presenceSet', {});
549
+ this.set('list', Ember.NativeArray.apply([]));
550
+ },
551
+
552
+ add: function(obj) {
553
+ var guid = Ember.guidFor(obj),
554
+ presenceSet = get(this, 'presenceSet'),
555
+ list = get(this, 'list');
556
+
557
+ if (guid in presenceSet) { return; }
558
+
559
+ presenceSet[guid] = true;
560
+ list.pushObject(obj);
561
+ },
562
+
563
+ remove: function(obj) {
564
+ var guid = Ember.guidFor(obj),
565
+ presenceSet = get(this, 'presenceSet'),
566
+ list = get(this, 'list');
567
+
568
+ delete presenceSet[guid];
569
+ list.removeObject(obj);
570
+ },
571
+
572
+ isEmpty: function() {
573
+ return getPath(this, 'list.length') === 0;
574
+ },
575
+
576
+ forEach: function(fn, self) {
577
+ get(this, 'list').forEach(function(item) {
578
+ fn.call(self, item);
579
+ });
580
+ }
581
+ });
582
+
583
+ // Implementors Note:
584
+ //
585
+ // The variables in this file are consistently named according to the following
586
+ // scheme:
587
+ //
588
+ // * +id+ means an identifier managed by an external source, provided inside the
589
+ // data hash provided by that source.
590
+ // * +clientId+ means a transient numerical identifier generated at runtime by
591
+ // the data store. It is important primarily because newly created objects may
592
+ // not yet have an externally generated id.
593
+ // * +type+ means a subclass of DS.Model.
594
+
595
+ /**
596
+ The store contains all of the hashes for data models loaded from the server.
597
+ It is also responsible for creating instances of DS.Model when you request one
598
+ of these data hashes, so that they can be bound to in your Handlebars templates.
599
+
600
+ Create a new store like this:
601
+
602
+ MyApp.store = DS.Store.create();
603
+
604
+ You can retrieve DS.Model instances from the store in several ways. To retrieve
605
+ a model for a specific id, use the `find()` method:
606
+
607
+ var model = MyApp.store.find(MyApp.Contact, 123);
608
+
609
+ By default, the store will talk to your backend using a standard REST mechanism.
610
+ You can customize how the store talks to your backend by specifying a custom adapter:
611
+
612
+ MyApp.store = DS.Store.create({
613
+ adapter: 'MyApp.CustomAdapter'
614
+ });
615
+
616
+ You can learn more about writing a custom adapter by reading the `DS.Adapter`
617
+ documentation.
618
+ */
619
+ DS.Store = Ember.Object.extend({
620
+
621
+ /**
622
+ Many methods can be invoked without specifying which store should be used.
623
+ In those cases, the first store created will be used as the default. If
624
+ an application has multiple stores, it should specify which store to use
625
+ when performing actions, such as finding records by id.
626
+
627
+ The init method registers this store as the default if none is specified.
628
+ */
629
+ init: function() {
630
+ if (!get(DS, 'defaultStore') || get(this, 'isDefaultStore')) {
631
+ set(DS, 'defaultStore', this);
632
+ }
633
+
634
+ set(this, 'data', []);
635
+ set(this, '_typeMap', {});
636
+ set(this, 'models', []);
637
+ set(this, 'modelArrays', []);
638
+ set(this, 'modelArraysByClientId', {});
639
+ set(this, 'defaultTransaction', DS.Transaction.create({ store: this }));
640
+
641
+ return this._super();
642
+ },
643
+
644
+ transaction: function() {
645
+ return DS.Transaction.create({ store: this });
646
+ },
647
+
648
+ modelArraysForClientId: function(clientId) {
649
+ var modelArrays = get(this, 'modelArraysByClientId');
650
+ var ret = modelArrays[clientId];
651
+
652
+ if (!ret) {
653
+ ret = modelArrays[clientId] = OrderedSet.create();
654
+ }
655
+
656
+ return ret;
657
+ },
658
+
659
+ /**
660
+ The adapter to use to communicate to a backend server or other persistence layer.
661
+
662
+ This can be specified as an instance, a class, or a property path that specifies
663
+ where the adapter can be located.
664
+
665
+ @property {DS.Adapter|String}
666
+ */
667
+ adapter: null,
668
+
669
+ _adapter: Ember.computed(function() {
670
+ var adapter = get(this, 'adapter');
671
+ if (typeof adapter === 'string') {
672
+ return getPath(this, adapter, false) || getPath(window, adapter);
673
+ }
674
+ return adapter;
675
+ }).property('adapter').cacheable(),
676
+
677
+ clientIdCounter: -1,
678
+
679
+ // ....................
680
+ // . CREATE NEW MODEL .
681
+ // ....................
682
+
683
+ createRecord: function(type, hash, transaction) {
684
+ hash = hash || {};
685
+
686
+ var id = hash[getPath(type, 'proto.primaryKey')] || null;
687
+
688
+ var model = type.create({
689
+ data: hash || {},
690
+ store: this,
691
+ transaction: transaction
692
+ });
693
+
694
+ model.adapterDidCreate();
695
+
696
+ var data = this.clientIdToHashMap(type);
697
+ var models = get(this, 'models');
698
+
699
+ var clientId = this.pushHash(hash, id, type);
700
+
701
+ set(model, 'clientId', clientId);
702
+
703
+ models[clientId] = model;
704
+
705
+ this.updateModelArrays(type, clientId, hash);
706
+
707
+ return model;
708
+ },
709
+
710
+ // ................
711
+ // . DELETE MODEL .
712
+ // ................
713
+
714
+ deleteRecord: function(model) {
715
+ model.deleteRecord();
716
+ },
717
+
718
+ // ...............
719
+ // . FIND MODELS .
720
+ // ...............
721
+
722
+ /**
723
+ Finds a model by its id. If the data for that model has already been
724
+ loaded, an instance of DS.Model with that data will be returned
725
+ immediately. Otherwise, an empty DS.Model instance will be returned in
726
+ the loading state. As soon as the requested data is available, the model
727
+ will be moved into the loaded state and all of the information will be
728
+ available.
729
+
730
+ Note that only one DS.Model instance is ever created per unique id for a
731
+ given type.
732
+
733
+ Example:
734
+
735
+ var record = MyApp.store.find(MyApp.Person, 1234);
736
+
737
+ @param {DS.Model} type
738
+ @param {String|Number} id
739
+ */
740
+ find: function(type, id, query) {
741
+ if (id === undefined) {
742
+ return this.findMany(type, null, null);
743
+ }
744
+
745
+ if (query !== undefined) {
746
+ return this.findMany(type, id, query);
747
+ } else if (Ember.typeOf(id) === 'object') {
748
+ return this.findQuery(type, id);
749
+ }
750
+
751
+ if (Ember.isArray(id)) {
752
+ return this.findMany(type, id);
753
+ }
754
+
755
+ var clientId = this.clientIdForId(type, id);
756
+
757
+ return this.findByClientId(type, clientId, id);
758
+ },
759
+
760
+ findByClientId: function(type, clientId, id) {
761
+ var model;
762
+
763
+ var models = get(this, 'models');
764
+ var data = this.clientIdToHashMap(type);
765
+
766
+ // If there is already a clientId assigned for this
767
+ // type/id combination, try to find an existing
768
+ // model for that id and return. Otherwise,
769
+ // materialize a new model and set its data to the
770
+ // value we already have.
771
+ if (clientId !== undefined) {
772
+ model = models[clientId];
773
+
774
+ if (!model) {
775
+ // create a new instance of the model in the
776
+ // 'isLoading' state
777
+ model = this.createModel(type, clientId);
778
+
779
+ // immediately set its data
780
+ model.setData(data[clientId] || null);
781
+ }
782
+ } else {
783
+ clientId = this.pushHash(null, id, type);
784
+
785
+ // create a new instance of the model in the
786
+ // 'isLoading' state
787
+ model = this.createModel(type, clientId);
788
+
789
+ // let the adapter set the data, possibly async
790
+ var adapter = get(this, '_adapter');
791
+ if (adapter && adapter.find) { adapter.find(this, type, id); }
792
+ else { throw fmt("Adapter is either null or does not implement `find` method", this); }
793
+ }
794
+
795
+ return model;
796
+ },
797
+
798
+ /** @private
799
+ */
800
+ findMany: function(type, ids, query) {
801
+ var idToClientIdMap = this.idToClientIdMap(type);
802
+ var data = this.clientIdToHashMap(type), needed;
803
+
804
+ var clientIds = Ember.A([]);
805
+
806
+ if (ids) {
807
+ needed = [];
808
+
809
+ ids.forEach(function(id) {
810
+ var clientId = idToClientIdMap[id];
811
+ if (clientId === undefined || data[clientId] === undefined) {
812
+ clientId = this.pushHash(null, id, type);
813
+ needed.push(id);
814
+ }
815
+
816
+ clientIds.push(clientId);
817
+ }, this);
818
+ } else {
819
+ needed = null;
820
+ }
821
+
822
+ if ((needed && get(needed, 'length') > 0) || query) {
823
+ var adapter = get(this, '_adapter');
824
+ if (adapter && adapter.findMany) { adapter.findMany(this, type, needed, query); }
825
+ else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }
826
+ }
827
+
828
+ return this.createModelArray(type, clientIds);
829
+ },
830
+
831
+ findQuery: function(type, query) {
832
+ var array = DS.AdapterPopulatedModelArray.create({ type: type, content: Ember.A([]), store: this });
833
+ var adapter = get(this, '_adapter');
834
+ if (adapter && adapter.findQuery) { adapter.findQuery(this, type, query, array); }
835
+ else { throw fmt("Adapter is either null or does not implement `findQuery` method", this); }
836
+ return array;
837
+ },
838
+
839
+ findAll: function(type) {
840
+
841
+ var typeMap = this.typeMapFor(type),
842
+ findAllCache = typeMap.findAllCache;
843
+
844
+ if (findAllCache) { return findAllCache; }
845
+
846
+ var array = DS.ModelArray.create({ type: type, content: Ember.A([]), store: this });
847
+ this.registerModelArray(array, type);
848
+
849
+ var adapter = get(this, '_adapter');
850
+ if (adapter && adapter.findAll) { adapter.findAll(this, type); }
851
+
852
+ typeMap.findAllCache = array;
853
+ return array;
854
+ },
855
+
856
+ filter: function(type, filter) {
857
+ var array = DS.FilteredModelArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
858
+
859
+ this.registerModelArray(array, type, filter);
860
+
861
+ return array;
862
+ },
863
+
864
+ // ............
865
+ // . UPDATING .
866
+ // ............
867
+
868
+ hashWasUpdated: function(type, clientId) {
869
+ var clientIdToHashMap = this.clientIdToHashMap(type);
870
+ var hash = clientIdToHashMap[clientId];
871
+
872
+ this.updateModelArrays(type, clientId, hash);
873
+ },
874
+
875
+ // ..............
876
+ // . PERSISTING .
877
+ // ..............
878
+
879
+ commit: function() {
880
+ get(this, 'defaultTransaction').commit();
881
+ },
882
+
883
+ didUpdateRecords: function(array, hashes) {
884
+ if (arguments.length === 2) {
885
+ array.forEach(function(model, idx) {
886
+ this.didUpdateRecord(model, hashes[idx]);
887
+ }, this);
888
+ } else {
889
+ array.forEach(function(model) {
890
+ this.didUpdateRecord(model);
891
+ }, this);
892
+ }
893
+ },
894
+
895
+ didUpdateRecord: function(model, hash) {
896
+ if (arguments.length === 2) {
897
+ var clientId = get(model, 'clientId');
898
+ var data = this.clientIdToHashMap(model.constructor);
899
+
900
+ data[clientId] = hash;
901
+ model.set('data', hash);
902
+ }
903
+
904
+ model.adapterDidUpdate();
905
+ },
906
+
907
+ didDeleteRecords: function(array) {
908
+ array.forEach(function(model) {
909
+ model.adapterDidDelete();
910
+ });
911
+ },
912
+
913
+ didDeleteRecord: function(model) {
914
+ model.adapterDidDelete();
915
+ },
916
+
917
+ didCreateRecords: function(type, array, hashes) {
918
+ var id, clientId, primaryKey = getPath(type, 'proto.primaryKey');
919
+
920
+ var idToClientIdMap = this.idToClientIdMap(type);
921
+ var data = this.clientIdToHashMap(type);
922
+ var idList = this.idList(type);
923
+
924
+ for (var i=0, l=get(array, 'length'); i<l; i++) {
925
+ var model = array[i], hash = hashes[i];
926
+ id = hash[primaryKey];
927
+ clientId = get(model, 'clientId');
928
+
929
+ data[clientId] = hash;
930
+ set(model, 'data', hash);
931
+
932
+ idToClientIdMap[id] = clientId;
933
+ idList.push(id);
934
+
935
+ model.adapterDidUpdate();
936
+ }
937
+ },
938
+
939
+ didCreateRecord: function(model, hash) {
940
+ var type = model.constructor;
941
+
942
+ var id, clientId, primaryKey = getPath(type, 'proto.primaryKey');
943
+
944
+ var idToClientIdMap = this.idToClientIdMap(type);
945
+ var data = this.clientIdToHashMap(type);
946
+ var idList = this.idList(type);
947
+
948
+ id = hash[primaryKey];
949
+
950
+ clientId = get(model, 'clientId');
951
+ data[clientId] = hash;
952
+ set(model, 'data', hash);
953
+
954
+ idToClientIdMap[id] = clientId;
955
+ idList.push(id);
956
+
957
+ model.adapterDidUpdate();
958
+ },
959
+
960
+ recordWasInvalid: function(record, errors) {
961
+ record.wasInvalid(errors);
962
+ },
963
+
964
+ // ................
965
+ // . MODEL ARRAYS .
966
+ // ................
967
+
968
+ registerModelArray: function(array, type, filter) {
969
+ var modelArrays = get(this, 'modelArrays');
970
+ var idToClientIdMap = this.idToClientIdMap(type);
971
+
972
+ modelArrays.push(array);
973
+
974
+ this.updateModelArrayFilter(array, type, filter);
975
+ },
976
+
977
+ createModelArray: function(type, clientIds) {
978
+ var array = DS.ModelArray.create({ type: type, content: clientIds, store: this });
979
+
980
+ clientIds.forEach(function(clientId) {
981
+ var modelArrays = this.modelArraysForClientId(clientId);
982
+ modelArrays.add(array);
983
+ }, this);
984
+
985
+ return array;
986
+ },
987
+
988
+ updateModelArrayFilter: function(array, type, filter) {
989
+ var data = this.clientIdToHashMap(type);
990
+ var allClientIds = this.clientIdList(type);
991
+
992
+ for (var i=0, l=allClientIds.length; i<l; i++) {
993
+ clientId = allClientIds[i];
994
+
995
+ hash = data[clientId];
996
+
997
+ if (hash) {
998
+ this.updateModelArray(array, filter, type, clientId, hash);
999
+ }
1000
+ }
1001
+ },
1002
+
1003
+ updateModelArrays: function(type, clientId, hash) {
1004
+ var modelArrays = get(this, 'modelArrays');
1005
+
1006
+ modelArrays.forEach(function(array) {
1007
+ modelArrayType = get(array, 'type');
1008
+ filter = get(array, 'filterFunction');
1009
+
1010
+ if (type !== modelArrayType) { return; }
1011
+
1012
+ this.updateModelArray(array, filter, type, clientId, hash);
1013
+ }, this);
1014
+ },
1015
+
1016
+ updateModelArray: function(array, filter, type, clientId, hash) {
1017
+ var shouldBeInArray;
1018
+
1019
+ if (!filter) {
1020
+ shouldBeInArray = true;
1021
+ } else {
1022
+ shouldBeInArray = filter(hash);
1023
+ }
1024
+
1025
+ var content = get(array, 'content');
1026
+ var alreadyInArray = content.indexOf(clientId) !== -1;
1027
+
1028
+ var modelArrays = this.modelArraysForClientId(clientId);
1029
+
1030
+ if (shouldBeInArray && !alreadyInArray) {
1031
+ modelArrays.add(array);
1032
+ content.pushObject(clientId);
1033
+ } else if (!shouldBeInArray && alreadyInArray) {
1034
+ modelArrays.remove(array);
1035
+ content.removeObject(clientId);
1036
+ }
1037
+ },
1038
+
1039
+ removeFromModelArrays: function(model) {
1040
+ var clientId = get(model, 'clientId');
1041
+ var modelArrays = this.modelArraysForClientId(clientId);
1042
+
1043
+ modelArrays.forEach(function(array) {
1044
+ var content = get(array, 'content');
1045
+ content.removeObject(clientId);
1046
+ });
1047
+ },
1048
+
1049
+ // ............
1050
+ // . TYPE MAP .
1051
+ // ............
1052
+
1053
+ typeMapFor: function(type) {
1054
+ var ids = get(this, '_typeMap');
1055
+ var guidForType = Ember.guidFor(type);
1056
+
1057
+ var typeMap = ids[guidForType];
1058
+
1059
+ if (typeMap) {
1060
+ return typeMap;
1061
+ } else {
1062
+ return (ids[guidForType] =
1063
+ {
1064
+ idToCid: {},
1065
+ idList: [],
1066
+ cidList: [],
1067
+ cidToHash: {}
1068
+ });
1069
+ }
1070
+ },
1071
+
1072
+ idToClientIdMap: function(type) {
1073
+ return this.typeMapFor(type).idToCid;
1074
+ },
1075
+
1076
+ idList: function(type) {
1077
+ return this.typeMapFor(type).idList;
1078
+ },
1079
+
1080
+ clientIdList: function(type) {
1081
+ return this.typeMapFor(type).cidList;
1082
+ },
1083
+
1084
+ clientIdToHashMap: function(type) {
1085
+ return this.typeMapFor(type).cidToHash;
1086
+ },
1087
+
1088
+ /** @private
1089
+
1090
+ For a given type and id combination, returns the client id used by the store.
1091
+ If no client id has been assigned yet, `undefined` is returned.
1092
+
1093
+ @param {DS.Model} type
1094
+ @param {String|Number} id
1095
+ */
1096
+ clientIdForId: function(type, id) {
1097
+ return this.typeMapFor(type).idToCid[id];
1098
+ },
1099
+
1100
+ idForHash: function(type, hash) {
1101
+ var primaryKey = getPath(type, 'proto.primaryKey');
1102
+
1103
+ ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
1104
+ return hash[primaryKey];
1105
+ },
1106
+
1107
+ // ................
1108
+ // . LOADING DATA .
1109
+ // ................
1110
+
1111
+ /**
1112
+ Load a new data hash into the store for a given id and type combination.
1113
+ If data for that model had been loaded previously, the new information
1114
+ overwrites the old.
1115
+
1116
+ If the model you are loading data for has outstanding changes that have not
1117
+ yet been saved, an exception will be thrown.
1118
+
1119
+ @param {DS.Model} type
1120
+ @param {String|Number} id
1121
+ @param {Object} hash the data hash to load
1122
+ */
1123
+ load: function(type, id, hash) {
1124
+ if (hash === undefined) {
1125
+ hash = id;
1126
+ var primaryKey = getPath(type, 'proto.primaryKey');
1127
+ ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
1128
+ id = hash[primaryKey];
1129
+ }
1130
+
1131
+ var data = this.clientIdToHashMap(type);
1132
+ var models = get(this, 'models');
1133
+
1134
+ var clientId = this.clientIdForId(type, id);
1135
+
1136
+ if (clientId !== undefined) {
1137
+ data[clientId] = hash;
1138
+
1139
+ var model = models[clientId];
1140
+ if (model) {
1141
+ model.willLoadData();
1142
+ model.setData(hash);
1143
+ }
1144
+ } else {
1145
+ clientId = this.pushHash(hash, id, type);
1146
+ }
1147
+
1148
+ this.updateModelArrays(type, clientId, hash);
1149
+
1150
+ return { id: id, clientId: clientId };
1151
+ },
1152
+
1153
+ loadMany: function(type, ids, hashes) {
1154
+ var clientIds = Ember.A([]);
1155
+
1156
+ if (hashes === undefined) {
1157
+ hashes = ids;
1158
+ ids = [];
1159
+ var primaryKey = getPath(type, 'proto.primaryKey');
1160
+
1161
+ ids = hashes.map(function(hash) {
1162
+ ember_assert("A data hash was loaded for a model of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", !!hash[primaryKey]);
1163
+ return hash[primaryKey];
1164
+ });
1165
+ }
1166
+
1167
+ for (var i=0, l=get(ids, 'length'); i<l; i++) {
1168
+ var loaded = this.load(type, ids[i], hashes[i]);
1169
+ clientIds.pushObject(loaded.clientId);
1170
+ }
1171
+
1172
+ return { clientIds: clientIds, ids: ids };
1173
+ },
1174
+
1175
+ /** @private
1176
+
1177
+ Stores a data hash for the specified type and id combination and returns
1178
+ the client id.
1179
+
1180
+ @param {Object} hash
1181
+ @param {String|Number} id
1182
+ @param {DS.Model} type
1183
+ @returns {Number}
1184
+ */
1185
+ pushHash: function(hash, id, type) {
1186
+ var idToClientIdMap = this.idToClientIdMap(type);
1187
+ var clientIdList = this.clientIdList(type);
1188
+ var idList = this.idList(type);
1189
+ var data = this.clientIdToHashMap(type);
1190
+
1191
+ var clientId = this.incrementProperty('clientIdCounter');
1192
+
1193
+ data[clientId] = hash;
1194
+
1195
+ // if we're creating an item, this process will be done
1196
+ // later, once the object has been persisted.
1197
+ if (id) {
1198
+ idToClientIdMap[id] = clientId;
1199
+ idList.push(id);
1200
+ }
1201
+
1202
+ clientIdList.push(clientId);
1203
+
1204
+ return clientId;
1205
+ },
1206
+
1207
+ // .........................
1208
+ // . MODEL MATERIALIZATION .
1209
+ // .........................
1210
+
1211
+ createModel: function(type, clientId) {
1212
+ var model;
1213
+
1214
+ get(this, 'models')[clientId] = model = type.create({ store: this, clientId: clientId });
1215
+ set(model, 'clientId', clientId);
1216
+ model.loadingData();
1217
+ return model;
1218
+ }
1219
+ });
1220
+
1221
+
1222
+ })({});
1223
+
1224
+
1225
+ (function(exports) {
1226
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
1227
+
1228
+ var stateProperty = Ember.computed(function(key) {
1229
+ var parent = get(this, 'parentState');
1230
+ if (parent) {
1231
+ return get(parent, key);
1232
+ }
1233
+ }).property();
1234
+
1235
+ DS.State = Ember.State.extend({
1236
+ isLoaded: stateProperty,
1237
+ isDirty: stateProperty,
1238
+ isSaving: stateProperty,
1239
+ isDeleted: stateProperty,
1240
+ isError: stateProperty,
1241
+ isNew: stateProperty,
1242
+ isValid: stateProperty
1243
+ });
1244
+
1245
+ var cantLoadData = function() {
1246
+ // TODO: get the current state name
1247
+ throw "You cannot load data into the store when its associated model is in its current state";
1248
+ };
1249
+
1250
+ var isEmptyObject = function(obj) {
1251
+ for (var prop in obj) {
1252
+ if (!obj.hasOwnProperty(prop)) { continue; }
1253
+ return false;
1254
+ }
1255
+
1256
+ return true;
1257
+ };
1258
+
1259
+ var setProperty = function(manager, context) {
1260
+ var key = context.key, value = context.value;
1261
+
1262
+ var model = get(manager, 'model'), type = model.constructor;
1263
+ var store = get(model, 'store');
1264
+ var data = get(model, 'data');
1265
+
1266
+ data[key] = value;
1267
+
1268
+ if (store) { store.hashWasUpdated(type, get(model, 'clientId')); }
1269
+ };
1270
+
1271
+ // several states share extremely common functionality, so we are factoring
1272
+ // them out into a common class.
1273
+ var DirtyState = DS.State.extend({
1274
+ // these states are virtually identical except that
1275
+ // they (thrice) use their states name explicitly.
1276
+ //
1277
+ // child classes implement stateName.
1278
+ stateName: null,
1279
+ isDirty: true,
1280
+ willLoadData: cantLoadData,
1281
+
1282
+ enter: function(manager) {
1283
+ var stateName = get(this, 'stateName'),
1284
+ model = get(manager, 'model');
1285
+
1286
+ model.withTransaction(function (t) {
1287
+ t.modelBecameDirty(stateName, model);
1288
+ });
1289
+ },
1290
+
1291
+ exit: function(manager) {
1292
+ var stateName = get(this, 'stateName'),
1293
+ model = get(manager, 'model');
1294
+
1295
+ this.notifyModel(model);
1296
+
1297
+ model.withTransaction(function (t) {
1298
+ t.modelBecameClean(stateName, model);
1299
+ });
1300
+ },
1301
+
1302
+ setProperty: setProperty,
1303
+
1304
+ willCommit: function(manager) {
1305
+ manager.goToState('saving');
1306
+ },
1307
+
1308
+ saving: DS.State.extend({
1309
+ isSaving: true,
1310
+
1311
+ didUpdate: function(manager) {
1312
+ manager.goToState('loaded');
1313
+ },
1314
+
1315
+ wasInvalid: function(manager, errors) {
1316
+ var model = get(manager, 'model');
1317
+
1318
+ set(model, 'errors', errors);
1319
+ manager.goToState('invalid');
1320
+ }
1321
+ }),
1322
+
1323
+ invalid: DS.State.extend({
1324
+ isValid: false,
1325
+
1326
+ setProperty: function(manager, context) {
1327
+ setProperty(manager, context);
1328
+
1329
+ var stateName = getPath(this, 'parentState.stateName'),
1330
+ model = get(manager, 'model'),
1331
+ errors = get(model, 'errors'),
1332
+ key = context.key;
1333
+
1334
+ delete errors[key];
1335
+
1336
+ if (isEmptyObject(errors)) {
1337
+ manager.goToState(stateName);
1338
+ }
1339
+ }
1340
+ })
1341
+ });
1342
+
1343
+ var states = {
1344
+ rootState: Ember.State.create({
1345
+ isLoaded: false,
1346
+ isDirty: false,
1347
+ isSaving: false,
1348
+ isDeleted: false,
1349
+ isError: false,
1350
+ isNew: false,
1351
+ isValid: true,
1352
+
1353
+ willLoadData: cantLoadData,
1354
+
1355
+ didCreate: function(manager) {
1356
+ manager.goToState('loaded.created');
1357
+ },
1358
+
1359
+ empty: DS.State.create({
1360
+ loadingData: function(manager) {
1361
+ manager.goToState('loading');
1362
+ }
1363
+ }),
1364
+
1365
+ loading: DS.State.create({
1366
+ willLoadData: Ember.K,
1367
+
1368
+ exit: function(manager) {
1369
+ var model = get(manager, 'model');
1370
+ model.didLoad();
1371
+ },
1372
+
1373
+ setData: function(manager, data) {
1374
+ var model = get(manager, 'model');
1375
+
1376
+ model.beginPropertyChanges();
1377
+ model.set('data', data);
1378
+
1379
+ if (data !== null) {
1380
+ manager.goToState('loaded');
1381
+ }
1382
+
1383
+ model.endPropertyChanges();
1384
+ }
1385
+ }),
1386
+
1387
+ loaded: DS.State.create({
1388
+ isLoaded: true,
1389
+
1390
+ willLoadData: Ember.K,
1391
+
1392
+ setProperty: function(manager, context) {
1393
+ setProperty(manager, context);
1394
+ manager.goToState('updated');
1395
+ },
1396
+
1397
+ 'delete': function(manager) {
1398
+ manager.goToState('deleted');
1399
+ },
1400
+
1401
+ created: DirtyState.create({
1402
+ stateName: 'created',
1403
+ isNew: true,
1404
+
1405
+ notifyModel: function(model) {
1406
+ model.didCreate();
1407
+ }
1408
+ }),
1409
+
1410
+ updated: DirtyState.create({
1411
+ stateName: 'updated',
1412
+
1413
+ notifyModel: function(model) {
1414
+ model.didUpdate();
1415
+ }
1416
+ })
1417
+ }),
1418
+
1419
+ deleted: DS.State.create({
1420
+ isDeleted: true,
1421
+ isLoaded: true,
1422
+ isDirty: true,
1423
+
1424
+ willLoadData: cantLoadData,
1425
+
1426
+ enter: function(manager) {
1427
+ var model = get(manager, 'model');
1428
+ var store = get(model, 'store');
1429
+
1430
+ if (store) {
1431
+ store.removeFromModelArrays(model);
1432
+ }
1433
+
1434
+ model.withTransaction(function(t) {
1435
+ t.modelBecameDirty('deleted', model);
1436
+ });
1437
+ },
1438
+
1439
+ willCommit: function(manager) {
1440
+ manager.goToState('saving');
1441
+ },
1442
+
1443
+ saving: DS.State.create({
1444
+ isSaving: true,
1445
+
1446
+ didDelete: function(manager) {
1447
+ manager.goToState('saved');
1448
+ },
1449
+
1450
+ exit: function(stateManager) {
1451
+ var model = get(stateManager, 'model');
1452
+
1453
+ model.withTransaction(function(t) {
1454
+ t.modelBecameClean('deleted', model);
1455
+ });
1456
+ }
1457
+ }),
1458
+
1459
+ saved: DS.State.create({
1460
+ isDirty: false
1461
+ })
1462
+ }),
1463
+
1464
+ error: DS.State.create({
1465
+ isError: true
1466
+ })
1467
+ })
1468
+ };
1469
+
1470
+ DS.StateManager = Ember.StateManager.extend({
1471
+ model: null,
1472
+ initialState: 'rootState',
1473
+ states: states
1474
+ });
1475
+
1476
+ var retrieveFromCurrentState = Ember.computed(function(key) {
1477
+ return get(getPath(this, 'stateManager.currentState'), key);
1478
+ }).property('stateManager.currentState').cacheable();
1479
+
1480
+ DS.Model = Ember.Object.extend({
1481
+ isLoaded: retrieveFromCurrentState,
1482
+ isDirty: retrieveFromCurrentState,
1483
+ isSaving: retrieveFromCurrentState,
1484
+ isDeleted: retrieveFromCurrentState,
1485
+ isError: retrieveFromCurrentState,
1486
+ isNew: retrieveFromCurrentState,
1487
+ isValid: retrieveFromCurrentState,
1488
+
1489
+ clientId: null,
1490
+
1491
+ // because unknownProperty is used, any internal property
1492
+ // must be initialized here.
1493
+ primaryKey: 'id',
1494
+ data: null,
1495
+ transaction: null,
1496
+
1497
+ didLoad: Ember.K,
1498
+ didUpdate: Ember.K,
1499
+ didCreate: Ember.K,
1500
+
1501
+ init: function() {
1502
+ var stateManager = DS.StateManager.create({
1503
+ model: this
1504
+ });
1505
+
1506
+ set(this, 'stateManager', stateManager);
1507
+ stateManager.goToState('empty');
1508
+ },
1509
+
1510
+ withTransaction: function(fn) {
1511
+ var transaction = get(this, 'transaction') || getPath(this, 'store.defaultTransaction');
1512
+
1513
+ if (transaction) { fn(transaction); }
1514
+ },
1515
+
1516
+ setData: function(data) {
1517
+ var stateManager = get(this, 'stateManager');
1518
+ stateManager.send('setData', data);
1519
+ },
1520
+
1521
+ setProperty: function(key, value) {
1522
+ var stateManager = get(this, 'stateManager');
1523
+ stateManager.send('setProperty', { key: key, value: value });
1524
+ },
1525
+
1526
+ deleteRecord: function() {
1527
+ var stateManager = get(this, 'stateManager');
1528
+ stateManager.send('delete');
1529
+ },
1530
+
1531
+ destroy: function() {
1532
+ this.deleteRecord();
1533
+ this._super();
1534
+ },
1535
+
1536
+ loadingData: function() {
1537
+ var stateManager = get(this, 'stateManager');
1538
+ stateManager.send('loadingData');
1539
+ },
1540
+
1541
+ willLoadData: function() {
1542
+ var stateManager = get(this, 'stateManager');
1543
+ stateManager.send('willLoadData');
1544
+ },
1545
+
1546
+ willCommit: function() {
1547
+ var stateManager = get(this, 'stateManager');
1548
+ stateManager.send('willCommit');
1549
+ },
1550
+
1551
+ adapterDidUpdate: function() {
1552
+ var stateManager = get(this, 'stateManager');
1553
+ stateManager.send('didUpdate');
1554
+ },
1555
+
1556
+ adapterDidCreate: function() {
1557
+ var stateManager = get(this, 'stateManager');
1558
+ stateManager.send('didCreate');
1559
+ },
1560
+
1561
+ adapterDidDelete: function() {
1562
+ var stateManager = get(this, 'stateManager');
1563
+ stateManager.send('didDelete');
1564
+ },
1565
+
1566
+ wasInvalid: function(errors) {
1567
+ var stateManager = get(this, 'stateManager');
1568
+ stateManager.send('wasInvalid', errors);
1569
+ },
1570
+
1571
+ unknownProperty: function(key) {
1572
+ var data = get(this, 'data');
1573
+
1574
+ if (data) {
1575
+ return get(data, key);
1576
+ }
1577
+ },
1578
+
1579
+ setUnknownProperty: function(key, value) {
1580
+ var data = get(this, 'data');
1581
+ ember_assert("You cannot set a model attribute before its data is loaded.", !!data);
1582
+
1583
+ this.setProperty(key, value);
1584
+ return value;
1585
+ }
1586
+ });
1587
+
1588
+ DS.Model.reopenClass({
1589
+ typeForAssociation: function(association) {
1590
+ var type = this.metaForProperty(association).type;
1591
+ if (typeof type === 'string') {
1592
+ type = getPath(this, type, false) || getPath(window, type);
1593
+ }
1594
+ return type;
1595
+ }
1596
+ });
1597
+
1598
+ DS.attr = function(type, options) {
1599
+ var transform = DS.attr.transforms[type];
1600
+ var transformFrom = transform.from;
1601
+ var transformTo = transform.to;
1602
+
1603
+ return Ember.computed(function(key, value) {
1604
+ var data = get(this, 'data');
1605
+
1606
+ key = (options && options.key) ? options.key : key;
1607
+
1608
+ if (value === undefined) {
1609
+ if (!data) { return; }
1610
+
1611
+ return transformFrom(data[key]);
1612
+ } else {
1613
+ ember_assert("You cannot set a model attribute before its data is loaded.", !!data);
1614
+
1615
+ value = transformTo(value);
1616
+ this.setProperty(key, value);
1617
+ return value;
1618
+ }
1619
+ }).property('data');
1620
+ };
1621
+
1622
+ var embeddedFindRecord = function(store, type, data, key, one) {
1623
+ var association = data ? get(data, key) : one ? null : [];
1624
+ if (one) {
1625
+ return association ? store.load(type, association).id : null;
1626
+ } else {
1627
+ return association ? store.loadMany(type, association).ids : [];
1628
+ }
1629
+ };
1630
+
1631
+ var referencedFindRecord = function(store, type, data, key, one) {
1632
+ return data ? get(data, key) : one ? null : [];
1633
+ };
1634
+
1635
+ var hasAssociation = function(type, options, one) {
1636
+ var embedded = options && options.embedded,
1637
+ findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
1638
+
1639
+ return Ember.computed(function(key) {
1640
+ var data = get(this, 'data'), ids, id, association,
1641
+ store = get(this, 'store');
1642
+
1643
+ if (typeof type === 'string') {
1644
+ type = getPath(this, type, false) || getPath(window, type);
1645
+ }
1646
+
1647
+ key = (options && options.key) ? options.key : key;
1648
+ if (one) {
1649
+ id = findRecord(store, type, data, key, true);
1650
+ association = id ? store.find(type, id) : null;
1651
+ } else {
1652
+ ids = findRecord(store, type, data, key);
1653
+ association = store.findMany(type, ids);
1654
+ }
1655
+
1656
+ return association;
1657
+ }).property('data').cacheable().meta({ type: type });
1658
+ };
1659
+
1660
+ DS.hasMany = function(type, options) {
1661
+ ember_assert("The type passed to DS.hasMany must be defined", !!type);
1662
+ return hasAssociation(type, options);
1663
+ };
1664
+
1665
+ DS.hasOne = function(type, options) {
1666
+ ember_assert("The type passed to DS.hasOne must be defined", !!type);
1667
+ return hasAssociation(type, options, true);
1668
+ };
1669
+
1670
+ DS.attr.transforms = {
1671
+ string: {
1672
+ from: function(serialized) {
1673
+ return Em.none(serialized) ? null : String(serialized);
1674
+ },
1675
+
1676
+ to: function(deserialized) {
1677
+ return Em.none(deserialized) ? null : String(deserialized);
1678
+ }
1679
+ },
1680
+
1681
+ integer: {
1682
+ from: function(serialized) {
1683
+ return Em.none(serialized) ? null : Number(serialized);
1684
+ },
1685
+
1686
+ to: function(deserialized) {
1687
+ return Em.none(deserialized) ? null : Number(deserialized);
1688
+ }
1689
+ },
1690
+
1691
+ boolean: {
1692
+ from: function(serialized) {
1693
+ return Boolean(serialized);
1694
+ },
1695
+
1696
+ to: function(deserialized) {
1697
+ return Boolean(deserialized);
1698
+ }
1699
+ },
1700
+
1701
+ date: {
1702
+ from: function(serialized) {
1703
+ var type = typeof serialized;
1704
+
1705
+ if (type === "string" || type === "number") {
1706
+ return new Date(serialized);
1707
+ } else if (serialized === null || serialized === undefined) {
1708
+ // if the value is not present in the data,
1709
+ // return undefined, not null.
1710
+ return serialized;
1711
+ } else {
1712
+ return null;
1713
+ }
1714
+ },
1715
+
1716
+ to: function(date) {
1717
+ if (date instanceof Date) {
1718
+ var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1719
+ var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1720
+
1721
+ var pad = function(num) {
1722
+ return num < 10 ? "0"+num : ""+num;
1723
+ };
1724
+
1725
+ var utcYear = date.getUTCFullYear(),
1726
+ utcMonth = date.getUTCMonth(),
1727
+ utcDayOfMonth = date.getUTCDate(),
1728
+ utcDay = date.getUTCDay(),
1729
+ utcHours = date.getUTCHours(),
1730
+ utcMinutes = date.getUTCMinutes(),
1731
+ utcSeconds = date.getUTCSeconds();
1732
+
1733
+
1734
+ var dayOfWeek = days[utcDay];
1735
+ var dayOfMonth = pad(utcDayOfMonth);
1736
+ var month = months[utcMonth];
1737
+
1738
+ return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
1739
+ pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
1740
+ } else if (date === undefined) {
1741
+ return undefined;
1742
+ } else {
1743
+ return null;
1744
+ }
1745
+ }
1746
+ }
1747
+ };
1748
+
1749
+ })({});
1750
+
1751
+
1752
+ (function(exports) {
1753
+ //Copyright (C) 2011 by Living Social, Inc.
1754
+
1755
+ //Permission is hereby granted, free of charge, to any person obtaining a copy of
1756
+ //this software and associated documentation files (the "Software"), to deal in
1757
+ //the Software without restriction, including without limitation the rights to
1758
+ //use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1759
+ //of the Software, and to permit persons to whom the Software is furnished to do
1760
+ //so, subject to the following conditions:
1761
+
1762
+ //The above copyright notice and this permission notice shall be included in all
1763
+ //copies or substantial portions of the Software.
1764
+
1765
+ //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1766
+ //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1767
+ //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1768
+ //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1769
+ //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1770
+ //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1771
+ //SOFTWARE.
1772
+ })({});