embient 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|