embient 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +29 -29
- data/lib/embient/version.rb +1 -1
- data/vendor/assets/javascripts/embient/ember-data.js +1457 -731
- data/vendor/assets/javascripts/embient/ember-routemanager.js +13 -17
- data/vendor/assets/javascripts/embient/ember.js +2223 -456
- data/vendor/assets/javascripts/embient/ember.min.js +5 -5
- data/vendor/assets/javascripts/embient/ember.prod.js +18594 -0
- metadata +20 -9
data/Gemfile.lock
CHANGED
@@ -1,38 +1,38 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
embient (0.0.
|
4
|
+
embient (0.0.9)
|
5
5
|
emberjs-rails
|
6
6
|
rails (>= 3.1.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actionmailer (3.2.
|
12
|
-
actionpack (= 3.2.
|
11
|
+
actionmailer (3.2.0)
|
12
|
+
actionpack (= 3.2.0)
|
13
13
|
mail (~> 2.4.0)
|
14
|
-
actionpack (3.2.
|
15
|
-
activemodel (= 3.2.
|
16
|
-
activesupport (= 3.2.
|
14
|
+
actionpack (3.2.0)
|
15
|
+
activemodel (= 3.2.0)
|
16
|
+
activesupport (= 3.2.0)
|
17
17
|
builder (~> 3.0.0)
|
18
18
|
erubis (~> 2.7.0)
|
19
|
-
journey (~> 1.0.
|
19
|
+
journey (~> 1.0.0)
|
20
20
|
rack (~> 1.4.0)
|
21
21
|
rack-cache (~> 1.1)
|
22
22
|
rack-test (~> 0.6.1)
|
23
23
|
sprockets (~> 2.1.2)
|
24
|
-
activemodel (3.2.
|
25
|
-
activesupport (= 3.2.
|
24
|
+
activemodel (3.2.0)
|
25
|
+
activesupport (= 3.2.0)
|
26
26
|
builder (~> 3.0.0)
|
27
|
-
activerecord (3.2.
|
28
|
-
activemodel (= 3.2.
|
29
|
-
activesupport (= 3.2.
|
27
|
+
activerecord (3.2.0)
|
28
|
+
activemodel (= 3.2.0)
|
29
|
+
activesupport (= 3.2.0)
|
30
30
|
arel (~> 3.0.0)
|
31
31
|
tzinfo (~> 0.3.29)
|
32
|
-
activeresource (3.2.
|
33
|
-
activemodel (= 3.2.
|
34
|
-
activesupport (= 3.2.
|
35
|
-
activesupport (3.2.
|
32
|
+
activeresource (3.2.0)
|
33
|
+
activemodel (= 3.2.0)
|
34
|
+
activesupport (= 3.2.0)
|
35
|
+
activesupport (3.2.0)
|
36
36
|
i18n (~> 0.6)
|
37
37
|
multi_json (~> 1.0)
|
38
38
|
arel (3.0.2)
|
@@ -52,13 +52,13 @@ GEM
|
|
52
52
|
hike (1.2.1)
|
53
53
|
i18n (0.6.0)
|
54
54
|
journey (1.0.3)
|
55
|
-
json (1.6.
|
55
|
+
json (1.6.6)
|
56
56
|
mail (2.4.4)
|
57
57
|
i18n (>= 0.4.0)
|
58
58
|
mime-types (~> 1.16)
|
59
59
|
treetop (~> 1.4.8)
|
60
60
|
mime-types (1.18)
|
61
|
-
multi_json (1.
|
61
|
+
multi_json (1.2.0)
|
62
62
|
polyglot (0.3.3)
|
63
63
|
rack (1.4.1)
|
64
64
|
rack-cache (1.2)
|
@@ -67,17 +67,17 @@ GEM
|
|
67
67
|
rack
|
68
68
|
rack-test (0.6.1)
|
69
69
|
rack (>= 1.0)
|
70
|
-
rails (3.2.
|
71
|
-
actionmailer (= 3.2.
|
72
|
-
actionpack (= 3.2.
|
73
|
-
activerecord (= 3.2.
|
74
|
-
activeresource (= 3.2.
|
75
|
-
activesupport (= 3.2.
|
70
|
+
rails (3.2.0)
|
71
|
+
actionmailer (= 3.2.0)
|
72
|
+
actionpack (= 3.2.0)
|
73
|
+
activerecord (= 3.2.0)
|
74
|
+
activeresource (= 3.2.0)
|
75
|
+
activesupport (= 3.2.0)
|
76
76
|
bundler (~> 1.0)
|
77
|
-
railties (= 3.2.
|
78
|
-
railties (3.2.
|
79
|
-
actionpack (= 3.2.
|
80
|
-
activesupport (= 3.2.
|
77
|
+
railties (= 3.2.0)
|
78
|
+
railties (3.2.0)
|
79
|
+
actionpack (= 3.2.0)
|
80
|
+
activesupport (= 3.2.0)
|
81
81
|
rack-ssl (~> 1.3.2)
|
82
82
|
rake (>= 0.8.7)
|
83
83
|
rdoc (~> 3.4)
|
@@ -94,7 +94,7 @@ GEM
|
|
94
94
|
treetop (1.4.10)
|
95
95
|
polyglot
|
96
96
|
polyglot (>= 0.3.1)
|
97
|
-
tzinfo (0.3.
|
97
|
+
tzinfo (0.3.33)
|
98
98
|
|
99
99
|
PLATFORMS
|
100
100
|
ruby
|
data/lib/embient/version.rb
CHANGED
@@ -1,510 +1,426 @@
|
|
1
|
-
|
2
|
-
(function(exports) {
|
1
|
+
(function() {
|
3
2
|
window.DS = Ember.Namespace.create({
|
4
|
-
CURRENT_API_REVISION:
|
3
|
+
CURRENT_API_REVISION: 4
|
5
4
|
});
|
6
5
|
|
7
|
-
})(
|
6
|
+
})();
|
8
7
|
|
9
8
|
|
10
|
-
(function(exports) {
|
11
|
-
DS.Adapter = Ember.Object.extend({
|
12
|
-
commit: function(store, commitDetails) {
|
13
|
-
commitDetails.updated.eachType(function(type, array) {
|
14
|
-
this.updateRecords(store, type, array.slice());
|
15
|
-
}, this);
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
}, this);
|
10
|
+
(function() {
|
11
|
+
var get = Ember.get, set = Ember.set;
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
13
|
+
/**
|
14
|
+
A record array is an array that contains records of a certain type. The record
|
15
|
+
array materializes records as needed when they are retrieved for the first
|
16
|
+
time. You should not create record arrays yourself. Instead, an instance of
|
17
|
+
DS.RecordArray or its subclasses will be returned by your application's store
|
18
|
+
in response to queries.
|
19
|
+
*/
|
25
20
|
|
26
|
-
|
27
|
-
models.forEach(function(model) {
|
28
|
-
this.createRecord(store, type, model);
|
29
|
-
}, this);
|
30
|
-
},
|
21
|
+
DS.RecordArray = Ember.ArrayProxy.extend({
|
31
22
|
|
32
|
-
|
33
|
-
|
34
|
-
this.updateRecord(store, type, model);
|
35
|
-
}, this);
|
36
|
-
},
|
23
|
+
/**
|
24
|
+
The model type contained by this record array.
|
37
25
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
}, this);
|
42
|
-
},
|
26
|
+
@type DS.Model
|
27
|
+
*/
|
28
|
+
type: null,
|
43
29
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
});
|
50
|
-
})({});
|
30
|
+
// The array of client ids backing the record array. When a
|
31
|
+
// record is requested from the record array, the record
|
32
|
+
// for the client id at the same index is materialized, if
|
33
|
+
// necessary, by the store.
|
34
|
+
content: null,
|
51
35
|
|
36
|
+
// The store that created this record array.
|
37
|
+
store: null,
|
52
38
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
init: function() {
|
40
|
+
set(this, 'recordCache', Ember.A([]));
|
41
|
+
this._super();
|
42
|
+
},
|
57
43
|
|
58
|
-
|
59
|
-
|
44
|
+
arrayDidChange: function(array, index, removed, added) {
|
45
|
+
var recordCache = get(this, 'recordCache');
|
46
|
+
recordCache.replace(index, 0, new Array(added));
|
60
47
|
|
61
|
-
|
62
|
-
store.loadMany(type, fixtures);
|
63
|
-
fixtures.hasLoaded = true;
|
64
|
-
}, 300);
|
48
|
+
this._super(array, index, removed, added);
|
65
49
|
},
|
66
50
|
|
67
|
-
|
68
|
-
this.
|
51
|
+
arrayWillChange: function(array, index, removed, added) {
|
52
|
+
this._super(array, index, removed, added);
|
53
|
+
|
54
|
+
var recordCache = get(this, 'recordCache');
|
55
|
+
recordCache.replace(index, removed);
|
69
56
|
},
|
70
57
|
|
71
|
-
|
72
|
-
var
|
58
|
+
objectAtContent: function(index) {
|
59
|
+
var recordCache = get(this, 'recordCache');
|
60
|
+
var record = recordCache.objectAt(index);
|
73
61
|
|
74
|
-
|
62
|
+
if (!record) {
|
63
|
+
var store = get(this, 'store');
|
64
|
+
var content = get(this, 'content');
|
75
65
|
|
76
|
-
|
77
|
-
|
78
|
-
|
66
|
+
var contentObject = content.objectAt(index);
|
67
|
+
|
68
|
+
if (contentObject !== undefined) {
|
69
|
+
record = store.findByClientId(get(this, 'type'), contentObject);
|
70
|
+
recordCache.replace(index, 1, [record]);
|
71
|
+
}
|
72
|
+
}
|
79
73
|
|
74
|
+
return record;
|
75
|
+
}
|
80
76
|
});
|
81
77
|
|
82
|
-
})(
|
78
|
+
})();
|
83
79
|
|
84
80
|
|
85
|
-
(function(exports) {
|
86
|
-
/*global jQuery*/
|
87
|
-
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
88
81
|
|
89
|
-
|
90
|
-
|
91
|
-
var root = this.rootForType(type);
|
82
|
+
(function() {
|
83
|
+
var get = Ember.get;
|
92
84
|
|
93
|
-
|
94
|
-
|
85
|
+
DS.FilteredRecordArray = DS.RecordArray.extend({
|
86
|
+
filterFunction: null,
|
95
87
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
this.sideload(store, type, json, root);
|
100
|
-
store.didCreateRecord(model, json[root]);
|
101
|
-
}
|
102
|
-
});
|
88
|
+
replace: function() {
|
89
|
+
var type = get(this, 'type').toString();
|
90
|
+
throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
|
103
91
|
},
|
104
92
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
var root = this.rootForType(type),
|
111
|
-
plural = this.pluralize(root);
|
93
|
+
updateFilter: Ember.observer(function() {
|
94
|
+
var store = get(this, 'store');
|
95
|
+
store.updateRecordArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
|
96
|
+
}, 'filterFunction')
|
97
|
+
});
|
112
98
|
|
113
|
-
|
114
|
-
data[plural] = models.map(function(model) {
|
115
|
-
return model.toJSON();
|
116
|
-
});
|
99
|
+
})();
|
117
100
|
|
118
|
-
this.ajax(this.buildURL(root), "POST", {
|
119
|
-
data: data,
|
120
101
|
|
121
|
-
success: function(json) {
|
122
|
-
this.sideload(store, type, json, plural);
|
123
|
-
store.didCreateRecords(type, models, json[plural]);
|
124
|
-
}
|
125
|
-
});
|
126
|
-
},
|
127
102
|
|
128
|
-
|
129
|
-
|
130
|
-
var root = this.rootForType(type);
|
103
|
+
(function() {
|
104
|
+
var get = Ember.get, set = Ember.set;
|
131
105
|
|
132
|
-
|
133
|
-
|
106
|
+
DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
|
107
|
+
query: null,
|
108
|
+
isLoaded: false,
|
134
109
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
this.sideload(store, type, json, root);
|
139
|
-
store.didUpdateRecord(model, json && json[root]);
|
140
|
-
}
|
141
|
-
});
|
110
|
+
replace: function() {
|
111
|
+
var type = get(this, 'type').toString();
|
112
|
+
throw new Error("The result of a server query (on " + type + ") is immutable.");
|
142
113
|
},
|
143
114
|
|
144
|
-
|
145
|
-
|
146
|
-
return this._super(store, type, models);
|
147
|
-
}
|
115
|
+
load: function(array) {
|
116
|
+
var store = get(this, 'store'), type = get(this, 'type');
|
148
117
|
|
149
|
-
var
|
150
|
-
plural = this.pluralize(root);
|
118
|
+
var clientIds = store.loadMany(type, array).clientIds;
|
151
119
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
120
|
+
this.beginPropertyChanges();
|
121
|
+
set(this, 'content', Ember.A(clientIds));
|
122
|
+
set(this, 'isLoaded', true);
|
123
|
+
this.endPropertyChanges();
|
124
|
+
}
|
125
|
+
});
|
156
126
|
|
157
|
-
this.ajax(this.buildURL(root, "bulk"), "PUT", {
|
158
|
-
data: data,
|
159
|
-
success: function(json) {
|
160
|
-
this.sideload(store, type, json, plural);
|
161
|
-
store.didUpdateRecords(models, json[plural]);
|
162
|
-
}
|
163
|
-
});
|
164
|
-
},
|
165
127
|
|
166
|
-
|
167
|
-
var id = get(model, 'id');
|
168
|
-
var root = this.rootForType(type);
|
128
|
+
})();
|
169
129
|
|
170
|
-
this.ajax(this.buildURL(root, id), "DELETE", {
|
171
|
-
success: function(json) {
|
172
|
-
if (json) { this.sideload(store, type, json); }
|
173
|
-
store.didDeleteRecord(model);
|
174
|
-
}
|
175
|
-
});
|
176
|
-
},
|
177
130
|
|
178
|
-
deleteRecords: function(store, type, models) {
|
179
|
-
if (get(this, 'bulkCommit') === false) {
|
180
|
-
return this._super(store, type, models);
|
181
|
-
}
|
182
131
|
|
183
|
-
|
184
|
-
|
132
|
+
(function() {
|
133
|
+
var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor;
|
185
134
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
135
|
+
var Set = function() {
|
136
|
+
this.hash = {};
|
137
|
+
this.list = [];
|
138
|
+
};
|
190
139
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
store.didDeleteRecords(models);
|
196
|
-
}
|
197
|
-
});
|
198
|
-
},
|
140
|
+
Set.prototype = {
|
141
|
+
add: function(item) {
|
142
|
+
var hash = this.hash,
|
143
|
+
guid = guidFor(item);
|
199
144
|
|
200
|
-
|
201
|
-
var root = this.rootForType(type);
|
145
|
+
if (hash.hasOwnProperty(guid)) { return; }
|
202
146
|
|
203
|
-
|
204
|
-
|
205
|
-
store.load(type, json[root]);
|
206
|
-
this.sideload(store, type, json, root);
|
207
|
-
}
|
208
|
-
});
|
147
|
+
hash[guid] = true;
|
148
|
+
this.list.push(item);
|
209
149
|
},
|
210
150
|
|
211
|
-
|
212
|
-
var
|
151
|
+
remove: function(item) {
|
152
|
+
var hash = this.hash,
|
153
|
+
guid = guidFor(item);
|
213
154
|
|
214
|
-
|
215
|
-
data: { ids: ids },
|
216
|
-
success: function(json) {
|
217
|
-
store.loadMany(type, ids, json[plural]);
|
218
|
-
this.sideload(store, type, json, plural);
|
219
|
-
}
|
220
|
-
});
|
221
|
-
},
|
155
|
+
if (!hash.hasOwnProperty(guid)) { return; }
|
222
156
|
|
223
|
-
|
224
|
-
var
|
157
|
+
delete hash[guid];
|
158
|
+
var list = this.list,
|
159
|
+
index = Ember.ArrayUtils.indexOf(this, item);
|
225
160
|
|
226
|
-
|
227
|
-
success: function(json) {
|
228
|
-
store.loadMany(type, json[plural]);
|
229
|
-
this.sideload(store, type, json, plural);
|
230
|
-
}
|
231
|
-
});
|
161
|
+
list.splice(index, 1);
|
232
162
|
},
|
233
163
|
|
234
|
-
|
235
|
-
|
164
|
+
isEmpty: function() {
|
165
|
+
return this.list.length === 0;
|
166
|
+
}
|
167
|
+
};
|
236
168
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
169
|
+
var ManyArrayState = Ember.State.extend({
|
170
|
+
recordWasAdded: function(manager, record) {
|
171
|
+
var dirty = manager.dirty, observer;
|
172
|
+
dirty.add(record);
|
173
|
+
|
174
|
+
observer = function() {
|
175
|
+
if (!get(record, 'isDirty')) {
|
176
|
+
record.removeObserver('isDirty', observer);
|
177
|
+
manager.send('childWasSaved', record);
|
242
178
|
}
|
243
|
-
}
|
244
|
-
},
|
179
|
+
};
|
245
180
|
|
246
|
-
|
181
|
+
record.addObserver('isDirty', observer);
|
182
|
+
},
|
247
183
|
|
248
|
-
|
184
|
+
recordWasRemoved: function(manager, record) {
|
185
|
+
var dirty = manager.dirty, observer;
|
186
|
+
dirty.add(record);
|
249
187
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
},
|
188
|
+
observer = function() {
|
189
|
+
record.removeObserver('isDirty', observer);
|
190
|
+
if (!get(record, 'isDirty')) { manager.send('childWasSaved', record); }
|
191
|
+
};
|
255
192
|
|
256
|
-
|
257
|
-
|
193
|
+
record.addObserver('isDirty', observer);
|
194
|
+
}
|
195
|
+
});
|
258
196
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
|
263
|
-
},
|
197
|
+
var states = {
|
198
|
+
clean: ManyArrayState.create({
|
199
|
+
isDirty: false,
|
264
200
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
hash.contentType = 'application/json';
|
270
|
-
hash.context = this;
|
201
|
+
recordWasAdded: function(manager, record) {
|
202
|
+
this._super(manager, record);
|
203
|
+
manager.goToState('dirty');
|
204
|
+
},
|
271
205
|
|
272
|
-
|
273
|
-
|
206
|
+
update: function(manager, clientIds) {
|
207
|
+
var manyArray = manager.manyArray;
|
208
|
+
set(manyArray, 'content', clientIds);
|
274
209
|
}
|
210
|
+
}),
|
275
211
|
|
276
|
-
|
277
|
-
|
212
|
+
dirty: ManyArrayState.create({
|
213
|
+
isDirty: true,
|
278
214
|
|
279
|
-
|
280
|
-
|
215
|
+
childWasSaved: function(manager, child) {
|
216
|
+
var dirty = manager.dirty;
|
217
|
+
dirty.remove(child);
|
281
218
|
|
282
|
-
|
283
|
-
|
284
|
-
if (prop === root) { continue; }
|
219
|
+
if (dirty.isEmpty()) { manager.send('arrayBecameSaved'); }
|
220
|
+
},
|
285
221
|
|
286
|
-
|
222
|
+
arrayBecameSaved: function(manager) {
|
223
|
+
manager.goToState('clean');
|
224
|
+
}
|
225
|
+
})
|
226
|
+
};
|
287
227
|
|
288
|
-
|
289
|
-
|
228
|
+
DS.ManyArrayStateManager = Ember.StateManager.extend({
|
229
|
+
manyArray: null,
|
230
|
+
initialState: 'clean',
|
231
|
+
states: states,
|
290
232
|
|
291
|
-
|
233
|
+
init: function() {
|
234
|
+
this._super();
|
235
|
+
this.dirty = new Set();
|
236
|
+
}
|
237
|
+
});
|
292
238
|
|
293
|
-
|
239
|
+
})();
|
294
240
|
|
295
|
-
ember_assert("Your server returned a hash with the key " + prop + " but you have no mapping for it", !!sideloadedType);
|
296
|
-
}
|
297
241
|
|
298
|
-
this.loadValue(store, sideloadedType, json[prop]);
|
299
|
-
}
|
300
|
-
},
|
301
|
-
|
302
|
-
loadValue: function(store, type, value) {
|
303
|
-
if (value instanceof Array) {
|
304
|
-
store.loadMany(type, value);
|
305
|
-
} else {
|
306
|
-
store.load(type, value);
|
307
|
-
}
|
308
|
-
},
|
309
|
-
|
310
|
-
buildURL: function(model, suffix) {
|
311
|
-
var url = [""];
|
312
|
-
|
313
|
-
if (this.namespace !== undefined) {
|
314
|
-
url.push(this.namespace);
|
315
|
-
}
|
316
242
|
|
317
|
-
|
318
|
-
|
319
|
-
url.push(suffix);
|
320
|
-
}
|
243
|
+
(function() {
|
244
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
321
245
|
|
322
|
-
|
323
|
-
|
324
|
-
});
|
246
|
+
DS.ManyArray = DS.RecordArray.extend({
|
247
|
+
init: function() {
|
248
|
+
set(this, 'stateManager', DS.ManyArrayStateManager.create({ manyArray: this }));
|
325
249
|
|
250
|
+
return this._super();
|
251
|
+
},
|
326
252
|
|
327
|
-
|
253
|
+
parentRecord: null,
|
328
254
|
|
255
|
+
isDirty: Ember.computed(function() {
|
256
|
+
return getPath(this, 'stateManager.currentState.isDirty');
|
257
|
+
}).property('stateManager.currentState').cacheable(),
|
329
258
|
|
330
|
-
|
331
|
-
var
|
259
|
+
fetch: function() {
|
260
|
+
var clientIds = get(this, 'content'),
|
261
|
+
store = get(this, 'store'),
|
262
|
+
type = get(this, 'type');
|
332
263
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
time. You should not create model arrays yourself. Instead, an instance of
|
337
|
-
DS.ModelArray or its subclasses will be returned by your application's store
|
338
|
-
in response to queries.
|
339
|
-
*/
|
264
|
+
var ids = clientIds.map(function(clientId) {
|
265
|
+
return store.clientIdToId[clientId];
|
266
|
+
});
|
340
267
|
|
341
|
-
|
268
|
+
store.fetchMany(type, ids);
|
269
|
+
},
|
342
270
|
|
343
|
-
|
344
|
-
|
271
|
+
// Overrides Ember.Array's replace method to implement
|
272
|
+
replace: function(index, removed, added) {
|
273
|
+
var parentRecord = get(this, 'parentRecord');
|
274
|
+
var pendingParent = parentRecord && !get(parentRecord, 'id');
|
275
|
+
var stateManager = get(this, 'stateManager');
|
345
276
|
|
346
|
-
|
347
|
-
|
348
|
-
type: null,
|
277
|
+
added = added.map(function(record) {
|
278
|
+
ember_assert("You can only add records of " + (get(this, 'type') && get(this, 'type').toString()) + " to this association.", !get(this, 'type') || (get(this, 'type') === record.constructor));
|
349
279
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
// necessary, by the store.
|
354
|
-
content: null,
|
280
|
+
if (pendingParent) {
|
281
|
+
record.send('waitingOn', parentRecord);
|
282
|
+
}
|
355
283
|
|
356
|
-
|
357
|
-
store: null,
|
284
|
+
this.assignInverse(record, parentRecord);
|
358
285
|
|
359
|
-
|
360
|
-
set(this, 'modelCache', Ember.A([]));
|
361
|
-
this._super();
|
362
|
-
},
|
286
|
+
stateManager.send('recordWasAdded', record);
|
363
287
|
|
364
|
-
|
365
|
-
|
366
|
-
modelCache.replace(index, 0, new Array(added));
|
288
|
+
return record.get('clientId');
|
289
|
+
}, this);
|
367
290
|
|
368
|
-
|
369
|
-
},
|
291
|
+
var store = this.store;
|
370
292
|
|
371
|
-
|
372
|
-
|
293
|
+
var len = index+removed, record;
|
294
|
+
for (var i = index; i < len; i++) {
|
295
|
+
// TODO: null out inverse FK
|
296
|
+
record = this.objectAt(i);
|
297
|
+
this.assignInverse(record, parentRecord, true);
|
298
|
+
stateManager.send('recordWasAdded', record);
|
299
|
+
}
|
373
300
|
|
374
|
-
|
375
|
-
modelCache.replace(index, removed);
|
301
|
+
this._super(index, removed, added);
|
376
302
|
},
|
377
303
|
|
378
|
-
|
379
|
-
var
|
380
|
-
|
304
|
+
assignInverse: function(record, parentRecord, remove) {
|
305
|
+
var associationMap = get(record.constructor, 'associations'),
|
306
|
+
possibleAssociations = associationMap.get(parentRecord.constructor),
|
307
|
+
possible, actual;
|
381
308
|
|
382
|
-
if (!
|
383
|
-
var store = get(this, 'store');
|
384
|
-
var content = get(this, 'content');
|
309
|
+
if (!possibleAssociations) { return; }
|
385
310
|
|
386
|
-
|
311
|
+
for (var i = 0, l = possibleAssociations.length; i < l; i++) {
|
312
|
+
possible = possibleAssociations[i];
|
387
313
|
|
388
|
-
if (
|
389
|
-
|
390
|
-
|
314
|
+
if (possible.kind === 'belongsTo') {
|
315
|
+
actual = possible;
|
316
|
+
break;
|
391
317
|
}
|
392
318
|
}
|
393
319
|
|
394
|
-
|
320
|
+
if (actual) {
|
321
|
+
set(record, actual.name, remove ? null : parentRecord);
|
322
|
+
}
|
395
323
|
}
|
396
324
|
});
|
397
325
|
|
398
|
-
})(
|
326
|
+
})();
|
399
327
|
|
400
328
|
|
401
|
-
(function(exports) {
|
402
|
-
var get = Ember.get;
|
403
|
-
|
404
|
-
DS.FilteredModelArray = DS.ModelArray.extend({
|
405
|
-
filterFunction: null,
|
406
|
-
|
407
|
-
replace: function() {
|
408
|
-
var type = get(this, 'type').toString();
|
409
|
-
throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
|
410
|
-
},
|
411
329
|
|
412
|
-
|
413
|
-
var store = get(this, 'store');
|
414
|
-
store.updateModelArrayFilter(this, get(this, 'type'), get(this, 'filterFunction'));
|
415
|
-
}, 'filterFunction')
|
416
|
-
});
|
330
|
+
(function() {
|
417
331
|
|
418
|
-
})(
|
332
|
+
})();
|
419
333
|
|
420
334
|
|
421
|
-
(function(exports) {
|
422
|
-
var get = Ember.get, set = Ember.set;
|
423
|
-
|
424
|
-
DS.AdapterPopulatedModelArray = DS.ModelArray.extend({
|
425
|
-
query: null,
|
426
|
-
isLoaded: false,
|
427
|
-
|
428
|
-
replace: function() {
|
429
|
-
var type = get(this, 'type').toString();
|
430
|
-
throw new Error("The result of a server query (on " + type + ") is immutable.");
|
431
|
-
},
|
432
335
|
|
433
|
-
|
434
|
-
|
336
|
+
(function() {
|
337
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
|
435
338
|
|
436
|
-
|
339
|
+
/**
|
340
|
+
A transaction allows you to collect multiple records into a unit of work
|
341
|
+
that can be committed or rolled back as a group.
|
437
342
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
343
|
+
For example, if a record has local modifications that have not yet
|
344
|
+
been saved, calling `commit()` on its transaction will cause those
|
345
|
+
modifications to be sent to the adapter to be saved. Calling
|
346
|
+
`rollback()` on its transaction would cause all of the modifications to
|
347
|
+
be discarded and the record to return to the last known state before
|
348
|
+
changes were made.
|
444
349
|
|
350
|
+
If a newly created record's transaction is rolled back, it will
|
351
|
+
immediately transition to the deleted state.
|
445
352
|
|
446
|
-
|
353
|
+
If you do not explicitly create a transaction, a record is assigned to
|
354
|
+
an implicit transaction called the default transaction. In these cases,
|
355
|
+
you can treat your application's instance of `DS.Store` as a transaction
|
356
|
+
and call the `commit()` and `rollback()` methods on the store itself.
|
447
357
|
|
358
|
+
Once a record has been successfully committed or rolled back, it will
|
359
|
+
be moved back to the implicit transaction. Because it will now be in
|
360
|
+
a clean state, it can be moved to a new transaction if you wish.
|
448
361
|
|
449
|
-
|
450
|
-
var get = Ember.get, set = Ember.set;
|
362
|
+
### Creating a Transaction
|
451
363
|
|
452
|
-
|
453
|
-
|
364
|
+
To create a new transaction, call the `transaction()` method of your
|
365
|
+
application's `DS.Store` instance:
|
454
366
|
|
455
|
-
|
456
|
-
replace: function(index, removed, added) {
|
457
|
-
var parentRecord = get(this, 'parentRecord');
|
458
|
-
var pendingParent = parentRecord && !get(parentRecord, 'id');
|
367
|
+
var transaction = App.store.transaction();
|
459
368
|
|
460
|
-
|
461
|
-
|
369
|
+
This will return a new instance of `DS.Transaction` with no records
|
370
|
+
yet assigned to it.
|
462
371
|
|
463
|
-
|
464
|
-
record.send('waitingOn', parentRecord);
|
465
|
-
}
|
372
|
+
### Adding Existing Records
|
466
373
|
|
467
|
-
|
374
|
+
Add records to a transaction using the `add()` method:
|
468
375
|
|
469
|
-
|
470
|
-
|
376
|
+
record = App.store.find(Person, 1);
|
377
|
+
transaction.add(record);
|
471
378
|
|
472
|
-
|
473
|
-
|
379
|
+
Note that only records whose `isDirty` flag is `false` may be added
|
380
|
+
to a transaction. Once modifications to a record have been made
|
381
|
+
(its `isDirty` flag is `true`), it is not longer able to be added to
|
382
|
+
a transaction.
|
474
383
|
|
475
|
-
|
476
|
-
var associationMap = get(record.constructor, 'associations'),
|
477
|
-
possibleAssociations = associationMap.get(record.constructor),
|
478
|
-
possible, actual;
|
384
|
+
### Creating New Records
|
479
385
|
|
480
|
-
|
386
|
+
Because newly created records are dirty from the time they are created,
|
387
|
+
and because dirty records can not be added to a transaction, you must
|
388
|
+
use the `createRecord()` method to assign new records to a transaction.
|
481
389
|
|
482
|
-
|
483
|
-
possible = possibleAssociations[i];
|
390
|
+
For example, instead of this:
|
484
391
|
|
485
|
-
|
486
|
-
|
487
|
-
break;
|
488
|
-
}
|
489
|
-
}
|
392
|
+
var transaction = store.transaction();
|
393
|
+
var person = Person.createRecord({ name: "Steve" });
|
490
394
|
|
491
|
-
|
492
|
-
|
493
|
-
}
|
494
|
-
}
|
495
|
-
});
|
395
|
+
// won't work because person is dirty
|
396
|
+
transaction.add(person);
|
496
397
|
|
497
|
-
|
398
|
+
Call `createRecord()` on the transaction directly:
|
498
399
|
|
400
|
+
var transaction = store.transaction();
|
401
|
+
transaction.createRecord(Person, { name: "Steve" });
|
499
402
|
|
500
|
-
|
501
|
-
})({});
|
403
|
+
### Asynchronous Commits
|
502
404
|
|
405
|
+
Typically, all of the records in a transaction will be committed
|
406
|
+
together. However, new records that have a dependency on other new
|
407
|
+
records need to wait for their parent record to be saved and assigned an
|
408
|
+
ID. In that case, the child record will continue to live in the
|
409
|
+
transaction until its parent is saved, at which time the transaction will
|
410
|
+
attempt to commit again.
|
503
411
|
|
504
|
-
|
505
|
-
|
412
|
+
For this reason, you should not re-use transactions once you have committed
|
413
|
+
them. Always make a new transaction and move the desired records to it before
|
414
|
+
calling commit.
|
415
|
+
*/
|
506
416
|
|
507
417
|
DS.Transaction = Ember.Object.extend({
|
418
|
+
/**
|
419
|
+
@private
|
420
|
+
|
421
|
+
Creates the bucket data structure used to segregate records by
|
422
|
+
type.
|
423
|
+
*/
|
508
424
|
init: function() {
|
509
425
|
set(this, 'buckets', {
|
510
426
|
clean: Ember.Map.create(),
|
@@ -514,30 +430,182 @@ DS.Transaction = Ember.Object.extend({
|
|
514
430
|
});
|
515
431
|
},
|
516
432
|
|
433
|
+
/**
|
434
|
+
Creates a new record of the given type and assigns it to the transaction
|
435
|
+
on which the method was called.
|
436
|
+
|
437
|
+
This is useful as only clean records can be added to a transaction and
|
438
|
+
new records created using other methods immediately become dirty.
|
439
|
+
|
440
|
+
@param {DS.Model} type the model type to create
|
441
|
+
@param {Object} hash the data hash to assign the new record
|
442
|
+
*/
|
517
443
|
createRecord: function(type, hash) {
|
518
444
|
var store = get(this, 'store');
|
519
445
|
|
520
446
|
return store.createRecord(type, hash, this);
|
521
447
|
},
|
522
448
|
|
449
|
+
/**
|
450
|
+
Adds an existing record to this transaction. Only records without
|
451
|
+
modficiations (i.e., records whose `isDirty` property is `false`)
|
452
|
+
can be added to a transaction.
|
453
|
+
|
454
|
+
@param {DS.Model} record the record to add to the transaction
|
455
|
+
*/
|
523
456
|
add: function(record) {
|
524
457
|
// we could probably make this work if someone has a valid use case. Do you?
|
525
458
|
ember_assert("Once a record has changed, you cannot move it into a different transaction", !get(record, 'isDirty'));
|
526
459
|
|
527
|
-
var
|
460
|
+
var recordTransaction = get(record, 'transaction'),
|
528
461
|
defaultTransaction = getPath(this, 'store.defaultTransaction');
|
529
462
|
|
530
|
-
ember_assert("Models cannot belong to more than one transaction at a time.",
|
463
|
+
ember_assert("Models cannot belong to more than one transaction at a time.", recordTransaction === defaultTransaction);
|
531
464
|
|
532
465
|
this.adoptRecord(record);
|
533
466
|
},
|
534
467
|
|
468
|
+
/**
|
469
|
+
Commits the transaction, which causes all of the modified records that
|
470
|
+
belong to the transaction to be sent to the adapter to be saved.
|
471
|
+
|
472
|
+
Once you call `commit()` on a transaction, you should not re-use it.
|
473
|
+
|
474
|
+
When a record is saved, it will be removed from this transaction and
|
475
|
+
moved back to the store's default transaction.
|
476
|
+
*/
|
477
|
+
commit: function() {
|
478
|
+
var self = this,
|
479
|
+
iterate;
|
480
|
+
|
481
|
+
iterate = function(bucketType, fn, binding) {
|
482
|
+
var dirty = self.bucketForType(bucketType);
|
483
|
+
|
484
|
+
dirty.forEach(function(type, records) {
|
485
|
+
if (records.isEmpty()) { return; }
|
486
|
+
|
487
|
+
var array = [];
|
488
|
+
|
489
|
+
records.forEach(function(record) {
|
490
|
+
record.send('willCommit');
|
491
|
+
|
492
|
+
if (get(record, 'isPending') === false) {
|
493
|
+
array.push(record);
|
494
|
+
}
|
495
|
+
});
|
496
|
+
|
497
|
+
fn.call(binding, type, array);
|
498
|
+
});
|
499
|
+
};
|
500
|
+
|
501
|
+
var commitDetails = {
|
502
|
+
updated: {
|
503
|
+
eachType: function(fn, binding) { iterate('updated', fn, binding); }
|
504
|
+
},
|
505
|
+
|
506
|
+
created: {
|
507
|
+
eachType: function(fn, binding) { iterate('created', fn, binding); }
|
508
|
+
},
|
509
|
+
|
510
|
+
deleted: {
|
511
|
+
eachType: function(fn, binding) { iterate('deleted', fn, binding); }
|
512
|
+
}
|
513
|
+
};
|
514
|
+
|
515
|
+
var store = get(this, 'store');
|
516
|
+
var adapter = get(store, '_adapter');
|
517
|
+
|
518
|
+
this.removeCleanRecords();
|
519
|
+
|
520
|
+
if (adapter && adapter.commit) { adapter.commit(store, commitDetails); }
|
521
|
+
else { throw fmt("Adapter is either null or do not implement `commit` method", this); }
|
522
|
+
},
|
523
|
+
|
524
|
+
/**
|
525
|
+
Rolling back a transaction resets the records that belong to
|
526
|
+
that transaction.
|
527
|
+
|
528
|
+
Updated records have their properties reset to the last known
|
529
|
+
value from the persistence layer. Deleted records are reverted
|
530
|
+
to a clean, non-deleted state. Newly created records immediately
|
531
|
+
become deleted, and are not sent to the adapter to be persisted.
|
532
|
+
|
533
|
+
After the transaction is rolled back, any records that belong
|
534
|
+
to it will return to the store's default transaction, and the
|
535
|
+
current transaction should not be used again.
|
536
|
+
*/
|
537
|
+
rollback: function() {
|
538
|
+
var store = get(this, 'store'),
|
539
|
+
dirty;
|
540
|
+
|
541
|
+
// Loop through all of the records in each of the dirty states
|
542
|
+
// and initiate a rollback on them. As a side effect of telling
|
543
|
+
// the record to roll back, it should also move itself out of
|
544
|
+
// the dirty bucket and into the clean bucket.
|
545
|
+
['created', 'updated', 'deleted'].forEach(function(bucketType) {
|
546
|
+
dirty = this.bucketForType(bucketType);
|
547
|
+
|
548
|
+
dirty.forEach(function(type, records) {
|
549
|
+
records.forEach(function(record) {
|
550
|
+
record.send('rollback');
|
551
|
+
});
|
552
|
+
});
|
553
|
+
}, this);
|
554
|
+
|
555
|
+
// Now that all records in the transaction are guaranteed to be
|
556
|
+
// clean, migrate them all to the store's default transaction.
|
557
|
+
this.removeCleanRecords();
|
558
|
+
},
|
559
|
+
|
560
|
+
/**
|
561
|
+
@private
|
562
|
+
|
563
|
+
Removes a record from this transaction and back to the store's
|
564
|
+
default transaction.
|
565
|
+
|
566
|
+
Note: This method is private for now, but should probably be exposed
|
567
|
+
in the future once we have stricter error checking (for example, in the
|
568
|
+
case of the record being dirty).
|
569
|
+
|
570
|
+
@param {DS.Model} record
|
571
|
+
*/
|
535
572
|
remove: function(record) {
|
536
573
|
var defaultTransaction = getPath(this, 'store.defaultTransaction');
|
537
|
-
|
538
574
|
defaultTransaction.adoptRecord(record);
|
539
575
|
},
|
540
576
|
|
577
|
+
/**
|
578
|
+
@private
|
579
|
+
|
580
|
+
Removes all of the records in the transaction's clean bucket.
|
581
|
+
*/
|
582
|
+
removeCleanRecords: function() {
|
583
|
+
var clean = this.bucketForType('clean'),
|
584
|
+
self = this;
|
585
|
+
|
586
|
+
clean.forEach(function(type, records) {
|
587
|
+
records.forEach(function(record) {
|
588
|
+
self.remove(record);
|
589
|
+
});
|
590
|
+
});
|
591
|
+
},
|
592
|
+
|
593
|
+
/**
|
594
|
+
@private
|
595
|
+
|
596
|
+
Returns the bucket for the given bucket type. For example, you might call
|
597
|
+
`this.bucketForType('updated')` to get the `Ember.Map` that contains all
|
598
|
+
of the records that have changes pending.
|
599
|
+
|
600
|
+
@param {String} bucketType the type of bucket
|
601
|
+
@returns Ember.Map
|
602
|
+
*/
|
603
|
+
bucketForType: function(bucketType) {
|
604
|
+
var buckets = get(this, 'buckets');
|
605
|
+
|
606
|
+
return get(buckets, bucketType);
|
607
|
+
},
|
608
|
+
|
541
609
|
/**
|
542
610
|
@private
|
543
611
|
|
@@ -549,6 +617,8 @@ DS.Transaction = Ember.Object.extend({
|
|
549
617
|
into a new transaction when the transaction is committed.
|
550
618
|
|
551
619
|
This method must not be called unless the record is clean.
|
620
|
+
|
621
|
+
@param {DS.Model} record
|
552
622
|
*/
|
553
623
|
adoptRecord: function(record) {
|
554
624
|
var oldTransaction = get(record, 'transaction');
|
@@ -561,14 +631,15 @@ DS.Transaction = Ember.Object.extend({
|
|
561
631
|
set(record, 'transaction', this);
|
562
632
|
},
|
563
633
|
|
564
|
-
|
565
|
-
|
566
|
-
this.addToBucket(kind, record);
|
567
|
-
},
|
634
|
+
/**
|
635
|
+
@private
|
568
636
|
|
569
|
-
|
570
|
-
|
571
|
-
|
637
|
+
Adds a record to the named bucket.
|
638
|
+
|
639
|
+
@param {String} bucketType one of `clean`, `created`, `updated`, or `deleted`
|
640
|
+
*/
|
641
|
+
addToBucket: function(bucketType, record) {
|
642
|
+
var bucket = this.bucketForType(bucketType),
|
572
643
|
type = record.constructor;
|
573
644
|
|
574
645
|
var records = bucket.get(type);
|
@@ -581,80 +652,58 @@ DS.Transaction = Ember.Object.extend({
|
|
581
652
|
records.add(record);
|
582
653
|
},
|
583
654
|
|
584
|
-
/**
|
585
|
-
|
586
|
-
|
655
|
+
/**
|
656
|
+
@private
|
657
|
+
|
658
|
+
Removes a record from the named bucket.
|
659
|
+
|
660
|
+
@param {String} bucketType one of `clean`, `created`, `updated`, or `deleted`
|
661
|
+
*/
|
662
|
+
removeFromBucket: function(bucketType, record) {
|
663
|
+
var bucket = this.bucketForType(bucketType),
|
587
664
|
type = record.constructor;
|
588
665
|
|
589
666
|
var records = bucket.get(type);
|
590
667
|
records.remove(record);
|
591
668
|
},
|
592
669
|
|
593
|
-
|
594
|
-
|
670
|
+
/**
|
671
|
+
@private
|
595
672
|
|
596
|
-
|
597
|
-
|
598
|
-
|
673
|
+
Called by a record's state manager to indicate that the record has entered
|
674
|
+
a dirty state. The record will be moved from the `clean` bucket and into
|
675
|
+
the appropriate dirty bucket.
|
599
676
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
dirty.forEach(function(type, models) {
|
607
|
-
if (models.isEmpty()) { return; }
|
608
|
-
|
609
|
-
var array = [];
|
610
|
-
|
611
|
-
models.forEach(function(model) {
|
612
|
-
model.send('willCommit');
|
613
|
-
|
614
|
-
if (get(model, 'isPending') === false) {
|
615
|
-
array.push(model);
|
616
|
-
}
|
617
|
-
});
|
618
|
-
|
619
|
-
fn.call(binding, type, array);
|
620
|
-
});
|
621
|
-
};
|
622
|
-
|
623
|
-
var commitDetails = {
|
624
|
-
updated: {
|
625
|
-
eachType: function(fn, binding) { iterate('updated', fn, binding); }
|
626
|
-
},
|
627
|
-
|
628
|
-
created: {
|
629
|
-
eachType: function(fn, binding) { iterate('created', fn, binding); }
|
630
|
-
},
|
631
|
-
|
632
|
-
deleted: {
|
633
|
-
eachType: function(fn, binding) { iterate('deleted', fn, binding); }
|
634
|
-
}
|
635
|
-
};
|
677
|
+
@param {String} bucketType one of `created`, `updated`, or `deleted`
|
678
|
+
*/
|
679
|
+
recordBecameDirty: function(bucketType, record) {
|
680
|
+
this.removeFromBucket('clean', record);
|
681
|
+
this.addToBucket(bucketType, record);
|
682
|
+
},
|
636
683
|
|
637
|
-
|
638
|
-
|
684
|
+
/**
|
685
|
+
@private
|
639
686
|
|
640
|
-
|
641
|
-
|
687
|
+
Called by a record's state manager to indicate that the record has entered
|
688
|
+
a clean state. The record will be moved from its current dirty bucket and into
|
689
|
+
the `clean` bucket.
|
642
690
|
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
}, this);
|
691
|
+
@param {String} bucketType one of `created`, `updated`, or `deleted`
|
692
|
+
*/
|
693
|
+
recordBecameClean: function(kind, record) {
|
694
|
+
this.removeFromBucket(kind, record);
|
648
695
|
|
649
|
-
|
650
|
-
|
696
|
+
var defaultTransaction = getPath(this, 'store.defaultTransaction');
|
697
|
+
defaultTransaction.adoptRecord(record);
|
651
698
|
}
|
652
699
|
});
|
653
700
|
|
654
|
-
})(
|
701
|
+
})();
|
655
702
|
|
656
703
|
|
657
|
-
|
704
|
+
|
705
|
+
(function() {
|
706
|
+
/*globals Ember*/
|
658
707
|
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, fmt = Ember.String.fmt;
|
659
708
|
|
660
709
|
var DATA_PROXY = {
|
@@ -663,6 +712,11 @@ var DATA_PROXY = {
|
|
663
712
|
}
|
664
713
|
};
|
665
714
|
|
715
|
+
// These values are used in the data cache when clientIds are
|
716
|
+
// needed but the underlying data has not yet been loaded by
|
717
|
+
// the server.
|
718
|
+
var UNLOADED = 'unloaded';
|
719
|
+
var LOADING = 'loading';
|
666
720
|
|
667
721
|
// Implementors Note:
|
668
722
|
//
|
@@ -677,7 +731,7 @@ var DATA_PROXY = {
|
|
677
731
|
// * +type+ means a subclass of DS.Model.
|
678
732
|
|
679
733
|
/**
|
680
|
-
The store contains all of the hashes for
|
734
|
+
The store contains all of the hashes for records loaded from the server.
|
681
735
|
It is also responsible for creating instances of DS.Model when you request one
|
682
736
|
of these data hashes, so that they can be bound to in your Handlebars templates.
|
683
737
|
|
@@ -686,9 +740,9 @@ var DATA_PROXY = {
|
|
686
740
|
MyApp.store = DS.Store.create();
|
687
741
|
|
688
742
|
You can retrieve DS.Model instances from the store in several ways. To retrieve
|
689
|
-
a
|
743
|
+
a record for a specific id, use the `find()` method:
|
690
744
|
|
691
|
-
var
|
745
|
+
var record = MyApp.store.find(MyApp.Contact, 123);
|
692
746
|
|
693
747
|
By default, the store will talk to your backend using a standard REST mechanism.
|
694
748
|
You can customize how the store talks to your backend by specifying a custom adapter:
|
@@ -726,7 +780,7 @@ DS.Store = Ember.Object.extend({
|
|
726
780
|
this.typeMaps = {};
|
727
781
|
this.recordCache = [];
|
728
782
|
this.clientIdToId = {};
|
729
|
-
this.
|
783
|
+
this.recordArraysByClientId = {};
|
730
784
|
|
731
785
|
set(this, 'defaultTransaction', this.transaction());
|
732
786
|
|
@@ -746,7 +800,7 @@ DS.Store = Ember.Object.extend({
|
|
746
800
|
/**
|
747
801
|
@private
|
748
802
|
|
749
|
-
This is used only by the
|
803
|
+
This is used only by the record's DataProxy. Do not use this directly.
|
750
804
|
*/
|
751
805
|
dataForRecord: function(record) {
|
752
806
|
var type = record.constructor,
|
@@ -783,11 +837,11 @@ DS.Store = Ember.Object.extend({
|
|
783
837
|
|
784
838
|
// A monotonically increasing number to be used to uniquely identify
|
785
839
|
// data hashes and records.
|
786
|
-
clientIdCounter:
|
840
|
+
clientIdCounter: 1,
|
787
841
|
|
788
|
-
//
|
789
|
-
// . CREATE NEW
|
790
|
-
//
|
842
|
+
// .....................
|
843
|
+
// . CREATE NEW RECORD .
|
844
|
+
// .....................
|
791
845
|
|
792
846
|
/**
|
793
847
|
Create a new record in the current store. The properties passed
|
@@ -839,14 +893,14 @@ DS.Store = Ember.Object.extend({
|
|
839
893
|
// Set the properties specified on the record.
|
840
894
|
record.setProperties(properties);
|
841
895
|
|
842
|
-
this.
|
896
|
+
this.updateRecordArrays(type, clientId, get(record, 'data'));
|
843
897
|
|
844
898
|
return record;
|
845
899
|
},
|
846
900
|
|
847
|
-
//
|
848
|
-
// . DELETE
|
849
|
-
//
|
901
|
+
// .................
|
902
|
+
// . DELETE RECORD .
|
903
|
+
// .................
|
850
904
|
|
851
905
|
/**
|
852
906
|
For symmetry, a record can be deleted via the store.
|
@@ -857,9 +911,9 @@ DS.Store = Ember.Object.extend({
|
|
857
911
|
record.send('deleteRecord');
|
858
912
|
},
|
859
913
|
|
860
|
-
//
|
861
|
-
// . FIND
|
862
|
-
//
|
914
|
+
// ................
|
915
|
+
// . FIND RECORDS .
|
916
|
+
// ................
|
863
917
|
|
864
918
|
/**
|
865
919
|
This is the main entry point into finding records. The first
|
@@ -879,7 +933,8 @@ DS.Store = Ember.Object.extend({
|
|
879
933
|
|
880
934
|
If the record with that `id` had not previously been loaded,
|
881
935
|
the store will return an empty record immediately and ask
|
882
|
-
the adapter to find the data by calling
|
936
|
+
the adapter to find the data by calling the adapter's `find`
|
937
|
+
method.
|
883
938
|
|
884
939
|
The `find` method will always return the same object for a
|
885
940
|
given type and `id`. To check whether the adapter has populated
|
@@ -893,11 +948,11 @@ DS.Store = Ember.Object.extend({
|
|
893
948
|
store.find(App.Person);
|
894
949
|
App.Person.find();
|
895
950
|
|
896
|
-
This will return a `
|
951
|
+
This will return a `RecordArray` representing all known records
|
897
952
|
for the given type and kick off a request to the adapter's
|
898
953
|
`findAll` method to load any additional records for the type.
|
899
954
|
|
900
|
-
The `
|
955
|
+
The `RecordArray` returned by `find()` is live. If any more
|
901
956
|
records for the type are added at a later time through any
|
902
957
|
mechanism, it will automatically update to reflect the change.
|
903
958
|
|
@@ -909,12 +964,12 @@ DS.Store = Ember.Object.extend({
|
|
909
964
|
store.find(App.Person, { page: 1 });
|
910
965
|
App.Person.find({ page: 1 });
|
911
966
|
|
912
|
-
This will return a `
|
913
|
-
be an empty `
|
914
|
-
`findQuery` method, which will populate the `
|
967
|
+
This will return a `RecordArray` immediately, but it will always
|
968
|
+
be an empty `RecordArray` at first. It will call the adapter's
|
969
|
+
`findQuery` method, which will populate the `RecordArray` once
|
915
970
|
the server has returned results.
|
916
971
|
|
917
|
-
You can check whether a query results `
|
972
|
+
You can check whether a query results `RecordArray` has loaded
|
918
973
|
by checking its `isLoaded` property.
|
919
974
|
*/
|
920
975
|
find: function(type, id, query) {
|
@@ -932,7 +987,7 @@ DS.Store = Ember.Object.extend({
|
|
932
987
|
return this.findMany(type, id);
|
933
988
|
}
|
934
989
|
|
935
|
-
var clientId = this.
|
990
|
+
var clientId = this.typeMapFor(type).idToCid[id];
|
936
991
|
|
937
992
|
return this.findByClientId(type, clientId, id);
|
938
993
|
},
|
@@ -940,31 +995,31 @@ DS.Store = Ember.Object.extend({
|
|
940
995
|
findByClientId: function(type, clientId, id) {
|
941
996
|
var recordCache = get(this, 'recordCache'),
|
942
997
|
dataCache = this.typeMapFor(type).cidToHash,
|
943
|
-
|
998
|
+
record;
|
944
999
|
|
945
1000
|
// If there is already a clientId assigned for this
|
946
1001
|
// type/id combination, try to find an existing
|
947
|
-
//
|
948
|
-
// materialize a new
|
1002
|
+
// record for that id and return. Otherwise,
|
1003
|
+
// materialize a new record and set its data to the
|
949
1004
|
// value we already have.
|
950
1005
|
if (clientId !== undefined) {
|
951
|
-
|
1006
|
+
record = recordCache[clientId];
|
952
1007
|
|
953
|
-
if (!
|
954
|
-
// create a new instance of the model in the
|
1008
|
+
if (!record) {
|
1009
|
+
// create a new instance of the model type in the
|
955
1010
|
// 'isLoading' state
|
956
|
-
|
1011
|
+
record = this.materializeRecord(type, clientId);
|
957
1012
|
|
958
|
-
if (dataCache[clientId]) {
|
959
|
-
|
1013
|
+
if (typeof dataCache[clientId] === 'object') {
|
1014
|
+
record.send('didChangeData');
|
960
1015
|
}
|
961
1016
|
}
|
962
1017
|
} else {
|
963
|
-
clientId = this.pushHash(
|
1018
|
+
clientId = this.pushHash(LOADING, id, type);
|
964
1019
|
|
965
|
-
// create a new instance of the model in the
|
1020
|
+
// create a new instance of the model type in the
|
966
1021
|
// 'isLoading' state
|
967
|
-
|
1022
|
+
record = this.materializeRecord(type, clientId);
|
968
1023
|
|
969
1024
|
// let the adapter set the data, possibly async
|
970
1025
|
var adapter = get(this, '_adapter');
|
@@ -972,14 +1027,29 @@ DS.Store = Ember.Object.extend({
|
|
972
1027
|
else { throw fmt("Adapter is either null or does not implement `find` method", this); }
|
973
1028
|
}
|
974
1029
|
|
975
|
-
return
|
1030
|
+
return record;
|
976
1031
|
},
|
977
1032
|
|
978
|
-
/**
|
1033
|
+
/**
|
1034
|
+
@private
|
1035
|
+
|
1036
|
+
Ask the adapter to fetch IDs that are not already loaded.
|
1037
|
+
|
1038
|
+
This method will convert `id`s to `clientId`s, filter out
|
1039
|
+
`clientId`s that already have a data hash present, and pass
|
1040
|
+
the remaining `id`s to the adapter.
|
1041
|
+
|
1042
|
+
@param {Class} type A model class
|
1043
|
+
@param {Array} ids An array of ids
|
1044
|
+
@param {Object} query
|
1045
|
+
|
1046
|
+
@returns {Array} An Array of all clientIds for the
|
1047
|
+
specified ids.
|
979
1048
|
*/
|
980
|
-
|
1049
|
+
fetchMany: function(type, ids, query) {
|
981
1050
|
var typeMap = this.typeMapFor(type),
|
982
1051
|
idToClientIdMap = typeMap.idToCid,
|
1052
|
+
dataCache = typeMap.cidToHash,
|
983
1053
|
data = typeMap.cidToHash,
|
984
1054
|
needed;
|
985
1055
|
|
@@ -989,29 +1059,56 @@ DS.Store = Ember.Object.extend({
|
|
989
1059
|
needed = [];
|
990
1060
|
|
991
1061
|
ids.forEach(function(id) {
|
1062
|
+
// Get the clientId for the given id
|
992
1063
|
var clientId = idToClientIdMap[id];
|
993
|
-
|
994
|
-
|
1064
|
+
|
1065
|
+
// If there is no `clientId` yet
|
1066
|
+
if (clientId === undefined) {
|
1067
|
+
// Create a new `clientId`, marking its data hash
|
1068
|
+
// as loading. Once the adapter returns the data
|
1069
|
+
// hash, it will be updated
|
1070
|
+
clientId = this.pushHash(LOADING, id, type);
|
1071
|
+
needed.push(id);
|
1072
|
+
|
1073
|
+
// If there is a clientId, but its data hash is
|
1074
|
+
// marked as unloaded (this happens when a
|
1075
|
+
// hasMany association creates clientIds for its
|
1076
|
+
// referenced ids before they were loaded)
|
1077
|
+
} else if (clientId && data[clientId] === UNLOADED) {
|
1078
|
+
// change the data hash marker to loading
|
1079
|
+
dataCache[clientId] = LOADING;
|
995
1080
|
needed.push(id);
|
996
1081
|
}
|
997
1082
|
|
1083
|
+
// this method is expected to return a list of
|
1084
|
+
// all of the clientIds for the specified ids,
|
1085
|
+
// unconditionally add it.
|
998
1086
|
clientIds.push(clientId);
|
999
1087
|
}, this);
|
1000
1088
|
} else {
|
1001
1089
|
needed = null;
|
1002
1090
|
}
|
1003
1091
|
|
1092
|
+
// If there are any needed ids, ask the adapter to load them
|
1004
1093
|
if ((needed && get(needed, 'length') > 0) || query) {
|
1005
1094
|
var adapter = get(this, '_adapter');
|
1006
1095
|
if (adapter && adapter.findMany) { adapter.findMany(this, type, needed, query); }
|
1007
1096
|
else { throw fmt("Adapter is either null or does not implement `findMany` method", this); }
|
1008
1097
|
}
|
1009
1098
|
|
1099
|
+
return clientIds;
|
1100
|
+
},
|
1101
|
+
|
1102
|
+
/** @private
|
1103
|
+
*/
|
1104
|
+
findMany: function(type, ids, query) {
|
1105
|
+
var clientIds = this.fetchMany(type, ids, query);
|
1106
|
+
|
1010
1107
|
return this.createManyArray(type, clientIds);
|
1011
1108
|
},
|
1012
1109
|
|
1013
1110
|
findQuery: function(type, query) {
|
1014
|
-
var array = DS.
|
1111
|
+
var array = DS.AdapterPopulatedRecordArray.create({ type: type, content: Ember.A([]), store: this });
|
1015
1112
|
var adapter = get(this, '_adapter');
|
1016
1113
|
if (adapter && adapter.findQuery) { adapter.findQuery(this, type, query, array); }
|
1017
1114
|
else { throw fmt("Adapter is either null or does not implement `findQuery` method", this); }
|
@@ -1025,8 +1122,8 @@ DS.Store = Ember.Object.extend({
|
|
1025
1122
|
|
1026
1123
|
if (findAllCache) { return findAllCache; }
|
1027
1124
|
|
1028
|
-
var array = DS.
|
1029
|
-
this.
|
1125
|
+
var array = DS.RecordArray.create({ type: type, content: Ember.A([]), store: this });
|
1126
|
+
this.registerRecordArray(array, type);
|
1030
1127
|
|
1031
1128
|
var adapter = get(this, '_adapter');
|
1032
1129
|
if (adapter && adapter.findAll) { adapter.findAll(this, type); }
|
@@ -1043,9 +1140,9 @@ DS.Store = Ember.Object.extend({
|
|
1043
1140
|
filter = query;
|
1044
1141
|
}
|
1045
1142
|
|
1046
|
-
var array = DS.
|
1143
|
+
var array = DS.FilteredRecordArray.create({ type: type, content: Ember.A([]), store: this, filterFunction: filter });
|
1047
1144
|
|
1048
|
-
this.
|
1145
|
+
this.registerRecordArray(array, type, filter);
|
1049
1146
|
|
1050
1147
|
return array;
|
1051
1148
|
},
|
@@ -1055,7 +1152,17 @@ DS.Store = Ember.Object.extend({
|
|
1055
1152
|
// ............
|
1056
1153
|
|
1057
1154
|
hashWasUpdated: function(type, clientId, record) {
|
1058
|
-
|
1155
|
+
// Because hash updates are invoked at the end of the run loop,
|
1156
|
+
// it is possible that a record might be deleted after its hash
|
1157
|
+
// has been modified and this method was scheduled to be called.
|
1158
|
+
//
|
1159
|
+
// If that's the case, the record would have already been removed
|
1160
|
+
// from all record arrays; calling updateRecordArrays would just
|
1161
|
+
// add it back. If the record is deleted, just bail. It shouldn't
|
1162
|
+
// give us any more trouble after this.
|
1163
|
+
|
1164
|
+
if (get(record, 'isDeleted')) { return; }
|
1165
|
+
this.updateRecordArrays(type, clientId, get(record, 'data'));
|
1059
1166
|
},
|
1060
1167
|
|
1061
1168
|
// ..............
|
@@ -1071,37 +1178,37 @@ DS.Store = Ember.Object.extend({
|
|
1071
1178
|
|
1072
1179
|
didUpdateRecords: function(array, hashes) {
|
1073
1180
|
if (hashes) {
|
1074
|
-
array.forEach(function(
|
1075
|
-
this.didUpdateRecord(
|
1181
|
+
array.forEach(function(record, idx) {
|
1182
|
+
this.didUpdateRecord(record, hashes[idx]);
|
1076
1183
|
}, this);
|
1077
1184
|
} else {
|
1078
|
-
array.forEach(function(
|
1079
|
-
this.didUpdateRecord(
|
1185
|
+
array.forEach(function(record) {
|
1186
|
+
this.didUpdateRecord(record);
|
1080
1187
|
}, this);
|
1081
1188
|
}
|
1082
1189
|
},
|
1083
1190
|
|
1084
|
-
didUpdateRecord: function(
|
1191
|
+
didUpdateRecord: function(record, hash) {
|
1085
1192
|
if (hash) {
|
1086
|
-
var clientId = get(
|
1087
|
-
dataCache = this.typeMapFor(
|
1193
|
+
var clientId = get(record, 'clientId'),
|
1194
|
+
dataCache = this.typeMapFor(record.constructor).cidToHash;
|
1088
1195
|
|
1089
1196
|
dataCache[clientId] = hash;
|
1090
|
-
|
1091
|
-
|
1197
|
+
record.send('didChangeData');
|
1198
|
+
record.hashWasUpdated();
|
1092
1199
|
}
|
1093
1200
|
|
1094
|
-
|
1201
|
+
record.send('didCommit');
|
1095
1202
|
},
|
1096
1203
|
|
1097
1204
|
didDeleteRecords: function(array) {
|
1098
|
-
array.forEach(function(
|
1099
|
-
|
1205
|
+
array.forEach(function(record) {
|
1206
|
+
record.send('didCommit');
|
1100
1207
|
});
|
1101
1208
|
},
|
1102
1209
|
|
1103
|
-
didDeleteRecord: function(
|
1104
|
-
|
1210
|
+
didDeleteRecord: function(record) {
|
1211
|
+
record.send('didCommit');
|
1105
1212
|
},
|
1106
1213
|
|
1107
1214
|
_didCreateRecord: function(record, hash, typeMap, clientId, primaryKey) {
|
@@ -1133,20 +1240,20 @@ DS.Store = Ember.Object.extend({
|
|
1133
1240
|
didCreateRecords: function(type, array, hashes) {
|
1134
1241
|
var primaryKey = type.proto().primaryKey,
|
1135
1242
|
typeMap = this.typeMapFor(type),
|
1136
|
-
|
1243
|
+
clientId;
|
1137
1244
|
|
1138
1245
|
for (var i=0, l=get(array, 'length'); i<l; i++) {
|
1139
|
-
var
|
1140
|
-
clientId = get(
|
1246
|
+
var record = array[i], hash = hashes[i];
|
1247
|
+
clientId = get(record, 'clientId');
|
1141
1248
|
|
1142
|
-
this._didCreateRecord(
|
1249
|
+
this._didCreateRecord(record, hash, typeMap, clientId, primaryKey);
|
1143
1250
|
}
|
1144
1251
|
},
|
1145
1252
|
|
1146
|
-
didCreateRecord: function(
|
1147
|
-
var type =
|
1253
|
+
didCreateRecord: function(record, hash) {
|
1254
|
+
var type = record.constructor,
|
1148
1255
|
typeMap = this.typeMapFor(type),
|
1149
|
-
|
1256
|
+
clientId, primaryKey;
|
1150
1257
|
|
1151
1258
|
// The hash is optional, but if it is not provided, the client must have
|
1152
1259
|
// provided a primary key.
|
@@ -1157,42 +1264,42 @@ DS.Store = Ember.Object.extend({
|
|
1157
1264
|
if (hash) {
|
1158
1265
|
ember_assert("The server must provide a primary key: " + primaryKey, get(hash, primaryKey));
|
1159
1266
|
} else {
|
1160
|
-
ember_assert("The server did not return data, and you did not create a primary key (" + primaryKey + ") on the client", get(get(
|
1267
|
+
ember_assert("The server did not return data, and you did not create a primary key (" + primaryKey + ") on the client", get(get(record, 'data'), primaryKey));
|
1161
1268
|
}
|
1162
1269
|
|
1163
|
-
clientId = get(
|
1270
|
+
clientId = get(record, 'clientId');
|
1164
1271
|
|
1165
|
-
this._didCreateRecord(
|
1272
|
+
this._didCreateRecord(record, hash, typeMap, clientId, primaryKey);
|
1166
1273
|
},
|
1167
1274
|
|
1168
1275
|
recordWasInvalid: function(record, errors) {
|
1169
1276
|
record.send('becameInvalid', errors);
|
1170
1277
|
},
|
1171
1278
|
|
1172
|
-
//
|
1173
|
-
// .
|
1174
|
-
//
|
1279
|
+
// .................
|
1280
|
+
// . RECORD ARRAYS .
|
1281
|
+
// .................
|
1175
1282
|
|
1176
|
-
|
1177
|
-
var
|
1283
|
+
registerRecordArray: function(array, type, filter) {
|
1284
|
+
var recordArrays = this.typeMapFor(type).recordArrays;
|
1178
1285
|
|
1179
|
-
|
1286
|
+
recordArrays.push(array);
|
1180
1287
|
|
1181
|
-
this.
|
1288
|
+
this.updateRecordArrayFilter(array, type, filter);
|
1182
1289
|
},
|
1183
1290
|
|
1184
1291
|
createManyArray: function(type, clientIds) {
|
1185
1292
|
var array = DS.ManyArray.create({ type: type, content: clientIds, store: this });
|
1186
1293
|
|
1187
1294
|
clientIds.forEach(function(clientId) {
|
1188
|
-
var
|
1189
|
-
|
1295
|
+
var recordArrays = this.recordArraysForClientId(clientId);
|
1296
|
+
recordArrays.add(array);
|
1190
1297
|
}, this);
|
1191
1298
|
|
1192
1299
|
return array;
|
1193
1300
|
},
|
1194
1301
|
|
1195
|
-
|
1302
|
+
updateRecordArrayFilter: function(array, type, filter) {
|
1196
1303
|
var typeMap = this.typeMapFor(type),
|
1197
1304
|
dataCache = typeMap.cidToHash,
|
1198
1305
|
clientIds = typeMap.clientIds,
|
@@ -1203,7 +1310,8 @@ DS.Store = Ember.Object.extend({
|
|
1203
1310
|
for (var i=0, l=clientIds.length; i<l; i++) {
|
1204
1311
|
clientId = clientIds[i];
|
1205
1312
|
|
1206
|
-
|
1313
|
+
hash = dataCache[clientId];
|
1314
|
+
if (typeof hash === 'object') {
|
1207
1315
|
if (record = recordCache[clientId]) {
|
1208
1316
|
proxy = get(record, 'data');
|
1209
1317
|
} else {
|
@@ -1211,22 +1319,22 @@ DS.Store = Ember.Object.extend({
|
|
1211
1319
|
proxy = DATA_PROXY;
|
1212
1320
|
}
|
1213
1321
|
|
1214
|
-
this.
|
1322
|
+
this.updateRecordArray(array, filter, type, clientId, proxy);
|
1215
1323
|
}
|
1216
1324
|
}
|
1217
1325
|
},
|
1218
1326
|
|
1219
|
-
|
1220
|
-
var
|
1221
|
-
|
1327
|
+
updateRecordArrays: function(type, clientId, dataProxy) {
|
1328
|
+
var recordArrays = this.typeMapFor(type).recordArrays,
|
1329
|
+
filter;
|
1222
1330
|
|
1223
|
-
|
1331
|
+
recordArrays.forEach(function(array) {
|
1224
1332
|
filter = get(array, 'filterFunction');
|
1225
|
-
this.
|
1333
|
+
this.updateRecordArray(array, filter, type, clientId, dataProxy);
|
1226
1334
|
}, this);
|
1227
1335
|
},
|
1228
1336
|
|
1229
|
-
|
1337
|
+
updateRecordArray: function(array, filter, type, clientId, dataProxy) {
|
1230
1338
|
var shouldBeInArray;
|
1231
1339
|
|
1232
1340
|
if (!filter) {
|
@@ -1238,22 +1346,22 @@ DS.Store = Ember.Object.extend({
|
|
1238
1346
|
var content = get(array, 'content');
|
1239
1347
|
var alreadyInArray = content.indexOf(clientId) !== -1;
|
1240
1348
|
|
1241
|
-
var
|
1349
|
+
var recordArrays = this.recordArraysForClientId(clientId);
|
1242
1350
|
|
1243
1351
|
if (shouldBeInArray && !alreadyInArray) {
|
1244
|
-
|
1352
|
+
recordArrays.add(array);
|
1245
1353
|
content.pushObject(clientId);
|
1246
1354
|
} else if (!shouldBeInArray && alreadyInArray) {
|
1247
|
-
|
1355
|
+
recordArrays.remove(array);
|
1248
1356
|
content.removeObject(clientId);
|
1249
1357
|
}
|
1250
1358
|
},
|
1251
1359
|
|
1252
|
-
|
1253
|
-
var clientId = get(
|
1254
|
-
var
|
1360
|
+
removeFromRecordArrays: function(record) {
|
1361
|
+
var clientId = get(record, 'clientId');
|
1362
|
+
var recordArrays = this.recordArraysForClientId(clientId);
|
1255
1363
|
|
1256
|
-
|
1364
|
+
recordArrays.forEach(function(array) {
|
1257
1365
|
var content = get(array, 'content');
|
1258
1366
|
content.removeObject(clientId);
|
1259
1367
|
});
|
@@ -1263,12 +1371,12 @@ DS.Store = Ember.Object.extend({
|
|
1263
1371
|
// . INDEXING .
|
1264
1372
|
// ............
|
1265
1373
|
|
1266
|
-
|
1267
|
-
var
|
1268
|
-
var ret =
|
1374
|
+
recordArraysForClientId: function(clientId) {
|
1375
|
+
var recordArrays = get(this, 'recordArraysByClientId');
|
1376
|
+
var ret = recordArrays[clientId];
|
1269
1377
|
|
1270
1378
|
if (!ret) {
|
1271
|
-
ret =
|
1379
|
+
ret = recordArrays[clientId] = Ember.OrderedSet.create();
|
1272
1380
|
}
|
1273
1381
|
|
1274
1382
|
return ret;
|
@@ -1288,7 +1396,7 @@ DS.Store = Ember.Object.extend({
|
|
1288
1396
|
idToCid: {},
|
1289
1397
|
clientIds: [],
|
1290
1398
|
cidToHash: {},
|
1291
|
-
|
1399
|
+
recordArrays: []
|
1292
1400
|
});
|
1293
1401
|
}
|
1294
1402
|
},
|
@@ -1296,13 +1404,17 @@ DS.Store = Ember.Object.extend({
|
|
1296
1404
|
/** @private
|
1297
1405
|
|
1298
1406
|
For a given type and id combination, returns the client id used by the store.
|
1299
|
-
If no client id has been assigned yet,
|
1407
|
+
If no client id has been assigned yet, one will be created and returned.
|
1300
1408
|
|
1301
1409
|
@param {DS.Model} type
|
1302
1410
|
@param {String|Number} id
|
1303
1411
|
*/
|
1304
1412
|
clientIdForId: function(type, id) {
|
1305
|
-
|
1413
|
+
var clientId = this.typeMapFor(type).idToCid[id];
|
1414
|
+
|
1415
|
+
if (clientId !== undefined) { return clientId; }
|
1416
|
+
|
1417
|
+
return this.pushHash(UNLOADED, id, type);
|
1306
1418
|
},
|
1307
1419
|
|
1308
1420
|
// ................
|
@@ -1311,10 +1423,10 @@ DS.Store = Ember.Object.extend({
|
|
1311
1423
|
|
1312
1424
|
/**
|
1313
1425
|
Load a new data hash into the store for a given id and type combination.
|
1314
|
-
If data for that
|
1426
|
+
If data for that record had been loaded previously, the new information
|
1315
1427
|
overwrites the old.
|
1316
1428
|
|
1317
|
-
If the
|
1429
|
+
If the record you are loading data for has outstanding changes that have not
|
1318
1430
|
yet been saved, an exception will be thrown.
|
1319
1431
|
|
1320
1432
|
@param {DS.Model} type
|
@@ -1325,7 +1437,7 @@ DS.Store = Ember.Object.extend({
|
|
1325
1437
|
if (hash === undefined) {
|
1326
1438
|
hash = id;
|
1327
1439
|
var primaryKey = type.proto().primaryKey;
|
1328
|
-
ember_assert("A data hash was loaded for a
|
1440
|
+
ember_assert("A data hash was loaded for a record of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", primaryKey in hash);
|
1329
1441
|
id = hash[primaryKey];
|
1330
1442
|
}
|
1331
1443
|
|
@@ -1337,16 +1449,16 @@ DS.Store = Ember.Object.extend({
|
|
1337
1449
|
if (clientId !== undefined) {
|
1338
1450
|
dataCache[clientId] = hash;
|
1339
1451
|
|
1340
|
-
var
|
1341
|
-
if (
|
1342
|
-
|
1452
|
+
var record = recordCache[clientId];
|
1453
|
+
if (record) {
|
1454
|
+
record.send('didChangeData');
|
1343
1455
|
}
|
1344
1456
|
} else {
|
1345
1457
|
clientId = this.pushHash(hash, id, type);
|
1346
1458
|
}
|
1347
1459
|
|
1348
1460
|
DATA_PROXY.savedData = hash;
|
1349
|
-
this.
|
1461
|
+
this.updateRecordArrays(type, clientId, DATA_PROXY);
|
1350
1462
|
|
1351
1463
|
return { id: id, clientId: clientId };
|
1352
1464
|
},
|
@@ -1406,22 +1518,22 @@ DS.Store = Ember.Object.extend({
|
|
1406
1518
|
return clientId;
|
1407
1519
|
},
|
1408
1520
|
|
1409
|
-
//
|
1410
|
-
// .
|
1411
|
-
//
|
1521
|
+
// ..........................
|
1522
|
+
// . RECORD MATERIALIZATION .
|
1523
|
+
// ..........................
|
1412
1524
|
|
1413
1525
|
materializeRecord: function(type, clientId) {
|
1414
|
-
var
|
1526
|
+
var record;
|
1415
1527
|
|
1416
|
-
get(this, 'recordCache')[clientId] =
|
1528
|
+
get(this, 'recordCache')[clientId] = record = type._create({
|
1417
1529
|
store: this,
|
1418
1530
|
clientId: clientId
|
1419
1531
|
});
|
1420
1532
|
|
1421
|
-
get(this, 'defaultTransaction').adoptRecord(
|
1533
|
+
get(this, 'defaultTransaction').adoptRecord(record);
|
1422
1534
|
|
1423
|
-
|
1424
|
-
return
|
1535
|
+
record.send('loadingData');
|
1536
|
+
return record;
|
1425
1537
|
},
|
1426
1538
|
|
1427
1539
|
destroy: function() {
|
@@ -1433,12 +1545,164 @@ DS.Store = Ember.Object.extend({
|
|
1433
1545
|
}
|
1434
1546
|
});
|
1435
1547
|
|
1436
|
-
})(
|
1548
|
+
})();
|
1437
1549
|
|
1438
1550
|
|
1439
|
-
|
1551
|
+
|
1552
|
+
(function() {
|
1440
1553
|
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, guidFor = Ember.guidFor;
|
1441
1554
|
|
1555
|
+
/**
|
1556
|
+
This file encapsulates the various states that a record can transition
|
1557
|
+
through during its lifecycle.
|
1558
|
+
|
1559
|
+
### State Manager
|
1560
|
+
|
1561
|
+
A record's state manager explicitly tracks what state a record is in
|
1562
|
+
at any given time. For instance, if a record is newly created and has
|
1563
|
+
not yet been sent to the adapter to be saved, it would be in the
|
1564
|
+
`created.uncommitted` state. If a record has had local modifications
|
1565
|
+
made to it that are in the process of being saved, the record would be
|
1566
|
+
in the `updated.inFlight` state. (These state paths will be explained
|
1567
|
+
in more detail below.)
|
1568
|
+
|
1569
|
+
Events are sent by the record or its store to the record's state manager.
|
1570
|
+
How the state manager reacts to these events is dependent on which state
|
1571
|
+
it is in. In some states, certain events will be invalid and will cause
|
1572
|
+
an exception to be raised.
|
1573
|
+
|
1574
|
+
States are hierarchical. For example, a record can be in the
|
1575
|
+
`deleted.start` state, then transition into the `deleted.inFlight` state.
|
1576
|
+
If a child state does not implement an event handler, the state manager
|
1577
|
+
will attempt to invoke the event on all parent states until the root state is
|
1578
|
+
reached. The state hierarchy of a record is described in terms of a path
|
1579
|
+
string. You can determine a record's current state by getting its manager's
|
1580
|
+
current state path:
|
1581
|
+
|
1582
|
+
record.getPath('manager.currentState.path');
|
1583
|
+
//=> "created.uncommitted"
|
1584
|
+
|
1585
|
+
The `DS.Model` states are themselves stateless. What we mean is that,
|
1586
|
+
though each instance of a record also has a unique instance of a
|
1587
|
+
`DS.StateManager`, the hierarchical states that each of *those* points
|
1588
|
+
to is a shared data structure. For performance reasons, instead of each
|
1589
|
+
record getting its own copy of the hierarchy of states, each state
|
1590
|
+
manager points to this global, immutable shared instance. How does a
|
1591
|
+
state know which record it should be acting on? We pass a reference to
|
1592
|
+
the current state manager as the first parameter to every method invoked
|
1593
|
+
on a state.
|
1594
|
+
|
1595
|
+
The state manager passed as the first parameter is where you should stash
|
1596
|
+
state about the record if needed; you should never store data on the state
|
1597
|
+
object itself. If you need access to the record being acted on, you can
|
1598
|
+
retrieve the state manager's `record` property. For example, if you had
|
1599
|
+
an event handler `myEvent`:
|
1600
|
+
|
1601
|
+
myEvent: function(manager) {
|
1602
|
+
var record = manager.get('record');
|
1603
|
+
record.doSomething();
|
1604
|
+
}
|
1605
|
+
|
1606
|
+
For more information about state managers in general, see the Ember.js
|
1607
|
+
documentation on `Ember.StateManager`.
|
1608
|
+
|
1609
|
+
### Events, Flags, and Transitions
|
1610
|
+
|
1611
|
+
A state may implement zero or more events, flags, or transitions.
|
1612
|
+
|
1613
|
+
#### Events
|
1614
|
+
|
1615
|
+
Events are named functions that are invoked when sent to a record. The
|
1616
|
+
state manager will first look for a method with the given name on the
|
1617
|
+
current state. If no method is found, it will search the current state's
|
1618
|
+
parent, and then its grandparent, and so on until reaching the top of
|
1619
|
+
the hierarchy. If the root is reached without an event handler being found,
|
1620
|
+
an exception will be raised. This can be very helpful when debugging new
|
1621
|
+
features.
|
1622
|
+
|
1623
|
+
Here's an example implementation of a state with a `myEvent` event handler:
|
1624
|
+
|
1625
|
+
aState: DS.State.create({
|
1626
|
+
myEvent: function(manager, param) {
|
1627
|
+
console.log("Received myEvent with "+param);
|
1628
|
+
}
|
1629
|
+
})
|
1630
|
+
|
1631
|
+
To trigger this event:
|
1632
|
+
|
1633
|
+
record.send('myEvent', 'foo');
|
1634
|
+
//=> "Received myEvent with foo"
|
1635
|
+
|
1636
|
+
Note that an optional parameter can be sent to a record's `send()` method,
|
1637
|
+
which will be passed as the second parameter to the event handler.
|
1638
|
+
|
1639
|
+
Events should transition to a different state if appropriate. This can be
|
1640
|
+
done by calling the state manager's `goToState()` method with a path to the
|
1641
|
+
desired state. The state manager will attempt to resolve the state path
|
1642
|
+
relative to the current state. If no state is found at that path, it will
|
1643
|
+
attempt to resolve it relative to the current state's parent, and then its
|
1644
|
+
parent, and so on until the root is reached. For example, imagine a hierarchy
|
1645
|
+
like this:
|
1646
|
+
|
1647
|
+
* created
|
1648
|
+
* start <-- currentState
|
1649
|
+
* inFlight
|
1650
|
+
* updated
|
1651
|
+
* inFlight
|
1652
|
+
|
1653
|
+
If we are currently in the `start` state, calling
|
1654
|
+
`goToState('inFlight')` would transition to the `created.inFlight` state,
|
1655
|
+
while calling `goToState('updated.inFlight')` would transition to
|
1656
|
+
the `updated.inFlight` state.
|
1657
|
+
|
1658
|
+
Remember that *only events* should ever cause a state transition. You should
|
1659
|
+
never call `goToState()` from outside a state's event handler. If you are
|
1660
|
+
tempted to do so, create a new event and send that to the state manager.
|
1661
|
+
|
1662
|
+
#### Flags
|
1663
|
+
|
1664
|
+
Flags are Boolean values that can be used to introspect a record's current
|
1665
|
+
state in a more user-friendly way than examining its state path. For example,
|
1666
|
+
instead of doing this:
|
1667
|
+
|
1668
|
+
var statePath = record.getPath('stateManager.currentState.path');
|
1669
|
+
if (statePath === 'created.inFlight') {
|
1670
|
+
doSomething();
|
1671
|
+
}
|
1672
|
+
|
1673
|
+
You can say:
|
1674
|
+
|
1675
|
+
if (record.get('isNew') && record.get('isSaving')) {
|
1676
|
+
doSomething();
|
1677
|
+
}
|
1678
|
+
|
1679
|
+
If your state does not set a value for a given flag, the value will
|
1680
|
+
be inherited from its parent (or the first place in the state hierarchy
|
1681
|
+
where it is defined).
|
1682
|
+
|
1683
|
+
The current set of flags are defined below. If you want to add a new flag,
|
1684
|
+
in addition to the area below, you will also need to declare it in the
|
1685
|
+
`DS.Model` class.
|
1686
|
+
|
1687
|
+
#### Transitions
|
1688
|
+
|
1689
|
+
Transitions are like event handlers but are called automatically upon
|
1690
|
+
entering or exiting a state. To implement a transition, just call a method
|
1691
|
+
either `enter` or `exit`:
|
1692
|
+
|
1693
|
+
myState: DS.State.create({
|
1694
|
+
// Gets called automatically when entering
|
1695
|
+
// this state.
|
1696
|
+
enter: function(manager) {
|
1697
|
+
console.log("Entered myState");
|
1698
|
+
}
|
1699
|
+
})
|
1700
|
+
|
1701
|
+
Note that enter and exit events are called once per transition. If the
|
1702
|
+
current state changes, but changes to another child state of the parent,
|
1703
|
+
the transition event on the parent will not be triggered.
|
1704
|
+
*/
|
1705
|
+
|
1442
1706
|
var stateProperty = Ember.computed(function(key) {
|
1443
1707
|
var parent = get(this, 'parentState');
|
1444
1708
|
if (parent) {
|
@@ -1482,18 +1746,27 @@ DS.State = Ember.State.extend({
|
|
1482
1746
|
var setProperty = function(manager, context) {
|
1483
1747
|
var key = context.key, value = context.value;
|
1484
1748
|
|
1485
|
-
var
|
1486
|
-
data = get(
|
1749
|
+
var record = get(manager, 'record'),
|
1750
|
+
data = get(record, 'data');
|
1487
1751
|
|
1488
1752
|
set(data, key, value);
|
1489
1753
|
};
|
1490
1754
|
|
1755
|
+
var setAssociation = function(manager, context) {
|
1756
|
+
var key = context.key, value = context.value;
|
1757
|
+
|
1758
|
+
var record = get(manager, 'record'),
|
1759
|
+
data = get(record, 'data');
|
1760
|
+
|
1761
|
+
data.setAssociation(key, value);
|
1762
|
+
};
|
1763
|
+
|
1491
1764
|
var didChangeData = function(manager) {
|
1492
|
-
var
|
1493
|
-
data = get(
|
1765
|
+
var record = get(manager, 'record'),
|
1766
|
+
data = get(record, 'data');
|
1494
1767
|
|
1495
1768
|
data._savedData = null;
|
1496
|
-
|
1769
|
+
record.notifyPropertyChange('data');
|
1497
1770
|
};
|
1498
1771
|
|
1499
1772
|
// The waitingOn event shares common functionality
|
@@ -1503,8 +1776,8 @@ var didChangeData = function(manager) {
|
|
1503
1776
|
// behavior, and then implement the behavior specific
|
1504
1777
|
// to the state.
|
1505
1778
|
var waitingOn = function(manager, object) {
|
1506
|
-
var
|
1507
|
-
pendingQueue = get(
|
1779
|
+
var record = get(manager, 'record'),
|
1780
|
+
pendingQueue = get(record, 'pendingQueue'),
|
1508
1781
|
objectGuid = guidFor(object);
|
1509
1782
|
|
1510
1783
|
var observer = function() {
|
@@ -1559,17 +1832,7 @@ var waitingOn = function(manager, object) {
|
|
1559
1832
|
// super points to the class definition.
|
1560
1833
|
var Uncommitted = Ember.Mixin.create({
|
1561
1834
|
setProperty: setProperty,
|
1562
|
-
|
1563
|
-
deleteRecord: function(manager) {
|
1564
|
-
this._super(manager);
|
1565
|
-
|
1566
|
-
var model = get(manager, 'model'),
|
1567
|
-
dirtyType = get(this, 'dirtyType');
|
1568
|
-
|
1569
|
-
model.withTransaction(function(t) {
|
1570
|
-
t.modelBecameClean(dirtyType, model);
|
1571
|
-
});
|
1572
|
-
}
|
1835
|
+
setAssociation: setAssociation,
|
1573
1836
|
});
|
1574
1837
|
|
1575
1838
|
// These mixins are mixed into substates of the concrete
|
@@ -1577,8 +1840,12 @@ var Uncommitted = Ember.Mixin.create({
|
|
1577
1840
|
|
1578
1841
|
var CreatedUncommitted = Ember.Mixin.create({
|
1579
1842
|
deleteRecord: function(manager) {
|
1843
|
+
var record = get(manager, 'record');
|
1580
1844
|
this._super(manager);
|
1581
1845
|
|
1846
|
+
record.withTransaction(function(t) {
|
1847
|
+
t.recordBecameClean('created', record);
|
1848
|
+
});
|
1582
1849
|
manager.goToState('deleted.saved');
|
1583
1850
|
}
|
1584
1851
|
});
|
@@ -1587,10 +1854,10 @@ var UpdatedUncommitted = Ember.Mixin.create({
|
|
1587
1854
|
deleteRecord: function(manager) {
|
1588
1855
|
this._super(manager);
|
1589
1856
|
|
1590
|
-
var
|
1857
|
+
var record = get(manager, 'record');
|
1591
1858
|
|
1592
|
-
|
1593
|
-
t.
|
1859
|
+
record.withTransaction(function(t) {
|
1860
|
+
t.recordBecameClean('updated', record);
|
1594
1861
|
});
|
1595
1862
|
|
1596
1863
|
manager.goToState('deleted');
|
@@ -1618,16 +1885,16 @@ var DirtyState = DS.State.extend({
|
|
1618
1885
|
// TRANSITIONS
|
1619
1886
|
enter: function(manager) {
|
1620
1887
|
var dirtyType = get(this, 'dirtyType'),
|
1621
|
-
|
1888
|
+
record = get(manager, 'record');
|
1622
1889
|
|
1623
|
-
|
1624
|
-
t.
|
1890
|
+
record.withTransaction(function (t) {
|
1891
|
+
t.recordBecameDirty(dirtyType, record);
|
1625
1892
|
});
|
1626
1893
|
},
|
1627
1894
|
|
1628
1895
|
exit: function(manager) {
|
1629
|
-
var
|
1630
|
-
manager.send('invokeLifecycleCallbacks',
|
1896
|
+
var record = get(manager, 'record');
|
1897
|
+
manager.send('invokeLifecycleCallbacks', record);
|
1631
1898
|
},
|
1632
1899
|
|
1633
1900
|
// EVENTS
|
@@ -1640,6 +1907,20 @@ var DirtyState = DS.State.extend({
|
|
1640
1907
|
|
1641
1908
|
willCommit: function(manager) {
|
1642
1909
|
manager.goToState('inFlight');
|
1910
|
+
},
|
1911
|
+
|
1912
|
+
rollback: function(manager) {
|
1913
|
+
var record = get(manager, 'record'),
|
1914
|
+
dirtyType = get(this, 'dirtyType'),
|
1915
|
+
data = get(record, 'data');
|
1916
|
+
|
1917
|
+
data.rollback();
|
1918
|
+
|
1919
|
+
record.withTransaction(function(t) {
|
1920
|
+
t.recordBecameClean(dirtyType, record);
|
1921
|
+
});
|
1922
|
+
|
1923
|
+
manager.goToState('loaded');
|
1643
1924
|
}
|
1644
1925
|
}, Uncommitted),
|
1645
1926
|
|
@@ -1653,10 +1934,10 @@ var DirtyState = DS.State.extend({
|
|
1653
1934
|
// TRANSITIONS
|
1654
1935
|
enter: function(manager) {
|
1655
1936
|
var dirtyType = get(this, 'dirtyType'),
|
1656
|
-
|
1937
|
+
record = get(manager, 'record');
|
1657
1938
|
|
1658
|
-
|
1659
|
-
t.
|
1939
|
+
record.withTransaction(function (t) {
|
1940
|
+
t.recordBecameClean(dirtyType, record);
|
1660
1941
|
});
|
1661
1942
|
},
|
1662
1943
|
|
@@ -1666,9 +1947,9 @@ var DirtyState = DS.State.extend({
|
|
1666
1947
|
},
|
1667
1948
|
|
1668
1949
|
becameInvalid: function(manager, errors) {
|
1669
|
-
var
|
1950
|
+
var record = get(manager, 'record');
|
1670
1951
|
|
1671
|
-
set(
|
1952
|
+
set(record, 'errors', errors);
|
1672
1953
|
manager.goToState('invalid');
|
1673
1954
|
},
|
1674
1955
|
|
@@ -1698,8 +1979,8 @@ var DirtyState = DS.State.extend({
|
|
1698
1979
|
uncommitted: DS.State.extend({
|
1699
1980
|
// EVENTS
|
1700
1981
|
deleteRecord: function(manager) {
|
1701
|
-
var
|
1702
|
-
pendingQueue = get(
|
1982
|
+
var record = get(manager, 'record'),
|
1983
|
+
pendingQueue = get(record, 'pendingQueue'),
|
1703
1984
|
tuple;
|
1704
1985
|
|
1705
1986
|
// since we are leaving the pending state, remove any
|
@@ -1717,8 +1998,8 @@ var DirtyState = DS.State.extend({
|
|
1717
1998
|
},
|
1718
1999
|
|
1719
2000
|
doneWaitingOn: function(manager, object) {
|
1720
|
-
var
|
1721
|
-
pendingQueue = get(
|
2001
|
+
var record = get(manager, 'record'),
|
2002
|
+
pendingQueue = get(record, 'pendingQueue'),
|
1722
2003
|
objectGuid = guidFor(object);
|
1723
2004
|
|
1724
2005
|
delete pendingQueue[objectGuid];
|
@@ -1744,8 +2025,8 @@ var DirtyState = DS.State.extend({
|
|
1744
2025
|
|
1745
2026
|
// EVENTS
|
1746
2027
|
doneWaitingOn: function(manager, object) {
|
1747
|
-
var
|
1748
|
-
pendingQueue = get(
|
2028
|
+
var record = get(manager, 'record'),
|
2029
|
+
pendingQueue = get(record, 'pendingQueue'),
|
1749
2030
|
objectGuid = guidFor(object);
|
1750
2031
|
|
1751
2032
|
delete pendingQueue[objectGuid];
|
@@ -1756,10 +2037,10 @@ var DirtyState = DS.State.extend({
|
|
1756
2037
|
},
|
1757
2038
|
|
1758
2039
|
doneWaiting: function(manager) {
|
1759
|
-
var
|
1760
|
-
transaction = get(
|
2040
|
+
var record = get(manager, 'record'),
|
2041
|
+
transaction = get(record, 'transaction');
|
1761
2042
|
|
1762
|
-
// Now that the
|
2043
|
+
// Now that the record is no longer pending, schedule
|
1763
2044
|
// the transaction to commit.
|
1764
2045
|
Ember.run.once(transaction, transaction.commit);
|
1765
2046
|
},
|
@@ -1783,11 +2064,13 @@ var DirtyState = DS.State.extend({
|
|
1783
2064
|
manager.goToState('deleted');
|
1784
2065
|
},
|
1785
2066
|
|
2067
|
+
setAssociation: setAssociation,
|
2068
|
+
|
1786
2069
|
setProperty: function(manager, context) {
|
1787
2070
|
setProperty(manager, context);
|
1788
2071
|
|
1789
|
-
var
|
1790
|
-
errors = get(
|
2072
|
+
var record = get(manager, 'record'),
|
2073
|
+
errors = get(record, 'errors'),
|
1791
2074
|
key = context.key;
|
1792
2075
|
|
1793
2076
|
delete errors[key];
|
@@ -1814,8 +2097,8 @@ var createdState = DirtyState.create({
|
|
1814
2097
|
isNew: true,
|
1815
2098
|
|
1816
2099
|
// EVENTS
|
1817
|
-
invokeLifecycleCallbacks: function(manager,
|
1818
|
-
|
2100
|
+
invokeLifecycleCallbacks: function(manager, record) {
|
2101
|
+
record.fire('didCreate');
|
1819
2102
|
}
|
1820
2103
|
});
|
1821
2104
|
|
@@ -1823,8 +2106,8 @@ var updatedState = DirtyState.create({
|
|
1823
2106
|
dirtyType: 'updated',
|
1824
2107
|
|
1825
2108
|
// EVENTS
|
1826
|
-
invokeLifecycleCallbacks: function(manager,
|
1827
|
-
|
2109
|
+
invokeLifecycleCallbacks: function(manager, record) {
|
2110
|
+
record.fire('didUpdate');
|
1828
2111
|
}
|
1829
2112
|
});
|
1830
2113
|
|
@@ -1833,6 +2116,15 @@ var updatedState = DirtyState.create({
|
|
1833
2116
|
createdState.states.uncommitted.reopen(CreatedUncommitted);
|
1834
2117
|
createdState.states.pending.states.uncommitted.reopen(CreatedUncommitted);
|
1835
2118
|
|
2119
|
+
// The created.uncommitted state needs to immediately transition to the
|
2120
|
+
// deleted state if it is rolled back.
|
2121
|
+
createdState.states.uncommitted.reopen({
|
2122
|
+
rollback: function(manager) {
|
2123
|
+
this._super(manager);
|
2124
|
+
manager.goToState('deleted.saved');
|
2125
|
+
}
|
2126
|
+
});
|
2127
|
+
|
1836
2128
|
// The updated.uncommitted state and updated.pending.uncommitted share
|
1837
2129
|
// some logic defined in UpdatedUncommitted.
|
1838
2130
|
updatedState.states.uncommitted.reopen(UpdatedUncommitted);
|
@@ -1879,8 +2171,8 @@ var states = {
|
|
1879
2171
|
loading: DS.State.create({
|
1880
2172
|
// TRANSITIONS
|
1881
2173
|
exit: function(manager) {
|
1882
|
-
var
|
1883
|
-
|
2174
|
+
var record = get(manager, 'record');
|
2175
|
+
record.fire('didLoad');
|
1884
2176
|
},
|
1885
2177
|
|
1886
2178
|
// EVENTS
|
@@ -1914,6 +2206,11 @@ var states = {
|
|
1914
2206
|
manager.goToState('updated');
|
1915
2207
|
},
|
1916
2208
|
|
2209
|
+
setAssociation: function(manager, context) {
|
2210
|
+
setAssociation(manager, context);
|
2211
|
+
manager.goToState('updated');
|
2212
|
+
},
|
2213
|
+
|
1917
2214
|
didChangeData: didChangeData,
|
1918
2215
|
|
1919
2216
|
deleteRecord: function(manager) {
|
@@ -1944,6 +2241,14 @@ var states = {
|
|
1944
2241
|
isLoaded: true,
|
1945
2242
|
isDirty: true,
|
1946
2243
|
|
2244
|
+
// TRANSITIONS
|
2245
|
+
enter: function(manager) {
|
2246
|
+
var record = get(manager, 'record'),
|
2247
|
+
store = get(record, 'store');
|
2248
|
+
|
2249
|
+
store.removeFromRecordArrays(record);
|
2250
|
+
},
|
2251
|
+
|
1947
2252
|
// SUBSTATES
|
1948
2253
|
|
1949
2254
|
// When a record is deleted, it enters the `start`
|
@@ -1952,21 +2257,27 @@ var states = {
|
|
1952
2257
|
start: DS.State.create({
|
1953
2258
|
// TRANSITIONS
|
1954
2259
|
enter: function(manager) {
|
1955
|
-
var
|
1956
|
-
var store = get(model, 'store');
|
1957
|
-
|
1958
|
-
if (store) {
|
1959
|
-
store.removeFromModelArrays(model);
|
1960
|
-
}
|
2260
|
+
var record = get(manager, 'record');
|
1961
2261
|
|
1962
|
-
|
1963
|
-
t.
|
2262
|
+
record.withTransaction(function(t) {
|
2263
|
+
t.recordBecameDirty('deleted', record);
|
1964
2264
|
});
|
1965
2265
|
},
|
1966
2266
|
|
1967
2267
|
// EVENTS
|
1968
2268
|
willCommit: function(manager) {
|
1969
2269
|
manager.goToState('inFlight');
|
2270
|
+
},
|
2271
|
+
|
2272
|
+
rollback: function(manager) {
|
2273
|
+
var record = get(manager, 'record'),
|
2274
|
+
data = get(record, 'data');
|
2275
|
+
|
2276
|
+
data.rollback();
|
2277
|
+
record.withTransaction(function(t) {
|
2278
|
+
t.recordBecameClean('deleted', record);
|
2279
|
+
});
|
2280
|
+
manager.goToState('loaded');
|
1970
2281
|
}
|
1971
2282
|
}),
|
1972
2283
|
|
@@ -1980,10 +2291,10 @@ var states = {
|
|
1980
2291
|
|
1981
2292
|
// TRANSITIONS
|
1982
2293
|
exit: function(stateManager) {
|
1983
|
-
var
|
2294
|
+
var record = get(stateManager, 'record');
|
1984
2295
|
|
1985
|
-
|
1986
|
-
t.
|
2296
|
+
record.withTransaction(function(t) {
|
2297
|
+
t.recordBecameClean('deleted', record);
|
1987
2298
|
});
|
1988
2299
|
},
|
1989
2300
|
|
@@ -2012,24 +2323,21 @@ var states = {
|
|
2012
2323
|
};
|
2013
2324
|
|
2014
2325
|
DS.StateManager = Ember.StateManager.extend({
|
2015
|
-
|
2326
|
+
record: null,
|
2016
2327
|
initialState: 'rootState',
|
2017
2328
|
states: states
|
2018
2329
|
});
|
2019
2330
|
|
2020
|
-
})(
|
2331
|
+
})();
|
2021
2332
|
|
2022
2333
|
|
2023
|
-
(function(exports) {
|
2024
|
-
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, none = Ember.none;
|
2025
2334
|
|
2026
|
-
|
2027
|
-
|
2028
|
-
}).property('stateManager.currentState').cacheable();
|
2335
|
+
(function() {
|
2336
|
+
var get = Ember.get, set = Ember.set;
|
2029
2337
|
|
2030
2338
|
// This object is a regular JS object for performance. It is only
|
2031
2339
|
// used internally for bookkeeping purposes.
|
2032
|
-
var DataProxy = function(record) {
|
2340
|
+
var DataProxy = DS._DataProxy = function(record) {
|
2033
2341
|
this.record = record;
|
2034
2342
|
this.unsavedData = {};
|
2035
2343
|
this.associations = {};
|
@@ -2111,6 +2419,8 @@ DataProxy.prototype = {
|
|
2111
2419
|
|
2112
2420
|
rollback: function() {
|
2113
2421
|
this.unsavedData = {};
|
2422
|
+
|
2423
|
+
this.record.notifyPropertyChange('data');
|
2114
2424
|
},
|
2115
2425
|
|
2116
2426
|
adapterDidUpdate: function(data) {
|
@@ -2118,7 +2428,18 @@ DataProxy.prototype = {
|
|
2118
2428
|
}
|
2119
2429
|
};
|
2120
2430
|
|
2121
|
-
|
2431
|
+
})();
|
2432
|
+
|
2433
|
+
|
2434
|
+
|
2435
|
+
(function() {
|
2436
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath, none = Ember.none;
|
2437
|
+
|
2438
|
+
var retrieveFromCurrentState = Ember.computed(function(key) {
|
2439
|
+
return get(getPath(this, 'stateManager.currentState'), key);
|
2440
|
+
}).property('stateManager.currentState').cacheable();
|
2441
|
+
|
2442
|
+
DS.Model = Ember.Object.extend(Ember.Evented, {
|
2122
2443
|
isLoaded: retrieveFromCurrentState,
|
2123
2444
|
isDirty: retrieveFromCurrentState,
|
2124
2445
|
isSaving: retrieveFromCurrentState,
|
@@ -2235,6 +2556,7 @@ DS.Model = Ember.Object.extend({
|
|
2235
2556
|
}
|
2236
2557
|
}
|
2237
2558
|
|
2559
|
+
key = options.key || get(this, 'namingConvention').keyToJSONKey(key);
|
2238
2560
|
json[key] = records;
|
2239
2561
|
},
|
2240
2562
|
|
@@ -2300,7 +2622,7 @@ DS.Model = Ember.Object.extend({
|
|
2300
2622
|
},
|
2301
2623
|
|
2302
2624
|
data: Ember.computed(function() {
|
2303
|
-
return new
|
2625
|
+
return new DS._DataProxy(this);
|
2304
2626
|
}).cacheable(),
|
2305
2627
|
|
2306
2628
|
didLoad: Ember.K,
|
@@ -2309,7 +2631,7 @@ DS.Model = Ember.Object.extend({
|
|
2309
2631
|
|
2310
2632
|
init: function() {
|
2311
2633
|
var stateManager = DS.StateManager.create({
|
2312
|
-
|
2634
|
+
record: this
|
2313
2635
|
});
|
2314
2636
|
|
2315
2637
|
set(this, 'pendingQueue', {});
|
@@ -2357,7 +2679,7 @@ DS.Model = Ember.Object.extend({
|
|
2357
2679
|
var data = get(this, 'data');
|
2358
2680
|
|
2359
2681
|
if (data && key in data) {
|
2360
|
-
ember_assert("You attempted to access the " + key + " property on a
|
2682
|
+
ember_assert("You attempted to access the " + key + " property on a record without defining an attribute.", false);
|
2361
2683
|
}
|
2362
2684
|
},
|
2363
2685
|
|
@@ -2365,7 +2687,7 @@ DS.Model = Ember.Object.extend({
|
|
2365
2687
|
var data = get(this, 'data');
|
2366
2688
|
|
2367
2689
|
if (data && key in data) {
|
2368
|
-
ember_assert("You attempted to set the " + key + " property on a
|
2690
|
+
ember_assert("You attempted to set the " + key + " property on a record without defining an attribute.", false);
|
2369
2691
|
} else {
|
2370
2692
|
return this._super(key, value);
|
2371
2693
|
}
|
@@ -2384,10 +2706,44 @@ DS.Model = Ember.Object.extend({
|
|
2384
2706
|
|
2385
2707
|
/** @private */
|
2386
2708
|
hashWasUpdated: function() {
|
2387
|
-
// At the end of the run loop, notify
|
2709
|
+
// At the end of the run loop, notify record arrays that
|
2388
2710
|
// this record has changed so they can re-evaluate its contents
|
2389
2711
|
// to determine membership.
|
2390
2712
|
Ember.run.once(this, this.notifyHashWasUpdated);
|
2713
|
+
},
|
2714
|
+
|
2715
|
+
dataDidChange: Ember.observer(function() {
|
2716
|
+
var associations = get(this.constructor, 'associationsByName'),
|
2717
|
+
data = get(this, 'data'), store = get(this, 'store'),
|
2718
|
+
idToClientId = store.idToClientId,
|
2719
|
+
cachedValue;
|
2720
|
+
|
2721
|
+
associations.forEach(function(name, association) {
|
2722
|
+
if (association.kind === 'hasMany') {
|
2723
|
+
cachedValue = this.cacheFor(name);
|
2724
|
+
|
2725
|
+
if (cachedValue) {
|
2726
|
+
var ids = data.get(name) || [];
|
2727
|
+
var clientIds = Ember.ArrayUtils.map(ids, function(id) {
|
2728
|
+
return store.clientIdForId(association.type, id);
|
2729
|
+
});
|
2730
|
+
|
2731
|
+
set(cachedValue, 'content', Ember.A(clientIds));
|
2732
|
+
cachedValue.fetch();
|
2733
|
+
}
|
2734
|
+
}
|
2735
|
+
}, this);
|
2736
|
+
}, 'data'),
|
2737
|
+
|
2738
|
+
/**
|
2739
|
+
@private
|
2740
|
+
|
2741
|
+
Override the default event firing from Ember.Evented to
|
2742
|
+
also call methods with the given name.
|
2743
|
+
*/
|
2744
|
+
fire: function(name) {
|
2745
|
+
this[name].apply(this, [].slice.call(arguments, 1));
|
2746
|
+
this._super.apply(this, arguments);
|
2391
2747
|
}
|
2392
2748
|
});
|
2393
2749
|
|
@@ -2418,10 +2774,11 @@ DS.Model.reopenClass({
|
|
2418
2774
|
createRecord: storeAlias('createRecord')
|
2419
2775
|
});
|
2420
2776
|
|
2421
|
-
})(
|
2777
|
+
})();
|
2778
|
+
|
2422
2779
|
|
2423
2780
|
|
2424
|
-
(function(
|
2781
|
+
(function() {
|
2425
2782
|
var get = Ember.get, getPath = Ember.getPath;
|
2426
2783
|
DS.Model.reopenClass({
|
2427
2784
|
attributes: Ember.computed(function() {
|
@@ -2573,75 +2930,27 @@ DS.attr.transforms = {
|
|
2573
2930
|
};
|
2574
2931
|
|
2575
2932
|
|
2576
|
-
})(
|
2577
|
-
|
2578
|
-
|
2579
|
-
(function(exports) {
|
2580
|
-
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
2581
|
-
DS.Model.reopenClass({
|
2582
|
-
typeForAssociation: function(name) {
|
2583
|
-
var association = get(this, 'associationsByName').get(name);
|
2584
|
-
return association && association.type;
|
2585
|
-
},
|
2586
|
-
|
2587
|
-
associations: Ember.computed(function() {
|
2588
|
-
var map = Ember.Map.create();
|
2589
|
-
|
2590
|
-
this.eachComputedProperty(function(name, meta) {
|
2591
|
-
if (meta.isAssociation) {
|
2592
|
-
var type = meta.type,
|
2593
|
-
typeList = map.get(type);
|
2594
|
-
|
2595
|
-
if (typeof type === 'string') {
|
2596
|
-
type = getPath(this, type, false) || getPath(window, type);
|
2597
|
-
meta.type = type;
|
2598
|
-
}
|
2599
|
-
|
2600
|
-
if (!typeList) {
|
2601
|
-
typeList = [];
|
2602
|
-
map.set(type, typeList);
|
2603
|
-
}
|
2604
|
-
|
2605
|
-
typeList.push({ name: name, kind: meta.kind });
|
2606
|
-
}
|
2607
|
-
});
|
2933
|
+
})();
|
2608
2934
|
|
2609
|
-
return map;
|
2610
|
-
}).cacheable(),
|
2611
2935
|
|
2612
|
-
associationsByName: Ember.computed(function() {
|
2613
|
-
var map = Ember.Map.create(), type;
|
2614
2936
|
|
2615
|
-
|
2616
|
-
if (meta.isAssociation) {
|
2617
|
-
meta.key = name;
|
2618
|
-
type = meta.type;
|
2937
|
+
(function() {
|
2619
2938
|
|
2620
|
-
|
2621
|
-
type = getPath(this, type, false) || getPath(window, type);
|
2622
|
-
meta.type = type;
|
2623
|
-
}
|
2939
|
+
})();
|
2624
2940
|
|
2625
|
-
map.set(name, meta);
|
2626
|
-
}
|
2627
|
-
});
|
2628
2941
|
|
2629
|
-
return map;
|
2630
|
-
}).cacheable()
|
2631
|
-
});
|
2632
2942
|
|
2943
|
+
(function() {
|
2944
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath,
|
2945
|
+
none = Ember.none;
|
2633
2946
|
|
2634
2947
|
var embeddedFindRecord = function(store, type, data, key, one) {
|
2635
|
-
var association =
|
2636
|
-
|
2637
|
-
return association ? store.load(type, association).id : null;
|
2638
|
-
} else {
|
2639
|
-
return association ? store.loadMany(type, association).ids : [];
|
2640
|
-
}
|
2948
|
+
var association = get(data, key);
|
2949
|
+
return none(association) ? undefined : store.load(type, association).id;
|
2641
2950
|
};
|
2642
2951
|
|
2643
2952
|
var referencedFindRecord = function(store, type, data, key, one) {
|
2644
|
-
return
|
2953
|
+
return get(data, key);
|
2645
2954
|
};
|
2646
2955
|
|
2647
2956
|
var hasAssociation = function(type, options, one) {
|
@@ -2650,12 +2959,7 @@ var hasAssociation = function(type, options, one) {
|
|
2650
2959
|
var embedded = options.embedded,
|
2651
2960
|
findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
|
2652
2961
|
|
2653
|
-
var meta = { type: type, isAssociation: true, options: options };
|
2654
|
-
if (one) {
|
2655
|
-
meta.kind = 'belongsTo';
|
2656
|
-
} else {
|
2657
|
-
meta.kind = 'hasMany';
|
2658
|
-
}
|
2962
|
+
var meta = { type: type, isAssociation: true, options: options, kind: 'belongsTo' };
|
2659
2963
|
|
2660
2964
|
return Ember.computed(function(key, value) {
|
2661
2965
|
var data = get(this, 'data'), ids, id, association,
|
@@ -2665,57 +2969,477 @@ var hasAssociation = function(type, options, one) {
|
|
2665
2969
|
type = getPath(this, type, false) || getPath(window, type);
|
2666
2970
|
}
|
2667
2971
|
|
2668
|
-
if (
|
2669
|
-
|
2670
|
-
|
2671
|
-
|
2672
|
-
|
2673
|
-
|
2674
|
-
|
2675
|
-
|
2676
|
-
|
2677
|
-
|
2678
|
-
|
2972
|
+
if (arguments.length === 2) {
|
2973
|
+
key = options.key || get(this, 'namingConvention').foreignKey(key);
|
2974
|
+
this.send('setAssociation', { key: key, value: value === null ? null : get(value, 'clientId') });
|
2975
|
+
//data.setAssociation(key, get(value, 'clientId'));
|
2976
|
+
// put the client id in `key` in the data hash
|
2977
|
+
return value;
|
2978
|
+
} else {
|
2979
|
+
// Embedded belongsTo associations should not look for
|
2980
|
+
// a foreign key.
|
2981
|
+
if (embedded) {
|
2982
|
+
key = options.key || get(this, 'namingConvention').keyToJSONKey(key);
|
2679
2983
|
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
2684
|
-
}
|
2685
|
-
id = findRecord(store, type, data, key, true);
|
2686
|
-
association = id ? store.find(type, id) : null;
|
2984
|
+
// Non-embedded associations should look for a foreign key.
|
2985
|
+
// For example, instead of person, we might look for person_id
|
2986
|
+
} else {
|
2987
|
+
key = options.key || get(this, 'namingConvention').foreignKey(key);
|
2687
2988
|
}
|
2688
|
-
|
2689
|
-
|
2690
|
-
ids = findRecord(store, type, data, key);
|
2691
|
-
association = store.findMany(type, ids);
|
2692
|
-
set(association, 'parentRecord', this);
|
2989
|
+
id = findRecord(store, type, data, key, true);
|
2990
|
+
association = id ? store.find(type, id) : null;
|
2693
2991
|
}
|
2694
2992
|
|
2695
2993
|
return association;
|
2696
2994
|
}).property('data').cacheable().meta(meta);
|
2697
2995
|
};
|
2698
2996
|
|
2699
|
-
DS.
|
2700
|
-
ember_assert("The type passed to DS.
|
2997
|
+
DS.belongsTo = function(type, options) {
|
2998
|
+
ember_assert("The type passed to DS.belongsTo must be defined", !!type);
|
2701
2999
|
return hasAssociation(type, options);
|
2702
3000
|
};
|
2703
3001
|
|
2704
|
-
|
2705
|
-
ember_assert("The type passed to DS.belongsTo must be defined", !!type);
|
2706
|
-
return hasAssociation(type, options, true);
|
2707
|
-
};
|
3002
|
+
})();
|
2708
3003
|
|
2709
|
-
DS.belongsTo = DS.hasOne;
|
2710
3004
|
|
2711
|
-
})({});
|
2712
3005
|
|
3006
|
+
(function() {
|
3007
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
3008
|
+
var embeddedFindRecord = function(store, type, data, key) {
|
3009
|
+
var association = get(data, key);
|
3010
|
+
return association ? store.loadMany(type, association).ids : [];
|
3011
|
+
};
|
2713
3012
|
|
2714
|
-
|
2715
|
-
|
3013
|
+
var referencedFindRecord = function(store, type, data, key, one) {
|
3014
|
+
return get(data, key);
|
3015
|
+
};
|
3016
|
+
|
3017
|
+
var hasAssociation = function(type, options) {
|
3018
|
+
options = options || {};
|
3019
|
+
|
3020
|
+
var embedded = options.embedded,
|
3021
|
+
findRecord = embedded ? embeddedFindRecord : referencedFindRecord;
|
3022
|
+
|
3023
|
+
var meta = { type: type, isAssociation: true, options: options, kind: 'hasMany' };
|
3024
|
+
|
3025
|
+
return Ember.computed(function(key, value) {
|
3026
|
+
var data = get(this, 'data'),
|
3027
|
+
store = get(this, 'store'),
|
3028
|
+
ids, id, association;
|
3029
|
+
|
3030
|
+
if (typeof type === 'string') {
|
3031
|
+
type = getPath(this, type, false) || getPath(window, type);
|
3032
|
+
}
|
3033
|
+
|
3034
|
+
key = options.key || key;
|
3035
|
+
ids = findRecord(store, type, data, key);
|
3036
|
+
association = store.findMany(type, ids);
|
3037
|
+
set(association, 'parentRecord', this);
|
3038
|
+
|
3039
|
+
return association;
|
3040
|
+
}).property().cacheable().meta(meta);
|
3041
|
+
};
|
3042
|
+
|
3043
|
+
DS.hasMany = function(type, options) {
|
3044
|
+
ember_assert("The type passed to DS.hasMany must be defined", !!type);
|
3045
|
+
return hasAssociation(type, options);
|
3046
|
+
};
|
3047
|
+
|
3048
|
+
})();
|
2716
3049
|
|
2717
3050
|
|
2718
|
-
|
3051
|
+
|
3052
|
+
(function() {
|
3053
|
+
var get = Ember.get, getPath = Ember.getPath;
|
3054
|
+
|
3055
|
+
DS.Model.reopenClass({
|
3056
|
+
typeForAssociation: function(name) {
|
3057
|
+
var association = get(this, 'associationsByName').get(name);
|
3058
|
+
return association && association.type;
|
3059
|
+
},
|
3060
|
+
|
3061
|
+
associations: Ember.computed(function() {
|
3062
|
+
var map = Ember.Map.create();
|
3063
|
+
|
3064
|
+
this.eachComputedProperty(function(name, meta) {
|
3065
|
+
if (meta.isAssociation) {
|
3066
|
+
var type = meta.type,
|
3067
|
+
typeList = map.get(type);
|
3068
|
+
|
3069
|
+
if (typeof type === 'string') {
|
3070
|
+
type = getPath(this, type, false) || getPath(window, type);
|
3071
|
+
meta.type = type;
|
3072
|
+
}
|
3073
|
+
|
3074
|
+
if (!typeList) {
|
3075
|
+
typeList = [];
|
3076
|
+
map.set(type, typeList);
|
3077
|
+
}
|
3078
|
+
|
3079
|
+
typeList.push({ name: name, kind: meta.kind });
|
3080
|
+
}
|
3081
|
+
});
|
3082
|
+
|
3083
|
+
return map;
|
3084
|
+
}).cacheable(),
|
3085
|
+
|
3086
|
+
associationsByName: Ember.computed(function() {
|
3087
|
+
var map = Ember.Map.create(), type;
|
3088
|
+
|
3089
|
+
this.eachComputedProperty(function(name, meta) {
|
3090
|
+
if (meta.isAssociation) {
|
3091
|
+
meta.key = name;
|
3092
|
+
type = meta.type;
|
3093
|
+
|
3094
|
+
if (typeof type === 'string') {
|
3095
|
+
type = getPath(this, type, false) || getPath(window, type);
|
3096
|
+
meta.type = type;
|
3097
|
+
}
|
3098
|
+
|
3099
|
+
map.set(name, meta);
|
3100
|
+
}
|
3101
|
+
});
|
3102
|
+
|
3103
|
+
return map;
|
3104
|
+
}).cacheable()
|
3105
|
+
});
|
3106
|
+
|
3107
|
+
})();
|
3108
|
+
|
3109
|
+
|
3110
|
+
|
3111
|
+
(function() {
|
3112
|
+
|
3113
|
+
})();
|
3114
|
+
|
3115
|
+
|
3116
|
+
|
3117
|
+
(function() {
|
3118
|
+
DS.Adapter = Ember.Object.extend({
|
3119
|
+
commit: function(store, commitDetails) {
|
3120
|
+
commitDetails.updated.eachType(function(type, array) {
|
3121
|
+
this.updateRecords(store, type, array.slice());
|
3122
|
+
}, this);
|
3123
|
+
|
3124
|
+
commitDetails.created.eachType(function(type, array) {
|
3125
|
+
this.createRecords(store, type, array.slice());
|
3126
|
+
}, this);
|
3127
|
+
|
3128
|
+
commitDetails.deleted.eachType(function(type, array) {
|
3129
|
+
this.deleteRecords(store, type, array.slice());
|
3130
|
+
}, this);
|
3131
|
+
},
|
3132
|
+
|
3133
|
+
createRecords: function(store, type, records) {
|
3134
|
+
records.forEach(function(record) {
|
3135
|
+
this.createRecord(store, type, record);
|
3136
|
+
}, this);
|
3137
|
+
},
|
3138
|
+
|
3139
|
+
updateRecords: function(store, type, records) {
|
3140
|
+
records.forEach(function(record) {
|
3141
|
+
this.updateRecord(store, type, record);
|
3142
|
+
}, this);
|
3143
|
+
},
|
3144
|
+
|
3145
|
+
deleteRecords: function(store, type, records) {
|
3146
|
+
records.forEach(function(record) {
|
3147
|
+
this.deleteRecord(store, type, record);
|
3148
|
+
}, this);
|
3149
|
+
},
|
3150
|
+
|
3151
|
+
findMany: function(store, type, ids) {
|
3152
|
+
ids.forEach(function(id) {
|
3153
|
+
this.find(store, type, id);
|
3154
|
+
}, this);
|
3155
|
+
}
|
3156
|
+
});
|
3157
|
+
|
3158
|
+
})();
|
3159
|
+
|
3160
|
+
|
3161
|
+
|
3162
|
+
(function() {
|
3163
|
+
DS.fixtureAdapter = DS.Adapter.create({
|
3164
|
+
find: function(store, type, id) {
|
3165
|
+
var fixtures = type.FIXTURES;
|
3166
|
+
|
3167
|
+
ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
|
3168
|
+
if (fixtures.hasLoaded) { return; }
|
3169
|
+
|
3170
|
+
setTimeout(function() {
|
3171
|
+
store.loadMany(type, fixtures);
|
3172
|
+
fixtures.hasLoaded = true;
|
3173
|
+
}, 300);
|
3174
|
+
},
|
3175
|
+
|
3176
|
+
findMany: function() {
|
3177
|
+
this.find.apply(this, arguments);
|
3178
|
+
},
|
3179
|
+
|
3180
|
+
findAll: function(store, type) {
|
3181
|
+
var fixtures = type.FIXTURES;
|
3182
|
+
|
3183
|
+
ember_assert("Unable to find fixtures for model type "+type.toString(), !!fixtures);
|
3184
|
+
|
3185
|
+
var ids = fixtures.map(function(item, index, self){ return item.id; });
|
3186
|
+
store.loadMany(type, ids, fixtures);
|
3187
|
+
}
|
3188
|
+
|
3189
|
+
});
|
3190
|
+
|
3191
|
+
})();
|
3192
|
+
|
3193
|
+
|
3194
|
+
|
3195
|
+
(function() {
|
3196
|
+
/*global jQuery*/
|
3197
|
+
|
3198
|
+
var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
|
3199
|
+
|
3200
|
+
DS.RESTAdapter = DS.Adapter.extend({
|
3201
|
+
createRecord: function(store, type, record) {
|
3202
|
+
var root = this.rootForType(type);
|
3203
|
+
|
3204
|
+
var data = {};
|
3205
|
+
data[root] = record.toJSON();
|
3206
|
+
|
3207
|
+
this.ajax(this.buildURL(root), "POST", {
|
3208
|
+
data: data,
|
3209
|
+
success: function(json) {
|
3210
|
+
this.sideload(store, type, json, root);
|
3211
|
+
store.didCreateRecord(record, json[root]);
|
3212
|
+
}
|
3213
|
+
});
|
3214
|
+
},
|
3215
|
+
|
3216
|
+
createRecords: function(store, type, records) {
|
3217
|
+
if (get(this, 'bulkCommit') === false) {
|
3218
|
+
return this._super(store, type, records);
|
3219
|
+
}
|
3220
|
+
|
3221
|
+
var root = this.rootForType(type),
|
3222
|
+
plural = this.pluralize(root);
|
3223
|
+
|
3224
|
+
var data = {};
|
3225
|
+
data[plural] = records.map(function(record) {
|
3226
|
+
return record.toJSON();
|
3227
|
+
});
|
3228
|
+
|
3229
|
+
this.ajax(this.buildURL(root), "POST", {
|
3230
|
+
data: data,
|
3231
|
+
|
3232
|
+
success: function(json) {
|
3233
|
+
this.sideload(store, type, json, plural);
|
3234
|
+
store.didCreateRecords(type, records, json[plural]);
|
3235
|
+
}
|
3236
|
+
});
|
3237
|
+
},
|
3238
|
+
|
3239
|
+
updateRecord: function(store, type, record) {
|
3240
|
+
var id = get(record, 'id');
|
3241
|
+
var root = this.rootForType(type);
|
3242
|
+
|
3243
|
+
var data = {};
|
3244
|
+
data[root] = record.toJSON();
|
3245
|
+
|
3246
|
+
this.ajax(this.buildURL(root, id), "PUT", {
|
3247
|
+
data: data,
|
3248
|
+
success: function(json) {
|
3249
|
+
this.sideload(store, type, json, root);
|
3250
|
+
store.didUpdateRecord(record, json && json[root]);
|
3251
|
+
}
|
3252
|
+
});
|
3253
|
+
},
|
3254
|
+
|
3255
|
+
updateRecords: function(store, type, records) {
|
3256
|
+
if (get(this, 'bulkCommit') === false) {
|
3257
|
+
return this._super(store, type, records);
|
3258
|
+
}
|
3259
|
+
|
3260
|
+
var root = this.rootForType(type),
|
3261
|
+
plural = this.pluralize(root);
|
3262
|
+
|
3263
|
+
var data = {};
|
3264
|
+
data[plural] = records.map(function(record) {
|
3265
|
+
return record.toJSON();
|
3266
|
+
});
|
3267
|
+
|
3268
|
+
this.ajax(this.buildURL(root, "bulk"), "PUT", {
|
3269
|
+
data: data,
|
3270
|
+
success: function(json) {
|
3271
|
+
this.sideload(store, type, json, plural);
|
3272
|
+
store.didUpdateRecords(records, json[plural]);
|
3273
|
+
}
|
3274
|
+
});
|
3275
|
+
},
|
3276
|
+
|
3277
|
+
deleteRecord: function(store, type, record) {
|
3278
|
+
var id = get(record, 'id');
|
3279
|
+
var root = this.rootForType(type);
|
3280
|
+
|
3281
|
+
this.ajax(this.buildURL(root, id), "DELETE", {
|
3282
|
+
success: function(json) {
|
3283
|
+
if (json) { this.sideload(store, type, json); }
|
3284
|
+
store.didDeleteRecord(record);
|
3285
|
+
}
|
3286
|
+
});
|
3287
|
+
},
|
3288
|
+
|
3289
|
+
deleteRecords: function(store, type, records) {
|
3290
|
+
if (get(this, 'bulkCommit') === false) {
|
3291
|
+
return this._super(store, type, records);
|
3292
|
+
}
|
3293
|
+
|
3294
|
+
var root = this.rootForType(type),
|
3295
|
+
plural = this.pluralize(root);
|
3296
|
+
|
3297
|
+
var data = {};
|
3298
|
+
data[plural] = records.map(function(record) {
|
3299
|
+
return get(record, 'id');
|
3300
|
+
});
|
3301
|
+
|
3302
|
+
this.ajax(this.buildURL(root, 'bulk'), "DELETE", {
|
3303
|
+
data: data,
|
3304
|
+
success: function(json) {
|
3305
|
+
if (json) { this.sideload(store, type, json); }
|
3306
|
+
store.didDeleteRecords(records);
|
3307
|
+
}
|
3308
|
+
});
|
3309
|
+
},
|
3310
|
+
|
3311
|
+
find: function(store, type, id) {
|
3312
|
+
var root = this.rootForType(type);
|
3313
|
+
|
3314
|
+
this.ajax(this.buildURL(root, id), "GET", {
|
3315
|
+
success: function(json) {
|
3316
|
+
store.load(type, json[root]);
|
3317
|
+
this.sideload(store, type, json, root);
|
3318
|
+
}
|
3319
|
+
});
|
3320
|
+
},
|
3321
|
+
|
3322
|
+
findMany: function(store, type, ids) {
|
3323
|
+
var root = this.rootForType(type), plural = this.pluralize(root);
|
3324
|
+
|
3325
|
+
this.ajax(this.buildURL(root), "GET", {
|
3326
|
+
data: { ids: ids },
|
3327
|
+
success: function(json) {
|
3328
|
+
store.loadMany(type, ids, json[plural]);
|
3329
|
+
this.sideload(store, type, json, plural);
|
3330
|
+
}
|
3331
|
+
});
|
3332
|
+
},
|
3333
|
+
|
3334
|
+
findAll: function(store, type) {
|
3335
|
+
var root = this.rootForType(type), plural = this.pluralize(root);
|
3336
|
+
|
3337
|
+
this.ajax(this.buildURL(root), "GET", {
|
3338
|
+
success: function(json) {
|
3339
|
+
store.loadMany(type, json[plural]);
|
3340
|
+
this.sideload(store, type, json, plural);
|
3341
|
+
}
|
3342
|
+
});
|
3343
|
+
},
|
3344
|
+
|
3345
|
+
findQuery: function(store, type, query, recordArray) {
|
3346
|
+
var root = this.rootForType(type), plural = this.pluralize(root);
|
3347
|
+
|
3348
|
+
this.ajax(this.buildURL(root), "GET", {
|
3349
|
+
data: query,
|
3350
|
+
success: function(json) {
|
3351
|
+
recordArray.load(json[plural]);
|
3352
|
+
this.sideload(store, type, json, plural);
|
3353
|
+
}
|
3354
|
+
});
|
3355
|
+
},
|
3356
|
+
|
3357
|
+
// HELPERS
|
3358
|
+
|
3359
|
+
plurals: {},
|
3360
|
+
|
3361
|
+
// define a plurals hash in your subclass to define
|
3362
|
+
// special-case pluralization
|
3363
|
+
pluralize: function(name) {
|
3364
|
+
return this.plurals[name] || name + "s";
|
3365
|
+
},
|
3366
|
+
|
3367
|
+
rootForType: function(type) {
|
3368
|
+
if (type.url) { return type.url; }
|
3369
|
+
|
3370
|
+
// use the last part of the name as the URL
|
3371
|
+
var parts = type.toString().split(".");
|
3372
|
+
var name = parts[parts.length - 1];
|
3373
|
+
return name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1);
|
3374
|
+
},
|
3375
|
+
|
3376
|
+
ajax: function(url, type, hash) {
|
3377
|
+
hash.url = url;
|
3378
|
+
hash.type = type;
|
3379
|
+
hash.dataType = 'json';
|
3380
|
+
hash.contentType = 'application/json';
|
3381
|
+
hash.context = this;
|
3382
|
+
|
3383
|
+
if (hash.data && type !== 'GET') {
|
3384
|
+
hash.data = JSON.stringify(hash.data);
|
3385
|
+
}
|
3386
|
+
|
3387
|
+
jQuery.ajax(hash);
|
3388
|
+
},
|
3389
|
+
|
3390
|
+
sideload: function(store, type, json, root) {
|
3391
|
+
var sideloadedType, mappings;
|
3392
|
+
|
3393
|
+
for (var prop in json) {
|
3394
|
+
if (!json.hasOwnProperty(prop)) { continue; }
|
3395
|
+
if (prop === root) { continue; }
|
3396
|
+
|
3397
|
+
sideloadedType = type.typeForAssociation(prop);
|
3398
|
+
|
3399
|
+
if (!sideloadedType) {
|
3400
|
+
mappings = get(this, 'mappings');
|
3401
|
+
|
3402
|
+
ember_assert("Your server returned a hash with the key " + prop + " but you have no mappings", !!mappings);
|
3403
|
+
|
3404
|
+
sideloadedType = get(get(this, 'mappings'), prop);
|
3405
|
+
|
3406
|
+
ember_assert("Your server returned a hash with the key " + prop + " but you have no mapping for it", !!sideloadedType);
|
3407
|
+
}
|
3408
|
+
|
3409
|
+
this.loadValue(store, sideloadedType, json[prop]);
|
3410
|
+
}
|
3411
|
+
},
|
3412
|
+
|
3413
|
+
loadValue: function(store, type, value) {
|
3414
|
+
if (value instanceof Array) {
|
3415
|
+
store.loadMany(type, value);
|
3416
|
+
} else {
|
3417
|
+
store.load(type, value);
|
3418
|
+
}
|
3419
|
+
},
|
3420
|
+
|
3421
|
+
buildURL: function(record, suffix) {
|
3422
|
+
var url = [""];
|
3423
|
+
|
3424
|
+
if (this.namespace !== undefined) {
|
3425
|
+
url.push(this.namespace);
|
3426
|
+
}
|
3427
|
+
|
3428
|
+
url.push(this.pluralize(record));
|
3429
|
+
if (suffix !== undefined) {
|
3430
|
+
url.push(suffix);
|
3431
|
+
}
|
3432
|
+
|
3433
|
+
return url.join("/");
|
3434
|
+
}
|
3435
|
+
});
|
3436
|
+
|
3437
|
+
|
3438
|
+
})();
|
3439
|
+
|
3440
|
+
|
3441
|
+
|
3442
|
+
(function() {
|
2719
3443
|
//Copyright (C) 2011 by Living Social, Inc.
|
2720
3444
|
|
2721
3445
|
//Permission is hereby granted, free of charge, to any person obtaining a copy of
|
@@ -2735,4 +3459,6 @@ DS.belongsTo = DS.hasOne;
|
|
2735
3459
|
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
2736
3460
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
2737
3461
|
//SOFTWARE.
|
2738
|
-
|
3462
|
+
|
3463
|
+
})();
|
3464
|
+
|