root / snf-cyclades-app / synnefo / ui / new_ui / ui / javascripts / ember-data.js @ 0467b6cd
History | View | Annotate | Download (272.3 kB)
1 | d0fe8c12 | Athina Bekakou | /*!
|
---|---|---|---|
2 | d0fe8c12 | Athina Bekakou | * @overview Ember Data
|
3 | d0fe8c12 | Athina Bekakou | * @copyright Copyright 2011-2014 Tilde Inc. and contributors.
|
4 | d0fe8c12 | Athina Bekakou | * Portions Copyright 2011 LivingSocial Inc.
|
5 | d0fe8c12 | Athina Bekakou | * @license Licensed under MIT license (see license.js)
|
6 | d0fe8c12 | Athina Bekakou | * @version 1.0.0-beta.5
|
7 | d0fe8c12 | Athina Bekakou | */
|
8 | d0fe8c12 | Athina Bekakou | |
9 | d0fe8c12 | Athina Bekakou | |
10 | d0fe8c12 | Athina Bekakou | (function() {
|
11 | d0fe8c12 | Athina Bekakou | var define, requireModule;
|
12 | d0fe8c12 | Athina Bekakou | |
13 | d0fe8c12 | Athina Bekakou | (function() {
|
14 | d0fe8c12 | Athina Bekakou | var registry = {}, seen = {};
|
15 | d0fe8c12 | Athina Bekakou | |
16 | d0fe8c12 | Athina Bekakou | define = function(name, deps, callback) { |
17 | d0fe8c12 | Athina Bekakou | registry[name] = { deps: deps, callback: callback }; |
18 | d0fe8c12 | Athina Bekakou | }; |
19 | d0fe8c12 | Athina Bekakou | |
20 | d0fe8c12 | Athina Bekakou | requireModule = function(name) { |
21 | d0fe8c12 | Athina Bekakou | if (seen[name]) { return seen[name]; } |
22 | d0fe8c12 | Athina Bekakou | seen[name] = {}; |
23 | d0fe8c12 | Athina Bekakou | |
24 | d0fe8c12 | Athina Bekakou | var mod, deps, callback, reified , exports;
|
25 | d0fe8c12 | Athina Bekakou | |
26 | d0fe8c12 | Athina Bekakou | mod = registry[name]; |
27 | d0fe8c12 | Athina Bekakou | |
28 | d0fe8c12 | Athina Bekakou | if (!mod) {
|
29 | d0fe8c12 | Athina Bekakou | throw new Error("Module '" + name + "' not found."); |
30 | d0fe8c12 | Athina Bekakou | } |
31 | d0fe8c12 | Athina Bekakou | |
32 | d0fe8c12 | Athina Bekakou | deps = mod.deps; |
33 | d0fe8c12 | Athina Bekakou | callback = mod.callback; |
34 | d0fe8c12 | Athina Bekakou | reified = []; |
35 | d0fe8c12 | Athina Bekakou | exports; |
36 | d0fe8c12 | Athina Bekakou | |
37 | d0fe8c12 | Athina Bekakou | for (var i=0, l=deps.length; i<l; i++) { |
38 | d0fe8c12 | Athina Bekakou | if (deps[i] === 'exports') { |
39 | d0fe8c12 | Athina Bekakou | reified.push(exports = {}); |
40 | d0fe8c12 | Athina Bekakou | } else {
|
41 | d0fe8c12 | Athina Bekakou | reified.push(requireModule(deps[i])); |
42 | d0fe8c12 | Athina Bekakou | } |
43 | d0fe8c12 | Athina Bekakou | } |
44 | d0fe8c12 | Athina Bekakou | |
45 | d0fe8c12 | Athina Bekakou | var value = callback.apply(this, reified); |
46 | d0fe8c12 | Athina Bekakou | return seen[name] = exports || value;
|
47 | d0fe8c12 | Athina Bekakou | }; |
48 | d0fe8c12 | Athina Bekakou | })(); |
49 | d0fe8c12 | Athina Bekakou | (function() {
|
50 | d0fe8c12 | Athina Bekakou | /**
|
51 | d0fe8c12 | Athina Bekakou | @module ember-data
|
52 | d0fe8c12 | Athina Bekakou | */
|
53 | d0fe8c12 | Athina Bekakou | |
54 | d0fe8c12 | Athina Bekakou | /**
|
55 | d0fe8c12 | Athina Bekakou | All Ember Data methods and functions are defined inside of this namespace.
|
56 | d0fe8c12 | Athina Bekakou | |
57 | d0fe8c12 | Athina Bekakou | @class DS
|
58 | d0fe8c12 | Athina Bekakou | @static
|
59 | d0fe8c12 | Athina Bekakou | */
|
60 | d0fe8c12 | Athina Bekakou | var DS;
|
61 | d0fe8c12 | Athina Bekakou | if ('undefined' === typeof DS) { |
62 | d0fe8c12 | Athina Bekakou | /**
|
63 | d0fe8c12 | Athina Bekakou | @property VERSION
|
64 | d0fe8c12 | Athina Bekakou | @type String
|
65 | d0fe8c12 | Athina Bekakou | @default '1.0.0-beta.5'
|
66 | d0fe8c12 | Athina Bekakou | @static
|
67 | d0fe8c12 | Athina Bekakou | */
|
68 | d0fe8c12 | Athina Bekakou | DS = Ember.Namespace.create({ |
69 | d0fe8c12 | Athina Bekakou | VERSION: '1.0.0-beta.5' |
70 | d0fe8c12 | Athina Bekakou | }); |
71 | d0fe8c12 | Athina Bekakou | |
72 | d0fe8c12 | Athina Bekakou | if ('undefined' !== typeof window) { |
73 | d0fe8c12 | Athina Bekakou | window.DS = DS; |
74 | d0fe8c12 | Athina Bekakou | } |
75 | d0fe8c12 | Athina Bekakou | |
76 | d0fe8c12 | Athina Bekakou | if (Ember.libraries) {
|
77 | d0fe8c12 | Athina Bekakou | Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION);
|
78 | d0fe8c12 | Athina Bekakou | } |
79 | d0fe8c12 | Athina Bekakou | } |
80 | d0fe8c12 | Athina Bekakou | |
81 | d0fe8c12 | Athina Bekakou | })(); |
82 | d0fe8c12 | Athina Bekakou | |
83 | d0fe8c12 | Athina Bekakou | |
84 | d0fe8c12 | Athina Bekakou | |
85 | d0fe8c12 | Athina Bekakou | (function() {
|
86 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
|
87 | d0fe8c12 | Athina Bekakou | |
88 | d0fe8c12 | Athina Bekakou | // Simple dispatcher to support overriding the aliased
|
89 | d0fe8c12 | Athina Bekakou | // method in subclasses.
|
90 | d0fe8c12 | Athina Bekakou | function aliasMethod(methodName) { |
91 | d0fe8c12 | Athina Bekakou | return function() { |
92 | d0fe8c12 | Athina Bekakou | return this[methodName].apply(this, arguments); |
93 | d0fe8c12 | Athina Bekakou | }; |
94 | d0fe8c12 | Athina Bekakou | } |
95 | d0fe8c12 | Athina Bekakou | |
96 | d0fe8c12 | Athina Bekakou | /**
|
97 | d0fe8c12 | Athina Bekakou | In Ember Data a Serializer is used to serialize and deserialize
|
98 | d0fe8c12 | Athina Bekakou | records when they are transfered in and out of an external source.
|
99 | d0fe8c12 | Athina Bekakou | This process involves normalizing property names, transforming
|
100 | d0fe8c12 | Athina Bekakou | attribute values and serializeing relationships.
|
101 | d0fe8c12 | Athina Bekakou | |
102 | d0fe8c12 | Athina Bekakou | For maximum performance Ember Data recomends you use the
|
103 | d0fe8c12 | Athina Bekakou | [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses.
|
104 | d0fe8c12 | Athina Bekakou | |
105 | d0fe8c12 | Athina Bekakou | `JSONSerializer` is useful for simpler or legacy backends that may
|
106 | d0fe8c12 | Athina Bekakou | not support the http://jsonapi.org/ spec.
|
107 | d0fe8c12 | Athina Bekakou | |
108 | d0fe8c12 | Athina Bekakou | @class JSONSerializer
|
109 | d0fe8c12 | Athina Bekakou | @namespace DS
|
110 | d0fe8c12 | Athina Bekakou | */
|
111 | d0fe8c12 | Athina Bekakou | DS.JSONSerializer = Ember.Object.extend({ |
112 | d0fe8c12 | Athina Bekakou | /**
|
113 | d0fe8c12 | Athina Bekakou | The primaryKey is used when serializing and deserializing
|
114 | d0fe8c12 | Athina Bekakou | data. Ember Data always uses the `id` propery to store the id of
|
115 | d0fe8c12 | Athina Bekakou | the record. The external source may not always follow this
|
116 | d0fe8c12 | Athina Bekakou | convention. In these cases it is usesful to override the
|
117 | d0fe8c12 | Athina Bekakou | primaryKey property to match the primaryKey of your external
|
118 | d0fe8c12 | Athina Bekakou | store.
|
119 | d0fe8c12 | Athina Bekakou | |
120 | d0fe8c12 | Athina Bekakou | Example
|
121 | d0fe8c12 | Athina Bekakou | |
122 | d0fe8c12 | Athina Bekakou | ```javascript
|
123 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.JSONSerializer.extend({
|
124 | d0fe8c12 | Athina Bekakou | primaryKey: '_id'
|
125 | d0fe8c12 | Athina Bekakou | });
|
126 | d0fe8c12 | Athina Bekakou | ```
|
127 | d0fe8c12 | Athina Bekakou | |
128 | d0fe8c12 | Athina Bekakou | @property primaryKey
|
129 | d0fe8c12 | Athina Bekakou | @type {String}
|
130 | d0fe8c12 | Athina Bekakou | @default 'id'
|
131 | d0fe8c12 | Athina Bekakou | */
|
132 | d0fe8c12 | Athina Bekakou | primaryKey: 'id', |
133 | d0fe8c12 | Athina Bekakou | |
134 | d0fe8c12 | Athina Bekakou | /**
|
135 | d0fe8c12 | Athina Bekakou | Given a subclass of `DS.Model` and a JSON object this method will
|
136 | d0fe8c12 | Athina Bekakou | iterate through each attribute of the `DS.Model` and invoke the
|
137 | d0fe8c12 | Athina Bekakou | `DS.Transform#deserialize` method on the matching property of the
|
138 | d0fe8c12 | Athina Bekakou | JSON object. This method is typically called after the
|
139 | d0fe8c12 | Athina Bekakou | serializer's `normalize` method.
|
140 | d0fe8c12 | Athina Bekakou | |
141 | d0fe8c12 | Athina Bekakou | @method applyTransforms
|
142 | d0fe8c12 | Athina Bekakou | @private
|
143 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
144 | d0fe8c12 | Athina Bekakou | @param {Object} data The data to transform
|
145 | d0fe8c12 | Athina Bekakou | @return {Object} data The transformed data object
|
146 | d0fe8c12 | Athina Bekakou | */
|
147 | d0fe8c12 | Athina Bekakou | applyTransforms: function(type, data) { |
148 | d0fe8c12 | Athina Bekakou | type.eachTransformedAttribute(function(key, type) {
|
149 | d0fe8c12 | Athina Bekakou | var transform = this.transformFor(type); |
150 | d0fe8c12 | Athina Bekakou | data[key] = transform.deserialize(data[key]); |
151 | d0fe8c12 | Athina Bekakou | }, this);
|
152 | d0fe8c12 | Athina Bekakou | |
153 | d0fe8c12 | Athina Bekakou | return data;
|
154 | d0fe8c12 | Athina Bekakou | }, |
155 | d0fe8c12 | Athina Bekakou | |
156 | d0fe8c12 | Athina Bekakou | /**
|
157 | d0fe8c12 | Athina Bekakou | Normalizes a part of the JSON payload returned by
|
158 | d0fe8c12 | Athina Bekakou | the server. You should override this method, munge the hash
|
159 | d0fe8c12 | Athina Bekakou | and call super if you have generic normalization to do.
|
160 | d0fe8c12 | Athina Bekakou | |
161 | d0fe8c12 | Athina Bekakou | It takes the type of the record that is being normalized
|
162 | d0fe8c12 | Athina Bekakou | (as a DS.Model class), the property where the hash was
|
163 | d0fe8c12 | Athina Bekakou | originally found, and the hash to normalize.
|
164 | d0fe8c12 | Athina Bekakou | |
165 | d0fe8c12 | Athina Bekakou | You can use this method, for example, to normalize underscored keys to camelized
|
166 | d0fe8c12 | Athina Bekakou | or other general-purpose normalizations.
|
167 | d0fe8c12 | Athina Bekakou | |
168 | d0fe8c12 | Athina Bekakou | Example
|
169 | d0fe8c12 | Athina Bekakou | |
170 | d0fe8c12 | Athina Bekakou | ```javascript
|
171 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.JSONSerializer.extend({
|
172 | d0fe8c12 | Athina Bekakou | normalize: function(type, hash) {
|
173 | d0fe8c12 | Athina Bekakou | var fields = Ember.get(type, 'fields');
|
174 | d0fe8c12 | Athina Bekakou | fields.forEach(function(field) {
|
175 | d0fe8c12 | Athina Bekakou | var payloadField = Ember.String.underscore(field);
|
176 | d0fe8c12 | Athina Bekakou | if (field === payloadField) { return; }
|
177 | d0fe8c12 | Athina Bekakou | |
178 | d0fe8c12 | Athina Bekakou | hash[field] = hash[payloadField];
|
179 | d0fe8c12 | Athina Bekakou | delete hash[payloadField];
|
180 | d0fe8c12 | Athina Bekakou | });
|
181 | d0fe8c12 | Athina Bekakou | return this._super.apply(this, arguments);
|
182 | d0fe8c12 | Athina Bekakou | }
|
183 | d0fe8c12 | Athina Bekakou | });
|
184 | d0fe8c12 | Athina Bekakou | ```
|
185 | d0fe8c12 | Athina Bekakou | |
186 | d0fe8c12 | Athina Bekakou | @method normalize
|
187 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
188 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
189 | d0fe8c12 | Athina Bekakou | @return {Object}
|
190 | d0fe8c12 | Athina Bekakou | */
|
191 | d0fe8c12 | Athina Bekakou | normalize: function(type, hash) { |
192 | d0fe8c12 | Athina Bekakou | if (!hash) { return hash; } |
193 | d0fe8c12 | Athina Bekakou | |
194 | d0fe8c12 | Athina Bekakou | this.applyTransforms(type, hash);
|
195 | d0fe8c12 | Athina Bekakou | return hash;
|
196 | d0fe8c12 | Athina Bekakou | }, |
197 | d0fe8c12 | Athina Bekakou | |
198 | d0fe8c12 | Athina Bekakou | // SERIALIZE
|
199 | d0fe8c12 | Athina Bekakou | /**
|
200 | d0fe8c12 | Athina Bekakou | Called when a record is saved in order to convert the
|
201 | d0fe8c12 | Athina Bekakou | record into JSON.
|
202 | d0fe8c12 | Athina Bekakou | |
203 | d0fe8c12 | Athina Bekakou | By default, it creates a JSON object with a key for
|
204 | d0fe8c12 | Athina Bekakou | each attribute and belongsTo relationship.
|
205 | d0fe8c12 | Athina Bekakou | |
206 | d0fe8c12 | Athina Bekakou | For example, consider this model:
|
207 | d0fe8c12 | Athina Bekakou | |
208 | d0fe8c12 | Athina Bekakou | ```javascript
|
209 | d0fe8c12 | Athina Bekakou | App.Comment = DS.Model.extend({
|
210 | d0fe8c12 | Athina Bekakou | title: DS.attr(),
|
211 | d0fe8c12 | Athina Bekakou | body: DS.attr(),
|
212 | d0fe8c12 | Athina Bekakou | |
213 | d0fe8c12 | Athina Bekakou | author: DS.belongsTo('user')
|
214 | d0fe8c12 | Athina Bekakou | });
|
215 | d0fe8c12 | Athina Bekakou | ```
|
216 | d0fe8c12 | Athina Bekakou | |
217 | d0fe8c12 | Athina Bekakou | The default serialization would create a JSON object like:
|
218 | d0fe8c12 | Athina Bekakou | |
219 | d0fe8c12 | Athina Bekakou | ```javascript
|
220 | d0fe8c12 | Athina Bekakou | {
|
221 | d0fe8c12 | Athina Bekakou | "title": "Rails is unagi",
|
222 | d0fe8c12 | Athina Bekakou | "body": "Rails? Omakase? O_O",
|
223 | d0fe8c12 | Athina Bekakou | "author": 12
|
224 | d0fe8c12 | Athina Bekakou | }
|
225 | d0fe8c12 | Athina Bekakou | ```
|
226 | d0fe8c12 | Athina Bekakou | |
227 | d0fe8c12 | Athina Bekakou | By default, attributes are passed through as-is, unless
|
228 | d0fe8c12 | Athina Bekakou | you specified an attribute type (`DS.attr('date')`). If
|
229 | d0fe8c12 | Athina Bekakou | you specify a transform, the JavaScript value will be
|
230 | d0fe8c12 | Athina Bekakou | serialized when inserted into the JSON hash.
|
231 | d0fe8c12 | Athina Bekakou | |
232 | d0fe8c12 | Athina Bekakou | By default, belongs-to relationships are converted into
|
233 | d0fe8c12 | Athina Bekakou | IDs when inserted into the JSON hash.
|
234 | d0fe8c12 | Athina Bekakou | |
235 | d0fe8c12 | Athina Bekakou | ## IDs
|
236 | d0fe8c12 | Athina Bekakou | |
237 | d0fe8c12 | Athina Bekakou | `serialize` takes an options hash with a single option:
|
238 | d0fe8c12 | Athina Bekakou | `includeId`. If this option is `true`, `serialize` will,
|
239 | d0fe8c12 | Athina Bekakou | by default include the ID in the JSON object it builds.
|
240 | d0fe8c12 | Athina Bekakou | |
241 | d0fe8c12 | Athina Bekakou | The adapter passes in `includeId: true` when serializing
|
242 | d0fe8c12 | Athina Bekakou | a record for `createRecord`, but not for `updateRecord`.
|
243 | d0fe8c12 | Athina Bekakou | |
244 | d0fe8c12 | Athina Bekakou | ## Customization
|
245 | d0fe8c12 | Athina Bekakou | |
246 | d0fe8c12 | Athina Bekakou | Your server may expect a different JSON format than the
|
247 | d0fe8c12 | Athina Bekakou | built-in serialization format.
|
248 | d0fe8c12 | Athina Bekakou | |
249 | d0fe8c12 | Athina Bekakou | In that case, you can implement `serialize` yourself and
|
250 | d0fe8c12 | Athina Bekakou | return a JSON hash of your choosing.
|
251 | d0fe8c12 | Athina Bekakou | |
252 | d0fe8c12 | Athina Bekakou | ```javascript
|
253 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
254 | d0fe8c12 | Athina Bekakou | serialize: function(post, options) {
|
255 | d0fe8c12 | Athina Bekakou | var json = {
|
256 | d0fe8c12 | Athina Bekakou | POST_TTL: post.get('title'),
|
257 | d0fe8c12 | Athina Bekakou | POST_BDY: post.get('body'),
|
258 | d0fe8c12 | Athina Bekakou | POST_CMS: post.get('comments').mapProperty('id')
|
259 | d0fe8c12 | Athina Bekakou | }
|
260 | d0fe8c12 | Athina Bekakou | |
261 | d0fe8c12 | Athina Bekakou | if (options.includeId) {
|
262 | d0fe8c12 | Athina Bekakou | json.POST_ID_ = post.get('id');
|
263 | d0fe8c12 | Athina Bekakou | }
|
264 | d0fe8c12 | Athina Bekakou | |
265 | d0fe8c12 | Athina Bekakou | return json;
|
266 | d0fe8c12 | Athina Bekakou | }
|
267 | d0fe8c12 | Athina Bekakou | });
|
268 | d0fe8c12 | Athina Bekakou | ```
|
269 | d0fe8c12 | Athina Bekakou | |
270 | d0fe8c12 | Athina Bekakou | ## Customizing an App-Wide Serializer
|
271 | d0fe8c12 | Athina Bekakou | |
272 | d0fe8c12 | Athina Bekakou | If you want to define a serializer for your entire
|
273 | d0fe8c12 | Athina Bekakou | application, you'll probably want to use `eachAttribute`
|
274 | d0fe8c12 | Athina Bekakou | and `eachRelationship` on the record.
|
275 | d0fe8c12 | Athina Bekakou | |
276 | d0fe8c12 | Athina Bekakou | ```javascript
|
277 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.JSONSerializer.extend({
|
278 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) {
|
279 | d0fe8c12 | Athina Bekakou | var json = {};
|
280 | d0fe8c12 | Athina Bekakou | |
281 | d0fe8c12 | Athina Bekakou | record.eachAttribute(function(name) {
|
282 | d0fe8c12 | Athina Bekakou | json[serverAttributeName(name)] = record.get(name);
|
283 | d0fe8c12 | Athina Bekakou | })
|
284 | d0fe8c12 | Athina Bekakou | |
285 | d0fe8c12 | Athina Bekakou | record.eachRelationship(function(name, relationship) {
|
286 | d0fe8c12 | Athina Bekakou | if (relationship.kind === 'hasMany') {
|
287 | d0fe8c12 | Athina Bekakou | json[serverHasManyName(name)] = record.get(name).mapBy('id');
|
288 | d0fe8c12 | Athina Bekakou | }
|
289 | d0fe8c12 | Athina Bekakou | });
|
290 | d0fe8c12 | Athina Bekakou | |
291 | d0fe8c12 | Athina Bekakou | if (options.includeId) {
|
292 | d0fe8c12 | Athina Bekakou | json.ID_ = record.get('id');
|
293 | d0fe8c12 | Athina Bekakou | }
|
294 | d0fe8c12 | Athina Bekakou | |
295 | d0fe8c12 | Athina Bekakou | return json;
|
296 | d0fe8c12 | Athina Bekakou | }
|
297 | d0fe8c12 | Athina Bekakou | });
|
298 | d0fe8c12 | Athina Bekakou | |
299 | d0fe8c12 | Athina Bekakou | function serverAttributeName(attribute) {
|
300 | d0fe8c12 | Athina Bekakou | return attribute.underscore().toUpperCase();
|
301 | d0fe8c12 | Athina Bekakou | }
|
302 | d0fe8c12 | Athina Bekakou | |
303 | d0fe8c12 | Athina Bekakou | function serverHasManyName(name) {
|
304 | d0fe8c12 | Athina Bekakou | return serverAttributeName(name.singularize()) + "_IDS";
|
305 | d0fe8c12 | Athina Bekakou | }
|
306 | d0fe8c12 | Athina Bekakou | ```
|
307 | d0fe8c12 | Athina Bekakou | |
308 | d0fe8c12 | Athina Bekakou | This serializer will generate JSON that looks like this:
|
309 | d0fe8c12 | Athina Bekakou | |
310 | d0fe8c12 | Athina Bekakou | ```javascript
|
311 | d0fe8c12 | Athina Bekakou | {
|
312 | d0fe8c12 | Athina Bekakou | "TITLE": "Rails is omakase",
|
313 | d0fe8c12 | Athina Bekakou | "BODY": "Yep. Omakase.",
|
314 | d0fe8c12 | Athina Bekakou | "COMMENT_IDS": [ 1, 2, 3 ]
|
315 | d0fe8c12 | Athina Bekakou | }
|
316 | d0fe8c12 | Athina Bekakou | ```
|
317 | d0fe8c12 | Athina Bekakou | |
318 | d0fe8c12 | Athina Bekakou | ## Tweaking the Default JSON
|
319 | d0fe8c12 | Athina Bekakou | |
320 | d0fe8c12 | Athina Bekakou | If you just want to do some small tweaks on the default JSON,
|
321 | d0fe8c12 | Athina Bekakou | you can call super first and make the tweaks on the returned
|
322 | d0fe8c12 | Athina Bekakou | JSON.
|
323 | d0fe8c12 | Athina Bekakou | |
324 | d0fe8c12 | Athina Bekakou | ```javascript
|
325 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
326 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) {
|
327 | d0fe8c12 | Athina Bekakou | var json = this._super.apply(this, arguments);
|
328 | d0fe8c12 | Athina Bekakou | |
329 | d0fe8c12 | Athina Bekakou | json.subject = json.title;
|
330 | d0fe8c12 | Athina Bekakou | delete json.title;
|
331 | d0fe8c12 | Athina Bekakou | |
332 | d0fe8c12 | Athina Bekakou | return json;
|
333 | d0fe8c12 | Athina Bekakou | }
|
334 | d0fe8c12 | Athina Bekakou | });
|
335 | d0fe8c12 | Athina Bekakou | ```
|
336 | d0fe8c12 | Athina Bekakou | |
337 | d0fe8c12 | Athina Bekakou | @method serialize
|
338 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} record
|
339 | d0fe8c12 | Athina Bekakou | @param {Object} options
|
340 | d0fe8c12 | Athina Bekakou | @return {Object} json
|
341 | d0fe8c12 | Athina Bekakou | */
|
342 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) { |
343 | d0fe8c12 | Athina Bekakou | var json = {};
|
344 | d0fe8c12 | Athina Bekakou | |
345 | d0fe8c12 | Athina Bekakou | if (options && options.includeId) {
|
346 | d0fe8c12 | Athina Bekakou | var id = get(record, 'id'); |
347 | d0fe8c12 | Athina Bekakou | |
348 | d0fe8c12 | Athina Bekakou | if (id) {
|
349 | d0fe8c12 | Athina Bekakou | json[get(this, 'primaryKey')] = id; |
350 | d0fe8c12 | Athina Bekakou | } |
351 | d0fe8c12 | Athina Bekakou | } |
352 | d0fe8c12 | Athina Bekakou | |
353 | d0fe8c12 | Athina Bekakou | record.eachAttribute(function(key, attribute) {
|
354 | d0fe8c12 | Athina Bekakou | this.serializeAttribute(record, json, key, attribute);
|
355 | d0fe8c12 | Athina Bekakou | }, this);
|
356 | d0fe8c12 | Athina Bekakou | |
357 | d0fe8c12 | Athina Bekakou | record.eachRelationship(function(key, relationship) {
|
358 | d0fe8c12 | Athina Bekakou | if (relationship.kind === 'belongsTo') { |
359 | d0fe8c12 | Athina Bekakou | this.serializeBelongsTo(record, json, relationship);
|
360 | d0fe8c12 | Athina Bekakou | } else if (relationship.kind === 'hasMany') { |
361 | d0fe8c12 | Athina Bekakou | this.serializeHasMany(record, json, relationship);
|
362 | d0fe8c12 | Athina Bekakou | } |
363 | d0fe8c12 | Athina Bekakou | }, this);
|
364 | d0fe8c12 | Athina Bekakou | |
365 | d0fe8c12 | Athina Bekakou | return json;
|
366 | d0fe8c12 | Athina Bekakou | }, |
367 | d0fe8c12 | Athina Bekakou | |
368 | d0fe8c12 | Athina Bekakou | /**
|
369 | d0fe8c12 | Athina Bekakou | `serializeAttribute` can be used to customize how `DS.attr`
|
370 | d0fe8c12 | Athina Bekakou | properties are serialized
|
371 | d0fe8c12 | Athina Bekakou | |
372 | d0fe8c12 | Athina Bekakou | For example if you wanted to ensure all you attributes were always
|
373 | d0fe8c12 | Athina Bekakou | serialized as properties on an `attributes` object you could
|
374 | d0fe8c12 | Athina Bekakou | write:
|
375 | d0fe8c12 | Athina Bekakou | |
376 | d0fe8c12 | Athina Bekakou | ```javascript
|
377 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.JSONSerializer.extend({
|
378 | d0fe8c12 | Athina Bekakou | serializeAttribute: function(record, json, key, attributes) {
|
379 | d0fe8c12 | Athina Bekakou | json.attributes = json.attributes || {};
|
380 | d0fe8c12 | Athina Bekakou | this._super(record, json.attributes, key, attributes);
|
381 | d0fe8c12 | Athina Bekakou | }
|
382 | d0fe8c12 | Athina Bekakou | });
|
383 | d0fe8c12 | Athina Bekakou | ```
|
384 | d0fe8c12 | Athina Bekakou | |
385 | d0fe8c12 | Athina Bekakou | @method serializeAttribute
|
386 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
387 | d0fe8c12 | Athina Bekakou | @param {Object} json
|
388 | d0fe8c12 | Athina Bekakou | @param {String} key
|
389 | d0fe8c12 | Athina Bekakou | @param {Object} attribute
|
390 | d0fe8c12 | Athina Bekakou | */
|
391 | d0fe8c12 | Athina Bekakou | serializeAttribute: function(record, json, key, attribute) { |
392 | d0fe8c12 | Athina Bekakou | var attrs = get(this, 'attrs'); |
393 | d0fe8c12 | Athina Bekakou | var value = get(record, key), type = attribute.type;
|
394 | d0fe8c12 | Athina Bekakou | |
395 | d0fe8c12 | Athina Bekakou | if (type) {
|
396 | d0fe8c12 | Athina Bekakou | var transform = this.transformFor(type); |
397 | d0fe8c12 | Athina Bekakou | value = transform.serialize(value); |
398 | d0fe8c12 | Athina Bekakou | } |
399 | d0fe8c12 | Athina Bekakou | |
400 | d0fe8c12 | Athina Bekakou | // if provided, use the mapping provided by `attrs` in
|
401 | d0fe8c12 | Athina Bekakou | // the serializer
|
402 | d0fe8c12 | Athina Bekakou | key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key); |
403 | d0fe8c12 | Athina Bekakou | |
404 | d0fe8c12 | Athina Bekakou | json[key] = value; |
405 | d0fe8c12 | Athina Bekakou | }, |
406 | d0fe8c12 | Athina Bekakou | |
407 | d0fe8c12 | Athina Bekakou | /**
|
408 | d0fe8c12 | Athina Bekakou | `serializeBelongsTo` can be used to customize how `DS.belongsTo`
|
409 | d0fe8c12 | Athina Bekakou | properties are serialized.
|
410 | d0fe8c12 | Athina Bekakou | |
411 | d0fe8c12 | Athina Bekakou | Example
|
412 | d0fe8c12 | Athina Bekakou | |
413 | d0fe8c12 | Athina Bekakou | ```javascript
|
414 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
415 | d0fe8c12 | Athina Bekakou | serializeBelongsTo: function(record, json, relationship) {
|
416 | d0fe8c12 | Athina Bekakou | var key = relationship.key;
|
417 | d0fe8c12 | Athina Bekakou | |
418 | d0fe8c12 | Athina Bekakou | var belongsTo = get(record, key);
|
419 | d0fe8c12 | Athina Bekakou | |
420 | d0fe8c12 | Athina Bekakou | key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
|
421 | d0fe8c12 | Athina Bekakou | |
422 | d0fe8c12 | Athina Bekakou | json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON();
|
423 | d0fe8c12 | Athina Bekakou | }
|
424 | d0fe8c12 | Athina Bekakou | });
|
425 | d0fe8c12 | Athina Bekakou | ```
|
426 | d0fe8c12 | Athina Bekakou | |
427 | d0fe8c12 | Athina Bekakou | @method serializeBelongsTo
|
428 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
429 | d0fe8c12 | Athina Bekakou | @param {Object} json
|
430 | d0fe8c12 | Athina Bekakou | @param {Object} relationship
|
431 | d0fe8c12 | Athina Bekakou | */
|
432 | d0fe8c12 | Athina Bekakou | serializeBelongsTo: function(record, json, relationship) { |
433 | d0fe8c12 | Athina Bekakou | var key = relationship.key;
|
434 | d0fe8c12 | Athina Bekakou | |
435 | d0fe8c12 | Athina Bekakou | var belongsTo = get(record, key);
|
436 | d0fe8c12 | Athina Bekakou | |
437 | d0fe8c12 | Athina Bekakou | key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; |
438 | d0fe8c12 | Athina Bekakou | |
439 | d0fe8c12 | Athina Bekakou | if (isNone(belongsTo)) {
|
440 | d0fe8c12 | Athina Bekakou | json[key] = belongsTo; |
441 | d0fe8c12 | Athina Bekakou | } else {
|
442 | d0fe8c12 | Athina Bekakou | json[key] = get(belongsTo, 'id');
|
443 | d0fe8c12 | Athina Bekakou | } |
444 | d0fe8c12 | Athina Bekakou | |
445 | d0fe8c12 | Athina Bekakou | if (relationship.options.polymorphic) {
|
446 | d0fe8c12 | Athina Bekakou | this.serializePolymorphicType(record, json, relationship);
|
447 | d0fe8c12 | Athina Bekakou | } |
448 | d0fe8c12 | Athina Bekakou | }, |
449 | d0fe8c12 | Athina Bekakou | |
450 | d0fe8c12 | Athina Bekakou | /**
|
451 | d0fe8c12 | Athina Bekakou | `serializeHasMany` can be used to customize how `DS.hasMany`
|
452 | d0fe8c12 | Athina Bekakou | properties are serialized.
|
453 | d0fe8c12 | Athina Bekakou | |
454 | d0fe8c12 | Athina Bekakou | Example
|
455 | d0fe8c12 | Athina Bekakou | |
456 | d0fe8c12 | Athina Bekakou | ```javascript
|
457 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
458 | d0fe8c12 | Athina Bekakou | serializeHasMany: function(record, json, relationship) {
|
459 | d0fe8c12 | Athina Bekakou | var key = relationship.key;
|
460 | d0fe8c12 | Athina Bekakou | if (key === 'comments') {
|
461 | d0fe8c12 | Athina Bekakou | return;
|
462 | d0fe8c12 | Athina Bekakou | } else {
|
463 | d0fe8c12 | Athina Bekakou | this._super.apply(this, arguments);
|
464 | d0fe8c12 | Athina Bekakou | }
|
465 | d0fe8c12 | Athina Bekakou | }
|
466 | d0fe8c12 | Athina Bekakou | });
|
467 | d0fe8c12 | Athina Bekakou | ```
|
468 | d0fe8c12 | Athina Bekakou | |
469 | d0fe8c12 | Athina Bekakou | @method serializeHasMany
|
470 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
471 | d0fe8c12 | Athina Bekakou | @param {Object} json
|
472 | d0fe8c12 | Athina Bekakou | @param {Object} relationship
|
473 | d0fe8c12 | Athina Bekakou | */
|
474 | d0fe8c12 | Athina Bekakou | serializeHasMany: function(record, json, relationship) { |
475 | d0fe8c12 | Athina Bekakou | var key = relationship.key;
|
476 | d0fe8c12 | Athina Bekakou | |
477 | d0fe8c12 | Athina Bekakou | var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
|
478 | d0fe8c12 | Athina Bekakou | |
479 | d0fe8c12 | Athina Bekakou | if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') { |
480 | d0fe8c12 | Athina Bekakou | json[key] = get(record, key).mapBy('id');
|
481 | d0fe8c12 | Athina Bekakou | // TODO support for polymorphic manyToNone and manyToMany relationships
|
482 | d0fe8c12 | Athina Bekakou | } |
483 | d0fe8c12 | Athina Bekakou | }, |
484 | d0fe8c12 | Athina Bekakou | |
485 | d0fe8c12 | Athina Bekakou | /**
|
486 | d0fe8c12 | Athina Bekakou | You can use this method to customize how polymorphic objects are
|
487 | d0fe8c12 | Athina Bekakou | serialized. Objects are considered to be polymorphic if
|
488 | d0fe8c12 | Athina Bekakou | `{polymorphic: true}` is pass as the second argument to the
|
489 | d0fe8c12 | Athina Bekakou | `DS.belongsTo` function.
|
490 | d0fe8c12 | Athina Bekakou | |
491 | d0fe8c12 | Athina Bekakou | Example
|
492 | d0fe8c12 | Athina Bekakou | |
493 | d0fe8c12 | Athina Bekakou | ```javascript
|
494 | d0fe8c12 | Athina Bekakou | App.CommentSerializer = DS.JSONSerializer.extend({
|
495 | d0fe8c12 | Athina Bekakou | serializePolymorphicType: function(record, json, relationship) {
|
496 | d0fe8c12 | Athina Bekakou | var key = relationship.key,
|
497 | d0fe8c12 | Athina Bekakou | belongsTo = get(record, key);
|
498 | d0fe8c12 | Athina Bekakou | key = this.keyForAttribute ? this.keyForAttribute(key) : key;
|
499 | d0fe8c12 | Athina Bekakou | json[key + "_type"] = belongsTo.constructor.typeKey;
|
500 | d0fe8c12 | Athina Bekakou | }
|
501 | d0fe8c12 | Athina Bekakou | });
|
502 | d0fe8c12 | Athina Bekakou | ```
|
503 | d0fe8c12 | Athina Bekakou | |
504 | d0fe8c12 | Athina Bekakou | @method serializePolymorphicType
|
505 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
506 | d0fe8c12 | Athina Bekakou | @param {Object} json
|
507 | d0fe8c12 | Athina Bekakou | @param {Object} relationship
|
508 | d0fe8c12 | Athina Bekakou | */
|
509 | d0fe8c12 | Athina Bekakou | serializePolymorphicType: Ember.K,
|
510 | d0fe8c12 | Athina Bekakou | |
511 | d0fe8c12 | Athina Bekakou | // EXTRACT
|
512 | d0fe8c12 | Athina Bekakou | |
513 | d0fe8c12 | Athina Bekakou | /**
|
514 | d0fe8c12 | Athina Bekakou | The `extract` method is used to deserialize payload data from the
|
515 | d0fe8c12 | Athina Bekakou | server. By default the `JSONSerializer` does not push the records
|
516 | d0fe8c12 | Athina Bekakou | into the store. However records that subclass `JSONSerializer`
|
517 | d0fe8c12 | Athina Bekakou | such as the `RESTSerializer` may push records into the store as
|
518 | d0fe8c12 | Athina Bekakou | part of the extract call.
|
519 | d0fe8c12 | Athina Bekakou | |
520 | d0fe8c12 | Athina Bekakou | This method deletegates to a more specific extract method based on
|
521 | d0fe8c12 | Athina Bekakou | the `requestType`.
|
522 | d0fe8c12 | Athina Bekakou | |
523 | d0fe8c12 | Athina Bekakou | Example
|
524 | d0fe8c12 | Athina Bekakou | |
525 | d0fe8c12 | Athina Bekakou | ```javascript
|
526 | d0fe8c12 | Athina Bekakou | var get = Ember.get;
|
527 | d0fe8c12 | Athina Bekakou | socket.on('message', function(message) {
|
528 | d0fe8c12 | Athina Bekakou | var modelName = message.model;
|
529 | d0fe8c12 | Athina Bekakou | var data = message.data;
|
530 | d0fe8c12 | Athina Bekakou | var type = store.modelFor(modelName);
|
531 | d0fe8c12 | Athina Bekakou | var serializer = store.serializerFor(type.typeKey);
|
532 | d0fe8c12 | Athina Bekakou | var record = serializer.extract(store, type, data, get(data, 'id'), 'single');
|
533 | d0fe8c12 | Athina Bekakou | store.push(modelName, record);
|
534 | d0fe8c12 | Athina Bekakou | });
|
535 | d0fe8c12 | Athina Bekakou | ```
|
536 | d0fe8c12 | Athina Bekakou | |
537 | d0fe8c12 | Athina Bekakou | @method extract
|
538 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
539 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
540 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
541 | d0fe8c12 | Athina Bekakou | @param {String or Number} id
|
542 | d0fe8c12 | Athina Bekakou | @param {String} requestType
|
543 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
544 | d0fe8c12 | Athina Bekakou | */
|
545 | d0fe8c12 | Athina Bekakou | extract: function(store, type, payload, id, requestType) { |
546 | d0fe8c12 | Athina Bekakou | this.extractMeta(store, type, payload);
|
547 | d0fe8c12 | Athina Bekakou | |
548 | d0fe8c12 | Athina Bekakou | var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1); |
549 | d0fe8c12 | Athina Bekakou | return this[specificExtract](store, type, payload, id, requestType); |
550 | d0fe8c12 | Athina Bekakou | }, |
551 | d0fe8c12 | Athina Bekakou | |
552 | d0fe8c12 | Athina Bekakou | /**
|
553 | d0fe8c12 | Athina Bekakou | `extractFindAll` is a hook into the extract method used when a
|
554 | d0fe8c12 | Athina Bekakou | call is made to `DS.Store#findAll`. By default this method is an
|
555 | d0fe8c12 | Athina Bekakou | alias for [extractArray](#method_extractArray).
|
556 | d0fe8c12 | Athina Bekakou | |
557 | d0fe8c12 | Athina Bekakou | @method extractFindAll
|
558 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
559 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
560 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
561 | d0fe8c12 | Athina Bekakou | @return {Array} array An array of deserialized objects
|
562 | d0fe8c12 | Athina Bekakou | */
|
563 | d0fe8c12 | Athina Bekakou | extractFindAll: aliasMethod('extractArray'), |
564 | d0fe8c12 | Athina Bekakou | /**
|
565 | d0fe8c12 | Athina Bekakou | `extractFindQuery` is a hook into the extract method used when a
|
566 | d0fe8c12 | Athina Bekakou | call is made to `DS.Store#findQuery`. By default this method is an
|
567 | d0fe8c12 | Athina Bekakou | alias for [extractArray](#method_extractArray).
|
568 | d0fe8c12 | Athina Bekakou | |
569 | d0fe8c12 | Athina Bekakou | @method extractFindQuery
|
570 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
571 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
572 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
573 | d0fe8c12 | Athina Bekakou | @return {Array} array An array of deserialized objects
|
574 | d0fe8c12 | Athina Bekakou | */
|
575 | d0fe8c12 | Athina Bekakou | extractFindQuery: aliasMethod('extractArray'), |
576 | d0fe8c12 | Athina Bekakou | /**
|
577 | d0fe8c12 | Athina Bekakou | `extractFindMany` is a hook into the extract method used when a
|
578 | d0fe8c12 | Athina Bekakou | call is made to `DS.Store#findMany`. By default this method is
|
579 | d0fe8c12 | Athina Bekakou | alias for [extractArray](#method_extractArray).
|
580 | d0fe8c12 | Athina Bekakou | |
581 | d0fe8c12 | Athina Bekakou | @method extractFindMany
|
582 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
583 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
584 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
585 | d0fe8c12 | Athina Bekakou | @return {Array} array An array of deserialized objects
|
586 | d0fe8c12 | Athina Bekakou | */
|
587 | d0fe8c12 | Athina Bekakou | extractFindMany: aliasMethod('extractArray'), |
588 | d0fe8c12 | Athina Bekakou | /**
|
589 | d0fe8c12 | Athina Bekakou | `extractFindHasMany` is a hook into the extract method used when a
|
590 | d0fe8c12 | Athina Bekakou | call is made to `DS.Store#findHasMany`. By default this method is
|
591 | d0fe8c12 | Athina Bekakou | alias for [extractArray](#method_extractArray).
|
592 | d0fe8c12 | Athina Bekakou | |
593 | d0fe8c12 | Athina Bekakou | @method extractFindHasMany
|
594 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
595 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
596 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
597 | d0fe8c12 | Athina Bekakou | @return {Array} array An array of deserialized objects
|
598 | d0fe8c12 | Athina Bekakou | */
|
599 | d0fe8c12 | Athina Bekakou | extractFindHasMany: aliasMethod('extractArray'), |
600 | d0fe8c12 | Athina Bekakou | |
601 | d0fe8c12 | Athina Bekakou | /**
|
602 | d0fe8c12 | Athina Bekakou | `extractCreateRecord` is a hook into the extract method used when a
|
603 | d0fe8c12 | Athina Bekakou | call is made to `DS.Store#createRecord`. By default this method is
|
604 | d0fe8c12 | Athina Bekakou | alias for [extractSave](#method_extractSave).
|
605 | d0fe8c12 | Athina Bekakou | |
606 | d0fe8c12 | Athina Bekakou | @method extractCreateRecord
|
607 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
608 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
609 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
610 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
611 | d0fe8c12 | Athina Bekakou | */
|
612 | d0fe8c12 | Athina Bekakou | extractCreateRecord: aliasMethod('extractSave'), |
613 | d0fe8c12 | Athina Bekakou | /**
|
614 | d0fe8c12 | Athina Bekakou | `extractUpdateRecord` is a hook into the extract method used when
|
615 | d0fe8c12 | Athina Bekakou | a call is made to `DS.Store#update`. By default this method is alias
|
616 | d0fe8c12 | Athina Bekakou | for [extractSave](#method_extractSave).
|
617 | d0fe8c12 | Athina Bekakou | |
618 | d0fe8c12 | Athina Bekakou | @method extractUpdateRecord
|
619 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
620 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
621 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
622 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
623 | d0fe8c12 | Athina Bekakou | */
|
624 | d0fe8c12 | Athina Bekakou | extractUpdateRecord: aliasMethod('extractSave'), |
625 | d0fe8c12 | Athina Bekakou | /**
|
626 | d0fe8c12 | Athina Bekakou | `extractDeleteRecord` is a hook into the extract method used when
|
627 | d0fe8c12 | Athina Bekakou | a call is made to `DS.Store#deleteRecord`. By default this method is
|
628 | d0fe8c12 | Athina Bekakou | alias for [extractSave](#method_extractSave).
|
629 | d0fe8c12 | Athina Bekakou | |
630 | d0fe8c12 | Athina Bekakou | @method extractDeleteRecord
|
631 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
632 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
633 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
634 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
635 | d0fe8c12 | Athina Bekakou | */
|
636 | d0fe8c12 | Athina Bekakou | extractDeleteRecord: aliasMethod('extractSave'), |
637 | d0fe8c12 | Athina Bekakou | |
638 | d0fe8c12 | Athina Bekakou | /**
|
639 | d0fe8c12 | Athina Bekakou | `extractFind` is a hook into the extract method used when
|
640 | d0fe8c12 | Athina Bekakou | a call is made to `DS.Store#find`. By default this method is
|
641 | d0fe8c12 | Athina Bekakou | alias for [extractSingle](#method_extractSingle).
|
642 | d0fe8c12 | Athina Bekakou | |
643 | d0fe8c12 | Athina Bekakou | @method extractFind
|
644 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
645 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
646 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
647 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
648 | d0fe8c12 | Athina Bekakou | */
|
649 | d0fe8c12 | Athina Bekakou | extractFind: aliasMethod('extractSingle'), |
650 | d0fe8c12 | Athina Bekakou | /**
|
651 | d0fe8c12 | Athina Bekakou | `extractFindBelongsTo` is a hook into the extract method used when
|
652 | d0fe8c12 | Athina Bekakou | a call is made to `DS.Store#findBelongsTo`. By default this method is
|
653 | d0fe8c12 | Athina Bekakou | alias for [extractSingle](#method_extractSingle).
|
654 | d0fe8c12 | Athina Bekakou | |
655 | d0fe8c12 | Athina Bekakou | @method extractFindBelongsTo
|
656 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
657 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
658 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
659 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
660 | d0fe8c12 | Athina Bekakou | */
|
661 | d0fe8c12 | Athina Bekakou | extractFindBelongsTo: aliasMethod('extractSingle'), |
662 | d0fe8c12 | Athina Bekakou | /**
|
663 | d0fe8c12 | Athina Bekakou | `extractSave` is a hook into the extract method used when a call
|
664 | d0fe8c12 | Athina Bekakou | is made to `DS.Model#save`. By default this method is alias
|
665 | d0fe8c12 | Athina Bekakou | for [extractSingle](#method_extractSingle).
|
666 | d0fe8c12 | Athina Bekakou | |
667 | d0fe8c12 | Athina Bekakou | @method extractSave
|
668 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
669 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
670 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
671 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
672 | d0fe8c12 | Athina Bekakou | */
|
673 | d0fe8c12 | Athina Bekakou | extractSave: aliasMethod('extractSingle'), |
674 | d0fe8c12 | Athina Bekakou | |
675 | d0fe8c12 | Athina Bekakou | /**
|
676 | d0fe8c12 | Athina Bekakou | `extractSingle` is used to deserialize a single record returned
|
677 | d0fe8c12 | Athina Bekakou | from the adapter.
|
678 | d0fe8c12 | Athina Bekakou | |
679 | d0fe8c12 | Athina Bekakou | Example
|
680 | d0fe8c12 | Athina Bekakou | |
681 | d0fe8c12 | Athina Bekakou | ```javascript
|
682 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
683 | d0fe8c12 | Athina Bekakou | extractSingle: function(store, type, payload) {
|
684 | d0fe8c12 | Athina Bekakou | payload.comments = payload._embedded.comment;
|
685 | d0fe8c12 | Athina Bekakou | delete payload._embedded;
|
686 | d0fe8c12 | Athina Bekakou | |
687 | d0fe8c12 | Athina Bekakou | return this._super(store, type, payload);
|
688 | d0fe8c12 | Athina Bekakou | },
|
689 | d0fe8c12 | Athina Bekakou | });
|
690 | d0fe8c12 | Athina Bekakou | ```
|
691 | d0fe8c12 | Athina Bekakou | |
692 | d0fe8c12 | Athina Bekakou | @method extractSingle
|
693 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
694 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
695 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
696 | d0fe8c12 | Athina Bekakou | @return {Object} json The deserialized payload
|
697 | d0fe8c12 | Athina Bekakou | */
|
698 | d0fe8c12 | Athina Bekakou | extractSingle: function(store, type, payload) { |
699 | d0fe8c12 | Athina Bekakou | return this.normalize(type, payload); |
700 | d0fe8c12 | Athina Bekakou | }, |
701 | d0fe8c12 | Athina Bekakou | |
702 | d0fe8c12 | Athina Bekakou | /**
|
703 | d0fe8c12 | Athina Bekakou | `extractArray` is used to deserialize an array of records
|
704 | d0fe8c12 | Athina Bekakou | returned from the adapter.
|
705 | d0fe8c12 | Athina Bekakou | |
706 | d0fe8c12 | Athina Bekakou | Example
|
707 | d0fe8c12 | Athina Bekakou | |
708 | d0fe8c12 | Athina Bekakou | ```javascript
|
709 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
710 | d0fe8c12 | Athina Bekakou | extractArray: function(store, type, payload) {
|
711 | d0fe8c12 | Athina Bekakou | return payload.map(function(json) {
|
712 | d0fe8c12 | Athina Bekakou | return this.extractSingle(json);
|
713 | d0fe8c12 | Athina Bekakou | }, this);
|
714 | d0fe8c12 | Athina Bekakou | }
|
715 | d0fe8c12 | Athina Bekakou | });
|
716 | d0fe8c12 | Athina Bekakou | ```
|
717 | d0fe8c12 | Athina Bekakou | |
718 | d0fe8c12 | Athina Bekakou | @method extractArray
|
719 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
720 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
721 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
722 | d0fe8c12 | Athina Bekakou | @return {Array} array An array of deserialized objects
|
723 | d0fe8c12 | Athina Bekakou | */
|
724 | d0fe8c12 | Athina Bekakou | extractArray: function(store, type, payload) { |
725 | d0fe8c12 | Athina Bekakou | return this.normalize(type, payload); |
726 | d0fe8c12 | Athina Bekakou | }, |
727 | d0fe8c12 | Athina Bekakou | |
728 | d0fe8c12 | Athina Bekakou | /**
|
729 | d0fe8c12 | Athina Bekakou | `extractMeta` is used to deserialize any meta information in the
|
730 | d0fe8c12 | Athina Bekakou | adapter payload. By default Ember Data expects meta information to
|
731 | d0fe8c12 | Athina Bekakou | be located on the `meta` property of the payload object.
|
732 | d0fe8c12 | Athina Bekakou | |
733 | d0fe8c12 | Athina Bekakou | Example
|
734 | d0fe8c12 | Athina Bekakou | |
735 | d0fe8c12 | Athina Bekakou | ```javascript
|
736 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
737 | d0fe8c12 | Athina Bekakou | extractMeta: function(store, type, payload) {
|
738 | d0fe8c12 | Athina Bekakou | if (payload && payload._pagination) {
|
739 | d0fe8c12 | Athina Bekakou | store.metaForType(type, payload._pagination);
|
740 | d0fe8c12 | Athina Bekakou | delete payload._pagination;
|
741 | d0fe8c12 | Athina Bekakou | }
|
742 | d0fe8c12 | Athina Bekakou | }
|
743 | d0fe8c12 | Athina Bekakou | });
|
744 | d0fe8c12 | Athina Bekakou | ```
|
745 | d0fe8c12 | Athina Bekakou | |
746 | d0fe8c12 | Athina Bekakou | @method extractMeta
|
747 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
748 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
749 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
750 | d0fe8c12 | Athina Bekakou | */
|
751 | d0fe8c12 | Athina Bekakou | extractMeta: function(store, type, payload) { |
752 | d0fe8c12 | Athina Bekakou | if (payload && payload.meta) {
|
753 | d0fe8c12 | Athina Bekakou | store.metaForType(type, payload.meta); |
754 | d0fe8c12 | Athina Bekakou | delete payload.meta;
|
755 | d0fe8c12 | Athina Bekakou | } |
756 | d0fe8c12 | Athina Bekakou | }, |
757 | d0fe8c12 | Athina Bekakou | |
758 | d0fe8c12 | Athina Bekakou | /**
|
759 | d0fe8c12 | Athina Bekakou | `keyForAttribute` can be used to define rules for how to convert an
|
760 | d0fe8c12 | Athina Bekakou | attribute name in your model to a key in your JSON.
|
761 | d0fe8c12 | Athina Bekakou | |
762 | d0fe8c12 | Athina Bekakou | Example
|
763 | d0fe8c12 | Athina Bekakou | |
764 | d0fe8c12 | Athina Bekakou | ```javascript
|
765 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.RESTSerializer.extend({
|
766 | d0fe8c12 | Athina Bekakou | keyForAttribute: function(attr) {
|
767 | d0fe8c12 | Athina Bekakou | return Ember.String.underscore(attr).toUpperCase();
|
768 | d0fe8c12 | Athina Bekakou | }
|
769 | d0fe8c12 | Athina Bekakou | });
|
770 | d0fe8c12 | Athina Bekakou | ```
|
771 | d0fe8c12 | Athina Bekakou | |
772 | d0fe8c12 | Athina Bekakou | @method keyForAttribute
|
773 | d0fe8c12 | Athina Bekakou | @param {String} key
|
774 | d0fe8c12 | Athina Bekakou | @return {String} normalized key
|
775 | d0fe8c12 | Athina Bekakou | */
|
776 | d0fe8c12 | Athina Bekakou | |
777 | d0fe8c12 | Athina Bekakou | |
778 | d0fe8c12 | Athina Bekakou | /**
|
779 | d0fe8c12 | Athina Bekakou | `keyForRelationship` can be used to define a custom key when
|
780 | d0fe8c12 | Athina Bekakou | serializeing relationship properties. By default `JSONSerializer`
|
781 | d0fe8c12 | Athina Bekakou | does not provide an implementation of this method.
|
782 | d0fe8c12 | Athina Bekakou | |
783 | d0fe8c12 | Athina Bekakou | Example
|
784 | d0fe8c12 | Athina Bekakou | |
785 | d0fe8c12 | Athina Bekakou | ```javascript
|
786 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.JSONSerializer.extend({
|
787 | d0fe8c12 | Athina Bekakou | keyForRelationship: function(key, relationship) {
|
788 | d0fe8c12 | Athina Bekakou | return 'rel_' + Ember.String.underscore(key);
|
789 | d0fe8c12 | Athina Bekakou | }
|
790 | d0fe8c12 | Athina Bekakou | });
|
791 | d0fe8c12 | Athina Bekakou | ```
|
792 | d0fe8c12 | Athina Bekakou | |
793 | d0fe8c12 | Athina Bekakou | @method keyForRelationship
|
794 | d0fe8c12 | Athina Bekakou | @param {String} key
|
795 | d0fe8c12 | Athina Bekakou | @param {String} relationship type
|
796 | d0fe8c12 | Athina Bekakou | @return {String} normalized key
|
797 | d0fe8c12 | Athina Bekakou | */
|
798 | d0fe8c12 | Athina Bekakou | |
799 | d0fe8c12 | Athina Bekakou | // HELPERS
|
800 | d0fe8c12 | Athina Bekakou | |
801 | d0fe8c12 | Athina Bekakou | /**
|
802 | d0fe8c12 | Athina Bekakou | @method transformFor
|
803 | d0fe8c12 | Athina Bekakou | @private
|
804 | d0fe8c12 | Athina Bekakou | @param {String} attributeType
|
805 | d0fe8c12 | Athina Bekakou | @param {Boolean} skipAssertion
|
806 | d0fe8c12 | Athina Bekakou | @return {DS.Transform} transform
|
807 | d0fe8c12 | Athina Bekakou | */
|
808 | d0fe8c12 | Athina Bekakou | transformFor: function(attributeType, skipAssertion) { |
809 | d0fe8c12 | Athina Bekakou | var transform = this.container.lookup('transform:' + attributeType); |
810 | d0fe8c12 | Athina Bekakou | Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform); |
811 | d0fe8c12 | Athina Bekakou | return transform;
|
812 | d0fe8c12 | Athina Bekakou | } |
813 | d0fe8c12 | Athina Bekakou | }); |
814 | d0fe8c12 | Athina Bekakou | |
815 | d0fe8c12 | Athina Bekakou | })(); |
816 | d0fe8c12 | Athina Bekakou | |
817 | d0fe8c12 | Athina Bekakou | |
818 | d0fe8c12 | Athina Bekakou | |
819 | d0fe8c12 | Athina Bekakou | (function() {
|
820 | d0fe8c12 | Athina Bekakou | /**
|
821 | d0fe8c12 | Athina Bekakou | @module ember-data
|
822 | d0fe8c12 | Athina Bekakou | */
|
823 | d0fe8c12 | Athina Bekakou | var get = Ember.get, capitalize = Ember.String.capitalize, underscore = Ember.String.underscore, DS = window.DS ;
|
824 | d0fe8c12 | Athina Bekakou | |
825 | d0fe8c12 | Athina Bekakou | /**
|
826 | d0fe8c12 | Athina Bekakou | Extend `Ember.DataAdapter` with ED specific code.
|
827 | d0fe8c12 | Athina Bekakou | |
828 | d0fe8c12 | Athina Bekakou | @class DebugAdapter
|
829 | d0fe8c12 | Athina Bekakou | @namespace DS
|
830 | d0fe8c12 | Athina Bekakou | @extends Ember.DataAdapter
|
831 | d0fe8c12 | Athina Bekakou | @private
|
832 | d0fe8c12 | Athina Bekakou | */
|
833 | d0fe8c12 | Athina Bekakou | DS.DebugAdapter = Ember.DataAdapter.extend({ |
834 | d0fe8c12 | Athina Bekakou | getFilters: function() { |
835 | d0fe8c12 | Athina Bekakou | return [
|
836 | d0fe8c12 | Athina Bekakou | { name: 'isNew', desc: 'New' }, |
837 | d0fe8c12 | Athina Bekakou | { name: 'isModified', desc: 'Modified' }, |
838 | d0fe8c12 | Athina Bekakou | { name: 'isClean', desc: 'Clean' } |
839 | d0fe8c12 | Athina Bekakou | ]; |
840 | d0fe8c12 | Athina Bekakou | }, |
841 | d0fe8c12 | Athina Bekakou | |
842 | d0fe8c12 | Athina Bekakou | detect: function(klass) { |
843 | d0fe8c12 | Athina Bekakou | return klass !== DS.Model && DS.Model.detect(klass);
|
844 | d0fe8c12 | Athina Bekakou | }, |
845 | d0fe8c12 | Athina Bekakou | |
846 | d0fe8c12 | Athina Bekakou | columnsForType: function(type) { |
847 | d0fe8c12 | Athina Bekakou | var columns = [{ name: 'id', desc: 'Id' }], count = 0, self = this; |
848 | d0fe8c12 | Athina Bekakou | get(type, 'attributes').forEach(function(name, meta) { |
849 | d0fe8c12 | Athina Bekakou | if (count++ > self.attributeLimit) { return false; } |
850 | d0fe8c12 | Athina Bekakou | var desc = capitalize(underscore(name).replace('_', ' ')); |
851 | d0fe8c12 | Athina Bekakou | columns.push({ name: name, desc: desc }); |
852 | d0fe8c12 | Athina Bekakou | }); |
853 | d0fe8c12 | Athina Bekakou | return columns;
|
854 | d0fe8c12 | Athina Bekakou | }, |
855 | d0fe8c12 | Athina Bekakou | |
856 | d0fe8c12 | Athina Bekakou | getRecords: function(type) { |
857 | d0fe8c12 | Athina Bekakou | return this.get('store').all(type); |
858 | d0fe8c12 | Athina Bekakou | }, |
859 | d0fe8c12 | Athina Bekakou | |
860 | d0fe8c12 | Athina Bekakou | getRecordColumnValues: function(record) { |
861 | d0fe8c12 | Athina Bekakou | var self = this, count = 0, |
862 | d0fe8c12 | Athina Bekakou | columnValues = { id: get(record, 'id') }; |
863 | d0fe8c12 | Athina Bekakou | |
864 | d0fe8c12 | Athina Bekakou | record.eachAttribute(function(key) {
|
865 | d0fe8c12 | Athina Bekakou | if (count++ > self.attributeLimit) {
|
866 | d0fe8c12 | Athina Bekakou | return false; |
867 | d0fe8c12 | Athina Bekakou | } |
868 | d0fe8c12 | Athina Bekakou | var value = get(record, key);
|
869 | d0fe8c12 | Athina Bekakou | columnValues[key] = value; |
870 | d0fe8c12 | Athina Bekakou | }); |
871 | d0fe8c12 | Athina Bekakou | return columnValues;
|
872 | d0fe8c12 | Athina Bekakou | }, |
873 | d0fe8c12 | Athina Bekakou | |
874 | d0fe8c12 | Athina Bekakou | getRecordKeywords: function(record) { |
875 | d0fe8c12 | Athina Bekakou | var keywords = [], keys = Ember.A(['id']); |
876 | d0fe8c12 | Athina Bekakou | record.eachAttribute(function(key) {
|
877 | d0fe8c12 | Athina Bekakou | keys.push(key); |
878 | d0fe8c12 | Athina Bekakou | }); |
879 | d0fe8c12 | Athina Bekakou | keys.forEach(function(key) {
|
880 | d0fe8c12 | Athina Bekakou | keywords.push(get(record, key)); |
881 | d0fe8c12 | Athina Bekakou | }); |
882 | d0fe8c12 | Athina Bekakou | return keywords;
|
883 | d0fe8c12 | Athina Bekakou | }, |
884 | d0fe8c12 | Athina Bekakou | |
885 | d0fe8c12 | Athina Bekakou | getRecordFilterValues: function(record) { |
886 | d0fe8c12 | Athina Bekakou | return {
|
887 | d0fe8c12 | Athina Bekakou | isNew: record.get('isNew'), |
888 | d0fe8c12 | Athina Bekakou | isModified: record.get('isDirty') && !record.get('isNew'), |
889 | d0fe8c12 | Athina Bekakou | isClean: !record.get('isDirty') |
890 | d0fe8c12 | Athina Bekakou | }; |
891 | d0fe8c12 | Athina Bekakou | }, |
892 | d0fe8c12 | Athina Bekakou | |
893 | d0fe8c12 | Athina Bekakou | getRecordColor: function(record) { |
894 | d0fe8c12 | Athina Bekakou | var color = 'black'; |
895 | d0fe8c12 | Athina Bekakou | if (record.get('isNew')) { |
896 | d0fe8c12 | Athina Bekakou | color = 'green';
|
897 | d0fe8c12 | Athina Bekakou | } else if (record.get('isDirty')) { |
898 | d0fe8c12 | Athina Bekakou | color = 'blue';
|
899 | d0fe8c12 | Athina Bekakou | } |
900 | d0fe8c12 | Athina Bekakou | return color;
|
901 | d0fe8c12 | Athina Bekakou | }, |
902 | d0fe8c12 | Athina Bekakou | |
903 | d0fe8c12 | Athina Bekakou | observeRecord: function(record, recordUpdated) { |
904 | d0fe8c12 | Athina Bekakou | var releaseMethods = Ember.A(), self = this, |
905 | d0fe8c12 | Athina Bekakou | keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); |
906 | d0fe8c12 | Athina Bekakou | |
907 | d0fe8c12 | Athina Bekakou | record.eachAttribute(function(key) {
|
908 | d0fe8c12 | Athina Bekakou | keysToObserve.push(key); |
909 | d0fe8c12 | Athina Bekakou | }); |
910 | d0fe8c12 | Athina Bekakou | |
911 | d0fe8c12 | Athina Bekakou | keysToObserve.forEach(function(key) {
|
912 | d0fe8c12 | Athina Bekakou | var handler = function() { |
913 | d0fe8c12 | Athina Bekakou | recordUpdated(self.wrapRecord(record)); |
914 | d0fe8c12 | Athina Bekakou | }; |
915 | d0fe8c12 | Athina Bekakou | Ember.addObserver(record, key, handler); |
916 | d0fe8c12 | Athina Bekakou | releaseMethods.push(function() {
|
917 | d0fe8c12 | Athina Bekakou | Ember.removeObserver(record, key, handler); |
918 | d0fe8c12 | Athina Bekakou | }); |
919 | d0fe8c12 | Athina Bekakou | }); |
920 | d0fe8c12 | Athina Bekakou | |
921 | d0fe8c12 | Athina Bekakou | var release = function() { |
922 | d0fe8c12 | Athina Bekakou | releaseMethods.forEach(function(fn) { fn(); } );
|
923 | d0fe8c12 | Athina Bekakou | }; |
924 | d0fe8c12 | Athina Bekakou | |
925 | d0fe8c12 | Athina Bekakou | return release;
|
926 | d0fe8c12 | Athina Bekakou | } |
927 | d0fe8c12 | Athina Bekakou | |
928 | d0fe8c12 | Athina Bekakou | }); |
929 | d0fe8c12 | Athina Bekakou | |
930 | d0fe8c12 | Athina Bekakou | })(); |
931 | d0fe8c12 | Athina Bekakou | |
932 | d0fe8c12 | Athina Bekakou | |
933 | d0fe8c12 | Athina Bekakou | |
934 | d0fe8c12 | Athina Bekakou | (function() {
|
935 | d0fe8c12 | Athina Bekakou | /**
|
936 | d0fe8c12 | Athina Bekakou | The `DS.Transform` class is used to serialize and deserialize model
|
937 | d0fe8c12 | Athina Bekakou | attributes when they are saved or loaded from an
|
938 | d0fe8c12 | Athina Bekakou | adapter. Subclassing `DS.Transform` is useful for creating custom
|
939 | d0fe8c12 | Athina Bekakou | attributes. All subclasses of `DS.Transform` must implement a
|
940 | d0fe8c12 | Athina Bekakou | `serialize` and a `deserialize` method.
|
941 | d0fe8c12 | Athina Bekakou | |
942 | d0fe8c12 | Athina Bekakou | Example
|
943 | d0fe8c12 | Athina Bekakou | |
944 | d0fe8c12 | Athina Bekakou | ```javascript
|
945 | d0fe8c12 | Athina Bekakou | App.RawTransform = DS.Transform.extend({
|
946 | d0fe8c12 | Athina Bekakou | deserialize: function(serialized) {
|
947 | d0fe8c12 | Athina Bekakou | return serialized;
|
948 | d0fe8c12 | Athina Bekakou | },
|
949 | d0fe8c12 | Athina Bekakou | serialize: function(deserialized) {
|
950 | d0fe8c12 | Athina Bekakou | return deserialized;
|
951 | d0fe8c12 | Athina Bekakou | }
|
952 | d0fe8c12 | Athina Bekakou | });
|
953 | d0fe8c12 | Athina Bekakou | ```
|
954 | d0fe8c12 | Athina Bekakou | |
955 | d0fe8c12 | Athina Bekakou | Usage
|
956 | d0fe8c12 | Athina Bekakou | |
957 | d0fe8c12 | Athina Bekakou | ```javascript
|
958 | d0fe8c12 | Athina Bekakou | var attr = DS.attr;
|
959 | d0fe8c12 | Athina Bekakou | App.Requirement = DS.Model.extend({
|
960 | d0fe8c12 | Athina Bekakou | name: attr('string'),
|
961 | d0fe8c12 | Athina Bekakou | optionsArray: attr('raw')
|
962 | d0fe8c12 | Athina Bekakou | });
|
963 | d0fe8c12 | Athina Bekakou | ```
|
964 | d0fe8c12 | Athina Bekakou | |
965 | d0fe8c12 | Athina Bekakou | @class Transform
|
966 | d0fe8c12 | Athina Bekakou | @namespace DS
|
967 | d0fe8c12 | Athina Bekakou | */
|
968 | d0fe8c12 | Athina Bekakou | DS.Transform = Ember.Object.extend({ |
969 | d0fe8c12 | Athina Bekakou | /**
|
970 | d0fe8c12 | Athina Bekakou | When given a deserialized value from a record attribute this
|
971 | d0fe8c12 | Athina Bekakou | method must return the serialized value.
|
972 | d0fe8c12 | Athina Bekakou | |
973 | d0fe8c12 | Athina Bekakou | Example
|
974 | d0fe8c12 | Athina Bekakou | |
975 | d0fe8c12 | Athina Bekakou | ```javascript
|
976 | d0fe8c12 | Athina Bekakou | serialize: function(deserialized) {
|
977 | d0fe8c12 | Athina Bekakou | return Ember.isEmpty(deserialized) ? null : Number(deserialized);
|
978 | d0fe8c12 | Athina Bekakou | }
|
979 | d0fe8c12 | Athina Bekakou | ```
|
980 | d0fe8c12 | Athina Bekakou | |
981 | d0fe8c12 | Athina Bekakou | @method serialize
|
982 | d0fe8c12 | Athina Bekakou | @param deserialized The deserialized value
|
983 | d0fe8c12 | Athina Bekakou | @return The serialized value
|
984 | d0fe8c12 | Athina Bekakou | */
|
985 | d0fe8c12 | Athina Bekakou | serialize: Ember.required(),
|
986 | d0fe8c12 | Athina Bekakou | |
987 | d0fe8c12 | Athina Bekakou | /**
|
988 | d0fe8c12 | Athina Bekakou | When given a serialize value from a JSON object this method must
|
989 | d0fe8c12 | Athina Bekakou | return the deserialized value for the record attribute.
|
990 | d0fe8c12 | Athina Bekakou | |
991 | d0fe8c12 | Athina Bekakou | Example
|
992 | d0fe8c12 | Athina Bekakou | |
993 | d0fe8c12 | Athina Bekakou | ```javascript
|
994 | d0fe8c12 | Athina Bekakou | deserialize: function(serialized) {
|
995 | d0fe8c12 | Athina Bekakou | return empty(serialized) ? null : Number(serialized);
|
996 | d0fe8c12 | Athina Bekakou | }
|
997 | d0fe8c12 | Athina Bekakou | ```
|
998 | d0fe8c12 | Athina Bekakou | |
999 | d0fe8c12 | Athina Bekakou | @method deserialize
|
1000 | d0fe8c12 | Athina Bekakou | @param serialized The serialized value
|
1001 | d0fe8c12 | Athina Bekakou | @return The deserialized value
|
1002 | d0fe8c12 | Athina Bekakou | */
|
1003 | d0fe8c12 | Athina Bekakou | deserialize: Ember.required()
|
1004 | d0fe8c12 | Athina Bekakou | |
1005 | d0fe8c12 | Athina Bekakou | }); |
1006 | d0fe8c12 | Athina Bekakou | |
1007 | d0fe8c12 | Athina Bekakou | })(); |
1008 | d0fe8c12 | Athina Bekakou | |
1009 | d0fe8c12 | Athina Bekakou | |
1010 | d0fe8c12 | Athina Bekakou | |
1011 | d0fe8c12 | Athina Bekakou | (function() {
|
1012 | d0fe8c12 | Athina Bekakou | |
1013 | d0fe8c12 | Athina Bekakou | /**
|
1014 | d0fe8c12 | Athina Bekakou | The `DS.BooleanTransform` class is used to serialize and deserialize
|
1015 | d0fe8c12 | Athina Bekakou | boolean attributes on Ember Data record objects. This transform is
|
1016 | d0fe8c12 | Athina Bekakou | used when `boolean` is passed as the type parameter to the
|
1017 | d0fe8c12 | Athina Bekakou | [DS.attr](../../data#method_attr) function.
|
1018 | d0fe8c12 | Athina Bekakou | |
1019 | d0fe8c12 | Athina Bekakou | Usage
|
1020 | d0fe8c12 | Athina Bekakou | |
1021 | d0fe8c12 | Athina Bekakou | ```javascript
|
1022 | d0fe8c12 | Athina Bekakou | var attr = DS.attr;
|
1023 | d0fe8c12 | Athina Bekakou | App.User = DS.Model.extend({
|
1024 | d0fe8c12 | Athina Bekakou | isAdmin: attr('boolean'),
|
1025 | d0fe8c12 | Athina Bekakou | name: attr('string'),
|
1026 | d0fe8c12 | Athina Bekakou | email: attr('string')
|
1027 | d0fe8c12 | Athina Bekakou | });
|
1028 | d0fe8c12 | Athina Bekakou | ```
|
1029 | d0fe8c12 | Athina Bekakou | |
1030 | d0fe8c12 | Athina Bekakou | @class BooleanTransform
|
1031 | d0fe8c12 | Athina Bekakou | @extends DS.Transform
|
1032 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1033 | d0fe8c12 | Athina Bekakou | */
|
1034 | d0fe8c12 | Athina Bekakou | DS.BooleanTransform = DS.Transform.extend({ |
1035 | d0fe8c12 | Athina Bekakou | deserialize: function(serialized) { |
1036 | d0fe8c12 | Athina Bekakou | var type = typeof serialized; |
1037 | d0fe8c12 | Athina Bekakou | |
1038 | d0fe8c12 | Athina Bekakou | if (type === "boolean") { |
1039 | d0fe8c12 | Athina Bekakou | return serialized;
|
1040 | d0fe8c12 | Athina Bekakou | } else if (type === "string") { |
1041 | d0fe8c12 | Athina Bekakou | return serialized.match(/^true$|^t$|^1$/i) !== null; |
1042 | d0fe8c12 | Athina Bekakou | } else if (type === "number") { |
1043 | d0fe8c12 | Athina Bekakou | return serialized === 1; |
1044 | d0fe8c12 | Athina Bekakou | } else {
|
1045 | d0fe8c12 | Athina Bekakou | return false; |
1046 | d0fe8c12 | Athina Bekakou | } |
1047 | d0fe8c12 | Athina Bekakou | }, |
1048 | d0fe8c12 | Athina Bekakou | |
1049 | d0fe8c12 | Athina Bekakou | serialize: function(deserialized) { |
1050 | d0fe8c12 | Athina Bekakou | return Boolean(deserialized);
|
1051 | d0fe8c12 | Athina Bekakou | } |
1052 | d0fe8c12 | Athina Bekakou | }); |
1053 | d0fe8c12 | Athina Bekakou | |
1054 | d0fe8c12 | Athina Bekakou | })(); |
1055 | d0fe8c12 | Athina Bekakou | |
1056 | d0fe8c12 | Athina Bekakou | |
1057 | d0fe8c12 | Athina Bekakou | |
1058 | d0fe8c12 | Athina Bekakou | (function() {
|
1059 | d0fe8c12 | Athina Bekakou | /**
|
1060 | d0fe8c12 | Athina Bekakou | The `DS.DateTransform` class is used to serialize and deserialize
|
1061 | d0fe8c12 | Athina Bekakou | date attributes on Ember Data record objects. This transform is used
|
1062 | d0fe8c12 | Athina Bekakou | when `date` is passed as the type parameter to the
|
1063 | d0fe8c12 | Athina Bekakou | [DS.attr](../../data#method_attr) function.
|
1064 | d0fe8c12 | Athina Bekakou | |
1065 | d0fe8c12 | Athina Bekakou | ```javascript
|
1066 | d0fe8c12 | Athina Bekakou | var attr = DS.attr;
|
1067 | d0fe8c12 | Athina Bekakou | App.Score = DS.Model.extend({
|
1068 | d0fe8c12 | Athina Bekakou | value: attr('number'),
|
1069 | d0fe8c12 | Athina Bekakou | player: DS.belongsTo('player'),
|
1070 | d0fe8c12 | Athina Bekakou | date: attr('date')
|
1071 | d0fe8c12 | Athina Bekakou | });
|
1072 | d0fe8c12 | Athina Bekakou | ```
|
1073 | d0fe8c12 | Athina Bekakou | |
1074 | d0fe8c12 | Athina Bekakou | @class DateTransform
|
1075 | d0fe8c12 | Athina Bekakou | @extends DS.Transform
|
1076 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1077 | d0fe8c12 | Athina Bekakou | */
|
1078 | d0fe8c12 | Athina Bekakou | DS.DateTransform = DS.Transform.extend({ |
1079 | d0fe8c12 | Athina Bekakou | |
1080 | d0fe8c12 | Athina Bekakou | deserialize: function(serialized) { |
1081 | d0fe8c12 | Athina Bekakou | var type = typeof serialized; |
1082 | d0fe8c12 | Athina Bekakou | |
1083 | d0fe8c12 | Athina Bekakou | if (type === "string") { |
1084 | d0fe8c12 | Athina Bekakou | return new Date(Ember.Date.parse(serialized)); |
1085 | d0fe8c12 | Athina Bekakou | } else if (type === "number") { |
1086 | d0fe8c12 | Athina Bekakou | return new Date(serialized); |
1087 | d0fe8c12 | Athina Bekakou | } else if (serialized === null || serialized === undefined) { |
1088 | d0fe8c12 | Athina Bekakou | // if the value is not present in the data,
|
1089 | d0fe8c12 | Athina Bekakou | // return undefined, not null.
|
1090 | d0fe8c12 | Athina Bekakou | return serialized;
|
1091 | d0fe8c12 | Athina Bekakou | } else {
|
1092 | d0fe8c12 | Athina Bekakou | return null; |
1093 | d0fe8c12 | Athina Bekakou | } |
1094 | d0fe8c12 | Athina Bekakou | }, |
1095 | d0fe8c12 | Athina Bekakou | |
1096 | d0fe8c12 | Athina Bekakou | serialize: function(date) { |
1097 | d0fe8c12 | Athina Bekakou | if (date instanceof Date) { |
1098 | d0fe8c12 | Athina Bekakou | var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; |
1099 | d0fe8c12 | Athina Bekakou | var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; |
1100 | d0fe8c12 | Athina Bekakou | |
1101 | d0fe8c12 | Athina Bekakou | var pad = function(num) { |
1102 | d0fe8c12 | Athina Bekakou | return num < 10 ? "0"+num : ""+num; |
1103 | d0fe8c12 | Athina Bekakou | }; |
1104 | d0fe8c12 | Athina Bekakou | |
1105 | d0fe8c12 | Athina Bekakou | var utcYear = date.getUTCFullYear(),
|
1106 | d0fe8c12 | Athina Bekakou | utcMonth = date.getUTCMonth(), |
1107 | d0fe8c12 | Athina Bekakou | utcDayOfMonth = date.getUTCDate(), |
1108 | d0fe8c12 | Athina Bekakou | utcDay = date.getUTCDay(), |
1109 | d0fe8c12 | Athina Bekakou | utcHours = date.getUTCHours(), |
1110 | d0fe8c12 | Athina Bekakou | utcMinutes = date.getUTCMinutes(), |
1111 | d0fe8c12 | Athina Bekakou | utcSeconds = date.getUTCSeconds(); |
1112 | d0fe8c12 | Athina Bekakou | |
1113 | d0fe8c12 | Athina Bekakou | |
1114 | d0fe8c12 | Athina Bekakou | var dayOfWeek = days[utcDay];
|
1115 | d0fe8c12 | Athina Bekakou | var dayOfMonth = pad(utcDayOfMonth);
|
1116 | d0fe8c12 | Athina Bekakou | var month = months[utcMonth];
|
1117 | d0fe8c12 | Athina Bekakou | |
1118 | d0fe8c12 | Athina Bekakou | return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " + |
1119 | d0fe8c12 | Athina Bekakou | pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT"; |
1120 | d0fe8c12 | Athina Bekakou | } else {
|
1121 | d0fe8c12 | Athina Bekakou | return null; |
1122 | d0fe8c12 | Athina Bekakou | } |
1123 | d0fe8c12 | Athina Bekakou | } |
1124 | d0fe8c12 | Athina Bekakou | |
1125 | d0fe8c12 | Athina Bekakou | }); |
1126 | d0fe8c12 | Athina Bekakou | |
1127 | d0fe8c12 | Athina Bekakou | })(); |
1128 | d0fe8c12 | Athina Bekakou | |
1129 | d0fe8c12 | Athina Bekakou | |
1130 | d0fe8c12 | Athina Bekakou | |
1131 | d0fe8c12 | Athina Bekakou | (function() {
|
1132 | d0fe8c12 | Athina Bekakou | var empty = Ember.isEmpty;
|
1133 | d0fe8c12 | Athina Bekakou | /**
|
1134 | d0fe8c12 | Athina Bekakou | The `DS.NumberTransform` class is used to serialize and deserialize
|
1135 | d0fe8c12 | Athina Bekakou | numeric attributes on Ember Data record objects. This transform is
|
1136 | d0fe8c12 | Athina Bekakou | used when `number` is passed as the type parameter to the
|
1137 | d0fe8c12 | Athina Bekakou | [DS.attr](../../data#method_attr) function.
|
1138 | d0fe8c12 | Athina Bekakou | |
1139 | d0fe8c12 | Athina Bekakou | Usage
|
1140 | d0fe8c12 | Athina Bekakou | |
1141 | d0fe8c12 | Athina Bekakou | ```javascript
|
1142 | d0fe8c12 | Athina Bekakou | var attr = DS.attr;
|
1143 | d0fe8c12 | Athina Bekakou | App.Score = DS.Model.extend({
|
1144 | d0fe8c12 | Athina Bekakou | value: attr('number'),
|
1145 | d0fe8c12 | Athina Bekakou | player: DS.belongsTo('player'),
|
1146 | d0fe8c12 | Athina Bekakou | date: attr('date')
|
1147 | d0fe8c12 | Athina Bekakou | });
|
1148 | d0fe8c12 | Athina Bekakou | ```
|
1149 | d0fe8c12 | Athina Bekakou | |
1150 | d0fe8c12 | Athina Bekakou | @class NumberTransform
|
1151 | d0fe8c12 | Athina Bekakou | @extends DS.Transform
|
1152 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1153 | d0fe8c12 | Athina Bekakou | */
|
1154 | d0fe8c12 | Athina Bekakou | DS.NumberTransform = DS.Transform.extend({ |
1155 | d0fe8c12 | Athina Bekakou | |
1156 | d0fe8c12 | Athina Bekakou | deserialize: function(serialized) { |
1157 | d0fe8c12 | Athina Bekakou | return empty(serialized) ? null : Number(serialized); |
1158 | d0fe8c12 | Athina Bekakou | }, |
1159 | d0fe8c12 | Athina Bekakou | |
1160 | d0fe8c12 | Athina Bekakou | serialize: function(deserialized) { |
1161 | d0fe8c12 | Athina Bekakou | return empty(deserialized) ? null : Number(deserialized); |
1162 | d0fe8c12 | Athina Bekakou | } |
1163 | d0fe8c12 | Athina Bekakou | }); |
1164 | d0fe8c12 | Athina Bekakou | |
1165 | d0fe8c12 | Athina Bekakou | })(); |
1166 | d0fe8c12 | Athina Bekakou | |
1167 | d0fe8c12 | Athina Bekakou | |
1168 | d0fe8c12 | Athina Bekakou | |
1169 | d0fe8c12 | Athina Bekakou | (function() {
|
1170 | d0fe8c12 | Athina Bekakou | var none = Ember.isNone;
|
1171 | d0fe8c12 | Athina Bekakou | |
1172 | d0fe8c12 | Athina Bekakou | /**
|
1173 | d0fe8c12 | Athina Bekakou | The `DS.StringTransform` class is used to serialize and deserialize
|
1174 | d0fe8c12 | Athina Bekakou | string attributes on Ember Data record objects. This transform is
|
1175 | d0fe8c12 | Athina Bekakou | used when `string` is passed as the type parameter to the
|
1176 | d0fe8c12 | Athina Bekakou | [DS.attr](../../data#method_attr) function.
|
1177 | d0fe8c12 | Athina Bekakou | |
1178 | d0fe8c12 | Athina Bekakou | Usage
|
1179 | d0fe8c12 | Athina Bekakou | |
1180 | d0fe8c12 | Athina Bekakou | ```javascript
|
1181 | d0fe8c12 | Athina Bekakou | var attr = DS.attr;
|
1182 | d0fe8c12 | Athina Bekakou | App.User = DS.Model.extend({
|
1183 | d0fe8c12 | Athina Bekakou | isAdmin: attr('boolean'),
|
1184 | d0fe8c12 | Athina Bekakou | name: attr('string'),
|
1185 | d0fe8c12 | Athina Bekakou | email: attr('string')
|
1186 | d0fe8c12 | Athina Bekakou | });
|
1187 | d0fe8c12 | Athina Bekakou | ```
|
1188 | d0fe8c12 | Athina Bekakou | |
1189 | d0fe8c12 | Athina Bekakou | @class StringTransform
|
1190 | d0fe8c12 | Athina Bekakou | @extends DS.Transform
|
1191 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1192 | d0fe8c12 | Athina Bekakou | */
|
1193 | d0fe8c12 | Athina Bekakou | DS.StringTransform = DS.Transform.extend({ |
1194 | d0fe8c12 | Athina Bekakou | |
1195 | d0fe8c12 | Athina Bekakou | deserialize: function(serialized) { |
1196 | d0fe8c12 | Athina Bekakou | return none(serialized) ? null : String(serialized); |
1197 | d0fe8c12 | Athina Bekakou | }, |
1198 | d0fe8c12 | Athina Bekakou | |
1199 | d0fe8c12 | Athina Bekakou | serialize: function(deserialized) { |
1200 | d0fe8c12 | Athina Bekakou | return none(deserialized) ? null : String(deserialized); |
1201 | d0fe8c12 | Athina Bekakou | } |
1202 | d0fe8c12 | Athina Bekakou | |
1203 | d0fe8c12 | Athina Bekakou | }); |
1204 | d0fe8c12 | Athina Bekakou | |
1205 | d0fe8c12 | Athina Bekakou | })(); |
1206 | d0fe8c12 | Athina Bekakou | |
1207 | d0fe8c12 | Athina Bekakou | |
1208 | d0fe8c12 | Athina Bekakou | |
1209 | d0fe8c12 | Athina Bekakou | (function() {
|
1210 | d0fe8c12 | Athina Bekakou | |
1211 | d0fe8c12 | Athina Bekakou | })(); |
1212 | d0fe8c12 | Athina Bekakou | |
1213 | d0fe8c12 | Athina Bekakou | |
1214 | d0fe8c12 | Athina Bekakou | |
1215 | d0fe8c12 | Athina Bekakou | (function() {
|
1216 | d0fe8c12 | Athina Bekakou | /**
|
1217 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1218 | d0fe8c12 | Athina Bekakou | */
|
1219 | d0fe8c12 | Athina Bekakou | |
1220 | d0fe8c12 | Athina Bekakou | var set = Ember.set;
|
1221 | d0fe8c12 | Athina Bekakou | |
1222 | d0fe8c12 | Athina Bekakou | /*
|
1223 | d0fe8c12 | Athina Bekakou | This code registers an injection for Ember.Application.
|
1224 | d0fe8c12 | Athina Bekakou | |
1225 | d0fe8c12 | Athina Bekakou | If an Ember.js developer defines a subclass of DS.Store on their application,
|
1226 | d0fe8c12 | Athina Bekakou | this code will automatically instantiate it and make it available on the
|
1227 | d0fe8c12 | Athina Bekakou | router.
|
1228 | d0fe8c12 | Athina Bekakou | |
1229 | d0fe8c12 | Athina Bekakou | Additionally, after an application's controllers have been injected, they will
|
1230 | d0fe8c12 | Athina Bekakou | each have the store made available to them.
|
1231 | d0fe8c12 | Athina Bekakou | |
1232 | d0fe8c12 | Athina Bekakou | For example, imagine an Ember.js application with the following classes:
|
1233 | d0fe8c12 | Athina Bekakou | |
1234 | d0fe8c12 | Athina Bekakou | App.Store = DS.Store.extend({
|
1235 | d0fe8c12 | Athina Bekakou | adapter: 'custom'
|
1236 | d0fe8c12 | Athina Bekakou | });
|
1237 | d0fe8c12 | Athina Bekakou | |
1238 | d0fe8c12 | Athina Bekakou | App.PostsController = Ember.ArrayController.extend({
|
1239 | d0fe8c12 | Athina Bekakou | // ...
|
1240 | d0fe8c12 | Athina Bekakou | });
|
1241 | d0fe8c12 | Athina Bekakou | |
1242 | d0fe8c12 | Athina Bekakou | When the application is initialized, `App.Store` will automatically be
|
1243 | d0fe8c12 | Athina Bekakou | instantiated, and the instance of `App.PostsController` will have its `store`
|
1244 | d0fe8c12 | Athina Bekakou | property set to that instance.
|
1245 | d0fe8c12 | Athina Bekakou | |
1246 | d0fe8c12 | Athina Bekakou | Note that this code will only be run if the `ember-application` package is
|
1247 | d0fe8c12 | Athina Bekakou | loaded. If Ember Data is being used in an environment other than a
|
1248 | d0fe8c12 | Athina Bekakou | typical application (e.g., node.js where only `ember-runtime` is available),
|
1249 | d0fe8c12 | Athina Bekakou | this code will be ignored.
|
1250 | d0fe8c12 | Athina Bekakou | */
|
1251 | d0fe8c12 | Athina Bekakou | |
1252 | d0fe8c12 | Athina Bekakou | Ember.onLoad('Ember.Application', function(Application) { |
1253 | d0fe8c12 | Athina Bekakou | Application.initializer({ |
1254 | d0fe8c12 | Athina Bekakou | name: "store", |
1255 | d0fe8c12 | Athina Bekakou | |
1256 | d0fe8c12 | Athina Bekakou | initialize: function(container, application) { |
1257 | d0fe8c12 | Athina Bekakou | application.register('store:main', application.Store || DS.Store);
|
1258 | d0fe8c12 | Athina Bekakou | application.register('serializer:_default', DS.JSONSerializer);
|
1259 | d0fe8c12 | Athina Bekakou | application.register('serializer:_rest', DS.RESTSerializer);
|
1260 | d0fe8c12 | Athina Bekakou | application.register('adapter:_rest', DS.RESTAdapter);
|
1261 | d0fe8c12 | Athina Bekakou | |
1262 | d0fe8c12 | Athina Bekakou | // Eagerly generate the store so defaultStore is populated.
|
1263 | d0fe8c12 | Athina Bekakou | // TODO: Do this in a finisher hook
|
1264 | d0fe8c12 | Athina Bekakou | container.lookup('store:main');
|
1265 | d0fe8c12 | Athina Bekakou | } |
1266 | d0fe8c12 | Athina Bekakou | }); |
1267 | d0fe8c12 | Athina Bekakou | |
1268 | d0fe8c12 | Athina Bekakou | Application.initializer({ |
1269 | d0fe8c12 | Athina Bekakou | name: "transforms", |
1270 | d0fe8c12 | Athina Bekakou | before: "store", |
1271 | d0fe8c12 | Athina Bekakou | |
1272 | d0fe8c12 | Athina Bekakou | initialize: function(container, application) { |
1273 | d0fe8c12 | Athina Bekakou | application.register('transform:boolean', DS.BooleanTransform);
|
1274 | d0fe8c12 | Athina Bekakou | application.register('transform:date', DS.DateTransform);
|
1275 | d0fe8c12 | Athina Bekakou | application.register('transform:number', DS.NumberTransform);
|
1276 | d0fe8c12 | Athina Bekakou | application.register('transform:string', DS.StringTransform);
|
1277 | d0fe8c12 | Athina Bekakou | } |
1278 | d0fe8c12 | Athina Bekakou | }); |
1279 | d0fe8c12 | Athina Bekakou | |
1280 | d0fe8c12 | Athina Bekakou | Application.initializer({ |
1281 | d0fe8c12 | Athina Bekakou | name: "dataAdapter", |
1282 | d0fe8c12 | Athina Bekakou | before: "store", |
1283 | d0fe8c12 | Athina Bekakou | |
1284 | d0fe8c12 | Athina Bekakou | initialize: function(container, application) { |
1285 | d0fe8c12 | Athina Bekakou | application.register('dataAdapter:main', DS.DebugAdapter);
|
1286 | d0fe8c12 | Athina Bekakou | } |
1287 | d0fe8c12 | Athina Bekakou | }); |
1288 | d0fe8c12 | Athina Bekakou | |
1289 | d0fe8c12 | Athina Bekakou | Application.initializer({ |
1290 | d0fe8c12 | Athina Bekakou | name: "injectStore", |
1291 | d0fe8c12 | Athina Bekakou | before: "store", |
1292 | d0fe8c12 | Athina Bekakou | |
1293 | d0fe8c12 | Athina Bekakou | initialize: function(container, application) { |
1294 | d0fe8c12 | Athina Bekakou | application.inject('controller', 'store', 'store:main'); |
1295 | d0fe8c12 | Athina Bekakou | application.inject('route', 'store', 'store:main'); |
1296 | d0fe8c12 | Athina Bekakou | application.inject('serializer', 'store', 'store:main'); |
1297 | d0fe8c12 | Athina Bekakou | application.inject('dataAdapter', 'store', 'store:main'); |
1298 | d0fe8c12 | Athina Bekakou | } |
1299 | d0fe8c12 | Athina Bekakou | }); |
1300 | d0fe8c12 | Athina Bekakou | |
1301 | d0fe8c12 | Athina Bekakou | }); |
1302 | d0fe8c12 | Athina Bekakou | |
1303 | d0fe8c12 | Athina Bekakou | })(); |
1304 | d0fe8c12 | Athina Bekakou | |
1305 | d0fe8c12 | Athina Bekakou | |
1306 | d0fe8c12 | Athina Bekakou | |
1307 | d0fe8c12 | Athina Bekakou | (function() {
|
1308 | d0fe8c12 | Athina Bekakou | /**
|
1309 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1310 | d0fe8c12 | Athina Bekakou | */
|
1311 | d0fe8c12 | Athina Bekakou | |
1312 | d0fe8c12 | Athina Bekakou | /**
|
1313 | d0fe8c12 | Athina Bekakou | Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
|
1314 | d0fe8c12 | Athina Bekakou | |
1315 | d0fe8c12 | Athina Bekakou | © 2011 Colin Snover <http://zetafleet.com>
|
1316 | d0fe8c12 | Athina Bekakou | |
1317 | d0fe8c12 | Athina Bekakou | Released under MIT license.
|
1318 | d0fe8c12 | Athina Bekakou | |
1319 | d0fe8c12 | Athina Bekakou | @class Date
|
1320 | d0fe8c12 | Athina Bekakou | @namespace Ember
|
1321 | d0fe8c12 | Athina Bekakou | @static
|
1322 | d0fe8c12 | Athina Bekakou | */
|
1323 | d0fe8c12 | Athina Bekakou | Ember.Date = Ember.Date || {}; |
1324 | d0fe8c12 | Athina Bekakou | |
1325 | d0fe8c12 | Athina Bekakou | var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; |
1326 | d0fe8c12 | Athina Bekakou | |
1327 | d0fe8c12 | Athina Bekakou | /**
|
1328 | d0fe8c12 | Athina Bekakou | @method parse
|
1329 | d0fe8c12 | Athina Bekakou | @param date
|
1330 | d0fe8c12 | Athina Bekakou | */
|
1331 | d0fe8c12 | Athina Bekakou | Ember.Date.parse = function (date) { |
1332 | d0fe8c12 | Athina Bekakou | var timestamp, struct, minutesOffset = 0; |
1333 | d0fe8c12 | Athina Bekakou | |
1334 | d0fe8c12 | Athina Bekakou | // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
|
1335 | d0fe8c12 | Athina Bekakou | // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
|
1336 | d0fe8c12 | Athina Bekakou | // implementations could be faster
|
1337 | d0fe8c12 | Athina Bekakou | // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
|
1338 | d0fe8c12 | Athina Bekakou | if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { |
1339 | d0fe8c12 | Athina Bekakou | // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
|
1340 | d0fe8c12 | Athina Bekakou | for (var i = 0, k; (k = numericKeys[i]); ++i) { |
1341 | d0fe8c12 | Athina Bekakou | struct[k] = +struct[k] || 0;
|
1342 | d0fe8c12 | Athina Bekakou | } |
1343 | d0fe8c12 | Athina Bekakou | |
1344 | d0fe8c12 | Athina Bekakou | // allow undefined days and months
|
1345 | d0fe8c12 | Athina Bekakou | struct[2] = (+struct[2] || 1) - 1; |
1346 | d0fe8c12 | Athina Bekakou | struct[3] = +struct[3] || 1; |
1347 | d0fe8c12 | Athina Bekakou | |
1348 | d0fe8c12 | Athina Bekakou | if (struct[8] !== 'Z' && struct[9] !== undefined) { |
1349 | d0fe8c12 | Athina Bekakou | minutesOffset = struct[10] * 60 + struct[11]; |
1350 | d0fe8c12 | Athina Bekakou | |
1351 | d0fe8c12 | Athina Bekakou | if (struct[9] === '+') { |
1352 | d0fe8c12 | Athina Bekakou | minutesOffset = 0 - minutesOffset;
|
1353 | d0fe8c12 | Athina Bekakou | } |
1354 | d0fe8c12 | Athina Bekakou | } |
1355 | d0fe8c12 | Athina Bekakou | |
1356 | d0fe8c12 | Athina Bekakou | timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); |
1357 | d0fe8c12 | Athina Bekakou | } |
1358 | d0fe8c12 | Athina Bekakou | else {
|
1359 | d0fe8c12 | Athina Bekakou | timestamp = origParse ? origParse(date) : NaN;
|
1360 | d0fe8c12 | Athina Bekakou | } |
1361 | d0fe8c12 | Athina Bekakou | |
1362 | d0fe8c12 | Athina Bekakou | return timestamp;
|
1363 | d0fe8c12 | Athina Bekakou | }; |
1364 | d0fe8c12 | Athina Bekakou | |
1365 | d0fe8c12 | Athina Bekakou | if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) { |
1366 | d0fe8c12 | Athina Bekakou | Date.parse = Ember.Date.parse; |
1367 | d0fe8c12 | Athina Bekakou | } |
1368 | d0fe8c12 | Athina Bekakou | |
1369 | d0fe8c12 | Athina Bekakou | })(); |
1370 | d0fe8c12 | Athina Bekakou | |
1371 | d0fe8c12 | Athina Bekakou | |
1372 | d0fe8c12 | Athina Bekakou | |
1373 | d0fe8c12 | Athina Bekakou | (function() {
|
1374 | d0fe8c12 | Athina Bekakou | |
1375 | d0fe8c12 | Athina Bekakou | })(); |
1376 | d0fe8c12 | Athina Bekakou | |
1377 | d0fe8c12 | Athina Bekakou | |
1378 | d0fe8c12 | Athina Bekakou | |
1379 | d0fe8c12 | Athina Bekakou | (function() {
|
1380 | d0fe8c12 | Athina Bekakou | /**
|
1381 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1382 | d0fe8c12 | Athina Bekakou | */
|
1383 | d0fe8c12 | Athina Bekakou | |
1384 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
1385 | d0fe8c12 | Athina Bekakou | |
1386 | d0fe8c12 | Athina Bekakou | /**
|
1387 | d0fe8c12 | Athina Bekakou | A record array is an array that contains records of a certain type. The record
|
1388 | d0fe8c12 | Athina Bekakou | array materializes records as needed when they are retrieved for the first
|
1389 | d0fe8c12 | Athina Bekakou | time. You should not create record arrays yourself. Instead, an instance of
|
1390 | d0fe8c12 | Athina Bekakou | `DS.RecordArray` or its subclasses will be returned by your application's store
|
1391 | d0fe8c12 | Athina Bekakou | in response to queries.
|
1392 | d0fe8c12 | Athina Bekakou | |
1393 | d0fe8c12 | Athina Bekakou | @class RecordArray
|
1394 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1395 | d0fe8c12 | Athina Bekakou | @extends Ember.ArrayProxy
|
1396 | d0fe8c12 | Athina Bekakou | @uses Ember.Evented
|
1397 | d0fe8c12 | Athina Bekakou | */
|
1398 | d0fe8c12 | Athina Bekakou | |
1399 | d0fe8c12 | Athina Bekakou | DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, { |
1400 | d0fe8c12 | Athina Bekakou | /**
|
1401 | d0fe8c12 | Athina Bekakou | The model type contained by this record array.
|
1402 | d0fe8c12 | Athina Bekakou | |
1403 | d0fe8c12 | Athina Bekakou | @property type
|
1404 | d0fe8c12 | Athina Bekakou | @type DS.Model
|
1405 | d0fe8c12 | Athina Bekakou | */
|
1406 | d0fe8c12 | Athina Bekakou | type: null, |
1407 | d0fe8c12 | Athina Bekakou | |
1408 | d0fe8c12 | Athina Bekakou | /**
|
1409 | d0fe8c12 | Athina Bekakou | The array of client ids backing the record array. When a
|
1410 | d0fe8c12 | Athina Bekakou | record is requested from the record array, the record
|
1411 | d0fe8c12 | Athina Bekakou | for the client id at the same index is materialized, if
|
1412 | d0fe8c12 | Athina Bekakou | necessary, by the store.
|
1413 | d0fe8c12 | Athina Bekakou | |
1414 | d0fe8c12 | Athina Bekakou | @property content
|
1415 | d0fe8c12 | Athina Bekakou | @private
|
1416 | d0fe8c12 | Athina Bekakou | @type Ember.Array
|
1417 | d0fe8c12 | Athina Bekakou | */
|
1418 | d0fe8c12 | Athina Bekakou | content: null, |
1419 | d0fe8c12 | Athina Bekakou | |
1420 | d0fe8c12 | Athina Bekakou | /**
|
1421 | d0fe8c12 | Athina Bekakou | The flag to signal a `RecordArray` is currently loading data.
|
1422 | d0fe8c12 | Athina Bekakou | |
1423 | d0fe8c12 | Athina Bekakou | Example
|
1424 | d0fe8c12 | Athina Bekakou | |
1425 | d0fe8c12 | Athina Bekakou | ```javascript
|
1426 | d0fe8c12 | Athina Bekakou | var people = store.all(App.Person);
|
1427 | d0fe8c12 | Athina Bekakou | people.get('isLoaded'); // true
|
1428 | d0fe8c12 | Athina Bekakou | ```
|
1429 | d0fe8c12 | Athina Bekakou | |
1430 | d0fe8c12 | Athina Bekakou | @property isLoaded
|
1431 | d0fe8c12 | Athina Bekakou | @type Boolean
|
1432 | d0fe8c12 | Athina Bekakou | */
|
1433 | d0fe8c12 | Athina Bekakou | isLoaded: false, |
1434 | d0fe8c12 | Athina Bekakou | /**
|
1435 | d0fe8c12 | Athina Bekakou | The flag to signal a `RecordArray` is currently loading data.
|
1436 | d0fe8c12 | Athina Bekakou | |
1437 | d0fe8c12 | Athina Bekakou | Example
|
1438 | d0fe8c12 | Athina Bekakou | |
1439 | d0fe8c12 | Athina Bekakou | ```javascript
|
1440 | d0fe8c12 | Athina Bekakou | var people = store.all(App.Person);
|
1441 | d0fe8c12 | Athina Bekakou | people.get('isUpdating'); // false
|
1442 | d0fe8c12 | Athina Bekakou | people.update();
|
1443 | d0fe8c12 | Athina Bekakou | people.get('isUpdating'); // true
|
1444 | d0fe8c12 | Athina Bekakou | ```
|
1445 | d0fe8c12 | Athina Bekakou | |
1446 | d0fe8c12 | Athina Bekakou | @property isUpdating
|
1447 | d0fe8c12 | Athina Bekakou | @type Boolean
|
1448 | d0fe8c12 | Athina Bekakou | */
|
1449 | d0fe8c12 | Athina Bekakou | isUpdating: false, |
1450 | d0fe8c12 | Athina Bekakou | |
1451 | d0fe8c12 | Athina Bekakou | /**
|
1452 | d0fe8c12 | Athina Bekakou | The store that created this record array.
|
1453 | d0fe8c12 | Athina Bekakou | |
1454 | d0fe8c12 | Athina Bekakou | @property store
|
1455 | d0fe8c12 | Athina Bekakou | @private
|
1456 | d0fe8c12 | Athina Bekakou | @type DS.Store
|
1457 | d0fe8c12 | Athina Bekakou | */
|
1458 | d0fe8c12 | Athina Bekakou | store: null, |
1459 | d0fe8c12 | Athina Bekakou | |
1460 | d0fe8c12 | Athina Bekakou | /**
|
1461 | d0fe8c12 | Athina Bekakou | Retrieves an object from the content by index.
|
1462 | d0fe8c12 | Athina Bekakou | |
1463 | d0fe8c12 | Athina Bekakou | @method objectAtContent
|
1464 | d0fe8c12 | Athina Bekakou | @private
|
1465 | d0fe8c12 | Athina Bekakou | @param {Number} index
|
1466 | d0fe8c12 | Athina Bekakou | @return {DS.Model} record
|
1467 | d0fe8c12 | Athina Bekakou | */
|
1468 | d0fe8c12 | Athina Bekakou | objectAtContent: function(index) { |
1469 | d0fe8c12 | Athina Bekakou | var content = get(this, 'content'); |
1470 | d0fe8c12 | Athina Bekakou | |
1471 | d0fe8c12 | Athina Bekakou | return content.objectAt(index);
|
1472 | d0fe8c12 | Athina Bekakou | }, |
1473 | d0fe8c12 | Athina Bekakou | |
1474 | d0fe8c12 | Athina Bekakou | /**
|
1475 | d0fe8c12 | Athina Bekakou | Used to get the latest version of all of the records in this array
|
1476 | d0fe8c12 | Athina Bekakou | from the adapter.
|
1477 | d0fe8c12 | Athina Bekakou | |
1478 | d0fe8c12 | Athina Bekakou | Example
|
1479 | d0fe8c12 | Athina Bekakou | |
1480 | d0fe8c12 | Athina Bekakou | ```javascript
|
1481 | d0fe8c12 | Athina Bekakou | var people = store.all(App.Person);
|
1482 | d0fe8c12 | Athina Bekakou | people.get('isUpdating'); // false
|
1483 | d0fe8c12 | Athina Bekakou | people.update();
|
1484 | d0fe8c12 | Athina Bekakou | people.get('isUpdating'); // true
|
1485 | d0fe8c12 | Athina Bekakou | ```
|
1486 | d0fe8c12 | Athina Bekakou | |
1487 | d0fe8c12 | Athina Bekakou | @method update
|
1488 | d0fe8c12 | Athina Bekakou | */
|
1489 | d0fe8c12 | Athina Bekakou | update: function() { |
1490 | d0fe8c12 | Athina Bekakou | if (get(this, 'isUpdating')) { return; } |
1491 | d0fe8c12 | Athina Bekakou | |
1492 | d0fe8c12 | Athina Bekakou | var store = get(this, 'store'), |
1493 | d0fe8c12 | Athina Bekakou | type = get(this, 'type'); |
1494 | d0fe8c12 | Athina Bekakou | |
1495 | d0fe8c12 | Athina Bekakou | store.fetchAll(type, this);
|
1496 | d0fe8c12 | Athina Bekakou | }, |
1497 | d0fe8c12 | Athina Bekakou | |
1498 | d0fe8c12 | Athina Bekakou | /**
|
1499 | d0fe8c12 | Athina Bekakou | Adds a record to the `RecordArray`.
|
1500 | d0fe8c12 | Athina Bekakou | |
1501 | d0fe8c12 | Athina Bekakou | @method addRecord
|
1502 | d0fe8c12 | Athina Bekakou | @private
|
1503 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
1504 | d0fe8c12 | Athina Bekakou | */
|
1505 | d0fe8c12 | Athina Bekakou | addRecord: function(record) { |
1506 | d0fe8c12 | Athina Bekakou | get(this, 'content').addObject(record); |
1507 | d0fe8c12 | Athina Bekakou | }, |
1508 | d0fe8c12 | Athina Bekakou | |
1509 | d0fe8c12 | Athina Bekakou | /**
|
1510 | d0fe8c12 | Athina Bekakou | Removes a record to the `RecordArray`.
|
1511 | d0fe8c12 | Athina Bekakou | |
1512 | d0fe8c12 | Athina Bekakou | @method removeRecord
|
1513 | d0fe8c12 | Athina Bekakou | @private
|
1514 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
1515 | d0fe8c12 | Athina Bekakou | */
|
1516 | d0fe8c12 | Athina Bekakou | removeRecord: function(record) { |
1517 | d0fe8c12 | Athina Bekakou | get(this, 'content').removeObject(record); |
1518 | d0fe8c12 | Athina Bekakou | }, |
1519 | d0fe8c12 | Athina Bekakou | |
1520 | d0fe8c12 | Athina Bekakou | /**
|
1521 | d0fe8c12 | Athina Bekakou | Saves all of the records in the `RecordArray`.
|
1522 | d0fe8c12 | Athina Bekakou | |
1523 | d0fe8c12 | Athina Bekakou | Example
|
1524 | d0fe8c12 | Athina Bekakou | |
1525 | d0fe8c12 | Athina Bekakou | ```javascript
|
1526 | d0fe8c12 | Athina Bekakou | var messages = store.all(App.Message);
|
1527 | d0fe8c12 | Athina Bekakou | messages.forEach(function(message) {
|
1528 | d0fe8c12 | Athina Bekakou | message.set('hasBeenSeen', true);
|
1529 | d0fe8c12 | Athina Bekakou | });
|
1530 | d0fe8c12 | Athina Bekakou | messages.save();
|
1531 | d0fe8c12 | Athina Bekakou | ```
|
1532 | d0fe8c12 | Athina Bekakou | |
1533 | d0fe8c12 | Athina Bekakou | @method save
|
1534 | d0fe8c12 | Athina Bekakou | @return {DS.PromiseArray} promise
|
1535 | d0fe8c12 | Athina Bekakou | */
|
1536 | d0fe8c12 | Athina Bekakou | save: function() { |
1537 | d0fe8c12 | Athina Bekakou | var promiseLabel = "DS: RecordArray#save " + get(this, 'type'); |
1538 | d0fe8c12 | Athina Bekakou | var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) { |
1539 | d0fe8c12 | Athina Bekakou | return Ember.A(array);
|
1540 | d0fe8c12 | Athina Bekakou | }, null, "DS: RecordArray#save apply Ember.NativeArray"); |
1541 | d0fe8c12 | Athina Bekakou | |
1542 | d0fe8c12 | Athina Bekakou | return DS.PromiseArray.create({ promise: promise }); |
1543 | d0fe8c12 | Athina Bekakou | } |
1544 | d0fe8c12 | Athina Bekakou | }); |
1545 | d0fe8c12 | Athina Bekakou | |
1546 | d0fe8c12 | Athina Bekakou | })(); |
1547 | d0fe8c12 | Athina Bekakou | |
1548 | d0fe8c12 | Athina Bekakou | |
1549 | d0fe8c12 | Athina Bekakou | |
1550 | d0fe8c12 | Athina Bekakou | (function() {
|
1551 | d0fe8c12 | Athina Bekakou | /**
|
1552 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1553 | d0fe8c12 | Athina Bekakou | */
|
1554 | d0fe8c12 | Athina Bekakou | |
1555 | d0fe8c12 | Athina Bekakou | var get = Ember.get;
|
1556 | d0fe8c12 | Athina Bekakou | |
1557 | d0fe8c12 | Athina Bekakou | /**
|
1558 | d0fe8c12 | Athina Bekakou | Represents a list of records whose membership is determined by the
|
1559 | d0fe8c12 | Athina Bekakou | store. As records are created, loaded, or modified, the store
|
1560 | d0fe8c12 | Athina Bekakou | evaluates them to determine if they should be part of the record
|
1561 | d0fe8c12 | Athina Bekakou | array.
|
1562 | d0fe8c12 | Athina Bekakou | |
1563 | d0fe8c12 | Athina Bekakou | @class FilteredRecordArray
|
1564 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1565 | d0fe8c12 | Athina Bekakou | @extends DS.RecordArray
|
1566 | d0fe8c12 | Athina Bekakou | */
|
1567 | d0fe8c12 | Athina Bekakou | DS.FilteredRecordArray = DS.RecordArray.extend({ |
1568 | d0fe8c12 | Athina Bekakou | /**
|
1569 | d0fe8c12 | Athina Bekakou | The filterFunction is a function used to test records from the store to
|
1570 | d0fe8c12 | Athina Bekakou | determine if they should be part of the record array.
|
1571 | d0fe8c12 | Athina Bekakou | |
1572 | d0fe8c12 | Athina Bekakou | Example
|
1573 | d0fe8c12 | Athina Bekakou | |
1574 | d0fe8c12 | Athina Bekakou | ```javascript
|
1575 | d0fe8c12 | Athina Bekakou | var allPeople = store.all('person');
|
1576 | d0fe8c12 | Athina Bekakou | allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"]
|
1577 | d0fe8c12 | Athina Bekakou | |
1578 | d0fe8c12 | Athina Bekakou | var people = store.filter('person', function(person) {
|
1579 | d0fe8c12 | Athina Bekakou | if (person.get('name').match(/Katz$/)) { return true; }
|
1580 | d0fe8c12 | Athina Bekakou | });
|
1581 | d0fe8c12 | Athina Bekakou | people.mapBy('name'); // ["Yehuda Katz"]
|
1582 | d0fe8c12 | Athina Bekakou | |
1583 | d0fe8c12 | Athina Bekakou | var notKatzFilter = function(person) {
|
1584 | d0fe8c12 | Athina Bekakou | return !person.get('name').match(/Katz$/);
|
1585 | d0fe8c12 | Athina Bekakou | };
|
1586 | d0fe8c12 | Athina Bekakou | people.set('filterFunction', notKatzFilter);
|
1587 | d0fe8c12 | Athina Bekakou | people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"]
|
1588 | d0fe8c12 | Athina Bekakou | ```
|
1589 | d0fe8c12 | Athina Bekakou | |
1590 | d0fe8c12 | Athina Bekakou | @method filterFunction
|
1591 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
1592 | d0fe8c12 | Athina Bekakou | @return {Boolean} `true` if the record should be in the array
|
1593 | d0fe8c12 | Athina Bekakou | */
|
1594 | d0fe8c12 | Athina Bekakou | filterFunction: null, |
1595 | d0fe8c12 | Athina Bekakou | isLoaded: true, |
1596 | d0fe8c12 | Athina Bekakou | |
1597 | d0fe8c12 | Athina Bekakou | replace: function() { |
1598 | d0fe8c12 | Athina Bekakou | var type = get(this, 'type').toString(); |
1599 | d0fe8c12 | Athina Bekakou | throw new Error("The result of a client-side filter (on " + type + ") is immutable."); |
1600 | d0fe8c12 | Athina Bekakou | }, |
1601 | d0fe8c12 | Athina Bekakou | |
1602 | d0fe8c12 | Athina Bekakou | /**
|
1603 | d0fe8c12 | Athina Bekakou | @method updateFilter
|
1604 | d0fe8c12 | Athina Bekakou | @private
|
1605 | d0fe8c12 | Athina Bekakou | */
|
1606 | d0fe8c12 | Athina Bekakou | updateFilter: Ember.observer(function() { |
1607 | d0fe8c12 | Athina Bekakou | var manager = get(this, 'manager'); |
1608 | d0fe8c12 | Athina Bekakou | manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction')); |
1609 | d0fe8c12 | Athina Bekakou | }, 'filterFunction')
|
1610 | d0fe8c12 | Athina Bekakou | }); |
1611 | d0fe8c12 | Athina Bekakou | |
1612 | d0fe8c12 | Athina Bekakou | })(); |
1613 | d0fe8c12 | Athina Bekakou | |
1614 | d0fe8c12 | Athina Bekakou | |
1615 | d0fe8c12 | Athina Bekakou | |
1616 | d0fe8c12 | Athina Bekakou | (function() {
|
1617 | d0fe8c12 | Athina Bekakou | /**
|
1618 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1619 | d0fe8c12 | Athina Bekakou | */
|
1620 | d0fe8c12 | Athina Bekakou | |
1621 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
1622 | d0fe8c12 | Athina Bekakou | |
1623 | d0fe8c12 | Athina Bekakou | /**
|
1624 | d0fe8c12 | Athina Bekakou | Represents an ordered list of records whose order and membership is
|
1625 | d0fe8c12 | Athina Bekakou | determined by the adapter. For example, a query sent to the adapter
|
1626 | d0fe8c12 | Athina Bekakou | may trigger a search on the server, whose results would be loaded
|
1627 | d0fe8c12 | Athina Bekakou | into an instance of the `AdapterPopulatedRecordArray`.
|
1628 | d0fe8c12 | Athina Bekakou | |
1629 | d0fe8c12 | Athina Bekakou | @class AdapterPopulatedRecordArray
|
1630 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1631 | d0fe8c12 | Athina Bekakou | @extends DS.RecordArray
|
1632 | d0fe8c12 | Athina Bekakou | */
|
1633 | d0fe8c12 | Athina Bekakou | DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({ |
1634 | d0fe8c12 | Athina Bekakou | query: null, |
1635 | d0fe8c12 | Athina Bekakou | |
1636 | d0fe8c12 | Athina Bekakou | replace: function() { |
1637 | d0fe8c12 | Athina Bekakou | var type = get(this, 'type').toString(); |
1638 | d0fe8c12 | Athina Bekakou | throw new Error("The result of a server query (on " + type + ") is immutable."); |
1639 | d0fe8c12 | Athina Bekakou | }, |
1640 | d0fe8c12 | Athina Bekakou | |
1641 | d0fe8c12 | Athina Bekakou | /**
|
1642 | d0fe8c12 | Athina Bekakou | @method load
|
1643 | d0fe8c12 | Athina Bekakou | @private
|
1644 | d0fe8c12 | Athina Bekakou | @param {Array} data
|
1645 | d0fe8c12 | Athina Bekakou | */
|
1646 | d0fe8c12 | Athina Bekakou | load: function(data) { |
1647 | d0fe8c12 | Athina Bekakou | var store = get(this, 'store'), |
1648 | d0fe8c12 | Athina Bekakou | type = get(this, 'type'), |
1649 | d0fe8c12 | Athina Bekakou | records = store.pushMany(type, data), |
1650 | d0fe8c12 | Athina Bekakou | meta = store.metadataFor(type); |
1651 | d0fe8c12 | Athina Bekakou | |
1652 | d0fe8c12 | Athina Bekakou | this.setProperties({
|
1653 | d0fe8c12 | Athina Bekakou | content: Ember.A(records),
|
1654 | d0fe8c12 | Athina Bekakou | isLoaded: true, |
1655 | d0fe8c12 | Athina Bekakou | meta: meta
|
1656 | d0fe8c12 | Athina Bekakou | }); |
1657 | d0fe8c12 | Athina Bekakou | |
1658 | d0fe8c12 | Athina Bekakou | // TODO: does triggering didLoad event should be the last action of the runLoop?
|
1659 | d0fe8c12 | Athina Bekakou | Ember.run.once(this, 'trigger', 'didLoad'); |
1660 | d0fe8c12 | Athina Bekakou | } |
1661 | d0fe8c12 | Athina Bekakou | }); |
1662 | d0fe8c12 | Athina Bekakou | |
1663 | d0fe8c12 | Athina Bekakou | })(); |
1664 | d0fe8c12 | Athina Bekakou | |
1665 | d0fe8c12 | Athina Bekakou | |
1666 | d0fe8c12 | Athina Bekakou | |
1667 | d0fe8c12 | Athina Bekakou | (function() {
|
1668 | d0fe8c12 | Athina Bekakou | /**
|
1669 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1670 | d0fe8c12 | Athina Bekakou | */
|
1671 | d0fe8c12 | Athina Bekakou | |
1672 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
1673 | d0fe8c12 | Athina Bekakou | var map = Ember.EnumerableUtils.map;
|
1674 | d0fe8c12 | Athina Bekakou | |
1675 | d0fe8c12 | Athina Bekakou | /**
|
1676 | d0fe8c12 | Athina Bekakou | A `ManyArray` is a `RecordArray` that represents the contents of a has-many
|
1677 | d0fe8c12 | Athina Bekakou | relationship.
|
1678 | d0fe8c12 | Athina Bekakou | |
1679 | d0fe8c12 | Athina Bekakou | The `ManyArray` is instantiated lazily the first time the relationship is
|
1680 | d0fe8c12 | Athina Bekakou | requested.
|
1681 | d0fe8c12 | Athina Bekakou | |
1682 | d0fe8c12 | Athina Bekakou | ### Inverses
|
1683 | d0fe8c12 | Athina Bekakou | |
1684 | d0fe8c12 | Athina Bekakou | Often, the relationships in Ember Data applications will have
|
1685 | d0fe8c12 | Athina Bekakou | an inverse. For example, imagine the following models are
|
1686 | d0fe8c12 | Athina Bekakou | defined:
|
1687 | d0fe8c12 | Athina Bekakou | |
1688 | d0fe8c12 | Athina Bekakou | ```javascript
|
1689 | d0fe8c12 | Athina Bekakou | App.Post = DS.Model.extend({
|
1690 | d0fe8c12 | Athina Bekakou | comments: DS.hasMany('comment')
|
1691 | d0fe8c12 | Athina Bekakou | });
|
1692 | d0fe8c12 | Athina Bekakou | |
1693 | d0fe8c12 | Athina Bekakou | App.Comment = DS.Model.extend({
|
1694 | d0fe8c12 | Athina Bekakou | post: DS.belongsTo('post')
|
1695 | d0fe8c12 | Athina Bekakou | });
|
1696 | d0fe8c12 | Athina Bekakou | ```
|
1697 | d0fe8c12 | Athina Bekakou | |
1698 | d0fe8c12 | Athina Bekakou | If you created a new instance of `App.Post` and added
|
1699 | d0fe8c12 | Athina Bekakou | a `App.Comment` record to its `comments` has-many
|
1700 | d0fe8c12 | Athina Bekakou | relationship, you would expect the comment's `post`
|
1701 | d0fe8c12 | Athina Bekakou | property to be set to the post that contained
|
1702 | d0fe8c12 | Athina Bekakou | the has-many.
|
1703 | d0fe8c12 | Athina Bekakou | |
1704 | d0fe8c12 | Athina Bekakou | We call the record to which a relationship belongs the
|
1705 | d0fe8c12 | Athina Bekakou | relationship's _owner_.
|
1706 | d0fe8c12 | Athina Bekakou | |
1707 | d0fe8c12 | Athina Bekakou | @class ManyArray
|
1708 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1709 | d0fe8c12 | Athina Bekakou | @extends DS.RecordArray
|
1710 | d0fe8c12 | Athina Bekakou | */
|
1711 | d0fe8c12 | Athina Bekakou | DS.ManyArray = DS.RecordArray.extend({ |
1712 | d0fe8c12 | Athina Bekakou | init: function() { |
1713 | d0fe8c12 | Athina Bekakou | this._super.apply(this, arguments); |
1714 | d0fe8c12 | Athina Bekakou | this._changesToSync = Ember.OrderedSet.create();
|
1715 | d0fe8c12 | Athina Bekakou | }, |
1716 | d0fe8c12 | Athina Bekakou | |
1717 | d0fe8c12 | Athina Bekakou | /**
|
1718 | d0fe8c12 | Athina Bekakou | The property name of the relationship
|
1719 | d0fe8c12 | Athina Bekakou | |
1720 | d0fe8c12 | Athina Bekakou | @property {String} name
|
1721 | d0fe8c12 | Athina Bekakou | @private
|
1722 | d0fe8c12 | Athina Bekakou | */
|
1723 | d0fe8c12 | Athina Bekakou | name: null, |
1724 | d0fe8c12 | Athina Bekakou | |
1725 | d0fe8c12 | Athina Bekakou | /**
|
1726 | d0fe8c12 | Athina Bekakou | The record to which this relationship belongs.
|
1727 | d0fe8c12 | Athina Bekakou | |
1728 | d0fe8c12 | Athina Bekakou | @property {DS.Model} owner
|
1729 | d0fe8c12 | Athina Bekakou | @private
|
1730 | d0fe8c12 | Athina Bekakou | */
|
1731 | d0fe8c12 | Athina Bekakou | owner: null, |
1732 | d0fe8c12 | Athina Bekakou | |
1733 | d0fe8c12 | Athina Bekakou | /**
|
1734 | d0fe8c12 | Athina Bekakou | `true` if the relationship is polymorphic, `false` otherwise.
|
1735 | d0fe8c12 | Athina Bekakou | |
1736 | d0fe8c12 | Athina Bekakou | @property {Boolean} isPolymorphic
|
1737 | d0fe8c12 | Athina Bekakou | @private
|
1738 | d0fe8c12 | Athina Bekakou | */
|
1739 | d0fe8c12 | Athina Bekakou | isPolymorphic: false, |
1740 | d0fe8c12 | Athina Bekakou | |
1741 | d0fe8c12 | Athina Bekakou | // LOADING STATE
|
1742 | d0fe8c12 | Athina Bekakou | |
1743 | d0fe8c12 | Athina Bekakou | isLoaded: false, |
1744 | d0fe8c12 | Athina Bekakou | |
1745 | d0fe8c12 | Athina Bekakou | /**
|
1746 | d0fe8c12 | Athina Bekakou | Used for async `hasMany` arrays
|
1747 | d0fe8c12 | Athina Bekakou | to keep track of when they will resolve.
|
1748 | d0fe8c12 | Athina Bekakou | |
1749 | d0fe8c12 | Athina Bekakou | @property {Ember.RSVP.Promise} promise
|
1750 | d0fe8c12 | Athina Bekakou | @private
|
1751 | d0fe8c12 | Athina Bekakou | */
|
1752 | d0fe8c12 | Athina Bekakou | promise: null, |
1753 | d0fe8c12 | Athina Bekakou | |
1754 | d0fe8c12 | Athina Bekakou | /**
|
1755 | d0fe8c12 | Athina Bekakou | @method loadingRecordsCount
|
1756 | d0fe8c12 | Athina Bekakou | @param {Number} count
|
1757 | d0fe8c12 | Athina Bekakou | @private
|
1758 | d0fe8c12 | Athina Bekakou | */
|
1759 | d0fe8c12 | Athina Bekakou | loadingRecordsCount: function(count) { |
1760 | d0fe8c12 | Athina Bekakou | this.loadingRecordsCount = count;
|
1761 | d0fe8c12 | Athina Bekakou | }, |
1762 | d0fe8c12 | Athina Bekakou | |
1763 | d0fe8c12 | Athina Bekakou | /**
|
1764 | d0fe8c12 | Athina Bekakou | @method loadedRecord
|
1765 | d0fe8c12 | Athina Bekakou | @private
|
1766 | d0fe8c12 | Athina Bekakou | */
|
1767 | d0fe8c12 | Athina Bekakou | loadedRecord: function() { |
1768 | d0fe8c12 | Athina Bekakou | this.loadingRecordsCount--;
|
1769 | d0fe8c12 | Athina Bekakou | if (this.loadingRecordsCount === 0) { |
1770 | d0fe8c12 | Athina Bekakou | set(this, 'isLoaded', true); |
1771 | d0fe8c12 | Athina Bekakou | this.trigger('didLoad'); |
1772 | d0fe8c12 | Athina Bekakou | } |
1773 | d0fe8c12 | Athina Bekakou | }, |
1774 | d0fe8c12 | Athina Bekakou | |
1775 | d0fe8c12 | Athina Bekakou | /**
|
1776 | d0fe8c12 | Athina Bekakou | @method fetch
|
1777 | d0fe8c12 | Athina Bekakou | @private
|
1778 | d0fe8c12 | Athina Bekakou | */
|
1779 | d0fe8c12 | Athina Bekakou | fetch: function() { |
1780 | d0fe8c12 | Athina Bekakou | var records = get(this, 'content'), |
1781 | d0fe8c12 | Athina Bekakou | store = get(this, 'store'), |
1782 | d0fe8c12 | Athina Bekakou | owner = get(this, 'owner'), |
1783 | d0fe8c12 | Athina Bekakou | resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type')); |
1784 | d0fe8c12 | Athina Bekakou | |
1785 | d0fe8c12 | Athina Bekakou | var unloadedRecords = records.filterProperty('isEmpty', true); |
1786 | d0fe8c12 | Athina Bekakou | store.fetchMany(unloadedRecords, owner, resolver); |
1787 | d0fe8c12 | Athina Bekakou | }, |
1788 | d0fe8c12 | Athina Bekakou | |
1789 | d0fe8c12 | Athina Bekakou | // Overrides Ember.Array's replace method to implement
|
1790 | d0fe8c12 | Athina Bekakou | replaceContent: function(index, removed, added) { |
1791 | d0fe8c12 | Athina Bekakou | // Map the array of record objects into an array of client ids.
|
1792 | d0fe8c12 | Athina Bekakou | added = map(added, function(record) {
|
1793 | d0fe8c12 | Athina Bekakou | Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.type.typeKey + "' allowed)", !this.type || record instanceof this.type); |
1794 | d0fe8c12 | Athina Bekakou | return record;
|
1795 | d0fe8c12 | Athina Bekakou | }, this);
|
1796 | d0fe8c12 | Athina Bekakou | |
1797 | d0fe8c12 | Athina Bekakou | this._super(index, removed, added);
|
1798 | d0fe8c12 | Athina Bekakou | }, |
1799 | d0fe8c12 | Athina Bekakou | |
1800 | d0fe8c12 | Athina Bekakou | arrangedContentDidChange: function() { |
1801 | d0fe8c12 | Athina Bekakou | Ember.run.once(this, 'fetch'); |
1802 | d0fe8c12 | Athina Bekakou | }, |
1803 | d0fe8c12 | Athina Bekakou | |
1804 | d0fe8c12 | Athina Bekakou | arrayContentWillChange: function(index, removed, added) { |
1805 | d0fe8c12 | Athina Bekakou | var owner = get(this, 'owner'), |
1806 | d0fe8c12 | Athina Bekakou | name = get(this, 'name'); |
1807 | d0fe8c12 | Athina Bekakou | |
1808 | d0fe8c12 | Athina Bekakou | if (!owner._suspendedRelationships) {
|
1809 | d0fe8c12 | Athina Bekakou | // This code is the first half of code that continues inside
|
1810 | d0fe8c12 | Athina Bekakou | // of arrayContentDidChange. It gets or creates a change from
|
1811 | d0fe8c12 | Athina Bekakou | // the child object, adds the current owner as the old
|
1812 | d0fe8c12 | Athina Bekakou | // parent if this is the first time the object was removed
|
1813 | d0fe8c12 | Athina Bekakou | // from a ManyArray, and sets `newParent` to null.
|
1814 | d0fe8c12 | Athina Bekakou | //
|
1815 | d0fe8c12 | Athina Bekakou | // Later, if the object is added to another ManyArray,
|
1816 | d0fe8c12 | Athina Bekakou | // the `arrayContentDidChange` will set `newParent` on
|
1817 | d0fe8c12 | Athina Bekakou | // the change.
|
1818 | d0fe8c12 | Athina Bekakou | for (var i=index; i<index+removed; i++) { |
1819 | d0fe8c12 | Athina Bekakou | var record = get(this, 'content').objectAt(i); |
1820 | d0fe8c12 | Athina Bekakou | |
1821 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange.createChange(owner, record, get(this, 'store'), { |
1822 | d0fe8c12 | Athina Bekakou | parentType: owner.constructor,
|
1823 | d0fe8c12 | Athina Bekakou | changeType: "remove", |
1824 | d0fe8c12 | Athina Bekakou | kind: "hasMany", |
1825 | d0fe8c12 | Athina Bekakou | key: name
|
1826 | d0fe8c12 | Athina Bekakou | }); |
1827 | d0fe8c12 | Athina Bekakou | |
1828 | d0fe8c12 | Athina Bekakou | this._changesToSync.add(change);
|
1829 | d0fe8c12 | Athina Bekakou | } |
1830 | d0fe8c12 | Athina Bekakou | } |
1831 | d0fe8c12 | Athina Bekakou | |
1832 | d0fe8c12 | Athina Bekakou | return this._super.apply(this, arguments); |
1833 | d0fe8c12 | Athina Bekakou | }, |
1834 | d0fe8c12 | Athina Bekakou | |
1835 | d0fe8c12 | Athina Bekakou | arrayContentDidChange: function(index, removed, added) { |
1836 | d0fe8c12 | Athina Bekakou | this._super.apply(this, arguments); |
1837 | d0fe8c12 | Athina Bekakou | |
1838 | d0fe8c12 | Athina Bekakou | var owner = get(this, 'owner'), |
1839 | d0fe8c12 | Athina Bekakou | name = get(this, 'name'), |
1840 | d0fe8c12 | Athina Bekakou | store = get(this, 'store'); |
1841 | d0fe8c12 | Athina Bekakou | |
1842 | d0fe8c12 | Athina Bekakou | if (!owner._suspendedRelationships) {
|
1843 | d0fe8c12 | Athina Bekakou | // This code is the second half of code that started in
|
1844 | d0fe8c12 | Athina Bekakou | // `arrayContentWillChange`. It gets or creates a change
|
1845 | d0fe8c12 | Athina Bekakou | // from the child object, and adds the current owner as
|
1846 | d0fe8c12 | Athina Bekakou | // the new parent.
|
1847 | d0fe8c12 | Athina Bekakou | for (var i=index; i<index+added; i++) { |
1848 | d0fe8c12 | Athina Bekakou | var record = get(this, 'content').objectAt(i); |
1849 | d0fe8c12 | Athina Bekakou | |
1850 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange.createChange(owner, record, store, {
|
1851 | d0fe8c12 | Athina Bekakou | parentType: owner.constructor,
|
1852 | d0fe8c12 | Athina Bekakou | changeType: "add", |
1853 | d0fe8c12 | Athina Bekakou | kind:"hasMany", |
1854 | d0fe8c12 | Athina Bekakou | key: name
|
1855 | d0fe8c12 | Athina Bekakou | }); |
1856 | d0fe8c12 | Athina Bekakou | change.hasManyName = name; |
1857 | d0fe8c12 | Athina Bekakou | |
1858 | d0fe8c12 | Athina Bekakou | this._changesToSync.add(change);
|
1859 | d0fe8c12 | Athina Bekakou | } |
1860 | d0fe8c12 | Athina Bekakou | |
1861 | d0fe8c12 | Athina Bekakou | // We wait until the array has finished being
|
1862 | d0fe8c12 | Athina Bekakou | // mutated before syncing the OneToManyChanges created
|
1863 | d0fe8c12 | Athina Bekakou | // in arrayContentWillChange, so that the array
|
1864 | d0fe8c12 | Athina Bekakou | // membership test in the sync() logic operates
|
1865 | d0fe8c12 | Athina Bekakou | // on the final results.
|
1866 | d0fe8c12 | Athina Bekakou | this._changesToSync.forEach(function(change) { |
1867 | d0fe8c12 | Athina Bekakou | change.sync(); |
1868 | d0fe8c12 | Athina Bekakou | }); |
1869 | d0fe8c12 | Athina Bekakou | |
1870 | d0fe8c12 | Athina Bekakou | this._changesToSync.clear();
|
1871 | d0fe8c12 | Athina Bekakou | } |
1872 | d0fe8c12 | Athina Bekakou | }, |
1873 | d0fe8c12 | Athina Bekakou | |
1874 | d0fe8c12 | Athina Bekakou | /**
|
1875 | d0fe8c12 | Athina Bekakou | Create a child record within the owner
|
1876 | d0fe8c12 | Athina Bekakou | |
1877 | d0fe8c12 | Athina Bekakou | @method createRecord
|
1878 | d0fe8c12 | Athina Bekakou | @private
|
1879 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
1880 | d0fe8c12 | Athina Bekakou | @return {DS.Model} record
|
1881 | d0fe8c12 | Athina Bekakou | */
|
1882 | d0fe8c12 | Athina Bekakou | createRecord: function(hash) { |
1883 | d0fe8c12 | Athina Bekakou | var owner = get(this, 'owner'), |
1884 | d0fe8c12 | Athina Bekakou | store = get(owner, 'store'),
|
1885 | d0fe8c12 | Athina Bekakou | type = get(this, 'type'), |
1886 | d0fe8c12 | Athina Bekakou | record; |
1887 | d0fe8c12 | Athina Bekakou | |
1888 | d0fe8c12 | Athina Bekakou | Ember.assert("You cannot add '" + type.typeKey + "' records to this polymorphic relationship.", !get(this, 'isPolymorphic')); |
1889 | d0fe8c12 | Athina Bekakou | |
1890 | d0fe8c12 | Athina Bekakou | record = store.createRecord.call(store, type, hash); |
1891 | d0fe8c12 | Athina Bekakou | this.pushObject(record);
|
1892 | d0fe8c12 | Athina Bekakou | |
1893 | d0fe8c12 | Athina Bekakou | return record;
|
1894 | d0fe8c12 | Athina Bekakou | } |
1895 | d0fe8c12 | Athina Bekakou | |
1896 | d0fe8c12 | Athina Bekakou | }); |
1897 | d0fe8c12 | Athina Bekakou | |
1898 | d0fe8c12 | Athina Bekakou | })(); |
1899 | d0fe8c12 | Athina Bekakou | |
1900 | d0fe8c12 | Athina Bekakou | |
1901 | d0fe8c12 | Athina Bekakou | |
1902 | d0fe8c12 | Athina Bekakou | (function() {
|
1903 | d0fe8c12 | Athina Bekakou | /**
|
1904 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1905 | d0fe8c12 | Athina Bekakou | */
|
1906 | d0fe8c12 | Athina Bekakou | |
1907 | d0fe8c12 | Athina Bekakou | })(); |
1908 | d0fe8c12 | Athina Bekakou | |
1909 | d0fe8c12 | Athina Bekakou | |
1910 | d0fe8c12 | Athina Bekakou | |
1911 | d0fe8c12 | Athina Bekakou | (function() {
|
1912 | d0fe8c12 | Athina Bekakou | /*globals Ember*/
|
1913 | d0fe8c12 | Athina Bekakou | /*jshint eqnull:true*/
|
1914 | d0fe8c12 | Athina Bekakou | /**
|
1915 | d0fe8c12 | Athina Bekakou | @module ember-data
|
1916 | d0fe8c12 | Athina Bekakou | */
|
1917 | d0fe8c12 | Athina Bekakou | |
1918 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
1919 | d0fe8c12 | Athina Bekakou | var once = Ember.run.once;
|
1920 | d0fe8c12 | Athina Bekakou | var isNone = Ember.isNone;
|
1921 | d0fe8c12 | Athina Bekakou | var forEach = Ember.EnumerableUtils.forEach;
|
1922 | d0fe8c12 | Athina Bekakou | var indexOf = Ember.EnumerableUtils.indexOf;
|
1923 | d0fe8c12 | Athina Bekakou | var map = Ember.EnumerableUtils.map;
|
1924 | d0fe8c12 | Athina Bekakou | var resolve = Ember.RSVP.resolve;
|
1925 | d0fe8c12 | Athina Bekakou | var copy = Ember.copy;
|
1926 | d0fe8c12 | Athina Bekakou | |
1927 | d0fe8c12 | Athina Bekakou | // Implementors Note:
|
1928 | d0fe8c12 | Athina Bekakou | //
|
1929 | d0fe8c12 | Athina Bekakou | // The variables in this file are consistently named according to the following
|
1930 | d0fe8c12 | Athina Bekakou | // scheme:
|
1931 | d0fe8c12 | Athina Bekakou | //
|
1932 | d0fe8c12 | Athina Bekakou | // * +id+ means an identifier managed by an external source, provided inside
|
1933 | d0fe8c12 | Athina Bekakou | // the data provided by that source. These are always coerced to be strings
|
1934 | d0fe8c12 | Athina Bekakou | // before being used internally.
|
1935 | d0fe8c12 | Athina Bekakou | // * +clientId+ means a transient numerical identifier generated at runtime by
|
1936 | d0fe8c12 | Athina Bekakou | // the data store. It is important primarily because newly created objects may
|
1937 | d0fe8c12 | Athina Bekakou | // not yet have an externally generated id.
|
1938 | d0fe8c12 | Athina Bekakou | // * +reference+ means a record reference object, which holds metadata about a
|
1939 | d0fe8c12 | Athina Bekakou | // record, even if it has not yet been fully materialized.
|
1940 | d0fe8c12 | Athina Bekakou | // * +type+ means a subclass of DS.Model.
|
1941 | d0fe8c12 | Athina Bekakou | |
1942 | d0fe8c12 | Athina Bekakou | // Used by the store to normalize IDs entering the store. Despite the fact
|
1943 | d0fe8c12 | Athina Bekakou | // that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`),
|
1944 | d0fe8c12 | Athina Bekakou | // it is important that internally we use strings, since IDs may be serialized
|
1945 | d0fe8c12 | Athina Bekakou | // and lose type information. For example, Ember's router may put a record's
|
1946 | d0fe8c12 | Athina Bekakou | // ID into the URL, and if we later try to deserialize that URL and find the
|
1947 | d0fe8c12 | Athina Bekakou | // corresponding record, we will not know if it is a string or a number.
|
1948 | d0fe8c12 | Athina Bekakou | var coerceId = function(id) { |
1949 | d0fe8c12 | Athina Bekakou | return id == null ? null : id+''; |
1950 | d0fe8c12 | Athina Bekakou | }; |
1951 | d0fe8c12 | Athina Bekakou | |
1952 | d0fe8c12 | Athina Bekakou | /**
|
1953 | d0fe8c12 | Athina Bekakou | The store contains all of the data for records loaded from the server.
|
1954 | d0fe8c12 | Athina Bekakou | It is also responsible for creating instances of `DS.Model` that wrap
|
1955 | d0fe8c12 | Athina Bekakou | the individual data for a record, so that they can be bound to in your
|
1956 | d0fe8c12 | Athina Bekakou | Handlebars templates.
|
1957 | d0fe8c12 | Athina Bekakou | |
1958 | d0fe8c12 | Athina Bekakou | Define your application's store like this:
|
1959 | d0fe8c12 | Athina Bekakou | |
1960 | d0fe8c12 | Athina Bekakou | ```javascript
|
1961 | d0fe8c12 | Athina Bekakou | MyApp.Store = DS.Store.extend();
|
1962 | d0fe8c12 | Athina Bekakou | ```
|
1963 | d0fe8c12 | Athina Bekakou | |
1964 | d0fe8c12 | Athina Bekakou | Most Ember.js applications will only have a single `DS.Store` that is
|
1965 | d0fe8c12 | Athina Bekakou | automatically created by their `Ember.Application`.
|
1966 | d0fe8c12 | Athina Bekakou | |
1967 | d0fe8c12 | Athina Bekakou | You can retrieve models from the store in several ways. To retrieve a record
|
1968 | d0fe8c12 | Athina Bekakou | for a specific id, use `DS.Store`'s `find()` method:
|
1969 | d0fe8c12 | Athina Bekakou | |
1970 | d0fe8c12 | Athina Bekakou | ```javascript
|
1971 | d0fe8c12 | Athina Bekakou | var person = store.find('person', 123);
|
1972 | d0fe8c12 | Athina Bekakou | ```
|
1973 | d0fe8c12 | Athina Bekakou | |
1974 | d0fe8c12 | Athina Bekakou | If your application has multiple `DS.Store` instances (an unusual case), you can
|
1975 | d0fe8c12 | Athina Bekakou | specify which store should be used:
|
1976 | d0fe8c12 | Athina Bekakou | |
1977 | d0fe8c12 | Athina Bekakou | ```javascript
|
1978 | d0fe8c12 | Athina Bekakou | var person = store.find(App.Person, 123);
|
1979 | d0fe8c12 | Athina Bekakou | ```
|
1980 | d0fe8c12 | Athina Bekakou | |
1981 | d0fe8c12 | Athina Bekakou | By default, the store will talk to your backend using a standard
|
1982 | d0fe8c12 | Athina Bekakou | REST mechanism. You can customize how the store talks to your
|
1983 | d0fe8c12 | Athina Bekakou | backend by specifying a custom adapter:
|
1984 | d0fe8c12 | Athina Bekakou | |
1985 | d0fe8c12 | Athina Bekakou | ```javascript
|
1986 | d0fe8c12 | Athina Bekakou | MyApp.store = DS.Store.create({
|
1987 | d0fe8c12 | Athina Bekakou | adapter: 'MyApp.CustomAdapter'
|
1988 | d0fe8c12 | Athina Bekakou | });
|
1989 | d0fe8c12 | Athina Bekakou | ```
|
1990 | d0fe8c12 | Athina Bekakou | |
1991 | d0fe8c12 | Athina Bekakou | You can learn more about writing a custom adapter by reading the `DS.Adapter`
|
1992 | d0fe8c12 | Athina Bekakou | documentation.
|
1993 | d0fe8c12 | Athina Bekakou | |
1994 | d0fe8c12 | Athina Bekakou | @class Store
|
1995 | d0fe8c12 | Athina Bekakou | @namespace DS
|
1996 | d0fe8c12 | Athina Bekakou | @extends Ember.Object
|
1997 | d0fe8c12 | Athina Bekakou | */
|
1998 | d0fe8c12 | Athina Bekakou | DS.Store = Ember.Object.extend({ |
1999 | d0fe8c12 | Athina Bekakou | |
2000 | d0fe8c12 | Athina Bekakou | /**
|
2001 | d0fe8c12 | Athina Bekakou | @method init
|
2002 | d0fe8c12 | Athina Bekakou | @private
|
2003 | d0fe8c12 | Athina Bekakou | */
|
2004 | d0fe8c12 | Athina Bekakou | init: function() { |
2005 | d0fe8c12 | Athina Bekakou | // internal bookkeeping; not observable
|
2006 | d0fe8c12 | Athina Bekakou | this.typeMaps = {};
|
2007 | d0fe8c12 | Athina Bekakou | this.recordArrayManager = DS.RecordArrayManager.create({
|
2008 | d0fe8c12 | Athina Bekakou | store: this |
2009 | d0fe8c12 | Athina Bekakou | }); |
2010 | d0fe8c12 | Athina Bekakou | this._relationshipChanges = {};
|
2011 | d0fe8c12 | Athina Bekakou | this._pendingSave = [];
|
2012 | d0fe8c12 | Athina Bekakou | }, |
2013 | d0fe8c12 | Athina Bekakou | |
2014 | d0fe8c12 | Athina Bekakou | /**
|
2015 | d0fe8c12 | Athina Bekakou | The adapter to use to communicate to a backend server or other persistence layer.
|
2016 | d0fe8c12 | Athina Bekakou | |
2017 | d0fe8c12 | Athina Bekakou | This can be specified as an instance, class, or string.
|
2018 | d0fe8c12 | Athina Bekakou | |
2019 | d0fe8c12 | Athina Bekakou | If you want to specify `App.CustomAdapter` as a string, do:
|
2020 | d0fe8c12 | Athina Bekakou | |
2021 | d0fe8c12 | Athina Bekakou | ```js
|
2022 | d0fe8c12 | Athina Bekakou | adapter: 'custom'
|
2023 | d0fe8c12 | Athina Bekakou | ```
|
2024 | d0fe8c12 | Athina Bekakou | |
2025 | d0fe8c12 | Athina Bekakou | @property adapter
|
2026 | d0fe8c12 | Athina Bekakou | @default DS.RESTAdapter
|
2027 | d0fe8c12 | Athina Bekakou | @type {DS.Adapter|String}
|
2028 | d0fe8c12 | Athina Bekakou | */
|
2029 | d0fe8c12 | Athina Bekakou | adapter: '_rest', |
2030 | d0fe8c12 | Athina Bekakou | |
2031 | d0fe8c12 | Athina Bekakou | /**
|
2032 | d0fe8c12 | Athina Bekakou | Returns a JSON representation of the record using a custom
|
2033 | d0fe8c12 | Athina Bekakou | type-specific serializer, if one exists.
|
2034 | d0fe8c12 | Athina Bekakou | |
2035 | d0fe8c12 | Athina Bekakou | The available options are:
|
2036 | d0fe8c12 | Athina Bekakou | |
2037 | d0fe8c12 | Athina Bekakou | * `includeId`: `true` if the record's ID should be included in
|
2038 | d0fe8c12 | Athina Bekakou | the JSON representation
|
2039 | d0fe8c12 | Athina Bekakou | |
2040 | d0fe8c12 | Athina Bekakou | @method serialize
|
2041 | d0fe8c12 | Athina Bekakou | @private
|
2042 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record the record to serialize
|
2043 | d0fe8c12 | Athina Bekakou | @param {Object} options an options hash
|
2044 | d0fe8c12 | Athina Bekakou | */
|
2045 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) { |
2046 | d0fe8c12 | Athina Bekakou | return this.serializerFor(record.constructor.typeKey).serialize(record, options); |
2047 | d0fe8c12 | Athina Bekakou | }, |
2048 | d0fe8c12 | Athina Bekakou | |
2049 | d0fe8c12 | Athina Bekakou | /**
|
2050 | d0fe8c12 | Athina Bekakou | This property returns the adapter, after resolving a possible
|
2051 | d0fe8c12 | Athina Bekakou | string key.
|
2052 | d0fe8c12 | Athina Bekakou | |
2053 | d0fe8c12 | Athina Bekakou | If the supplied `adapter` was a class, or a String property
|
2054 | d0fe8c12 | Athina Bekakou | path resolved to a class, this property will instantiate the
|
2055 | d0fe8c12 | Athina Bekakou | class.
|
2056 | d0fe8c12 | Athina Bekakou | |
2057 | d0fe8c12 | Athina Bekakou | This property is cacheable, so the same instance of a specified
|
2058 | d0fe8c12 | Athina Bekakou | adapter class should be used for the lifetime of the store.
|
2059 | d0fe8c12 | Athina Bekakou | |
2060 | d0fe8c12 | Athina Bekakou | @property defaultAdapter
|
2061 | d0fe8c12 | Athina Bekakou | @private
|
2062 | d0fe8c12 | Athina Bekakou | @returns DS.Adapter
|
2063 | d0fe8c12 | Athina Bekakou | */
|
2064 | d0fe8c12 | Athina Bekakou | defaultAdapter: Ember.computed('adapter', function() { |
2065 | d0fe8c12 | Athina Bekakou | var adapter = get(this, 'adapter'); |
2066 | d0fe8c12 | Athina Bekakou | |
2067 | d0fe8c12 | Athina Bekakou | Ember.assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name or a factory', !(adapter instanceof DS.Adapter)); |
2068 | d0fe8c12 | Athina Bekakou | |
2069 | d0fe8c12 | Athina Bekakou | if (typeof adapter === 'string') { |
2070 | d0fe8c12 | Athina Bekakou | adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:_rest'); |
2071 | d0fe8c12 | Athina Bekakou | } |
2072 | d0fe8c12 | Athina Bekakou | |
2073 | d0fe8c12 | Athina Bekakou | if (DS.Adapter.detect(adapter)) {
|
2074 | d0fe8c12 | Athina Bekakou | adapter = adapter.create({ container: this.container }); |
2075 | d0fe8c12 | Athina Bekakou | } |
2076 | d0fe8c12 | Athina Bekakou | |
2077 | d0fe8c12 | Athina Bekakou | return adapter;
|
2078 | d0fe8c12 | Athina Bekakou | }), |
2079 | d0fe8c12 | Athina Bekakou | |
2080 | d0fe8c12 | Athina Bekakou | // .....................
|
2081 | d0fe8c12 | Athina Bekakou | // . CREATE NEW RECORD .
|
2082 | d0fe8c12 | Athina Bekakou | // .....................
|
2083 | d0fe8c12 | Athina Bekakou | |
2084 | d0fe8c12 | Athina Bekakou | /**
|
2085 | d0fe8c12 | Athina Bekakou | Create a new record in the current store. The properties passed
|
2086 | d0fe8c12 | Athina Bekakou | to this method are set on the newly created record.
|
2087 | d0fe8c12 | Athina Bekakou | |
2088 | d0fe8c12 | Athina Bekakou | To create a new instance of `App.Post`:
|
2089 | d0fe8c12 | Athina Bekakou | |
2090 | d0fe8c12 | Athina Bekakou | ```js
|
2091 | d0fe8c12 | Athina Bekakou | store.createRecord('post', {
|
2092 | d0fe8c12 | Athina Bekakou | title: "Rails is omakase"
|
2093 | d0fe8c12 | Athina Bekakou | });
|
2094 | d0fe8c12 | Athina Bekakou | ```
|
2095 | d0fe8c12 | Athina Bekakou | |
2096 | d0fe8c12 | Athina Bekakou | @method createRecord
|
2097 | d0fe8c12 | Athina Bekakou | @param {String} type
|
2098 | d0fe8c12 | Athina Bekakou | @param {Object} properties a hash of properties to set on the
|
2099 | d0fe8c12 | Athina Bekakou | newly created record.
|
2100 | d0fe8c12 | Athina Bekakou | @returns {DS.Model} record
|
2101 | d0fe8c12 | Athina Bekakou | */
|
2102 | d0fe8c12 | Athina Bekakou | createRecord: function(type, properties) { |
2103 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2104 | d0fe8c12 | Athina Bekakou | |
2105 | d0fe8c12 | Athina Bekakou | properties = copy(properties) || {}; |
2106 | d0fe8c12 | Athina Bekakou | |
2107 | d0fe8c12 | Athina Bekakou | // If the passed properties do not include a primary key,
|
2108 | d0fe8c12 | Athina Bekakou | // give the adapter an opportunity to generate one. Typically,
|
2109 | d0fe8c12 | Athina Bekakou | // client-side ID generators will use something like uuid.js
|
2110 | d0fe8c12 | Athina Bekakou | // to avoid conflicts.
|
2111 | d0fe8c12 | Athina Bekakou | |
2112 | d0fe8c12 | Athina Bekakou | if (isNone(properties.id)) {
|
2113 | d0fe8c12 | Athina Bekakou | properties.id = this._generateId(type);
|
2114 | d0fe8c12 | Athina Bekakou | } |
2115 | d0fe8c12 | Athina Bekakou | |
2116 | d0fe8c12 | Athina Bekakou | // Coerce ID to a string
|
2117 | d0fe8c12 | Athina Bekakou | properties.id = coerceId(properties.id); |
2118 | d0fe8c12 | Athina Bekakou | |
2119 | d0fe8c12 | Athina Bekakou | var record = this.buildRecord(type, properties.id); |
2120 | d0fe8c12 | Athina Bekakou | |
2121 | d0fe8c12 | Athina Bekakou | // Move the record out of its initial `empty` state into
|
2122 | d0fe8c12 | Athina Bekakou | // the `loaded` state.
|
2123 | d0fe8c12 | Athina Bekakou | record.loadedData(); |
2124 | d0fe8c12 | Athina Bekakou | |
2125 | d0fe8c12 | Athina Bekakou | // Set the properties specified on the record.
|
2126 | d0fe8c12 | Athina Bekakou | record.setProperties(properties); |
2127 | d0fe8c12 | Athina Bekakou | |
2128 | d0fe8c12 | Athina Bekakou | return record;
|
2129 | d0fe8c12 | Athina Bekakou | }, |
2130 | d0fe8c12 | Athina Bekakou | |
2131 | d0fe8c12 | Athina Bekakou | /**
|
2132 | d0fe8c12 | Athina Bekakou | If possible, this method asks the adapter to generate an ID for
|
2133 | d0fe8c12 | Athina Bekakou | a newly created record.
|
2134 | d0fe8c12 | Athina Bekakou | |
2135 | d0fe8c12 | Athina Bekakou | @method _generateId
|
2136 | d0fe8c12 | Athina Bekakou | @private
|
2137 | d0fe8c12 | Athina Bekakou | @param {String} type
|
2138 | d0fe8c12 | Athina Bekakou | @returns {String} if the adapter can generate one, an ID
|
2139 | d0fe8c12 | Athina Bekakou | */
|
2140 | d0fe8c12 | Athina Bekakou | _generateId: function(type) { |
2141 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(type); |
2142 | d0fe8c12 | Athina Bekakou | |
2143 | d0fe8c12 | Athina Bekakou | if (adapter && adapter.generateIdForRecord) {
|
2144 | d0fe8c12 | Athina Bekakou | return adapter.generateIdForRecord(this); |
2145 | d0fe8c12 | Athina Bekakou | } |
2146 | d0fe8c12 | Athina Bekakou | |
2147 | d0fe8c12 | Athina Bekakou | return null; |
2148 | d0fe8c12 | Athina Bekakou | }, |
2149 | d0fe8c12 | Athina Bekakou | |
2150 | d0fe8c12 | Athina Bekakou | // .................
|
2151 | d0fe8c12 | Athina Bekakou | // . DELETE RECORD .
|
2152 | d0fe8c12 | Athina Bekakou | // .................
|
2153 | d0fe8c12 | Athina Bekakou | |
2154 | d0fe8c12 | Athina Bekakou | /**
|
2155 | d0fe8c12 | Athina Bekakou | For symmetry, a record can be deleted via the store.
|
2156 | d0fe8c12 | Athina Bekakou | |
2157 | d0fe8c12 | Athina Bekakou | Example
|
2158 | d0fe8c12 | Athina Bekakou | |
2159 | d0fe8c12 | Athina Bekakou | ```javascript
|
2160 | d0fe8c12 | Athina Bekakou | var post = store.createRecord('post', {
|
2161 | d0fe8c12 | Athina Bekakou | title: "Rails is omakase"
|
2162 | d0fe8c12 | Athina Bekakou | });
|
2163 | d0fe8c12 | Athina Bekakou | |
2164 | d0fe8c12 | Athina Bekakou | store.deletedRecord(post);
|
2165 | d0fe8c12 | Athina Bekakou | ```
|
2166 | d0fe8c12 | Athina Bekakou | |
2167 | d0fe8c12 | Athina Bekakou | @method deleteRecord
|
2168 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2169 | d0fe8c12 | Athina Bekakou | */
|
2170 | d0fe8c12 | Athina Bekakou | deleteRecord: function(record) { |
2171 | d0fe8c12 | Athina Bekakou | record.deleteRecord(); |
2172 | d0fe8c12 | Athina Bekakou | }, |
2173 | d0fe8c12 | Athina Bekakou | |
2174 | d0fe8c12 | Athina Bekakou | /**
|
2175 | d0fe8c12 | Athina Bekakou | For symmetry, a record can be unloaded via the store. Only
|
2176 | d0fe8c12 | Athina Bekakou | non-dirty records can be unloaded.
|
2177 | d0fe8c12 | Athina Bekakou | |
2178 | d0fe8c12 | Athina Bekakou | Example
|
2179 | d0fe8c12 | Athina Bekakou | |
2180 | d0fe8c12 | Athina Bekakou | ```javascript
|
2181 | d0fe8c12 | Athina Bekakou | store.find('post', 1).then(function(post) {
|
2182 | d0fe8c12 | Athina Bekakou | store.unloadRecord(post);
|
2183 | d0fe8c12 | Athina Bekakou | });
|
2184 | d0fe8c12 | Athina Bekakou | ```
|
2185 | d0fe8c12 | Athina Bekakou | |
2186 | d0fe8c12 | Athina Bekakou | @method unloadRecord
|
2187 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2188 | d0fe8c12 | Athina Bekakou | */
|
2189 | d0fe8c12 | Athina Bekakou | unloadRecord: function(record) { |
2190 | d0fe8c12 | Athina Bekakou | record.unloadRecord(); |
2191 | d0fe8c12 | Athina Bekakou | }, |
2192 | d0fe8c12 | Athina Bekakou | |
2193 | d0fe8c12 | Athina Bekakou | // ................
|
2194 | d0fe8c12 | Athina Bekakou | // . FIND RECORDS .
|
2195 | d0fe8c12 | Athina Bekakou | // ................
|
2196 | d0fe8c12 | Athina Bekakou | |
2197 | d0fe8c12 | Athina Bekakou | /**
|
2198 | d0fe8c12 | Athina Bekakou | This is the main entry point into finding records. The first parameter to
|
2199 | d0fe8c12 | Athina Bekakou | this method is the model's name as a string.
|
2200 | d0fe8c12 | Athina Bekakou | |
2201 | d0fe8c12 | Athina Bekakou | ---
|
2202 | d0fe8c12 | Athina Bekakou | |
2203 | d0fe8c12 | Athina Bekakou | To find a record by ID, pass the `id` as the second parameter:
|
2204 | d0fe8c12 | Athina Bekakou | |
2205 | d0fe8c12 | Athina Bekakou | ```javascript
|
2206 | d0fe8c12 | Athina Bekakou | store.find('person', 1);
|
2207 | d0fe8c12 | Athina Bekakou | ```
|
2208 | d0fe8c12 | Athina Bekakou | |
2209 | d0fe8c12 | Athina Bekakou | The `find` method will always return a **promise** that will be resolved
|
2210 | d0fe8c12 | Athina Bekakou | with the record. If the record was already in the store, the promise will
|
2211 | d0fe8c12 | Athina Bekakou | be resolved immediately. Otherwise, the store will ask the adapter's `find`
|
2212 | d0fe8c12 | Athina Bekakou | method to find the necessary data.
|
2213 | d0fe8c12 | Athina Bekakou | |
2214 | d0fe8c12 | Athina Bekakou | The `find` method will always resolve its promise with the same object for
|
2215 | d0fe8c12 | Athina Bekakou | a given type and `id`.
|
2216 | d0fe8c12 | Athina Bekakou | |
2217 | d0fe8c12 | Athina Bekakou | ---
|
2218 | d0fe8c12 | Athina Bekakou | |
2219 | d0fe8c12 | Athina Bekakou | To find all records for a type, call `find` with no additional parameters:
|
2220 | d0fe8c12 | Athina Bekakou | |
2221 | d0fe8c12 | Athina Bekakou | ```javascript
|
2222 | d0fe8c12 | Athina Bekakou | store.find('person');
|
2223 | d0fe8c12 | Athina Bekakou | ```
|
2224 | d0fe8c12 | Athina Bekakou | |
2225 | d0fe8c12 | Athina Bekakou | This will ask the adapter's `findAll` method to find the records for the
|
2226 | d0fe8c12 | Athina Bekakou | given type, and return a promise that will be resolved once the server
|
2227 | d0fe8c12 | Athina Bekakou | returns the values.
|
2228 | d0fe8c12 | Athina Bekakou | |
2229 | d0fe8c12 | Athina Bekakou | ---
|
2230 | d0fe8c12 | Athina Bekakou | |
2231 | d0fe8c12 | Athina Bekakou | To find a record by a query, call `find` with a hash as the second
|
2232 | d0fe8c12 | Athina Bekakou | parameter:
|
2233 | d0fe8c12 | Athina Bekakou | |
2234 | d0fe8c12 | Athina Bekakou | ```javascript
|
2235 | d0fe8c12 | Athina Bekakou | store.find(App.Person, { page: 1 });
|
2236 | d0fe8c12 | Athina Bekakou | ```
|
2237 | d0fe8c12 | Athina Bekakou | |
2238 | d0fe8c12 | Athina Bekakou | This will ask the adapter's `findQuery` method to find the records for
|
2239 | d0fe8c12 | Athina Bekakou | the query, and return a promise that will be resolved once the server
|
2240 | d0fe8c12 | Athina Bekakou | responds.
|
2241 | d0fe8c12 | Athina Bekakou | |
2242 | d0fe8c12 | Athina Bekakou | @method find
|
2243 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2244 | d0fe8c12 | Athina Bekakou | @param {Object|String|Integer|null} id
|
2245 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
2246 | d0fe8c12 | Athina Bekakou | */
|
2247 | d0fe8c12 | Athina Bekakou | find: function(type, id) { |
2248 | d0fe8c12 | Athina Bekakou | if (id === undefined) { |
2249 | d0fe8c12 | Athina Bekakou | return this.findAll(type); |
2250 | d0fe8c12 | Athina Bekakou | } |
2251 | d0fe8c12 | Athina Bekakou | |
2252 | d0fe8c12 | Athina Bekakou | // We are passed a query instead of an id.
|
2253 | d0fe8c12 | Athina Bekakou | if (Ember.typeOf(id) === 'object') { |
2254 | d0fe8c12 | Athina Bekakou | return this.findQuery(type, id); |
2255 | d0fe8c12 | Athina Bekakou | } |
2256 | d0fe8c12 | Athina Bekakou | |
2257 | d0fe8c12 | Athina Bekakou | return this.findById(type, coerceId(id)); |
2258 | d0fe8c12 | Athina Bekakou | }, |
2259 | d0fe8c12 | Athina Bekakou | |
2260 | d0fe8c12 | Athina Bekakou | /**
|
2261 | d0fe8c12 | Athina Bekakou | This method returns a record for a given type and id combination.
|
2262 | d0fe8c12 | Athina Bekakou | |
2263 | d0fe8c12 | Athina Bekakou | @method findById
|
2264 | d0fe8c12 | Athina Bekakou | @private
|
2265 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2266 | d0fe8c12 | Athina Bekakou | @param {String|Integer} id
|
2267 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
2268 | d0fe8c12 | Athina Bekakou | */
|
2269 | d0fe8c12 | Athina Bekakou | findById: function(type, id) { |
2270 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2271 | d0fe8c12 | Athina Bekakou | |
2272 | d0fe8c12 | Athina Bekakou | var record = this.recordForId(type, id); |
2273 | d0fe8c12 | Athina Bekakou | |
2274 | d0fe8c12 | Athina Bekakou | var promise = this.fetchRecord(record) || resolve(record, "DS: Store#findById " + type + " with id: " + id); |
2275 | d0fe8c12 | Athina Bekakou | return promiseObject(promise);
|
2276 | d0fe8c12 | Athina Bekakou | }, |
2277 | d0fe8c12 | Athina Bekakou | |
2278 | d0fe8c12 | Athina Bekakou | /**
|
2279 | d0fe8c12 | Athina Bekakou | This method makes a series of requests to the adapter's `find` method
|
2280 | d0fe8c12 | Athina Bekakou | and returns a promise that resolves once they are all loaded.
|
2281 | d0fe8c12 | Athina Bekakou | |
2282 | d0fe8c12 | Athina Bekakou | @private
|
2283 | d0fe8c12 | Athina Bekakou | @method findByIds
|
2284 | d0fe8c12 | Athina Bekakou | @param {String} type
|
2285 | d0fe8c12 | Athina Bekakou | @param {Array} ids
|
2286 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
2287 | d0fe8c12 | Athina Bekakou | */
|
2288 | d0fe8c12 | Athina Bekakou | findByIds: function(type, ids) { |
2289 | d0fe8c12 | Athina Bekakou | var store = this; |
2290 | d0fe8c12 | Athina Bekakou | var promiseLabel = "DS: Store#findByIds " + type; |
2291 | d0fe8c12 | Athina Bekakou | return promiseArray(Ember.RSVP.all(map(ids, function(id) { |
2292 | d0fe8c12 | Athina Bekakou | return store.findById(type, id);
|
2293 | d0fe8c12 | Athina Bekakou | })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete")); |
2294 | d0fe8c12 | Athina Bekakou | }, |
2295 | d0fe8c12 | Athina Bekakou | |
2296 | d0fe8c12 | Athina Bekakou | /**
|
2297 | d0fe8c12 | Athina Bekakou | This method is called by `findById` if it discovers that a particular
|
2298 | d0fe8c12 | Athina Bekakou | type/id pair hasn't been loaded yet to kick off a request to the
|
2299 | d0fe8c12 | Athina Bekakou | adapter.
|
2300 | d0fe8c12 | Athina Bekakou | |
2301 | d0fe8c12 | Athina Bekakou | @method fetchRecord
|
2302 | d0fe8c12 | Athina Bekakou | @private
|
2303 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2304 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
2305 | d0fe8c12 | Athina Bekakou | */
|
2306 | d0fe8c12 | Athina Bekakou | fetchRecord: function(record) { |
2307 | d0fe8c12 | Athina Bekakou | if (isNone(record)) { return null; } |
2308 | d0fe8c12 | Athina Bekakou | if (record._loadingPromise) { return record._loadingPromise; } |
2309 | d0fe8c12 | Athina Bekakou | if (!get(record, 'isEmpty')) { return null; } |
2310 | d0fe8c12 | Athina Bekakou | |
2311 | d0fe8c12 | Athina Bekakou | var type = record.constructor,
|
2312 | d0fe8c12 | Athina Bekakou | id = get(record, 'id');
|
2313 | d0fe8c12 | Athina Bekakou | |
2314 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(type); |
2315 | d0fe8c12 | Athina Bekakou | |
2316 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter); |
2317 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find); |
2318 | d0fe8c12 | Athina Bekakou | |
2319 | d0fe8c12 | Athina Bekakou | var promise = _find(adapter, this, type, id); |
2320 | d0fe8c12 | Athina Bekakou | record.loadingData(promise); |
2321 | d0fe8c12 | Athina Bekakou | return promise;
|
2322 | d0fe8c12 | Athina Bekakou | }, |
2323 | d0fe8c12 | Athina Bekakou | |
2324 | d0fe8c12 | Athina Bekakou | /**
|
2325 | d0fe8c12 | Athina Bekakou | Get a record by a given type and ID without triggering a fetch.
|
2326 | d0fe8c12 | Athina Bekakou | |
2327 | d0fe8c12 | Athina Bekakou | This method will synchronously return the record if it's available.
|
2328 | d0fe8c12 | Athina Bekakou | Otherwise, it will return null.
|
2329 | d0fe8c12 | Athina Bekakou | |
2330 | d0fe8c12 | Athina Bekakou | ```js
|
2331 | d0fe8c12 | Athina Bekakou | var post = store.getById('post', 1);
|
2332 | d0fe8c12 | Athina Bekakou | ```
|
2333 | d0fe8c12 | Athina Bekakou | |
2334 | d0fe8c12 | Athina Bekakou | @method getById
|
2335 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2336 | d0fe8c12 | Athina Bekakou | @param {String|Integer} id
|
2337 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2338 | d0fe8c12 | Athina Bekakou | */
|
2339 | d0fe8c12 | Athina Bekakou | getById: function(type, id) { |
2340 | d0fe8c12 | Athina Bekakou | if (this.hasRecordForId(type, id)) { |
2341 | d0fe8c12 | Athina Bekakou | return this.recordForId(type, id); |
2342 | d0fe8c12 | Athina Bekakou | } else {
|
2343 | d0fe8c12 | Athina Bekakou | return null; |
2344 | d0fe8c12 | Athina Bekakou | } |
2345 | d0fe8c12 | Athina Bekakou | }, |
2346 | d0fe8c12 | Athina Bekakou | |
2347 | d0fe8c12 | Athina Bekakou | /**
|
2348 | d0fe8c12 | Athina Bekakou | This method is called by the record's `reload` method.
|
2349 | d0fe8c12 | Athina Bekakou | |
2350 | d0fe8c12 | Athina Bekakou | This method calls the adapter's `find` method, which returns a promise. When
|
2351 | d0fe8c12 | Athina Bekakou | **that** promise resolves, `reloadRecord` will resolve the promise returned
|
2352 | d0fe8c12 | Athina Bekakou | by the record's `reload`.
|
2353 | d0fe8c12 | Athina Bekakou | |
2354 | d0fe8c12 | Athina Bekakou | @method reloadRecord
|
2355 | d0fe8c12 | Athina Bekakou | @private
|
2356 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2357 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
2358 | d0fe8c12 | Athina Bekakou | */
|
2359 | d0fe8c12 | Athina Bekakou | reloadRecord: function(record) { |
2360 | d0fe8c12 | Athina Bekakou | var type = record.constructor,
|
2361 | d0fe8c12 | Athina Bekakou | adapter = this.adapterFor(type),
|
2362 | d0fe8c12 | Athina Bekakou | id = get(record, 'id');
|
2363 | d0fe8c12 | Athina Bekakou | |
2364 | d0fe8c12 | Athina Bekakou | Ember.assert("You cannot reload a record without an ID", id);
|
2365 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter); |
2366 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to reload a record but your adapter does not implement `find`", adapter.find);
|
2367 | d0fe8c12 | Athina Bekakou | |
2368 | d0fe8c12 | Athina Bekakou | return _find(adapter, this, type, id); |
2369 | d0fe8c12 | Athina Bekakou | }, |
2370 | d0fe8c12 | Athina Bekakou | |
2371 | d0fe8c12 | Athina Bekakou | /**
|
2372 | d0fe8c12 | Athina Bekakou | This method takes a list of records, groups the records by type,
|
2373 | d0fe8c12 | Athina Bekakou | converts the records into IDs, and then invokes the adapter's `findMany`
|
2374 | d0fe8c12 | Athina Bekakou | method.
|
2375 | d0fe8c12 | Athina Bekakou | |
2376 | d0fe8c12 | Athina Bekakou | The records are grouped by type to invoke `findMany` on adapters
|
2377 | d0fe8c12 | Athina Bekakou | for each unique type in records.
|
2378 | d0fe8c12 | Athina Bekakou | |
2379 | d0fe8c12 | Athina Bekakou | It is used both by a brand new relationship (via the `findMany`
|
2380 | d0fe8c12 | Athina Bekakou | method) or when the data underlying an existing relationship
|
2381 | d0fe8c12 | Athina Bekakou | changes.
|
2382 | d0fe8c12 | Athina Bekakou | |
2383 | d0fe8c12 | Athina Bekakou | @method fetchMany
|
2384 | d0fe8c12 | Athina Bekakou | @private
|
2385 | d0fe8c12 | Athina Bekakou | @param {Array} records
|
2386 | d0fe8c12 | Athina Bekakou | @param {DS.Model} owner
|
2387 | d0fe8c12 | Athina Bekakou | @param {Resolver} resolver
|
2388 | d0fe8c12 | Athina Bekakou | */
|
2389 | d0fe8c12 | Athina Bekakou | fetchMany: function(records, owner, resolver) { |
2390 | d0fe8c12 | Athina Bekakou | if (!records.length) { return; } |
2391 | d0fe8c12 | Athina Bekakou | |
2392 | d0fe8c12 | Athina Bekakou | // Group By Type
|
2393 | d0fe8c12 | Athina Bekakou | var recordsByTypeMap = Ember.MapWithDefault.create({
|
2394 | d0fe8c12 | Athina Bekakou | defaultValue: function() { return Ember.A(); } |
2395 | d0fe8c12 | Athina Bekakou | }); |
2396 | d0fe8c12 | Athina Bekakou | |
2397 | d0fe8c12 | Athina Bekakou | forEach(records, function(record) {
|
2398 | d0fe8c12 | Athina Bekakou | recordsByTypeMap.get(record.constructor).push(record); |
2399 | d0fe8c12 | Athina Bekakou | }); |
2400 | d0fe8c12 | Athina Bekakou | |
2401 | d0fe8c12 | Athina Bekakou | forEach(recordsByTypeMap, function(type, records) {
|
2402 | d0fe8c12 | Athina Bekakou | var ids = records.mapProperty('id'), |
2403 | d0fe8c12 | Athina Bekakou | adapter = this.adapterFor(type);
|
2404 | d0fe8c12 | Athina Bekakou | |
2405 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter); |
2406 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load many records but your adapter does not implement `findMany`", adapter.findMany);
|
2407 | d0fe8c12 | Athina Bekakou | |
2408 | d0fe8c12 | Athina Bekakou | resolver.resolve(_findMany(adapter, this, type, ids, owner));
|
2409 | d0fe8c12 | Athina Bekakou | }, this);
|
2410 | d0fe8c12 | Athina Bekakou | }, |
2411 | d0fe8c12 | Athina Bekakou | |
2412 | d0fe8c12 | Athina Bekakou | /**
|
2413 | d0fe8c12 | Athina Bekakou | Returns true if a record for a given type and ID is already loaded.
|
2414 | d0fe8c12 | Athina Bekakou | |
2415 | d0fe8c12 | Athina Bekakou | @method hasRecordForId
|
2416 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2417 | d0fe8c12 | Athina Bekakou | @param {String|Integer} id
|
2418 | d0fe8c12 | Athina Bekakou | @returns {Boolean}
|
2419 | d0fe8c12 | Athina Bekakou | */
|
2420 | d0fe8c12 | Athina Bekakou | hasRecordForId: function(type, id) { |
2421 | d0fe8c12 | Athina Bekakou | id = coerceId(id); |
2422 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2423 | d0fe8c12 | Athina Bekakou | return !!this.typeMapFor(type).idToRecord[id]; |
2424 | d0fe8c12 | Athina Bekakou | }, |
2425 | d0fe8c12 | Athina Bekakou | |
2426 | d0fe8c12 | Athina Bekakou | /**
|
2427 | d0fe8c12 | Athina Bekakou | Returns id record for a given type and ID. If one isn't already loaded,
|
2428 | d0fe8c12 | Athina Bekakou | it builds a new record and leaves it in the `empty` state.
|
2429 | d0fe8c12 | Athina Bekakou | |
2430 | d0fe8c12 | Athina Bekakou | @method recordForId
|
2431 | d0fe8c12 | Athina Bekakou | @private
|
2432 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2433 | d0fe8c12 | Athina Bekakou | @param {String|Integer} id
|
2434 | d0fe8c12 | Athina Bekakou | @returns {DS.Model} record
|
2435 | d0fe8c12 | Athina Bekakou | */
|
2436 | d0fe8c12 | Athina Bekakou | recordForId: function(type, id) { |
2437 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2438 | d0fe8c12 | Athina Bekakou | |
2439 | d0fe8c12 | Athina Bekakou | id = coerceId(id); |
2440 | d0fe8c12 | Athina Bekakou | |
2441 | d0fe8c12 | Athina Bekakou | var record = this.typeMapFor(type).idToRecord[id]; |
2442 | d0fe8c12 | Athina Bekakou | |
2443 | d0fe8c12 | Athina Bekakou | if (!record) {
|
2444 | d0fe8c12 | Athina Bekakou | record = this.buildRecord(type, id);
|
2445 | d0fe8c12 | Athina Bekakou | } |
2446 | d0fe8c12 | Athina Bekakou | |
2447 | d0fe8c12 | Athina Bekakou | return record;
|
2448 | d0fe8c12 | Athina Bekakou | }, |
2449 | d0fe8c12 | Athina Bekakou | |
2450 | d0fe8c12 | Athina Bekakou | /**
|
2451 | d0fe8c12 | Athina Bekakou | @method findMany
|
2452 | d0fe8c12 | Athina Bekakou | @private
|
2453 | d0fe8c12 | Athina Bekakou | @param {DS.Model} owner
|
2454 | d0fe8c12 | Athina Bekakou | @param {Array} records
|
2455 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2456 | d0fe8c12 | Athina Bekakou | @param {Resolver} resolver
|
2457 | d0fe8c12 | Athina Bekakou | @return {DS.ManyArray} records
|
2458 | d0fe8c12 | Athina Bekakou | */
|
2459 | d0fe8c12 | Athina Bekakou | findMany: function(owner, records, type, resolver) { |
2460 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2461 | d0fe8c12 | Athina Bekakou | |
2462 | d0fe8c12 | Athina Bekakou | records = Ember.A(records); |
2463 | d0fe8c12 | Athina Bekakou | |
2464 | d0fe8c12 | Athina Bekakou | var unloadedRecords = records.filterProperty('isEmpty', true), |
2465 | d0fe8c12 | Athina Bekakou | manyArray = this.recordArrayManager.createManyArray(type, records);
|
2466 | d0fe8c12 | Athina Bekakou | |
2467 | d0fe8c12 | Athina Bekakou | forEach(unloadedRecords, function(record) {
|
2468 | d0fe8c12 | Athina Bekakou | record.loadingData(); |
2469 | d0fe8c12 | Athina Bekakou | }); |
2470 | d0fe8c12 | Athina Bekakou | |
2471 | d0fe8c12 | Athina Bekakou | manyArray.loadingRecordsCount = unloadedRecords.length; |
2472 | d0fe8c12 | Athina Bekakou | |
2473 | d0fe8c12 | Athina Bekakou | if (unloadedRecords.length) {
|
2474 | d0fe8c12 | Athina Bekakou | forEach(unloadedRecords, function(record) {
|
2475 | d0fe8c12 | Athina Bekakou | this.recordArrayManager.registerWaitingRecordArray(record, manyArray);
|
2476 | d0fe8c12 | Athina Bekakou | }, this);
|
2477 | d0fe8c12 | Athina Bekakou | |
2478 | d0fe8c12 | Athina Bekakou | this.fetchMany(unloadedRecords, owner, resolver);
|
2479 | d0fe8c12 | Athina Bekakou | } else {
|
2480 | d0fe8c12 | Athina Bekakou | if (resolver) { resolver.resolve(); }
|
2481 | d0fe8c12 | Athina Bekakou | manyArray.set('isLoaded', true); |
2482 | d0fe8c12 | Athina Bekakou | Ember.run.once(manyArray, 'trigger', 'didLoad'); |
2483 | d0fe8c12 | Athina Bekakou | } |
2484 | d0fe8c12 | Athina Bekakou | |
2485 | d0fe8c12 | Athina Bekakou | return manyArray;
|
2486 | d0fe8c12 | Athina Bekakou | }, |
2487 | d0fe8c12 | Athina Bekakou | |
2488 | d0fe8c12 | Athina Bekakou | /**
|
2489 | d0fe8c12 | Athina Bekakou | If a relationship was originally populated by the adapter as a link
|
2490 | d0fe8c12 | Athina Bekakou | (as opposed to a list of IDs), this method is called when the
|
2491 | d0fe8c12 | Athina Bekakou | relationship is fetched.
|
2492 | d0fe8c12 | Athina Bekakou | |
2493 | d0fe8c12 | Athina Bekakou | The link (which is usually a URL) is passed through unchanged, so the
|
2494 | d0fe8c12 | Athina Bekakou | adapter can make whatever request it wants.
|
2495 | d0fe8c12 | Athina Bekakou | |
2496 | d0fe8c12 | Athina Bekakou | The usual use-case is for the server to register a URL as a link, and
|
2497 | d0fe8c12 | Athina Bekakou | then use that URL in the future to make a request for the relationship.
|
2498 | d0fe8c12 | Athina Bekakou | |
2499 | d0fe8c12 | Athina Bekakou | @method findHasMany
|
2500 | d0fe8c12 | Athina Bekakou | @private
|
2501 | d0fe8c12 | Athina Bekakou | @param {DS.Model} owner
|
2502 | d0fe8c12 | Athina Bekakou | @param {any} link
|
2503 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2504 | d0fe8c12 | Athina Bekakou | @param {Resolver} resolver
|
2505 | d0fe8c12 | Athina Bekakou | @return {DS.ManyArray}
|
2506 | d0fe8c12 | Athina Bekakou | */
|
2507 | d0fe8c12 | Athina Bekakou | findHasMany: function(owner, link, relationship, resolver) { |
2508 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(owner.constructor); |
2509 | d0fe8c12 | Athina Bekakou | |
2510 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter); |
2511 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", adapter.findHasMany);
|
2512 | d0fe8c12 | Athina Bekakou | |
2513 | d0fe8c12 | Athina Bekakou | var records = this.recordArrayManager.createManyArray(relationship.type, Ember.A([])); |
2514 | d0fe8c12 | Athina Bekakou | resolver.resolve(_findHasMany(adapter, this, owner, link, relationship));
|
2515 | d0fe8c12 | Athina Bekakou | return records;
|
2516 | d0fe8c12 | Athina Bekakou | }, |
2517 | d0fe8c12 | Athina Bekakou | |
2518 | d0fe8c12 | Athina Bekakou | /**
|
2519 | d0fe8c12 | Athina Bekakou | @method findBelongsTo
|
2520 | d0fe8c12 | Athina Bekakou | @private
|
2521 | d0fe8c12 | Athina Bekakou | @param {DS.Model} owner
|
2522 | d0fe8c12 | Athina Bekakou | @param {any} link
|
2523 | d0fe8c12 | Athina Bekakou | @param {Relationship} relationship
|
2524 | d0fe8c12 | Athina Bekakou | @param {Resolver} resolver
|
2525 | d0fe8c12 | Athina Bekakou | */
|
2526 | d0fe8c12 | Athina Bekakou | findBelongsTo: function(owner, link, relationship, resolver) { |
2527 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(owner.constructor); |
2528 | d0fe8c12 | Athina Bekakou | |
2529 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter); |
2530 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", adapter.findBelongsTo);
|
2531 | d0fe8c12 | Athina Bekakou | |
2532 | d0fe8c12 | Athina Bekakou | resolver.resolve(_findBelongsTo(adapter, this, owner, link, relationship));
|
2533 | d0fe8c12 | Athina Bekakou | }, |
2534 | d0fe8c12 | Athina Bekakou | |
2535 | d0fe8c12 | Athina Bekakou | /**
|
2536 | d0fe8c12 | Athina Bekakou | This method delegates a query to the adapter. This is the one place where
|
2537 | d0fe8c12 | Athina Bekakou | adapter-level semantics are exposed to the application.
|
2538 | d0fe8c12 | Athina Bekakou | |
2539 | d0fe8c12 | Athina Bekakou | Exposing queries this way seems preferable to creating an abstract query
|
2540 | d0fe8c12 | Athina Bekakou | language for all server-side queries, and then require all adapters to
|
2541 | d0fe8c12 | Athina Bekakou | implement them.
|
2542 | d0fe8c12 | Athina Bekakou | |
2543 | d0fe8c12 | Athina Bekakou | This method returns a promise, which is resolved with a `RecordArray`
|
2544 | d0fe8c12 | Athina Bekakou | once the server returns.
|
2545 | d0fe8c12 | Athina Bekakou | |
2546 | d0fe8c12 | Athina Bekakou | @method findQuery
|
2547 | d0fe8c12 | Athina Bekakou | @private
|
2548 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2549 | d0fe8c12 | Athina Bekakou | @param {any} query an opaque query to be used by the adapter
|
2550 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
2551 | d0fe8c12 | Athina Bekakou | */
|
2552 | d0fe8c12 | Athina Bekakou | findQuery: function(type, query) { |
2553 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2554 | d0fe8c12 | Athina Bekakou | |
2555 | d0fe8c12 | Athina Bekakou | var array = this.recordArrayManager |
2556 | d0fe8c12 | Athina Bekakou | .createAdapterPopulatedRecordArray(type, query); |
2557 | d0fe8c12 | Athina Bekakou | |
2558 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(type), |
2559 | d0fe8c12 | Athina Bekakou | promiseLabel = "DS: Store#findQuery " + type,
|
2560 | d0fe8c12 | Athina Bekakou | resolver = Ember.RSVP.defer(promiseLabel); |
2561 | d0fe8c12 | Athina Bekakou | |
2562 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter); |
2563 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", adapter.findQuery);
|
2564 | d0fe8c12 | Athina Bekakou | |
2565 | d0fe8c12 | Athina Bekakou | resolver.resolve(_findQuery(adapter, this, type, query, array));
|
2566 | d0fe8c12 | Athina Bekakou | |
2567 | d0fe8c12 | Athina Bekakou | return promiseArray(resolver.promise);
|
2568 | d0fe8c12 | Athina Bekakou | }, |
2569 | d0fe8c12 | Athina Bekakou | |
2570 | d0fe8c12 | Athina Bekakou | /**
|
2571 | d0fe8c12 | Athina Bekakou | This method returns an array of all records adapter can find.
|
2572 | d0fe8c12 | Athina Bekakou | It triggers the adapter's `findAll` method to give it an opportunity to populate
|
2573 | d0fe8c12 | Athina Bekakou | the array with records of that type.
|
2574 | d0fe8c12 | Athina Bekakou | |
2575 | d0fe8c12 | Athina Bekakou | @method findAll
|
2576 | d0fe8c12 | Athina Bekakou | @private
|
2577 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2578 | d0fe8c12 | Athina Bekakou | @return {DS.AdapterPopulatedRecordArray}
|
2579 | d0fe8c12 | Athina Bekakou | */
|
2580 | d0fe8c12 | Athina Bekakou | findAll: function(type) { |
2581 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2582 | d0fe8c12 | Athina Bekakou | |
2583 | d0fe8c12 | Athina Bekakou | return this.fetchAll(type, this.all(type)); |
2584 | d0fe8c12 | Athina Bekakou | }, |
2585 | d0fe8c12 | Athina Bekakou | |
2586 | d0fe8c12 | Athina Bekakou | /**
|
2587 | d0fe8c12 | Athina Bekakou | @method fetchAll
|
2588 | d0fe8c12 | Athina Bekakou | @private
|
2589 | d0fe8c12 | Athina Bekakou | @param {DS.Model} type
|
2590 | d0fe8c12 | Athina Bekakou | @param {DS.RecordArray} array
|
2591 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
2592 | d0fe8c12 | Athina Bekakou | */
|
2593 | d0fe8c12 | Athina Bekakou | fetchAll: function(type, array) { |
2594 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(type), |
2595 | d0fe8c12 | Athina Bekakou | sinceToken = this.typeMapFor(type).metadata.since;
|
2596 | d0fe8c12 | Athina Bekakou | |
2597 | d0fe8c12 | Athina Bekakou | set(array, 'isUpdating', true); |
2598 | d0fe8c12 | Athina Bekakou | |
2599 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter); |
2600 | d0fe8c12 | Athina Bekakou | Ember.assert("You tried to load all records but your adapter does not implement `findAll`", adapter.findAll);
|
2601 | d0fe8c12 | Athina Bekakou | |
2602 | d0fe8c12 | Athina Bekakou | return promiseArray(_findAll(adapter, this, type, sinceToken)); |
2603 | d0fe8c12 | Athina Bekakou | }, |
2604 | d0fe8c12 | Athina Bekakou | |
2605 | d0fe8c12 | Athina Bekakou | /**
|
2606 | d0fe8c12 | Athina Bekakou | @method didUpdateAll
|
2607 | d0fe8c12 | Athina Bekakou | @param {DS.Model} type
|
2608 | d0fe8c12 | Athina Bekakou | */
|
2609 | d0fe8c12 | Athina Bekakou | didUpdateAll: function(type) { |
2610 | d0fe8c12 | Athina Bekakou | var findAllCache = this.typeMapFor(type).findAllCache; |
2611 | d0fe8c12 | Athina Bekakou | set(findAllCache, 'isUpdating', false); |
2612 | d0fe8c12 | Athina Bekakou | }, |
2613 | d0fe8c12 | Athina Bekakou | |
2614 | d0fe8c12 | Athina Bekakou | /**
|
2615 | d0fe8c12 | Athina Bekakou | This method returns a filtered array that contains all of the known records
|
2616 | d0fe8c12 | Athina Bekakou | for a given type.
|
2617 | d0fe8c12 | Athina Bekakou | |
2618 | d0fe8c12 | Athina Bekakou | Note that because it's just a filter, it will have any locally
|
2619 | d0fe8c12 | Athina Bekakou | created records of the type.
|
2620 | d0fe8c12 | Athina Bekakou | |
2621 | d0fe8c12 | Athina Bekakou | Also note that multiple calls to `all` for a given type will always
|
2622 | d0fe8c12 | Athina Bekakou | return the same RecordArray.
|
2623 | d0fe8c12 | Athina Bekakou | |
2624 | d0fe8c12 | Athina Bekakou | Example
|
2625 | d0fe8c12 | Athina Bekakou | |
2626 | d0fe8c12 | Athina Bekakou | ```javascript
|
2627 | d0fe8c12 | Athina Bekakou | var local_posts = store.all(App.Post);
|
2628 | d0fe8c12 | Athina Bekakou | ```
|
2629 | d0fe8c12 | Athina Bekakou | |
2630 | d0fe8c12 | Athina Bekakou | @method all
|
2631 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2632 | d0fe8c12 | Athina Bekakou | @return {DS.RecordArray}
|
2633 | d0fe8c12 | Athina Bekakou | */
|
2634 | d0fe8c12 | Athina Bekakou | all: function(type) { |
2635 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2636 | d0fe8c12 | Athina Bekakou | |
2637 | d0fe8c12 | Athina Bekakou | var typeMap = this.typeMapFor(type), |
2638 | d0fe8c12 | Athina Bekakou | findAllCache = typeMap.findAllCache; |
2639 | d0fe8c12 | Athina Bekakou | |
2640 | d0fe8c12 | Athina Bekakou | if (findAllCache) { return findAllCache; } |
2641 | d0fe8c12 | Athina Bekakou | |
2642 | d0fe8c12 | Athina Bekakou | var array = this.recordArrayManager.createRecordArray(type); |
2643 | d0fe8c12 | Athina Bekakou | |
2644 | d0fe8c12 | Athina Bekakou | typeMap.findAllCache = array; |
2645 | d0fe8c12 | Athina Bekakou | return array;
|
2646 | d0fe8c12 | Athina Bekakou | }, |
2647 | d0fe8c12 | Athina Bekakou | |
2648 | d0fe8c12 | Athina Bekakou | |
2649 | d0fe8c12 | Athina Bekakou | /**
|
2650 | d0fe8c12 | Athina Bekakou | This method unloads all of the known records for a given type.
|
2651 | d0fe8c12 | Athina Bekakou | |
2652 | d0fe8c12 | Athina Bekakou | ```javascript
|
2653 | d0fe8c12 | Athina Bekakou | store.unloadAll(App.Post);
|
2654 | d0fe8c12 | Athina Bekakou | ```
|
2655 | d0fe8c12 | Athina Bekakou | |
2656 | d0fe8c12 | Athina Bekakou | @method unloadAll
|
2657 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2658 | d0fe8c12 | Athina Bekakou | */
|
2659 | d0fe8c12 | Athina Bekakou | unloadAll: function(type) { |
2660 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2661 | d0fe8c12 | Athina Bekakou | |
2662 | d0fe8c12 | Athina Bekakou | var typeMap = this.typeMapFor(type), |
2663 | d0fe8c12 | Athina Bekakou | records = typeMap.records, record; |
2664 | d0fe8c12 | Athina Bekakou | |
2665 | d0fe8c12 | Athina Bekakou | while(record = records.pop()) {
|
2666 | d0fe8c12 | Athina Bekakou | record.unloadRecord(); |
2667 | d0fe8c12 | Athina Bekakou | } |
2668 | d0fe8c12 | Athina Bekakou | |
2669 | d0fe8c12 | Athina Bekakou | typeMap.findAllCache = null;
|
2670 | d0fe8c12 | Athina Bekakou | }, |
2671 | d0fe8c12 | Athina Bekakou | |
2672 | d0fe8c12 | Athina Bekakou | /**
|
2673 | d0fe8c12 | Athina Bekakou | Takes a type and filter function, and returns a live RecordArray that
|
2674 | d0fe8c12 | Athina Bekakou | remains up to date as new records are loaded into the store or created
|
2675 | d0fe8c12 | Athina Bekakou | locally.
|
2676 | d0fe8c12 | Athina Bekakou | |
2677 | d0fe8c12 | Athina Bekakou | The callback function takes a materialized record, and returns true
|
2678 | d0fe8c12 | Athina Bekakou | if the record should be included in the filter and false if it should
|
2679 | d0fe8c12 | Athina Bekakou | not.
|
2680 | d0fe8c12 | Athina Bekakou | |
2681 | d0fe8c12 | Athina Bekakou | The filter function is called once on all records for the type when
|
2682 | d0fe8c12 | Athina Bekakou | it is created, and then once on each newly loaded or created record.
|
2683 | d0fe8c12 | Athina Bekakou | |
2684 | d0fe8c12 | Athina Bekakou | If any of a record's properties change, or if it changes state, the
|
2685 | d0fe8c12 | Athina Bekakou | filter function will be invoked again to determine whether it should
|
2686 | d0fe8c12 | Athina Bekakou | still be in the array.
|
2687 | d0fe8c12 | Athina Bekakou | |
2688 | d0fe8c12 | Athina Bekakou | Optionally you can pass a query which will be triggered at first. The
|
2689 | d0fe8c12 | Athina Bekakou | results returned by the server could then appear in the filter if they
|
2690 | d0fe8c12 | Athina Bekakou | match the filter function.
|
2691 | d0fe8c12 | Athina Bekakou | |
2692 | d0fe8c12 | Athina Bekakou | Example
|
2693 | d0fe8c12 | Athina Bekakou | |
2694 | d0fe8c12 | Athina Bekakou | ```javascript
|
2695 | d0fe8c12 | Athina Bekakou | store.filter(App.Post, {unread: true}, function(post) {
|
2696 | d0fe8c12 | Athina Bekakou | return post.get('unread');
|
2697 | d0fe8c12 | Athina Bekakou | }).then(function(unreadPosts) {
|
2698 | d0fe8c12 | Athina Bekakou | unreadPosts.get('length'); // 5
|
2699 | d0fe8c12 | Athina Bekakou | var unreadPost = unreadPosts.objectAt(0);
|
2700 | d0fe8c12 | Athina Bekakou | unreadPosts.set('unread', false);
|
2701 | d0fe8c12 | Athina Bekakou | unreadPosts.get('length'); // 4
|
2702 | d0fe8c12 | Athina Bekakou | });
|
2703 | d0fe8c12 | Athina Bekakou | ```
|
2704 | d0fe8c12 | Athina Bekakou | |
2705 | d0fe8c12 | Athina Bekakou | @method filter
|
2706 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2707 | d0fe8c12 | Athina Bekakou | @param {Object} query optional query
|
2708 | d0fe8c12 | Athina Bekakou | @param {Function} filter
|
2709 | d0fe8c12 | Athina Bekakou | @return {DS.PromiseArray}
|
2710 | d0fe8c12 | Athina Bekakou | */
|
2711 | d0fe8c12 | Athina Bekakou | filter: function(type, query, filter) { |
2712 | d0fe8c12 | Athina Bekakou | var promise;
|
2713 | d0fe8c12 | Athina Bekakou | |
2714 | d0fe8c12 | Athina Bekakou | // allow an optional server query
|
2715 | d0fe8c12 | Athina Bekakou | if (arguments.length === 3) { |
2716 | d0fe8c12 | Athina Bekakou | promise = this.findQuery(type, query);
|
2717 | d0fe8c12 | Athina Bekakou | } else if (arguments.length === 2) { |
2718 | d0fe8c12 | Athina Bekakou | filter = query; |
2719 | d0fe8c12 | Athina Bekakou | } |
2720 | d0fe8c12 | Athina Bekakou | |
2721 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2722 | d0fe8c12 | Athina Bekakou | |
2723 | d0fe8c12 | Athina Bekakou | var array = this.recordArrayManager |
2724 | d0fe8c12 | Athina Bekakou | .createFilteredRecordArray(type, filter); |
2725 | d0fe8c12 | Athina Bekakou | promise = promise || resolve(array); |
2726 | d0fe8c12 | Athina Bekakou | |
2727 | d0fe8c12 | Athina Bekakou | return promiseArray(promise.then(function() { |
2728 | d0fe8c12 | Athina Bekakou | return array;
|
2729 | d0fe8c12 | Athina Bekakou | }, null, "DS: Store#filter of " + type)); |
2730 | d0fe8c12 | Athina Bekakou | }, |
2731 | d0fe8c12 | Athina Bekakou | |
2732 | d0fe8c12 | Athina Bekakou | /**
|
2733 | d0fe8c12 | Athina Bekakou | This method returns if a certain record is already loaded
|
2734 | d0fe8c12 | Athina Bekakou | in the store. Use this function to know beforehand if a find()
|
2735 | d0fe8c12 | Athina Bekakou | will result in a request or that it will be a cache hit.
|
2736 | d0fe8c12 | Athina Bekakou | |
2737 | d0fe8c12 | Athina Bekakou | Example
|
2738 | d0fe8c12 | Athina Bekakou | |
2739 | d0fe8c12 | Athina Bekakou | ```javascript
|
2740 | d0fe8c12 | Athina Bekakou | store.recordIsLoaded(App.Post, 1); // false
|
2741 | d0fe8c12 | Athina Bekakou | store.find(App.Post, 1).then(function() {
|
2742 | d0fe8c12 | Athina Bekakou | store.recordIsLoaded(App.Post, 1); // true
|
2743 | d0fe8c12 | Athina Bekakou | });
|
2744 | d0fe8c12 | Athina Bekakou | ```
|
2745 | d0fe8c12 | Athina Bekakou | |
2746 | d0fe8c12 | Athina Bekakou | @method recordIsLoaded
|
2747 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2748 | d0fe8c12 | Athina Bekakou | @param {string} id
|
2749 | d0fe8c12 | Athina Bekakou | @return {boolean}
|
2750 | d0fe8c12 | Athina Bekakou | */
|
2751 | d0fe8c12 | Athina Bekakou | recordIsLoaded: function(type, id) { |
2752 | d0fe8c12 | Athina Bekakou | if (!this.hasRecordForId(type, id)) { return false; } |
2753 | d0fe8c12 | Athina Bekakou | return !get(this.recordForId(type, id), 'isEmpty'); |
2754 | d0fe8c12 | Athina Bekakou | }, |
2755 | d0fe8c12 | Athina Bekakou | |
2756 | d0fe8c12 | Athina Bekakou | /**
|
2757 | d0fe8c12 | Athina Bekakou | This method returns the metadata for a specific type.
|
2758 | d0fe8c12 | Athina Bekakou | |
2759 | d0fe8c12 | Athina Bekakou | @method metadataFor
|
2760 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2761 | d0fe8c12 | Athina Bekakou | @return {object}
|
2762 | d0fe8c12 | Athina Bekakou | */
|
2763 | d0fe8c12 | Athina Bekakou | metadataFor: function(type) { |
2764 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
2765 | d0fe8c12 | Athina Bekakou | return this.typeMapFor(type).metadata; |
2766 | d0fe8c12 | Athina Bekakou | }, |
2767 | d0fe8c12 | Athina Bekakou | |
2768 | d0fe8c12 | Athina Bekakou | // ............
|
2769 | d0fe8c12 | Athina Bekakou | // . UPDATING .
|
2770 | d0fe8c12 | Athina Bekakou | // ............
|
2771 | d0fe8c12 | Athina Bekakou | |
2772 | d0fe8c12 | Athina Bekakou | /**
|
2773 | d0fe8c12 | Athina Bekakou | If the adapter updates attributes or acknowledges creation
|
2774 | d0fe8c12 | Athina Bekakou | or deletion, the record will notify the store to update its
|
2775 | d0fe8c12 | Athina Bekakou | membership in any filters.
|
2776 | d0fe8c12 | Athina Bekakou | To avoid thrashing, this method is invoked only once per
|
2777 | d0fe8c12 | Athina Bekakou | |
2778 | d0fe8c12 | Athina Bekakou | run loop per record.
|
2779 | d0fe8c12 | Athina Bekakou | |
2780 | d0fe8c12 | Athina Bekakou | @method dataWasUpdated
|
2781 | d0fe8c12 | Athina Bekakou | @private
|
2782 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
2783 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2784 | d0fe8c12 | Athina Bekakou | */
|
2785 | d0fe8c12 | Athina Bekakou | dataWasUpdated: function(type, record) { |
2786 | d0fe8c12 | Athina Bekakou | this.recordArrayManager.recordDidChange(record);
|
2787 | d0fe8c12 | Athina Bekakou | }, |
2788 | d0fe8c12 | Athina Bekakou | |
2789 | d0fe8c12 | Athina Bekakou | // ..............
|
2790 | d0fe8c12 | Athina Bekakou | // . PERSISTING .
|
2791 | d0fe8c12 | Athina Bekakou | // ..............
|
2792 | d0fe8c12 | Athina Bekakou | |
2793 | d0fe8c12 | Athina Bekakou | /**
|
2794 | d0fe8c12 | Athina Bekakou | This method is called by `record.save`, and gets passed a
|
2795 | d0fe8c12 | Athina Bekakou | resolver for the promise that `record.save` returns.
|
2796 | d0fe8c12 | Athina Bekakou | |
2797 | d0fe8c12 | Athina Bekakou | It schedules saving to happen at the end of the run loop.
|
2798 | d0fe8c12 | Athina Bekakou | |
2799 | d0fe8c12 | Athina Bekakou | @method scheduleSave
|
2800 | d0fe8c12 | Athina Bekakou | @private
|
2801 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2802 | d0fe8c12 | Athina Bekakou | @param {Resolver} resolver
|
2803 | d0fe8c12 | Athina Bekakou | */
|
2804 | d0fe8c12 | Athina Bekakou | scheduleSave: function(record, resolver) { |
2805 | d0fe8c12 | Athina Bekakou | record.adapterWillCommit(); |
2806 | d0fe8c12 | Athina Bekakou | this._pendingSave.push([record, resolver]);
|
2807 | d0fe8c12 | Athina Bekakou | once(this, 'flushPendingSave'); |
2808 | d0fe8c12 | Athina Bekakou | }, |
2809 | d0fe8c12 | Athina Bekakou | |
2810 | d0fe8c12 | Athina Bekakou | /**
|
2811 | d0fe8c12 | Athina Bekakou | This method is called at the end of the run loop, and
|
2812 | d0fe8c12 | Athina Bekakou | flushes any records passed into `scheduleSave`
|
2813 | d0fe8c12 | Athina Bekakou | |
2814 | d0fe8c12 | Athina Bekakou | @method flushPendingSave
|
2815 | d0fe8c12 | Athina Bekakou | @private
|
2816 | d0fe8c12 | Athina Bekakou | */
|
2817 | d0fe8c12 | Athina Bekakou | flushPendingSave: function() { |
2818 | d0fe8c12 | Athina Bekakou | var pending = this._pendingSave.slice(); |
2819 | d0fe8c12 | Athina Bekakou | this._pendingSave = [];
|
2820 | d0fe8c12 | Athina Bekakou | |
2821 | d0fe8c12 | Athina Bekakou | forEach(pending, function(tuple) {
|
2822 | d0fe8c12 | Athina Bekakou | var record = tuple[0], resolver = tuple[1], |
2823 | d0fe8c12 | Athina Bekakou | adapter = this.adapterFor(record.constructor),
|
2824 | d0fe8c12 | Athina Bekakou | operation; |
2825 | d0fe8c12 | Athina Bekakou | |
2826 | d0fe8c12 | Athina Bekakou | if (get(record, 'isNew')) { |
2827 | d0fe8c12 | Athina Bekakou | operation = 'createRecord';
|
2828 | d0fe8c12 | Athina Bekakou | } else if (get(record, 'isDeleted')) { |
2829 | d0fe8c12 | Athina Bekakou | operation = 'deleteRecord';
|
2830 | d0fe8c12 | Athina Bekakou | } else {
|
2831 | d0fe8c12 | Athina Bekakou | operation = 'updateRecord';
|
2832 | d0fe8c12 | Athina Bekakou | } |
2833 | d0fe8c12 | Athina Bekakou | |
2834 | d0fe8c12 | Athina Bekakou | resolver.resolve(_commit(adapter, this, operation, record));
|
2835 | d0fe8c12 | Athina Bekakou | }, this);
|
2836 | d0fe8c12 | Athina Bekakou | }, |
2837 | d0fe8c12 | Athina Bekakou | |
2838 | d0fe8c12 | Athina Bekakou | /**
|
2839 | d0fe8c12 | Athina Bekakou | This method is called once the promise returned by an
|
2840 | d0fe8c12 | Athina Bekakou | adapter's `createRecord`, `updateRecord` or `deleteRecord`
|
2841 | d0fe8c12 | Athina Bekakou | is resolved.
|
2842 | d0fe8c12 | Athina Bekakou | |
2843 | d0fe8c12 | Athina Bekakou | If the data provides a server-generated ID, it will
|
2844 | d0fe8c12 | Athina Bekakou | update the record and the store's indexes.
|
2845 | d0fe8c12 | Athina Bekakou | |
2846 | d0fe8c12 | Athina Bekakou | @method didSaveRecord
|
2847 | d0fe8c12 | Athina Bekakou | @private
|
2848 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record the in-flight record
|
2849 | d0fe8c12 | Athina Bekakou | @param {Object} data optional data (see above)
|
2850 | d0fe8c12 | Athina Bekakou | */
|
2851 | d0fe8c12 | Athina Bekakou | didSaveRecord: function(record, data) { |
2852 | d0fe8c12 | Athina Bekakou | if (data) {
|
2853 | d0fe8c12 | Athina Bekakou | // normalize relationship IDs into records
|
2854 | d0fe8c12 | Athina Bekakou | data = normalizeRelationships(this, record.constructor, data, record);
|
2855 | d0fe8c12 | Athina Bekakou | |
2856 | d0fe8c12 | Athina Bekakou | this.updateId(record, data);
|
2857 | d0fe8c12 | Athina Bekakou | } |
2858 | d0fe8c12 | Athina Bekakou | |
2859 | d0fe8c12 | Athina Bekakou | record.adapterDidCommit(data); |
2860 | d0fe8c12 | Athina Bekakou | }, |
2861 | d0fe8c12 | Athina Bekakou | |
2862 | d0fe8c12 | Athina Bekakou | /**
|
2863 | d0fe8c12 | Athina Bekakou | This method is called once the promise returned by an
|
2864 | d0fe8c12 | Athina Bekakou | adapter's `createRecord`, `updateRecord` or `deleteRecord`
|
2865 | d0fe8c12 | Athina Bekakou | is rejected with a `DS.InvalidError`.
|
2866 | d0fe8c12 | Athina Bekakou | |
2867 | d0fe8c12 | Athina Bekakou | @method recordWasInvalid
|
2868 | d0fe8c12 | Athina Bekakou | @private
|
2869 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2870 | d0fe8c12 | Athina Bekakou | @param {Object} errors
|
2871 | d0fe8c12 | Athina Bekakou | */
|
2872 | d0fe8c12 | Athina Bekakou | recordWasInvalid: function(record, errors) { |
2873 | d0fe8c12 | Athina Bekakou | record.adapterDidInvalidate(errors); |
2874 | d0fe8c12 | Athina Bekakou | }, |
2875 | d0fe8c12 | Athina Bekakou | |
2876 | d0fe8c12 | Athina Bekakou | /**
|
2877 | d0fe8c12 | Athina Bekakou | This method is called once the promise returned by an
|
2878 | d0fe8c12 | Athina Bekakou | adapter's `createRecord`, `updateRecord` or `deleteRecord`
|
2879 | d0fe8c12 | Athina Bekakou | is rejected (with anything other than a `DS.InvalidError`).
|
2880 | d0fe8c12 | Athina Bekakou | |
2881 | d0fe8c12 | Athina Bekakou | @method recordWasError
|
2882 | d0fe8c12 | Athina Bekakou | @private
|
2883 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2884 | d0fe8c12 | Athina Bekakou | */
|
2885 | d0fe8c12 | Athina Bekakou | recordWasError: function(record) { |
2886 | d0fe8c12 | Athina Bekakou | record.adapterDidError(); |
2887 | d0fe8c12 | Athina Bekakou | }, |
2888 | d0fe8c12 | Athina Bekakou | |
2889 | d0fe8c12 | Athina Bekakou | /**
|
2890 | d0fe8c12 | Athina Bekakou | When an adapter's `createRecord`, `updateRecord` or `deleteRecord`
|
2891 | d0fe8c12 | Athina Bekakou | resolves with data, this method extracts the ID from the supplied
|
2892 | d0fe8c12 | Athina Bekakou | data.
|
2893 | d0fe8c12 | Athina Bekakou | |
2894 | d0fe8c12 | Athina Bekakou | @method updateId
|
2895 | d0fe8c12 | Athina Bekakou | @private
|
2896 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
2897 | d0fe8c12 | Athina Bekakou | @param {Object} data
|
2898 | d0fe8c12 | Athina Bekakou | */
|
2899 | d0fe8c12 | Athina Bekakou | updateId: function(record, data) { |
2900 | d0fe8c12 | Athina Bekakou | var oldId = get(record, 'id'), |
2901 | d0fe8c12 | Athina Bekakou | id = coerceId(data.id); |
2902 | d0fe8c12 | Athina Bekakou | |
2903 | d0fe8c12 | Athina Bekakou | Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); |
2904 | d0fe8c12 | Athina Bekakou | |
2905 | d0fe8c12 | Athina Bekakou | this.typeMapFor(record.constructor).idToRecord[id] = record;
|
2906 | d0fe8c12 | Athina Bekakou | |
2907 | d0fe8c12 | Athina Bekakou | set(record, 'id', id);
|
2908 | d0fe8c12 | Athina Bekakou | }, |
2909 | d0fe8c12 | Athina Bekakou | |
2910 | d0fe8c12 | Athina Bekakou | /**
|
2911 | d0fe8c12 | Athina Bekakou | Returns a map of IDs to client IDs for a given type.
|
2912 | d0fe8c12 | Athina Bekakou | |
2913 | d0fe8c12 | Athina Bekakou | @method typeMapFor
|
2914 | d0fe8c12 | Athina Bekakou | @private
|
2915 | d0fe8c12 | Athina Bekakou | @param type
|
2916 | d0fe8c12 | Athina Bekakou | @return {Object} typeMap
|
2917 | d0fe8c12 | Athina Bekakou | */
|
2918 | d0fe8c12 | Athina Bekakou | typeMapFor: function(type) { |
2919 | d0fe8c12 | Athina Bekakou | var typeMaps = get(this, 'typeMaps'), |
2920 | d0fe8c12 | Athina Bekakou | guid = Ember.guidFor(type), |
2921 | d0fe8c12 | Athina Bekakou | typeMap; |
2922 | d0fe8c12 | Athina Bekakou | |
2923 | d0fe8c12 | Athina Bekakou | typeMap = typeMaps[guid]; |
2924 | d0fe8c12 | Athina Bekakou | |
2925 | d0fe8c12 | Athina Bekakou | if (typeMap) { return typeMap; } |
2926 | d0fe8c12 | Athina Bekakou | |
2927 | d0fe8c12 | Athina Bekakou | typeMap = { |
2928 | d0fe8c12 | Athina Bekakou | idToRecord: {},
|
2929 | d0fe8c12 | Athina Bekakou | records: [],
|
2930 | d0fe8c12 | Athina Bekakou | metadata: {}
|
2931 | d0fe8c12 | Athina Bekakou | }; |
2932 | d0fe8c12 | Athina Bekakou | |
2933 | d0fe8c12 | Athina Bekakou | typeMaps[guid] = typeMap; |
2934 | d0fe8c12 | Athina Bekakou | |
2935 | d0fe8c12 | Athina Bekakou | return typeMap;
|
2936 | d0fe8c12 | Athina Bekakou | }, |
2937 | d0fe8c12 | Athina Bekakou | |
2938 | d0fe8c12 | Athina Bekakou | // ................
|
2939 | d0fe8c12 | Athina Bekakou | // . LOADING DATA .
|
2940 | d0fe8c12 | Athina Bekakou | // ................
|
2941 | d0fe8c12 | Athina Bekakou | |
2942 | d0fe8c12 | Athina Bekakou | /**
|
2943 | d0fe8c12 | Athina Bekakou | This internal method is used by `push`.
|
2944 | d0fe8c12 | Athina Bekakou | |
2945 | d0fe8c12 | Athina Bekakou | @method _load
|
2946 | d0fe8c12 | Athina Bekakou | @private
|
2947 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
2948 | d0fe8c12 | Athina Bekakou | @param {Object} data
|
2949 | d0fe8c12 | Athina Bekakou | @param {Boolean} partial the data should be merged into
|
2950 | d0fe8c12 | Athina Bekakou | the existing data, not replace it.
|
2951 | d0fe8c12 | Athina Bekakou | */
|
2952 | d0fe8c12 | Athina Bekakou | _load: function(type, data, partial) { |
2953 | d0fe8c12 | Athina Bekakou | var id = coerceId(data.id),
|
2954 | d0fe8c12 | Athina Bekakou | record = this.recordForId(type, id);
|
2955 | d0fe8c12 | Athina Bekakou | |
2956 | d0fe8c12 | Athina Bekakou | record.setupData(data, partial); |
2957 | d0fe8c12 | Athina Bekakou | this.recordArrayManager.recordDidChange(record);
|
2958 | d0fe8c12 | Athina Bekakou | |
2959 | d0fe8c12 | Athina Bekakou | return record;
|
2960 | d0fe8c12 | Athina Bekakou | }, |
2961 | d0fe8c12 | Athina Bekakou | |
2962 | d0fe8c12 | Athina Bekakou | /**
|
2963 | d0fe8c12 | Athina Bekakou | Returns a model class for a particular key. Used by
|
2964 | d0fe8c12 | Athina Bekakou | methods that take a type key (like `find`, `createRecord`,
|
2965 | d0fe8c12 | Athina Bekakou | etc.)
|
2966 | d0fe8c12 | Athina Bekakou | |
2967 | d0fe8c12 | Athina Bekakou | @method modelFor
|
2968 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} key
|
2969 | d0fe8c12 | Athina Bekakou | @returns {subclass of DS.Model}
|
2970 | d0fe8c12 | Athina Bekakou | */
|
2971 | d0fe8c12 | Athina Bekakou | modelFor: function(key) { |
2972 | d0fe8c12 | Athina Bekakou | var factory;
|
2973 | d0fe8c12 | Athina Bekakou | |
2974 | d0fe8c12 | Athina Bekakou | |
2975 | d0fe8c12 | Athina Bekakou | if (typeof key === 'string') { |
2976 | d0fe8c12 | Athina Bekakou | var normalizedKey = this.container.normalize('model:' + key); |
2977 | d0fe8c12 | Athina Bekakou | |
2978 | d0fe8c12 | Athina Bekakou | factory = this.container.lookupFactory(normalizedKey);
|
2979 | d0fe8c12 | Athina Bekakou | if (!factory) { throw new Ember.Error("No model was found for '" + key + "'"); } |
2980 | d0fe8c12 | Athina Bekakou | factory.typeKey = normalizedKey.split(':', 2)[1]; |
2981 | d0fe8c12 | Athina Bekakou | } else {
|
2982 | d0fe8c12 | Athina Bekakou | // A factory already supplied.
|
2983 | d0fe8c12 | Athina Bekakou | factory = key; |
2984 | d0fe8c12 | Athina Bekakou | } |
2985 | d0fe8c12 | Athina Bekakou | |
2986 | d0fe8c12 | Athina Bekakou | factory.store = this;
|
2987 | d0fe8c12 | Athina Bekakou | return factory;
|
2988 | d0fe8c12 | Athina Bekakou | }, |
2989 | d0fe8c12 | Athina Bekakou | |
2990 | d0fe8c12 | Athina Bekakou | /**
|
2991 | d0fe8c12 | Athina Bekakou | Push some data for a given type into the store.
|
2992 | d0fe8c12 | Athina Bekakou | |
2993 | d0fe8c12 | Athina Bekakou | This method expects normalized data:
|
2994 | d0fe8c12 | Athina Bekakou | |
2995 | d0fe8c12 | Athina Bekakou | * The ID is a key named `id` (an ID is mandatory)
|
2996 | d0fe8c12 | Athina Bekakou | * The names of attributes are the ones you used in
|
2997 | d0fe8c12 | Athina Bekakou | your model's `DS.attr`s.
|
2998 | d0fe8c12 | Athina Bekakou | * Your relationships must be:
|
2999 | d0fe8c12 | Athina Bekakou | * represented as IDs or Arrays of IDs
|
3000 | d0fe8c12 | Athina Bekakou | * represented as model instances
|
3001 | d0fe8c12 | Athina Bekakou | * represented as URLs, under the `links` key
|
3002 | d0fe8c12 | Athina Bekakou | |
3003 | d0fe8c12 | Athina Bekakou | For this model:
|
3004 | d0fe8c12 | Athina Bekakou | |
3005 | d0fe8c12 | Athina Bekakou | ```js
|
3006 | d0fe8c12 | Athina Bekakou | App.Person = DS.Model.extend({
|
3007 | d0fe8c12 | Athina Bekakou | firstName: DS.attr(),
|
3008 | d0fe8c12 | Athina Bekakou | lastName: DS.attr(),
|
3009 | d0fe8c12 | Athina Bekakou | |
3010 | d0fe8c12 | Athina Bekakou | children: DS.hasMany('person')
|
3011 | d0fe8c12 | Athina Bekakou | });
|
3012 | d0fe8c12 | Athina Bekakou | ```
|
3013 | d0fe8c12 | Athina Bekakou | |
3014 | d0fe8c12 | Athina Bekakou | To represent the children as IDs:
|
3015 | d0fe8c12 | Athina Bekakou | |
3016 | d0fe8c12 | Athina Bekakou | ```js
|
3017 | d0fe8c12 | Athina Bekakou | {
|
3018 | d0fe8c12 | Athina Bekakou | id: 1,
|
3019 | d0fe8c12 | Athina Bekakou | firstName: "Tom",
|
3020 | d0fe8c12 | Athina Bekakou | lastName: "Dale",
|
3021 | d0fe8c12 | Athina Bekakou | children: [1, 2, 3]
|
3022 | d0fe8c12 | Athina Bekakou | }
|
3023 | d0fe8c12 | Athina Bekakou | ```
|
3024 | d0fe8c12 | Athina Bekakou | |
3025 | d0fe8c12 | Athina Bekakou | To represent the children relationship as a URL:
|
3026 | d0fe8c12 | Athina Bekakou | |
3027 | d0fe8c12 | Athina Bekakou | ```js
|
3028 | d0fe8c12 | Athina Bekakou | {
|
3029 | d0fe8c12 | Athina Bekakou | id: 1,
|
3030 | d0fe8c12 | Athina Bekakou | firstName: "Tom",
|
3031 | d0fe8c12 | Athina Bekakou | lastName: "Dale",
|
3032 | d0fe8c12 | Athina Bekakou | links: {
|
3033 | d0fe8c12 | Athina Bekakou | children: "/people/1/children"
|
3034 | d0fe8c12 | Athina Bekakou | }
|
3035 | d0fe8c12 | Athina Bekakou | }
|
3036 | d0fe8c12 | Athina Bekakou | ```
|
3037 | d0fe8c12 | Athina Bekakou | |
3038 | d0fe8c12 | Athina Bekakou | If you're streaming data or implementing an adapter,
|
3039 | d0fe8c12 | Athina Bekakou | make sure that you have converted the incoming data
|
3040 | d0fe8c12 | Athina Bekakou | into this form.
|
3041 | d0fe8c12 | Athina Bekakou | |
3042 | d0fe8c12 | Athina Bekakou | This method can be used both to push in brand new
|
3043 | d0fe8c12 | Athina Bekakou | records, as well as to update existing records.
|
3044 | d0fe8c12 | Athina Bekakou | |
3045 | d0fe8c12 | Athina Bekakou | @method push
|
3046 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
3047 | d0fe8c12 | Athina Bekakou | @param {Object} data
|
3048 | d0fe8c12 | Athina Bekakou | @returns {DS.Model} the record that was created or
|
3049 | d0fe8c12 | Athina Bekakou | updated.
|
3050 | d0fe8c12 | Athina Bekakou | */
|
3051 | d0fe8c12 | Athina Bekakou | push: function(type, data, _partial) { |
3052 | d0fe8c12 | Athina Bekakou | // _partial is an internal param used by `update`.
|
3053 | d0fe8c12 | Athina Bekakou | // If passed, it means that the data should be
|
3054 | d0fe8c12 | Athina Bekakou | // merged into the existing data, not replace it.
|
3055 | d0fe8c12 | Athina Bekakou | |
3056 | d0fe8c12 | Athina Bekakou | Ember.assert("You must include an `id` in a hash passed to `push`", data.id != null); |
3057 | d0fe8c12 | Athina Bekakou | |
3058 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
3059 | d0fe8c12 | Athina Bekakou | |
3060 | d0fe8c12 | Athina Bekakou | // normalize relationship IDs into records
|
3061 | d0fe8c12 | Athina Bekakou | data = normalizeRelationships(this, type, data);
|
3062 | d0fe8c12 | Athina Bekakou | |
3063 | d0fe8c12 | Athina Bekakou | this._load(type, data, _partial);
|
3064 | d0fe8c12 | Athina Bekakou | |
3065 | d0fe8c12 | Athina Bekakou | return this.recordForId(type, data.id); |
3066 | d0fe8c12 | Athina Bekakou | }, |
3067 | d0fe8c12 | Athina Bekakou | |
3068 | d0fe8c12 | Athina Bekakou | /**
|
3069 | d0fe8c12 | Athina Bekakou | Push some raw data into the store.
|
3070 | d0fe8c12 | Athina Bekakou | |
3071 | d0fe8c12 | Athina Bekakou | The data will be automatically deserialized using the
|
3072 | d0fe8c12 | Athina Bekakou | serializer for the `type` param.
|
3073 | d0fe8c12 | Athina Bekakou | |
3074 | d0fe8c12 | Athina Bekakou | This method can be used both to push in brand new
|
3075 | d0fe8c12 | Athina Bekakou | records, as well as to update existing records.
|
3076 | d0fe8c12 | Athina Bekakou | |
3077 | d0fe8c12 | Athina Bekakou | You can push in more than one type of object at once.
|
3078 | d0fe8c12 | Athina Bekakou | All objects should be in the format expected by the
|
3079 | d0fe8c12 | Athina Bekakou | serializer.
|
3080 | d0fe8c12 | Athina Bekakou | |
3081 | d0fe8c12 | Athina Bekakou | ```js
|
3082 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.ActiveModelSerializer;
|
3083 | d0fe8c12 | Athina Bekakou | |
3084 | d0fe8c12 | Athina Bekakou | var pushData = {
|
3085 | d0fe8c12 | Athina Bekakou | posts: [
|
3086 | d0fe8c12 | Athina Bekakou | {id: 1, post_title: "Great post", comment_ids: [2]}
|
3087 | d0fe8c12 | Athina Bekakou | ],
|
3088 | d0fe8c12 | Athina Bekakou | comments: [
|
3089 | d0fe8c12 | Athina Bekakou | {id: 2, comment_body: "Insightful comment"}
|
3090 | d0fe8c12 | Athina Bekakou | ]
|
3091 | d0fe8c12 | Athina Bekakou | }
|
3092 | d0fe8c12 | Athina Bekakou | |
3093 | d0fe8c12 | Athina Bekakou | store.pushPayload('post', pushData);
|
3094 | d0fe8c12 | Athina Bekakou | ```
|
3095 | d0fe8c12 | Athina Bekakou | |
3096 | d0fe8c12 | Athina Bekakou | @method pushPayload
|
3097 | d0fe8c12 | Athina Bekakou | @param {String} type
|
3098 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
3099 | d0fe8c12 | Athina Bekakou | @return {DS.Model} the record that was created or updated.
|
3100 | d0fe8c12 | Athina Bekakou | */
|
3101 | d0fe8c12 | Athina Bekakou | pushPayload: function (type, payload) { |
3102 | d0fe8c12 | Athina Bekakou | var serializer;
|
3103 | d0fe8c12 | Athina Bekakou | if (!payload) {
|
3104 | d0fe8c12 | Athina Bekakou | payload = type; |
3105 | d0fe8c12 | Athina Bekakou | serializer = defaultSerializer(this.container);
|
3106 | d0fe8c12 | Athina Bekakou | Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", serializer.pushPayload);
|
3107 | d0fe8c12 | Athina Bekakou | } else {
|
3108 | d0fe8c12 | Athina Bekakou | serializer = this.serializerFor(type);
|
3109 | d0fe8c12 | Athina Bekakou | } |
3110 | d0fe8c12 | Athina Bekakou | serializer.pushPayload(this, payload);
|
3111 | d0fe8c12 | Athina Bekakou | }, |
3112 | d0fe8c12 | Athina Bekakou | |
3113 | d0fe8c12 | Athina Bekakou | update: function(type, data) { |
3114 | d0fe8c12 | Athina Bekakou | Ember.assert("You must include an `id` in a hash passed to `update`", data.id != null); |
3115 | d0fe8c12 | Athina Bekakou | |
3116 | d0fe8c12 | Athina Bekakou | return this.push(type, data, true); |
3117 | d0fe8c12 | Athina Bekakou | }, |
3118 | d0fe8c12 | Athina Bekakou | |
3119 | d0fe8c12 | Athina Bekakou | /**
|
3120 | d0fe8c12 | Athina Bekakou | If you have an Array of normalized data to push,
|
3121 | d0fe8c12 | Athina Bekakou | you can call `pushMany` with the Array, and it will
|
3122 | d0fe8c12 | Athina Bekakou | call `push` repeatedly for you.
|
3123 | d0fe8c12 | Athina Bekakou | |
3124 | d0fe8c12 | Athina Bekakou | @method pushMany
|
3125 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
3126 | d0fe8c12 | Athina Bekakou | @param {Array} datas
|
3127 | d0fe8c12 | Athina Bekakou | @return {Array}
|
3128 | d0fe8c12 | Athina Bekakou | */
|
3129 | d0fe8c12 | Athina Bekakou | pushMany: function(type, datas) { |
3130 | d0fe8c12 | Athina Bekakou | return map(datas, function(data) { |
3131 | d0fe8c12 | Athina Bekakou | return this.push(type, data); |
3132 | d0fe8c12 | Athina Bekakou | }, this);
|
3133 | d0fe8c12 | Athina Bekakou | }, |
3134 | d0fe8c12 | Athina Bekakou | |
3135 | d0fe8c12 | Athina Bekakou | /**
|
3136 | d0fe8c12 | Athina Bekakou | If you have some metadata to set for a type
|
3137 | d0fe8c12 | Athina Bekakou | you can call `metaForType`.
|
3138 | d0fe8c12 | Athina Bekakou | |
3139 | d0fe8c12 | Athina Bekakou | @method metaForType
|
3140 | d0fe8c12 | Athina Bekakou | @param {String or subclass of DS.Model} type
|
3141 | d0fe8c12 | Athina Bekakou | @param {Object} metadata
|
3142 | d0fe8c12 | Athina Bekakou | */
|
3143 | d0fe8c12 | Athina Bekakou | metaForType: function(type, metadata) { |
3144 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
3145 | d0fe8c12 | Athina Bekakou | |
3146 | d0fe8c12 | Athina Bekakou | Ember.merge(this.typeMapFor(type).metadata, metadata);
|
3147 | d0fe8c12 | Athina Bekakou | }, |
3148 | d0fe8c12 | Athina Bekakou | |
3149 | d0fe8c12 | Athina Bekakou | /**
|
3150 | d0fe8c12 | Athina Bekakou | Build a brand new record for a given type, ID, and
|
3151 | d0fe8c12 | Athina Bekakou | initial data.
|
3152 | d0fe8c12 | Athina Bekakou | |
3153 | d0fe8c12 | Athina Bekakou | @method buildRecord
|
3154 | d0fe8c12 | Athina Bekakou | @private
|
3155 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
3156 | d0fe8c12 | Athina Bekakou | @param {String} id
|
3157 | d0fe8c12 | Athina Bekakou | @param {Object} data
|
3158 | d0fe8c12 | Athina Bekakou | @returns {DS.Model} record
|
3159 | d0fe8c12 | Athina Bekakou | */
|
3160 | d0fe8c12 | Athina Bekakou | buildRecord: function(type, id, data) { |
3161 | d0fe8c12 | Athina Bekakou | var typeMap = this.typeMapFor(type), |
3162 | d0fe8c12 | Athina Bekakou | idToRecord = typeMap.idToRecord; |
3163 | d0fe8c12 | Athina Bekakou | |
3164 | d0fe8c12 | Athina Bekakou | Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord[id]); |
3165 | d0fe8c12 | Athina Bekakou | |
3166 | d0fe8c12 | Athina Bekakou | // lookupFactory should really return an object that creates
|
3167 | d0fe8c12 | Athina Bekakou | // instances with the injections applied
|
3168 | d0fe8c12 | Athina Bekakou | var record = type._create({
|
3169 | d0fe8c12 | Athina Bekakou | id: id,
|
3170 | d0fe8c12 | Athina Bekakou | store: this, |
3171 | d0fe8c12 | Athina Bekakou | container: this.container |
3172 | d0fe8c12 | Athina Bekakou | }); |
3173 | d0fe8c12 | Athina Bekakou | |
3174 | d0fe8c12 | Athina Bekakou | if (data) {
|
3175 | d0fe8c12 | Athina Bekakou | record.setupData(data); |
3176 | d0fe8c12 | Athina Bekakou | } |
3177 | d0fe8c12 | Athina Bekakou | |
3178 | d0fe8c12 | Athina Bekakou | // if we're creating an item, this process will be done
|
3179 | d0fe8c12 | Athina Bekakou | // later, once the object has been persisted.
|
3180 | d0fe8c12 | Athina Bekakou | if (id) {
|
3181 | d0fe8c12 | Athina Bekakou | idToRecord[id] = record; |
3182 | d0fe8c12 | Athina Bekakou | } |
3183 | d0fe8c12 | Athina Bekakou | |
3184 | d0fe8c12 | Athina Bekakou | typeMap.records.push(record); |
3185 | d0fe8c12 | Athina Bekakou | |
3186 | d0fe8c12 | Athina Bekakou | return record;
|
3187 | d0fe8c12 | Athina Bekakou | }, |
3188 | d0fe8c12 | Athina Bekakou | |
3189 | d0fe8c12 | Athina Bekakou | // ...............
|
3190 | d0fe8c12 | Athina Bekakou | // . DESTRUCTION .
|
3191 | d0fe8c12 | Athina Bekakou | // ...............
|
3192 | d0fe8c12 | Athina Bekakou | |
3193 | d0fe8c12 | Athina Bekakou | /**
|
3194 | d0fe8c12 | Athina Bekakou | When a record is destroyed, this un-indexes it and
|
3195 | d0fe8c12 | Athina Bekakou | removes it from any record arrays so it can be GCed.
|
3196 | d0fe8c12 | Athina Bekakou | |
3197 | d0fe8c12 | Athina Bekakou | @method dematerializeRecord
|
3198 | d0fe8c12 | Athina Bekakou | @private
|
3199 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
3200 | d0fe8c12 | Athina Bekakou | */
|
3201 | d0fe8c12 | Athina Bekakou | dematerializeRecord: function(record) { |
3202 | d0fe8c12 | Athina Bekakou | var type = record.constructor,
|
3203 | d0fe8c12 | Athina Bekakou | typeMap = this.typeMapFor(type),
|
3204 | d0fe8c12 | Athina Bekakou | id = get(record, 'id');
|
3205 | d0fe8c12 | Athina Bekakou | |
3206 | d0fe8c12 | Athina Bekakou | record.updateRecordArrays(); |
3207 | d0fe8c12 | Athina Bekakou | |
3208 | d0fe8c12 | Athina Bekakou | if (id) {
|
3209 | d0fe8c12 | Athina Bekakou | delete typeMap.idToRecord[id];
|
3210 | d0fe8c12 | Athina Bekakou | } |
3211 | d0fe8c12 | Athina Bekakou | |
3212 | d0fe8c12 | Athina Bekakou | var loc = indexOf(typeMap.records, record);
|
3213 | d0fe8c12 | Athina Bekakou | typeMap.records.splice(loc, 1);
|
3214 | d0fe8c12 | Athina Bekakou | }, |
3215 | d0fe8c12 | Athina Bekakou | |
3216 | d0fe8c12 | Athina Bekakou | // ........................
|
3217 | d0fe8c12 | Athina Bekakou | // . RELATIONSHIP CHANGES .
|
3218 | d0fe8c12 | Athina Bekakou | // ........................
|
3219 | d0fe8c12 | Athina Bekakou | |
3220 | d0fe8c12 | Athina Bekakou | addRelationshipChangeFor: function(childRecord, childKey, parentRecord, parentKey, change) { |
3221 | d0fe8c12 | Athina Bekakou | var clientId = childRecord.clientId,
|
3222 | d0fe8c12 | Athina Bekakou | parentClientId = parentRecord ? parentRecord : parentRecord; |
3223 | d0fe8c12 | Athina Bekakou | var key = childKey + parentKey;
|
3224 | d0fe8c12 | Athina Bekakou | var changes = this._relationshipChanges; |
3225 | d0fe8c12 | Athina Bekakou | if (!(clientId in changes)) { |
3226 | d0fe8c12 | Athina Bekakou | changes[clientId] = {}; |
3227 | d0fe8c12 | Athina Bekakou | } |
3228 | d0fe8c12 | Athina Bekakou | if (!(parentClientId in changes[clientId])) { |
3229 | d0fe8c12 | Athina Bekakou | changes[clientId][parentClientId] = {}; |
3230 | d0fe8c12 | Athina Bekakou | } |
3231 | d0fe8c12 | Athina Bekakou | if (!(key in changes[clientId][parentClientId])) { |
3232 | d0fe8c12 | Athina Bekakou | changes[clientId][parentClientId][key] = {}; |
3233 | d0fe8c12 | Athina Bekakou | } |
3234 | d0fe8c12 | Athina Bekakou | changes[clientId][parentClientId][key][change.changeType] = change; |
3235 | d0fe8c12 | Athina Bekakou | }, |
3236 | d0fe8c12 | Athina Bekakou | |
3237 | d0fe8c12 | Athina Bekakou | removeRelationshipChangeFor: function(clientRecord, childKey, parentRecord, parentKey, type) { |
3238 | d0fe8c12 | Athina Bekakou | var clientId = clientRecord.clientId,
|
3239 | d0fe8c12 | Athina Bekakou | parentClientId = parentRecord ? parentRecord.clientId : parentRecord; |
3240 | d0fe8c12 | Athina Bekakou | var changes = this._relationshipChanges; |
3241 | d0fe8c12 | Athina Bekakou | var key = childKey + parentKey;
|
3242 | d0fe8c12 | Athina Bekakou | if (!(clientId in changes) || !(parentClientId in changes[clientId]) || !(key in changes[clientId][parentClientId])){ |
3243 | d0fe8c12 | Athina Bekakou | return;
|
3244 | d0fe8c12 | Athina Bekakou | } |
3245 | d0fe8c12 | Athina Bekakou | delete changes[clientId][parentClientId][key][type];
|
3246 | d0fe8c12 | Athina Bekakou | }, |
3247 | d0fe8c12 | Athina Bekakou | |
3248 | d0fe8c12 | Athina Bekakou | relationshipChangePairsFor: function(record){ |
3249 | d0fe8c12 | Athina Bekakou | var toReturn = [];
|
3250 | d0fe8c12 | Athina Bekakou | |
3251 | d0fe8c12 | Athina Bekakou | if( !record ) { return toReturn; } |
3252 | d0fe8c12 | Athina Bekakou | |
3253 | d0fe8c12 | Athina Bekakou | //TODO(Igor) What about the other side
|
3254 | d0fe8c12 | Athina Bekakou | var changesObject = this._relationshipChanges[record.clientId]; |
3255 | d0fe8c12 | Athina Bekakou | for (var objKey in changesObject){ |
3256 | d0fe8c12 | Athina Bekakou | if(changesObject.hasOwnProperty(objKey)){
|
3257 | d0fe8c12 | Athina Bekakou | for (var changeKey in changesObject[objKey]){ |
3258 | d0fe8c12 | Athina Bekakou | if(changesObject[objKey].hasOwnProperty(changeKey)){
|
3259 | d0fe8c12 | Athina Bekakou | toReturn.push(changesObject[objKey][changeKey]); |
3260 | d0fe8c12 | Athina Bekakou | } |
3261 | d0fe8c12 | Athina Bekakou | } |
3262 | d0fe8c12 | Athina Bekakou | } |
3263 | d0fe8c12 | Athina Bekakou | } |
3264 | d0fe8c12 | Athina Bekakou | return toReturn;
|
3265 | d0fe8c12 | Athina Bekakou | }, |
3266 | d0fe8c12 | Athina Bekakou | |
3267 | d0fe8c12 | Athina Bekakou | // ......................
|
3268 | d0fe8c12 | Athina Bekakou | // . PER-TYPE ADAPTERS
|
3269 | d0fe8c12 | Athina Bekakou | // ......................
|
3270 | d0fe8c12 | Athina Bekakou | |
3271 | d0fe8c12 | Athina Bekakou | /**
|
3272 | d0fe8c12 | Athina Bekakou | Returns the adapter for a given type.
|
3273 | d0fe8c12 | Athina Bekakou | |
3274 | d0fe8c12 | Athina Bekakou | @method adapterFor
|
3275 | d0fe8c12 | Athina Bekakou | @private
|
3276 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
3277 | d0fe8c12 | Athina Bekakou | @returns DS.Adapter
|
3278 | d0fe8c12 | Athina Bekakou | */
|
3279 | d0fe8c12 | Athina Bekakou | adapterFor: function(type) { |
3280 | d0fe8c12 | Athina Bekakou | var container = this.container, adapter; |
3281 | d0fe8c12 | Athina Bekakou | |
3282 | d0fe8c12 | Athina Bekakou | if (container) {
|
3283 | d0fe8c12 | Athina Bekakou | adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application'); |
3284 | d0fe8c12 | Athina Bekakou | } |
3285 | d0fe8c12 | Athina Bekakou | |
3286 | d0fe8c12 | Athina Bekakou | return adapter || get(this, 'defaultAdapter'); |
3287 | d0fe8c12 | Athina Bekakou | }, |
3288 | d0fe8c12 | Athina Bekakou | |
3289 | d0fe8c12 | Athina Bekakou | // ..............................
|
3290 | d0fe8c12 | Athina Bekakou | // . RECORD CHANGE NOTIFICATION .
|
3291 | d0fe8c12 | Athina Bekakou | // ..............................
|
3292 | d0fe8c12 | Athina Bekakou | |
3293 | d0fe8c12 | Athina Bekakou | /**
|
3294 | d0fe8c12 | Athina Bekakou | Returns an instance of the serializer for a given type. For
|
3295 | d0fe8c12 | Athina Bekakou | example, `serializerFor('person')` will return an instance of
|
3296 | d0fe8c12 | Athina Bekakou | `App.PersonSerializer`.
|
3297 | d0fe8c12 | Athina Bekakou | |
3298 | d0fe8c12 | Athina Bekakou | If no `App.PersonSerializer` is found, this method will look
|
3299 | d0fe8c12 | Athina Bekakou | for an `App.ApplicationSerializer` (the default serializer for
|
3300 | d0fe8c12 | Athina Bekakou | your entire application).
|
3301 | d0fe8c12 | Athina Bekakou | |
3302 | d0fe8c12 | Athina Bekakou | If no `App.ApplicationSerializer` is found, it will fall back
|
3303 | d0fe8c12 | Athina Bekakou | to an instance of `DS.JSONSerializer`.
|
3304 | d0fe8c12 | Athina Bekakou | |
3305 | d0fe8c12 | Athina Bekakou | @method serializerFor
|
3306 | d0fe8c12 | Athina Bekakou | @private
|
3307 | d0fe8c12 | Athina Bekakou | @param {String} type the record to serialize
|
3308 | d0fe8c12 | Athina Bekakou | @return {DS.Serializer}
|
3309 | d0fe8c12 | Athina Bekakou | */
|
3310 | d0fe8c12 | Athina Bekakou | serializerFor: function(type) { |
3311 | d0fe8c12 | Athina Bekakou | type = this.modelFor(type);
|
3312 | d0fe8c12 | Athina Bekakou | var adapter = this.adapterFor(type); |
3313 | d0fe8c12 | Athina Bekakou | |
3314 | d0fe8c12 | Athina Bekakou | return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer); |
3315 | d0fe8c12 | Athina Bekakou | } |
3316 | d0fe8c12 | Athina Bekakou | }); |
3317 | d0fe8c12 | Athina Bekakou | |
3318 | d0fe8c12 | Athina Bekakou | function normalizeRelationships(store, type, data, record) { |
3319 | d0fe8c12 | Athina Bekakou | type.eachRelationship(function(key, relationship) {
|
3320 | d0fe8c12 | Athina Bekakou | // A link (usually a URL) was already provided in
|
3321 | d0fe8c12 | Athina Bekakou | // normalized form
|
3322 | d0fe8c12 | Athina Bekakou | if (data.links && data.links[key]) {
|
3323 | d0fe8c12 | Athina Bekakou | if (record && relationship.options.async) { record._relationships[key] = null; } |
3324 | d0fe8c12 | Athina Bekakou | return;
|
3325 | d0fe8c12 | Athina Bekakou | } |
3326 | d0fe8c12 | Athina Bekakou | |
3327 | d0fe8c12 | Athina Bekakou | var kind = relationship.kind,
|
3328 | d0fe8c12 | Athina Bekakou | value = data[key]; |
3329 | d0fe8c12 | Athina Bekakou | |
3330 | d0fe8c12 | Athina Bekakou | if (value == null) { return; } |
3331 | d0fe8c12 | Athina Bekakou | |
3332 | d0fe8c12 | Athina Bekakou | if (kind === 'belongsTo') { |
3333 | d0fe8c12 | Athina Bekakou | deserializeRecordId(store, data, key, relationship, value); |
3334 | d0fe8c12 | Athina Bekakou | } else if (kind === 'hasMany') { |
3335 | d0fe8c12 | Athina Bekakou | deserializeRecordIds(store, data, key, relationship, value); |
3336 | d0fe8c12 | Athina Bekakou | addUnsavedRecords(record, key, value); |
3337 | d0fe8c12 | Athina Bekakou | } |
3338 | d0fe8c12 | Athina Bekakou | }); |
3339 | d0fe8c12 | Athina Bekakou | |
3340 | d0fe8c12 | Athina Bekakou | return data;
|
3341 | d0fe8c12 | Athina Bekakou | } |
3342 | d0fe8c12 | Athina Bekakou | |
3343 | d0fe8c12 | Athina Bekakou | function deserializeRecordId(store, data, key, relationship, id) { |
3344 | d0fe8c12 | Athina Bekakou | if (isNone(id) || id instanceof DS.Model) { |
3345 | d0fe8c12 | Athina Bekakou | return;
|
3346 | d0fe8c12 | Athina Bekakou | } |
3347 | d0fe8c12 | Athina Bekakou | |
3348 | d0fe8c12 | Athina Bekakou | var type;
|
3349 | d0fe8c12 | Athina Bekakou | |
3350 | d0fe8c12 | Athina Bekakou | if (typeof id === 'number' || typeof id === 'string') { |
3351 | d0fe8c12 | Athina Bekakou | type = typeFor(relationship, key, data); |
3352 | d0fe8c12 | Athina Bekakou | data[key] = store.recordForId(type, id); |
3353 | d0fe8c12 | Athina Bekakou | } else if (typeof id === 'object') { |
3354 | d0fe8c12 | Athina Bekakou | // polymorphic
|
3355 | d0fe8c12 | Athina Bekakou | data[key] = store.recordForId(id.type, id.id); |
3356 | d0fe8c12 | Athina Bekakou | } |
3357 | d0fe8c12 | Athina Bekakou | } |
3358 | d0fe8c12 | Athina Bekakou | |
3359 | d0fe8c12 | Athina Bekakou | function typeFor(relationship, key, data) { |
3360 | d0fe8c12 | Athina Bekakou | if (relationship.options.polymorphic) {
|
3361 | d0fe8c12 | Athina Bekakou | return data[key + "Type"]; |
3362 | d0fe8c12 | Athina Bekakou | } else {
|
3363 | d0fe8c12 | Athina Bekakou | return relationship.type;
|
3364 | d0fe8c12 | Athina Bekakou | } |
3365 | d0fe8c12 | Athina Bekakou | } |
3366 | d0fe8c12 | Athina Bekakou | |
3367 | d0fe8c12 | Athina Bekakou | function deserializeRecordIds(store, data, key, relationship, ids) { |
3368 | d0fe8c12 | Athina Bekakou | for (var i=0, l=ids.length; i<l; i++) { |
3369 | d0fe8c12 | Athina Bekakou | deserializeRecordId(store, ids, i, relationship, ids[i]); |
3370 | d0fe8c12 | Athina Bekakou | } |
3371 | d0fe8c12 | Athina Bekakou | } |
3372 | d0fe8c12 | Athina Bekakou | |
3373 | d0fe8c12 | Athina Bekakou | // If there are any unsaved records that are in a hasMany they won't be
|
3374 | d0fe8c12 | Athina Bekakou | // in the payload, so add them back in manually.
|
3375 | d0fe8c12 | Athina Bekakou | function addUnsavedRecords(record, key, data) { |
3376 | d0fe8c12 | Athina Bekakou | if(record) {
|
3377 | d0fe8c12 | Athina Bekakou | data.pushObjects(record.get(key).filterBy('isNew'));
|
3378 | d0fe8c12 | Athina Bekakou | } |
3379 | d0fe8c12 | Athina Bekakou | } |
3380 | d0fe8c12 | Athina Bekakou | |
3381 | d0fe8c12 | Athina Bekakou | // Delegation to the adapter and promise management
|
3382 | d0fe8c12 | Athina Bekakou | /**
|
3383 | d0fe8c12 | Athina Bekakou | A `PromiseArray` is an object that acts like both an `Ember.Array`
|
3384 | d0fe8c12 | Athina Bekakou | and a promise. When the promise is resolved the the resulting value
|
3385 | d0fe8c12 | Athina Bekakou | will be set to the `PromiseArray`'s `content` property. This makes
|
3386 | d0fe8c12 | Athina Bekakou | it easy to create data bindings with the `PromiseArray` that will be
|
3387 | d0fe8c12 | Athina Bekakou | updated when the promise resolves.
|
3388 | d0fe8c12 | Athina Bekakou | |
3389 | d0fe8c12 | Athina Bekakou | For more information see the [Ember.PromiseProxyMixin
|
3390 | d0fe8c12 | Athina Bekakou | documentation](/api/classes/Ember.PromiseProxyMixin.html).
|
3391 | d0fe8c12 | Athina Bekakou | |
3392 | d0fe8c12 | Athina Bekakou | Example
|
3393 | d0fe8c12 | Athina Bekakou | |
3394 | d0fe8c12 | Athina Bekakou | ```javascript
|
3395 | d0fe8c12 | Athina Bekakou | var promiseArray = DS.PromiseArray.create({
|
3396 | d0fe8c12 | Athina Bekakou | promise: $.getJSON('/some/remote/data.json')
|
3397 | d0fe8c12 | Athina Bekakou | });
|
3398 | d0fe8c12 | Athina Bekakou | |
3399 | d0fe8c12 | Athina Bekakou | promiseArray.get('length'); // 0
|
3400 | d0fe8c12 | Athina Bekakou | |
3401 | d0fe8c12 | Athina Bekakou | promiseArray.then(function() {
|
3402 | d0fe8c12 | Athina Bekakou | promiseArray.get('length'); // 100
|
3403 | d0fe8c12 | Athina Bekakou | });
|
3404 | d0fe8c12 | Athina Bekakou | ```
|
3405 | d0fe8c12 | Athina Bekakou | |
3406 | d0fe8c12 | Athina Bekakou | @class PromiseArray
|
3407 | d0fe8c12 | Athina Bekakou | @namespace DS
|
3408 | d0fe8c12 | Athina Bekakou | @extends Ember.ArrayProxy
|
3409 | d0fe8c12 | Athina Bekakou | @uses Ember.PromiseProxyMixin
|
3410 | d0fe8c12 | Athina Bekakou | */
|
3411 | d0fe8c12 | Athina Bekakou | DS.PromiseArray = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin); |
3412 | d0fe8c12 | Athina Bekakou | /**
|
3413 | d0fe8c12 | Athina Bekakou | A `PromiseObject` is an object that acts like both an `Ember.Object`
|
3414 | d0fe8c12 | Athina Bekakou | and a promise. When the promise is resolved the the resulting value
|
3415 | d0fe8c12 | Athina Bekakou | will be set to the `PromiseObject`'s `content` property. This makes
|
3416 | d0fe8c12 | Athina Bekakou | it easy to create data bindings with the `PromiseObject` that will
|
3417 | d0fe8c12 | Athina Bekakou | be updated when the promise resolves.
|
3418 | d0fe8c12 | Athina Bekakou | |
3419 | d0fe8c12 | Athina Bekakou | For more information see the [Ember.PromiseProxyMixin
|
3420 | d0fe8c12 | Athina Bekakou | documentation](/api/classes/Ember.PromiseProxyMixin.html).
|
3421 | d0fe8c12 | Athina Bekakou | |
3422 | d0fe8c12 | Athina Bekakou | Example
|
3423 | d0fe8c12 | Athina Bekakou | |
3424 | d0fe8c12 | Athina Bekakou | ```javascript
|
3425 | d0fe8c12 | Athina Bekakou | var promiseObject = DS.PromiseObject.create({
|
3426 | d0fe8c12 | Athina Bekakou | promise: $.getJSON('/some/remote/data.json')
|
3427 | d0fe8c12 | Athina Bekakou | });
|
3428 | d0fe8c12 | Athina Bekakou | |
3429 | d0fe8c12 | Athina Bekakou | promiseObject.get('name'); // null
|
3430 | d0fe8c12 | Athina Bekakou | |
3431 | d0fe8c12 | Athina Bekakou | promiseObject.then(function() {
|
3432 | d0fe8c12 | Athina Bekakou | promiseObject.get('name'); // 'Tomster'
|
3433 | d0fe8c12 | Athina Bekakou | });
|
3434 | d0fe8c12 | Athina Bekakou | ```
|
3435 | d0fe8c12 | Athina Bekakou | |
3436 | d0fe8c12 | Athina Bekakou | @class PromiseObject
|
3437 | d0fe8c12 | Athina Bekakou | @namespace DS
|
3438 | d0fe8c12 | Athina Bekakou | @extends Ember.ObjectProxy
|
3439 | d0fe8c12 | Athina Bekakou | @uses Ember.PromiseProxyMixin
|
3440 | d0fe8c12 | Athina Bekakou | */
|
3441 | d0fe8c12 | Athina Bekakou | DS.PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin); |
3442 | d0fe8c12 | Athina Bekakou | |
3443 | d0fe8c12 | Athina Bekakou | function promiseObject(promise) { |
3444 | d0fe8c12 | Athina Bekakou | return DS.PromiseObject.create({ promise: promise }); |
3445 | d0fe8c12 | Athina Bekakou | } |
3446 | d0fe8c12 | Athina Bekakou | |
3447 | d0fe8c12 | Athina Bekakou | function promiseArray(promise) { |
3448 | d0fe8c12 | Athina Bekakou | return DS.PromiseArray.create({ promise: promise }); |
3449 | d0fe8c12 | Athina Bekakou | } |
3450 | d0fe8c12 | Athina Bekakou | |
3451 | d0fe8c12 | Athina Bekakou | function isThenable(object) { |
3452 | d0fe8c12 | Athina Bekakou | return object && typeof object.then === 'function'; |
3453 | d0fe8c12 | Athina Bekakou | } |
3454 | d0fe8c12 | Athina Bekakou | |
3455 | d0fe8c12 | Athina Bekakou | function serializerFor(container, type, defaultSerializer) { |
3456 | d0fe8c12 | Athina Bekakou | return container.lookup('serializer:'+type) || |
3457 | d0fe8c12 | Athina Bekakou | container.lookup('serializer:application') ||
|
3458 | d0fe8c12 | Athina Bekakou | container.lookup('serializer:' + defaultSerializer) ||
|
3459 | d0fe8c12 | Athina Bekakou | container.lookup('serializer:_default');
|
3460 | d0fe8c12 | Athina Bekakou | } |
3461 | d0fe8c12 | Athina Bekakou | |
3462 | d0fe8c12 | Athina Bekakou | function defaultSerializer(container) { |
3463 | d0fe8c12 | Athina Bekakou | return container.lookup('serializer:application') || |
3464 | d0fe8c12 | Athina Bekakou | container.lookup('serializer:_default');
|
3465 | d0fe8c12 | Athina Bekakou | } |
3466 | d0fe8c12 | Athina Bekakou | |
3467 | d0fe8c12 | Athina Bekakou | function serializerForAdapter(adapter, type) { |
3468 | d0fe8c12 | Athina Bekakou | var serializer = adapter.serializer,
|
3469 | d0fe8c12 | Athina Bekakou | defaultSerializer = adapter.defaultSerializer, |
3470 | d0fe8c12 | Athina Bekakou | container = adapter.container; |
3471 | d0fe8c12 | Athina Bekakou | |
3472 | d0fe8c12 | Athina Bekakou | if (container && serializer === undefined) { |
3473 | d0fe8c12 | Athina Bekakou | serializer = serializerFor(container, type.typeKey, defaultSerializer); |
3474 | d0fe8c12 | Athina Bekakou | } |
3475 | d0fe8c12 | Athina Bekakou | |
3476 | d0fe8c12 | Athina Bekakou | if (serializer === null || serializer === undefined) { |
3477 | d0fe8c12 | Athina Bekakou | serializer = { |
3478 | d0fe8c12 | Athina Bekakou | extract: function(store, type, payload) { return payload; } |
3479 | d0fe8c12 | Athina Bekakou | }; |
3480 | d0fe8c12 | Athina Bekakou | } |
3481 | d0fe8c12 | Athina Bekakou | |
3482 | d0fe8c12 | Athina Bekakou | return serializer;
|
3483 | d0fe8c12 | Athina Bekakou | } |
3484 | d0fe8c12 | Athina Bekakou | |
3485 | d0fe8c12 | Athina Bekakou | function _find(adapter, store, type, id) { |
3486 | d0fe8c12 | Athina Bekakou | var promise = adapter.find(store, type, id),
|
3487 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, type); |
3488 | d0fe8c12 | Athina Bekakou | |
3489 | d0fe8c12 | Athina Bekakou | return resolve(promise, "DS: Handle Adapter#find of " + type + " with id: " + id).then(function(payload) { |
3490 | d0fe8c12 | Athina Bekakou | Ember.assert("You made a request for a " + type.typeKey + " with id " + id + ", but the adapter's response did not have any data", payload); |
3491 | d0fe8c12 | Athina Bekakou | payload = serializer.extract(store, type, payload, id, 'find');
|
3492 | d0fe8c12 | Athina Bekakou | |
3493 | d0fe8c12 | Athina Bekakou | return store.push(type, payload);
|
3494 | d0fe8c12 | Athina Bekakou | }, function(error) {
|
3495 | d0fe8c12 | Athina Bekakou | var record = store.getById(type, id);
|
3496 | d0fe8c12 | Athina Bekakou | record.notFound(); |
3497 | d0fe8c12 | Athina Bekakou | throw error;
|
3498 | d0fe8c12 | Athina Bekakou | }, "DS: Extract payload of '" + type + "'"); |
3499 | d0fe8c12 | Athina Bekakou | } |
3500 | d0fe8c12 | Athina Bekakou | |
3501 | d0fe8c12 | Athina Bekakou | function _findMany(adapter, store, type, ids, owner) { |
3502 | d0fe8c12 | Athina Bekakou | var promise = adapter.findMany(store, type, ids, owner),
|
3503 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, type); |
3504 | d0fe8c12 | Athina Bekakou | |
3505 | d0fe8c12 | Athina Bekakou | return resolve(promise, "DS: Handle Adapter#findMany of " + type).then(function(payload) { |
3506 | d0fe8c12 | Athina Bekakou | payload = serializer.extract(store, type, payload, null, 'findMany'); |
3507 | d0fe8c12 | Athina Bekakou | |
3508 | d0fe8c12 | Athina Bekakou | Ember.assert("The response from a findMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); |
3509 | d0fe8c12 | Athina Bekakou | |
3510 | d0fe8c12 | Athina Bekakou | store.pushMany(type, payload); |
3511 | d0fe8c12 | Athina Bekakou | }, null, "DS: Extract payload of " + type); |
3512 | d0fe8c12 | Athina Bekakou | } |
3513 | d0fe8c12 | Athina Bekakou | |
3514 | d0fe8c12 | Athina Bekakou | function _findHasMany(adapter, store, record, link, relationship) { |
3515 | d0fe8c12 | Athina Bekakou | var promise = adapter.findHasMany(store, record, link, relationship),
|
3516 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, relationship.type); |
3517 | d0fe8c12 | Athina Bekakou | |
3518 | d0fe8c12 | Athina Bekakou | return resolve(promise, "DS: Handle Adapter#findHasMany of " + record + " : " + relationship.type).then(function(payload) { |
3519 | d0fe8c12 | Athina Bekakou | payload = serializer.extract(store, relationship.type, payload, null, 'findHasMany'); |
3520 | d0fe8c12 | Athina Bekakou | |
3521 | d0fe8c12 | Athina Bekakou | Ember.assert("The response from a findHasMany must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); |
3522 | d0fe8c12 | Athina Bekakou | |
3523 | d0fe8c12 | Athina Bekakou | var records = store.pushMany(relationship.type, payload);
|
3524 | d0fe8c12 | Athina Bekakou | record.updateHasMany(relationship.key, records); |
3525 | d0fe8c12 | Athina Bekakou | }, null, "DS: Extract payload of " + record + " : hasMany " + relationship.type); |
3526 | d0fe8c12 | Athina Bekakou | } |
3527 | d0fe8c12 | Athina Bekakou | |
3528 | d0fe8c12 | Athina Bekakou | function _findBelongsTo(adapter, store, record, link, relationship) { |
3529 | d0fe8c12 | Athina Bekakou | var promise = adapter.findBelongsTo(store, record, link, relationship),
|
3530 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, relationship.type); |
3531 | d0fe8c12 | Athina Bekakou | |
3532 | d0fe8c12 | Athina Bekakou | return resolve(promise, "DS: Handle Adapter#findBelongsTo of " + record + " : " + relationship.type).then(function(payload) { |
3533 | d0fe8c12 | Athina Bekakou | payload = serializer.extract(store, relationship.type, payload, null, 'findBelongsTo'); |
3534 | d0fe8c12 | Athina Bekakou | |
3535 | d0fe8c12 | Athina Bekakou | var record = store.push(relationship.type, payload);
|
3536 | d0fe8c12 | Athina Bekakou | record.updateBelongsTo(relationship.key, record); |
3537 | d0fe8c12 | Athina Bekakou | return record;
|
3538 | d0fe8c12 | Athina Bekakou | }, null, "DS: Extract payload of " + record + " : " + relationship.type); |
3539 | d0fe8c12 | Athina Bekakou | } |
3540 | d0fe8c12 | Athina Bekakou | |
3541 | d0fe8c12 | Athina Bekakou | function _findAll(adapter, store, type, sinceToken) { |
3542 | d0fe8c12 | Athina Bekakou | var promise = adapter.findAll(store, type, sinceToken),
|
3543 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, type); |
3544 | d0fe8c12 | Athina Bekakou | |
3545 | d0fe8c12 | Athina Bekakou | return resolve(promise, "DS: Handle Adapter#findAll of " + type).then(function(payload) { |
3546 | d0fe8c12 | Athina Bekakou | payload = serializer.extract(store, type, payload, null, 'findAll'); |
3547 | d0fe8c12 | Athina Bekakou | |
3548 | d0fe8c12 | Athina Bekakou | Ember.assert("The response from a findAll must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); |
3549 | d0fe8c12 | Athina Bekakou | |
3550 | d0fe8c12 | Athina Bekakou | store.pushMany(type, payload); |
3551 | d0fe8c12 | Athina Bekakou | store.didUpdateAll(type); |
3552 | d0fe8c12 | Athina Bekakou | return store.all(type);
|
3553 | d0fe8c12 | Athina Bekakou | }, null, "DS: Extract payload of findAll " + type); |
3554 | d0fe8c12 | Athina Bekakou | } |
3555 | d0fe8c12 | Athina Bekakou | |
3556 | d0fe8c12 | Athina Bekakou | function _findQuery(adapter, store, type, query, recordArray) { |
3557 | d0fe8c12 | Athina Bekakou | var promise = adapter.findQuery(store, type, query, recordArray),
|
3558 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, type); |
3559 | d0fe8c12 | Athina Bekakou | |
3560 | d0fe8c12 | Athina Bekakou | return resolve(promise, "DS: Handle Adapter#findQuery of " + type).then(function(payload) { |
3561 | d0fe8c12 | Athina Bekakou | payload = serializer.extract(store, type, payload, null, 'findQuery'); |
3562 | d0fe8c12 | Athina Bekakou | |
3563 | d0fe8c12 | Athina Bekakou | Ember.assert("The response from a findQuery must be an Array, not " + Ember.inspect(payload), Ember.typeOf(payload) === 'array'); |
3564 | d0fe8c12 | Athina Bekakou | |
3565 | d0fe8c12 | Athina Bekakou | recordArray.load(payload); |
3566 | d0fe8c12 | Athina Bekakou | return recordArray;
|
3567 | d0fe8c12 | Athina Bekakou | }, null, "DS: Extract payload of findQuery " + type); |
3568 | d0fe8c12 | Athina Bekakou | } |
3569 | d0fe8c12 | Athina Bekakou | |
3570 | d0fe8c12 | Athina Bekakou | function _commit(adapter, store, operation, record) { |
3571 | d0fe8c12 | Athina Bekakou | var type = record.constructor,
|
3572 | d0fe8c12 | Athina Bekakou | promise = adapter[operation](store, type, record), |
3573 | d0fe8c12 | Athina Bekakou | serializer = serializerForAdapter(adapter, type); |
3574 | d0fe8c12 | Athina Bekakou | |
3575 | d0fe8c12 | Athina Bekakou | Ember.assert("Your adapter's '" + operation + "' method must return a promise, but it returned " + promise, isThenable(promise)); |
3576 | d0fe8c12 | Athina Bekakou | |
3577 | d0fe8c12 | Athina Bekakou | return promise.then(function(payload) { |
3578 | d0fe8c12 | Athina Bekakou | if (payload) { payload = serializer.extract(store, type, payload, get(record, 'id'), operation); } |
3579 | d0fe8c12 | Athina Bekakou | store.didSaveRecord(record, payload); |
3580 | d0fe8c12 | Athina Bekakou | return record;
|
3581 | d0fe8c12 | Athina Bekakou | }, function(reason) {
|
3582 | d0fe8c12 | Athina Bekakou | if (reason instanceof DS.InvalidError) { |
3583 | d0fe8c12 | Athina Bekakou | store.recordWasInvalid(record, reason.errors); |
3584 | d0fe8c12 | Athina Bekakou | } else {
|
3585 | d0fe8c12 | Athina Bekakou | store.recordWasError(record, reason); |
3586 | d0fe8c12 | Athina Bekakou | } |
3587 | d0fe8c12 | Athina Bekakou | |
3588 | d0fe8c12 | Athina Bekakou | throw reason;
|
3589 | d0fe8c12 | Athina Bekakou | }, "DS: Extract and notify about " + operation + " completion of " + record); |
3590 | d0fe8c12 | Athina Bekakou | } |
3591 | d0fe8c12 | Athina Bekakou | |
3592 | d0fe8c12 | Athina Bekakou | })(); |
3593 | d0fe8c12 | Athina Bekakou | |
3594 | d0fe8c12 | Athina Bekakou | |
3595 | d0fe8c12 | Athina Bekakou | |
3596 | d0fe8c12 | Athina Bekakou | (function() {
|
3597 | d0fe8c12 | Athina Bekakou | /**
|
3598 | d0fe8c12 | Athina Bekakou | @module ember-data
|
3599 | d0fe8c12 | Athina Bekakou | */
|
3600 | d0fe8c12 | Athina Bekakou | |
3601 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
3602 | d0fe8c12 | Athina Bekakou | /*
|
3603 | d0fe8c12 | Athina Bekakou | This file encapsulates the various states that a record can transition
|
3604 | d0fe8c12 | Athina Bekakou | through during its lifecycle.
|
3605 | d0fe8c12 | Athina Bekakou | */
|
3606 | d0fe8c12 | Athina Bekakou | /**
|
3607 | d0fe8c12 | Athina Bekakou | ### State
|
3608 | d0fe8c12 | Athina Bekakou | |
3609 | d0fe8c12 | Athina Bekakou | Each record has a `currentState` property that explicitly tracks what
|
3610 | d0fe8c12 | Athina Bekakou | state a record is in at any given time. For instance, if a record is
|
3611 | d0fe8c12 | Athina Bekakou | newly created and has not yet been sent to the adapter to be saved,
|
3612 | d0fe8c12 | Athina Bekakou | it would be in the `root.loaded.created.uncommitted` state. If a
|
3613 | d0fe8c12 | Athina Bekakou | record has had local modifications made to it that are in the
|
3614 | d0fe8c12 | Athina Bekakou | process of being saved, the record would be in the
|
3615 | d0fe8c12 | Athina Bekakou | `root.loaded.updated.inFlight` state. (These state paths will be
|
3616 | d0fe8c12 | Athina Bekakou | explained in more detail below.)
|
3617 | d0fe8c12 | Athina Bekakou | |
3618 | d0fe8c12 | Athina Bekakou | Events are sent by the record or its store to the record's
|
3619 | d0fe8c12 | Athina Bekakou | `currentState` property. How the state reacts to these events is
|
3620 | d0fe8c12 | Athina Bekakou | dependent on which state it is in. In some states, certain events
|
3621 | d0fe8c12 | Athina Bekakou | will be invalid and will cause an exception to be raised.
|
3622 | d0fe8c12 | Athina Bekakou | |
3623 | d0fe8c12 | Athina Bekakou | States are hierarchical and every state is a substate of the
|
3624 | d0fe8c12 | Athina Bekakou | `RootState`. For example, a record can be in the
|
3625 | d0fe8c12 | Athina Bekakou | `root.deleted.uncommitted` state, then transition into the
|
3626 | d0fe8c12 | Athina Bekakou | `root.deleted.inFlight` state. If a child state does not implement
|
3627 | d0fe8c12 | Athina Bekakou | an event handler, the state manager will attempt to invoke the event
|
3628 | d0fe8c12 | Athina Bekakou | on all parent states until the root state is reached. The state
|
3629 | d0fe8c12 | Athina Bekakou | hierarchy of a record is described in terms of a path string. You
|
3630 | d0fe8c12 | Athina Bekakou | can determine a record's current state by getting the state's
|
3631 | d0fe8c12 | Athina Bekakou | `stateName` property:
|
3632 | d0fe8c12 | Athina Bekakou | |
3633 | d0fe8c12 | Athina Bekakou | ```javascript
|
3634 | d0fe8c12 | Athina Bekakou | record.get('currentState.stateName');
|
3635 | d0fe8c12 | Athina Bekakou | //=> "root.created.uncommitted"
|
3636 | d0fe8c12 | Athina Bekakou | ```
|
3637 | d0fe8c12 | Athina Bekakou | |
3638 | d0fe8c12 | Athina Bekakou | The hierarchy of valid states that ship with ember data looks like
|
3639 | d0fe8c12 | Athina Bekakou | this:
|
3640 | d0fe8c12 | Athina Bekakou | |
3641 | d0fe8c12 | Athina Bekakou | ```text
|
3642 | d0fe8c12 | Athina Bekakou | * root
|
3643 | d0fe8c12 | Athina Bekakou | * deleted
|
3644 | d0fe8c12 | Athina Bekakou | * saved
|
3645 | d0fe8c12 | Athina Bekakou | * uncommitted
|
3646 | d0fe8c12 | Athina Bekakou | * inFlight
|
3647 | d0fe8c12 | Athina Bekakou | * empty
|
3648 | d0fe8c12 | Athina Bekakou | * loaded
|
3649 | d0fe8c12 | Athina Bekakou | * created
|
3650 | d0fe8c12 | Athina Bekakou | * uncommitted
|
3651 | d0fe8c12 | Athina Bekakou | * inFlight
|
3652 | d0fe8c12 | Athina Bekakou | * saved
|
3653 | d0fe8c12 | Athina Bekakou | * updated
|
3654 | d0fe8c12 | Athina Bekakou | * uncommitted
|
3655 | d0fe8c12 | Athina Bekakou | * inFlight
|
3656 | d0fe8c12 | Athina Bekakou | * loading
|
3657 | d0fe8c12 | Athina Bekakou | ```
|
3658 | d0fe8c12 | Athina Bekakou | |
3659 | d0fe8c12 | Athina Bekakou | The `DS.Model` states are themselves stateless. What we mean is
|
3660 | d0fe8c12 | Athina Bekakou | that, the hierarchical states that each of *those* points to is a
|
3661 | d0fe8c12 | Athina Bekakou | shared data structure. For performance reasons, instead of each
|
3662 | d0fe8c12 | Athina Bekakou | record getting its own copy of the hierarchy of states, each record
|
3663 | d0fe8c12 | Athina Bekakou | points to this global, immutable shared instance. How does a state
|
3664 | d0fe8c12 | Athina Bekakou | know which record it should be acting on? We pass the record
|
3665 | d0fe8c12 | Athina Bekakou | instance into the state's event handlers as the first argument.
|
3666 | d0fe8c12 | Athina Bekakou | |
3667 | d0fe8c12 | Athina Bekakou | The record passed as the first parameter is where you should stash
|
3668 | d0fe8c12 | Athina Bekakou | state about the record if needed; you should never store data on the state
|
3669 | d0fe8c12 | Athina Bekakou | object itself.
|
3670 | d0fe8c12 | Athina Bekakou | |
3671 | d0fe8c12 | Athina Bekakou | ### Events and Flags
|
3672 | d0fe8c12 | Athina Bekakou | |
3673 | d0fe8c12 | Athina Bekakou | A state may implement zero or more events and flags.
|
3674 | d0fe8c12 | Athina Bekakou | |
3675 | d0fe8c12 | Athina Bekakou | #### Events
|
3676 | d0fe8c12 | Athina Bekakou | |
3677 | d0fe8c12 | Athina Bekakou | Events are named functions that are invoked when sent to a record. The
|
3678 | d0fe8c12 | Athina Bekakou | record will first look for a method with the given name on the
|
3679 | d0fe8c12 | Athina Bekakou | current state. If no method is found, it will search the current
|
3680 | d0fe8c12 | Athina Bekakou | state's parent, and then its grandparent, and so on until reaching
|
3681 | d0fe8c12 | Athina Bekakou | the top of the hierarchy. If the root is reached without an event
|
3682 | d0fe8c12 | Athina Bekakou | handler being found, an exception will be raised. This can be very
|
3683 | d0fe8c12 | Athina Bekakou | helpful when debugging new features.
|
3684 | d0fe8c12 | Athina Bekakou | |
3685 | d0fe8c12 | Athina Bekakou | Here's an example implementation of a state with a `myEvent` event handler:
|
3686 | d0fe8c12 | Athina Bekakou | |
3687 | d0fe8c12 | Athina Bekakou | ```javascript
|
3688 | d0fe8c12 | Athina Bekakou | aState: DS.State.create({
|
3689 | d0fe8c12 | Athina Bekakou | myEvent: function(manager, param) {
|
3690 | d0fe8c12 | Athina Bekakou | console.log("Received myEvent with", param);
|
3691 | d0fe8c12 | Athina Bekakou | }
|
3692 | d0fe8c12 | Athina Bekakou | })
|
3693 | d0fe8c12 | Athina Bekakou | ```
|
3694 | d0fe8c12 | Athina Bekakou | |
3695 | d0fe8c12 | Athina Bekakou | To trigger this event:
|
3696 | d0fe8c12 | Athina Bekakou | |
3697 | d0fe8c12 | Athina Bekakou | ```javascript
|
3698 | d0fe8c12 | Athina Bekakou | record.send('myEvent', 'foo');
|
3699 | d0fe8c12 | Athina Bekakou | //=> "Received myEvent with foo"
|
3700 | d0fe8c12 | Athina Bekakou | ```
|
3701 | d0fe8c12 | Athina Bekakou | |
3702 | d0fe8c12 | Athina Bekakou | Note that an optional parameter can be sent to a record's `send()` method,
|
3703 | d0fe8c12 | Athina Bekakou | which will be passed as the second parameter to the event handler.
|
3704 | d0fe8c12 | Athina Bekakou | |
3705 | d0fe8c12 | Athina Bekakou | Events should transition to a different state if appropriate. This can be
|
3706 | d0fe8c12 | Athina Bekakou | done by calling the record's `transitionTo()` method with a path to the
|
3707 | d0fe8c12 | Athina Bekakou | desired state. The state manager will attempt to resolve the state path
|
3708 | d0fe8c12 | Athina Bekakou | relative to the current state. If no state is found at that path, it will
|
3709 | d0fe8c12 | Athina Bekakou | attempt to resolve it relative to the current state's parent, and then its
|
3710 | d0fe8c12 | Athina Bekakou | parent, and so on until the root is reached. For example, imagine a hierarchy
|
3711 | d0fe8c12 | Athina Bekakou | like this:
|
3712 | d0fe8c12 | Athina Bekakou | |
3713 | d0fe8c12 | Athina Bekakou | * created
|
3714 | d0fe8c12 | Athina Bekakou | * uncommitted <-- currentState
|
3715 | d0fe8c12 | Athina Bekakou | * inFlight
|
3716 | d0fe8c12 | Athina Bekakou | * updated
|
3717 | d0fe8c12 | Athina Bekakou | * inFlight
|
3718 | d0fe8c12 | Athina Bekakou | |
3719 | d0fe8c12 | Athina Bekakou | If we are currently in the `uncommitted` state, calling
|
3720 | d0fe8c12 | Athina Bekakou | `transitionTo('inFlight')` would transition to the `created.inFlight` state,
|
3721 | d0fe8c12 | Athina Bekakou | while calling `transitionTo('updated.inFlight')` would transition to
|
3722 | d0fe8c12 | Athina Bekakou | the `updated.inFlight` state.
|
3723 | d0fe8c12 | Athina Bekakou | |
3724 | d0fe8c12 | Athina Bekakou | Remember that *only events* should ever cause a state transition. You should
|
3725 | d0fe8c12 | Athina Bekakou | never call `transitionTo()` from outside a state's event handler. If you are
|
3726 | d0fe8c12 | Athina Bekakou | tempted to do so, create a new event and send that to the state manager.
|
3727 | d0fe8c12 | Athina Bekakou | |
3728 | d0fe8c12 | Athina Bekakou | #### Flags
|
3729 | d0fe8c12 | Athina Bekakou | |
3730 | d0fe8c12 | Athina Bekakou | Flags are Boolean values that can be used to introspect a record's current
|
3731 | d0fe8c12 | Athina Bekakou | state in a more user-friendly way than examining its state path. For example,
|
3732 | d0fe8c12 | Athina Bekakou | instead of doing this:
|
3733 | d0fe8c12 | Athina Bekakou | |
3734 | d0fe8c12 | Athina Bekakou | ```javascript
|
3735 | d0fe8c12 | Athina Bekakou | var statePath = record.get('stateManager.currentPath');
|
3736 | d0fe8c12 | Athina Bekakou | if (statePath === 'created.inFlight') {
|
3737 | d0fe8c12 | Athina Bekakou | doSomething();
|
3738 | d0fe8c12 | Athina Bekakou | }
|
3739 | d0fe8c12 | Athina Bekakou | ```
|
3740 | d0fe8c12 | Athina Bekakou | |
3741 | d0fe8c12 | Athina Bekakou | You can say:
|
3742 | d0fe8c12 | Athina Bekakou | |
3743 | d0fe8c12 | Athina Bekakou | ```javascript
|
3744 | d0fe8c12 | Athina Bekakou | if (record.get('isNew') && record.get('isSaving')) {
|
3745 | d0fe8c12 | Athina Bekakou | doSomething();
|
3746 | d0fe8c12 | Athina Bekakou | }
|
3747 | d0fe8c12 | Athina Bekakou | ```
|
3748 | d0fe8c12 | Athina Bekakou | |
3749 | d0fe8c12 | Athina Bekakou | If your state does not set a value for a given flag, the value will
|
3750 | d0fe8c12 | Athina Bekakou | be inherited from its parent (or the first place in the state hierarchy
|
3751 | d0fe8c12 | Athina Bekakou | where it is defined).
|
3752 | d0fe8c12 | Athina Bekakou | |
3753 | d0fe8c12 | Athina Bekakou | The current set of flags are defined below. If you want to add a new flag,
|
3754 | d0fe8c12 | Athina Bekakou | in addition to the area below, you will also need to declare it in the
|
3755 | d0fe8c12 | Athina Bekakou | `DS.Model` class.
|
3756 | d0fe8c12 | Athina Bekakou | |
3757 | d0fe8c12 | Athina Bekakou | |
3758 | d0fe8c12 | Athina Bekakou | * [isEmpty](DS.Model.html#property_isEmpty)
|
3759 | d0fe8c12 | Athina Bekakou | * [isLoading](DS.Model.html#property_isLoading)
|
3760 | d0fe8c12 | Athina Bekakou | * [isLoaded](DS.Model.html#property_isLoaded)
|
3761 | d0fe8c12 | Athina Bekakou | * [isDirty](DS.Model.html#property_isDirty)
|
3762 | d0fe8c12 | Athina Bekakou | * [isSaving](DS.Model.html#property_isSaving)
|
3763 | d0fe8c12 | Athina Bekakou | * [isDeleted](DS.Model.html#property_isDeleted)
|
3764 | d0fe8c12 | Athina Bekakou | * [isNew](DS.Model.html#property_isNew)
|
3765 | d0fe8c12 | Athina Bekakou | * [isValid](DS.Model.html#property_isValid)
|
3766 | d0fe8c12 | Athina Bekakou | |
3767 | d0fe8c12 | Athina Bekakou | @namespace DS
|
3768 | d0fe8c12 | Athina Bekakou | @class RootState
|
3769 | d0fe8c12 | Athina Bekakou | */
|
3770 | d0fe8c12 | Athina Bekakou | |
3771 | d0fe8c12 | Athina Bekakou | var hasDefinedProperties = function(object) { |
3772 | d0fe8c12 | Athina Bekakou | // Ignore internal property defined by simulated `Ember.create`.
|
3773 | d0fe8c12 | Athina Bekakou | var names = Ember.keys(object);
|
3774 | d0fe8c12 | Athina Bekakou | var i, l, name;
|
3775 | d0fe8c12 | Athina Bekakou | for (i = 0, l = names.length; i < l; i++ ) { |
3776 | d0fe8c12 | Athina Bekakou | name = names[i]; |
3777 | d0fe8c12 | Athina Bekakou | if (object.hasOwnProperty(name) && object[name]) { return true; } |
3778 | d0fe8c12 | Athina Bekakou | } |
3779 | d0fe8c12 | Athina Bekakou | |
3780 | d0fe8c12 | Athina Bekakou | return false; |
3781 | d0fe8c12 | Athina Bekakou | }; |
3782 | d0fe8c12 | Athina Bekakou | |
3783 | d0fe8c12 | Athina Bekakou | var didSetProperty = function(record, context) { |
3784 | d0fe8c12 | Athina Bekakou | if (context.value === context.originalValue) {
|
3785 | d0fe8c12 | Athina Bekakou | delete record._attributes[context.name];
|
3786 | d0fe8c12 | Athina Bekakou | record.send('propertyWasReset', context.name);
|
3787 | d0fe8c12 | Athina Bekakou | } else if (context.value !== context.oldValue) { |
3788 | d0fe8c12 | Athina Bekakou | record.send('becomeDirty');
|
3789 | d0fe8c12 | Athina Bekakou | } |
3790 | d0fe8c12 | Athina Bekakou | |
3791 | d0fe8c12 | Athina Bekakou | record.updateRecordArraysLater(); |
3792 | d0fe8c12 | Athina Bekakou | }; |
3793 | d0fe8c12 | Athina Bekakou | |
3794 | d0fe8c12 | Athina Bekakou | // Implementation notes:
|
3795 | d0fe8c12 | Athina Bekakou | //
|
3796 | d0fe8c12 | Athina Bekakou | // Each state has a boolean value for all of the following flags:
|
3797 | d0fe8c12 | Athina Bekakou | //
|
3798 | d0fe8c12 | Athina Bekakou | // * isLoaded: The record has a populated `data` property. When a
|
3799 | d0fe8c12 | Athina Bekakou | // record is loaded via `store.find`, `isLoaded` is false
|
3800 | d0fe8c12 | Athina Bekakou | // until the adapter sets it. When a record is created locally,
|
3801 | d0fe8c12 | Athina Bekakou | // its `isLoaded` property is always true.
|
3802 | d0fe8c12 | Athina Bekakou | // * isDirty: The record has local changes that have not yet been
|
3803 | d0fe8c12 | Athina Bekakou | // saved by the adapter. This includes records that have been
|
3804 | d0fe8c12 | Athina Bekakou | // created (but not yet saved) or deleted.
|
3805 | d0fe8c12 | Athina Bekakou | // * isSaving: The record has been committed, but
|
3806 | d0fe8c12 | Athina Bekakou | // the adapter has not yet acknowledged that the changes have
|
3807 | d0fe8c12 | Athina Bekakou | // been persisted to the backend.
|
3808 | d0fe8c12 | Athina Bekakou | // * isDeleted: The record was marked for deletion. When `isDeleted`
|
3809 | d0fe8c12 | Athina Bekakou | // is true and `isDirty` is true, the record is deleted locally
|
3810 | d0fe8c12 | Athina Bekakou | // but the deletion was not yet persisted. When `isSaving` is
|
3811 | d0fe8c12 | Athina Bekakou | // true, the change is in-flight. When both `isDirty` and
|
3812 | d0fe8c12 | Athina Bekakou | // `isSaving` are false, the change has persisted.
|
3813 | d0fe8c12 | Athina Bekakou | // * isError: The adapter reported that it was unable to save
|
3814 | d0fe8c12 | Athina Bekakou | // local changes to the backend. This may also result in the
|
3815 | d0fe8c12 | Athina Bekakou | // record having its `isValid` property become false if the
|
3816 | d0fe8c12 | Athina Bekakou | // adapter reported that server-side validations failed.
|
3817 | d0fe8c12 | Athina Bekakou | // * isNew: The record was created on the client and the adapter
|
3818 | d0fe8c12 | Athina Bekakou | // did not yet report that it was successfully saved.
|
3819 | d0fe8c12 | Athina Bekakou | // * isValid: No client-side validations have failed and the
|
3820 | d0fe8c12 | Athina Bekakou | // adapter did not report any server-side validation failures.
|
3821 | d0fe8c12 | Athina Bekakou | |
3822 | d0fe8c12 | Athina Bekakou | // The dirty state is a abstract state whose functionality is
|
3823 | d0fe8c12 | Athina Bekakou | // shared between the `created` and `updated` states.
|
3824 | d0fe8c12 | Athina Bekakou | //
|
3825 | d0fe8c12 | Athina Bekakou | // The deleted state shares the `isDirty` flag with the
|
3826 | d0fe8c12 | Athina Bekakou | // subclasses of `DirtyState`, but with a very different
|
3827 | d0fe8c12 | Athina Bekakou | // implementation.
|
3828 | d0fe8c12 | Athina Bekakou | //
|
3829 | d0fe8c12 | Athina Bekakou | // Dirty states have three child states:
|
3830 | d0fe8c12 | Athina Bekakou | //
|
3831 | d0fe8c12 | Athina Bekakou | // `uncommitted`: the store has not yet handed off the record
|
3832 | d0fe8c12 | Athina Bekakou | // to be saved.
|
3833 | d0fe8c12 | Athina Bekakou | // `inFlight`: the store has handed off the record to be saved,
|
3834 | d0fe8c12 | Athina Bekakou | // but the adapter has not yet acknowledged success.
|
3835 | d0fe8c12 | Athina Bekakou | // `invalid`: the record has invalid information and cannot be
|
3836 | d0fe8c12 | Athina Bekakou | // send to the adapter yet.
|
3837 | d0fe8c12 | Athina Bekakou | var DirtyState = {
|
3838 | d0fe8c12 | Athina Bekakou | initialState: 'uncommitted', |
3839 | d0fe8c12 | Athina Bekakou | |
3840 | d0fe8c12 | Athina Bekakou | // FLAGS
|
3841 | d0fe8c12 | Athina Bekakou | isDirty: true, |
3842 | d0fe8c12 | Athina Bekakou | |
3843 | d0fe8c12 | Athina Bekakou | // SUBSTATES
|
3844 | d0fe8c12 | Athina Bekakou | |
3845 | d0fe8c12 | Athina Bekakou | // When a record first becomes dirty, it is `uncommitted`.
|
3846 | d0fe8c12 | Athina Bekakou | // This means that there are local pending changes, but they
|
3847 | d0fe8c12 | Athina Bekakou | // have not yet begun to be saved, and are not invalid.
|
3848 | d0fe8c12 | Athina Bekakou | uncommitted: {
|
3849 | d0fe8c12 | Athina Bekakou | // EVENTS
|
3850 | d0fe8c12 | Athina Bekakou | didSetProperty: didSetProperty,
|
3851 | d0fe8c12 | Athina Bekakou | |
3852 | d0fe8c12 | Athina Bekakou | propertyWasReset: function(record, name) { |
3853 | d0fe8c12 | Athina Bekakou | var stillDirty = false; |
3854 | d0fe8c12 | Athina Bekakou | |
3855 | d0fe8c12 | Athina Bekakou | for (var prop in record._attributes) { |
3856 | d0fe8c12 | Athina Bekakou | stillDirty = true;
|
3857 | d0fe8c12 | Athina Bekakou | break;
|
3858 | d0fe8c12 | Athina Bekakou | } |
3859 | d0fe8c12 | Athina Bekakou | |
3860 | d0fe8c12 | Athina Bekakou | if (!stillDirty) { record.send('rolledBack'); } |
3861 | d0fe8c12 | Athina Bekakou | }, |
3862 | d0fe8c12 | Athina Bekakou | |
3863 | d0fe8c12 | Athina Bekakou | pushedData: Ember.K,
|
3864 | d0fe8c12 | Athina Bekakou | |
3865 | d0fe8c12 | Athina Bekakou | becomeDirty: Ember.K,
|
3866 | d0fe8c12 | Athina Bekakou | |
3867 | d0fe8c12 | Athina Bekakou | willCommit: function(record) { |
3868 | d0fe8c12 | Athina Bekakou | record.transitionTo('inFlight');
|
3869 | d0fe8c12 | Athina Bekakou | }, |
3870 | d0fe8c12 | Athina Bekakou | |
3871 | d0fe8c12 | Athina Bekakou | reloadRecord: function(record, resolve) { |
3872 | d0fe8c12 | Athina Bekakou | resolve(get(record, 'store').reloadRecord(record));
|
3873 | d0fe8c12 | Athina Bekakou | }, |
3874 | d0fe8c12 | Athina Bekakou | |
3875 | d0fe8c12 | Athina Bekakou | rolledBack: function(record) { |
3876 | d0fe8c12 | Athina Bekakou | record.transitionTo('loaded.saved');
|
3877 | d0fe8c12 | Athina Bekakou | }, |
3878 | d0fe8c12 | Athina Bekakou | |
3879 | d0fe8c12 | Athina Bekakou | becameInvalid: function(record) { |
3880 | d0fe8c12 | Athina Bekakou | record.transitionTo('invalid');
|
3881 | d0fe8c12 | Athina Bekakou | }, |
3882 | d0fe8c12 | Athina Bekakou | |
3883 | d0fe8c12 | Athina Bekakou | rollback: function(record) { |
3884 | d0fe8c12 | Athina Bekakou | record.rollback(); |
3885 | d0fe8c12 | Athina Bekakou | } |
3886 | d0fe8c12 | Athina Bekakou | }, |
3887 | d0fe8c12 | Athina Bekakou | |
3888 | d0fe8c12 | Athina Bekakou | // Once a record has been handed off to the adapter to be
|
3889 | d0fe8c12 | Athina Bekakou | // saved, it is in the 'in flight' state. Changes to the
|
3890 | d0fe8c12 | Athina Bekakou | // record cannot be made during this window.
|
3891 | d0fe8c12 | Athina Bekakou | inFlight: {
|
3892 | d0fe8c12 | Athina Bekakou | // FLAGS
|
3893 | d0fe8c12 | Athina Bekakou | isSaving: true, |
3894 | d0fe8c12 | Athina Bekakou | |
3895 | d0fe8c12 | Athina Bekakou | // EVENTS
|
3896 | d0fe8c12 | Athina Bekakou | didSetProperty: didSetProperty,
|
3897 | d0fe8c12 | Athina Bekakou | becomeDirty: Ember.K,
|
3898 | d0fe8c12 | Athina Bekakou | pushedData: Ember.K,
|
3899 | d0fe8c12 | Athina Bekakou | |
3900 | d0fe8c12 | Athina Bekakou | // TODO: More robust semantics around save-while-in-flight
|
3901 | d0fe8c12 | Athina Bekakou | willCommit: Ember.K,
|
3902 | d0fe8c12 | Athina Bekakou | |
3903 | d0fe8c12 | Athina Bekakou | didCommit: function(record) { |
3904 | d0fe8c12 | Athina Bekakou | var dirtyType = get(this, 'dirtyType'); |
3905 | d0fe8c12 | Athina Bekakou | |
3906 | d0fe8c12 | Athina Bekakou | record.transitionTo('saved');
|
3907 | d0fe8c12 | Athina Bekakou | record.send('invokeLifecycleCallbacks', dirtyType);
|
3908 | d0fe8c12 | Athina Bekakou | }, |
3909 | d0fe8c12 | Athina Bekakou | |
3910 | d0fe8c12 | Athina Bekakou | becameInvalid: function(record) { |
3911 | d0fe8c12 | Athina Bekakou | record.transitionTo('invalid');
|
3912 | d0fe8c12 | Athina Bekakou | record.send('invokeLifecycleCallbacks');
|
3913 | d0fe8c12 | Athina Bekakou | }, |
3914 | d0fe8c12 | Athina Bekakou | |
3915 | d0fe8c12 | Athina Bekakou | becameError: function(record) { |
3916 | d0fe8c12 | Athina Bekakou | record.transitionTo('uncommitted');
|
3917 | d0fe8c12 | Athina Bekakou | record.triggerLater('becameError', record);
|
3918 | d0fe8c12 | Athina Bekakou | } |
3919 | d0fe8c12 | Athina Bekakou | }, |
3920 | d0fe8c12 | Athina Bekakou | |
3921 | d0fe8c12 | Athina Bekakou | // A record is in the `invalid` state when its client-side
|
3922 | d0fe8c12 | Athina Bekakou | // invalidations have failed, or if the adapter has indicated
|
3923 | d0fe8c12 | Athina Bekakou | // the the record failed server-side invalidations.
|
3924 | d0fe8c12 | Athina Bekakou | invalid: {
|
3925 | d0fe8c12 | Athina Bekakou | // FLAGS
|
3926 | d0fe8c12 | Athina Bekakou | isValid: false, |
3927 | d0fe8c12 | Athina Bekakou | |
3928 | d0fe8c12 | Athina Bekakou | // EVENTS
|
3929 | d0fe8c12 | Athina Bekakou | deleteRecord: function(record) { |
3930 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.uncommitted');
|
3931 | d0fe8c12 | Athina Bekakou | record.clearRelationships(); |
3932 | d0fe8c12 | Athina Bekakou | }, |
3933 | d0fe8c12 | Athina Bekakou | |
3934 | d0fe8c12 | Athina Bekakou | didSetProperty: function(record, context) { |
3935 | d0fe8c12 | Athina Bekakou | get(record, 'errors').remove(context.name);
|
3936 | d0fe8c12 | Athina Bekakou | |
3937 | d0fe8c12 | Athina Bekakou | didSetProperty(record, context); |
3938 | d0fe8c12 | Athina Bekakou | }, |
3939 | d0fe8c12 | Athina Bekakou | |
3940 | d0fe8c12 | Athina Bekakou | becomeDirty: Ember.K,
|
3941 | d0fe8c12 | Athina Bekakou | |
3942 | d0fe8c12 | Athina Bekakou | rolledBack: function(record) { |
3943 | d0fe8c12 | Athina Bekakou | get(record, 'errors').clear();
|
3944 | d0fe8c12 | Athina Bekakou | }, |
3945 | d0fe8c12 | Athina Bekakou | |
3946 | d0fe8c12 | Athina Bekakou | becameValid: function(record) { |
3947 | d0fe8c12 | Athina Bekakou | record.transitionTo('uncommitted');
|
3948 | d0fe8c12 | Athina Bekakou | }, |
3949 | d0fe8c12 | Athina Bekakou | |
3950 | d0fe8c12 | Athina Bekakou | invokeLifecycleCallbacks: function(record) { |
3951 | d0fe8c12 | Athina Bekakou | record.triggerLater('becameInvalid', record);
|
3952 | d0fe8c12 | Athina Bekakou | } |
3953 | d0fe8c12 | Athina Bekakou | } |
3954 | d0fe8c12 | Athina Bekakou | }; |
3955 | d0fe8c12 | Athina Bekakou | |
3956 | d0fe8c12 | Athina Bekakou | // The created and updated states are created outside the state
|
3957 | d0fe8c12 | Athina Bekakou | // chart so we can reopen their substates and add mixins as
|
3958 | d0fe8c12 | Athina Bekakou | // necessary.
|
3959 | d0fe8c12 | Athina Bekakou | |
3960 | d0fe8c12 | Athina Bekakou | function deepClone(object) { |
3961 | d0fe8c12 | Athina Bekakou | var clone = {}, value;
|
3962 | d0fe8c12 | Athina Bekakou | |
3963 | d0fe8c12 | Athina Bekakou | for (var prop in object) { |
3964 | d0fe8c12 | Athina Bekakou | value = object[prop]; |
3965 | d0fe8c12 | Athina Bekakou | if (value && typeof value === 'object') { |
3966 | d0fe8c12 | Athina Bekakou | clone[prop] = deepClone(value); |
3967 | d0fe8c12 | Athina Bekakou | } else {
|
3968 | d0fe8c12 | Athina Bekakou | clone[prop] = value; |
3969 | d0fe8c12 | Athina Bekakou | } |
3970 | d0fe8c12 | Athina Bekakou | } |
3971 | d0fe8c12 | Athina Bekakou | |
3972 | d0fe8c12 | Athina Bekakou | return clone;
|
3973 | d0fe8c12 | Athina Bekakou | } |
3974 | d0fe8c12 | Athina Bekakou | |
3975 | d0fe8c12 | Athina Bekakou | function mixin(original, hash) { |
3976 | d0fe8c12 | Athina Bekakou | for (var prop in hash) { |
3977 | d0fe8c12 | Athina Bekakou | original[prop] = hash[prop]; |
3978 | d0fe8c12 | Athina Bekakou | } |
3979 | d0fe8c12 | Athina Bekakou | |
3980 | d0fe8c12 | Athina Bekakou | return original;
|
3981 | d0fe8c12 | Athina Bekakou | } |
3982 | d0fe8c12 | Athina Bekakou | |
3983 | d0fe8c12 | Athina Bekakou | function dirtyState(options) { |
3984 | d0fe8c12 | Athina Bekakou | var newState = deepClone(DirtyState);
|
3985 | d0fe8c12 | Athina Bekakou | return mixin(newState, options);
|
3986 | d0fe8c12 | Athina Bekakou | } |
3987 | d0fe8c12 | Athina Bekakou | |
3988 | d0fe8c12 | Athina Bekakou | var createdState = dirtyState({
|
3989 | d0fe8c12 | Athina Bekakou | dirtyType: 'created', |
3990 | d0fe8c12 | Athina Bekakou | |
3991 | d0fe8c12 | Athina Bekakou | // FLAGS
|
3992 | d0fe8c12 | Athina Bekakou | isNew: true |
3993 | d0fe8c12 | Athina Bekakou | }); |
3994 | d0fe8c12 | Athina Bekakou | |
3995 | d0fe8c12 | Athina Bekakou | createdState.uncommitted.rolledBack = function(record) { |
3996 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.saved');
|
3997 | d0fe8c12 | Athina Bekakou | }; |
3998 | d0fe8c12 | Athina Bekakou | |
3999 | d0fe8c12 | Athina Bekakou | var updatedState = dirtyState({
|
4000 | d0fe8c12 | Athina Bekakou | dirtyType: 'updated' |
4001 | d0fe8c12 | Athina Bekakou | }); |
4002 | d0fe8c12 | Athina Bekakou | |
4003 | d0fe8c12 | Athina Bekakou | createdState.uncommitted.deleteRecord = function(record) { |
4004 | d0fe8c12 | Athina Bekakou | record.clearRelationships(); |
4005 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.saved');
|
4006 | d0fe8c12 | Athina Bekakou | }; |
4007 | d0fe8c12 | Athina Bekakou | |
4008 | d0fe8c12 | Athina Bekakou | createdState.uncommitted.rollback = function(record) { |
4009 | d0fe8c12 | Athina Bekakou | DirtyState.uncommitted.rollback.apply(this, arguments); |
4010 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.saved');
|
4011 | d0fe8c12 | Athina Bekakou | }; |
4012 | d0fe8c12 | Athina Bekakou | |
4013 | d0fe8c12 | Athina Bekakou | updatedState.uncommitted.deleteRecord = function(record) { |
4014 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.uncommitted');
|
4015 | d0fe8c12 | Athina Bekakou | record.clearRelationships(); |
4016 | d0fe8c12 | Athina Bekakou | }; |
4017 | d0fe8c12 | Athina Bekakou | |
4018 | d0fe8c12 | Athina Bekakou | var RootState = {
|
4019 | d0fe8c12 | Athina Bekakou | // FLAGS
|
4020 | d0fe8c12 | Athina Bekakou | isEmpty: false, |
4021 | d0fe8c12 | Athina Bekakou | isLoading: false, |
4022 | d0fe8c12 | Athina Bekakou | isLoaded: false, |
4023 | d0fe8c12 | Athina Bekakou | isDirty: false, |
4024 | d0fe8c12 | Athina Bekakou | isSaving: false, |
4025 | d0fe8c12 | Athina Bekakou | isDeleted: false, |
4026 | d0fe8c12 | Athina Bekakou | isNew: false, |
4027 | d0fe8c12 | Athina Bekakou | isValid: true, |
4028 | d0fe8c12 | Athina Bekakou | |
4029 | d0fe8c12 | Athina Bekakou | // DEFAULT EVENTS
|
4030 | d0fe8c12 | Athina Bekakou | |
4031 | d0fe8c12 | Athina Bekakou | // Trying to roll back if you're not in the dirty state
|
4032 | d0fe8c12 | Athina Bekakou | // doesn't change your state. For example, if you're in the
|
4033 | d0fe8c12 | Athina Bekakou | // in-flight state, rolling back the record doesn't move
|
4034 | d0fe8c12 | Athina Bekakou | // you out of the in-flight state.
|
4035 | d0fe8c12 | Athina Bekakou | rolledBack: Ember.K,
|
4036 | d0fe8c12 | Athina Bekakou | |
4037 | d0fe8c12 | Athina Bekakou | propertyWasReset: Ember.K,
|
4038 | d0fe8c12 | Athina Bekakou | |
4039 | d0fe8c12 | Athina Bekakou | // SUBSTATES
|
4040 | d0fe8c12 | Athina Bekakou | |
4041 | d0fe8c12 | Athina Bekakou | // A record begins its lifecycle in the `empty` state.
|
4042 | d0fe8c12 | Athina Bekakou | // If its data will come from the adapter, it will
|
4043 | d0fe8c12 | Athina Bekakou | // transition into the `loading` state. Otherwise, if
|
4044 | d0fe8c12 | Athina Bekakou | // the record is being created on the client, it will
|
4045 | d0fe8c12 | Athina Bekakou | // transition into the `created` state.
|
4046 | d0fe8c12 | Athina Bekakou | empty: {
|
4047 | d0fe8c12 | Athina Bekakou | isEmpty: true, |
4048 | d0fe8c12 | Athina Bekakou | |
4049 | d0fe8c12 | Athina Bekakou | // EVENTS
|
4050 | d0fe8c12 | Athina Bekakou | loadingData: function(record, promise) { |
4051 | d0fe8c12 | Athina Bekakou | record._loadingPromise = promise; |
4052 | d0fe8c12 | Athina Bekakou | record.transitionTo('loading');
|
4053 | d0fe8c12 | Athina Bekakou | }, |
4054 | d0fe8c12 | Athina Bekakou | |
4055 | d0fe8c12 | Athina Bekakou | loadedData: function(record) { |
4056 | d0fe8c12 | Athina Bekakou | record.transitionTo('loaded.created.uncommitted');
|
4057 | d0fe8c12 | Athina Bekakou | |
4058 | d0fe8c12 | Athina Bekakou | record.suspendRelationshipObservers(function() {
|
4059 | d0fe8c12 | Athina Bekakou | record.notifyPropertyChange('data');
|
4060 | d0fe8c12 | Athina Bekakou | }); |
4061 | d0fe8c12 | Athina Bekakou | }, |
4062 | d0fe8c12 | Athina Bekakou | |
4063 | d0fe8c12 | Athina Bekakou | pushedData: function(record) { |
4064 | d0fe8c12 | Athina Bekakou | record.transitionTo('loaded.saved');
|
4065 | d0fe8c12 | Athina Bekakou | record.triggerLater('didLoad');
|
4066 | d0fe8c12 | Athina Bekakou | } |
4067 | d0fe8c12 | Athina Bekakou | }, |
4068 | d0fe8c12 | Athina Bekakou | |
4069 | d0fe8c12 | Athina Bekakou | // A record enters this state when the store askes
|
4070 | d0fe8c12 | Athina Bekakou | // the adapter for its data. It remains in this state
|
4071 | d0fe8c12 | Athina Bekakou | // until the adapter provides the requested data.
|
4072 | d0fe8c12 | Athina Bekakou | //
|
4073 | d0fe8c12 | Athina Bekakou | // Usually, this process is asynchronous, using an
|
4074 | d0fe8c12 | Athina Bekakou | // XHR to retrieve the data.
|
4075 | d0fe8c12 | Athina Bekakou | loading: {
|
4076 | d0fe8c12 | Athina Bekakou | // FLAGS
|
4077 | d0fe8c12 | Athina Bekakou | isLoading: true, |
4078 | d0fe8c12 | Athina Bekakou | |
4079 | d0fe8c12 | Athina Bekakou | exit: function(record) { |
4080 | d0fe8c12 | Athina Bekakou | record._loadingPromise = null;
|
4081 | d0fe8c12 | Athina Bekakou | }, |
4082 | d0fe8c12 | Athina Bekakou | |
4083 | d0fe8c12 | Athina Bekakou | // EVENTS
|
4084 | d0fe8c12 | Athina Bekakou | pushedData: function(record) { |
4085 | d0fe8c12 | Athina Bekakou | record.transitionTo('loaded.saved');
|
4086 | d0fe8c12 | Athina Bekakou | record.triggerLater('didLoad');
|
4087 | d0fe8c12 | Athina Bekakou | set(record, 'isError', false); |
4088 | d0fe8c12 | Athina Bekakou | }, |
4089 | d0fe8c12 | Athina Bekakou | |
4090 | d0fe8c12 | Athina Bekakou | becameError: function(record) { |
4091 | d0fe8c12 | Athina Bekakou | record.triggerLater('becameError', record);
|
4092 | d0fe8c12 | Athina Bekakou | }, |
4093 | d0fe8c12 | Athina Bekakou | |
4094 | d0fe8c12 | Athina Bekakou | notFound: function(record) { |
4095 | d0fe8c12 | Athina Bekakou | record.transitionTo('empty');
|
4096 | d0fe8c12 | Athina Bekakou | } |
4097 | d0fe8c12 | Athina Bekakou | }, |
4098 | d0fe8c12 | Athina Bekakou | |
4099 | d0fe8c12 | Athina Bekakou | // A record enters this state when its data is populated.
|
4100 | d0fe8c12 | Athina Bekakou | // Most of a record's lifecycle is spent inside substates
|
4101 | d0fe8c12 | Athina Bekakou | // of the `loaded` state.
|
4102 | d0fe8c12 | Athina Bekakou | loaded: {
|
4103 | d0fe8c12 | Athina Bekakou | initialState: 'saved', |
4104 | d0fe8c12 | Athina Bekakou | |
4105 | d0fe8c12 | Athina Bekakou | // FLAGS
|
4106 | d0fe8c12 | Athina Bekakou | isLoaded: true, |
4107 | d0fe8c12 | Athina Bekakou | |
4108 | d0fe8c12 | Athina Bekakou | // SUBSTATES
|
4109 | d0fe8c12 | Athina Bekakou | |
4110 | d0fe8c12 | Athina Bekakou | // If there are no local changes to a record, it remains
|
4111 | d0fe8c12 | Athina Bekakou | // in the `saved` state.
|
4112 | d0fe8c12 | Athina Bekakou | saved: {
|
4113 | d0fe8c12 | Athina Bekakou | setup: function(record) { |
4114 | d0fe8c12 | Athina Bekakou | var attrs = record._attributes,
|
4115 | d0fe8c12 | Athina Bekakou | isDirty = false;
|
4116 | d0fe8c12 | Athina Bekakou | |
4117 | d0fe8c12 | Athina Bekakou | for (var prop in attrs) { |
4118 | d0fe8c12 | Athina Bekakou | if (attrs.hasOwnProperty(prop)) {
|
4119 | d0fe8c12 | Athina Bekakou | isDirty = true;
|
4120 | d0fe8c12 | Athina Bekakou | break;
|
4121 | d0fe8c12 | Athina Bekakou | } |
4122 | d0fe8c12 | Athina Bekakou | } |
4123 | d0fe8c12 | Athina Bekakou | |
4124 | d0fe8c12 | Athina Bekakou | if (isDirty) {
|
4125 | d0fe8c12 | Athina Bekakou | record.adapterDidDirty(); |
4126 | d0fe8c12 | Athina Bekakou | } |
4127 | d0fe8c12 | Athina Bekakou | }, |
4128 | d0fe8c12 | Athina Bekakou | |
4129 | d0fe8c12 | Athina Bekakou | // EVENTS
|
4130 | d0fe8c12 | Athina Bekakou | didSetProperty: didSetProperty,
|
4131 | d0fe8c12 | Athina Bekakou | |
4132 | d0fe8c12 | Athina Bekakou | pushedData: Ember.K,
|
4133 | d0fe8c12 | Athina Bekakou | |
4134 | d0fe8c12 | Athina Bekakou | becomeDirty: function(record) { |
4135 | d0fe8c12 | Athina Bekakou | record.transitionTo('updated.uncommitted');
|
4136 | d0fe8c12 | Athina Bekakou | }, |
4137 | d0fe8c12 | Athina Bekakou | |
4138 | d0fe8c12 | Athina Bekakou | willCommit: function(record) { |
4139 | d0fe8c12 | Athina Bekakou | record.transitionTo('updated.inFlight');
|
4140 | d0fe8c12 | Athina Bekakou | }, |
4141 | d0fe8c12 | Athina Bekakou | |
4142 | d0fe8c12 | Athina Bekakou | reloadRecord: function(record, resolve) { |
4143 | d0fe8c12 | Athina Bekakou | resolve(get(record, 'store').reloadRecord(record));
|
4144 | d0fe8c12 | Athina Bekakou | }, |
4145 | d0fe8c12 | Athina Bekakou | |
4146 | d0fe8c12 | Athina Bekakou | deleteRecord: function(record) { |
4147 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.uncommitted');
|
4148 | d0fe8c12 | Athina Bekakou | record.clearRelationships(); |
4149 | d0fe8c12 | Athina Bekakou | }, |
4150 | d0fe8c12 | Athina Bekakou | |
4151 | d0fe8c12 | Athina Bekakou | unloadRecord: function(record) { |
4152 | d0fe8c12 | Athina Bekakou | // clear relationships before moving to deleted state
|
4153 | d0fe8c12 | Athina Bekakou | // otherwise it fails
|
4154 | d0fe8c12 | Athina Bekakou | record.clearRelationships(); |
4155 | d0fe8c12 | Athina Bekakou | record.transitionTo('deleted.saved');
|
4156 | d0fe8c12 | Athina Bekakou | }, |
4157 | d0fe8c12 | Athina Bekakou | |
4158 | d0fe8c12 | Athina Bekakou | didCommit: function(record) { |
4159 | d0fe8c12 | Athina Bekakou | record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType')); |
4160 | d0fe8c12 | Athina Bekakou | }, |
4161 | d0fe8c12 | Athina Bekakou | |
4162 | d0fe8c12 | Athina Bekakou | // loaded.saved.notFound would be triggered by a failed
|
4163 | d0fe8c12 | Athina Bekakou | // `reload()` on an unchanged record
|
4164 | d0fe8c12 | Athina Bekakou | notFound: Ember.K
|
4165 | d0fe8c12 | Athina Bekakou | |
4166 | d0fe8c12 | Athina Bekakou | }, |
4167 | d0fe8c12 | Athina Bekakou | |
4168 | d0fe8c12 | Athina Bekakou | // A record is in this state after it has been locally
|
4169 | d0fe8c12 | Athina Bekakou | // created but before the adapter has indicated that
|
4170 | d0fe8c12 | Athina Bekakou | // it has been saved.
|
4171 | d0fe8c12 | Athina Bekakou | created: createdState,
|
4172 | d0fe8c12 | Athina Bekakou | |
4173 | d0fe8c12 | Athina Bekakou | // A record is in this state if it has already been
|
4174 | d0fe8c12 | Athina Bekakou | // saved to the server, but there are new local changes
|
4175 | d0fe8c12 | Athina Bekakou | // that have not yet been saved.
|
4176 | d0fe8c12 | Athina Bekakou | updated: updatedState
|
4177 | d0fe8c12 | Athina Bekakou | }, |
4178 | d0fe8c12 | Athina Bekakou | |
4179 | d0fe8c12 | Athina Bekakou | // A record is in this state if it was deleted from the store.
|
4180 | d0fe8c12 | Athina Bekakou | deleted: {
|
4181 | d0fe8c12 | Athina Bekakou | initialState: 'uncommitted', |
4182 | d0fe8c12 | Athina Bekakou | dirtyType: 'deleted', |
4183 | d0fe8c12 | Athina Bekakou | |
4184 | d0fe8c12 | Athina Bekakou | // FLAGS
|
4185 | d0fe8c12 | Athina Bekakou | isDeleted: true, |
4186 | d0fe8c12 | Athina Bekakou | isLoaded: true, |
4187 | d0fe8c12 | Athina Bekakou | isDirty: true, |
4188 | d0fe8c12 | Athina Bekakou | |
4189 | d0fe8c12 | Athina Bekakou | // TRANSITIONS
|
4190 | d0fe8c12 | Athina Bekakou | setup: function(record) { |
4191 | d0fe8c12 | Athina Bekakou | record.updateRecordArrays(); |
4192 | d0fe8c12 | Athina Bekakou | }, |
4193 | d0fe8c12 | Athina Bekakou | |
4194 | d0fe8c12 | Athina Bekakou | // SUBSTATES
|
4195 | d0fe8c12 | Athina Bekakou | |
4196 | d0fe8c12 | Athina Bekakou | // When a record is deleted, it enters the `start`
|
4197 | d0fe8c12 | Athina Bekakou | // state. It will exit this state when the record
|
4198 | d0fe8c12 | Athina Bekakou | // starts to commit.
|
4199 | d0fe8c12 | Athina Bekakou | uncommitted: {
|
4200 | d0fe8c12 | Athina Bekakou | |
4201 | d0fe8c12 | Athina Bekakou | // EVENTS
|
4202 | d0fe8c12 | Athina Bekakou | |
4203 | d0fe8c12 | Athina Bekakou | willCommit: function(record) { |
4204 | d0fe8c12 | Athina Bekakou | record.transitionTo('inFlight');
|
4205 | d0fe8c12 | Athina Bekakou | }, |
4206 | d0fe8c12 | Athina Bekakou | |
4207 | d0fe8c12 | Athina Bekakou | rollback: function(record) { |
4208 | d0fe8c12 | Athina Bekakou | record.rollback(); |
4209 | d0fe8c12 | Athina Bekakou | }, |
4210 | d0fe8c12 | Athina Bekakou | |
4211 | d0fe8c12 | Athina Bekakou | becomeDirty: Ember.K,
|
4212 | d0fe8c12 | Athina Bekakou | deleteRecord: Ember.K,
|
4213 | d0fe8c12 | Athina Bekakou | |
4214 | d0fe8c12 | Athina Bekakou | rolledBack: function(record) { |
4215 | d0fe8c12 | Athina Bekakou | record.transitionTo('loaded.saved');
|
4216 | d0fe8c12 | Athina Bekakou | } |
4217 | d0fe8c12 | Athina Bekakou | }, |
4218 | d0fe8c12 | Athina Bekakou | |
4219 | d0fe8c12 | Athina Bekakou | // After a record starts committing, but
|
4220 | d0fe8c12 | Athina Bekakou | // before the adapter indicates that the deletion
|
4221 | d0fe8c12 | Athina Bekakou | // has saved to the server, a record is in the
|
4222 | d0fe8c12 | Athina Bekakou | // `inFlight` substate of `deleted`.
|
4223 | d0fe8c12 | Athina Bekakou | inFlight: {
|
4224 | d0fe8c12 | Athina Bekakou | // FLAGS
|
4225 | d0fe8c12 | Athina Bekakou | isSaving: true, |
4226 | d0fe8c12 | Athina Bekakou | |
4227 | d0fe8c12 | Athina Bekakou | // EVENTS
|
4228 | d0fe8c12 | Athina Bekakou | |
4229 | d0fe8c12 | Athina Bekakou | // TODO: More robust semantics around save-while-in-flight
|
4230 | d0fe8c12 | Athina Bekakou | willCommit: Ember.K,
|
4231 | d0fe8c12 | Athina Bekakou | didCommit: function(record) { |
4232 | d0fe8c12 | Athina Bekakou | record.transitionTo('saved');
|
4233 | d0fe8c12 | Athina Bekakou | |
4234 | d0fe8c12 | Athina Bekakou | record.send('invokeLifecycleCallbacks');
|
4235 | d0fe8c12 | Athina Bekakou | }, |
4236 | d0fe8c12 | Athina Bekakou | |
4237 | d0fe8c12 | Athina Bekakou | becameError: function(record) { |
4238 | d0fe8c12 | Athina Bekakou | record.transitionTo('uncommitted');
|
4239 | d0fe8c12 | Athina Bekakou | record.triggerLater('becameError', record);
|
4240 | d0fe8c12 | Athina Bekakou | } |
4241 | d0fe8c12 | Athina Bekakou | }, |
4242 | d0fe8c12 | Athina Bekakou | |
4243 | d0fe8c12 | Athina Bekakou | // Once the adapter indicates that the deletion has
|
4244 | d0fe8c12 | Athina Bekakou | // been saved, the record enters the `saved` substate
|
4245 | d0fe8c12 | Athina Bekakou | // of `deleted`.
|
4246 | d0fe8c12 | Athina Bekakou | saved: {
|
4247 | d0fe8c12 | Athina Bekakou | // FLAGS
|
4248 | d0fe8c12 | Athina Bekakou | isDirty: false, |
4249 | d0fe8c12 | Athina Bekakou | |
4250 | d0fe8c12 | Athina Bekakou | setup: function(record) { |
4251 | d0fe8c12 | Athina Bekakou | var store = get(record, 'store'); |
4252 | d0fe8c12 | Athina Bekakou | store.dematerializeRecord(record); |
4253 | d0fe8c12 | Athina Bekakou | }, |
4254 | d0fe8c12 | Athina Bekakou | |
4255 | d0fe8c12 | Athina Bekakou | invokeLifecycleCallbacks: function(record) { |
4256 | d0fe8c12 | Athina Bekakou | record.triggerLater('didDelete', record);
|
4257 | d0fe8c12 | Athina Bekakou | record.triggerLater('didCommit', record);
|
4258 | d0fe8c12 | Athina Bekakou | } |
4259 | d0fe8c12 | Athina Bekakou | } |
4260 | d0fe8c12 | Athina Bekakou | }, |
4261 | d0fe8c12 | Athina Bekakou | |
4262 | d0fe8c12 | Athina Bekakou | invokeLifecycleCallbacks: function(record, dirtyType) { |
4263 | d0fe8c12 | Athina Bekakou | if (dirtyType === 'created') { |
4264 | d0fe8c12 | Athina Bekakou | record.triggerLater('didCreate', record);
|
4265 | d0fe8c12 | Athina Bekakou | } else {
|
4266 | d0fe8c12 | Athina Bekakou | record.triggerLater('didUpdate', record);
|
4267 | d0fe8c12 | Athina Bekakou | } |
4268 | d0fe8c12 | Athina Bekakou | |
4269 | d0fe8c12 | Athina Bekakou | record.triggerLater('didCommit', record);
|
4270 | d0fe8c12 | Athina Bekakou | } |
4271 | d0fe8c12 | Athina Bekakou | }; |
4272 | d0fe8c12 | Athina Bekakou | |
4273 | d0fe8c12 | Athina Bekakou | function wireState(object, parent, name) { |
4274 | d0fe8c12 | Athina Bekakou | /*jshint proto:true*/
|
4275 | d0fe8c12 | Athina Bekakou | // TODO: Use Object.create and copy instead
|
4276 | d0fe8c12 | Athina Bekakou | object = mixin(parent ? Ember.create(parent) : {}, object); |
4277 | d0fe8c12 | Athina Bekakou | object.parentState = parent; |
4278 | d0fe8c12 | Athina Bekakou | object.stateName = name; |
4279 | d0fe8c12 | Athina Bekakou | |
4280 | d0fe8c12 | Athina Bekakou | for (var prop in object) { |
4281 | d0fe8c12 | Athina Bekakou | if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; } |
4282 | d0fe8c12 | Athina Bekakou | if (typeof object[prop] === 'object') { |
4283 | d0fe8c12 | Athina Bekakou | object[prop] = wireState(object[prop], object, name + "." + prop);
|
4284 | d0fe8c12 | Athina Bekakou | } |
4285 | d0fe8c12 | Athina Bekakou | } |
4286 | d0fe8c12 | Athina Bekakou | |
4287 | d0fe8c12 | Athina Bekakou | return object;
|
4288 | d0fe8c12 | Athina Bekakou | } |
4289 | d0fe8c12 | Athina Bekakou | |
4290 | d0fe8c12 | Athina Bekakou | RootState = wireState(RootState, null, "root"); |
4291 | d0fe8c12 | Athina Bekakou | |
4292 | d0fe8c12 | Athina Bekakou | DS.RootState = RootState; |
4293 | d0fe8c12 | Athina Bekakou | |
4294 | d0fe8c12 | Athina Bekakou | })(); |
4295 | d0fe8c12 | Athina Bekakou | |
4296 | d0fe8c12 | Athina Bekakou | |
4297 | d0fe8c12 | Athina Bekakou | |
4298 | d0fe8c12 | Athina Bekakou | (function() {
|
4299 | d0fe8c12 | Athina Bekakou | var get = Ember.get, isEmpty = Ember.isEmpty;
|
4300 | d0fe8c12 | Athina Bekakou | |
4301 | d0fe8c12 | Athina Bekakou | /**
|
4302 | d0fe8c12 | Athina Bekakou | @module ember-data
|
4303 | d0fe8c12 | Athina Bekakou | */
|
4304 | d0fe8c12 | Athina Bekakou | |
4305 | d0fe8c12 | Athina Bekakou | /**
|
4306 | d0fe8c12 | Athina Bekakou | Holds validation errors for a given record organized by attribute names.
|
4307 | d0fe8c12 | Athina Bekakou | |
4308 | d0fe8c12 | Athina Bekakou | @class Errors
|
4309 | d0fe8c12 | Athina Bekakou | @namespace DS
|
4310 | d0fe8c12 | Athina Bekakou | @extends Ember.Object
|
4311 | d0fe8c12 | Athina Bekakou | @uses Ember.Enumerable
|
4312 | d0fe8c12 | Athina Bekakou | @uses Ember.Evented
|
4313 | d0fe8c12 | Athina Bekakou | */
|
4314 | d0fe8c12 | Athina Bekakou | DS.Errors = Ember.Object.extend(Ember.Enumerable, Ember.Evented, { |
4315 | d0fe8c12 | Athina Bekakou | /**
|
4316 | d0fe8c12 | Athina Bekakou | Register with target handler
|
4317 | d0fe8c12 | Athina Bekakou | |
4318 | d0fe8c12 | Athina Bekakou | @method registerHandlers
|
4319 | d0fe8c12 | Athina Bekakou | @param {Object} target
|
4320 | d0fe8c12 | Athina Bekakou | @param {Function} becameInvalid
|
4321 | d0fe8c12 | Athina Bekakou | @param {Function} becameValid
|
4322 | d0fe8c12 | Athina Bekakou | */
|
4323 | d0fe8c12 | Athina Bekakou | registerHandlers: function(target, becameInvalid, becameValid) { |
4324 | d0fe8c12 | Athina Bekakou | this.on('becameInvalid', target, becameInvalid); |
4325 | d0fe8c12 | Athina Bekakou | this.on('becameValid', target, becameValid); |
4326 | d0fe8c12 | Athina Bekakou | }, |
4327 | d0fe8c12 | Athina Bekakou | |
4328 | d0fe8c12 | Athina Bekakou | /**
|
4329 | d0fe8c12 | Athina Bekakou | @property errorsByAttributeName
|
4330 | d0fe8c12 | Athina Bekakou | @type {Ember.MapWithDefault}
|
4331 | d0fe8c12 | Athina Bekakou | @private
|
4332 | d0fe8c12 | Athina Bekakou | */
|
4333 | d0fe8c12 | Athina Bekakou | errorsByAttributeName: Ember.reduceComputed("content", { |
4334 | d0fe8c12 | Athina Bekakou | initialValue: function() { |
4335 | d0fe8c12 | Athina Bekakou | return Ember.MapWithDefault.create({
|
4336 | d0fe8c12 | Athina Bekakou | defaultValue: function() { |
4337 | d0fe8c12 | Athina Bekakou | return Ember.A();
|
4338 | d0fe8c12 | Athina Bekakou | } |
4339 | d0fe8c12 | Athina Bekakou | }); |
4340 | d0fe8c12 | Athina Bekakou | }, |
4341 | d0fe8c12 | Athina Bekakou | |
4342 | d0fe8c12 | Athina Bekakou | addedItem: function(errors, error) { |
4343 | d0fe8c12 | Athina Bekakou | errors.get(error.attribute).pushObject(error); |
4344 | d0fe8c12 | Athina Bekakou | |
4345 | d0fe8c12 | Athina Bekakou | return errors;
|
4346 | d0fe8c12 | Athina Bekakou | }, |
4347 | d0fe8c12 | Athina Bekakou | |
4348 | d0fe8c12 | Athina Bekakou | removedItem: function(errors, error) { |
4349 | d0fe8c12 | Athina Bekakou | errors.get(error.attribute).removeObject(error); |
4350 | d0fe8c12 | Athina Bekakou | |
4351 | d0fe8c12 | Athina Bekakou | return errors;
|
4352 | d0fe8c12 | Athina Bekakou | } |
4353 | d0fe8c12 | Athina Bekakou | }), |
4354 | d0fe8c12 | Athina Bekakou | |
4355 | d0fe8c12 | Athina Bekakou | /**
|
4356 | d0fe8c12 | Athina Bekakou | Returns errors for a given attribute
|
4357 | d0fe8c12 | Athina Bekakou | |
4358 | d0fe8c12 | Athina Bekakou | @method errorsFor
|
4359 | d0fe8c12 | Athina Bekakou | @param {String} attribute
|
4360 | d0fe8c12 | Athina Bekakou | @returns {Array}
|
4361 | d0fe8c12 | Athina Bekakou | */
|
4362 | d0fe8c12 | Athina Bekakou | errorsFor: function(attribute) { |
4363 | d0fe8c12 | Athina Bekakou | return get(this, 'errorsByAttributeName').get(attribute); |
4364 | d0fe8c12 | Athina Bekakou | }, |
4365 | d0fe8c12 | Athina Bekakou | |
4366 | d0fe8c12 | Athina Bekakou | /**
|
4367 | d0fe8c12 | Athina Bekakou | */
|
4368 | d0fe8c12 | Athina Bekakou | messages: Ember.computed.mapBy('content', 'message'), |
4369 | d0fe8c12 | Athina Bekakou | |
4370 | d0fe8c12 | Athina Bekakou | /**
|
4371 | d0fe8c12 | Athina Bekakou | @property content
|
4372 | d0fe8c12 | Athina Bekakou | @type {Array}
|
4373 | d0fe8c12 | Athina Bekakou | @private
|
4374 | d0fe8c12 | Athina Bekakou | */
|
4375 | d0fe8c12 | Athina Bekakou | content: Ember.computed(function() { |
4376 | d0fe8c12 | Athina Bekakou | return Ember.A();
|
4377 | d0fe8c12 | Athina Bekakou | }), |
4378 | d0fe8c12 | Athina Bekakou | |
4379 | d0fe8c12 | Athina Bekakou | /**
|
4380 | d0fe8c12 | Athina Bekakou | @method unknownProperty
|
4381 | d0fe8c12 | Athina Bekakou | @private
|
4382 | d0fe8c12 | Athina Bekakou | */
|
4383 | d0fe8c12 | Athina Bekakou | unknownProperty: function(attribute) { |
4384 | d0fe8c12 | Athina Bekakou | var errors = this.errorsFor(attribute); |
4385 | d0fe8c12 | Athina Bekakou | if (isEmpty(errors)) { return null; } |
4386 | d0fe8c12 | Athina Bekakou | return errors;
|
4387 | d0fe8c12 | Athina Bekakou | }, |
4388 | d0fe8c12 | Athina Bekakou | |
4389 | d0fe8c12 | Athina Bekakou | /**
|
4390 | d0fe8c12 | Athina Bekakou | @method nextObject
|
4391 | d0fe8c12 | Athina Bekakou | @private
|
4392 | d0fe8c12 | Athina Bekakou | */
|
4393 | d0fe8c12 | Athina Bekakou | nextObject: function(index, previousObject, context) { |
4394 | d0fe8c12 | Athina Bekakou | return get(this, 'content').objectAt(index); |
4395 | d0fe8c12 | Athina Bekakou | }, |
4396 | d0fe8c12 | Athina Bekakou | |
4397 | d0fe8c12 | Athina Bekakou | /**
|
4398 | d0fe8c12 | Athina Bekakou | Total number of errors.
|
4399 | d0fe8c12 | Athina Bekakou | |
4400 | d0fe8c12 | Athina Bekakou | @property length
|
4401 | d0fe8c12 | Athina Bekakou | @type {Number}
|
4402 | d0fe8c12 | Athina Bekakou | @readOnly
|
4403 | d0fe8c12 | Athina Bekakou | */
|
4404 | d0fe8c12 | Athina Bekakou | length: Ember.computed.oneWay('content.length').readOnly(), |
4405 | d0fe8c12 | Athina Bekakou | |
4406 | d0fe8c12 | Athina Bekakou | /**
|
4407 | d0fe8c12 | Athina Bekakou | @property isEmpty
|
4408 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4409 | d0fe8c12 | Athina Bekakou | @readOnly
|
4410 | d0fe8c12 | Athina Bekakou | */
|
4411 | d0fe8c12 | Athina Bekakou | isEmpty: Ember.computed.not('length').readOnly(), |
4412 | d0fe8c12 | Athina Bekakou | |
4413 | d0fe8c12 | Athina Bekakou | /**
|
4414 | d0fe8c12 | Athina Bekakou | Adds error messages to a given attribute and sends
|
4415 | d0fe8c12 | Athina Bekakou | `becameInvalid` event to the record.
|
4416 | d0fe8c12 | Athina Bekakou | |
4417 | d0fe8c12 | Athina Bekakou | @method add
|
4418 | d0fe8c12 | Athina Bekakou | @param {String} attribute
|
4419 | d0fe8c12 | Athina Bekakou | @param {Array|String} messages
|
4420 | d0fe8c12 | Athina Bekakou | */
|
4421 | d0fe8c12 | Athina Bekakou | add: function(attribute, messages) { |
4422 | d0fe8c12 | Athina Bekakou | var wasEmpty = get(this, 'isEmpty'); |
4423 | d0fe8c12 | Athina Bekakou | |
4424 | d0fe8c12 | Athina Bekakou | messages = this._findOrCreateMessages(attribute, messages);
|
4425 | d0fe8c12 | Athina Bekakou | get(this, 'content').addObjects(messages); |
4426 | d0fe8c12 | Athina Bekakou | |
4427 | d0fe8c12 | Athina Bekakou | this.notifyPropertyChange(attribute);
|
4428 | d0fe8c12 | Athina Bekakou | this.enumerableContentDidChange();
|
4429 | d0fe8c12 | Athina Bekakou | |
4430 | d0fe8c12 | Athina Bekakou | if (wasEmpty && !get(this, 'isEmpty')) { |
4431 | d0fe8c12 | Athina Bekakou | this.trigger('becameInvalid'); |
4432 | d0fe8c12 | Athina Bekakou | } |
4433 | d0fe8c12 | Athina Bekakou | }, |
4434 | d0fe8c12 | Athina Bekakou | |
4435 | d0fe8c12 | Athina Bekakou | /**
|
4436 | d0fe8c12 | Athina Bekakou | @method _findOrCreateMessages
|
4437 | d0fe8c12 | Athina Bekakou | @private
|
4438 | d0fe8c12 | Athina Bekakou | */
|
4439 | d0fe8c12 | Athina Bekakou | _findOrCreateMessages: function(attribute, messages) { |
4440 | d0fe8c12 | Athina Bekakou | var errors = this.errorsFor(attribute); |
4441 | d0fe8c12 | Athina Bekakou | |
4442 | d0fe8c12 | Athina Bekakou | return Ember.makeArray(messages).map(function(message) { |
4443 | d0fe8c12 | Athina Bekakou | return errors.findBy('message', message) || { |
4444 | d0fe8c12 | Athina Bekakou | attribute: attribute,
|
4445 | d0fe8c12 | Athina Bekakou | message: message
|
4446 | d0fe8c12 | Athina Bekakou | }; |
4447 | d0fe8c12 | Athina Bekakou | }); |
4448 | d0fe8c12 | Athina Bekakou | }, |
4449 | d0fe8c12 | Athina Bekakou | |
4450 | d0fe8c12 | Athina Bekakou | /**
|
4451 | d0fe8c12 | Athina Bekakou | Removes all error messages from the given attribute and sends
|
4452 | d0fe8c12 | Athina Bekakou | `becameValid` event to the record if there no more errors left.
|
4453 | d0fe8c12 | Athina Bekakou | |
4454 | d0fe8c12 | Athina Bekakou | @method remove
|
4455 | d0fe8c12 | Athina Bekakou | @param {String} attribute
|
4456 | d0fe8c12 | Athina Bekakou | */
|
4457 | d0fe8c12 | Athina Bekakou | remove: function(attribute) { |
4458 | d0fe8c12 | Athina Bekakou | if (get(this, 'isEmpty')) { return; } |
4459 | d0fe8c12 | Athina Bekakou | |
4460 | d0fe8c12 | Athina Bekakou | var content = get(this, 'content').rejectBy('attribute', attribute); |
4461 | d0fe8c12 | Athina Bekakou | get(this, 'content').setObjects(content); |
4462 | d0fe8c12 | Athina Bekakou | |
4463 | d0fe8c12 | Athina Bekakou | this.notifyPropertyChange(attribute);
|
4464 | d0fe8c12 | Athina Bekakou | this.enumerableContentDidChange();
|
4465 | d0fe8c12 | Athina Bekakou | |
4466 | d0fe8c12 | Athina Bekakou | if (get(this, 'isEmpty')) { |
4467 | d0fe8c12 | Athina Bekakou | this.trigger('becameValid'); |
4468 | d0fe8c12 | Athina Bekakou | } |
4469 | d0fe8c12 | Athina Bekakou | }, |
4470 | d0fe8c12 | Athina Bekakou | |
4471 | d0fe8c12 | Athina Bekakou | /**
|
4472 | d0fe8c12 | Athina Bekakou | Removes all error messages and sends `becameValid` event
|
4473 | d0fe8c12 | Athina Bekakou | to the record.
|
4474 | d0fe8c12 | Athina Bekakou | |
4475 | d0fe8c12 | Athina Bekakou | @method clear
|
4476 | d0fe8c12 | Athina Bekakou | */
|
4477 | d0fe8c12 | Athina Bekakou | clear: function() { |
4478 | d0fe8c12 | Athina Bekakou | if (get(this, 'isEmpty')) { return; } |
4479 | d0fe8c12 | Athina Bekakou | |
4480 | d0fe8c12 | Athina Bekakou | get(this, 'content').clear(); |
4481 | d0fe8c12 | Athina Bekakou | this.enumerableContentDidChange();
|
4482 | d0fe8c12 | Athina Bekakou | |
4483 | d0fe8c12 | Athina Bekakou | this.trigger('becameValid'); |
4484 | d0fe8c12 | Athina Bekakou | }, |
4485 | d0fe8c12 | Athina Bekakou | |
4486 | d0fe8c12 | Athina Bekakou | /**
|
4487 | d0fe8c12 | Athina Bekakou | Checks if there is error messages for the given attribute.
|
4488 | d0fe8c12 | Athina Bekakou | |
4489 | d0fe8c12 | Athina Bekakou | @method has
|
4490 | d0fe8c12 | Athina Bekakou | @param {String} attribute
|
4491 | d0fe8c12 | Athina Bekakou | @returns {Boolean} true if there some errors on given attribute
|
4492 | d0fe8c12 | Athina Bekakou | */
|
4493 | d0fe8c12 | Athina Bekakou | has: function(attribute) { |
4494 | d0fe8c12 | Athina Bekakou | return !isEmpty(this.errorsFor(attribute)); |
4495 | d0fe8c12 | Athina Bekakou | } |
4496 | d0fe8c12 | Athina Bekakou | }); |
4497 | d0fe8c12 | Athina Bekakou | |
4498 | d0fe8c12 | Athina Bekakou | })(); |
4499 | d0fe8c12 | Athina Bekakou | |
4500 | d0fe8c12 | Athina Bekakou | |
4501 | d0fe8c12 | Athina Bekakou | |
4502 | d0fe8c12 | Athina Bekakou | (function() {
|
4503 | d0fe8c12 | Athina Bekakou | /**
|
4504 | d0fe8c12 | Athina Bekakou | @module ember-data
|
4505 | d0fe8c12 | Athina Bekakou | */
|
4506 | d0fe8c12 | Athina Bekakou | |
4507 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set,
|
4508 | d0fe8c12 | Athina Bekakou | merge = Ember.merge, once = Ember.run.once; |
4509 | d0fe8c12 | Athina Bekakou | |
4510 | d0fe8c12 | Athina Bekakou | var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) { |
4511 | d0fe8c12 | Athina Bekakou | return get(get(this, 'currentState'), key); |
4512 | d0fe8c12 | Athina Bekakou | }).readOnly(); |
4513 | d0fe8c12 | Athina Bekakou | |
4514 | d0fe8c12 | Athina Bekakou | /**
|
4515 | d0fe8c12 | Athina Bekakou | |
4516 | d0fe8c12 | Athina Bekakou | The model class that all Ember Data records descend from.
|
4517 | d0fe8c12 | Athina Bekakou | |
4518 | d0fe8c12 | Athina Bekakou | @class Model
|
4519 | d0fe8c12 | Athina Bekakou | @namespace DS
|
4520 | d0fe8c12 | Athina Bekakou | @extends Ember.Object
|
4521 | d0fe8c12 | Athina Bekakou | @uses Ember.Evented
|
4522 | d0fe8c12 | Athina Bekakou | */
|
4523 | d0fe8c12 | Athina Bekakou | DS.Model = Ember.Object.extend(Ember.Evented, { |
4524 | d0fe8c12 | Athina Bekakou | /**
|
4525 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `empty`
|
4526 | d0fe8c12 | Athina Bekakou | state. Empty is the first state all records enter after they have
|
4527 | d0fe8c12 | Athina Bekakou | been created. Most records created by the store will quickly
|
4528 | d0fe8c12 | Athina Bekakou | transition to the `loading` state if data needs to be fetched from
|
4529 | d0fe8c12 | Athina Bekakou | the server or the `created` state if the record is created on the
|
4530 | d0fe8c12 | Athina Bekakou | client. A record can also enter the empty state if the adapter is
|
4531 | d0fe8c12 | Athina Bekakou | unable to locate the record.
|
4532 | d0fe8c12 | Athina Bekakou | |
4533 | d0fe8c12 | Athina Bekakou | @property isEmpty
|
4534 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4535 | d0fe8c12 | Athina Bekakou | @readOnly
|
4536 | d0fe8c12 | Athina Bekakou | */
|
4537 | d0fe8c12 | Athina Bekakou | isEmpty: retrieveFromCurrentState,
|
4538 | d0fe8c12 | Athina Bekakou | /**
|
4539 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `loading` state. A
|
4540 | d0fe8c12 | Athina Bekakou | record enters this state when the store askes the adapter for its
|
4541 | d0fe8c12 | Athina Bekakou | data. It remains in this state until the adapter provides the
|
4542 | d0fe8c12 | Athina Bekakou | requested data.
|
4543 | d0fe8c12 | Athina Bekakou | |
4544 | d0fe8c12 | Athina Bekakou | @property isLoading
|
4545 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4546 | d0fe8c12 | Athina Bekakou | @readOnly
|
4547 | d0fe8c12 | Athina Bekakou | */
|
4548 | d0fe8c12 | Athina Bekakou | isLoading: retrieveFromCurrentState,
|
4549 | d0fe8c12 | Athina Bekakou | /**
|
4550 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `loaded` state. A
|
4551 | d0fe8c12 | Athina Bekakou | record enters this state when its data is populated. Most of a
|
4552 | d0fe8c12 | Athina Bekakou | record's lifecycle is spent inside substates of the `loaded`
|
4553 | d0fe8c12 | Athina Bekakou | state.
|
4554 | d0fe8c12 | Athina Bekakou | |
4555 | d0fe8c12 | Athina Bekakou | Example
|
4556 | d0fe8c12 | Athina Bekakou | |
4557 | d0fe8c12 | Athina Bekakou | ```javascript
|
4558 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4559 | d0fe8c12 | Athina Bekakou | record.get('isLoaded'); // true
|
4560 | d0fe8c12 | Athina Bekakou | |
4561 | d0fe8c12 | Athina Bekakou | store.find('model', 1).then(function(model) {
|
4562 | d0fe8c12 | Athina Bekakou | model.get('isLoaded'); // true
|
4563 | d0fe8c12 | Athina Bekakou | });
|
4564 | d0fe8c12 | Athina Bekakou | ```
|
4565 | d0fe8c12 | Athina Bekakou | |
4566 | d0fe8c12 | Athina Bekakou | @property isLoaded
|
4567 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4568 | d0fe8c12 | Athina Bekakou | @readOnly
|
4569 | d0fe8c12 | Athina Bekakou | */
|
4570 | d0fe8c12 | Athina Bekakou | isLoaded: retrieveFromCurrentState,
|
4571 | d0fe8c12 | Athina Bekakou | /**
|
4572 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `dirty` state. The
|
4573 | d0fe8c12 | Athina Bekakou | record has local changes that have not yet been saved by the
|
4574 | d0fe8c12 | Athina Bekakou | adapter. This includes records that have been created (but not yet
|
4575 | d0fe8c12 | Athina Bekakou | saved) or deleted.
|
4576 | d0fe8c12 | Athina Bekakou | |
4577 | d0fe8c12 | Athina Bekakou | Example
|
4578 | d0fe8c12 | Athina Bekakou | |
4579 | d0fe8c12 | Athina Bekakou | ```javascript
|
4580 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4581 | d0fe8c12 | Athina Bekakou | record.get('isDirty'); // true
|
4582 | d0fe8c12 | Athina Bekakou | |
4583 | d0fe8c12 | Athina Bekakou | store.find('model', 1).then(function(model) {
|
4584 | d0fe8c12 | Athina Bekakou | model.get('isDirty'); // false
|
4585 | d0fe8c12 | Athina Bekakou | model.set('foo', 'some value');
|
4586 | d0fe8c12 | Athina Bekakou | model.set('isDirty'); // true
|
4587 | d0fe8c12 | Athina Bekakou | });
|
4588 | d0fe8c12 | Athina Bekakou | ```
|
4589 | d0fe8c12 | Athina Bekakou | |
4590 | d0fe8c12 | Athina Bekakou | @property isDirty
|
4591 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4592 | d0fe8c12 | Athina Bekakou | @readOnly
|
4593 | d0fe8c12 | Athina Bekakou | */
|
4594 | d0fe8c12 | Athina Bekakou | isDirty: retrieveFromCurrentState,
|
4595 | d0fe8c12 | Athina Bekakou | /**
|
4596 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `saving` state. A
|
4597 | d0fe8c12 | Athina Bekakou | record enters the saving state when `save` is called, but the
|
4598 | d0fe8c12 | Athina Bekakou | adapter has not yet acknowledged that the changes have been
|
4599 | d0fe8c12 | Athina Bekakou | persisted to the backend.
|
4600 | d0fe8c12 | Athina Bekakou | |
4601 | d0fe8c12 | Athina Bekakou | Example
|
4602 | d0fe8c12 | Athina Bekakou | |
4603 | d0fe8c12 | Athina Bekakou | ```javascript
|
4604 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4605 | d0fe8c12 | Athina Bekakou | record.get('isSaving'); // false
|
4606 | d0fe8c12 | Athina Bekakou | var promise = record.save();
|
4607 | d0fe8c12 | Athina Bekakou | record.get('isSaving'); // true
|
4608 | d0fe8c12 | Athina Bekakou | promise.then(function() {
|
4609 | d0fe8c12 | Athina Bekakou | record.get('isSaving'); // false
|
4610 | d0fe8c12 | Athina Bekakou | });
|
4611 | d0fe8c12 | Athina Bekakou | ```
|
4612 | d0fe8c12 | Athina Bekakou | |
4613 | d0fe8c12 | Athina Bekakou | @property isSaving
|
4614 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4615 | d0fe8c12 | Athina Bekakou | @readOnly
|
4616 | d0fe8c12 | Athina Bekakou | */
|
4617 | d0fe8c12 | Athina Bekakou | isSaving: retrieveFromCurrentState,
|
4618 | d0fe8c12 | Athina Bekakou | /**
|
4619 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `deleted` state
|
4620 | d0fe8c12 | Athina Bekakou | and has been marked for deletion. When `isDeleted` is true and
|
4621 | d0fe8c12 | Athina Bekakou | `isDirty` is true, the record is deleted locally but the deletion
|
4622 | d0fe8c12 | Athina Bekakou | was not yet persisted. When `isSaving` is true, the change is
|
4623 | d0fe8c12 | Athina Bekakou | in-flight. When both `isDirty` and `isSaving` are false, the
|
4624 | d0fe8c12 | Athina Bekakou | change has persisted.
|
4625 | d0fe8c12 | Athina Bekakou | |
4626 | d0fe8c12 | Athina Bekakou | Example
|
4627 | d0fe8c12 | Athina Bekakou | |
4628 | d0fe8c12 | Athina Bekakou | ```javascript
|
4629 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4630 | d0fe8c12 | Athina Bekakou | record.get('isDeleted'); // false
|
4631 | d0fe8c12 | Athina Bekakou | record.deleteRecord();
|
4632 | d0fe8c12 | Athina Bekakou | record.get('isDeleted'); // true
|
4633 | d0fe8c12 | Athina Bekakou | ```
|
4634 | d0fe8c12 | Athina Bekakou | |
4635 | d0fe8c12 | Athina Bekakou | @property isDeleted
|
4636 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4637 | d0fe8c12 | Athina Bekakou | @readOnly
|
4638 | d0fe8c12 | Athina Bekakou | */
|
4639 | d0fe8c12 | Athina Bekakou | isDeleted: retrieveFromCurrentState,
|
4640 | d0fe8c12 | Athina Bekakou | /**
|
4641 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `new` state. A
|
4642 | d0fe8c12 | Athina Bekakou | record will be in the `new` state when it has been created on the
|
4643 | d0fe8c12 | Athina Bekakou | client and the adapter has not yet report that it was successfully
|
4644 | d0fe8c12 | Athina Bekakou | saved.
|
4645 | d0fe8c12 | Athina Bekakou | |
4646 | d0fe8c12 | Athina Bekakou | Example
|
4647 | d0fe8c12 | Athina Bekakou | |
4648 | d0fe8c12 | Athina Bekakou | ```javascript
|
4649 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4650 | d0fe8c12 | Athina Bekakou | record.get('isNew'); // true
|
4651 | d0fe8c12 | Athina Bekakou | |
4652 | d0fe8c12 | Athina Bekakou | store.find('model', 1).then(function(model) {
|
4653 | d0fe8c12 | Athina Bekakou | model.get('isNew'); // false
|
4654 | d0fe8c12 | Athina Bekakou | });
|
4655 | d0fe8c12 | Athina Bekakou | ```
|
4656 | d0fe8c12 | Athina Bekakou | |
4657 | d0fe8c12 | Athina Bekakou | @property isNew
|
4658 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4659 | d0fe8c12 | Athina Bekakou | @readOnly
|
4660 | d0fe8c12 | Athina Bekakou | */
|
4661 | d0fe8c12 | Athina Bekakou | isNew: retrieveFromCurrentState,
|
4662 | d0fe8c12 | Athina Bekakou | /**
|
4663 | d0fe8c12 | Athina Bekakou | If this property is `true` the record is in the `valid` state. A
|
4664 | d0fe8c12 | Athina Bekakou | record will be in the `valid` state when no client-side
|
4665 | d0fe8c12 | Athina Bekakou | validations have failed and the adapter did not report any
|
4666 | d0fe8c12 | Athina Bekakou | server-side validation failures.
|
4667 | d0fe8c12 | Athina Bekakou | |
4668 | d0fe8c12 | Athina Bekakou | @property isValid
|
4669 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4670 | d0fe8c12 | Athina Bekakou | @readOnly
|
4671 | d0fe8c12 | Athina Bekakou | */
|
4672 | d0fe8c12 | Athina Bekakou | isValid: retrieveFromCurrentState,
|
4673 | d0fe8c12 | Athina Bekakou | /**
|
4674 | d0fe8c12 | Athina Bekakou | If the record is in the dirty state this property will report what
|
4675 | d0fe8c12 | Athina Bekakou | kind of change has caused it to move into the dirty
|
4676 | d0fe8c12 | Athina Bekakou | state. Possible values are:
|
4677 | d0fe8c12 | Athina Bekakou | |
4678 | d0fe8c12 | Athina Bekakou | - `created` The record has been created by the client and not yet saved to the adapter.
|
4679 | d0fe8c12 | Athina Bekakou | - `updated` The record has been updated by the client and not yet saved to the adapter.
|
4680 | d0fe8c12 | Athina Bekakou | - `deleted` The record has been deleted by the client and not yet saved to the adapter.
|
4681 | d0fe8c12 | Athina Bekakou | |
4682 | d0fe8c12 | Athina Bekakou | Example
|
4683 | d0fe8c12 | Athina Bekakou | |
4684 | d0fe8c12 | Athina Bekakou | ```javascript
|
4685 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4686 | d0fe8c12 | Athina Bekakou | record.get('dirtyType'); // 'created'
|
4687 | d0fe8c12 | Athina Bekakou | ```
|
4688 | d0fe8c12 | Athina Bekakou | |
4689 | d0fe8c12 | Athina Bekakou | @property dirtyType
|
4690 | d0fe8c12 | Athina Bekakou | @type {String}
|
4691 | d0fe8c12 | Athina Bekakou | @readOnly
|
4692 | d0fe8c12 | Athina Bekakou | */
|
4693 | d0fe8c12 | Athina Bekakou | dirtyType: retrieveFromCurrentState,
|
4694 | d0fe8c12 | Athina Bekakou | |
4695 | d0fe8c12 | Athina Bekakou | /**
|
4696 | d0fe8c12 | Athina Bekakou | If `true` the adapter reported that it was unable to save local
|
4697 | d0fe8c12 | Athina Bekakou | changes to the backend. This may also result in the record having
|
4698 | d0fe8c12 | Athina Bekakou | its `isValid` property become false if the adapter reported that
|
4699 | d0fe8c12 | Athina Bekakou | server-side validations failed.
|
4700 | d0fe8c12 | Athina Bekakou | |
4701 | d0fe8c12 | Athina Bekakou | Example
|
4702 | d0fe8c12 | Athina Bekakou | |
4703 | d0fe8c12 | Athina Bekakou | ```javascript
|
4704 | d0fe8c12 | Athina Bekakou | record.get('isError'); // false
|
4705 | d0fe8c12 | Athina Bekakou | record.set('foo', 'invalid value');
|
4706 | d0fe8c12 | Athina Bekakou | record.save().then(null, function() {
|
4707 | d0fe8c12 | Athina Bekakou | record.get('isError'); // true
|
4708 | d0fe8c12 | Athina Bekakou | });
|
4709 | d0fe8c12 | Athina Bekakou | ```
|
4710 | d0fe8c12 | Athina Bekakou | |
4711 | d0fe8c12 | Athina Bekakou | @property isError
|
4712 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4713 | d0fe8c12 | Athina Bekakou | @readOnly
|
4714 | d0fe8c12 | Athina Bekakou | */
|
4715 | d0fe8c12 | Athina Bekakou | isError: false, |
4716 | d0fe8c12 | Athina Bekakou | /**
|
4717 | d0fe8c12 | Athina Bekakou | If `true` the store is attempting to reload the record form the adapter.
|
4718 | d0fe8c12 | Athina Bekakou | |
4719 | d0fe8c12 | Athina Bekakou | Example
|
4720 | d0fe8c12 | Athina Bekakou | |
4721 | d0fe8c12 | Athina Bekakou | ```javascript
|
4722 | d0fe8c12 | Athina Bekakou | record.get('isReloading'); // false
|
4723 | d0fe8c12 | Athina Bekakou | record.reload();
|
4724 | d0fe8c12 | Athina Bekakou | record.get('isReloading'); // true
|
4725 | d0fe8c12 | Athina Bekakou | ```
|
4726 | d0fe8c12 | Athina Bekakou | |
4727 | d0fe8c12 | Athina Bekakou | @property isReloading
|
4728 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
4729 | d0fe8c12 | Athina Bekakou | @readOnly
|
4730 | d0fe8c12 | Athina Bekakou | */
|
4731 | d0fe8c12 | Athina Bekakou | isReloading: false, |
4732 | d0fe8c12 | Athina Bekakou | |
4733 | d0fe8c12 | Athina Bekakou | /**
|
4734 | d0fe8c12 | Athina Bekakou | The `clientId` property is a transient numerical identifier
|
4735 | d0fe8c12 | Athina Bekakou | generated at runtime by the data store. It is important
|
4736 | d0fe8c12 | Athina Bekakou | primarily because newly created objects may not yet have an
|
4737 | d0fe8c12 | Athina Bekakou | externally generated id.
|
4738 | d0fe8c12 | Athina Bekakou | |
4739 | d0fe8c12 | Athina Bekakou | @property clientId
|
4740 | d0fe8c12 | Athina Bekakou | @private
|
4741 | d0fe8c12 | Athina Bekakou | @type {Number|String}
|
4742 | d0fe8c12 | Athina Bekakou | */
|
4743 | d0fe8c12 | Athina Bekakou | clientId: null, |
4744 | d0fe8c12 | Athina Bekakou | /**
|
4745 | d0fe8c12 | Athina Bekakou | All ember models have an id property. This is an identifier
|
4746 | d0fe8c12 | Athina Bekakou | managed by an external source. These are always coerced to be
|
4747 | d0fe8c12 | Athina Bekakou | strings before being used internally. Note when declaring the
|
4748 | d0fe8c12 | Athina Bekakou | attributes for a model it is an error to declare an id
|
4749 | d0fe8c12 | Athina Bekakou | attribute.
|
4750 | d0fe8c12 | Athina Bekakou | |
4751 | d0fe8c12 | Athina Bekakou | ```javascript
|
4752 | d0fe8c12 | Athina Bekakou | var record = store.createRecord(App.Model);
|
4753 | d0fe8c12 | Athina Bekakou | record.get('id'); // null
|
4754 | d0fe8c12 | Athina Bekakou | |
4755 | d0fe8c12 | Athina Bekakou | store.find('model', 1).then(function(model) {
|
4756 | d0fe8c12 | Athina Bekakou | model.get('id'); // '1'
|
4757 | d0fe8c12 | Athina Bekakou | });
|
4758 | d0fe8c12 | Athina Bekakou | ```
|
4759 | d0fe8c12 | Athina Bekakou | |
4760 | d0fe8c12 | Athina Bekakou | @property id
|
4761 | d0fe8c12 | Athina Bekakou | @type {String}
|
4762 | d0fe8c12 | Athina Bekakou | */
|
4763 | d0fe8c12 | Athina Bekakou | id: null, |
4764 | d0fe8c12 | Athina Bekakou | transaction: null, |
4765 | d0fe8c12 | Athina Bekakou | /**
|
4766 | d0fe8c12 | Athina Bekakou | @property currentState
|
4767 | d0fe8c12 | Athina Bekakou | @private
|
4768 | d0fe8c12 | Athina Bekakou | @type {Object}
|
4769 | d0fe8c12 | Athina Bekakou | */
|
4770 | d0fe8c12 | Athina Bekakou | currentState: null, |
4771 | d0fe8c12 | Athina Bekakou | /**
|
4772 | d0fe8c12 | Athina Bekakou | When the record is in the `invalid` state this object will contain
|
4773 | d0fe8c12 | Athina Bekakou | any errors returned by the adapter. When present the errors hash
|
4774 | d0fe8c12 | Athina Bekakou | typically contains keys coresponding to the invalid property names
|
4775 | d0fe8c12 | Athina Bekakou | and values which are an array of error messages.
|
4776 | d0fe8c12 | Athina Bekakou | |
4777 | d0fe8c12 | Athina Bekakou | ```javascript
|
4778 | d0fe8c12 | Athina Bekakou | record.get('errors.length'); // 0
|
4779 | d0fe8c12 | Athina Bekakou | record.set('foo', 'invalid value');
|
4780 | d0fe8c12 | Athina Bekakou | record.save().then(null, function() {
|
4781 | d0fe8c12 | Athina Bekakou | record.get('errors').get('foo'); // ['foo should be a number.']
|
4782 | d0fe8c12 | Athina Bekakou | });
|
4783 | d0fe8c12 | Athina Bekakou | ```
|
4784 | d0fe8c12 | Athina Bekakou | |
4785 | d0fe8c12 | Athina Bekakou | @property errors
|
4786 | d0fe8c12 | Athina Bekakou | @type {Object}
|
4787 | d0fe8c12 | Athina Bekakou | */
|
4788 | d0fe8c12 | Athina Bekakou | errors: null, |
4789 | d0fe8c12 | Athina Bekakou | |
4790 | d0fe8c12 | Athina Bekakou | /**
|
4791 | d0fe8c12 | Athina Bekakou | Create a JSON representation of the record, using the serialization
|
4792 | d0fe8c12 | Athina Bekakou | strategy of the store's adapter.
|
4793 | d0fe8c12 | Athina Bekakou | |
4794 | d0fe8c12 | Athina Bekakou | `serialize` takes an optional hash as a parameter, currently
|
4795 | d0fe8c12 | Athina Bekakou | supported options are:
|
4796 | d0fe8c12 | Athina Bekakou | |
4797 | d0fe8c12 | Athina Bekakou | - `includeId`: `true` if the record's ID should be included in the
|
4798 | d0fe8c12 | Athina Bekakou | JSON representation.
|
4799 | d0fe8c12 | Athina Bekakou | |
4800 | d0fe8c12 | Athina Bekakou | @method serialize
|
4801 | d0fe8c12 | Athina Bekakou | @param {Object} options
|
4802 | d0fe8c12 | Athina Bekakou | @returns {Object} an object whose values are primitive JSON values only
|
4803 | d0fe8c12 | Athina Bekakou | */
|
4804 | d0fe8c12 | Athina Bekakou | serialize: function(options) { |
4805 | d0fe8c12 | Athina Bekakou | var store = get(this, 'store'); |
4806 | d0fe8c12 | Athina Bekakou | return store.serialize(this, options); |
4807 | d0fe8c12 | Athina Bekakou | }, |
4808 | d0fe8c12 | Athina Bekakou | |
4809 | d0fe8c12 | Athina Bekakou | /**
|
4810 | d0fe8c12 | Athina Bekakou | Use [DS.JSONSerializer](DS.JSONSerializer.html) to
|
4811 | d0fe8c12 | Athina Bekakou | get the JSON representation of a record.
|
4812 | d0fe8c12 | Athina Bekakou | |
4813 | d0fe8c12 | Athina Bekakou | `toJSON` takes an optional hash as a parameter, currently
|
4814 | d0fe8c12 | Athina Bekakou | supported options are:
|
4815 | d0fe8c12 | Athina Bekakou | |
4816 | d0fe8c12 | Athina Bekakou | - `includeId`: `true` if the record's ID should be included in the
|
4817 | d0fe8c12 | Athina Bekakou | JSON representation.
|
4818 | d0fe8c12 | Athina Bekakou | |
4819 | d0fe8c12 | Athina Bekakou | @method toJSON
|
4820 | d0fe8c12 | Athina Bekakou | @param {Object} options
|
4821 | d0fe8c12 | Athina Bekakou | @returns {Object} A JSON representation of the object.
|
4822 | d0fe8c12 | Athina Bekakou | */
|
4823 | d0fe8c12 | Athina Bekakou | toJSON: function(options) { |
4824 | d0fe8c12 | Athina Bekakou | // container is for lazy transform lookups
|
4825 | d0fe8c12 | Athina Bekakou | var serializer = DS.JSONSerializer.create({ container: this.container }); |
4826 | d0fe8c12 | Athina Bekakou | return serializer.serialize(this, options); |
4827 | d0fe8c12 | Athina Bekakou | }, |
4828 | d0fe8c12 | Athina Bekakou | |
4829 | d0fe8c12 | Athina Bekakou | /**
|
4830 | d0fe8c12 | Athina Bekakou | Fired when the record is loaded from the server.
|
4831 | d0fe8c12 | Athina Bekakou | |
4832 | d0fe8c12 | Athina Bekakou | @event didLoad
|
4833 | d0fe8c12 | Athina Bekakou | */
|
4834 | d0fe8c12 | Athina Bekakou | didLoad: Ember.K,
|
4835 | d0fe8c12 | Athina Bekakou | |
4836 | d0fe8c12 | Athina Bekakou | /**
|
4837 | d0fe8c12 | Athina Bekakou | Fired when the record is updated.
|
4838 | d0fe8c12 | Athina Bekakou | |
4839 | d0fe8c12 | Athina Bekakou | @event didUpdate
|
4840 | d0fe8c12 | Athina Bekakou | */
|
4841 | d0fe8c12 | Athina Bekakou | didUpdate: Ember.K,
|
4842 | d0fe8c12 | Athina Bekakou | |
4843 | d0fe8c12 | Athina Bekakou | /**
|
4844 | d0fe8c12 | Athina Bekakou | Fired when the record is created.
|
4845 | d0fe8c12 | Athina Bekakou | |
4846 | d0fe8c12 | Athina Bekakou | @event didCreate
|
4847 | d0fe8c12 | Athina Bekakou | */
|
4848 | d0fe8c12 | Athina Bekakou | didCreate: Ember.K,
|
4849 | d0fe8c12 | Athina Bekakou | |
4850 | d0fe8c12 | Athina Bekakou | /**
|
4851 | d0fe8c12 | Athina Bekakou | Fired when the record is deleted.
|
4852 | d0fe8c12 | Athina Bekakou | |
4853 | d0fe8c12 | Athina Bekakou | @event didDelete
|
4854 | d0fe8c12 | Athina Bekakou | */
|
4855 | d0fe8c12 | Athina Bekakou | didDelete: Ember.K,
|
4856 | d0fe8c12 | Athina Bekakou | |
4857 | d0fe8c12 | Athina Bekakou | /**
|
4858 | d0fe8c12 | Athina Bekakou | Fired when the record becomes invalid.
|
4859 | d0fe8c12 | Athina Bekakou | |
4860 | d0fe8c12 | Athina Bekakou | @event becameInvalid
|
4861 | d0fe8c12 | Athina Bekakou | */
|
4862 | d0fe8c12 | Athina Bekakou | becameInvalid: Ember.K,
|
4863 | d0fe8c12 | Athina Bekakou | |
4864 | d0fe8c12 | Athina Bekakou | /**
|
4865 | d0fe8c12 | Athina Bekakou | Fired when the record enters the error state.
|
4866 | d0fe8c12 | Athina Bekakou | |
4867 | d0fe8c12 | Athina Bekakou | @event becameError
|
4868 | d0fe8c12 | Athina Bekakou | */
|
4869 | d0fe8c12 | Athina Bekakou | becameError: Ember.K,
|
4870 | d0fe8c12 | Athina Bekakou | |
4871 | d0fe8c12 | Athina Bekakou | /**
|
4872 | d0fe8c12 | Athina Bekakou | @property data
|
4873 | d0fe8c12 | Athina Bekakou | @private
|
4874 | d0fe8c12 | Athina Bekakou | @type {Object}
|
4875 | d0fe8c12 | Athina Bekakou | */
|
4876 | d0fe8c12 | Athina Bekakou | data: Ember.computed(function() { |
4877 | d0fe8c12 | Athina Bekakou | this._data = this._data || {}; |
4878 | d0fe8c12 | Athina Bekakou | return this._data; |
4879 | d0fe8c12 | Athina Bekakou | }).property(), |
4880 | d0fe8c12 | Athina Bekakou | |
4881 | d0fe8c12 | Athina Bekakou | _data: null, |
4882 | d0fe8c12 | Athina Bekakou | |
4883 | d0fe8c12 | Athina Bekakou | init: function() { |
4884 | d0fe8c12 | Athina Bekakou | set(this, 'currentState', DS.RootState.empty); |
4885 | d0fe8c12 | Athina Bekakou | var errors = DS.Errors.create();
|
4886 | d0fe8c12 | Athina Bekakou | errors.registerHandlers(this, function() { |
4887 | d0fe8c12 | Athina Bekakou | this.send('becameInvalid'); |
4888 | d0fe8c12 | Athina Bekakou | }, function() {
|
4889 | d0fe8c12 | Athina Bekakou | this.send('becameValid'); |
4890 | d0fe8c12 | Athina Bekakou | }); |
4891 | d0fe8c12 | Athina Bekakou | set(this, 'errors', errors); |
4892 | d0fe8c12 | Athina Bekakou | this._super();
|
4893 | d0fe8c12 | Athina Bekakou | this._setup();
|
4894 | d0fe8c12 | Athina Bekakou | }, |
4895 | d0fe8c12 | Athina Bekakou | |
4896 | d0fe8c12 | Athina Bekakou | _setup: function() { |
4897 | d0fe8c12 | Athina Bekakou | this._changesToSync = {};
|
4898 | d0fe8c12 | Athina Bekakou | this._deferredTriggers = [];
|
4899 | d0fe8c12 | Athina Bekakou | this._data = {};
|
4900 | d0fe8c12 | Athina Bekakou | this._attributes = {};
|
4901 | d0fe8c12 | Athina Bekakou | this._inFlightAttributes = {};
|
4902 | d0fe8c12 | Athina Bekakou | this._relationships = {};
|
4903 | d0fe8c12 | Athina Bekakou | }, |
4904 | d0fe8c12 | Athina Bekakou | |
4905 | d0fe8c12 | Athina Bekakou | /**
|
4906 | d0fe8c12 | Athina Bekakou | @method send
|
4907 | d0fe8c12 | Athina Bekakou | @private
|
4908 | d0fe8c12 | Athina Bekakou | @param {String} name
|
4909 | d0fe8c12 | Athina Bekakou | @param {Object} context
|
4910 | d0fe8c12 | Athina Bekakou | */
|
4911 | d0fe8c12 | Athina Bekakou | send: function(name, context) { |
4912 | d0fe8c12 | Athina Bekakou | var currentState = get(this, 'currentState'); |
4913 | d0fe8c12 | Athina Bekakou | |
4914 | d0fe8c12 | Athina Bekakou | if (!currentState[name]) {
|
4915 | d0fe8c12 | Athina Bekakou | this._unhandledEvent(currentState, name, context);
|
4916 | d0fe8c12 | Athina Bekakou | } |
4917 | d0fe8c12 | Athina Bekakou | |
4918 | d0fe8c12 | Athina Bekakou | return currentState[name](this, context); |
4919 | d0fe8c12 | Athina Bekakou | }, |
4920 | d0fe8c12 | Athina Bekakou | |
4921 | d0fe8c12 | Athina Bekakou | /**
|
4922 | d0fe8c12 | Athina Bekakou | @method transitionTo
|
4923 | d0fe8c12 | Athina Bekakou | @private
|
4924 | d0fe8c12 | Athina Bekakou | @param {String} name
|
4925 | d0fe8c12 | Athina Bekakou | */
|
4926 | d0fe8c12 | Athina Bekakou | transitionTo: function(name) { |
4927 | d0fe8c12 | Athina Bekakou | // POSSIBLE TODO: Remove this code and replace with
|
4928 | d0fe8c12 | Athina Bekakou | // always having direct references to state objects
|
4929 | d0fe8c12 | Athina Bekakou | |
4930 | d0fe8c12 | Athina Bekakou | var pivotName = name.split(".", 1), |
4931 | d0fe8c12 | Athina Bekakou | currentState = get(this, 'currentState'), |
4932 | d0fe8c12 | Athina Bekakou | state = currentState; |
4933 | d0fe8c12 | Athina Bekakou | |
4934 | d0fe8c12 | Athina Bekakou | do {
|
4935 | d0fe8c12 | Athina Bekakou | if (state.exit) { state.exit(this); } |
4936 | d0fe8c12 | Athina Bekakou | state = state.parentState; |
4937 | d0fe8c12 | Athina Bekakou | } while (!state.hasOwnProperty(pivotName));
|
4938 | d0fe8c12 | Athina Bekakou | |
4939 | d0fe8c12 | Athina Bekakou | var path = name.split("."); |
4940 | d0fe8c12 | Athina Bekakou | |
4941 | d0fe8c12 | Athina Bekakou | var setups = [], enters = [], i, l;
|
4942 | d0fe8c12 | Athina Bekakou | |
4943 | d0fe8c12 | Athina Bekakou | for (i=0, l=path.length; i<l; i++) { |
4944 | d0fe8c12 | Athina Bekakou | state = state[path[i]]; |
4945 | d0fe8c12 | Athina Bekakou | |
4946 | d0fe8c12 | Athina Bekakou | if (state.enter) { enters.push(state); }
|
4947 | d0fe8c12 | Athina Bekakou | if (state.setup) { setups.push(state); }
|
4948 | d0fe8c12 | Athina Bekakou | } |
4949 | d0fe8c12 | Athina Bekakou | |
4950 | d0fe8c12 | Athina Bekakou | for (i=0, l=enters.length; i<l; i++) { |
4951 | d0fe8c12 | Athina Bekakou | enters[i].enter(this);
|
4952 | d0fe8c12 | Athina Bekakou | } |
4953 | d0fe8c12 | Athina Bekakou | |
4954 | d0fe8c12 | Athina Bekakou | set(this, 'currentState', state); |
4955 | d0fe8c12 | Athina Bekakou | |
4956 | d0fe8c12 | Athina Bekakou | for (i=0, l=setups.length; i<l; i++) { |
4957 | d0fe8c12 | Athina Bekakou | setups[i].setup(this);
|
4958 | d0fe8c12 | Athina Bekakou | } |
4959 | d0fe8c12 | Athina Bekakou | |
4960 | d0fe8c12 | Athina Bekakou | this.updateRecordArraysLater();
|
4961 | d0fe8c12 | Athina Bekakou | }, |
4962 | d0fe8c12 | Athina Bekakou | |
4963 | d0fe8c12 | Athina Bekakou | _unhandledEvent: function(state, name, context) { |
4964 | d0fe8c12 | Athina Bekakou | var errorMessage = "Attempted to handle event `" + name + "` "; |
4965 | d0fe8c12 | Athina Bekakou | errorMessage += "on " + String(this) + " while in state "; |
4966 | d0fe8c12 | Athina Bekakou | errorMessage += state.stateName + ". ";
|
4967 | d0fe8c12 | Athina Bekakou | |
4968 | d0fe8c12 | Athina Bekakou | if (context !== undefined) { |
4969 | d0fe8c12 | Athina Bekakou | errorMessage += "Called with " + Ember.inspect(context) + "."; |
4970 | d0fe8c12 | Athina Bekakou | } |
4971 | d0fe8c12 | Athina Bekakou | |
4972 | d0fe8c12 | Athina Bekakou | throw new Ember.Error(errorMessage); |
4973 | d0fe8c12 | Athina Bekakou | }, |
4974 | d0fe8c12 | Athina Bekakou | |
4975 | d0fe8c12 | Athina Bekakou | withTransaction: function(fn) { |
4976 | d0fe8c12 | Athina Bekakou | var transaction = get(this, 'transaction'); |
4977 | d0fe8c12 | Athina Bekakou | if (transaction) { fn(transaction); }
|
4978 | d0fe8c12 | Athina Bekakou | }, |
4979 | d0fe8c12 | Athina Bekakou | |
4980 | d0fe8c12 | Athina Bekakou | /**
|
4981 | d0fe8c12 | Athina Bekakou | @method loadingData
|
4982 | d0fe8c12 | Athina Bekakou | @private
|
4983 | d0fe8c12 | Athina Bekakou | @param {Promise} promise
|
4984 | d0fe8c12 | Athina Bekakou | */
|
4985 | d0fe8c12 | Athina Bekakou | loadingData: function(promise) { |
4986 | d0fe8c12 | Athina Bekakou | this.send('loadingData', promise); |
4987 | d0fe8c12 | Athina Bekakou | }, |
4988 | d0fe8c12 | Athina Bekakou | |
4989 | d0fe8c12 | Athina Bekakou | /**
|
4990 | d0fe8c12 | Athina Bekakou | @method loadedData
|
4991 | d0fe8c12 | Athina Bekakou | @private
|
4992 | d0fe8c12 | Athina Bekakou | */
|
4993 | d0fe8c12 | Athina Bekakou | loadedData: function() { |
4994 | d0fe8c12 | Athina Bekakou | this.send('loadedData'); |
4995 | d0fe8c12 | Athina Bekakou | }, |
4996 | d0fe8c12 | Athina Bekakou | |
4997 | d0fe8c12 | Athina Bekakou | /**
|
4998 | d0fe8c12 | Athina Bekakou | @method notFound
|
4999 | d0fe8c12 | Athina Bekakou | @private
|
5000 | d0fe8c12 | Athina Bekakou | */
|
5001 | d0fe8c12 | Athina Bekakou | notFound: function() { |
5002 | d0fe8c12 | Athina Bekakou | this.send('notFound'); |
5003 | d0fe8c12 | Athina Bekakou | }, |
5004 | d0fe8c12 | Athina Bekakou | |
5005 | d0fe8c12 | Athina Bekakou | /**
|
5006 | d0fe8c12 | Athina Bekakou | @method pushedData
|
5007 | d0fe8c12 | Athina Bekakou | @private
|
5008 | d0fe8c12 | Athina Bekakou | */
|
5009 | d0fe8c12 | Athina Bekakou | pushedData: function() { |
5010 | d0fe8c12 | Athina Bekakou | this.send('pushedData'); |
5011 | d0fe8c12 | Athina Bekakou | }, |
5012 | d0fe8c12 | Athina Bekakou | |
5013 | d0fe8c12 | Athina Bekakou | /**
|
5014 | d0fe8c12 | Athina Bekakou | Marks the record as deleted but does not save it. You must call
|
5015 | d0fe8c12 | Athina Bekakou | `save` afterwards if you want to persist it. You might use this
|
5016 | d0fe8c12 | Athina Bekakou | method if you want to allow the user to still `rollback()` a
|
5017 | d0fe8c12 | Athina Bekakou | delete after it was made.
|
5018 | d0fe8c12 | Athina Bekakou | |
5019 | d0fe8c12 | Athina Bekakou | Example
|
5020 | d0fe8c12 | Athina Bekakou | |
5021 | d0fe8c12 | Athina Bekakou | ```javascript
|
5022 | d0fe8c12 | Athina Bekakou | App.ModelDeleteRoute = Ember.Route.extend({
|
5023 | d0fe8c12 | Athina Bekakou | actions: {
|
5024 | d0fe8c12 | Athina Bekakou | softDelete: function() {
|
5025 | d0fe8c12 | Athina Bekakou | this.get('model').deleteRecord();
|
5026 | d0fe8c12 | Athina Bekakou | },
|
5027 | d0fe8c12 | Athina Bekakou | confirm: function() {
|
5028 | d0fe8c12 | Athina Bekakou | this.get('model').save();
|
5029 | d0fe8c12 | Athina Bekakou | },
|
5030 | d0fe8c12 | Athina Bekakou | undo: function() {
|
5031 | d0fe8c12 | Athina Bekakou | this.get('model').rollback();
|
5032 | d0fe8c12 | Athina Bekakou | }
|
5033 | d0fe8c12 | Athina Bekakou | }
|
5034 | d0fe8c12 | Athina Bekakou | });
|
5035 | d0fe8c12 | Athina Bekakou | ```
|
5036 | d0fe8c12 | Athina Bekakou | |
5037 | d0fe8c12 | Athina Bekakou | @method deleteRecord
|
5038 | d0fe8c12 | Athina Bekakou | */
|
5039 | d0fe8c12 | Athina Bekakou | deleteRecord: function() { |
5040 | d0fe8c12 | Athina Bekakou | this.send('deleteRecord'); |
5041 | d0fe8c12 | Athina Bekakou | }, |
5042 | d0fe8c12 | Athina Bekakou | |
5043 | d0fe8c12 | Athina Bekakou | /**
|
5044 | d0fe8c12 | Athina Bekakou | Same as `deleteRecord`, but saves the record immediately.
|
5045 | d0fe8c12 | Athina Bekakou | |
5046 | d0fe8c12 | Athina Bekakou | Example
|
5047 | d0fe8c12 | Athina Bekakou | |
5048 | d0fe8c12 | Athina Bekakou | ```javascript
|
5049 | d0fe8c12 | Athina Bekakou | App.ModelDeleteRoute = Ember.Route.extend({
|
5050 | d0fe8c12 | Athina Bekakou | actions: {
|
5051 | d0fe8c12 | Athina Bekakou | delete: function() {
|
5052 | d0fe8c12 | Athina Bekakou | var controller = this.controller;
|
5053 | d0fe8c12 | Athina Bekakou | this.get('model').destroyRecord().then(function() {
|
5054 | d0fe8c12 | Athina Bekakou | controller.transitionToRoute('model.index');
|
5055 | d0fe8c12 | Athina Bekakou | });
|
5056 | d0fe8c12 | Athina Bekakou | }
|
5057 | d0fe8c12 | Athina Bekakou | }
|
5058 | d0fe8c12 | Athina Bekakou | });
|
5059 | d0fe8c12 | Athina Bekakou | ```
|
5060 | d0fe8c12 | Athina Bekakou | |
5061 | d0fe8c12 | Athina Bekakou | @method destroyRecord
|
5062 | d0fe8c12 | Athina Bekakou | @return {Promise} a promise that will be resolved when the adapter returns
|
5063 | d0fe8c12 | Athina Bekakou | successfully or rejected if the adapter returns with an error.
|
5064 | d0fe8c12 | Athina Bekakou | */
|
5065 | d0fe8c12 | Athina Bekakou | destroyRecord: function() { |
5066 | d0fe8c12 | Athina Bekakou | this.deleteRecord();
|
5067 | d0fe8c12 | Athina Bekakou | return this.save(); |
5068 | d0fe8c12 | Athina Bekakou | }, |
5069 | d0fe8c12 | Athina Bekakou | |
5070 | d0fe8c12 | Athina Bekakou | /**
|
5071 | d0fe8c12 | Athina Bekakou | @method unloadRecord
|
5072 | d0fe8c12 | Athina Bekakou | @private
|
5073 | d0fe8c12 | Athina Bekakou | */
|
5074 | d0fe8c12 | Athina Bekakou | unloadRecord: function() { |
5075 | d0fe8c12 | Athina Bekakou | Ember.assert("You can only unload a loaded, non-dirty record.", !get(this, 'isDirty')); |
5076 | d0fe8c12 | Athina Bekakou | |
5077 | d0fe8c12 | Athina Bekakou | this.send('unloadRecord'); |
5078 | d0fe8c12 | Athina Bekakou | }, |
5079 | d0fe8c12 | Athina Bekakou | |
5080 | d0fe8c12 | Athina Bekakou | /**
|
5081 | d0fe8c12 | Athina Bekakou | @method clearRelationships
|
5082 | d0fe8c12 | Athina Bekakou | @private
|
5083 | d0fe8c12 | Athina Bekakou | */
|
5084 | d0fe8c12 | Athina Bekakou | clearRelationships: function() { |
5085 | d0fe8c12 | Athina Bekakou | this.eachRelationship(function(name, relationship) { |
5086 | d0fe8c12 | Athina Bekakou | if (relationship.kind === 'belongsTo') { |
5087 | d0fe8c12 | Athina Bekakou | set(this, name, null); |
5088 | d0fe8c12 | Athina Bekakou | } else if (relationship.kind === 'hasMany') { |
5089 | d0fe8c12 | Athina Bekakou | var hasMany = this._relationships[relationship.name]; |
5090 | d0fe8c12 | Athina Bekakou | if (hasMany) { hasMany.clear(); }
|
5091 | d0fe8c12 | Athina Bekakou | } |
5092 | d0fe8c12 | Athina Bekakou | }, this);
|
5093 | d0fe8c12 | Athina Bekakou | }, |
5094 | d0fe8c12 | Athina Bekakou | |
5095 | d0fe8c12 | Athina Bekakou | /**
|
5096 | d0fe8c12 | Athina Bekakou | @method updateRecordArrays
|
5097 | d0fe8c12 | Athina Bekakou | @private
|
5098 | d0fe8c12 | Athina Bekakou | */
|
5099 | d0fe8c12 | Athina Bekakou | updateRecordArrays: function() { |
5100 | d0fe8c12 | Athina Bekakou | get(this, 'store').dataWasUpdated(this.constructor, this); |
5101 | d0fe8c12 | Athina Bekakou | }, |
5102 | d0fe8c12 | Athina Bekakou | |
5103 | d0fe8c12 | Athina Bekakou | /**
|
5104 | d0fe8c12 | Athina Bekakou | Returns an object, whose keys are changed properties, and value is
|
5105 | d0fe8c12 | Athina Bekakou | an [oldProp, newProp] array.
|
5106 | d0fe8c12 | Athina Bekakou | |
5107 | d0fe8c12 | Athina Bekakou | Example
|
5108 | d0fe8c12 | Athina Bekakou | |
5109 | d0fe8c12 | Athina Bekakou | ```javascript
|
5110 | d0fe8c12 | Athina Bekakou | App.Mascot = DS.Model.extend({
|
5111 | d0fe8c12 | Athina Bekakou | name: attr('string')
|
5112 | d0fe8c12 | Athina Bekakou | });
|
5113 | d0fe8c12 | Athina Bekakou | |
5114 | d0fe8c12 | Athina Bekakou | var person = store.createRecord('person');
|
5115 | d0fe8c12 | Athina Bekakou | person.changedAttributes(); // {}
|
5116 | d0fe8c12 | Athina Bekakou | person.set('name', 'Tomster');
|
5117 | d0fe8c12 | Athina Bekakou | person.changedAttributes(); // {name: [undefined, 'Tomster']}
|
5118 | d0fe8c12 | Athina Bekakou | ```
|
5119 | d0fe8c12 | Athina Bekakou | |
5120 | d0fe8c12 | Athina Bekakou | @method changedAttributes
|
5121 | d0fe8c12 | Athina Bekakou | @return {Object} an object, whose keys are changed properties,
|
5122 | d0fe8c12 | Athina Bekakou | and value is an [oldProp, newProp] array.
|
5123 | d0fe8c12 | Athina Bekakou | */
|
5124 | d0fe8c12 | Athina Bekakou | changedAttributes: function() { |
5125 | d0fe8c12 | Athina Bekakou | var oldData = get(this, '_data'), |
5126 | d0fe8c12 | Athina Bekakou | newData = get(this, '_attributes'), |
5127 | d0fe8c12 | Athina Bekakou | diffData = {}, |
5128 | d0fe8c12 | Athina Bekakou | prop; |
5129 | d0fe8c12 | Athina Bekakou | |
5130 | d0fe8c12 | Athina Bekakou | for (prop in newData) { |
5131 | d0fe8c12 | Athina Bekakou | diffData[prop] = [oldData[prop], newData[prop]]; |
5132 | d0fe8c12 | Athina Bekakou | } |
5133 | d0fe8c12 | Athina Bekakou | |
5134 | d0fe8c12 | Athina Bekakou | return diffData;
|
5135 | d0fe8c12 | Athina Bekakou | }, |
5136 | d0fe8c12 | Athina Bekakou | |
5137 | d0fe8c12 | Athina Bekakou | /**
|
5138 | d0fe8c12 | Athina Bekakou | @method adapterWillCommit
|
5139 | d0fe8c12 | Athina Bekakou | @private
|
5140 | d0fe8c12 | Athina Bekakou | */
|
5141 | d0fe8c12 | Athina Bekakou | adapterWillCommit: function() { |
5142 | d0fe8c12 | Athina Bekakou | this.send('willCommit'); |
5143 | d0fe8c12 | Athina Bekakou | }, |
5144 | d0fe8c12 | Athina Bekakou | |
5145 | d0fe8c12 | Athina Bekakou | /**
|
5146 | d0fe8c12 | Athina Bekakou | If the adapter did not return a hash in response to a commit,
|
5147 | d0fe8c12 | Athina Bekakou | merge the changed attributes and relationships into the existing
|
5148 | d0fe8c12 | Athina Bekakou | saved data.
|
5149 | d0fe8c12 | Athina Bekakou | |
5150 | d0fe8c12 | Athina Bekakou | @method adapterDidCommit
|
5151 | d0fe8c12 | Athina Bekakou | */
|
5152 | d0fe8c12 | Athina Bekakou | adapterDidCommit: function(data) { |
5153 | d0fe8c12 | Athina Bekakou | set(this, 'isError', false); |
5154 | d0fe8c12 | Athina Bekakou | |
5155 | d0fe8c12 | Athina Bekakou | if (data) {
|
5156 | d0fe8c12 | Athina Bekakou | this._data = data;
|
5157 | d0fe8c12 | Athina Bekakou | } else {
|
5158 | d0fe8c12 | Athina Bekakou | Ember.mixin(this._data, this._inFlightAttributes); |
5159 | d0fe8c12 | Athina Bekakou | } |
5160 | d0fe8c12 | Athina Bekakou | |
5161 | d0fe8c12 | Athina Bekakou | this._inFlightAttributes = {};
|
5162 | d0fe8c12 | Athina Bekakou | |
5163 | d0fe8c12 | Athina Bekakou | this.send('didCommit'); |
5164 | d0fe8c12 | Athina Bekakou | this.updateRecordArraysLater();
|
5165 | d0fe8c12 | Athina Bekakou | |
5166 | d0fe8c12 | Athina Bekakou | if (!data) { return; } |
5167 | d0fe8c12 | Athina Bekakou | |
5168 | d0fe8c12 | Athina Bekakou | this.suspendRelationshipObservers(function() { |
5169 | d0fe8c12 | Athina Bekakou | this.notifyPropertyChange('data'); |
5170 | d0fe8c12 | Athina Bekakou | }); |
5171 | d0fe8c12 | Athina Bekakou | }, |
5172 | d0fe8c12 | Athina Bekakou | |
5173 | d0fe8c12 | Athina Bekakou | /**
|
5174 | d0fe8c12 | Athina Bekakou | @method adapterDidDirty
|
5175 | d0fe8c12 | Athina Bekakou | @private
|
5176 | d0fe8c12 | Athina Bekakou | */
|
5177 | d0fe8c12 | Athina Bekakou | adapterDidDirty: function() { |
5178 | d0fe8c12 | Athina Bekakou | this.send('becomeDirty'); |
5179 | d0fe8c12 | Athina Bekakou | this.updateRecordArraysLater();
|
5180 | d0fe8c12 | Athina Bekakou | }, |
5181 | d0fe8c12 | Athina Bekakou | |
5182 | d0fe8c12 | Athina Bekakou | dataDidChange: Ember.observer(function() { |
5183 | d0fe8c12 | Athina Bekakou | this.reloadHasManys();
|
5184 | d0fe8c12 | Athina Bekakou | }, 'data'),
|
5185 | d0fe8c12 | Athina Bekakou | |
5186 | d0fe8c12 | Athina Bekakou | reloadHasManys: function() { |
5187 | d0fe8c12 | Athina Bekakou | var relationships = get(this.constructor, 'relationshipsByName'); |
5188 | d0fe8c12 | Athina Bekakou | this.updateRecordArraysLater();
|
5189 | d0fe8c12 | Athina Bekakou | relationships.forEach(function(name, relationship) {
|
5190 | d0fe8c12 | Athina Bekakou | if (this._data.links && this._data.links[name]) { return; } |
5191 | d0fe8c12 | Athina Bekakou | if (relationship.kind === 'hasMany') { |
5192 | d0fe8c12 | Athina Bekakou | this.hasManyDidChange(relationship.key);
|
5193 | d0fe8c12 | Athina Bekakou | } |
5194 | d0fe8c12 | Athina Bekakou | }, this);
|
5195 | d0fe8c12 | Athina Bekakou | }, |
5196 | d0fe8c12 | Athina Bekakou | |
5197 | d0fe8c12 | Athina Bekakou | hasManyDidChange: function(key) { |
5198 | d0fe8c12 | Athina Bekakou | var hasMany = this._relationships[key]; |
5199 | d0fe8c12 | Athina Bekakou | |
5200 | d0fe8c12 | Athina Bekakou | if (hasMany) {
|
5201 | d0fe8c12 | Athina Bekakou | var records = this._data[key] || []; |
5202 | d0fe8c12 | Athina Bekakou | |
5203 | d0fe8c12 | Athina Bekakou | set(hasMany, 'content', Ember.A(records));
|
5204 | d0fe8c12 | Athina Bekakou | set(hasMany, 'isLoaded', true); |
5205 | d0fe8c12 | Athina Bekakou | hasMany.trigger('didLoad');
|
5206 | d0fe8c12 | Athina Bekakou | } |
5207 | d0fe8c12 | Athina Bekakou | }, |
5208 | d0fe8c12 | Athina Bekakou | |
5209 | d0fe8c12 | Athina Bekakou | /**
|
5210 | d0fe8c12 | Athina Bekakou | @method updateRecordArraysLater
|
5211 | d0fe8c12 | Athina Bekakou | @private
|
5212 | d0fe8c12 | Athina Bekakou | */
|
5213 | d0fe8c12 | Athina Bekakou | updateRecordArraysLater: function() { |
5214 | d0fe8c12 | Athina Bekakou | Ember.run.once(this, this.updateRecordArrays); |
5215 | d0fe8c12 | Athina Bekakou | }, |
5216 | d0fe8c12 | Athina Bekakou | |
5217 | d0fe8c12 | Athina Bekakou | /**
|
5218 | d0fe8c12 | Athina Bekakou | @method setupData
|
5219 | d0fe8c12 | Athina Bekakou | @private
|
5220 | d0fe8c12 | Athina Bekakou | @param {Object} data
|
5221 | d0fe8c12 | Athina Bekakou | @param {Boolean} partial the data should be merged into
|
5222 | d0fe8c12 | Athina Bekakou | the existing data, not replace it.
|
5223 | d0fe8c12 | Athina Bekakou | */
|
5224 | d0fe8c12 | Athina Bekakou | setupData: function(data, partial) { |
5225 | d0fe8c12 | Athina Bekakou | if (partial) {
|
5226 | d0fe8c12 | Athina Bekakou | Ember.merge(this._data, data);
|
5227 | d0fe8c12 | Athina Bekakou | } else {
|
5228 | d0fe8c12 | Athina Bekakou | this._data = data;
|
5229 | d0fe8c12 | Athina Bekakou | } |
5230 | d0fe8c12 | Athina Bekakou | |
5231 | d0fe8c12 | Athina Bekakou | var relationships = this._relationships; |
5232 | d0fe8c12 | Athina Bekakou | |
5233 | d0fe8c12 | Athina Bekakou | this.eachRelationship(function(name, rel) { |
5234 | d0fe8c12 | Athina Bekakou | if (data.links && data.links[name]) { return; } |
5235 | d0fe8c12 | Athina Bekakou | if (rel.options.async) { relationships[name] = null; } |
5236 | d0fe8c12 | Athina Bekakou | }); |
5237 | d0fe8c12 | Athina Bekakou | |
5238 | d0fe8c12 | Athina Bekakou | if (data) { this.pushedData(); } |
5239 | d0fe8c12 | Athina Bekakou | |
5240 | d0fe8c12 | Athina Bekakou | this.suspendRelationshipObservers(function() { |
5241 | d0fe8c12 | Athina Bekakou | this.notifyPropertyChange('data'); |
5242 | d0fe8c12 | Athina Bekakou | }); |
5243 | d0fe8c12 | Athina Bekakou | }, |
5244 | d0fe8c12 | Athina Bekakou | |
5245 | d0fe8c12 | Athina Bekakou | materializeId: function(id) { |
5246 | d0fe8c12 | Athina Bekakou | set(this, 'id', id); |
5247 | d0fe8c12 | Athina Bekakou | }, |
5248 | d0fe8c12 | Athina Bekakou | |
5249 | d0fe8c12 | Athina Bekakou | materializeAttributes: function(attributes) { |
5250 | d0fe8c12 | Athina Bekakou | Ember.assert("Must pass a hash of attributes to materializeAttributes", !!attributes);
|
5251 | d0fe8c12 | Athina Bekakou | merge(this._data, attributes);
|
5252 | d0fe8c12 | Athina Bekakou | }, |
5253 | d0fe8c12 | Athina Bekakou | |
5254 | d0fe8c12 | Athina Bekakou | materializeAttribute: function(name, value) { |
5255 | d0fe8c12 | Athina Bekakou | this._data[name] = value;
|
5256 | d0fe8c12 | Athina Bekakou | }, |
5257 | d0fe8c12 | Athina Bekakou | |
5258 | d0fe8c12 | Athina Bekakou | /**
|
5259 | d0fe8c12 | Athina Bekakou | @method updateHasMany
|
5260 | d0fe8c12 | Athina Bekakou | @private
|
5261 | d0fe8c12 | Athina Bekakou | @param {String} name
|
5262 | d0fe8c12 | Athina Bekakou | @param {Array} records
|
5263 | d0fe8c12 | Athina Bekakou | */
|
5264 | d0fe8c12 | Athina Bekakou | updateHasMany: function(name, records) { |
5265 | d0fe8c12 | Athina Bekakou | this._data[name] = records;
|
5266 | d0fe8c12 | Athina Bekakou | this.hasManyDidChange(name);
|
5267 | d0fe8c12 | Athina Bekakou | }, |
5268 | d0fe8c12 | Athina Bekakou | |
5269 | d0fe8c12 | Athina Bekakou | /**
|
5270 | d0fe8c12 | Athina Bekakou | @method updateBelongsTo
|
5271 | d0fe8c12 | Athina Bekakou | @private
|
5272 | d0fe8c12 | Athina Bekakou | @param {String} name
|
5273 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
5274 | d0fe8c12 | Athina Bekakou | */
|
5275 | d0fe8c12 | Athina Bekakou | updateBelongsTo: function(name, record) { |
5276 | d0fe8c12 | Athina Bekakou | this._data[name] = record;
|
5277 | d0fe8c12 | Athina Bekakou | }, |
5278 | d0fe8c12 | Athina Bekakou | |
5279 | d0fe8c12 | Athina Bekakou | /**
|
5280 | d0fe8c12 | Athina Bekakou | If the model `isDirty` this function will which discard any unsaved
|
5281 | d0fe8c12 | Athina Bekakou | changes
|
5282 | d0fe8c12 | Athina Bekakou | |
5283 | d0fe8c12 | Athina Bekakou | Example
|
5284 | d0fe8c12 | Athina Bekakou | |
5285 | d0fe8c12 | Athina Bekakou | ```javascript
|
5286 | d0fe8c12 | Athina Bekakou | record.get('name'); // 'Untitled Document'
|
5287 | d0fe8c12 | Athina Bekakou | record.set('name', 'Doc 1');
|
5288 | d0fe8c12 | Athina Bekakou | record.get('name'); // 'Doc 1'
|
5289 | d0fe8c12 | Athina Bekakou | record.rollback();
|
5290 | d0fe8c12 | Athina Bekakou | record.get('name'); // 'Untitled Document'
|
5291 | d0fe8c12 | Athina Bekakou | ```
|
5292 | d0fe8c12 | Athina Bekakou | |
5293 | d0fe8c12 | Athina Bekakou | @method rollback
|
5294 | d0fe8c12 | Athina Bekakou | */
|
5295 | d0fe8c12 | Athina Bekakou | rollback: function() { |
5296 | d0fe8c12 | Athina Bekakou | this._attributes = {};
|
5297 | d0fe8c12 | Athina Bekakou | |
5298 | d0fe8c12 | Athina Bekakou | if (get(this, 'isError')) { |
5299 | d0fe8c12 | Athina Bekakou | this._inFlightAttributes = {};
|
5300 | d0fe8c12 | Athina Bekakou | set(this, 'isError', false); |
5301 | d0fe8c12 | Athina Bekakou | } |
5302 | d0fe8c12 | Athina Bekakou | |
5303 | d0fe8c12 | Athina Bekakou | if (!get(this, 'isValid')) { |
5304 | d0fe8c12 | Athina Bekakou | this._inFlightAttributes = {};
|
5305 | d0fe8c12 | Athina Bekakou | } |
5306 | d0fe8c12 | Athina Bekakou | |
5307 | d0fe8c12 | Athina Bekakou | this.send('rolledBack'); |
5308 | d0fe8c12 | Athina Bekakou | |
5309 | d0fe8c12 | Athina Bekakou | this.suspendRelationshipObservers(function() { |
5310 | d0fe8c12 | Athina Bekakou | this.notifyPropertyChange('data'); |
5311 | d0fe8c12 | Athina Bekakou | }); |
5312 | d0fe8c12 | Athina Bekakou | }, |
5313 | d0fe8c12 | Athina Bekakou | |
5314 | d0fe8c12 | Athina Bekakou | toStringExtension: function() { |
5315 | d0fe8c12 | Athina Bekakou | return get(this, 'id'); |
5316 | d0fe8c12 | Athina Bekakou | }, |
5317 | d0fe8c12 | Athina Bekakou | |
5318 | d0fe8c12 | Athina Bekakou | /**
|
5319 | d0fe8c12 | Athina Bekakou | The goal of this method is to temporarily disable specific observers
|
5320 | d0fe8c12 | Athina Bekakou | that take action in response to application changes.
|
5321 | d0fe8c12 | Athina Bekakou | |
5322 | d0fe8c12 | Athina Bekakou | This allows the system to make changes (such as materialization and
|
5323 | d0fe8c12 | Athina Bekakou | rollback) that should not trigger secondary behavior (such as setting an
|
5324 | d0fe8c12 | Athina Bekakou | inverse relationship or marking records as dirty).
|
5325 | d0fe8c12 | Athina Bekakou | |
5326 | d0fe8c12 | Athina Bekakou | The specific implementation will likely change as Ember proper provides
|
5327 | d0fe8c12 | Athina Bekakou | better infrastructure for suspending groups of observers, and if Array
|
5328 | d0fe8c12 | Athina Bekakou | observation becomes more unified with regular observers.
|
5329 | d0fe8c12 | Athina Bekakou | |
5330 | d0fe8c12 | Athina Bekakou | @method suspendRelationshipObservers
|
5331 | d0fe8c12 | Athina Bekakou | @private
|
5332 | d0fe8c12 | Athina Bekakou | @param callback
|
5333 | d0fe8c12 | Athina Bekakou | @param binding
|
5334 | d0fe8c12 | Athina Bekakou | */
|
5335 | d0fe8c12 | Athina Bekakou | suspendRelationshipObservers: function(callback, binding) { |
5336 | d0fe8c12 | Athina Bekakou | var observers = get(this.constructor, 'relationshipNames').belongsTo; |
5337 | d0fe8c12 | Athina Bekakou | var self = this; |
5338 | d0fe8c12 | Athina Bekakou | |
5339 | d0fe8c12 | Athina Bekakou | try {
|
5340 | d0fe8c12 | Athina Bekakou | this._suspendedRelationships = true; |
5341 | d0fe8c12 | Athina Bekakou | Ember._suspendObservers(self, observers, null, 'belongsToDidChange', function() { |
5342 | d0fe8c12 | Athina Bekakou | Ember._suspendBeforeObservers(self, observers, null, 'belongsToWillChange', function() { |
5343 | d0fe8c12 | Athina Bekakou | callback.call(binding || self); |
5344 | d0fe8c12 | Athina Bekakou | }); |
5345 | d0fe8c12 | Athina Bekakou | }); |
5346 | d0fe8c12 | Athina Bekakou | } finally {
|
5347 | d0fe8c12 | Athina Bekakou | this._suspendedRelationships = false; |
5348 | d0fe8c12 | Athina Bekakou | } |
5349 | d0fe8c12 | Athina Bekakou | }, |
5350 | d0fe8c12 | Athina Bekakou | |
5351 | d0fe8c12 | Athina Bekakou | /**
|
5352 | d0fe8c12 | Athina Bekakou | Save the record and persist any changes to the record to an
|
5353 | d0fe8c12 | Athina Bekakou | extenal source via the adapter.
|
5354 | d0fe8c12 | Athina Bekakou | |
5355 | d0fe8c12 | Athina Bekakou | Example
|
5356 | d0fe8c12 | Athina Bekakou | |
5357 | d0fe8c12 | Athina Bekakou | ```javascript
|
5358 | d0fe8c12 | Athina Bekakou | record.set('name', 'Tomster');
|
5359 | d0fe8c12 | Athina Bekakou | record.save().then(function(){
|
5360 | d0fe8c12 | Athina Bekakou | // Success callback
|
5361 | d0fe8c12 | Athina Bekakou | }, function() {
|
5362 | d0fe8c12 | Athina Bekakou | // Error callback
|
5363 | d0fe8c12 | Athina Bekakou | });
|
5364 | d0fe8c12 | Athina Bekakou | ```
|
5365 | d0fe8c12 | Athina Bekakou | @method save
|
5366 | d0fe8c12 | Athina Bekakou | @return {Promise} a promise that will be resolved when the adapter returns
|
5367 | d0fe8c12 | Athina Bekakou | successfully or rejected if the adapter returns with an error.
|
5368 | d0fe8c12 | Athina Bekakou | */
|
5369 | d0fe8c12 | Athina Bekakou | save: function() { |
5370 | d0fe8c12 | Athina Bekakou | var promiseLabel = "DS: Model#save " + this; |
5371 | d0fe8c12 | Athina Bekakou | var resolver = Ember.RSVP.defer(promiseLabel);
|
5372 | d0fe8c12 | Athina Bekakou | |
5373 | d0fe8c12 | Athina Bekakou | this.get('store').scheduleSave(this, resolver); |
5374 | d0fe8c12 | Athina Bekakou | this._inFlightAttributes = this._attributes; |
5375 | d0fe8c12 | Athina Bekakou | this._attributes = {};
|
5376 | d0fe8c12 | Athina Bekakou | |
5377 | d0fe8c12 | Athina Bekakou | return DS.PromiseObject.create({ promise: resolver.promise }); |
5378 | d0fe8c12 | Athina Bekakou | }, |
5379 | d0fe8c12 | Athina Bekakou | |
5380 | d0fe8c12 | Athina Bekakou | /**
|
5381 | d0fe8c12 | Athina Bekakou | Reload the record from the adapter.
|
5382 | d0fe8c12 | Athina Bekakou | |
5383 | d0fe8c12 | Athina Bekakou | This will only work if the record has already finished loading
|
5384 | d0fe8c12 | Athina Bekakou | and has not yet been modified (`isLoaded` but not `isDirty`,
|
5385 | d0fe8c12 | Athina Bekakou | or `isSaving`).
|
5386 | d0fe8c12 | Athina Bekakou | |
5387 | d0fe8c12 | Athina Bekakou | Example
|
5388 | d0fe8c12 | Athina Bekakou | |
5389 | d0fe8c12 | Athina Bekakou | ```javascript
|
5390 | d0fe8c12 | Athina Bekakou | App.ModelViewRoute = Ember.Route.extend({
|
5391 | d0fe8c12 | Athina Bekakou | actions: {
|
5392 | d0fe8c12 | Athina Bekakou | reload: function() {
|
5393 | d0fe8c12 | Athina Bekakou | this.get('model').reload();
|
5394 | d0fe8c12 | Athina Bekakou | }
|
5395 | d0fe8c12 | Athina Bekakou | }
|
5396 | d0fe8c12 | Athina Bekakou | });
|
5397 | d0fe8c12 | Athina Bekakou | ```
|
5398 | d0fe8c12 | Athina Bekakou | |
5399 | d0fe8c12 | Athina Bekakou | @method reload
|
5400 | d0fe8c12 | Athina Bekakou | @return {Promise} a promise that will be resolved with the record when the
|
5401 | d0fe8c12 | Athina Bekakou | adapter returns successfully or rejected if the adapter returns
|
5402 | d0fe8c12 | Athina Bekakou | with an error.
|
5403 | d0fe8c12 | Athina Bekakou | */
|
5404 | d0fe8c12 | Athina Bekakou | reload: function() { |
5405 | d0fe8c12 | Athina Bekakou | set(this, 'isReloading', true); |
5406 | d0fe8c12 | Athina Bekakou | |
5407 | d0fe8c12 | Athina Bekakou | var record = this; |
5408 | d0fe8c12 | Athina Bekakou | |
5409 | d0fe8c12 | Athina Bekakou | var promiseLabel = "DS: Model#reload of " + this; |
5410 | d0fe8c12 | Athina Bekakou | var promise = new Ember.RSVP.Promise(function(resolve){ |
5411 | d0fe8c12 | Athina Bekakou | record.send('reloadRecord', resolve);
|
5412 | d0fe8c12 | Athina Bekakou | }, promiseLabel).then(function() {
|
5413 | d0fe8c12 | Athina Bekakou | record.set('isReloading', false); |
5414 | d0fe8c12 | Athina Bekakou | record.set('isError', false); |
5415 | d0fe8c12 | Athina Bekakou | return record;
|
5416 | d0fe8c12 | Athina Bekakou | }, function(reason) {
|
5417 | d0fe8c12 | Athina Bekakou | record.set('isError', true); |
5418 | d0fe8c12 | Athina Bekakou | throw reason;
|
5419 | d0fe8c12 | Athina Bekakou | }, "DS: Model#reload complete, update flags");
|
5420 | d0fe8c12 | Athina Bekakou | |
5421 | d0fe8c12 | Athina Bekakou | return DS.PromiseObject.create({ promise: promise }); |
5422 | d0fe8c12 | Athina Bekakou | }, |
5423 | d0fe8c12 | Athina Bekakou | |
5424 | d0fe8c12 | Athina Bekakou | // FOR USE DURING COMMIT PROCESS
|
5425 | d0fe8c12 | Athina Bekakou | |
5426 | d0fe8c12 | Athina Bekakou | adapterDidUpdateAttribute: function(attributeName, value) { |
5427 | d0fe8c12 | Athina Bekakou | |
5428 | d0fe8c12 | Athina Bekakou | // If a value is passed in, update the internal attributes and clear
|
5429 | d0fe8c12 | Athina Bekakou | // the attribute cache so it picks up the new value. Otherwise,
|
5430 | d0fe8c12 | Athina Bekakou | // collapse the current value into the internal attributes because
|
5431 | d0fe8c12 | Athina Bekakou | // the adapter has acknowledged it.
|
5432 | d0fe8c12 | Athina Bekakou | if (value !== undefined) { |
5433 | d0fe8c12 | Athina Bekakou | this._data[attributeName] = value;
|
5434 | d0fe8c12 | Athina Bekakou | this.notifyPropertyChange(attributeName);
|
5435 | d0fe8c12 | Athina Bekakou | } else {
|
5436 | d0fe8c12 | Athina Bekakou | this._data[attributeName] = this._inFlightAttributes[attributeName]; |
5437 | d0fe8c12 | Athina Bekakou | } |
5438 | d0fe8c12 | Athina Bekakou | |
5439 | d0fe8c12 | Athina Bekakou | this.updateRecordArraysLater();
|
5440 | d0fe8c12 | Athina Bekakou | }, |
5441 | d0fe8c12 | Athina Bekakou | |
5442 | d0fe8c12 | Athina Bekakou | /**
|
5443 | d0fe8c12 | Athina Bekakou | @method adapterDidInvalidate
|
5444 | d0fe8c12 | Athina Bekakou | @private
|
5445 | d0fe8c12 | Athina Bekakou | */
|
5446 | d0fe8c12 | Athina Bekakou | adapterDidInvalidate: function(errors) { |
5447 | d0fe8c12 | Athina Bekakou | var recordErrors = get(this, 'errors'); |
5448 | d0fe8c12 | Athina Bekakou | function addError(name) { |
5449 | d0fe8c12 | Athina Bekakou | if (errors[name]) {
|
5450 | d0fe8c12 | Athina Bekakou | recordErrors.add(name, errors[name]); |
5451 | d0fe8c12 | Athina Bekakou | } |
5452 | d0fe8c12 | Athina Bekakou | } |
5453 | d0fe8c12 | Athina Bekakou | |
5454 | d0fe8c12 | Athina Bekakou | this.eachAttribute(addError);
|
5455 | d0fe8c12 | Athina Bekakou | this.eachRelationship(addError);
|
5456 | d0fe8c12 | Athina Bekakou | }, |
5457 | d0fe8c12 | Athina Bekakou | |
5458 | d0fe8c12 | Athina Bekakou | /**
|
5459 | d0fe8c12 | Athina Bekakou | @method adapterDidError
|
5460 | d0fe8c12 | Athina Bekakou | @private
|
5461 | d0fe8c12 | Athina Bekakou | */
|
5462 | d0fe8c12 | Athina Bekakou | adapterDidError: function() { |
5463 | d0fe8c12 | Athina Bekakou | this.send('becameError'); |
5464 | d0fe8c12 | Athina Bekakou | set(this, 'isError', true); |
5465 | d0fe8c12 | Athina Bekakou | }, |
5466 | d0fe8c12 | Athina Bekakou | |
5467 | d0fe8c12 | Athina Bekakou | /**
|
5468 | d0fe8c12 | Athina Bekakou | Override the default event firing from Ember.Evented to
|
5469 | d0fe8c12 | Athina Bekakou | also call methods with the given name.
|
5470 | d0fe8c12 | Athina Bekakou | |
5471 | d0fe8c12 | Athina Bekakou | @method trigger
|
5472 | d0fe8c12 | Athina Bekakou | @private
|
5473 | d0fe8c12 | Athina Bekakou | @param name
|
5474 | d0fe8c12 | Athina Bekakou | */
|
5475 | d0fe8c12 | Athina Bekakou | trigger: function(name) { |
5476 | d0fe8c12 | Athina Bekakou | Ember.tryInvoke(this, name, [].slice.call(arguments, 1)); |
5477 | d0fe8c12 | Athina Bekakou | this._super.apply(this, arguments); |
5478 | d0fe8c12 | Athina Bekakou | }, |
5479 | d0fe8c12 | Athina Bekakou | |
5480 | d0fe8c12 | Athina Bekakou | triggerLater: function() { |
5481 | d0fe8c12 | Athina Bekakou | this._deferredTriggers.push(arguments); |
5482 | d0fe8c12 | Athina Bekakou | once(this, '_triggerDeferredTriggers'); |
5483 | d0fe8c12 | Athina Bekakou | }, |
5484 | d0fe8c12 | Athina Bekakou | |
5485 | d0fe8c12 | Athina Bekakou | _triggerDeferredTriggers: function() { |
5486 | d0fe8c12 | Athina Bekakou | for (var i=0, l=this._deferredTriggers.length; i<l; i++) { |
5487 | d0fe8c12 | Athina Bekakou | this.trigger.apply(this, this._deferredTriggers[i]); |
5488 | d0fe8c12 | Athina Bekakou | } |
5489 | d0fe8c12 | Athina Bekakou | |
5490 | d0fe8c12 | Athina Bekakou | this._deferredTriggers = [];
|
5491 | d0fe8c12 | Athina Bekakou | } |
5492 | d0fe8c12 | Athina Bekakou | }); |
5493 | d0fe8c12 | Athina Bekakou | |
5494 | d0fe8c12 | Athina Bekakou | DS.Model.reopenClass({ |
5495 | d0fe8c12 | Athina Bekakou | |
5496 | d0fe8c12 | Athina Bekakou | /**
|
5497 | d0fe8c12 | Athina Bekakou | Alias DS.Model's `create` method to `_create`. This allows us to create DS.Model
|
5498 | d0fe8c12 | Athina Bekakou | instances from within the store, but if end users accidentally call `create()`
|
5499 | d0fe8c12 | Athina Bekakou | (instead of `createRecord()`), we can raise an error.
|
5500 | d0fe8c12 | Athina Bekakou | |
5501 | d0fe8c12 | Athina Bekakou | @method _create
|
5502 | d0fe8c12 | Athina Bekakou | @private
|
5503 | d0fe8c12 | Athina Bekakou | @static
|
5504 | d0fe8c12 | Athina Bekakou | */
|
5505 | d0fe8c12 | Athina Bekakou | _create: DS.Model.create,
|
5506 | d0fe8c12 | Athina Bekakou | |
5507 | d0fe8c12 | Athina Bekakou | /**
|
5508 | d0fe8c12 | Athina Bekakou | Override the class' `create()` method to raise an error. This
|
5509 | d0fe8c12 | Athina Bekakou | prevents end users from inadvertently calling `create()` instead
|
5510 | d0fe8c12 | Athina Bekakou | of `createRecord()`. The store is still able to create instances
|
5511 | d0fe8c12 | Athina Bekakou | by calling the `_create()` method. To create an instance of a
|
5512 | d0fe8c12 | Athina Bekakou | `DS.Model` use [store.createRecord](DS.Store.html#method_createRecord).
|
5513 | d0fe8c12 | Athina Bekakou | |
5514 | d0fe8c12 | Athina Bekakou | @method create
|
5515 | d0fe8c12 | Athina Bekakou | @private
|
5516 | d0fe8c12 | Athina Bekakou | @static
|
5517 | d0fe8c12 | Athina Bekakou | */
|
5518 | d0fe8c12 | Athina Bekakou | create: function() { |
5519 | d0fe8c12 | Athina Bekakou | throw new Ember.Error("You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set."); |
5520 | d0fe8c12 | Athina Bekakou | } |
5521 | d0fe8c12 | Athina Bekakou | }); |
5522 | d0fe8c12 | Athina Bekakou | |
5523 | d0fe8c12 | Athina Bekakou | })(); |
5524 | d0fe8c12 | Athina Bekakou | |
5525 | d0fe8c12 | Athina Bekakou | |
5526 | d0fe8c12 | Athina Bekakou | |
5527 | d0fe8c12 | Athina Bekakou | (function() {
|
5528 | d0fe8c12 | Athina Bekakou | /**
|
5529 | d0fe8c12 | Athina Bekakou | @module ember-data
|
5530 | d0fe8c12 | Athina Bekakou | */
|
5531 | d0fe8c12 | Athina Bekakou | |
5532 | d0fe8c12 | Athina Bekakou | var get = Ember.get;
|
5533 | d0fe8c12 | Athina Bekakou | |
5534 | d0fe8c12 | Athina Bekakou | /**
|
5535 | d0fe8c12 | Athina Bekakou | @class Model
|
5536 | d0fe8c12 | Athina Bekakou | @namespace DS
|
5537 | d0fe8c12 | Athina Bekakou | */
|
5538 | d0fe8c12 | Athina Bekakou | DS.Model.reopenClass({ |
5539 | d0fe8c12 | Athina Bekakou | /**
|
5540 | d0fe8c12 | Athina Bekakou | A map whose keys are the attributes of the model (properties
|
5541 | d0fe8c12 | Athina Bekakou | described by DS.attr) and whose values are the meta object for the
|
5542 | d0fe8c12 | Athina Bekakou | property.
|
5543 | d0fe8c12 | Athina Bekakou | |
5544 | d0fe8c12 | Athina Bekakou | Example
|
5545 | d0fe8c12 | Athina Bekakou | |
5546 | d0fe8c12 | Athina Bekakou | ```javascript
|
5547 | d0fe8c12 | Athina Bekakou | |
5548 | d0fe8c12 | Athina Bekakou | App.Person = DS.Model.extend({
|
5549 | d0fe8c12 | Athina Bekakou | firstName: attr('string'),
|
5550 | d0fe8c12 | Athina Bekakou | lastName: attr('string'),
|
5551 | d0fe8c12 | Athina Bekakou | birthday: attr('date')
|
5552 | d0fe8c12 | Athina Bekakou | });
|
5553 | d0fe8c12 | Athina Bekakou | |
5554 | d0fe8c12 | Athina Bekakou | var attributes = Ember.get(App.Person, 'attributes')
|
5555 | d0fe8c12 | Athina Bekakou | |
5556 | d0fe8c12 | Athina Bekakou | attributes.forEach(function(name, meta) {
|
5557 | d0fe8c12 | Athina Bekakou | console.log(name, meta);
|
5558 | d0fe8c12 | Athina Bekakou | });
|
5559 | d0fe8c12 | Athina Bekakou | |
5560 | d0fe8c12 | Athina Bekakou | // prints:
|
5561 | d0fe8c12 | Athina Bekakou | // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
|
5562 | d0fe8c12 | Athina Bekakou | // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
|
5563 | d0fe8c12 | Athina Bekakou | // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
|
5564 | d0fe8c12 | Athina Bekakou | ```
|
5565 | d0fe8c12 | Athina Bekakou | |
5566 | d0fe8c12 | Athina Bekakou | @property attributes
|
5567 | d0fe8c12 | Athina Bekakou | @static
|
5568 | d0fe8c12 | Athina Bekakou | @type {Ember.Map}
|
5569 | d0fe8c12 | Athina Bekakou | @readOnly
|
5570 | d0fe8c12 | Athina Bekakou | */
|
5571 | d0fe8c12 | Athina Bekakou | attributes: Ember.computed(function() { |
5572 | d0fe8c12 | Athina Bekakou | var map = Ember.Map.create();
|
5573 | d0fe8c12 | Athina Bekakou | |
5574 | d0fe8c12 | Athina Bekakou | this.eachComputedProperty(function(name, meta) { |
5575 | d0fe8c12 | Athina Bekakou | if (meta.isAttribute) {
|
5576 | d0fe8c12 | Athina Bekakou | Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.toString(), name !== 'id'); |
5577 | d0fe8c12 | Athina Bekakou | |
5578 | d0fe8c12 | Athina Bekakou | meta.name = name; |
5579 | d0fe8c12 | Athina Bekakou | map.set(name, meta); |
5580 | d0fe8c12 | Athina Bekakou | } |
5581 | d0fe8c12 | Athina Bekakou | }); |
5582 | d0fe8c12 | Athina Bekakou | |
5583 | d0fe8c12 | Athina Bekakou | return map;
|
5584 | d0fe8c12 | Athina Bekakou | }), |
5585 | d0fe8c12 | Athina Bekakou | |
5586 | d0fe8c12 | Athina Bekakou | /**
|
5587 | d0fe8c12 | Athina Bekakou | A map whose keys are the attributes of the model (properties
|
5588 | d0fe8c12 | Athina Bekakou | described by DS.attr) and whose values are type of transformation
|
5589 | d0fe8c12 | Athina Bekakou | applied to each attribute. This map does not include any
|
5590 | d0fe8c12 | Athina Bekakou | attributes that do not have an transformation type.
|
5591 | d0fe8c12 | Athina Bekakou | |
5592 | d0fe8c12 | Athina Bekakou | Example
|
5593 | d0fe8c12 | Athina Bekakou | |
5594 | d0fe8c12 | Athina Bekakou | ```javascript
|
5595 | d0fe8c12 | Athina Bekakou | App.Person = DS.Model.extend({
|
5596 | d0fe8c12 | Athina Bekakou | firstName: attr(),
|
5597 | d0fe8c12 | Athina Bekakou | lastName: attr('string'),
|
5598 | d0fe8c12 | Athina Bekakou | birthday: attr('date')
|
5599 | d0fe8c12 | Athina Bekakou | });
|
5600 | d0fe8c12 | Athina Bekakou | |
5601 | d0fe8c12 | Athina Bekakou | var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')
|
5602 | d0fe8c12 | Athina Bekakou | |
5603 | d0fe8c12 | Athina Bekakou | transformedAttributes.forEach(function(field, type) {
|
5604 | d0fe8c12 | Athina Bekakou | console.log(field, type);
|
5605 | d0fe8c12 | Athina Bekakou | });
|
5606 | d0fe8c12 | Athina Bekakou | |
5607 | d0fe8c12 | Athina Bekakou | // prints:
|
5608 | d0fe8c12 | Athina Bekakou | // lastName string
|
5609 | d0fe8c12 | Athina Bekakou | // birthday date
|
5610 | d0fe8c12 | Athina Bekakou | ```
|
5611 | d0fe8c12 | Athina Bekakou | |
5612 | d0fe8c12 | Athina Bekakou | @property transformedAttributes
|
5613 | d0fe8c12 | Athina Bekakou | @static
|
5614 | d0fe8c12 | Athina Bekakou | @type {Ember.Map}
|
5615 | d0fe8c12 | Athina Bekakou | @readOnly
|
5616 | d0fe8c12 | Athina Bekakou | */
|
5617 | d0fe8c12 | Athina Bekakou | transformedAttributes: Ember.computed(function() { |
5618 | d0fe8c12 | Athina Bekakou | var map = Ember.Map.create();
|
5619 | d0fe8c12 | Athina Bekakou | |
5620 | d0fe8c12 | Athina Bekakou | this.eachAttribute(function(key, meta) { |
5621 | d0fe8c12 | Athina Bekakou | if (meta.type) {
|
5622 | d0fe8c12 | Athina Bekakou | map.set(key, meta.type); |
5623 | d0fe8c12 | Athina Bekakou | } |
5624 | d0fe8c12 | Athina Bekakou | }); |
5625 | d0fe8c12 | Athina Bekakou | |
5626 | d0fe8c12 | Athina Bekakou | return map;
|
5627 | d0fe8c12 | Athina Bekakou | }), |
5628 | d0fe8c12 | Athina Bekakou | |
5629 | d0fe8c12 | Athina Bekakou | /**
|
5630 | d0fe8c12 | Athina Bekakou | Iterates through the attributes of the model, calling the passed function on each
|
5631 | d0fe8c12 | Athina Bekakou | attribute.
|
5632 | d0fe8c12 | Athina Bekakou | |
5633 | d0fe8c12 | Athina Bekakou | The callback method you provide should have the following signature (all
|
5634 | d0fe8c12 | Athina Bekakou | parameters are optional):
|
5635 | d0fe8c12 | Athina Bekakou | |
5636 | d0fe8c12 | Athina Bekakou | ```javascript
|
5637 | d0fe8c12 | Athina Bekakou | function(name, meta);
|
5638 | d0fe8c12 | Athina Bekakou | ```
|
5639 | d0fe8c12 | Athina Bekakou | |
5640 | d0fe8c12 | Athina Bekakou | - `name` the name of the current property in the iteration
|
5641 | d0fe8c12 | Athina Bekakou | - `meta` the meta object for the attribute property in the iteration
|
5642 | d0fe8c12 | Athina Bekakou | |
5643 | d0fe8c12 | Athina Bekakou | Note that in addition to a callback, you can also pass an optional target
|
5644 | d0fe8c12 | Athina Bekakou | object that will be set as `this` on the context.
|
5645 | d0fe8c12 | Athina Bekakou | |
5646 | d0fe8c12 | Athina Bekakou | Example
|
5647 | d0fe8c12 | Athina Bekakou | |
5648 | d0fe8c12 | Athina Bekakou | ```javascript
|
5649 | d0fe8c12 | Athina Bekakou | App.Person = DS.Model.extend({
|
5650 | d0fe8c12 | Athina Bekakou | firstName: attr('string'),
|
5651 | d0fe8c12 | Athina Bekakou | lastName: attr('string'),
|
5652 | d0fe8c12 | Athina Bekakou | birthday: attr('date')
|
5653 | d0fe8c12 | Athina Bekakou | });
|
5654 | d0fe8c12 | Athina Bekakou | |
5655 | d0fe8c12 | Athina Bekakou | App.Person.eachAttribute(function(name, meta) {
|
5656 | d0fe8c12 | Athina Bekakou | console.log(name, meta);
|
5657 | d0fe8c12 | Athina Bekakou | });
|
5658 | d0fe8c12 | Athina Bekakou | |
5659 | d0fe8c12 | Athina Bekakou | // prints:
|
5660 | d0fe8c12 | Athina Bekakou | // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
|
5661 | d0fe8c12 | Athina Bekakou | // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
|
5662 | d0fe8c12 | Athina Bekakou | // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
|
5663 | d0fe8c12 | Athina Bekakou | ```
|
5664 | d0fe8c12 | Athina Bekakou | |
5665 | d0fe8c12 | Athina Bekakou | @method eachAttribute
|
5666 | d0fe8c12 | Athina Bekakou | @param {Function} callback The callback to execute
|
5667 | d0fe8c12 | Athina Bekakou | @param {Object} [target] The target object to use
|
5668 | d0fe8c12 | Athina Bekakou | @static
|
5669 | d0fe8c12 | Athina Bekakou | */
|
5670 | d0fe8c12 | Athina Bekakou | eachAttribute: function(callback, binding) { |
5671 | d0fe8c12 | Athina Bekakou | get(this, 'attributes').forEach(function(name, meta) { |
5672 | d0fe8c12 | Athina Bekakou | callback.call(binding, name, meta); |
5673 | d0fe8c12 | Athina Bekakou | }, binding); |
5674 | d0fe8c12 | Athina Bekakou | }, |
5675 | d0fe8c12 | Athina Bekakou | |
5676 | d0fe8c12 | Athina Bekakou | /**
|
5677 | d0fe8c12 | Athina Bekakou | Iterates through the transformedAttributes of the model, calling
|
5678 | d0fe8c12 | Athina Bekakou | the passed function on each attribute. Note the callback will not be
|
5679 | d0fe8c12 | Athina Bekakou | called for any attributes that do not have an transformation type.
|
5680 | d0fe8c12 | Athina Bekakou | |
5681 | d0fe8c12 | Athina Bekakou | The callback method you provide should have the following signature (all
|
5682 | d0fe8c12 | Athina Bekakou | parameters are optional):
|
5683 | d0fe8c12 | Athina Bekakou | |
5684 | d0fe8c12 | Athina Bekakou | ```javascript
|
5685 | d0fe8c12 | Athina Bekakou | function(name, type);
|
5686 | d0fe8c12 | Athina Bekakou | ```
|
5687 | d0fe8c12 | Athina Bekakou | |
5688 | d0fe8c12 | Athina Bekakou | - `name` the name of the current property in the iteration
|
5689 | d0fe8c12 | Athina Bekakou | - `type` a tring contrining the name of the type of transformed
|
5690 | d0fe8c12 | Athina Bekakou | applied to the attribute
|
5691 | d0fe8c12 | Athina Bekakou | |
5692 | d0fe8c12 | Athina Bekakou | Note that in addition to a callback, you can also pass an optional target
|
5693 | d0fe8c12 | Athina Bekakou | object that will be set as `this` on the context.
|
5694 | d0fe8c12 | Athina Bekakou | |
5695 | d0fe8c12 | Athina Bekakou | Example
|
5696 | d0fe8c12 | Athina Bekakou | |
5697 | d0fe8c12 | Athina Bekakou | ```javascript
|
5698 | d0fe8c12 | Athina Bekakou | App.Person = DS.Model.extend({
|
5699 | d0fe8c12 | Athina Bekakou | firstName: attr(),
|
5700 | d0fe8c12 | Athina Bekakou | lastName: attr('string'),
|
5701 | d0fe8c12 | Athina Bekakou | birthday: attr('date')
|
5702 | d0fe8c12 | Athina Bekakou | });
|
5703 | d0fe8c12 | Athina Bekakou | |
5704 | d0fe8c12 | Athina Bekakou | App.Person.eachTransformedAttribute(function(name, type) {
|
5705 | d0fe8c12 | Athina Bekakou | console.log(name, type);
|
5706 | d0fe8c12 | Athina Bekakou | });
|
5707 | d0fe8c12 | Athina Bekakou | |
5708 | d0fe8c12 | Athina Bekakou | // prints:
|
5709 | d0fe8c12 | Athina Bekakou | // lastName string
|
5710 | d0fe8c12 | Athina Bekakou | // birthday date
|
5711 | d0fe8c12 | Athina Bekakou | ```
|
5712 | d0fe8c12 | Athina Bekakou | |
5713 | d0fe8c12 | Athina Bekakou | @method eachTransformedAttribute
|
5714 | d0fe8c12 | Athina Bekakou | @param {Function} callback The callback to execute
|
5715 | d0fe8c12 | Athina Bekakou | @param {Object} [target] The target object to use
|
5716 | d0fe8c12 | Athina Bekakou | @static
|
5717 | d0fe8c12 | Athina Bekakou | */
|
5718 | d0fe8c12 | Athina Bekakou | eachTransformedAttribute: function(callback, binding) { |
5719 | d0fe8c12 | Athina Bekakou | get(this, 'transformedAttributes').forEach(function(name, type) { |
5720 | d0fe8c12 | Athina Bekakou | callback.call(binding, name, type); |
5721 | d0fe8c12 | Athina Bekakou | }); |
5722 | d0fe8c12 | Athina Bekakou | } |
5723 | d0fe8c12 | Athina Bekakou | }); |
5724 | d0fe8c12 | Athina Bekakou | |
5725 | d0fe8c12 | Athina Bekakou | |
5726 | d0fe8c12 | Athina Bekakou | DS.Model.reopen({ |
5727 | d0fe8c12 | Athina Bekakou | eachAttribute: function(callback, binding) { |
5728 | d0fe8c12 | Athina Bekakou | this.constructor.eachAttribute(callback, binding);
|
5729 | d0fe8c12 | Athina Bekakou | } |
5730 | d0fe8c12 | Athina Bekakou | }); |
5731 | d0fe8c12 | Athina Bekakou | |
5732 | d0fe8c12 | Athina Bekakou | function getDefaultValue(record, options, key) { |
5733 | d0fe8c12 | Athina Bekakou | if (typeof options.defaultValue === "function") { |
5734 | d0fe8c12 | Athina Bekakou | return options.defaultValue();
|
5735 | d0fe8c12 | Athina Bekakou | } else {
|
5736 | d0fe8c12 | Athina Bekakou | return options.defaultValue;
|
5737 | d0fe8c12 | Athina Bekakou | } |
5738 | d0fe8c12 | Athina Bekakou | } |
5739 | d0fe8c12 | Athina Bekakou | |
5740 | d0fe8c12 | Athina Bekakou | function hasValue(record, key) { |
5741 | d0fe8c12 | Athina Bekakou | return record._attributes.hasOwnProperty(key) ||
|
5742 | d0fe8c12 | Athina Bekakou | record._inFlightAttributes.hasOwnProperty(key) || |
5743 | d0fe8c12 | Athina Bekakou | record._data.hasOwnProperty(key); |
5744 | d0fe8c12 | Athina Bekakou | } |
5745 | d0fe8c12 | Athina Bekakou | |
5746 | d0fe8c12 | Athina Bekakou | function getValue(record, key) { |
5747 | d0fe8c12 | Athina Bekakou | if (record._attributes.hasOwnProperty(key)) {
|
5748 | d0fe8c12 | Athina Bekakou | return record._attributes[key];
|
5749 | d0fe8c12 | Athina Bekakou | } else if (record._inFlightAttributes.hasOwnProperty(key)) { |
5750 | d0fe8c12 | Athina Bekakou | return record._inFlightAttributes[key];
|
5751 | d0fe8c12 | Athina Bekakou | } else {
|
5752 | d0fe8c12 | Athina Bekakou | return record._data[key];
|
5753 | d0fe8c12 | Athina Bekakou | } |
5754 | d0fe8c12 | Athina Bekakou | } |
5755 | d0fe8c12 | Athina Bekakou | |
5756 | d0fe8c12 | Athina Bekakou | /**
|
5757 | d0fe8c12 | Athina Bekakou | `DS.attr` defines an attribute on a [DS.Model](DS.Model.html).
|
5758 | d0fe8c12 | Athina Bekakou | By default, attributes are passed through as-is, however you can specify an
|
5759 | d0fe8c12 | Athina Bekakou | optional type to have the value automatically transformed.
|
5760 | d0fe8c12 | Athina Bekakou | Ember Data ships with four basic transform types: `string`, `number`,
|
5761 | d0fe8c12 | Athina Bekakou | `boolean` and `date`. You can define your own transforms by subclassing
|
5762 | d0fe8c12 | Athina Bekakou | [DS.Transform](DS.Transform.html).
|
5763 | d0fe8c12 | Athina Bekakou | |
5764 | d0fe8c12 | Athina Bekakou | `DS.attr` takes an optional hash as a second parameter, currently
|
5765 | d0fe8c12 | Athina Bekakou | supported options are:
|
5766 | d0fe8c12 | Athina Bekakou | |
5767 | d0fe8c12 | Athina Bekakou | - `defaultValue`: Pass a string or a function to be called to set the attribute
|
5768 | d0fe8c12 | Athina Bekakou | to a default value if none is supplied.
|
5769 | d0fe8c12 | Athina Bekakou | |
5770 | d0fe8c12 | Athina Bekakou | Example
|
5771 | d0fe8c12 | Athina Bekakou | |
5772 | d0fe8c12 | Athina Bekakou | ```javascript
|
5773 | d0fe8c12 | Athina Bekakou | var attr = DS.attr;
|
5774 | d0fe8c12 | Athina Bekakou | |
5775 | d0fe8c12 | Athina Bekakou | App.User = DS.Model.extend({
|
5776 | d0fe8c12 | Athina Bekakou | username: attr('string'),
|
5777 | d0fe8c12 | Athina Bekakou | email: attr('string'),
|
5778 | d0fe8c12 | Athina Bekakou | verified: attr('boolean', {defaultValue: false})
|
5779 | d0fe8c12 | Athina Bekakou | });
|
5780 | d0fe8c12 | Athina Bekakou | ```
|
5781 | d0fe8c12 | Athina Bekakou | |
5782 | d0fe8c12 | Athina Bekakou | @namespace
|
5783 | d0fe8c12 | Athina Bekakou | @method attr
|
5784 | d0fe8c12 | Athina Bekakou | @for DS
|
5785 | d0fe8c12 | Athina Bekakou | @param {String} type the attribute type
|
5786 | d0fe8c12 | Athina Bekakou | @param {Object} options a hash of options
|
5787 | d0fe8c12 | Athina Bekakou | @return {Attribute}
|
5788 | d0fe8c12 | Athina Bekakou | */
|
5789 | d0fe8c12 | Athina Bekakou | |
5790 | d0fe8c12 | Athina Bekakou | DS.attr = function(type, options) { |
5791 | d0fe8c12 | Athina Bekakou | options = options || {}; |
5792 | d0fe8c12 | Athina Bekakou | |
5793 | d0fe8c12 | Athina Bekakou | var meta = {
|
5794 | d0fe8c12 | Athina Bekakou | type: type,
|
5795 | d0fe8c12 | Athina Bekakou | isAttribute: true, |
5796 | d0fe8c12 | Athina Bekakou | options: options
|
5797 | d0fe8c12 | Athina Bekakou | }; |
5798 | d0fe8c12 | Athina Bekakou | |
5799 | d0fe8c12 | Athina Bekakou | return Ember.computed(function(key, value) { |
5800 | d0fe8c12 | Athina Bekakou | if (arguments.length > 1) { |
5801 | d0fe8c12 | Athina Bekakou | Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id'); |
5802 | d0fe8c12 | Athina Bekakou | var oldValue = this._attributes[key] || this._inFlightAttributes[key] || this._data[key]; |
5803 | d0fe8c12 | Athina Bekakou | |
5804 | d0fe8c12 | Athina Bekakou | this.send('didSetProperty', { |
5805 | d0fe8c12 | Athina Bekakou | name: key,
|
5806 | d0fe8c12 | Athina Bekakou | oldValue: oldValue,
|
5807 | d0fe8c12 | Athina Bekakou | originalValue: this._data[key], |
5808 | d0fe8c12 | Athina Bekakou | value: value
|
5809 | d0fe8c12 | Athina Bekakou | }); |
5810 | d0fe8c12 | Athina Bekakou | |
5811 | d0fe8c12 | Athina Bekakou | this._attributes[key] = value;
|
5812 | d0fe8c12 | Athina Bekakou | return value;
|
5813 | d0fe8c12 | Athina Bekakou | } else if (hasValue(this, key)) { |
5814 | d0fe8c12 | Athina Bekakou | return getValue(this, key); |
5815 | d0fe8c12 | Athina Bekakou | } else {
|
5816 | d0fe8c12 | Athina Bekakou | return getDefaultValue(this, options, key); |
5817 | d0fe8c12 | Athina Bekakou | } |
5818 | d0fe8c12 | Athina Bekakou | |
5819 | d0fe8c12 | Athina Bekakou | // `data` is never set directly. However, it may be
|
5820 | d0fe8c12 | Athina Bekakou | // invalidated from the state manager's setData
|
5821 | d0fe8c12 | Athina Bekakou | // event.
|
5822 | d0fe8c12 | Athina Bekakou | }).property('data').meta(meta);
|
5823 | d0fe8c12 | Athina Bekakou | }; |
5824 | d0fe8c12 | Athina Bekakou | |
5825 | d0fe8c12 | Athina Bekakou | |
5826 | d0fe8c12 | Athina Bekakou | })(); |
5827 | d0fe8c12 | Athina Bekakou | |
5828 | d0fe8c12 | Athina Bekakou | |
5829 | d0fe8c12 | Athina Bekakou | |
5830 | d0fe8c12 | Athina Bekakou | (function() {
|
5831 | d0fe8c12 | Athina Bekakou | /**
|
5832 | d0fe8c12 | Athina Bekakou | @module ember-data
|
5833 | d0fe8c12 | Athina Bekakou | */
|
5834 | d0fe8c12 | Athina Bekakou | |
5835 | d0fe8c12 | Athina Bekakou | })(); |
5836 | d0fe8c12 | Athina Bekakou | |
5837 | d0fe8c12 | Athina Bekakou | |
5838 | d0fe8c12 | Athina Bekakou | |
5839 | d0fe8c12 | Athina Bekakou | (function() {
|
5840 | d0fe8c12 | Athina Bekakou | /**
|
5841 | d0fe8c12 | Athina Bekakou | @module ember-data
|
5842 | d0fe8c12 | Athina Bekakou | */
|
5843 | d0fe8c12 | Athina Bekakou | |
5844 | d0fe8c12 | Athina Bekakou | /**
|
5845 | d0fe8c12 | Athina Bekakou | An AttributeChange object is created whenever a record's
|
5846 | d0fe8c12 | Athina Bekakou | attribute changes value. It is used to track changes to a
|
5847 | d0fe8c12 | Athina Bekakou | record between transaction commits.
|
5848 | d0fe8c12 | Athina Bekakou | |
5849 | d0fe8c12 | Athina Bekakou | @class AttributeChange
|
5850 | d0fe8c12 | Athina Bekakou | @namespace DS
|
5851 | d0fe8c12 | Athina Bekakou | @private
|
5852 | d0fe8c12 | Athina Bekakou | @constructor
|
5853 | d0fe8c12 | Athina Bekakou | */
|
5854 | d0fe8c12 | Athina Bekakou | var AttributeChange = DS.AttributeChange = function(options) { |
5855 | d0fe8c12 | Athina Bekakou | this.record = options.record;
|
5856 | d0fe8c12 | Athina Bekakou | this.store = options.store;
|
5857 | d0fe8c12 | Athina Bekakou | this.name = options.name;
|
5858 | d0fe8c12 | Athina Bekakou | this.value = options.value;
|
5859 | d0fe8c12 | Athina Bekakou | this.oldValue = options.oldValue;
|
5860 | d0fe8c12 | Athina Bekakou | }; |
5861 | d0fe8c12 | Athina Bekakou | |
5862 | d0fe8c12 | Athina Bekakou | AttributeChange.createChange = function(options) { |
5863 | d0fe8c12 | Athina Bekakou | return new AttributeChange(options); |
5864 | d0fe8c12 | Athina Bekakou | }; |
5865 | d0fe8c12 | Athina Bekakou | |
5866 | d0fe8c12 | Athina Bekakou | AttributeChange.prototype = { |
5867 | d0fe8c12 | Athina Bekakou | sync: function() { |
5868 | d0fe8c12 | Athina Bekakou | if (this.value !== this.oldValue) { |
5869 | d0fe8c12 | Athina Bekakou | this.record.send('becomeDirty'); |
5870 | d0fe8c12 | Athina Bekakou | this.record.updateRecordArraysLater();
|
5871 | d0fe8c12 | Athina Bekakou | } |
5872 | d0fe8c12 | Athina Bekakou | |
5873 | d0fe8c12 | Athina Bekakou | // TODO: Use this object in the commit process
|
5874 | d0fe8c12 | Athina Bekakou | this.destroy();
|
5875 | d0fe8c12 | Athina Bekakou | }, |
5876 | d0fe8c12 | Athina Bekakou | |
5877 | d0fe8c12 | Athina Bekakou | /**
|
5878 | d0fe8c12 | Athina Bekakou | If the AttributeChange is destroyed (either by being rolled back
|
5879 | d0fe8c12 | Athina Bekakou | or being committed), remove it from the list of pending changes
|
5880 | d0fe8c12 | Athina Bekakou | on the record.
|
5881 | d0fe8c12 | Athina Bekakou | |
5882 | d0fe8c12 | Athina Bekakou | @method destroy
|
5883 | d0fe8c12 | Athina Bekakou | */
|
5884 | d0fe8c12 | Athina Bekakou | destroy: function() { |
5885 | d0fe8c12 | Athina Bekakou | delete this.record._changesToSync[this.name]; |
5886 | d0fe8c12 | Athina Bekakou | } |
5887 | d0fe8c12 | Athina Bekakou | }; |
5888 | d0fe8c12 | Athina Bekakou | |
5889 | d0fe8c12 | Athina Bekakou | })(); |
5890 | d0fe8c12 | Athina Bekakou | |
5891 | d0fe8c12 | Athina Bekakou | |
5892 | d0fe8c12 | Athina Bekakou | |
5893 | d0fe8c12 | Athina Bekakou | (function() {
|
5894 | d0fe8c12 | Athina Bekakou | /**
|
5895 | d0fe8c12 | Athina Bekakou | @module ember-data
|
5896 | d0fe8c12 | Athina Bekakou | */
|
5897 | d0fe8c12 | Athina Bekakou | |
5898 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
5899 | d0fe8c12 | Athina Bekakou | var forEach = Ember.EnumerableUtils.forEach;
|
5900 | d0fe8c12 | Athina Bekakou | |
5901 | d0fe8c12 | Athina Bekakou | /**
|
5902 | d0fe8c12 | Athina Bekakou | @class RelationshipChange
|
5903 | d0fe8c12 | Athina Bekakou | @namespace DS
|
5904 | d0fe8c12 | Athina Bekakou | @private
|
5905 | d0fe8c12 | Athina Bekakou | @construtor
|
5906 | d0fe8c12 | Athina Bekakou | */
|
5907 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange = function(options) { |
5908 | d0fe8c12 | Athina Bekakou | this.parentRecord = options.parentRecord;
|
5909 | d0fe8c12 | Athina Bekakou | this.childRecord = options.childRecord;
|
5910 | d0fe8c12 | Athina Bekakou | this.firstRecord = options.firstRecord;
|
5911 | d0fe8c12 | Athina Bekakou | this.firstRecordKind = options.firstRecordKind;
|
5912 | d0fe8c12 | Athina Bekakou | this.firstRecordName = options.firstRecordName;
|
5913 | d0fe8c12 | Athina Bekakou | this.secondRecord = options.secondRecord;
|
5914 | d0fe8c12 | Athina Bekakou | this.secondRecordKind = options.secondRecordKind;
|
5915 | d0fe8c12 | Athina Bekakou | this.secondRecordName = options.secondRecordName;
|
5916 | d0fe8c12 | Athina Bekakou | this.changeType = options.changeType;
|
5917 | d0fe8c12 | Athina Bekakou | this.store = options.store;
|
5918 | d0fe8c12 | Athina Bekakou | |
5919 | d0fe8c12 | Athina Bekakou | this.committed = {};
|
5920 | d0fe8c12 | Athina Bekakou | }; |
5921 | d0fe8c12 | Athina Bekakou | |
5922 | d0fe8c12 | Athina Bekakou | /**
|
5923 | d0fe8c12 | Athina Bekakou | @class RelationshipChangeAdd
|
5924 | d0fe8c12 | Athina Bekakou | @namespace DS
|
5925 | d0fe8c12 | Athina Bekakou | @private
|
5926 | d0fe8c12 | Athina Bekakou | @construtor
|
5927 | d0fe8c12 | Athina Bekakou | */
|
5928 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeAdd = function(options){ |
5929 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange.call(this, options);
|
5930 | d0fe8c12 | Athina Bekakou | }; |
5931 | d0fe8c12 | Athina Bekakou | |
5932 | d0fe8c12 | Athina Bekakou | /**
|
5933 | d0fe8c12 | Athina Bekakou | @class RelationshipChangeRemove
|
5934 | d0fe8c12 | Athina Bekakou | @namespace DS
|
5935 | d0fe8c12 | Athina Bekakou | @private
|
5936 | d0fe8c12 | Athina Bekakou | @construtor
|
5937 | d0fe8c12 | Athina Bekakou | */
|
5938 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeRemove = function(options){ |
5939 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange.call(this, options);
|
5940 | d0fe8c12 | Athina Bekakou | }; |
5941 | d0fe8c12 | Athina Bekakou | |
5942 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange.create = function(options) { |
5943 | d0fe8c12 | Athina Bekakou | return new DS.RelationshipChange(options); |
5944 | d0fe8c12 | Athina Bekakou | }; |
5945 | d0fe8c12 | Athina Bekakou | |
5946 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeAdd.create = function(options) { |
5947 | d0fe8c12 | Athina Bekakou | return new DS.RelationshipChangeAdd(options); |
5948 | d0fe8c12 | Athina Bekakou | }; |
5949 | d0fe8c12 | Athina Bekakou | |
5950 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeRemove.create = function(options) { |
5951 | d0fe8c12 | Athina Bekakou | return new DS.RelationshipChangeRemove(options); |
5952 | d0fe8c12 | Athina Bekakou | }; |
5953 | d0fe8c12 | Athina Bekakou | |
5954 | d0fe8c12 | Athina Bekakou | DS.OneToManyChange = {}; |
5955 | d0fe8c12 | Athina Bekakou | DS.OneToNoneChange = {}; |
5956 | d0fe8c12 | Athina Bekakou | DS.ManyToNoneChange = {}; |
5957 | d0fe8c12 | Athina Bekakou | DS.OneToOneChange = {}; |
5958 | d0fe8c12 | Athina Bekakou | DS.ManyToManyChange = {}; |
5959 | d0fe8c12 | Athina Bekakou | |
5960 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange._createChange = function(options){ |
5961 | d0fe8c12 | Athina Bekakou | if(options.changeType === "add"){ |
5962 | d0fe8c12 | Athina Bekakou | return DS.RelationshipChangeAdd.create(options);
|
5963 | d0fe8c12 | Athina Bekakou | } |
5964 | d0fe8c12 | Athina Bekakou | if(options.changeType === "remove"){ |
5965 | d0fe8c12 | Athina Bekakou | return DS.RelationshipChangeRemove.create(options);
|
5966 | d0fe8c12 | Athina Bekakou | } |
5967 | d0fe8c12 | Athina Bekakou | }; |
5968 | d0fe8c12 | Athina Bekakou | |
5969 | d0fe8c12 | Athina Bekakou | |
5970 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange.determineRelationshipType = function(recordType, knownSide){ |
5971 | d0fe8c12 | Athina Bekakou | var knownKey = knownSide.key, key, otherKind;
|
5972 | d0fe8c12 | Athina Bekakou | var knownKind = knownSide.kind;
|
5973 | d0fe8c12 | Athina Bekakou | |
5974 | d0fe8c12 | Athina Bekakou | var inverse = recordType.inverseFor(knownKey);
|
5975 | d0fe8c12 | Athina Bekakou | |
5976 | d0fe8c12 | Athina Bekakou | if (inverse){
|
5977 | d0fe8c12 | Athina Bekakou | key = inverse.name; |
5978 | d0fe8c12 | Athina Bekakou | otherKind = inverse.kind; |
5979 | d0fe8c12 | Athina Bekakou | } |
5980 | d0fe8c12 | Athina Bekakou | |
5981 | d0fe8c12 | Athina Bekakou | if (!inverse){
|
5982 | d0fe8c12 | Athina Bekakou | return knownKind === "belongsTo" ? "oneToNone" : "manyToNone"; |
5983 | d0fe8c12 | Athina Bekakou | } |
5984 | d0fe8c12 | Athina Bekakou | else{
|
5985 | d0fe8c12 | Athina Bekakou | if(otherKind === "belongsTo"){ |
5986 | d0fe8c12 | Athina Bekakou | return knownKind === "belongsTo" ? "oneToOne" : "manyToOne"; |
5987 | d0fe8c12 | Athina Bekakou | } |
5988 | d0fe8c12 | Athina Bekakou | else{
|
5989 | d0fe8c12 | Athina Bekakou | return knownKind === "belongsTo" ? "oneToMany" : "manyToMany"; |
5990 | d0fe8c12 | Athina Bekakou | } |
5991 | d0fe8c12 | Athina Bekakou | } |
5992 | d0fe8c12 | Athina Bekakou | |
5993 | d0fe8c12 | Athina Bekakou | }; |
5994 | d0fe8c12 | Athina Bekakou | |
5995 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange.createChange = function(firstRecord, secondRecord, store, options){ |
5996 | d0fe8c12 | Athina Bekakou | // Get the type of the child based on the child's client ID
|
5997 | d0fe8c12 | Athina Bekakou | var firstRecordType = firstRecord.constructor, changeType;
|
5998 | d0fe8c12 | Athina Bekakou | changeType = DS.RelationshipChange.determineRelationshipType(firstRecordType, options); |
5999 | d0fe8c12 | Athina Bekakou | if (changeType === "oneToMany"){ |
6000 | d0fe8c12 | Athina Bekakou | return DS.OneToManyChange.createChange(firstRecord, secondRecord, store, options);
|
6001 | d0fe8c12 | Athina Bekakou | } |
6002 | d0fe8c12 | Athina Bekakou | else if (changeType === "manyToOne"){ |
6003 | d0fe8c12 | Athina Bekakou | return DS.OneToManyChange.createChange(secondRecord, firstRecord, store, options);
|
6004 | d0fe8c12 | Athina Bekakou | } |
6005 | d0fe8c12 | Athina Bekakou | else if (changeType === "oneToNone"){ |
6006 | d0fe8c12 | Athina Bekakou | return DS.OneToNoneChange.createChange(firstRecord, secondRecord, store, options);
|
6007 | d0fe8c12 | Athina Bekakou | } |
6008 | d0fe8c12 | Athina Bekakou | else if (changeType === "manyToNone"){ |
6009 | d0fe8c12 | Athina Bekakou | return DS.ManyToNoneChange.createChange(firstRecord, secondRecord, store, options);
|
6010 | d0fe8c12 | Athina Bekakou | } |
6011 | d0fe8c12 | Athina Bekakou | else if (changeType === "oneToOne"){ |
6012 | d0fe8c12 | Athina Bekakou | return DS.OneToOneChange.createChange(firstRecord, secondRecord, store, options);
|
6013 | d0fe8c12 | Athina Bekakou | } |
6014 | d0fe8c12 | Athina Bekakou | else if (changeType === "manyToMany"){ |
6015 | d0fe8c12 | Athina Bekakou | return DS.ManyToManyChange.createChange(firstRecord, secondRecord, store, options);
|
6016 | d0fe8c12 | Athina Bekakou | } |
6017 | d0fe8c12 | Athina Bekakou | }; |
6018 | d0fe8c12 | Athina Bekakou | |
6019 | d0fe8c12 | Athina Bekakou | DS.OneToNoneChange.createChange = function(childRecord, parentRecord, store, options) { |
6020 | d0fe8c12 | Athina Bekakou | var key = options.key;
|
6021 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange._createChange({
|
6022 | d0fe8c12 | Athina Bekakou | parentRecord: parentRecord,
|
6023 | d0fe8c12 | Athina Bekakou | childRecord: childRecord,
|
6024 | d0fe8c12 | Athina Bekakou | firstRecord: childRecord,
|
6025 | d0fe8c12 | Athina Bekakou | store: store,
|
6026 | d0fe8c12 | Athina Bekakou | changeType: options.changeType,
|
6027 | d0fe8c12 | Athina Bekakou | firstRecordName: key,
|
6028 | d0fe8c12 | Athina Bekakou | firstRecordKind: "belongsTo" |
6029 | d0fe8c12 | Athina Bekakou | }); |
6030 | d0fe8c12 | Athina Bekakou | |
6031 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
|
6032 | d0fe8c12 | Athina Bekakou | |
6033 | d0fe8c12 | Athina Bekakou | return change;
|
6034 | d0fe8c12 | Athina Bekakou | }; |
6035 | d0fe8c12 | Athina Bekakou | |
6036 | d0fe8c12 | Athina Bekakou | DS.ManyToNoneChange.createChange = function(childRecord, parentRecord, store, options) { |
6037 | d0fe8c12 | Athina Bekakou | var key = options.key;
|
6038 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange._createChange({
|
6039 | d0fe8c12 | Athina Bekakou | parentRecord: childRecord,
|
6040 | d0fe8c12 | Athina Bekakou | childRecord: parentRecord,
|
6041 | d0fe8c12 | Athina Bekakou | secondRecord: childRecord,
|
6042 | d0fe8c12 | Athina Bekakou | store: store,
|
6043 | d0fe8c12 | Athina Bekakou | changeType: options.changeType,
|
6044 | d0fe8c12 | Athina Bekakou | secondRecordName: options.key,
|
6045 | d0fe8c12 | Athina Bekakou | secondRecordKind: "hasMany" |
6046 | d0fe8c12 | Athina Bekakou | }); |
6047 | d0fe8c12 | Athina Bekakou | |
6048 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
|
6049 | d0fe8c12 | Athina Bekakou | return change;
|
6050 | d0fe8c12 | Athina Bekakou | }; |
6051 | d0fe8c12 | Athina Bekakou | |
6052 | d0fe8c12 | Athina Bekakou | |
6053 | d0fe8c12 | Athina Bekakou | DS.ManyToManyChange.createChange = function(childRecord, parentRecord, store, options) { |
6054 | d0fe8c12 | Athina Bekakou | // If the name of the belongsTo side of the relationship is specified,
|
6055 | d0fe8c12 | Athina Bekakou | // use that
|
6056 | d0fe8c12 | Athina Bekakou | // If the type of the parent is specified, look it up on the child's type
|
6057 | d0fe8c12 | Athina Bekakou | // definition.
|
6058 | d0fe8c12 | Athina Bekakou | var key = options.key;
|
6059 | d0fe8c12 | Athina Bekakou | |
6060 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange._createChange({
|
6061 | d0fe8c12 | Athina Bekakou | parentRecord: parentRecord,
|
6062 | d0fe8c12 | Athina Bekakou | childRecord: childRecord,
|
6063 | d0fe8c12 | Athina Bekakou | firstRecord: childRecord,
|
6064 | d0fe8c12 | Athina Bekakou | secondRecord: parentRecord,
|
6065 | d0fe8c12 | Athina Bekakou | firstRecordKind: "hasMany", |
6066 | d0fe8c12 | Athina Bekakou | secondRecordKind: "hasMany", |
6067 | d0fe8c12 | Athina Bekakou | store: store,
|
6068 | d0fe8c12 | Athina Bekakou | changeType: options.changeType,
|
6069 | d0fe8c12 | Athina Bekakou | firstRecordName: key
|
6070 | d0fe8c12 | Athina Bekakou | }); |
6071 | d0fe8c12 | Athina Bekakou | |
6072 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
|
6073 | d0fe8c12 | Athina Bekakou | |
6074 | d0fe8c12 | Athina Bekakou | |
6075 | d0fe8c12 | Athina Bekakou | return change;
|
6076 | d0fe8c12 | Athina Bekakou | }; |
6077 | d0fe8c12 | Athina Bekakou | |
6078 | d0fe8c12 | Athina Bekakou | DS.OneToOneChange.createChange = function(childRecord, parentRecord, store, options) { |
6079 | d0fe8c12 | Athina Bekakou | var key;
|
6080 | d0fe8c12 | Athina Bekakou | |
6081 | d0fe8c12 | Athina Bekakou | // If the name of the belongsTo side of the relationship is specified,
|
6082 | d0fe8c12 | Athina Bekakou | // use that
|
6083 | d0fe8c12 | Athina Bekakou | // If the type of the parent is specified, look it up on the child's type
|
6084 | d0fe8c12 | Athina Bekakou | // definition.
|
6085 | d0fe8c12 | Athina Bekakou | if (options.parentType) {
|
6086 | d0fe8c12 | Athina Bekakou | key = options.parentType.inverseFor(options.key).name; |
6087 | d0fe8c12 | Athina Bekakou | } else if (options.key) { |
6088 | d0fe8c12 | Athina Bekakou | key = options.key; |
6089 | d0fe8c12 | Athina Bekakou | } else {
|
6090 | d0fe8c12 | Athina Bekakou | Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false); |
6091 | d0fe8c12 | Athina Bekakou | } |
6092 | d0fe8c12 | Athina Bekakou | |
6093 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange._createChange({
|
6094 | d0fe8c12 | Athina Bekakou | parentRecord: parentRecord,
|
6095 | d0fe8c12 | Athina Bekakou | childRecord: childRecord,
|
6096 | d0fe8c12 | Athina Bekakou | firstRecord: childRecord,
|
6097 | d0fe8c12 | Athina Bekakou | secondRecord: parentRecord,
|
6098 | d0fe8c12 | Athina Bekakou | firstRecordKind: "belongsTo", |
6099 | d0fe8c12 | Athina Bekakou | secondRecordKind: "belongsTo", |
6100 | d0fe8c12 | Athina Bekakou | store: store,
|
6101 | d0fe8c12 | Athina Bekakou | changeType: options.changeType,
|
6102 | d0fe8c12 | Athina Bekakou | firstRecordName: key
|
6103 | d0fe8c12 | Athina Bekakou | }); |
6104 | d0fe8c12 | Athina Bekakou | |
6105 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
|
6106 | d0fe8c12 | Athina Bekakou | |
6107 | d0fe8c12 | Athina Bekakou | |
6108 | d0fe8c12 | Athina Bekakou | return change;
|
6109 | d0fe8c12 | Athina Bekakou | }; |
6110 | d0fe8c12 | Athina Bekakou | |
6111 | d0fe8c12 | Athina Bekakou | DS.OneToOneChange.maintainInvariant = function(options, store, childRecord, key){ |
6112 | d0fe8c12 | Athina Bekakou | if (options.changeType === "add" && store.recordIsMaterialized(childRecord)) { |
6113 | d0fe8c12 | Athina Bekakou | var oldParent = get(childRecord, key);
|
6114 | d0fe8c12 | Athina Bekakou | if (oldParent){
|
6115 | d0fe8c12 | Athina Bekakou | var correspondingChange = DS.OneToOneChange.createChange(childRecord, oldParent, store, {
|
6116 | d0fe8c12 | Athina Bekakou | parentType: options.parentType,
|
6117 | d0fe8c12 | Athina Bekakou | hasManyName: options.hasManyName,
|
6118 | d0fe8c12 | Athina Bekakou | changeType: "remove", |
6119 | d0fe8c12 | Athina Bekakou | key: options.key
|
6120 | d0fe8c12 | Athina Bekakou | }); |
6121 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, options.parentRecord , null, correspondingChange);
|
6122 | d0fe8c12 | Athina Bekakou | correspondingChange.sync(); |
6123 | d0fe8c12 | Athina Bekakou | } |
6124 | d0fe8c12 | Athina Bekakou | } |
6125 | d0fe8c12 | Athina Bekakou | }; |
6126 | d0fe8c12 | Athina Bekakou | |
6127 | d0fe8c12 | Athina Bekakou | DS.OneToManyChange.createChange = function(childRecord, parentRecord, store, options) { |
6128 | d0fe8c12 | Athina Bekakou | var key;
|
6129 | d0fe8c12 | Athina Bekakou | |
6130 | d0fe8c12 | Athina Bekakou | // If the name of the belongsTo side of the relationship is specified,
|
6131 | d0fe8c12 | Athina Bekakou | // use that
|
6132 | d0fe8c12 | Athina Bekakou | // If the type of the parent is specified, look it up on the child's type
|
6133 | d0fe8c12 | Athina Bekakou | // definition.
|
6134 | d0fe8c12 | Athina Bekakou | if (options.parentType) {
|
6135 | d0fe8c12 | Athina Bekakou | key = options.parentType.inverseFor(options.key).name; |
6136 | d0fe8c12 | Athina Bekakou | DS.OneToManyChange.maintainInvariant( options, store, childRecord, key ); |
6137 | d0fe8c12 | Athina Bekakou | } else if (options.key) { |
6138 | d0fe8c12 | Athina Bekakou | key = options.key; |
6139 | d0fe8c12 | Athina Bekakou | } else {
|
6140 | d0fe8c12 | Athina Bekakou | Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false); |
6141 | d0fe8c12 | Athina Bekakou | } |
6142 | d0fe8c12 | Athina Bekakou | |
6143 | d0fe8c12 | Athina Bekakou | var change = DS.RelationshipChange._createChange({
|
6144 | d0fe8c12 | Athina Bekakou | parentRecord: parentRecord,
|
6145 | d0fe8c12 | Athina Bekakou | childRecord: childRecord,
|
6146 | d0fe8c12 | Athina Bekakou | firstRecord: childRecord,
|
6147 | d0fe8c12 | Athina Bekakou | secondRecord: parentRecord,
|
6148 | d0fe8c12 | Athina Bekakou | firstRecordKind: "belongsTo", |
6149 | d0fe8c12 | Athina Bekakou | secondRecordKind: "hasMany", |
6150 | d0fe8c12 | Athina Bekakou | store: store,
|
6151 | d0fe8c12 | Athina Bekakou | changeType: options.changeType,
|
6152 | d0fe8c12 | Athina Bekakou | firstRecordName: key
|
6153 | d0fe8c12 | Athina Bekakou | }); |
6154 | d0fe8c12 | Athina Bekakou | |
6155 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, parentRecord, change.getSecondRecordName(), change); |
6156 | d0fe8c12 | Athina Bekakou | |
6157 | d0fe8c12 | Athina Bekakou | |
6158 | d0fe8c12 | Athina Bekakou | return change;
|
6159 | d0fe8c12 | Athina Bekakou | }; |
6160 | d0fe8c12 | Athina Bekakou | |
6161 | d0fe8c12 | Athina Bekakou | |
6162 | d0fe8c12 | Athina Bekakou | DS.OneToManyChange.maintainInvariant = function(options, store, childRecord, key){ |
6163 | d0fe8c12 | Athina Bekakou | if (options.changeType === "add" && childRecord) { |
6164 | d0fe8c12 | Athina Bekakou | var oldParent = get(childRecord, key);
|
6165 | d0fe8c12 | Athina Bekakou | if (oldParent){
|
6166 | d0fe8c12 | Athina Bekakou | var correspondingChange = DS.OneToManyChange.createChange(childRecord, oldParent, store, {
|
6167 | d0fe8c12 | Athina Bekakou | parentType: options.parentType,
|
6168 | d0fe8c12 | Athina Bekakou | hasManyName: options.hasManyName,
|
6169 | d0fe8c12 | Athina Bekakou | changeType: "remove", |
6170 | d0fe8c12 | Athina Bekakou | key: options.key
|
6171 | d0fe8c12 | Athina Bekakou | }); |
6172 | d0fe8c12 | Athina Bekakou | store.addRelationshipChangeFor(childRecord, key, options.parentRecord, correspondingChange.getSecondRecordName(), correspondingChange); |
6173 | d0fe8c12 | Athina Bekakou | correspondingChange.sync(); |
6174 | d0fe8c12 | Athina Bekakou | } |
6175 | d0fe8c12 | Athina Bekakou | } |
6176 | d0fe8c12 | Athina Bekakou | }; |
6177 | d0fe8c12 | Athina Bekakou | |
6178 | d0fe8c12 | Athina Bekakou | /**
|
6179 | d0fe8c12 | Athina Bekakou | @class RelationshipChange
|
6180 | d0fe8c12 | Athina Bekakou | @namespace DS
|
6181 | d0fe8c12 | Athina Bekakou | */
|
6182 | d0fe8c12 | Athina Bekakou | DS.RelationshipChange.prototype = { |
6183 | d0fe8c12 | Athina Bekakou | |
6184 | d0fe8c12 | Athina Bekakou | getSecondRecordName: function() { |
6185 | d0fe8c12 | Athina Bekakou | var name = this.secondRecordName, parent; |
6186 | d0fe8c12 | Athina Bekakou | |
6187 | d0fe8c12 | Athina Bekakou | if (!name) {
|
6188 | d0fe8c12 | Athina Bekakou | parent = this.secondRecord;
|
6189 | d0fe8c12 | Athina Bekakou | if (!parent) { return; } |
6190 | d0fe8c12 | Athina Bekakou | |
6191 | d0fe8c12 | Athina Bekakou | var childType = this.firstRecord.constructor; |
6192 | d0fe8c12 | Athina Bekakou | var inverse = childType.inverseFor(this.firstRecordName); |
6193 | d0fe8c12 | Athina Bekakou | this.secondRecordName = inverse.name;
|
6194 | d0fe8c12 | Athina Bekakou | } |
6195 | d0fe8c12 | Athina Bekakou | |
6196 | d0fe8c12 | Athina Bekakou | return this.secondRecordName; |
6197 | d0fe8c12 | Athina Bekakou | }, |
6198 | d0fe8c12 | Athina Bekakou | |
6199 | d0fe8c12 | Athina Bekakou | /**
|
6200 | d0fe8c12 | Athina Bekakou | Get the name of the relationship on the belongsTo side.
|
6201 | d0fe8c12 | Athina Bekakou | |
6202 | d0fe8c12 | Athina Bekakou | @method getFirstRecordName
|
6203 | d0fe8c12 | Athina Bekakou | @return {String}
|
6204 | d0fe8c12 | Athina Bekakou | */
|
6205 | d0fe8c12 | Athina Bekakou | getFirstRecordName: function() { |
6206 | d0fe8c12 | Athina Bekakou | var name = this.firstRecordName; |
6207 | d0fe8c12 | Athina Bekakou | return name;
|
6208 | d0fe8c12 | Athina Bekakou | }, |
6209 | d0fe8c12 | Athina Bekakou | |
6210 | d0fe8c12 | Athina Bekakou | /**
|
6211 | d0fe8c12 | Athina Bekakou | @method destroy
|
6212 | d0fe8c12 | Athina Bekakou | @private
|
6213 | d0fe8c12 | Athina Bekakou | */
|
6214 | d0fe8c12 | Athina Bekakou | destroy: function() { |
6215 | d0fe8c12 | Athina Bekakou | var childRecord = this.childRecord, |
6216 | d0fe8c12 | Athina Bekakou | belongsToName = this.getFirstRecordName(),
|
6217 | d0fe8c12 | Athina Bekakou | hasManyName = this.getSecondRecordName(),
|
6218 | d0fe8c12 | Athina Bekakou | store = this.store;
|
6219 | d0fe8c12 | Athina Bekakou | |
6220 | d0fe8c12 | Athina Bekakou | store.removeRelationshipChangeFor(childRecord, belongsToName, this.parentRecord, hasManyName, this.changeType); |
6221 | d0fe8c12 | Athina Bekakou | }, |
6222 | d0fe8c12 | Athina Bekakou | |
6223 | d0fe8c12 | Athina Bekakou | getSecondRecord: function(){ |
6224 | d0fe8c12 | Athina Bekakou | return this.secondRecord; |
6225 | d0fe8c12 | Athina Bekakou | }, |
6226 | d0fe8c12 | Athina Bekakou | |
6227 | d0fe8c12 | Athina Bekakou | /**
|
6228 | d0fe8c12 | Athina Bekakou | @method getFirstRecord
|
6229 | d0fe8c12 | Athina Bekakou | @private
|
6230 | d0fe8c12 | Athina Bekakou | */
|
6231 | d0fe8c12 | Athina Bekakou | getFirstRecord: function() { |
6232 | d0fe8c12 | Athina Bekakou | return this.firstRecord; |
6233 | d0fe8c12 | Athina Bekakou | }, |
6234 | d0fe8c12 | Athina Bekakou | |
6235 | d0fe8c12 | Athina Bekakou | coalesce: function(){ |
6236 | d0fe8c12 | Athina Bekakou | var relationshipPairs = this.store.relationshipChangePairsFor(this.firstRecord); |
6237 | d0fe8c12 | Athina Bekakou | forEach(relationshipPairs, function(pair){
|
6238 | d0fe8c12 | Athina Bekakou | var addedChange = pair["add"]; |
6239 | d0fe8c12 | Athina Bekakou | var removedChange = pair["remove"]; |
6240 | d0fe8c12 | Athina Bekakou | if(addedChange && removedChange) {
|
6241 | d0fe8c12 | Athina Bekakou | addedChange.destroy(); |
6242 | d0fe8c12 | Athina Bekakou | removedChange.destroy(); |
6243 | d0fe8c12 | Athina Bekakou | } |
6244 | d0fe8c12 | Athina Bekakou | }); |
6245 | d0fe8c12 | Athina Bekakou | } |
6246 | d0fe8c12 | Athina Bekakou | }; |
6247 | d0fe8c12 | Athina Bekakou | |
6248 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeAdd.prototype = Ember.create(DS.RelationshipChange.create({})); |
6249 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeRemove.prototype = Ember.create(DS.RelationshipChange.create({})); |
6250 | d0fe8c12 | Athina Bekakou | |
6251 | d0fe8c12 | Athina Bekakou | // the object is a value, and not a promise
|
6252 | d0fe8c12 | Athina Bekakou | function isValue(object) { |
6253 | d0fe8c12 | Athina Bekakou | return typeof object === 'object' && (!object.then || typeof object.then !== 'function'); |
6254 | d0fe8c12 | Athina Bekakou | } |
6255 | d0fe8c12 | Athina Bekakou | |
6256 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeAdd.prototype.changeType = "add";
|
6257 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeAdd.prototype.sync = function() { |
6258 | d0fe8c12 | Athina Bekakou | var secondRecordName = this.getSecondRecordName(), |
6259 | d0fe8c12 | Athina Bekakou | firstRecordName = this.getFirstRecordName(),
|
6260 | d0fe8c12 | Athina Bekakou | firstRecord = this.getFirstRecord(),
|
6261 | d0fe8c12 | Athina Bekakou | secondRecord = this.getSecondRecord();
|
6262 | d0fe8c12 | Athina Bekakou | |
6263 | d0fe8c12 | Athina Bekakou | //Ember.assert("You specified a hasMany (" + hasManyName + ") on " + (!belongsToName && (newParent || oldParent || this.lastParent).constructor) + " but did not specify an inverse belongsTo on " + child.constructor, belongsToName);
|
6264 | d0fe8c12 | Athina Bekakou | //Ember.assert("You specified a belongsTo (" + belongsToName + ") on " + child.constructor + " but did not specify an inverse hasMany on " + (!hasManyName && (newParent || oldParent || this.lastParentRecord).constructor), hasManyName);
|
6265 | d0fe8c12 | Athina Bekakou | |
6266 | d0fe8c12 | Athina Bekakou | if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) { |
6267 | d0fe8c12 | Athina Bekakou | if(this.secondRecordKind === "belongsTo"){ |
6268 | d0fe8c12 | Athina Bekakou | secondRecord.suspendRelationshipObservers(function(){
|
6269 | d0fe8c12 | Athina Bekakou | set(secondRecord, secondRecordName, firstRecord); |
6270 | d0fe8c12 | Athina Bekakou | }); |
6271 | d0fe8c12 | Athina Bekakou | |
6272 | d0fe8c12 | Athina Bekakou | } |
6273 | d0fe8c12 | Athina Bekakou | else if(this.secondRecordKind === "hasMany"){ |
6274 | d0fe8c12 | Athina Bekakou | secondRecord.suspendRelationshipObservers(function(){
|
6275 | d0fe8c12 | Athina Bekakou | var relationship = get(secondRecord, secondRecordName);
|
6276 | d0fe8c12 | Athina Bekakou | if (isValue(relationship)) { relationship.addObject(firstRecord); }
|
6277 | d0fe8c12 | Athina Bekakou | }); |
6278 | d0fe8c12 | Athina Bekakou | } |
6279 | d0fe8c12 | Athina Bekakou | } |
6280 | d0fe8c12 | Athina Bekakou | |
6281 | d0fe8c12 | Athina Bekakou | if (firstRecord instanceof DS.Model && secondRecord instanceof DS.Model && get(firstRecord, firstRecordName) !== secondRecord) { |
6282 | d0fe8c12 | Athina Bekakou | if(this.firstRecordKind === "belongsTo"){ |
6283 | d0fe8c12 | Athina Bekakou | firstRecord.suspendRelationshipObservers(function(){
|
6284 | d0fe8c12 | Athina Bekakou | set(firstRecord, firstRecordName, secondRecord); |
6285 | d0fe8c12 | Athina Bekakou | }); |
6286 | d0fe8c12 | Athina Bekakou | } |
6287 | d0fe8c12 | Athina Bekakou | else if(this.firstRecordKind === "hasMany"){ |
6288 | d0fe8c12 | Athina Bekakou | firstRecord.suspendRelationshipObservers(function(){
|
6289 | d0fe8c12 | Athina Bekakou | var relationship = get(firstRecord, firstRecordName);
|
6290 | d0fe8c12 | Athina Bekakou | if (isValue(relationship)) { relationship.addObject(secondRecord); }
|
6291 | d0fe8c12 | Athina Bekakou | }); |
6292 | d0fe8c12 | Athina Bekakou | } |
6293 | d0fe8c12 | Athina Bekakou | } |
6294 | d0fe8c12 | Athina Bekakou | |
6295 | d0fe8c12 | Athina Bekakou | this.coalesce();
|
6296 | d0fe8c12 | Athina Bekakou | }; |
6297 | d0fe8c12 | Athina Bekakou | |
6298 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeRemove.prototype.changeType = "remove";
|
6299 | d0fe8c12 | Athina Bekakou | DS.RelationshipChangeRemove.prototype.sync = function() { |
6300 | d0fe8c12 | Athina Bekakou | var secondRecordName = this.getSecondRecordName(), |
6301 | d0fe8c12 | Athina Bekakou | firstRecordName = this.getFirstRecordName(),
|
6302 | d0fe8c12 | Athina Bekakou | firstRecord = this.getFirstRecord(),
|
6303 | d0fe8c12 | Athina Bekakou | secondRecord = this.getSecondRecord();
|
6304 | d0fe8c12 | Athina Bekakou | |
6305 | d0fe8c12 | Athina Bekakou | //Ember.assert("You specified a hasMany (" + hasManyName + ") on " + (!belongsToName && (newParent || oldParent || this.lastParent).constructor) + " but did not specify an inverse belongsTo on " + child.constructor, belongsToName);
|
6306 | d0fe8c12 | Athina Bekakou | //Ember.assert("You specified a belongsTo (" + belongsToName + ") on " + child.constructor + " but did not specify an inverse hasMany on " + (!hasManyName && (newParent || oldParent || this.lastParentRecord).constructor), hasManyName);
|
6307 | d0fe8c12 | Athina Bekakou | |
6308 | d0fe8c12 | Athina Bekakou | if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) { |
6309 | d0fe8c12 | Athina Bekakou | if(this.secondRecordKind === "belongsTo"){ |
6310 | d0fe8c12 | Athina Bekakou | secondRecord.suspendRelationshipObservers(function(){
|
6311 | d0fe8c12 | Athina Bekakou | set(secondRecord, secondRecordName, null);
|
6312 | d0fe8c12 | Athina Bekakou | }); |
6313 | d0fe8c12 | Athina Bekakou | } |
6314 | d0fe8c12 | Athina Bekakou | else if(this.secondRecordKind === "hasMany"){ |
6315 | d0fe8c12 | Athina Bekakou | secondRecord.suspendRelationshipObservers(function(){
|
6316 | d0fe8c12 | Athina Bekakou | var relationship = get(secondRecord, secondRecordName);
|
6317 | d0fe8c12 | Athina Bekakou | if (isValue(relationship)) { relationship.removeObject(firstRecord); }
|
6318 | d0fe8c12 | Athina Bekakou | }); |
6319 | d0fe8c12 | Athina Bekakou | } |
6320 | d0fe8c12 | Athina Bekakou | } |
6321 | d0fe8c12 | Athina Bekakou | |
6322 | d0fe8c12 | Athina Bekakou | if (firstRecord instanceof DS.Model && get(firstRecord, firstRecordName)) { |
6323 | d0fe8c12 | Athina Bekakou | if(this.firstRecordKind === "belongsTo"){ |
6324 | d0fe8c12 | Athina Bekakou | firstRecord.suspendRelationshipObservers(function(){
|
6325 | d0fe8c12 | Athina Bekakou | set(firstRecord, firstRecordName, null);
|
6326 | d0fe8c12 | Athina Bekakou | }); |
6327 | d0fe8c12 | Athina Bekakou | } |
6328 | d0fe8c12 | Athina Bekakou | else if(this.firstRecordKind === "hasMany"){ |
6329 | d0fe8c12 | Athina Bekakou | firstRecord.suspendRelationshipObservers(function(){
|
6330 | d0fe8c12 | Athina Bekakou | var relationship = get(firstRecord, firstRecordName);
|
6331 | d0fe8c12 | Athina Bekakou | if (isValue(relationship)) { relationship.removeObject(secondRecord); }
|
6332 | d0fe8c12 | Athina Bekakou | }); |
6333 | d0fe8c12 | Athina Bekakou | } |
6334 | d0fe8c12 | Athina Bekakou | } |
6335 | d0fe8c12 | Athina Bekakou | |
6336 | d0fe8c12 | Athina Bekakou | this.coalesce();
|
6337 | d0fe8c12 | Athina Bekakou | }; |
6338 | d0fe8c12 | Athina Bekakou | |
6339 | d0fe8c12 | Athina Bekakou | })(); |
6340 | d0fe8c12 | Athina Bekakou | |
6341 | d0fe8c12 | Athina Bekakou | |
6342 | d0fe8c12 | Athina Bekakou | |
6343 | d0fe8c12 | Athina Bekakou | (function() {
|
6344 | d0fe8c12 | Athina Bekakou | /**
|
6345 | d0fe8c12 | Athina Bekakou | @module ember-data
|
6346 | d0fe8c12 | Athina Bekakou | */
|
6347 | d0fe8c12 | Athina Bekakou | |
6348 | d0fe8c12 | Athina Bekakou | })(); |
6349 | d0fe8c12 | Athina Bekakou | |
6350 | d0fe8c12 | Athina Bekakou | |
6351 | d0fe8c12 | Athina Bekakou | |
6352 | d0fe8c12 | Athina Bekakou | (function() {
|
6353 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set,
|
6354 | d0fe8c12 | Athina Bekakou | isNone = Ember.isNone; |
6355 | d0fe8c12 | Athina Bekakou | |
6356 | d0fe8c12 | Athina Bekakou | /**
|
6357 | d0fe8c12 | Athina Bekakou | @module ember-data
|
6358 | d0fe8c12 | Athina Bekakou | */
|
6359 | d0fe8c12 | Athina Bekakou | |
6360 | d0fe8c12 | Athina Bekakou | function asyncBelongsTo(type, options, meta) { |
6361 | d0fe8c12 | Athina Bekakou | return Ember.computed(function(key, value) { |
6362 | d0fe8c12 | Athina Bekakou | var data = get(this, 'data'), |
6363 | d0fe8c12 | Athina Bekakou | store = get(this, 'store'), |
6364 | d0fe8c12 | Athina Bekakou | promiseLabel = "DS: Async belongsTo " + this + " : " + key; |
6365 | d0fe8c12 | Athina Bekakou | |
6366 | d0fe8c12 | Athina Bekakou | if (arguments.length === 2) { |
6367 | d0fe8c12 | Athina Bekakou | Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof store.modelFor(type)); |
6368 | d0fe8c12 | Athina Bekakou | return value === undefined ? null : DS.PromiseObject.create({ promise: Ember.RSVP.resolve(value, promiseLabel) }); |
6369 | d0fe8c12 | Athina Bekakou | } |
6370 | d0fe8c12 | Athina Bekakou | |
6371 | d0fe8c12 | Athina Bekakou | var link = data.links && data.links[key],
|
6372 | d0fe8c12 | Athina Bekakou | belongsTo = data[key]; |
6373 | d0fe8c12 | Athina Bekakou | |
6374 | d0fe8c12 | Athina Bekakou | if(!isNone(belongsTo)) {
|
6375 | d0fe8c12 | Athina Bekakou | var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo, promiseLabel);
|
6376 | d0fe8c12 | Athina Bekakou | return DS.PromiseObject.create({ promise: promise}); |
6377 | d0fe8c12 | Athina Bekakou | } else if (link) { |
6378 | d0fe8c12 | Athina Bekakou | var resolver = Ember.RSVP.defer("DS: Async belongsTo (link) " + this + " : " + key); |
6379 | d0fe8c12 | Athina Bekakou | store.findBelongsTo(this, link, meta, resolver);
|
6380 | d0fe8c12 | Athina Bekakou | return DS.PromiseObject.create({ promise: resolver.promise }); |
6381 | d0fe8c12 | Athina Bekakou | } else {
|
6382 | d0fe8c12 | Athina Bekakou | return null; |
6383 | d0fe8c12 | Athina Bekakou | } |
6384 | d0fe8c12 | Athina Bekakou | }).property('data').meta(meta);
|
6385 | d0fe8c12 | Athina Bekakou | } |
6386 | d0fe8c12 | Athina Bekakou | |
6387 | d0fe8c12 | Athina Bekakou | /**
|
6388 | d0fe8c12 | Athina Bekakou | `DS.belongsTo` is used to define One-To-One and One-To-Many
|
6389 | d0fe8c12 | Athina Bekakou | relationships on a [DS.Model](DS.Model.html).
|
6390 | d0fe8c12 | Athina Bekakou | |
6391 | d0fe8c12 | Athina Bekakou | |
6392 | d0fe8c12 | Athina Bekakou | `DS.belongsTo` takes an optional hash as a second parameter, currently
|
6393 | d0fe8c12 | Athina Bekakou | supported options are:
|
6394 | d0fe8c12 | Athina Bekakou | |
6395 | d0fe8c12 | Athina Bekakou | - `async`: A boolean value used to explicitly declare this to be an async relationship.
|
6396 | d0fe8c12 | Athina Bekakou | - `inverse`: A string used to identify the inverse property on a
|
6397 | d0fe8c12 | Athina Bekakou | related model in a One-To-Many relationship. See [Explicit Inverses](#toc_explicit-inverses)
|
6398 | d0fe8c12 | Athina Bekakou | |
6399 | d0fe8c12 | Athina Bekakou | #### One-To-One
|
6400 | d0fe8c12 | Athina Bekakou | To declare a one-to-one relationship between two models, use
|
6401 | d0fe8c12 | Athina Bekakou | `DS.belongsTo`:
|
6402 | d0fe8c12 | Athina Bekakou | |
6403 | d0fe8c12 | Athina Bekakou | ```javascript
|
6404 | d0fe8c12 | Athina Bekakou | App.User = DS.Model.extend({
|
6405 | d0fe8c12 | Athina Bekakou | profile: DS.belongsTo('profile')
|
6406 | d0fe8c12 | Athina Bekakou | });
|
6407 | d0fe8c12 | Athina Bekakou | |
6408 | d0fe8c12 | Athina Bekakou | App.Profile = DS.Model.extend({
|
6409 | d0fe8c12 | Athina Bekakou | user: DS.belongsTo('user')
|
6410 | d0fe8c12 | Athina Bekakou | });
|
6411 | d0fe8c12 | Athina Bekakou | ```
|
6412 | d0fe8c12 | Athina Bekakou | |
6413 | d0fe8c12 | Athina Bekakou | #### One-To-Many
|
6414 | d0fe8c12 | Athina Bekakou | To declare a one-to-many relationship between two models, use
|
6415 | d0fe8c12 | Athina Bekakou | `DS.belongsTo` in combination with `DS.hasMany`, like this:
|
6416 | d0fe8c12 | Athina Bekakou | |
6417 | d0fe8c12 | Athina Bekakou | ```javascript
|
6418 | d0fe8c12 | Athina Bekakou | App.Post = DS.Model.extend({
|
6419 | d0fe8c12 | Athina Bekakou | comments: DS.hasMany('comment')
|
6420 | d0fe8c12 | Athina Bekakou | });
|
6421 | d0fe8c12 | Athina Bekakou | |
6422 | d0fe8c12 | Athina Bekakou | App.Comment = DS.Model.extend({
|
6423 | d0fe8c12 | Athina Bekakou | post: DS.belongsTo('post')
|
6424 | d0fe8c12 | Athina Bekakou | });
|
6425 | d0fe8c12 | Athina Bekakou | ```
|
6426 | d0fe8c12 | Athina Bekakou | |
6427 | d0fe8c12 | Athina Bekakou | @namespace
|
6428 | d0fe8c12 | Athina Bekakou | @method belongsTo
|
6429 | d0fe8c12 | Athina Bekakou | @for DS
|
6430 | d0fe8c12 | Athina Bekakou | @param {String or DS.Model} type the model type of the relationship
|
6431 | d0fe8c12 | Athina Bekakou | @param {Object} options a hash of options
|
6432 | d0fe8c12 | Athina Bekakou | @return {Ember.computed} relationship
|
6433 | d0fe8c12 | Athina Bekakou | */
|
6434 | d0fe8c12 | Athina Bekakou | DS.belongsTo = function(type, options) { |
6435 | d0fe8c12 | Athina Bekakou | if (typeof type === 'object') { |
6436 | d0fe8c12 | Athina Bekakou | options = type; |
6437 | d0fe8c12 | Athina Bekakou | type = undefined;
|
6438 | d0fe8c12 | Athina Bekakou | } else {
|
6439 | d0fe8c12 | Athina Bekakou | Ember.assert("The first argument DS.belongsTo must be a model type or string, like DS.belongsTo(App.Person)", !!type && (typeof type === 'string' || DS.Model.detect(type))); |
6440 | d0fe8c12 | Athina Bekakou | } |
6441 | d0fe8c12 | Athina Bekakou | |
6442 | d0fe8c12 | Athina Bekakou | options = options || {}; |
6443 | d0fe8c12 | Athina Bekakou | |
6444 | d0fe8c12 | Athina Bekakou | var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo' }; |
6445 | d0fe8c12 | Athina Bekakou | |
6446 | d0fe8c12 | Athina Bekakou | if (options.async) {
|
6447 | d0fe8c12 | Athina Bekakou | return asyncBelongsTo(type, options, meta);
|
6448 | d0fe8c12 | Athina Bekakou | } |
6449 | d0fe8c12 | Athina Bekakou | |
6450 | d0fe8c12 | Athina Bekakou | return Ember.computed(function(key, value) { |
6451 | d0fe8c12 | Athina Bekakou | var data = get(this, 'data'), |
6452 | d0fe8c12 | Athina Bekakou | store = get(this, 'store'), belongsTo, typeClass; |
6453 | d0fe8c12 | Athina Bekakou | |
6454 | d0fe8c12 | Athina Bekakou | if (typeof type === 'string') { |
6455 | d0fe8c12 | Athina Bekakou | typeClass = store.modelFor(type); |
6456 | d0fe8c12 | Athina Bekakou | } else {
|
6457 | d0fe8c12 | Athina Bekakou | typeClass = type; |
6458 | d0fe8c12 | Athina Bekakou | } |
6459 | d0fe8c12 | Athina Bekakou | |
6460 | d0fe8c12 | Athina Bekakou | if (arguments.length === 2) { |
6461 | d0fe8c12 | Athina Bekakou | Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof typeClass); |
6462 | d0fe8c12 | Athina Bekakou | return value === undefined ? null : value; |
6463 | d0fe8c12 | Athina Bekakou | } |
6464 | d0fe8c12 | Athina Bekakou | |
6465 | d0fe8c12 | Athina Bekakou | belongsTo = data[key]; |
6466 | d0fe8c12 | Athina Bekakou | |
6467 | d0fe8c12 | Athina Bekakou | if (isNone(belongsTo)) { return null; } |
6468 | d0fe8c12 | Athina Bekakou | |
6469 | d0fe8c12 | Athina Bekakou | store.fetchRecord(belongsTo); |
6470 | d0fe8c12 | Athina Bekakou | |
6471 | d0fe8c12 | Athina Bekakou | return belongsTo;
|
6472 | d0fe8c12 | Athina Bekakou | }).property('data').meta(meta);
|
6473 | d0fe8c12 | Athina Bekakou | }; |
6474 | d0fe8c12 | Athina Bekakou | |
6475 | d0fe8c12 | Athina Bekakou | /**
|
6476 | d0fe8c12 | Athina Bekakou | These observers observe all `belongsTo` relationships on the record. See
|
6477 | d0fe8c12 | Athina Bekakou | `relationships/ext` to see how these observers get their dependencies.
|
6478 | d0fe8c12 | Athina Bekakou | |
6479 | d0fe8c12 | Athina Bekakou | @class Model
|
6480 | d0fe8c12 | Athina Bekakou | @namespace DS
|
6481 | d0fe8c12 | Athina Bekakou | */
|
6482 | d0fe8c12 | Athina Bekakou | DS.Model.reopen({ |
6483 | d0fe8c12 | Athina Bekakou | |
6484 | d0fe8c12 | Athina Bekakou | /**
|
6485 | d0fe8c12 | Athina Bekakou | @method belongsToWillChange
|
6486 | d0fe8c12 | Athina Bekakou | @private
|
6487 | d0fe8c12 | Athina Bekakou | @static
|
6488 | d0fe8c12 | Athina Bekakou | @param record
|
6489 | d0fe8c12 | Athina Bekakou | @param key
|
6490 | d0fe8c12 | Athina Bekakou | */
|
6491 | d0fe8c12 | Athina Bekakou | belongsToWillChange: Ember.beforeObserver(function(record, key) { |
6492 | d0fe8c12 | Athina Bekakou | if (get(record, 'isLoaded')) { |
6493 | d0fe8c12 | Athina Bekakou | var oldParent = get(record, key);
|
6494 | d0fe8c12 | Athina Bekakou | |
6495 | d0fe8c12 | Athina Bekakou | if (oldParent) {
|
6496 | d0fe8c12 | Athina Bekakou | var store = get(record, 'store'), |
6497 | d0fe8c12 | Athina Bekakou | change = DS.RelationshipChange.createChange(record, oldParent, store, { key: key, kind: "belongsTo", changeType: "remove" }); |
6498 | d0fe8c12 | Athina Bekakou | |
6499 | d0fe8c12 | Athina Bekakou | change.sync(); |
6500 | d0fe8c12 | Athina Bekakou | this._changesToSync[key] = change;
|
6501 | d0fe8c12 | Athina Bekakou | } |
6502 | d0fe8c12 | Athina Bekakou | } |
6503 | d0fe8c12 | Athina Bekakou | }), |
6504 | d0fe8c12 | Athina Bekakou | |
6505 | d0fe8c12 | Athina Bekakou | /**
|
6506 | d0fe8c12 | Athina Bekakou | @method belongsToDidChange
|
6507 | d0fe8c12 | Athina Bekakou | @private
|
6508 | d0fe8c12 | Athina Bekakou | @static
|
6509 | d0fe8c12 | Athina Bekakou | @param record
|
6510 | d0fe8c12 | Athina Bekakou | @param key
|
6511 | d0fe8c12 | Athina Bekakou | */
|
6512 | d0fe8c12 | Athina Bekakou | belongsToDidChange: Ember.immediateObserver(function(record, key) { |
6513 | d0fe8c12 | Athina Bekakou | if (get(record, 'isLoaded')) { |
6514 | d0fe8c12 | Athina Bekakou | var newParent = get(record, key);
|
6515 | d0fe8c12 | Athina Bekakou | |
6516 | d0fe8c12 | Athina Bekakou | if (newParent) {
|
6517 | d0fe8c12 | Athina Bekakou | var store = get(record, 'store'), |
6518 | d0fe8c12 | Athina Bekakou | change = DS.RelationshipChange.createChange(record, newParent, store, { key: key, kind: "belongsTo", changeType: "add" }); |
6519 | d0fe8c12 | Athina Bekakou | |
6520 | d0fe8c12 | Athina Bekakou | change.sync(); |
6521 | d0fe8c12 | Athina Bekakou | } |
6522 | d0fe8c12 | Athina Bekakou | } |
6523 | d0fe8c12 | Athina Bekakou | |
6524 | d0fe8c12 | Athina Bekakou | delete this._changesToSync[key]; |
6525 | d0fe8c12 | Athina Bekakou | }) |
6526 | d0fe8c12 | Athina Bekakou | }); |
6527 | d0fe8c12 | Athina Bekakou | |
6528 | d0fe8c12 | Athina Bekakou | })(); |
6529 | d0fe8c12 | Athina Bekakou | |
6530 | d0fe8c12 | Athina Bekakou | |
6531 | d0fe8c12 | Athina Bekakou | |
6532 | d0fe8c12 | Athina Bekakou | (function() {
|
6533 | d0fe8c12 | Athina Bekakou | /**
|
6534 | d0fe8c12 | Athina Bekakou | @module ember-data
|
6535 | d0fe8c12 | Athina Bekakou | */
|
6536 | d0fe8c12 | Athina Bekakou | |
6537 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
|
6538 | d0fe8c12 | Athina Bekakou | |
6539 | d0fe8c12 | Athina Bekakou | function asyncHasMany(type, options, meta) { |
6540 | d0fe8c12 | Athina Bekakou | return Ember.computed(function(key, value) { |
6541 | d0fe8c12 | Athina Bekakou | var relationship = this._relationships[key], |
6542 | d0fe8c12 | Athina Bekakou | promiseLabel = "DS: Async hasMany " + this + " : " + key; |
6543 | d0fe8c12 | Athina Bekakou | |
6544 | d0fe8c12 | Athina Bekakou | if (!relationship) {
|
6545 | d0fe8c12 | Athina Bekakou | var resolver = Ember.RSVP.defer(promiseLabel);
|
6546 | d0fe8c12 | Athina Bekakou | relationship = buildRelationship(this, key, options, function(store, data) { |
6547 | d0fe8c12 | Athina Bekakou | var link = data.links && data.links[key];
|
6548 | d0fe8c12 | Athina Bekakou | var rel;
|
6549 | d0fe8c12 | Athina Bekakou | if (link) {
|
6550 | d0fe8c12 | Athina Bekakou | rel = store.findHasMany(this, link, meta, resolver);
|
6551 | d0fe8c12 | Athina Bekakou | } else {
|
6552 | d0fe8c12 | Athina Bekakou | rel = store.findMany(this, data[key], meta.type, resolver);
|
6553 | d0fe8c12 | Athina Bekakou | } |
6554 | d0fe8c12 | Athina Bekakou | // cache the promise so we can use it
|
6555 | d0fe8c12 | Athina Bekakou | // when we come back and don't need to rebuild
|
6556 | d0fe8c12 | Athina Bekakou | // the relationship.
|
6557 | d0fe8c12 | Athina Bekakou | set(rel, 'promise', resolver.promise);
|
6558 | d0fe8c12 | Athina Bekakou | return rel;
|
6559 | d0fe8c12 | Athina Bekakou | }); |
6560 | d0fe8c12 | Athina Bekakou | } |
6561 | d0fe8c12 | Athina Bekakou | |
6562 | d0fe8c12 | Athina Bekakou | var promise = relationship.get('promise').then(function() { |
6563 | d0fe8c12 | Athina Bekakou | return relationship;
|
6564 | d0fe8c12 | Athina Bekakou | }, null, "DS: Async hasMany records received"); |
6565 | d0fe8c12 | Athina Bekakou | |
6566 | d0fe8c12 | Athina Bekakou | return DS.PromiseArray.create({ promise: promise }); |
6567 | d0fe8c12 | Athina Bekakou | }).property('data').meta(meta);
|
6568 | d0fe8c12 | Athina Bekakou | } |
6569 | d0fe8c12 | Athina Bekakou | |
6570 | d0fe8c12 | Athina Bekakou | function buildRelationship(record, key, options, callback) { |
6571 | d0fe8c12 | Athina Bekakou | var rels = record._relationships;
|
6572 | d0fe8c12 | Athina Bekakou | |
6573 | d0fe8c12 | Athina Bekakou | if (rels[key]) { return rels[key]; } |
6574 | d0fe8c12 | Athina Bekakou | |
6575 | d0fe8c12 | Athina Bekakou | var data = get(record, 'data'), |
6576 | d0fe8c12 | Athina Bekakou | store = get(record, 'store');
|
6577 | d0fe8c12 | Athina Bekakou | |
6578 | d0fe8c12 | Athina Bekakou | var relationship = rels[key] = callback.call(record, store, data);
|
6579 | d0fe8c12 | Athina Bekakou | |
6580 | d0fe8c12 | Athina Bekakou | return setProperties(relationship, {
|
6581 | d0fe8c12 | Athina Bekakou | owner: record, name: key, isPolymorphic: options.polymorphic |
6582 | d0fe8c12 | Athina Bekakou | }); |
6583 | d0fe8c12 | Athina Bekakou | } |
6584 | d0fe8c12 | Athina Bekakou | |
6585 | d0fe8c12 | Athina Bekakou | function hasRelationship(type, options) { |
6586 | d0fe8c12 | Athina Bekakou | options = options || {}; |
6587 | d0fe8c12 | Athina Bekakou | |
6588 | d0fe8c12 | Athina Bekakou | var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' }; |
6589 | d0fe8c12 | Athina Bekakou | |
6590 | d0fe8c12 | Athina Bekakou | if (options.async) {
|
6591 | d0fe8c12 | Athina Bekakou | return asyncHasMany(type, options, meta);
|
6592 | d0fe8c12 | Athina Bekakou | } |
6593 | d0fe8c12 | Athina Bekakou | |
6594 | d0fe8c12 | Athina Bekakou | return Ember.computed(function(key, value) { |
6595 | d0fe8c12 | Athina Bekakou | return buildRelationship(this, key, options, function(store, data) { |
6596 | d0fe8c12 | Athina Bekakou | var records = data[key];
|
6597 | d0fe8c12 | Athina Bekakou | Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", Ember.A(records).everyProperty('isEmpty', false)); |
6598 | d0fe8c12 | Athina Bekakou | return store.findMany(this, data[key], meta.type); |
6599 | d0fe8c12 | Athina Bekakou | }); |
6600 | d0fe8c12 | Athina Bekakou | }).property('data').meta(meta);
|
6601 | d0fe8c12 | Athina Bekakou | } |
6602 | d0fe8c12 | Athina Bekakou | |
6603 | d0fe8c12 | Athina Bekakou | /**
|
6604 | d0fe8c12 | Athina Bekakou | `DS.hasMany` is used to define One-To-Many and Many-To-Many
|
6605 | d0fe8c12 | Athina Bekakou | relationships on a [DS.Model](DS.Model.html).
|
6606 | d0fe8c12 | Athina Bekakou | |
6607 | d0fe8c12 | Athina Bekakou | `DS.hasMany` takes an optional hash as a second parameter, currently
|
6608 | d0fe8c12 | Athina Bekakou | supported options are:
|
6609 | d0fe8c12 | Athina Bekakou | |
6610 | d0fe8c12 | Athina Bekakou | - `async`: A boolean value used to explicitly declare this to be an async relationship.
|
6611 | d0fe8c12 | Athina Bekakou | - `inverse`: A string used to identify the inverse property on a related model.
|
6612 | d0fe8c12 | Athina Bekakou | |
6613 | d0fe8c12 | Athina Bekakou | #### One-To-Many
|
6614 | d0fe8c12 | Athina Bekakou | To declare a one-to-many relationship between two models, use
|
6615 | d0fe8c12 | Athina Bekakou | `DS.belongsTo` in combination with `DS.hasMany`, like this:
|
6616 | d0fe8c12 | Athina Bekakou | |
6617 | d0fe8c12 | Athina Bekakou | ```javascript
|
6618 | d0fe8c12 | Athina Bekakou | App.Post = DS.Model.extend({
|
6619 | d0fe8c12 | Athina Bekakou | comments: DS.hasMany('comment')
|
6620 | d0fe8c12 | Athina Bekakou | });
|
6621 | d0fe8c12 | Athina Bekakou | |
6622 | d0fe8c12 | Athina Bekakou | App.Comment = DS.Model.extend({
|
6623 | d0fe8c12 | Athina Bekakou | post: DS.belongsTo('post')
|
6624 | d0fe8c12 | Athina Bekakou | });
|
6625 | d0fe8c12 | Athina Bekakou | ```
|
6626 | d0fe8c12 | Athina Bekakou | |
6627 | d0fe8c12 | Athina Bekakou | #### Many-To-Many
|
6628 | d0fe8c12 | Athina Bekakou | To declare a many-to-many relationship between two models, use
|
6629 | d0fe8c12 | Athina Bekakou | `DS.hasMany`:
|
6630 | d0fe8c12 | Athina Bekakou | |
6631 | d0fe8c12 | Athina Bekakou | ```javascript
|
6632 | d0fe8c12 | Athina Bekakou | App.Post = DS.Model.extend({
|
6633 | d0fe8c12 | Athina Bekakou | tags: DS.hasMany('tag')
|
6634 | d0fe8c12 | Athina Bekakou | });
|
6635 | d0fe8c12 | Athina Bekakou | |
6636 | d0fe8c12 | Athina Bekakou | App.Tag = DS.Model.extend({
|
6637 | d0fe8c12 | Athina Bekakou | posts: DS.hasMany('post')
|
6638 | d0fe8c12 | Athina Bekakou | });
|
6639 | d0fe8c12 | Athina Bekakou | ```
|
6640 | d0fe8c12 | Athina Bekakou | |
6641 | d0fe8c12 | Athina Bekakou | #### Explicit Inverses
|
6642 | d0fe8c12 | Athina Bekakou | |
6643 | d0fe8c12 | Athina Bekakou | Ember Data will do its best to discover which relationships map to
|
6644 | d0fe8c12 | Athina Bekakou | one another. In the one-to-many code above, for example, Ember Data
|
6645 | d0fe8c12 | Athina Bekakou | can figure out that changing the `comments` relationship should update
|
6646 | d0fe8c12 | Athina Bekakou | the `post` relationship on the inverse because post is the only
|
6647 | d0fe8c12 | Athina Bekakou | relationship to that model.
|
6648 | d0fe8c12 | Athina Bekakou | |
6649 | d0fe8c12 | Athina Bekakou | However, sometimes you may have multiple `belongsTo`/`hasManys` for the
|
6650 | d0fe8c12 | Athina Bekakou | same type. You can specify which property on the related model is
|
6651 | d0fe8c12 | Athina Bekakou | the inverse using `DS.hasMany`'s `inverse` option:
|
6652 | d0fe8c12 | Athina Bekakou | |
6653 | d0fe8c12 | Athina Bekakou | ```javascript
|
6654 | d0fe8c12 | Athina Bekakou | var belongsTo = DS.belongsTo,
|
6655 | d0fe8c12 | Athina Bekakou | hasMany = DS.hasMany;
|
6656 | d0fe8c12 | Athina Bekakou | |
6657 | d0fe8c12 | Athina Bekakou | App.Comment = DS.Model.extend({
|
6658 | d0fe8c12 | Athina Bekakou | onePost: belongsTo('post'),
|
6659 | d0fe8c12 | Athina Bekakou | twoPost: belongsTo('post'),
|
6660 | d0fe8c12 | Athina Bekakou | redPost: belongsTo('post'),
|
6661 | d0fe8c12 | Athina Bekakou | bluePost: belongsTo('post')
|
6662 | d0fe8c12 | Athina Bekakou | });
|
6663 | d0fe8c12 | Athina Bekakou | |
6664 | d0fe8c12 | Athina Bekakou | App.Post = DS.Model.extend({
|
6665 | d0fe8c12 | Athina Bekakou | comments: hasMany('comment', {
|
6666 | d0fe8c12 | Athina Bekakou | inverse: 'redPost'
|
6667 | d0fe8c12 | Athina Bekakou | })
|
6668 | d0fe8c12 | Athina Bekakou | });
|
6669 | d0fe8c12 | Athina Bekakou | ```
|
6670 | d0fe8c12 | Athina Bekakou | |
6671 | d0fe8c12 | Athina Bekakou | You can also specify an inverse on a `belongsTo`, which works how
|
6672 | d0fe8c12 | Athina Bekakou | you'd expect.
|
6673 | d0fe8c12 | Athina Bekakou | |
6674 | d0fe8c12 | Athina Bekakou | @namespace
|
6675 | d0fe8c12 | Athina Bekakou | @method hasMany
|
6676 | d0fe8c12 | Athina Bekakou | @for DS
|
6677 | d0fe8c12 | Athina Bekakou | @param {String or DS.Model} type the model type of the relationship
|
6678 | d0fe8c12 | Athina Bekakou | @param {Object} options a hash of options
|
6679 | d0fe8c12 | Athina Bekakou | @return {Ember.computed} relationship
|
6680 | d0fe8c12 | Athina Bekakou | */
|
6681 | d0fe8c12 | Athina Bekakou | DS.hasMany = function(type, options) { |
6682 | d0fe8c12 | Athina Bekakou | if (typeof type === 'object') { |
6683 | d0fe8c12 | Athina Bekakou | options = type; |
6684 | d0fe8c12 | Athina Bekakou | type = undefined;
|
6685 | d0fe8c12 | Athina Bekakou | } |
6686 | d0fe8c12 | Athina Bekakou | return hasRelationship(type, options);
|
6687 | d0fe8c12 | Athina Bekakou | }; |
6688 | d0fe8c12 | Athina Bekakou | |
6689 | d0fe8c12 | Athina Bekakou | })(); |
6690 | d0fe8c12 | Athina Bekakou | |
6691 | d0fe8c12 | Athina Bekakou | |
6692 | d0fe8c12 | Athina Bekakou | |
6693 | d0fe8c12 | Athina Bekakou | (function() {
|
6694 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
6695 | d0fe8c12 | Athina Bekakou | |
6696 | d0fe8c12 | Athina Bekakou | /**
|
6697 | d0fe8c12 | Athina Bekakou | @module ember-data
|
6698 | d0fe8c12 | Athina Bekakou | */
|
6699 | d0fe8c12 | Athina Bekakou | |
6700 | d0fe8c12 | Athina Bekakou | /*
|
6701 | d0fe8c12 | Athina Bekakou | This file defines several extensions to the base `DS.Model` class that
|
6702 | d0fe8c12 | Athina Bekakou | add support for one-to-many relationships.
|
6703 | d0fe8c12 | Athina Bekakou | */
|
6704 | d0fe8c12 | Athina Bekakou | |
6705 | d0fe8c12 | Athina Bekakou | /**
|
6706 | d0fe8c12 | Athina Bekakou | @class Model
|
6707 | d0fe8c12 | Athina Bekakou | @namespace DS
|
6708 | d0fe8c12 | Athina Bekakou | */
|
6709 | d0fe8c12 | Athina Bekakou | DS.Model.reopen({ |
6710 | d0fe8c12 | Athina Bekakou | |
6711 | d0fe8c12 | Athina Bekakou | /**
|
6712 | d0fe8c12 | Athina Bekakou | This Ember.js hook allows an object to be notified when a property
|
6713 | d0fe8c12 | Athina Bekakou | is defined.
|
6714 | d0fe8c12 | Athina Bekakou | |
6715 | d0fe8c12 | Athina Bekakou | In this case, we use it to be notified when an Ember Data user defines a
|
6716 | d0fe8c12 | Athina Bekakou | belongs-to relationship. In that case, we need to set up observers for
|
6717 | d0fe8c12 | Athina Bekakou | each one, allowing us to track relationship changes and automatically
|
6718 | d0fe8c12 | Athina Bekakou | reflect changes in the inverse has-many array.
|
6719 | d0fe8c12 | Athina Bekakou | |
6720 | d0fe8c12 | Athina Bekakou | This hook passes the class being set up, as well as the key and value
|
6721 | d0fe8c12 | Athina Bekakou | being defined. So, for example, when the user does this:
|
6722 | d0fe8c12 | Athina Bekakou | |
6723 | d0fe8c12 | Athina Bekakou | ```javascript
|
6724 | d0fe8c12 | Athina Bekakou | DS.Model.extend({
|
6725 | d0fe8c12 | Athina Bekakou | parent: DS.belongsTo('user')
|
6726 | d0fe8c12 | Athina Bekakou | });
|
6727 | d0fe8c12 | Athina Bekakou | ```
|
6728 | d0fe8c12 | Athina Bekakou | |
6729 | d0fe8c12 | Athina Bekakou | This hook would be called with "parent" as the key and the computed
|
6730 | d0fe8c12 | Athina Bekakou | property returned by `DS.belongsTo` as the value.
|
6731 | d0fe8c12 | Athina Bekakou | |
6732 | d0fe8c12 | Athina Bekakou | @method didDefineProperty
|
6733 | d0fe8c12 | Athina Bekakou | @param proto
|
6734 | d0fe8c12 | Athina Bekakou | @param key
|
6735 | d0fe8c12 | Athina Bekakou | @param value
|
6736 | d0fe8c12 | Athina Bekakou | */
|
6737 | d0fe8c12 | Athina Bekakou | didDefineProperty: function(proto, key, value) { |
6738 | d0fe8c12 | Athina Bekakou | // Check if the value being set is a computed property.
|
6739 | d0fe8c12 | Athina Bekakou | if (value instanceof Ember.Descriptor) { |
6740 | d0fe8c12 | Athina Bekakou | |
6741 | d0fe8c12 | Athina Bekakou | // If it is, get the metadata for the relationship. This is
|
6742 | d0fe8c12 | Athina Bekakou | // populated by the `DS.belongsTo` helper when it is creating
|
6743 | d0fe8c12 | Athina Bekakou | // the computed property.
|
6744 | d0fe8c12 | Athina Bekakou | var meta = value.meta();
|
6745 | d0fe8c12 | Athina Bekakou | |
6746 | d0fe8c12 | Athina Bekakou | if (meta.isRelationship && meta.kind === 'belongsTo') { |
6747 | d0fe8c12 | Athina Bekakou | Ember.addObserver(proto, key, null, 'belongsToDidChange'); |
6748 | d0fe8c12 | Athina Bekakou | Ember.addBeforeObserver(proto, key, null, 'belongsToWillChange'); |
6749 | d0fe8c12 | Athina Bekakou | } |
6750 | d0fe8c12 | Athina Bekakou | |
6751 | d0fe8c12 | Athina Bekakou | meta.parentType = proto.constructor; |
6752 | d0fe8c12 | Athina Bekakou | } |
6753 | d0fe8c12 | Athina Bekakou | } |
6754 | d0fe8c12 | Athina Bekakou | }); |
6755 | d0fe8c12 | Athina Bekakou | |
6756 | d0fe8c12 | Athina Bekakou | /*
|
6757 | d0fe8c12 | Athina Bekakou | These DS.Model extensions add class methods that provide relationship
|
6758 | d0fe8c12 | Athina Bekakou | introspection abilities about relationships.
|
6759 | d0fe8c12 | Athina Bekakou | |
6760 | d0fe8c12 | Athina Bekakou | A note about the computed properties contained here:
|
6761 | d0fe8c12 | Athina Bekakou | |
6762 | d0fe8c12 | Athina Bekakou | **These properties are effectively sealed once called for the first time.**
|
6763 | d0fe8c12 | Athina Bekakou | To avoid repeatedly doing expensive iteration over a model's fields, these
|
6764 | d0fe8c12 | Athina Bekakou | values are computed once and then cached for the remainder of the runtime of
|
6765 | d0fe8c12 | Athina Bekakou | your application.
|
6766 | d0fe8c12 | Athina Bekakou | |
6767 | d0fe8c12 | Athina Bekakou | If your application needs to modify a class after its initial definition
|
6768 | d0fe8c12 | Athina Bekakou | (for example, using `reopen()` to add additional attributes), make sure you
|
6769 | d0fe8c12 | Athina Bekakou | do it before using your model with the store, which uses these properties
|
6770 | d0fe8c12 | Athina Bekakou | extensively.
|
6771 | d0fe8c12 | Athina Bekakou | */
|
6772 | d0fe8c12 | Athina Bekakou | |
6773 | d0fe8c12 | Athina Bekakou | DS.Model.reopenClass({ |
6774 | d0fe8c12 | Athina Bekakou | /**
|
6775 | d0fe8c12 | Athina Bekakou | For a given relationship name, returns the model type of the relationship.
|
6776 | d0fe8c12 | Athina Bekakou | |
6777 | d0fe8c12 | Athina Bekakou | For example, if you define a model like this:
|
6778 | d0fe8c12 | Athina Bekakou | |
6779 | d0fe8c12 | Athina Bekakou | ```javascript
|
6780 | d0fe8c12 | Athina Bekakou | App.Post = DS.Model.extend({
|
6781 | d0fe8c12 | Athina Bekakou | comments: DS.hasMany('comment')
|
6782 | d0fe8c12 | Athina Bekakou | });
|
6783 | d0fe8c12 | Athina Bekakou | ```
|
6784 | d0fe8c12 | Athina Bekakou | |
6785 | d0fe8c12 | Athina Bekakou | Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
|
6786 | d0fe8c12 | Athina Bekakou | |
6787 | d0fe8c12 | Athina Bekakou | @method typeForRelationship
|
6788 | d0fe8c12 | Athina Bekakou | @static
|
6789 | d0fe8c12 | Athina Bekakou | @param {String} name the name of the relationship
|
6790 | d0fe8c12 | Athina Bekakou | @return {subclass of DS.Model} the type of the relationship, or undefined
|
6791 | d0fe8c12 | Athina Bekakou | */
|
6792 | d0fe8c12 | Athina Bekakou | typeForRelationship: function(name) { |
6793 | d0fe8c12 | Athina Bekakou | var relationship = get(this, 'relationshipsByName').get(name); |
6794 | d0fe8c12 | Athina Bekakou | return relationship && relationship.type;
|
6795 | d0fe8c12 | Athina Bekakou | }, |
6796 | d0fe8c12 | Athina Bekakou | |
6797 | d0fe8c12 | Athina Bekakou | inverseFor: function(name) { |
6798 | d0fe8c12 | Athina Bekakou | var inverseType = this.typeForRelationship(name); |
6799 | d0fe8c12 | Athina Bekakou | |
6800 | d0fe8c12 | Athina Bekakou | if (!inverseType) { return null; } |
6801 | d0fe8c12 | Athina Bekakou | |
6802 | d0fe8c12 | Athina Bekakou | var options = this.metaForProperty(name).options; |
6803 | d0fe8c12 | Athina Bekakou | |
6804 | d0fe8c12 | Athina Bekakou | if (options.inverse === null) { return null; } |
6805 | d0fe8c12 | Athina Bekakou | |
6806 | d0fe8c12 | Athina Bekakou | var inverseName, inverseKind;
|
6807 | d0fe8c12 | Athina Bekakou | |
6808 | d0fe8c12 | Athina Bekakou | if (options.inverse) {
|
6809 | d0fe8c12 | Athina Bekakou | inverseName = options.inverse; |
6810 | d0fe8c12 | Athina Bekakou | inverseKind = Ember.get(inverseType, 'relationshipsByName').get(inverseName).kind;
|
6811 | d0fe8c12 | Athina Bekakou | } else {
|
6812 | d0fe8c12 | Athina Bekakou | var possibleRelationships = findPossibleInverses(this, inverseType); |
6813 | d0fe8c12 | Athina Bekakou | |
6814 | d0fe8c12 | Athina Bekakou | if (possibleRelationships.length === 0) { return null; } |
6815 | d0fe8c12 | Athina Bekakou | |
6816 | d0fe8c12 | Athina Bekakou | Ember.assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", possibleRelationships.length === 1); |
6817 | d0fe8c12 | Athina Bekakou | |
6818 | d0fe8c12 | Athina Bekakou | inverseName = possibleRelationships[0].name;
|
6819 | d0fe8c12 | Athina Bekakou | inverseKind = possibleRelationships[0].kind;
|
6820 | d0fe8c12 | Athina Bekakou | } |
6821 | d0fe8c12 | Athina Bekakou | |
6822 | d0fe8c12 | Athina Bekakou | function findPossibleInverses(type, inverseType, possibleRelationships) { |
6823 | d0fe8c12 | Athina Bekakou | possibleRelationships = possibleRelationships || []; |
6824 | d0fe8c12 | Athina Bekakou | |
6825 | d0fe8c12 | Athina Bekakou | var relationshipMap = get(inverseType, 'relationships'); |
6826 | d0fe8c12 | Athina Bekakou | if (!relationshipMap) { return; } |
6827 | d0fe8c12 | Athina Bekakou | |
6828 | d0fe8c12 | Athina Bekakou | var relationships = relationshipMap.get(type);
|
6829 | d0fe8c12 | Athina Bekakou | if (relationships) {
|
6830 | d0fe8c12 | Athina Bekakou | possibleRelationships.push.apply(possibleRelationships, relationshipMap.get(type)); |
6831 | d0fe8c12 | Athina Bekakou | } |
6832 | d0fe8c12 | Athina Bekakou | |
6833 | d0fe8c12 | Athina Bekakou | if (type.superclass) {
|
6834 | d0fe8c12 | Athina Bekakou | findPossibleInverses(type.superclass, inverseType, possibleRelationships); |
6835 | d0fe8c12 | Athina Bekakou | } |
6836 | d0fe8c12 | Athina Bekakou | |
6837 | d0fe8c12 | Athina Bekakou | return possibleRelationships;
|
6838 | d0fe8c12 | Athina Bekakou | } |
6839 | d0fe8c12 | Athina Bekakou | |
6840 | d0fe8c12 | Athina Bekakou | return {
|
6841 | d0fe8c12 | Athina Bekakou | type: inverseType,
|
6842 | d0fe8c12 | Athina Bekakou | name: inverseName,
|
6843 | d0fe8c12 | Athina Bekakou | kind: inverseKind
|
6844 | d0fe8c12 | Athina Bekakou | }; |
6845 | d0fe8c12 | Athina Bekakou | }, |
6846 | d0fe8c12 | Athina Bekakou | |
6847 | d0fe8c12 | Athina Bekakou | /**
|
6848 | d0fe8c12 | Athina Bekakou | The model's relationships as a map, keyed on the type of the
|
6849 | d0fe8c12 | Athina Bekakou | relationship. The value of each entry is an array containing a descriptor
|
6850 | d0fe8c12 | Athina Bekakou | for each relationship with that type, describing the name of the relationship
|
6851 | d0fe8c12 | Athina Bekakou | as well as the type.
|
6852 | d0fe8c12 | Athina Bekakou | |
6853 | d0fe8c12 | Athina Bekakou | For example, given the following model definition:
|
6854 | d0fe8c12 | Athina Bekakou | |
6855 | d0fe8c12 | Athina Bekakou | ```javascript
|
6856 | d0fe8c12 | Athina Bekakou | App.Blog = DS.Model.extend({
|
6857 | d0fe8c12 | Athina Bekakou | users: DS.hasMany('user'),
|
6858 | d0fe8c12 | Athina Bekakou | owner: DS.belongsTo('user'),
|
6859 | d0fe8c12 | Athina Bekakou | posts: DS.hasMany('post')
|
6860 | d0fe8c12 | Athina Bekakou | });
|
6861 | d0fe8c12 | Athina Bekakou | ```
|
6862 | d0fe8c12 | Athina Bekakou | |
6863 | d0fe8c12 | Athina Bekakou | This computed property would return a map describing these
|
6864 | d0fe8c12 | Athina Bekakou | relationships, like this:
|
6865 | d0fe8c12 | Athina Bekakou | |
6866 | d0fe8c12 | Athina Bekakou | ```javascript
|
6867 | d0fe8c12 | Athina Bekakou | var relationships = Ember.get(App.Blog, 'relationships');
|
6868 | d0fe8c12 | Athina Bekakou | relationships.get(App.User);
|
6869 | d0fe8c12 | Athina Bekakou | //=> [ { name: 'users', kind: 'hasMany' },
|
6870 | d0fe8c12 | Athina Bekakou | // { name: 'owner', kind: 'belongsTo' } ]
|
6871 | d0fe8c12 | Athina Bekakou | relationships.get(App.Post);
|
6872 | d0fe8c12 | Athina Bekakou | //=> [ { name: 'posts', kind: 'hasMany' } ]
|
6873 | d0fe8c12 | Athina Bekakou | ```
|
6874 | d0fe8c12 | Athina Bekakou | |
6875 | d0fe8c12 | Athina Bekakou | @property relationships
|
6876 | d0fe8c12 | Athina Bekakou | @static
|
6877 | d0fe8c12 | Athina Bekakou | @type Ember.Map
|
6878 | d0fe8c12 | Athina Bekakou | @readOnly
|
6879 | d0fe8c12 | Athina Bekakou | */
|
6880 | d0fe8c12 | Athina Bekakou | relationships: Ember.computed(function() { |
6881 | d0fe8c12 | Athina Bekakou | var map = new Ember.MapWithDefault({ |
6882 | d0fe8c12 | Athina Bekakou | defaultValue: function() { return []; } |
6883 | d0fe8c12 | Athina Bekakou | }); |
6884 | d0fe8c12 | Athina Bekakou | |
6885 | d0fe8c12 | Athina Bekakou | // Loop through each computed property on the class
|
6886 | d0fe8c12 | Athina Bekakou | this.eachComputedProperty(function(name, meta) { |
6887 | d0fe8c12 | Athina Bekakou | |
6888 | d0fe8c12 | Athina Bekakou | // If the computed property is a relationship, add
|
6889 | d0fe8c12 | Athina Bekakou | // it to the map.
|
6890 | d0fe8c12 | Athina Bekakou | if (meta.isRelationship) {
|
6891 | d0fe8c12 | Athina Bekakou | if (typeof meta.type === 'string') { |
6892 | d0fe8c12 | Athina Bekakou | meta.type = this.store.modelFor(meta.type);
|
6893 | d0fe8c12 | Athina Bekakou | } |
6894 | d0fe8c12 | Athina Bekakou | |
6895 | d0fe8c12 | Athina Bekakou | var relationshipsForType = map.get(meta.type);
|
6896 | d0fe8c12 | Athina Bekakou | |
6897 | d0fe8c12 | Athina Bekakou | relationshipsForType.push({ name: name, kind: meta.kind }); |
6898 | d0fe8c12 | Athina Bekakou | } |
6899 | d0fe8c12 | Athina Bekakou | }); |
6900 | d0fe8c12 | Athina Bekakou | |
6901 | d0fe8c12 | Athina Bekakou | return map;
|
6902 | d0fe8c12 | Athina Bekakou | }), |
6903 | d0fe8c12 | Athina Bekakou | |
6904 | d0fe8c12 | Athina Bekakou | /**
|
6905 | d0fe8c12 | Athina Bekakou | A hash containing lists of the model's relationships, grouped
|
6906 | d0fe8c12 | Athina Bekakou | by the relationship kind. For example, given a model with this
|
6907 | d0fe8c12 | Athina Bekakou | definition:
|
6908 | d0fe8c12 | Athina Bekakou | |
6909 | d0fe8c12 | Athina Bekakou | ```javascript
|
6910 | d0fe8c12 | Athina Bekakou | App.Blog = DS.Model.extend({
|
6911 | d0fe8c12 | Athina Bekakou | users: DS.hasMany('user'),
|
6912 | d0fe8c12 | Athina Bekakou | owner: DS.belongsTo('user'),
|
6913 | d0fe8c12 | Athina Bekakou | |
6914 | d0fe8c12 | Athina Bekakou | posts: DS.hasMany('post')
|
6915 | d0fe8c12 | Athina Bekakou | });
|
6916 | d0fe8c12 | Athina Bekakou | ```
|
6917 | d0fe8c12 | Athina Bekakou | |
6918 | d0fe8c12 | Athina Bekakou | This property would contain the following:
|
6919 | d0fe8c12 | Athina Bekakou | |
6920 | d0fe8c12 | Athina Bekakou | ```javascript
|
6921 | d0fe8c12 | Athina Bekakou | var relationshipNames = Ember.get(App.Blog, 'relationshipNames');
|
6922 | d0fe8c12 | Athina Bekakou | relationshipNames.hasMany;
|
6923 | d0fe8c12 | Athina Bekakou | //=> ['users', 'posts']
|
6924 | d0fe8c12 | Athina Bekakou | relationshipNames.belongsTo;
|
6925 | d0fe8c12 | Athina Bekakou | //=> ['owner']
|
6926 | d0fe8c12 | Athina Bekakou | ```
|
6927 | d0fe8c12 | Athina Bekakou | |
6928 | d0fe8c12 | Athina Bekakou | @property relationshipNames
|
6929 | d0fe8c12 | Athina Bekakou | @static
|
6930 | d0fe8c12 | Athina Bekakou | @type Object
|
6931 | d0fe8c12 | Athina Bekakou | @readOnly
|
6932 | d0fe8c12 | Athina Bekakou | */
|
6933 | d0fe8c12 | Athina Bekakou | relationshipNames: Ember.computed(function() { |
6934 | d0fe8c12 | Athina Bekakou | var names = { hasMany: [], belongsTo: [] }; |
6935 | d0fe8c12 | Athina Bekakou | |
6936 | d0fe8c12 | Athina Bekakou | this.eachComputedProperty(function(name, meta) { |
6937 | d0fe8c12 | Athina Bekakou | if (meta.isRelationship) {
|
6938 | d0fe8c12 | Athina Bekakou | names[meta.kind].push(name); |
6939 | d0fe8c12 | Athina Bekakou | } |
6940 | d0fe8c12 | Athina Bekakou | }); |
6941 | d0fe8c12 | Athina Bekakou | |
6942 | d0fe8c12 | Athina Bekakou | return names;
|
6943 | d0fe8c12 | Athina Bekakou | }), |
6944 | d0fe8c12 | Athina Bekakou | |
6945 | d0fe8c12 | Athina Bekakou | /**
|
6946 | d0fe8c12 | Athina Bekakou | An array of types directly related to a model. Each type will be
|
6947 | d0fe8c12 | Athina Bekakou | included once, regardless of the number of relationships it has with
|
6948 | d0fe8c12 | Athina Bekakou | the model.
|
6949 | d0fe8c12 | Athina Bekakou | |
6950 | d0fe8c12 | Athina Bekakou | For example, given a model with this definition:
|
6951 | d0fe8c12 | Athina Bekakou | |
6952 | d0fe8c12 | Athina Bekakou | ```javascript
|
6953 | d0fe8c12 | Athina Bekakou | App.Blog = DS.Model.extend({
|
6954 | d0fe8c12 | Athina Bekakou | users: DS.hasMany('user'),
|
6955 | d0fe8c12 | Athina Bekakou | owner: DS.belongsTo('user'),
|
6956 | d0fe8c12 | Athina Bekakou | |
6957 | d0fe8c12 | Athina Bekakou | posts: DS.hasMany('post')
|
6958 | d0fe8c12 | Athina Bekakou | });
|
6959 | d0fe8c12 | Athina Bekakou | ```
|
6960 | d0fe8c12 | Athina Bekakou | |
6961 | d0fe8c12 | Athina Bekakou | This property would contain the following:
|
6962 | d0fe8c12 | Athina Bekakou | |
6963 | d0fe8c12 | Athina Bekakou | ```javascript
|
6964 | d0fe8c12 | Athina Bekakou | var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
|
6965 | d0fe8c12 | Athina Bekakou | //=> [ App.User, App.Post ]
|
6966 | d0fe8c12 | Athina Bekakou | ```
|
6967 | d0fe8c12 | Athina Bekakou | |
6968 | d0fe8c12 | Athina Bekakou | @property relatedTypes
|
6969 | d0fe8c12 | Athina Bekakou | @static
|
6970 | d0fe8c12 | Athina Bekakou | @type Ember.Array
|
6971 | d0fe8c12 | Athina Bekakou | @readOnly
|
6972 | d0fe8c12 | Athina Bekakou | */
|
6973 | d0fe8c12 | Athina Bekakou | relatedTypes: Ember.computed(function() { |
6974 | d0fe8c12 | Athina Bekakou | var type,
|
6975 | d0fe8c12 | Athina Bekakou | types = Ember.A(); |
6976 | d0fe8c12 | Athina Bekakou | |
6977 | d0fe8c12 | Athina Bekakou | // Loop through each computed property on the class,
|
6978 | d0fe8c12 | Athina Bekakou | // and create an array of the unique types involved
|
6979 | d0fe8c12 | Athina Bekakou | // in relationships
|
6980 | d0fe8c12 | Athina Bekakou | this.eachComputedProperty(function(name, meta) { |
6981 | d0fe8c12 | Athina Bekakou | if (meta.isRelationship) {
|
6982 | d0fe8c12 | Athina Bekakou | type = meta.type; |
6983 | d0fe8c12 | Athina Bekakou | |
6984 | d0fe8c12 | Athina Bekakou | if (typeof type === 'string') { |
6985 | d0fe8c12 | Athina Bekakou | type = get(this, type, false) || this.store.modelFor(type); |
6986 | d0fe8c12 | Athina Bekakou | } |
6987 | d0fe8c12 | Athina Bekakou | |
6988 | d0fe8c12 | Athina Bekakou | Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type); |
6989 | d0fe8c12 | Athina Bekakou | |
6990 | d0fe8c12 | Athina Bekakou | if (!types.contains(type)) {
|
6991 | d0fe8c12 | Athina Bekakou | Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type); |
6992 | d0fe8c12 | Athina Bekakou | types.push(type); |
6993 | d0fe8c12 | Athina Bekakou | } |
6994 | d0fe8c12 | Athina Bekakou | } |
6995 | d0fe8c12 | Athina Bekakou | }); |
6996 | d0fe8c12 | Athina Bekakou | |
6997 | d0fe8c12 | Athina Bekakou | return types;
|
6998 | d0fe8c12 | Athina Bekakou | }), |
6999 | d0fe8c12 | Athina Bekakou | |
7000 | d0fe8c12 | Athina Bekakou | /**
|
7001 | d0fe8c12 | Athina Bekakou | A map whose keys are the relationships of a model and whose values are
|
7002 | d0fe8c12 | Athina Bekakou | relationship descriptors.
|
7003 | d0fe8c12 | Athina Bekakou | |
7004 | d0fe8c12 | Athina Bekakou | For example, given a model with this
|
7005 | d0fe8c12 | Athina Bekakou | definition:
|
7006 | d0fe8c12 | Athina Bekakou | |
7007 | d0fe8c12 | Athina Bekakou | ```javascript
|
7008 | d0fe8c12 | Athina Bekakou | App.Blog = DS.Model.extend({
|
7009 | d0fe8c12 | Athina Bekakou | users: DS.hasMany('user'),
|
7010 | d0fe8c12 | Athina Bekakou | owner: DS.belongsTo('user'),
|
7011 | d0fe8c12 | Athina Bekakou | |
7012 | d0fe8c12 | Athina Bekakou | posts: DS.hasMany('post')
|
7013 | d0fe8c12 | Athina Bekakou | });
|
7014 | d0fe8c12 | Athina Bekakou | ```
|
7015 | d0fe8c12 | Athina Bekakou | |
7016 | d0fe8c12 | Athina Bekakou | This property would contain the following:
|
7017 | d0fe8c12 | Athina Bekakou | |
7018 | d0fe8c12 | Athina Bekakou | ```javascript
|
7019 | d0fe8c12 | Athina Bekakou | var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName');
|
7020 | d0fe8c12 | Athina Bekakou | relationshipsByName.get('users');
|
7021 | d0fe8c12 | Athina Bekakou | //=> { key: 'users', kind: 'hasMany', type: App.User }
|
7022 | d0fe8c12 | Athina Bekakou | relationshipsByName.get('owner');
|
7023 | d0fe8c12 | Athina Bekakou | //=> { key: 'owner', kind: 'belongsTo', type: App.User }
|
7024 | d0fe8c12 | Athina Bekakou | ```
|
7025 | d0fe8c12 | Athina Bekakou | |
7026 | d0fe8c12 | Athina Bekakou | @property relationshipsByName
|
7027 | d0fe8c12 | Athina Bekakou | @static
|
7028 | d0fe8c12 | Athina Bekakou | @type Ember.Map
|
7029 | d0fe8c12 | Athina Bekakou | @readOnly
|
7030 | d0fe8c12 | Athina Bekakou | */
|
7031 | d0fe8c12 | Athina Bekakou | relationshipsByName: Ember.computed(function() { |
7032 | d0fe8c12 | Athina Bekakou | var map = Ember.Map.create(), type;
|
7033 | d0fe8c12 | Athina Bekakou | |
7034 | d0fe8c12 | Athina Bekakou | this.eachComputedProperty(function(name, meta) { |
7035 | d0fe8c12 | Athina Bekakou | if (meta.isRelationship) {
|
7036 | d0fe8c12 | Athina Bekakou | meta.key = name; |
7037 | d0fe8c12 | Athina Bekakou | type = meta.type; |
7038 | d0fe8c12 | Athina Bekakou | |
7039 | d0fe8c12 | Athina Bekakou | if (!type && meta.kind === 'hasMany') { |
7040 | d0fe8c12 | Athina Bekakou | type = Ember.String.singularize(name); |
7041 | d0fe8c12 | Athina Bekakou | } else if (!type) { |
7042 | d0fe8c12 | Athina Bekakou | type = name; |
7043 | d0fe8c12 | Athina Bekakou | } |
7044 | d0fe8c12 | Athina Bekakou | |
7045 | d0fe8c12 | Athina Bekakou | if (typeof type === 'string') { |
7046 | d0fe8c12 | Athina Bekakou | meta.type = this.store.modelFor(type);
|
7047 | d0fe8c12 | Athina Bekakou | } |
7048 | d0fe8c12 | Athina Bekakou | |
7049 | d0fe8c12 | Athina Bekakou | map.set(name, meta); |
7050 | d0fe8c12 | Athina Bekakou | } |
7051 | d0fe8c12 | Athina Bekakou | }); |
7052 | d0fe8c12 | Athina Bekakou | |
7053 | d0fe8c12 | Athina Bekakou | return map;
|
7054 | d0fe8c12 | Athina Bekakou | }), |
7055 | d0fe8c12 | Athina Bekakou | |
7056 | d0fe8c12 | Athina Bekakou | /**
|
7057 | d0fe8c12 | Athina Bekakou | A map whose keys are the fields of the model and whose values are strings
|
7058 | d0fe8c12 | Athina Bekakou | describing the kind of the field. A model's fields are the union of all of its
|
7059 | d0fe8c12 | Athina Bekakou | attributes and relationships.
|
7060 | d0fe8c12 | Athina Bekakou | |
7061 | d0fe8c12 | Athina Bekakou | For example:
|
7062 | d0fe8c12 | Athina Bekakou | |
7063 | d0fe8c12 | Athina Bekakou | ```javascript
|
7064 | d0fe8c12 | Athina Bekakou | |
7065 | d0fe8c12 | Athina Bekakou | App.Blog = DS.Model.extend({
|
7066 | d0fe8c12 | Athina Bekakou | users: DS.hasMany('user'),
|
7067 | d0fe8c12 | Athina Bekakou | owner: DS.belongsTo('user'),
|
7068 | d0fe8c12 | Athina Bekakou | |
7069 | d0fe8c12 | Athina Bekakou | posts: DS.hasMany('post'),
|
7070 | d0fe8c12 | Athina Bekakou | |
7071 | d0fe8c12 | Athina Bekakou | title: DS.attr('string')
|
7072 | d0fe8c12 | Athina Bekakou | });
|
7073 | d0fe8c12 | Athina Bekakou | |
7074 | d0fe8c12 | Athina Bekakou | var fields = Ember.get(App.Blog, 'fields');
|
7075 | d0fe8c12 | Athina Bekakou | fields.forEach(function(field, kind) {
|
7076 | d0fe8c12 | Athina Bekakou | console.log(field, kind);
|
7077 | d0fe8c12 | Athina Bekakou | });
|
7078 | d0fe8c12 | Athina Bekakou | |
7079 | d0fe8c12 | Athina Bekakou | // prints:
|
7080 | d0fe8c12 | Athina Bekakou | // users, hasMany
|
7081 | d0fe8c12 | Athina Bekakou | // owner, belongsTo
|
7082 | d0fe8c12 | Athina Bekakou | // posts, hasMany
|
7083 | d0fe8c12 | Athina Bekakou | // title, attribute
|
7084 | d0fe8c12 | Athina Bekakou | ```
|
7085 | d0fe8c12 | Athina Bekakou | |
7086 | d0fe8c12 | Athina Bekakou | @property fields
|
7087 | d0fe8c12 | Athina Bekakou | @static
|
7088 | d0fe8c12 | Athina Bekakou | @type Ember.Map
|
7089 | d0fe8c12 | Athina Bekakou | @readOnly
|
7090 | d0fe8c12 | Athina Bekakou | */
|
7091 | d0fe8c12 | Athina Bekakou | fields: Ember.computed(function() { |
7092 | d0fe8c12 | Athina Bekakou | var map = Ember.Map.create();
|
7093 | d0fe8c12 | Athina Bekakou | |
7094 | d0fe8c12 | Athina Bekakou | this.eachComputedProperty(function(name, meta) { |
7095 | d0fe8c12 | Athina Bekakou | if (meta.isRelationship) {
|
7096 | d0fe8c12 | Athina Bekakou | map.set(name, meta.kind); |
7097 | d0fe8c12 | Athina Bekakou | } else if (meta.isAttribute) { |
7098 | d0fe8c12 | Athina Bekakou | map.set(name, 'attribute');
|
7099 | d0fe8c12 | Athina Bekakou | } |
7100 | d0fe8c12 | Athina Bekakou | }); |
7101 | d0fe8c12 | Athina Bekakou | |
7102 | d0fe8c12 | Athina Bekakou | return map;
|
7103 | d0fe8c12 | Athina Bekakou | }), |
7104 | d0fe8c12 | Athina Bekakou | |
7105 | d0fe8c12 | Athina Bekakou | /**
|
7106 | d0fe8c12 | Athina Bekakou | Given a callback, iterates over each of the relationships in the model,
|
7107 | d0fe8c12 | Athina Bekakou | invoking the callback with the name of each relationship and its relationship
|
7108 | d0fe8c12 | Athina Bekakou | descriptor.
|
7109 | d0fe8c12 | Athina Bekakou | |
7110 | d0fe8c12 | Athina Bekakou | @method eachRelationship
|
7111 | d0fe8c12 | Athina Bekakou | @static
|
7112 | d0fe8c12 | Athina Bekakou | @param {Function} callback the callback to invoke
|
7113 | d0fe8c12 | Athina Bekakou | @param {any} binding the value to which the callback's `this` should be bound
|
7114 | d0fe8c12 | Athina Bekakou | */
|
7115 | d0fe8c12 | Athina Bekakou | eachRelationship: function(callback, binding) { |
7116 | d0fe8c12 | Athina Bekakou | get(this, 'relationshipsByName').forEach(function(name, relationship) { |
7117 | d0fe8c12 | Athina Bekakou | callback.call(binding, name, relationship); |
7118 | d0fe8c12 | Athina Bekakou | }); |
7119 | d0fe8c12 | Athina Bekakou | }, |
7120 | d0fe8c12 | Athina Bekakou | |
7121 | d0fe8c12 | Athina Bekakou | /**
|
7122 | d0fe8c12 | Athina Bekakou | Given a callback, iterates over each of the types related to a model,
|
7123 | d0fe8c12 | Athina Bekakou | invoking the callback with the related type's class. Each type will be
|
7124 | d0fe8c12 | Athina Bekakou | returned just once, regardless of how many different relationships it has
|
7125 | d0fe8c12 | Athina Bekakou | with a model.
|
7126 | d0fe8c12 | Athina Bekakou | |
7127 | d0fe8c12 | Athina Bekakou | @method eachRelatedType
|
7128 | d0fe8c12 | Athina Bekakou | @static
|
7129 | d0fe8c12 | Athina Bekakou | @param {Function} callback the callback to invoke
|
7130 | d0fe8c12 | Athina Bekakou | @param {any} binding the value to which the callback's `this` should be bound
|
7131 | d0fe8c12 | Athina Bekakou | */
|
7132 | d0fe8c12 | Athina Bekakou | eachRelatedType: function(callback, binding) { |
7133 | d0fe8c12 | Athina Bekakou | get(this, 'relatedTypes').forEach(function(type) { |
7134 | d0fe8c12 | Athina Bekakou | callback.call(binding, type); |
7135 | d0fe8c12 | Athina Bekakou | }); |
7136 | d0fe8c12 | Athina Bekakou | } |
7137 | d0fe8c12 | Athina Bekakou | }); |
7138 | d0fe8c12 | Athina Bekakou | |
7139 | d0fe8c12 | Athina Bekakou | DS.Model.reopen({ |
7140 | d0fe8c12 | Athina Bekakou | /**
|
7141 | d0fe8c12 | Athina Bekakou | Given a callback, iterates over each of the relationships in the model,
|
7142 | d0fe8c12 | Athina Bekakou | invoking the callback with the name of each relationship and its relationship
|
7143 | d0fe8c12 | Athina Bekakou | descriptor.
|
7144 | d0fe8c12 | Athina Bekakou | |
7145 | d0fe8c12 | Athina Bekakou | @method eachRelationship
|
7146 | d0fe8c12 | Athina Bekakou | @param {Function} callback the callback to invoke
|
7147 | d0fe8c12 | Athina Bekakou | @param {any} binding the value to which the callback's `this` should be bound
|
7148 | d0fe8c12 | Athina Bekakou | */
|
7149 | d0fe8c12 | Athina Bekakou | eachRelationship: function(callback, binding) { |
7150 | d0fe8c12 | Athina Bekakou | this.constructor.eachRelationship(callback, binding);
|
7151 | d0fe8c12 | Athina Bekakou | } |
7152 | d0fe8c12 | Athina Bekakou | }); |
7153 | d0fe8c12 | Athina Bekakou | |
7154 | d0fe8c12 | Athina Bekakou | })(); |
7155 | d0fe8c12 | Athina Bekakou | |
7156 | d0fe8c12 | Athina Bekakou | |
7157 | d0fe8c12 | Athina Bekakou | |
7158 | d0fe8c12 | Athina Bekakou | (function() {
|
7159 | d0fe8c12 | Athina Bekakou | /**
|
7160 | d0fe8c12 | Athina Bekakou | @module ember-data
|
7161 | d0fe8c12 | Athina Bekakou | */
|
7162 | d0fe8c12 | Athina Bekakou | |
7163 | d0fe8c12 | Athina Bekakou | })(); |
7164 | d0fe8c12 | Athina Bekakou | |
7165 | d0fe8c12 | Athina Bekakou | |
7166 | d0fe8c12 | Athina Bekakou | |
7167 | d0fe8c12 | Athina Bekakou | (function() {
|
7168 | d0fe8c12 | Athina Bekakou | /**
|
7169 | d0fe8c12 | Athina Bekakou | @module ember-data
|
7170 | d0fe8c12 | Athina Bekakou | */
|
7171 | d0fe8c12 | Athina Bekakou | |
7172 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
7173 | d0fe8c12 | Athina Bekakou | var once = Ember.run.once;
|
7174 | d0fe8c12 | Athina Bekakou | var forEach = Ember.EnumerableUtils.forEach;
|
7175 | d0fe8c12 | Athina Bekakou | |
7176 | d0fe8c12 | Athina Bekakou | /**
|
7177 | d0fe8c12 | Athina Bekakou | @class RecordArrayManager
|
7178 | d0fe8c12 | Athina Bekakou | @namespace DS
|
7179 | d0fe8c12 | Athina Bekakou | @private
|
7180 | d0fe8c12 | Athina Bekakou | @extends Ember.Object
|
7181 | d0fe8c12 | Athina Bekakou | */
|
7182 | d0fe8c12 | Athina Bekakou | DS.RecordArrayManager = Ember.Object.extend({ |
7183 | d0fe8c12 | Athina Bekakou | init: function() { |
7184 | d0fe8c12 | Athina Bekakou | this.filteredRecordArrays = Ember.MapWithDefault.create({
|
7185 | d0fe8c12 | Athina Bekakou | defaultValue: function() { return []; } |
7186 | d0fe8c12 | Athina Bekakou | }); |
7187 | d0fe8c12 | Athina Bekakou | |
7188 | d0fe8c12 | Athina Bekakou | this.changedRecords = [];
|
7189 | d0fe8c12 | Athina Bekakou | }, |
7190 | d0fe8c12 | Athina Bekakou | |
7191 | d0fe8c12 | Athina Bekakou | recordDidChange: function(record) { |
7192 | d0fe8c12 | Athina Bekakou | this.changedRecords.push(record);
|
7193 | d0fe8c12 | Athina Bekakou | once(this, this.updateRecordArrays); |
7194 | d0fe8c12 | Athina Bekakou | }, |
7195 | d0fe8c12 | Athina Bekakou | |
7196 | d0fe8c12 | Athina Bekakou | recordArraysForRecord: function(record) { |
7197 | d0fe8c12 | Athina Bekakou | record._recordArrays = record._recordArrays || Ember.OrderedSet.create(); |
7198 | d0fe8c12 | Athina Bekakou | return record._recordArrays;
|
7199 | d0fe8c12 | Athina Bekakou | }, |
7200 | d0fe8c12 | Athina Bekakou | |
7201 | d0fe8c12 | Athina Bekakou | /**
|
7202 | d0fe8c12 | Athina Bekakou | This method is invoked whenever data is loaded into the store by the
|
7203 | d0fe8c12 | Athina Bekakou | adapter or updated by the adapter, or when a record has changed.
|
7204 | d0fe8c12 | Athina Bekakou | |
7205 | d0fe8c12 | Athina Bekakou | It updates all record arrays that a record belongs to.
|
7206 | d0fe8c12 | Athina Bekakou | |
7207 | d0fe8c12 | Athina Bekakou | To avoid thrashing, it only runs at most once per run loop.
|
7208 | d0fe8c12 | Athina Bekakou | |
7209 | d0fe8c12 | Athina Bekakou | @method updateRecordArrays
|
7210 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7211 | d0fe8c12 | Athina Bekakou | @param {Number|String} clientId
|
7212 | d0fe8c12 | Athina Bekakou | */
|
7213 | d0fe8c12 | Athina Bekakou | updateRecordArrays: function() { |
7214 | d0fe8c12 | Athina Bekakou | forEach(this.changedRecords, function(record) { |
7215 | d0fe8c12 | Athina Bekakou | if (get(record, 'isDeleted')) { |
7216 | d0fe8c12 | Athina Bekakou | this._recordWasDeleted(record);
|
7217 | d0fe8c12 | Athina Bekakou | } else {
|
7218 | d0fe8c12 | Athina Bekakou | this._recordWasChanged(record);
|
7219 | d0fe8c12 | Athina Bekakou | } |
7220 | d0fe8c12 | Athina Bekakou | }, this);
|
7221 | d0fe8c12 | Athina Bekakou | |
7222 | d0fe8c12 | Athina Bekakou | this.changedRecords = [];
|
7223 | d0fe8c12 | Athina Bekakou | }, |
7224 | d0fe8c12 | Athina Bekakou | |
7225 | d0fe8c12 | Athina Bekakou | _recordWasDeleted: function (record) { |
7226 | d0fe8c12 | Athina Bekakou | var recordArrays = record._recordArrays;
|
7227 | d0fe8c12 | Athina Bekakou | |
7228 | d0fe8c12 | Athina Bekakou | if (!recordArrays) { return; } |
7229 | d0fe8c12 | Athina Bekakou | |
7230 | d0fe8c12 | Athina Bekakou | forEach(recordArrays, function(array) {
|
7231 | d0fe8c12 | Athina Bekakou | array.removeRecord(record); |
7232 | d0fe8c12 | Athina Bekakou | }); |
7233 | d0fe8c12 | Athina Bekakou | }, |
7234 | d0fe8c12 | Athina Bekakou | |
7235 | d0fe8c12 | Athina Bekakou | _recordWasChanged: function (record) { |
7236 | d0fe8c12 | Athina Bekakou | var type = record.constructor,
|
7237 | d0fe8c12 | Athina Bekakou | recordArrays = this.filteredRecordArrays.get(type),
|
7238 | d0fe8c12 | Athina Bekakou | filter; |
7239 | d0fe8c12 | Athina Bekakou | |
7240 | d0fe8c12 | Athina Bekakou | forEach(recordArrays, function(array) {
|
7241 | d0fe8c12 | Athina Bekakou | filter = get(array, 'filterFunction');
|
7242 | d0fe8c12 | Athina Bekakou | this.updateRecordArray(array, filter, type, record);
|
7243 | d0fe8c12 | Athina Bekakou | }, this);
|
7244 | d0fe8c12 | Athina Bekakou | |
7245 | d0fe8c12 | Athina Bekakou | // loop through all manyArrays containing an unloaded copy of this
|
7246 | d0fe8c12 | Athina Bekakou | // clientId and notify them that the record was loaded.
|
7247 | d0fe8c12 | Athina Bekakou | var manyArrays = record._loadingRecordArrays;
|
7248 | d0fe8c12 | Athina Bekakou | |
7249 | d0fe8c12 | Athina Bekakou | if (manyArrays) {
|
7250 | d0fe8c12 | Athina Bekakou | for (var i=0, l=manyArrays.length; i<l; i++) { |
7251 | d0fe8c12 | Athina Bekakou | manyArrays[i].loadedRecord(); |
7252 | d0fe8c12 | Athina Bekakou | } |
7253 | d0fe8c12 | Athina Bekakou | |
7254 | d0fe8c12 | Athina Bekakou | record._loadingRecordArrays = []; |
7255 | d0fe8c12 | Athina Bekakou | } |
7256 | d0fe8c12 | Athina Bekakou | }, |
7257 | d0fe8c12 | Athina Bekakou | |
7258 | d0fe8c12 | Athina Bekakou | /**
|
7259 | d0fe8c12 | Athina Bekakou | Update an individual filter.
|
7260 | d0fe8c12 | Athina Bekakou | |
7261 | d0fe8c12 | Athina Bekakou | @method updateRecordArray
|
7262 | d0fe8c12 | Athina Bekakou | @param {DS.FilteredRecordArray} array
|
7263 | d0fe8c12 | Athina Bekakou | @param {Function} filter
|
7264 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7265 | d0fe8c12 | Athina Bekakou | @param {Number|String} clientId
|
7266 | d0fe8c12 | Athina Bekakou | */
|
7267 | d0fe8c12 | Athina Bekakou | updateRecordArray: function(array, filter, type, record) { |
7268 | d0fe8c12 | Athina Bekakou | var shouldBeInArray;
|
7269 | d0fe8c12 | Athina Bekakou | |
7270 | d0fe8c12 | Athina Bekakou | if (!filter) {
|
7271 | d0fe8c12 | Athina Bekakou | shouldBeInArray = true;
|
7272 | d0fe8c12 | Athina Bekakou | } else {
|
7273 | d0fe8c12 | Athina Bekakou | shouldBeInArray = filter(record); |
7274 | d0fe8c12 | Athina Bekakou | } |
7275 | d0fe8c12 | Athina Bekakou | |
7276 | d0fe8c12 | Athina Bekakou | var recordArrays = this.recordArraysForRecord(record); |
7277 | d0fe8c12 | Athina Bekakou | |
7278 | d0fe8c12 | Athina Bekakou | if (shouldBeInArray) {
|
7279 | d0fe8c12 | Athina Bekakou | recordArrays.add(array); |
7280 | d0fe8c12 | Athina Bekakou | array.addRecord(record); |
7281 | d0fe8c12 | Athina Bekakou | } else if (!shouldBeInArray) { |
7282 | d0fe8c12 | Athina Bekakou | recordArrays.remove(array); |
7283 | d0fe8c12 | Athina Bekakou | array.removeRecord(record); |
7284 | d0fe8c12 | Athina Bekakou | } |
7285 | d0fe8c12 | Athina Bekakou | }, |
7286 | d0fe8c12 | Athina Bekakou | |
7287 | d0fe8c12 | Athina Bekakou | /**
|
7288 | d0fe8c12 | Athina Bekakou | This method is invoked if the `filterFunction` property is
|
7289 | d0fe8c12 | Athina Bekakou | changed on a `DS.FilteredRecordArray`.
|
7290 | d0fe8c12 | Athina Bekakou | |
7291 | d0fe8c12 | Athina Bekakou | It essentially re-runs the filter from scratch. This same
|
7292 | d0fe8c12 | Athina Bekakou | method is invoked when the filter is created in th first place.
|
7293 | d0fe8c12 | Athina Bekakou | |
7294 | d0fe8c12 | Athina Bekakou | @method updateFilter
|
7295 | d0fe8c12 | Athina Bekakou | @param array
|
7296 | d0fe8c12 | Athina Bekakou | @param type
|
7297 | d0fe8c12 | Athina Bekakou | @param filter
|
7298 | d0fe8c12 | Athina Bekakou | */
|
7299 | d0fe8c12 | Athina Bekakou | updateFilter: function(array, type, filter) { |
7300 | d0fe8c12 | Athina Bekakou | var typeMap = this.store.typeMapFor(type), |
7301 | d0fe8c12 | Athina Bekakou | records = typeMap.records, record; |
7302 | d0fe8c12 | Athina Bekakou | |
7303 | d0fe8c12 | Athina Bekakou | for (var i=0, l=records.length; i<l; i++) { |
7304 | d0fe8c12 | Athina Bekakou | record = records[i]; |
7305 | d0fe8c12 | Athina Bekakou | |
7306 | d0fe8c12 | Athina Bekakou | if (!get(record, 'isDeleted') && !get(record, 'isEmpty')) { |
7307 | d0fe8c12 | Athina Bekakou | this.updateRecordArray(array, filter, type, record);
|
7308 | d0fe8c12 | Athina Bekakou | } |
7309 | d0fe8c12 | Athina Bekakou | } |
7310 | d0fe8c12 | Athina Bekakou | }, |
7311 | d0fe8c12 | Athina Bekakou | |
7312 | d0fe8c12 | Athina Bekakou | /**
|
7313 | d0fe8c12 | Athina Bekakou | Create a `DS.ManyArray` for a type and list of record references, and index
|
7314 | d0fe8c12 | Athina Bekakou | the `ManyArray` under each reference. This allows us to efficiently remove
|
7315 | d0fe8c12 | Athina Bekakou | records from `ManyArray`s when they are deleted.
|
7316 | d0fe8c12 | Athina Bekakou | |
7317 | d0fe8c12 | Athina Bekakou | @method createManyArray
|
7318 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7319 | d0fe8c12 | Athina Bekakou | @param {Array} references
|
7320 | d0fe8c12 | Athina Bekakou | @return {DS.ManyArray}
|
7321 | d0fe8c12 | Athina Bekakou | */
|
7322 | d0fe8c12 | Athina Bekakou | createManyArray: function(type, records) { |
7323 | d0fe8c12 | Athina Bekakou | var manyArray = DS.ManyArray.create({
|
7324 | d0fe8c12 | Athina Bekakou | type: type,
|
7325 | d0fe8c12 | Athina Bekakou | content: records,
|
7326 | d0fe8c12 | Athina Bekakou | store: this.store |
7327 | d0fe8c12 | Athina Bekakou | }); |
7328 | d0fe8c12 | Athina Bekakou | |
7329 | d0fe8c12 | Athina Bekakou | forEach(records, function(record) {
|
7330 | d0fe8c12 | Athina Bekakou | var arrays = this.recordArraysForRecord(record); |
7331 | d0fe8c12 | Athina Bekakou | arrays.add(manyArray); |
7332 | d0fe8c12 | Athina Bekakou | }, this);
|
7333 | d0fe8c12 | Athina Bekakou | |
7334 | d0fe8c12 | Athina Bekakou | return manyArray;
|
7335 | d0fe8c12 | Athina Bekakou | }, |
7336 | d0fe8c12 | Athina Bekakou | |
7337 | d0fe8c12 | Athina Bekakou | /**
|
7338 | d0fe8c12 | Athina Bekakou | Create a `DS.RecordArray` for a type and register it for updates.
|
7339 | d0fe8c12 | Athina Bekakou | |
7340 | d0fe8c12 | Athina Bekakou | @method createRecordArray
|
7341 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7342 | d0fe8c12 | Athina Bekakou | @return {DS.RecordArray}
|
7343 | d0fe8c12 | Athina Bekakou | */
|
7344 | d0fe8c12 | Athina Bekakou | createRecordArray: function(type) { |
7345 | d0fe8c12 | Athina Bekakou | var array = DS.RecordArray.create({
|
7346 | d0fe8c12 | Athina Bekakou | type: type,
|
7347 | d0fe8c12 | Athina Bekakou | content: Ember.A(),
|
7348 | d0fe8c12 | Athina Bekakou | store: this.store, |
7349 | d0fe8c12 | Athina Bekakou | isLoaded: true |
7350 | d0fe8c12 | Athina Bekakou | }); |
7351 | d0fe8c12 | Athina Bekakou | |
7352 | d0fe8c12 | Athina Bekakou | this.registerFilteredRecordArray(array, type);
|
7353 | d0fe8c12 | Athina Bekakou | |
7354 | d0fe8c12 | Athina Bekakou | return array;
|
7355 | d0fe8c12 | Athina Bekakou | }, |
7356 | d0fe8c12 | Athina Bekakou | |
7357 | d0fe8c12 | Athina Bekakou | /**
|
7358 | d0fe8c12 | Athina Bekakou | Create a `DS.FilteredRecordArray` for a type and register it for updates.
|
7359 | d0fe8c12 | Athina Bekakou | |
7360 | d0fe8c12 | Athina Bekakou | @method createFilteredRecordArray
|
7361 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7362 | d0fe8c12 | Athina Bekakou | @param {Function} filter
|
7363 | d0fe8c12 | Athina Bekakou | @return {DS.FilteredRecordArray}
|
7364 | d0fe8c12 | Athina Bekakou | */
|
7365 | d0fe8c12 | Athina Bekakou | createFilteredRecordArray: function(type, filter) { |
7366 | d0fe8c12 | Athina Bekakou | var array = DS.FilteredRecordArray.create({
|
7367 | d0fe8c12 | Athina Bekakou | type: type,
|
7368 | d0fe8c12 | Athina Bekakou | content: Ember.A(),
|
7369 | d0fe8c12 | Athina Bekakou | store: this.store, |
7370 | d0fe8c12 | Athina Bekakou | manager: this, |
7371 | d0fe8c12 | Athina Bekakou | filterFunction: filter
|
7372 | d0fe8c12 | Athina Bekakou | }); |
7373 | d0fe8c12 | Athina Bekakou | |
7374 | d0fe8c12 | Athina Bekakou | this.registerFilteredRecordArray(array, type, filter);
|
7375 | d0fe8c12 | Athina Bekakou | |
7376 | d0fe8c12 | Athina Bekakou | return array;
|
7377 | d0fe8c12 | Athina Bekakou | }, |
7378 | d0fe8c12 | Athina Bekakou | |
7379 | d0fe8c12 | Athina Bekakou | /**
|
7380 | d0fe8c12 | Athina Bekakou | Create a `DS.AdapterPopulatedRecordArray` for a type with given query.
|
7381 | d0fe8c12 | Athina Bekakou | |
7382 | d0fe8c12 | Athina Bekakou | @method createAdapterPopulatedRecordArray
|
7383 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7384 | d0fe8c12 | Athina Bekakou | @param {Object} query
|
7385 | d0fe8c12 | Athina Bekakou | @return {DS.AdapterPopulatedRecordArray}
|
7386 | d0fe8c12 | Athina Bekakou | */
|
7387 | d0fe8c12 | Athina Bekakou | createAdapterPopulatedRecordArray: function(type, query) { |
7388 | d0fe8c12 | Athina Bekakou | return DS.AdapterPopulatedRecordArray.create({
|
7389 | d0fe8c12 | Athina Bekakou | type: type,
|
7390 | d0fe8c12 | Athina Bekakou | query: query,
|
7391 | d0fe8c12 | Athina Bekakou | content: Ember.A(),
|
7392 | d0fe8c12 | Athina Bekakou | store: this.store |
7393 | d0fe8c12 | Athina Bekakou | }); |
7394 | d0fe8c12 | Athina Bekakou | }, |
7395 | d0fe8c12 | Athina Bekakou | |
7396 | d0fe8c12 | Athina Bekakou | /**
|
7397 | d0fe8c12 | Athina Bekakou | Register a RecordArray for a given type to be backed by
|
7398 | d0fe8c12 | Athina Bekakou | a filter function. This will cause the array to update
|
7399 | d0fe8c12 | Athina Bekakou | automatically when records of that type change attribute
|
7400 | d0fe8c12 | Athina Bekakou | values or states.
|
7401 | d0fe8c12 | Athina Bekakou | |
7402 | d0fe8c12 | Athina Bekakou | @method registerFilteredRecordArray
|
7403 | d0fe8c12 | Athina Bekakou | @param {DS.RecordArray} array
|
7404 | d0fe8c12 | Athina Bekakou | @param {Class} type
|
7405 | d0fe8c12 | Athina Bekakou | @param {Function} filter
|
7406 | d0fe8c12 | Athina Bekakou | */
|
7407 | d0fe8c12 | Athina Bekakou | registerFilteredRecordArray: function(array, type, filter) { |
7408 | d0fe8c12 | Athina Bekakou | var recordArrays = this.filteredRecordArrays.get(type); |
7409 | d0fe8c12 | Athina Bekakou | recordArrays.push(array); |
7410 | d0fe8c12 | Athina Bekakou | |
7411 | d0fe8c12 | Athina Bekakou | this.updateFilter(array, type, filter);
|
7412 | d0fe8c12 | Athina Bekakou | }, |
7413 | d0fe8c12 | Athina Bekakou | |
7414 | d0fe8c12 | Athina Bekakou | // Internally, we maintain a map of all unloaded IDs requested by
|
7415 | d0fe8c12 | Athina Bekakou | // a ManyArray. As the adapter loads data into the store, the
|
7416 | d0fe8c12 | Athina Bekakou | // store notifies any interested ManyArrays. When the ManyArray's
|
7417 | d0fe8c12 | Athina Bekakou | // total number of loading records drops to zero, it becomes
|
7418 | d0fe8c12 | Athina Bekakou | // `isLoaded` and fires a `didLoad` event.
|
7419 | d0fe8c12 | Athina Bekakou | registerWaitingRecordArray: function(record, array) { |
7420 | d0fe8c12 | Athina Bekakou | var loadingRecordArrays = record._loadingRecordArrays || [];
|
7421 | d0fe8c12 | Athina Bekakou | loadingRecordArrays.push(array); |
7422 | d0fe8c12 | Athina Bekakou | record._loadingRecordArrays = loadingRecordArrays; |
7423 | d0fe8c12 | Athina Bekakou | } |
7424 | d0fe8c12 | Athina Bekakou | }); |
7425 | d0fe8c12 | Athina Bekakou | |
7426 | d0fe8c12 | Athina Bekakou | })(); |
7427 | d0fe8c12 | Athina Bekakou | |
7428 | d0fe8c12 | Athina Bekakou | |
7429 | d0fe8c12 | Athina Bekakou | |
7430 | d0fe8c12 | Athina Bekakou | (function() {
|
7431 | d0fe8c12 | Athina Bekakou | /**
|
7432 | d0fe8c12 | Athina Bekakou | @module ember-data
|
7433 | d0fe8c12 | Athina Bekakou | */
|
7434 | d0fe8c12 | Athina Bekakou | |
7435 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
7436 | d0fe8c12 | Athina Bekakou | var map = Ember.ArrayPolyfills.map;
|
7437 | d0fe8c12 | Athina Bekakou | |
7438 | d0fe8c12 | Athina Bekakou | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; |
7439 | d0fe8c12 | Athina Bekakou | |
7440 | d0fe8c12 | Athina Bekakou | /**
|
7441 | d0fe8c12 | Athina Bekakou | A `DS.InvalidError` is used by an adapter to signal the external API
|
7442 | d0fe8c12 | Athina Bekakou | was unable to process a request because the content was not
|
7443 | d0fe8c12 | Athina Bekakou | semantically correct or meaningful per the API. Usually this means a
|
7444 | d0fe8c12 | Athina Bekakou | record failed some form of server side validation. When a promise
|
7445 | d0fe8c12 | Athina Bekakou | from an adapter is rejected with a `DS.InvalidError` the record will
|
7446 | d0fe8c12 | Athina Bekakou | transition to the `invalid` state and the errors will be set to the
|
7447 | d0fe8c12 | Athina Bekakou | `errors` property on the record.
|
7448 | d0fe8c12 | Athina Bekakou | |
7449 | d0fe8c12 | Athina Bekakou | Example
|
7450 | d0fe8c12 | Athina Bekakou | |
7451 | d0fe8c12 | Athina Bekakou | ```javascript
|
7452 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.RESTAdapter.extend({
|
7453 | d0fe8c12 | Athina Bekakou | ajaxError: function(jqXHR) {
|
7454 | d0fe8c12 | Athina Bekakou | var error = this._super(jqXHR);
|
7455 | d0fe8c12 | Athina Bekakou | |
7456 | d0fe8c12 | Athina Bekakou | if (jqXHR && jqXHR.status === 422) {
|
7457 | d0fe8c12 | Athina Bekakou | var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
|
7458 | d0fe8c12 | Athina Bekakou | return new DS.InvalidError(jsonErrors);
|
7459 | d0fe8c12 | Athina Bekakou | } else {
|
7460 | d0fe8c12 | Athina Bekakou | return error;
|
7461 | d0fe8c12 | Athina Bekakou | }
|
7462 | d0fe8c12 | Athina Bekakou | }
|
7463 | d0fe8c12 | Athina Bekakou | });
|
7464 | d0fe8c12 | Athina Bekakou | ```
|
7465 | d0fe8c12 | Athina Bekakou | |
7466 | d0fe8c12 | Athina Bekakou | @class InvalidError
|
7467 | d0fe8c12 | Athina Bekakou | @namespace DS
|
7468 | d0fe8c12 | Athina Bekakou | */
|
7469 | d0fe8c12 | Athina Bekakou | DS.InvalidError = function(errors) { |
7470 | d0fe8c12 | Athina Bekakou | var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors)); |
7471 | d0fe8c12 | Athina Bekakou | this.errors = errors;
|
7472 | d0fe8c12 | Athina Bekakou | |
7473 | d0fe8c12 | Athina Bekakou | for (var i=0, l=errorProps.length; i<l; i++) { |
7474 | d0fe8c12 | Athina Bekakou | this[errorProps[i]] = tmp[errorProps[i]];
|
7475 | d0fe8c12 | Athina Bekakou | } |
7476 | d0fe8c12 | Athina Bekakou | }; |
7477 | d0fe8c12 | Athina Bekakou | DS.InvalidError.prototype = Ember.create(Error.prototype); |
7478 | d0fe8c12 | Athina Bekakou | |
7479 | d0fe8c12 | Athina Bekakou | /**
|
7480 | d0fe8c12 | Athina Bekakou | An adapter is an object that receives requests from a store and
|
7481 | d0fe8c12 | Athina Bekakou | translates them into the appropriate action to take against your
|
7482 | d0fe8c12 | Athina Bekakou | persistence layer. The persistence layer is usually an HTTP API, but
|
7483 | d0fe8c12 | Athina Bekakou | may be anything, such as the browser's local storage. Typically the
|
7484 | d0fe8c12 | Athina Bekakou | adapter is not invoked directly instead its functionality is accessed
|
7485 | d0fe8c12 | Athina Bekakou | through the `store`.
|
7486 | d0fe8c12 | Athina Bekakou | |
7487 | d0fe8c12 | Athina Bekakou | ### Creating an Adapter
|
7488 | d0fe8c12 | Athina Bekakou | |
7489 | d0fe8c12 | Athina Bekakou | First, create a new subclass of `DS.Adapter`:
|
7490 | d0fe8c12 | Athina Bekakou | |
7491 | d0fe8c12 | Athina Bekakou | ```javascript
|
7492 | d0fe8c12 | Athina Bekakou | App.MyAdapter = DS.Adapter.extend({
|
7493 | d0fe8c12 | Athina Bekakou | // ...your code here
|
7494 | d0fe8c12 | Athina Bekakou | });
|
7495 | d0fe8c12 | Athina Bekakou | ```
|
7496 | d0fe8c12 | Athina Bekakou | |
7497 | d0fe8c12 | Athina Bekakou | To tell your store which adapter to use, set its `adapter` property:
|
7498 | d0fe8c12 | Athina Bekakou | |
7499 | d0fe8c12 | Athina Bekakou | ```javascript
|
7500 | d0fe8c12 | Athina Bekakou | App.store = DS.Store.create({
|
7501 | d0fe8c12 | Athina Bekakou | adapter: App.MyAdapter.create()
|
7502 | d0fe8c12 | Athina Bekakou | });
|
7503 | d0fe8c12 | Athina Bekakou | ```
|
7504 | d0fe8c12 | Athina Bekakou | |
7505 | d0fe8c12 | Athina Bekakou | `DS.Adapter` is an abstract base class that you should override in your
|
7506 | d0fe8c12 | Athina Bekakou | application to customize it for your backend. The minimum set of methods
|
7507 | d0fe8c12 | Athina Bekakou | that you should implement is:
|
7508 | d0fe8c12 | Athina Bekakou | |
7509 | d0fe8c12 | Athina Bekakou | * `find()`
|
7510 | d0fe8c12 | Athina Bekakou | * `createRecord()`
|
7511 | d0fe8c12 | Athina Bekakou | * `updateRecord()`
|
7512 | d0fe8c12 | Athina Bekakou | * `deleteRecord()`
|
7513 | d0fe8c12 | Athina Bekakou | * `findAll()`
|
7514 | d0fe8c12 | Athina Bekakou | * `findQuery()`
|
7515 | d0fe8c12 | Athina Bekakou | |
7516 | d0fe8c12 | Athina Bekakou | To improve the network performance of your application, you can optimize
|
7517 | d0fe8c12 | Athina Bekakou | your adapter by overriding these lower-level methods:
|
7518 | d0fe8c12 | Athina Bekakou | |
7519 | d0fe8c12 | Athina Bekakou | * `findMany()`
|
7520 | d0fe8c12 | Athina Bekakou | |
7521 | d0fe8c12 | Athina Bekakou | |
7522 | d0fe8c12 | Athina Bekakou | For an example implementation, see `DS.RESTAdapter`, the
|
7523 | d0fe8c12 | Athina Bekakou | included REST adapter.
|
7524 | d0fe8c12 | Athina Bekakou | |
7525 | d0fe8c12 | Athina Bekakou | @class Adapter
|
7526 | d0fe8c12 | Athina Bekakou | @namespace DS
|
7527 | d0fe8c12 | Athina Bekakou | @extends Ember.Object
|
7528 | d0fe8c12 | Athina Bekakou | */
|
7529 | d0fe8c12 | Athina Bekakou | |
7530 | d0fe8c12 | Athina Bekakou | DS.Adapter = Ember.Object.extend({ |
7531 | d0fe8c12 | Athina Bekakou | |
7532 | d0fe8c12 | Athina Bekakou | /**
|
7533 | d0fe8c12 | Athina Bekakou | If you would like your adapter to use a custom serializer you can
|
7534 | d0fe8c12 | Athina Bekakou | set the `defaultSerializer` property to be the name of the custom
|
7535 | d0fe8c12 | Athina Bekakou | serializer.
|
7536 | d0fe8c12 | Athina Bekakou | |
7537 | d0fe8c12 | Athina Bekakou | Note the `defaultSerializer` serializer has a lower priority then
|
7538 | d0fe8c12 | Athina Bekakou | a model specific serializer (i.e. `PostSerializer`) or the
|
7539 | d0fe8c12 | Athina Bekakou | `application` serializer.
|
7540 | d0fe8c12 | Athina Bekakou | |
7541 | d0fe8c12 | Athina Bekakou | ```javascript
|
7542 | d0fe8c12 | Athina Bekakou | var DjangoAdapter = DS.Adapter.extend({
|
7543 | d0fe8c12 | Athina Bekakou | defaultSerializer: 'django'
|
7544 | d0fe8c12 | Athina Bekakou | });
|
7545 | d0fe8c12 | Athina Bekakou | ```
|
7546 | d0fe8c12 | Athina Bekakou | |
7547 | d0fe8c12 | Athina Bekakou | @property defaultSerializer
|
7548 | d0fe8c12 | Athina Bekakou | @type {String}
|
7549 | d0fe8c12 | Athina Bekakou | */
|
7550 | d0fe8c12 | Athina Bekakou | |
7551 | d0fe8c12 | Athina Bekakou | /**
|
7552 | d0fe8c12 | Athina Bekakou | The `find()` method is invoked when the store is asked for a record that
|
7553 | d0fe8c12 | Athina Bekakou | has not previously been loaded. In response to `find()` being called, you
|
7554 | d0fe8c12 | Athina Bekakou | should query your persistence layer for a record with the given ID. Once
|
7555 | d0fe8c12 | Athina Bekakou | found, you can asynchronously call the store's `push()` method to push
|
7556 | d0fe8c12 | Athina Bekakou | the record into the store.
|
7557 | d0fe8c12 | Athina Bekakou | |
7558 | d0fe8c12 | Athina Bekakou | Here is an example `find` implementation:
|
7559 | d0fe8c12 | Athina Bekakou | |
7560 | d0fe8c12 | Athina Bekakou | ```javascript
|
7561 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7562 | d0fe8c12 | Athina Bekakou | find: function(store, type, id) {
|
7563 | d0fe8c12 | Athina Bekakou | var url = [type, id].join('/');
|
7564 | d0fe8c12 | Athina Bekakou | |
7565 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7566 | d0fe8c12 | Athina Bekakou | jQuery.getJSON(url).then(function(data) {
|
7567 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7568 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7569 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7570 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7571 | d0fe8c12 | Athina Bekakou | });
|
7572 | d0fe8c12 | Athina Bekakou | });
|
7573 | d0fe8c12 | Athina Bekakou | }
|
7574 | d0fe8c12 | Athina Bekakou | });
|
7575 | d0fe8c12 | Athina Bekakou | ```
|
7576 | d0fe8c12 | Athina Bekakou | |
7577 | d0fe8c12 | Athina Bekakou | @method find
|
7578 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7579 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
7580 | d0fe8c12 | Athina Bekakou | @param {String} id
|
7581 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7582 | d0fe8c12 | Athina Bekakou | */
|
7583 | d0fe8c12 | Athina Bekakou | find: Ember.required(Function),
|
7584 | d0fe8c12 | Athina Bekakou | |
7585 | d0fe8c12 | Athina Bekakou | /**
|
7586 | d0fe8c12 | Athina Bekakou | The `findAll()` method is called when you call `find` on the store
|
7587 | d0fe8c12 | Athina Bekakou | without an ID (i.e. `store.find('post')`).
|
7588 | d0fe8c12 | Athina Bekakou | |
7589 | d0fe8c12 | Athina Bekakou | Example
|
7590 | d0fe8c12 | Athina Bekakou | |
7591 | d0fe8c12 | Athina Bekakou | ```javascript
|
7592 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7593 | d0fe8c12 | Athina Bekakou | findAll: function(store, type, sinceToken) {
|
7594 | d0fe8c12 | Athina Bekakou | var url = type;
|
7595 | d0fe8c12 | Athina Bekakou | var query = { since: sinceToken };
|
7596 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7597 | d0fe8c12 | Athina Bekakou | jQuery.getJSON(url, query).then(function(data) {
|
7598 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7599 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7600 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7601 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7602 | d0fe8c12 | Athina Bekakou | });
|
7603 | d0fe8c12 | Athina Bekakou | });
|
7604 | d0fe8c12 | Athina Bekakou | }
|
7605 | d0fe8c12 | Athina Bekakou | });
|
7606 | d0fe8c12 | Athina Bekakou | ```
|
7607 | d0fe8c12 | Athina Bekakou | |
7608 | d0fe8c12 | Athina Bekakou | @private
|
7609 | d0fe8c12 | Athina Bekakou | @method findAll
|
7610 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7611 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
7612 | d0fe8c12 | Athina Bekakou | @param {String} sinceToken
|
7613 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7614 | d0fe8c12 | Athina Bekakou | */
|
7615 | d0fe8c12 | Athina Bekakou | findAll: null, |
7616 | d0fe8c12 | Athina Bekakou | |
7617 | d0fe8c12 | Athina Bekakou | /**
|
7618 | d0fe8c12 | Athina Bekakou | This method is called when you call `find` on the store with a
|
7619 | d0fe8c12 | Athina Bekakou | query object as the second parameter (i.e. `store.find('person', {
|
7620 | d0fe8c12 | Athina Bekakou | page: 1 })`).
|
7621 | d0fe8c12 | Athina Bekakou | |
7622 | d0fe8c12 | Athina Bekakou | Example
|
7623 | d0fe8c12 | Athina Bekakou | |
7624 | d0fe8c12 | Athina Bekakou | ```javascript
|
7625 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7626 | d0fe8c12 | Athina Bekakou | findQuery: function(store, type, query) {
|
7627 | d0fe8c12 | Athina Bekakou | var url = type;
|
7628 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7629 | d0fe8c12 | Athina Bekakou | jQuery.getJSON(url, query).then(function(data) {
|
7630 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7631 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7632 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7633 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7634 | d0fe8c12 | Athina Bekakou | });
|
7635 | d0fe8c12 | Athina Bekakou | });
|
7636 | d0fe8c12 | Athina Bekakou | }
|
7637 | d0fe8c12 | Athina Bekakou | });
|
7638 | d0fe8c12 | Athina Bekakou | ```
|
7639 | d0fe8c12 | Athina Bekakou | |
7640 | d0fe8c12 | Athina Bekakou | @private
|
7641 | d0fe8c12 | Athina Bekakou | @method findQuery
|
7642 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7643 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
7644 | d0fe8c12 | Athina Bekakou | @param {Object} query
|
7645 | d0fe8c12 | Athina Bekakou | @param {DS.AdapterPopulatedRecordArray} recordArray
|
7646 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7647 | d0fe8c12 | Athina Bekakou | */
|
7648 | d0fe8c12 | Athina Bekakou | findQuery: null, |
7649 | d0fe8c12 | Athina Bekakou | |
7650 | d0fe8c12 | Athina Bekakou | /**
|
7651 | d0fe8c12 | Athina Bekakou | If the globally unique IDs for your records should be generated on the client,
|
7652 | d0fe8c12 | Athina Bekakou | implement the `generateIdForRecord()` method. This method will be invoked
|
7653 | d0fe8c12 | Athina Bekakou | each time you create a new record, and the value returned from it will be
|
7654 | d0fe8c12 | Athina Bekakou | assigned to the record's `primaryKey`.
|
7655 | d0fe8c12 | Athina Bekakou | |
7656 | d0fe8c12 | Athina Bekakou | Most traditional REST-like HTTP APIs will not use this method. Instead, the ID
|
7657 | d0fe8c12 | Athina Bekakou | of the record will be set by the server, and your adapter will update the store
|
7658 | d0fe8c12 | Athina Bekakou | with the new ID when it calls `didCreateRecord()`. Only implement this method if
|
7659 | d0fe8c12 | Athina Bekakou | you intend to generate record IDs on the client-side.
|
7660 | d0fe8c12 | Athina Bekakou | |
7661 | d0fe8c12 | Athina Bekakou | The `generateIdForRecord()` method will be invoked with the requesting store as
|
7662 | d0fe8c12 | Athina Bekakou | the first parameter and the newly created record as the second parameter:
|
7663 | d0fe8c12 | Athina Bekakou | |
7664 | d0fe8c12 | Athina Bekakou | ```javascript
|
7665 | d0fe8c12 | Athina Bekakou | generateIdForRecord: function(store, record) {
|
7666 | d0fe8c12 | Athina Bekakou | var uuid = App.generateUUIDWithStatisticallyLowOddsOfCollision();
|
7667 | d0fe8c12 | Athina Bekakou | return uuid;
|
7668 | d0fe8c12 | Athina Bekakou | }
|
7669 | d0fe8c12 | Athina Bekakou | ```
|
7670 | d0fe8c12 | Athina Bekakou | |
7671 | d0fe8c12 | Athina Bekakou | @method generateIdForRecord
|
7672 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7673 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7674 | d0fe8c12 | Athina Bekakou | @return {String|Number} id
|
7675 | d0fe8c12 | Athina Bekakou | */
|
7676 | d0fe8c12 | Athina Bekakou | generateIdForRecord: null, |
7677 | d0fe8c12 | Athina Bekakou | |
7678 | d0fe8c12 | Athina Bekakou | /**
|
7679 | d0fe8c12 | Athina Bekakou | Proxies to the serializer's `serialize` method.
|
7680 | d0fe8c12 | Athina Bekakou | |
7681 | d0fe8c12 | Athina Bekakou | Example
|
7682 | d0fe8c12 | Athina Bekakou | |
7683 | d0fe8c12 | Athina Bekakou | ```javascript
|
7684 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7685 | d0fe8c12 | Athina Bekakou | createRecord: function(store, type, record) {
|
7686 | d0fe8c12 | Athina Bekakou | var data = this.serialize(record, { includeId: true });
|
7687 | d0fe8c12 | Athina Bekakou | var url = type;
|
7688 | d0fe8c12 | Athina Bekakou | |
7689 | d0fe8c12 | Athina Bekakou | // ...
|
7690 | d0fe8c12 | Athina Bekakou | }
|
7691 | d0fe8c12 | Athina Bekakou | });
|
7692 | d0fe8c12 | Athina Bekakou | ```
|
7693 | d0fe8c12 | Athina Bekakou | |
7694 | d0fe8c12 | Athina Bekakou | @method serialize
|
7695 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7696 | d0fe8c12 | Athina Bekakou | @param {Object} options
|
7697 | d0fe8c12 | Athina Bekakou | @return {Object} serialized record
|
7698 | d0fe8c12 | Athina Bekakou | */
|
7699 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) { |
7700 | d0fe8c12 | Athina Bekakou | return get(record, 'store').serializerFor(record.constructor.typeKey).serialize(record, options); |
7701 | d0fe8c12 | Athina Bekakou | }, |
7702 | d0fe8c12 | Athina Bekakou | |
7703 | d0fe8c12 | Athina Bekakou | /**
|
7704 | d0fe8c12 | Athina Bekakou | Implement this method in a subclass to handle the creation of
|
7705 | d0fe8c12 | Athina Bekakou | new records.
|
7706 | d0fe8c12 | Athina Bekakou | |
7707 | d0fe8c12 | Athina Bekakou | Serializes the record and send it to the server.
|
7708 | d0fe8c12 | Athina Bekakou | |
7709 | d0fe8c12 | Athina Bekakou | Example
|
7710 | d0fe8c12 | Athina Bekakou | |
7711 | d0fe8c12 | Athina Bekakou | ```javascript
|
7712 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7713 | d0fe8c12 | Athina Bekakou | createRecord: function(store, type, record) {
|
7714 | d0fe8c12 | Athina Bekakou | var data = this.serialize(record, { includeId: true });
|
7715 | d0fe8c12 | Athina Bekakou | var url = type;
|
7716 | d0fe8c12 | Athina Bekakou | |
7717 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7718 | d0fe8c12 | Athina Bekakou | jQuery.ajax({
|
7719 | d0fe8c12 | Athina Bekakou | type: 'POST',
|
7720 | d0fe8c12 | Athina Bekakou | url: url,
|
7721 | d0fe8c12 | Athina Bekakou | dataType: 'json',
|
7722 | d0fe8c12 | Athina Bekakou | data: data
|
7723 | d0fe8c12 | Athina Bekakou | }).then(function(data) {
|
7724 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7725 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7726 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7727 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7728 | d0fe8c12 | Athina Bekakou | });
|
7729 | d0fe8c12 | Athina Bekakou | });
|
7730 | d0fe8c12 | Athina Bekakou | }
|
7731 | d0fe8c12 | Athina Bekakou | });
|
7732 | d0fe8c12 | Athina Bekakou | ```
|
7733 | d0fe8c12 | Athina Bekakou | |
7734 | d0fe8c12 | Athina Bekakou | @method createRecord
|
7735 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7736 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type the DS.Model class of the record
|
7737 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7738 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7739 | d0fe8c12 | Athina Bekakou | */
|
7740 | d0fe8c12 | Athina Bekakou | createRecord: Ember.required(Function),
|
7741 | d0fe8c12 | Athina Bekakou | |
7742 | d0fe8c12 | Athina Bekakou | /**
|
7743 | d0fe8c12 | Athina Bekakou | Implement this method in a subclass to handle the updating of
|
7744 | d0fe8c12 | Athina Bekakou | a record.
|
7745 | d0fe8c12 | Athina Bekakou | |
7746 | d0fe8c12 | Athina Bekakou | Serializes the record update and send it to the server.
|
7747 | d0fe8c12 | Athina Bekakou | |
7748 | d0fe8c12 | Athina Bekakou | Example
|
7749 | d0fe8c12 | Athina Bekakou | |
7750 | d0fe8c12 | Athina Bekakou | ```javascript
|
7751 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7752 | d0fe8c12 | Athina Bekakou | updateRecord: function(store, type, record) {
|
7753 | d0fe8c12 | Athina Bekakou | var data = this.serialize(record, { includeId: true });
|
7754 | d0fe8c12 | Athina Bekakou | var id = record.get('id');
|
7755 | d0fe8c12 | Athina Bekakou | var url = [type, id].join('/');
|
7756 | d0fe8c12 | Athina Bekakou | |
7757 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7758 | d0fe8c12 | Athina Bekakou | jQuery.ajax({
|
7759 | d0fe8c12 | Athina Bekakou | type: 'PUT',
|
7760 | d0fe8c12 | Athina Bekakou | url: url,
|
7761 | d0fe8c12 | Athina Bekakou | dataType: 'json',
|
7762 | d0fe8c12 | Athina Bekakou | data: data
|
7763 | d0fe8c12 | Athina Bekakou | }).then(function(data) {
|
7764 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7765 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7766 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7767 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7768 | d0fe8c12 | Athina Bekakou | });
|
7769 | d0fe8c12 | Athina Bekakou | });
|
7770 | d0fe8c12 | Athina Bekakou | }
|
7771 | d0fe8c12 | Athina Bekakou | });
|
7772 | d0fe8c12 | Athina Bekakou | ```
|
7773 | d0fe8c12 | Athina Bekakou | |
7774 | d0fe8c12 | Athina Bekakou | @method updateRecord
|
7775 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7776 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type the DS.Model class of the record
|
7777 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7778 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7779 | d0fe8c12 | Athina Bekakou | */
|
7780 | d0fe8c12 | Athina Bekakou | updateRecord: Ember.required(Function),
|
7781 | d0fe8c12 | Athina Bekakou | |
7782 | d0fe8c12 | Athina Bekakou | /**
|
7783 | d0fe8c12 | Athina Bekakou | Implement this method in a subclass to handle the deletion of
|
7784 | d0fe8c12 | Athina Bekakou | a record.
|
7785 | d0fe8c12 | Athina Bekakou | |
7786 | d0fe8c12 | Athina Bekakou | Sends a delete request for the record to the server.
|
7787 | d0fe8c12 | Athina Bekakou | |
7788 | d0fe8c12 | Athina Bekakou | Example
|
7789 | d0fe8c12 | Athina Bekakou | |
7790 | d0fe8c12 | Athina Bekakou | ```javascript
|
7791 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7792 | d0fe8c12 | Athina Bekakou | deleteRecord: function(store, type, record) {
|
7793 | d0fe8c12 | Athina Bekakou | var data = this.serialize(record, { includeId: true });
|
7794 | d0fe8c12 | Athina Bekakou | var id = record.get('id');
|
7795 | d0fe8c12 | Athina Bekakou | var url = [type, id].join('/');
|
7796 | d0fe8c12 | Athina Bekakou | |
7797 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7798 | d0fe8c12 | Athina Bekakou | jQuery.ajax({
|
7799 | d0fe8c12 | Athina Bekakou | type: 'DELETE',
|
7800 | d0fe8c12 | Athina Bekakou | url: url,
|
7801 | d0fe8c12 | Athina Bekakou | dataType: 'json',
|
7802 | d0fe8c12 | Athina Bekakou | data: data
|
7803 | d0fe8c12 | Athina Bekakou | }).then(function(data) {
|
7804 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7805 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7806 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7807 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7808 | d0fe8c12 | Athina Bekakou | });
|
7809 | d0fe8c12 | Athina Bekakou | });
|
7810 | d0fe8c12 | Athina Bekakou | }
|
7811 | d0fe8c12 | Athina Bekakou | });
|
7812 | d0fe8c12 | Athina Bekakou | ```
|
7813 | d0fe8c12 | Athina Bekakou | |
7814 | d0fe8c12 | Athina Bekakou | @method deleteRecord
|
7815 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7816 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type the DS.Model class of the record
|
7817 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7818 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7819 | d0fe8c12 | Athina Bekakou | */
|
7820 | d0fe8c12 | Athina Bekakou | deleteRecord: Ember.required(Function),
|
7821 | d0fe8c12 | Athina Bekakou | |
7822 | d0fe8c12 | Athina Bekakou | /**
|
7823 | d0fe8c12 | Athina Bekakou | Find multiple records at once.
|
7824 | d0fe8c12 | Athina Bekakou | |
7825 | d0fe8c12 | Athina Bekakou | By default, it loops over the provided ids and calls `find` on each.
|
7826 | d0fe8c12 | Athina Bekakou | May be overwritten to improve performance and reduce the number of
|
7827 | d0fe8c12 | Athina Bekakou | server requests.
|
7828 | d0fe8c12 | Athina Bekakou | |
7829 | d0fe8c12 | Athina Bekakou | Example
|
7830 | d0fe8c12 | Athina Bekakou | |
7831 | d0fe8c12 | Athina Bekakou | ```javascript
|
7832 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.Adapter.extend({
|
7833 | d0fe8c12 | Athina Bekakou | findMany: function(store, type, ids) {
|
7834 | d0fe8c12 | Athina Bekakou | var url = type;
|
7835 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) {
|
7836 | d0fe8c12 | Athina Bekakou | jQuery.getJSON(url, {ids: ids}).then(function(data) {
|
7837 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, data);
|
7838 | d0fe8c12 | Athina Bekakou | }, function(jqXHR) {
|
7839 | d0fe8c12 | Athina Bekakou | jqXHR.then = null; // tame jQuery's ill mannered promises
|
7840 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, jqXHR);
|
7841 | d0fe8c12 | Athina Bekakou | });
|
7842 | d0fe8c12 | Athina Bekakou | });
|
7843 | d0fe8c12 | Athina Bekakou | }
|
7844 | d0fe8c12 | Athina Bekakou | });
|
7845 | d0fe8c12 | Athina Bekakou | ```
|
7846 | d0fe8c12 | Athina Bekakou | |
7847 | d0fe8c12 | Athina Bekakou | @method findMany
|
7848 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7849 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type the DS.Model class of the records
|
7850 | d0fe8c12 | Athina Bekakou | @param {Array} ids
|
7851 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
7852 | d0fe8c12 | Athina Bekakou | */
|
7853 | d0fe8c12 | Athina Bekakou | findMany: function(store, type, ids) { |
7854 | d0fe8c12 | Athina Bekakou | var promises = map.call(ids, function(id) { |
7855 | d0fe8c12 | Athina Bekakou | return this.find(store, type, id); |
7856 | d0fe8c12 | Athina Bekakou | }, this);
|
7857 | d0fe8c12 | Athina Bekakou | |
7858 | d0fe8c12 | Athina Bekakou | return Ember.RSVP.all(promises);
|
7859 | d0fe8c12 | Athina Bekakou | } |
7860 | d0fe8c12 | Athina Bekakou | }); |
7861 | d0fe8c12 | Athina Bekakou | |
7862 | d0fe8c12 | Athina Bekakou | })(); |
7863 | d0fe8c12 | Athina Bekakou | |
7864 | d0fe8c12 | Athina Bekakou | |
7865 | d0fe8c12 | Athina Bekakou | |
7866 | d0fe8c12 | Athina Bekakou | (function() {
|
7867 | d0fe8c12 | Athina Bekakou | /**
|
7868 | d0fe8c12 | Athina Bekakou | @module ember-data
|
7869 | d0fe8c12 | Athina Bekakou | */
|
7870 | d0fe8c12 | Athina Bekakou | |
7871 | d0fe8c12 | Athina Bekakou | var get = Ember.get, fmt = Ember.String.fmt,
|
7872 | d0fe8c12 | Athina Bekakou | indexOf = Ember.EnumerableUtils.indexOf; |
7873 | d0fe8c12 | Athina Bekakou | |
7874 | d0fe8c12 | Athina Bekakou | var counter = 0; |
7875 | d0fe8c12 | Athina Bekakou | |
7876 | d0fe8c12 | Athina Bekakou | /**
|
7877 | d0fe8c12 | Athina Bekakou | `DS.FixtureAdapter` is an adapter that loads records from memory.
|
7878 | d0fe8c12 | Athina Bekakou | Its primarily used for development and testing. You can also use
|
7879 | d0fe8c12 | Athina Bekakou | `DS.FixtureAdapter` while working on the API but are not ready to
|
7880 | d0fe8c12 | Athina Bekakou | integrate yet. It is a fully functioning adapter. All CRUD methods
|
7881 | d0fe8c12 | Athina Bekakou | are implemented. You can also implement query logic that a remote
|
7882 | d0fe8c12 | Athina Bekakou | system would do. Its possible to do develop your entire application
|
7883 | d0fe8c12 | Athina Bekakou | with `DS.FixtureAdapter`.
|
7884 | d0fe8c12 | Athina Bekakou | |
7885 | d0fe8c12 | Athina Bekakou | For information on how to use the `FixtureAdapter` in your
|
7886 | d0fe8c12 | Athina Bekakou | application please see the [FixtureAdapter
|
7887 | d0fe8c12 | Athina Bekakou | guide](/guides/models/the-fixture-adapter/).
|
7888 | d0fe8c12 | Athina Bekakou | |
7889 | d0fe8c12 | Athina Bekakou | @class FixtureAdapter
|
7890 | d0fe8c12 | Athina Bekakou | @namespace DS
|
7891 | d0fe8c12 | Athina Bekakou | @extends DS.Adapter
|
7892 | d0fe8c12 | Athina Bekakou | */
|
7893 | d0fe8c12 | Athina Bekakou | DS.FixtureAdapter = DS.Adapter.extend({ |
7894 | d0fe8c12 | Athina Bekakou | // by default, fixtures are already in normalized form
|
7895 | d0fe8c12 | Athina Bekakou | serializer: null, |
7896 | d0fe8c12 | Athina Bekakou | |
7897 | d0fe8c12 | Athina Bekakou | /**
|
7898 | d0fe8c12 | Athina Bekakou | If `simulateRemoteResponse` is `true` the `FixtureAdapter` will
|
7899 | d0fe8c12 | Athina Bekakou | wait a number of milliseconds before resolving promises with the
|
7900 | d0fe8c12 | Athina Bekakou | fixture values. The wait time can be configured via the `latency`
|
7901 | d0fe8c12 | Athina Bekakou | property.
|
7902 | d0fe8c12 | Athina Bekakou | |
7903 | d0fe8c12 | Athina Bekakou | @property simulateRemoteResponse
|
7904 | d0fe8c12 | Athina Bekakou | @type {Boolean}
|
7905 | d0fe8c12 | Athina Bekakou | @default true
|
7906 | d0fe8c12 | Athina Bekakou | */
|
7907 | d0fe8c12 | Athina Bekakou | simulateRemoteResponse: true, |
7908 | d0fe8c12 | Athina Bekakou | |
7909 | d0fe8c12 | Athina Bekakou | /**
|
7910 | d0fe8c12 | Athina Bekakou | By default the `FixtureAdapter` will simulate a wait of the
|
7911 | d0fe8c12 | Athina Bekakou | `latency` milliseconds before resolving promises with the fixture
|
7912 | d0fe8c12 | Athina Bekakou | values. This behavior can be turned off via the
|
7913 | d0fe8c12 | Athina Bekakou | `simulateRemoteResponse` property.
|
7914 | d0fe8c12 | Athina Bekakou | |
7915 | d0fe8c12 | Athina Bekakou | @property latency
|
7916 | d0fe8c12 | Athina Bekakou | @type {Number}
|
7917 | d0fe8c12 | Athina Bekakou | @default 50
|
7918 | d0fe8c12 | Athina Bekakou | */
|
7919 | d0fe8c12 | Athina Bekakou | latency: 50, |
7920 | d0fe8c12 | Athina Bekakou | |
7921 | d0fe8c12 | Athina Bekakou | /**
|
7922 | d0fe8c12 | Athina Bekakou | Implement this method in order to provide data associated with a type
|
7923 | d0fe8c12 | Athina Bekakou | |
7924 | d0fe8c12 | Athina Bekakou | @method fixturesForType
|
7925 | d0fe8c12 | Athina Bekakou | @param {Subclass of DS.Model} type
|
7926 | d0fe8c12 | Athina Bekakou | @return {Array}
|
7927 | d0fe8c12 | Athina Bekakou | */
|
7928 | d0fe8c12 | Athina Bekakou | fixturesForType: function(type) { |
7929 | d0fe8c12 | Athina Bekakou | if (type.FIXTURES) {
|
7930 | d0fe8c12 | Athina Bekakou | var fixtures = Ember.A(type.FIXTURES);
|
7931 | d0fe8c12 | Athina Bekakou | return fixtures.map(function(fixture){ |
7932 | d0fe8c12 | Athina Bekakou | var fixtureIdType = typeof fixture.id; |
7933 | d0fe8c12 | Athina Bekakou | if(fixtureIdType !== "number" && fixtureIdType !== "string"){ |
7934 | d0fe8c12 | Athina Bekakou | throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture])); |
7935 | d0fe8c12 | Athina Bekakou | } |
7936 | d0fe8c12 | Athina Bekakou | fixture.id = fixture.id + '';
|
7937 | d0fe8c12 | Athina Bekakou | return fixture;
|
7938 | d0fe8c12 | Athina Bekakou | }); |
7939 | d0fe8c12 | Athina Bekakou | } |
7940 | d0fe8c12 | Athina Bekakou | return null; |
7941 | d0fe8c12 | Athina Bekakou | }, |
7942 | d0fe8c12 | Athina Bekakou | |
7943 | d0fe8c12 | Athina Bekakou | /**
|
7944 | d0fe8c12 | Athina Bekakou | Implement this method in order to query fixtures data
|
7945 | d0fe8c12 | Athina Bekakou | |
7946 | d0fe8c12 | Athina Bekakou | @method queryFixtures
|
7947 | d0fe8c12 | Athina Bekakou | @param {Array} fixture
|
7948 | d0fe8c12 | Athina Bekakou | @param {Object} query
|
7949 | d0fe8c12 | Athina Bekakou | @param {Subclass of DS.Model} type
|
7950 | d0fe8c12 | Athina Bekakou | @return {Promise|Array}
|
7951 | d0fe8c12 | Athina Bekakou | */
|
7952 | d0fe8c12 | Athina Bekakou | queryFixtures: function(fixtures, query, type) { |
7953 | d0fe8c12 | Athina Bekakou | Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
|
7954 | d0fe8c12 | Athina Bekakou | }, |
7955 | d0fe8c12 | Athina Bekakou | |
7956 | d0fe8c12 | Athina Bekakou | /**
|
7957 | d0fe8c12 | Athina Bekakou | @method updateFixtures
|
7958 | d0fe8c12 | Athina Bekakou | @param {Subclass of DS.Model} type
|
7959 | d0fe8c12 | Athina Bekakou | @param {Array} fixture
|
7960 | d0fe8c12 | Athina Bekakou | */
|
7961 | d0fe8c12 | Athina Bekakou | updateFixtures: function(type, fixture) { |
7962 | d0fe8c12 | Athina Bekakou | if(!type.FIXTURES) {
|
7963 | d0fe8c12 | Athina Bekakou | type.FIXTURES = []; |
7964 | d0fe8c12 | Athina Bekakou | } |
7965 | d0fe8c12 | Athina Bekakou | |
7966 | d0fe8c12 | Athina Bekakou | var fixtures = type.FIXTURES;
|
7967 | d0fe8c12 | Athina Bekakou | |
7968 | d0fe8c12 | Athina Bekakou | this.deleteLoadedFixture(type, fixture);
|
7969 | d0fe8c12 | Athina Bekakou | |
7970 | d0fe8c12 | Athina Bekakou | fixtures.push(fixture); |
7971 | d0fe8c12 | Athina Bekakou | }, |
7972 | d0fe8c12 | Athina Bekakou | |
7973 | d0fe8c12 | Athina Bekakou | /**
|
7974 | d0fe8c12 | Athina Bekakou | Implement this method in order to provide json for CRUD methods
|
7975 | d0fe8c12 | Athina Bekakou | |
7976 | d0fe8c12 | Athina Bekakou | @method mockJSON
|
7977 | d0fe8c12 | Athina Bekakou | @param {Subclass of DS.Model} type
|
7978 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7979 | d0fe8c12 | Athina Bekakou | */
|
7980 | d0fe8c12 | Athina Bekakou | mockJSON: function(store, type, record) { |
7981 | d0fe8c12 | Athina Bekakou | return store.serializerFor(type).serialize(record, { includeId: true }); |
7982 | d0fe8c12 | Athina Bekakou | }, |
7983 | d0fe8c12 | Athina Bekakou | |
7984 | d0fe8c12 | Athina Bekakou | /**
|
7985 | d0fe8c12 | Athina Bekakou | @method generateIdForRecord
|
7986 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7987 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
7988 | d0fe8c12 | Athina Bekakou | @return {String} id
|
7989 | d0fe8c12 | Athina Bekakou | */
|
7990 | d0fe8c12 | Athina Bekakou | generateIdForRecord: function(store) { |
7991 | d0fe8c12 | Athina Bekakou | return "fixture-" + counter++; |
7992 | d0fe8c12 | Athina Bekakou | }, |
7993 | d0fe8c12 | Athina Bekakou | |
7994 | d0fe8c12 | Athina Bekakou | /**
|
7995 | d0fe8c12 | Athina Bekakou | @method find
|
7996 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
7997 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
7998 | d0fe8c12 | Athina Bekakou | @param {String} id
|
7999 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8000 | d0fe8c12 | Athina Bekakou | */
|
8001 | d0fe8c12 | Athina Bekakou | find: function(store, type, id) { |
8002 | d0fe8c12 | Athina Bekakou | var fixtures = this.fixturesForType(type), |
8003 | d0fe8c12 | Athina Bekakou | fixture; |
8004 | d0fe8c12 | Athina Bekakou | |
8005 | d0fe8c12 | Athina Bekakou | Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
8006 | d0fe8c12 | Athina Bekakou | |
8007 | d0fe8c12 | Athina Bekakou | if (fixtures) {
|
8008 | d0fe8c12 | Athina Bekakou | fixture = Ember.A(fixtures).findProperty('id', id);
|
8009 | d0fe8c12 | Athina Bekakou | } |
8010 | d0fe8c12 | Athina Bekakou | |
8011 | d0fe8c12 | Athina Bekakou | if (fixture) {
|
8012 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8013 | d0fe8c12 | Athina Bekakou | return fixture;
|
8014 | d0fe8c12 | Athina Bekakou | }, this);
|
8015 | d0fe8c12 | Athina Bekakou | } |
8016 | d0fe8c12 | Athina Bekakou | }, |
8017 | d0fe8c12 | Athina Bekakou | |
8018 | d0fe8c12 | Athina Bekakou | /**
|
8019 | d0fe8c12 | Athina Bekakou | @method findMany
|
8020 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8021 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8022 | d0fe8c12 | Athina Bekakou | @param {Array} ids
|
8023 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8024 | d0fe8c12 | Athina Bekakou | */
|
8025 | d0fe8c12 | Athina Bekakou | findMany: function(store, type, ids) { |
8026 | d0fe8c12 | Athina Bekakou | var fixtures = this.fixturesForType(type); |
8027 | d0fe8c12 | Athina Bekakou | |
8028 | d0fe8c12 | Athina Bekakou | Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
8029 | d0fe8c12 | Athina Bekakou | |
8030 | d0fe8c12 | Athina Bekakou | if (fixtures) {
|
8031 | d0fe8c12 | Athina Bekakou | fixtures = fixtures.filter(function(item) {
|
8032 | d0fe8c12 | Athina Bekakou | return indexOf(ids, item.id) !== -1; |
8033 | d0fe8c12 | Athina Bekakou | }); |
8034 | d0fe8c12 | Athina Bekakou | } |
8035 | d0fe8c12 | Athina Bekakou | |
8036 | d0fe8c12 | Athina Bekakou | if (fixtures) {
|
8037 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8038 | d0fe8c12 | Athina Bekakou | return fixtures;
|
8039 | d0fe8c12 | Athina Bekakou | }, this);
|
8040 | d0fe8c12 | Athina Bekakou | } |
8041 | d0fe8c12 | Athina Bekakou | }, |
8042 | d0fe8c12 | Athina Bekakou | |
8043 | d0fe8c12 | Athina Bekakou | /**
|
8044 | d0fe8c12 | Athina Bekakou | @private
|
8045 | d0fe8c12 | Athina Bekakou | @method findAll
|
8046 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8047 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8048 | d0fe8c12 | Athina Bekakou | @param {String} sinceToken
|
8049 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8050 | d0fe8c12 | Athina Bekakou | */
|
8051 | d0fe8c12 | Athina Bekakou | findAll: function(store, type) { |
8052 | d0fe8c12 | Athina Bekakou | var fixtures = this.fixturesForType(type); |
8053 | d0fe8c12 | Athina Bekakou | |
8054 | d0fe8c12 | Athina Bekakou | Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
8055 | d0fe8c12 | Athina Bekakou | |
8056 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8057 | d0fe8c12 | Athina Bekakou | return fixtures;
|
8058 | d0fe8c12 | Athina Bekakou | }, this);
|
8059 | d0fe8c12 | Athina Bekakou | }, |
8060 | d0fe8c12 | Athina Bekakou | |
8061 | d0fe8c12 | Athina Bekakou | /**
|
8062 | d0fe8c12 | Athina Bekakou | @private
|
8063 | d0fe8c12 | Athina Bekakou | @method findQuery
|
8064 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8065 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8066 | d0fe8c12 | Athina Bekakou | @param {Object} query
|
8067 | d0fe8c12 | Athina Bekakou | @param {DS.AdapterPopulatedRecordArray} recordArray
|
8068 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8069 | d0fe8c12 | Athina Bekakou | */
|
8070 | d0fe8c12 | Athina Bekakou | findQuery: function(store, type, query, array) { |
8071 | d0fe8c12 | Athina Bekakou | var fixtures = this.fixturesForType(type); |
8072 | d0fe8c12 | Athina Bekakou | |
8073 | d0fe8c12 | Athina Bekakou | Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures);
|
8074 | d0fe8c12 | Athina Bekakou | |
8075 | d0fe8c12 | Athina Bekakou | fixtures = this.queryFixtures(fixtures, query, type);
|
8076 | d0fe8c12 | Athina Bekakou | |
8077 | d0fe8c12 | Athina Bekakou | if (fixtures) {
|
8078 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8079 | d0fe8c12 | Athina Bekakou | return fixtures;
|
8080 | d0fe8c12 | Athina Bekakou | }, this);
|
8081 | d0fe8c12 | Athina Bekakou | } |
8082 | d0fe8c12 | Athina Bekakou | }, |
8083 | d0fe8c12 | Athina Bekakou | |
8084 | d0fe8c12 | Athina Bekakou | /**
|
8085 | d0fe8c12 | Athina Bekakou | @method createRecord
|
8086 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8087 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8088 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
8089 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8090 | d0fe8c12 | Athina Bekakou | */
|
8091 | d0fe8c12 | Athina Bekakou | createRecord: function(store, type, record) { |
8092 | d0fe8c12 | Athina Bekakou | var fixture = this.mockJSON(store, type, record); |
8093 | d0fe8c12 | Athina Bekakou | |
8094 | d0fe8c12 | Athina Bekakou | this.updateFixtures(type, fixture);
|
8095 | d0fe8c12 | Athina Bekakou | |
8096 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8097 | d0fe8c12 | Athina Bekakou | return fixture;
|
8098 | d0fe8c12 | Athina Bekakou | }, this);
|
8099 | d0fe8c12 | Athina Bekakou | }, |
8100 | d0fe8c12 | Athina Bekakou | |
8101 | d0fe8c12 | Athina Bekakou | /**
|
8102 | d0fe8c12 | Athina Bekakou | @method updateRecord
|
8103 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8104 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8105 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
8106 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8107 | d0fe8c12 | Athina Bekakou | */
|
8108 | d0fe8c12 | Athina Bekakou | updateRecord: function(store, type, record) { |
8109 | d0fe8c12 | Athina Bekakou | var fixture = this.mockJSON(store, type, record); |
8110 | d0fe8c12 | Athina Bekakou | |
8111 | d0fe8c12 | Athina Bekakou | this.updateFixtures(type, fixture);
|
8112 | d0fe8c12 | Athina Bekakou | |
8113 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8114 | d0fe8c12 | Athina Bekakou | return fixture;
|
8115 | d0fe8c12 | Athina Bekakou | }, this);
|
8116 | d0fe8c12 | Athina Bekakou | }, |
8117 | d0fe8c12 | Athina Bekakou | |
8118 | d0fe8c12 | Athina Bekakou | /**
|
8119 | d0fe8c12 | Athina Bekakou | @method deleteRecord
|
8120 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8121 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8122 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
8123 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
8124 | d0fe8c12 | Athina Bekakou | */
|
8125 | d0fe8c12 | Athina Bekakou | deleteRecord: function(store, type, record) { |
8126 | d0fe8c12 | Athina Bekakou | var fixture = this.mockJSON(store, type, record); |
8127 | d0fe8c12 | Athina Bekakou | |
8128 | d0fe8c12 | Athina Bekakou | this.deleteLoadedFixture(type, fixture);
|
8129 | d0fe8c12 | Athina Bekakou | |
8130 | d0fe8c12 | Athina Bekakou | return this.simulateRemoteCall(function() { |
8131 | d0fe8c12 | Athina Bekakou | // no payload in a deletion
|
8132 | d0fe8c12 | Athina Bekakou | return null; |
8133 | d0fe8c12 | Athina Bekakou | }); |
8134 | d0fe8c12 | Athina Bekakou | }, |
8135 | d0fe8c12 | Athina Bekakou | |
8136 | d0fe8c12 | Athina Bekakou | /*
|
8137 | d0fe8c12 | Athina Bekakou | @method deleteLoadedFixture
|
8138 | d0fe8c12 | Athina Bekakou | @private
|
8139 | d0fe8c12 | Athina Bekakou | @param type
|
8140 | d0fe8c12 | Athina Bekakou | @param record
|
8141 | d0fe8c12 | Athina Bekakou | */
|
8142 | d0fe8c12 | Athina Bekakou | deleteLoadedFixture: function(type, record) { |
8143 | d0fe8c12 | Athina Bekakou | var existingFixture = this.findExistingFixture(type, record); |
8144 | d0fe8c12 | Athina Bekakou | |
8145 | d0fe8c12 | Athina Bekakou | if(existingFixture) {
|
8146 | d0fe8c12 | Athina Bekakou | var index = indexOf(type.FIXTURES, existingFixture);
|
8147 | d0fe8c12 | Athina Bekakou | type.FIXTURES.splice(index, 1);
|
8148 | d0fe8c12 | Athina Bekakou | return true; |
8149 | d0fe8c12 | Athina Bekakou | } |
8150 | d0fe8c12 | Athina Bekakou | }, |
8151 | d0fe8c12 | Athina Bekakou | |
8152 | d0fe8c12 | Athina Bekakou | /*
|
8153 | d0fe8c12 | Athina Bekakou | @method findExistingFixture
|
8154 | d0fe8c12 | Athina Bekakou | @private
|
8155 | d0fe8c12 | Athina Bekakou | @param type
|
8156 | d0fe8c12 | Athina Bekakou | @param record
|
8157 | d0fe8c12 | Athina Bekakou | */
|
8158 | d0fe8c12 | Athina Bekakou | findExistingFixture: function(type, record) { |
8159 | d0fe8c12 | Athina Bekakou | var fixtures = this.fixturesForType(type); |
8160 | d0fe8c12 | Athina Bekakou | var id = get(record, 'id'); |
8161 | d0fe8c12 | Athina Bekakou | |
8162 | d0fe8c12 | Athina Bekakou | return this.findFixtureById(fixtures, id); |
8163 | d0fe8c12 | Athina Bekakou | }, |
8164 | d0fe8c12 | Athina Bekakou | |
8165 | d0fe8c12 | Athina Bekakou | /*
|
8166 | d0fe8c12 | Athina Bekakou | @method findFixtureById
|
8167 | d0fe8c12 | Athina Bekakou | @private
|
8168 | d0fe8c12 | Athina Bekakou | @param fixtures
|
8169 | d0fe8c12 | Athina Bekakou | @param id
|
8170 | d0fe8c12 | Athina Bekakou | */
|
8171 | d0fe8c12 | Athina Bekakou | findFixtureById: function(fixtures, id) { |
8172 | d0fe8c12 | Athina Bekakou | return Ember.A(fixtures).find(function(r) { |
8173 | d0fe8c12 | Athina Bekakou | if(''+get(r, 'id') === ''+id) { |
8174 | d0fe8c12 | Athina Bekakou | return true; |
8175 | d0fe8c12 | Athina Bekakou | } else {
|
8176 | d0fe8c12 | Athina Bekakou | return false; |
8177 | d0fe8c12 | Athina Bekakou | } |
8178 | d0fe8c12 | Athina Bekakou | }); |
8179 | d0fe8c12 | Athina Bekakou | }, |
8180 | d0fe8c12 | Athina Bekakou | |
8181 | d0fe8c12 | Athina Bekakou | /*
|
8182 | d0fe8c12 | Athina Bekakou | @method simulateRemoteCall
|
8183 | d0fe8c12 | Athina Bekakou | @private
|
8184 | d0fe8c12 | Athina Bekakou | @param callback
|
8185 | d0fe8c12 | Athina Bekakou | @param context
|
8186 | d0fe8c12 | Athina Bekakou | */
|
8187 | d0fe8c12 | Athina Bekakou | simulateRemoteCall: function(callback, context) { |
8188 | d0fe8c12 | Athina Bekakou | var adapter = this; |
8189 | d0fe8c12 | Athina Bekakou | |
8190 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve) { |
8191 | d0fe8c12 | Athina Bekakou | if (get(adapter, 'simulateRemoteResponse')) { |
8192 | d0fe8c12 | Athina Bekakou | // Schedule with setTimeout
|
8193 | d0fe8c12 | Athina Bekakou | Ember.run.later(function() {
|
8194 | d0fe8c12 | Athina Bekakou | resolve(callback.call(context)); |
8195 | d0fe8c12 | Athina Bekakou | }, get(adapter, 'latency'));
|
8196 | d0fe8c12 | Athina Bekakou | } else {
|
8197 | d0fe8c12 | Athina Bekakou | // Asynchronous, but at the of the runloop with zero latency
|
8198 | d0fe8c12 | Athina Bekakou | Ember.run.schedule('actions', null, function() { |
8199 | d0fe8c12 | Athina Bekakou | resolve(callback.call(context)); |
8200 | d0fe8c12 | Athina Bekakou | }); |
8201 | d0fe8c12 | Athina Bekakou | } |
8202 | d0fe8c12 | Athina Bekakou | }, "DS: FixtureAdapter#simulateRemoteCall");
|
8203 | d0fe8c12 | Athina Bekakou | } |
8204 | d0fe8c12 | Athina Bekakou | }); |
8205 | d0fe8c12 | Athina Bekakou | |
8206 | d0fe8c12 | Athina Bekakou | })(); |
8207 | d0fe8c12 | Athina Bekakou | |
8208 | d0fe8c12 | Athina Bekakou | |
8209 | d0fe8c12 | Athina Bekakou | |
8210 | d0fe8c12 | Athina Bekakou | (function() {
|
8211 | d0fe8c12 | Athina Bekakou | /**
|
8212 | d0fe8c12 | Athina Bekakou | @module ember-data
|
8213 | d0fe8c12 | Athina Bekakou | */
|
8214 | d0fe8c12 | Athina Bekakou | |
8215 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
8216 | d0fe8c12 | Athina Bekakou | var forEach = Ember.ArrayPolyfills.forEach;
|
8217 | d0fe8c12 | Athina Bekakou | var map = Ember.ArrayPolyfills.map;
|
8218 | d0fe8c12 | Athina Bekakou | |
8219 | d0fe8c12 | Athina Bekakou | function coerceId(id) { |
8220 | d0fe8c12 | Athina Bekakou | return id == null ? null : id+''; |
8221 | d0fe8c12 | Athina Bekakou | } |
8222 | d0fe8c12 | Athina Bekakou | |
8223 | d0fe8c12 | Athina Bekakou | /**
|
8224 | d0fe8c12 | Athina Bekakou | Normally, applications will use the `RESTSerializer` by implementing
|
8225 | d0fe8c12 | Athina Bekakou | the `normalize` method and individual normalizations under
|
8226 | d0fe8c12 | Athina Bekakou | `normalizeHash`.
|
8227 | d0fe8c12 | Athina Bekakou | |
8228 | d0fe8c12 | Athina Bekakou | This allows you to do whatever kind of munging you need, and is
|
8229 | d0fe8c12 | Athina Bekakou | especially useful if your server is inconsistent and you need to
|
8230 | d0fe8c12 | Athina Bekakou | do munging differently for many different kinds of responses.
|
8231 | d0fe8c12 | Athina Bekakou | |
8232 | d0fe8c12 | Athina Bekakou | See the `normalize` documentation for more information.
|
8233 | d0fe8c12 | Athina Bekakou | |
8234 | d0fe8c12 | Athina Bekakou | ## Across the Board Normalization
|
8235 | d0fe8c12 | Athina Bekakou | |
8236 | d0fe8c12 | Athina Bekakou | There are also a number of hooks that you might find useful to defined
|
8237 | d0fe8c12 | Athina Bekakou | across-the-board rules for your payload. These rules will be useful
|
8238 | d0fe8c12 | Athina Bekakou | if your server is consistent, or if you're building an adapter for
|
8239 | d0fe8c12 | Athina Bekakou | an infrastructure service, like Parse, and want to encode service
|
8240 | d0fe8c12 | Athina Bekakou | conventions.
|
8241 | d0fe8c12 | Athina Bekakou | |
8242 | d0fe8c12 | Athina Bekakou | For example, if all of your keys are underscored and all-caps, but
|
8243 | d0fe8c12 | Athina Bekakou | otherwise consistent with the names you use in your models, you
|
8244 | d0fe8c12 | Athina Bekakou | can implement across-the-board rules for how to convert an attribute
|
8245 | d0fe8c12 | Athina Bekakou | name in your model to a key in your JSON.
|
8246 | d0fe8c12 | Athina Bekakou | |
8247 | d0fe8c12 | Athina Bekakou | ```js
|
8248 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.RESTSerializer.extend({
|
8249 | d0fe8c12 | Athina Bekakou | keyForAttribute: function(attr) {
|
8250 | d0fe8c12 | Athina Bekakou | return Ember.String.underscore(attr).toUpperCase();
|
8251 | d0fe8c12 | Athina Bekakou | }
|
8252 | d0fe8c12 | Athina Bekakou | });
|
8253 | d0fe8c12 | Athina Bekakou | ```
|
8254 | d0fe8c12 | Athina Bekakou | |
8255 | d0fe8c12 | Athina Bekakou | You can also implement `keyForRelationship`, which takes the name
|
8256 | d0fe8c12 | Athina Bekakou | of the relationship as the first parameter, and the kind of
|
8257 | d0fe8c12 | Athina Bekakou | relationship (`hasMany` or `belongsTo`) as the second parameter.
|
8258 | d0fe8c12 | Athina Bekakou | |
8259 | d0fe8c12 | Athina Bekakou | @class RESTSerializer
|
8260 | d0fe8c12 | Athina Bekakou | @namespace DS
|
8261 | d0fe8c12 | Athina Bekakou | @extends DS.JSONSerializer
|
8262 | d0fe8c12 | Athina Bekakou | */
|
8263 | d0fe8c12 | Athina Bekakou | DS.RESTSerializer = DS.JSONSerializer.extend({ |
8264 | d0fe8c12 | Athina Bekakou | /**
|
8265 | d0fe8c12 | Athina Bekakou | If you want to do normalizations specific to some part of the payload, you
|
8266 | d0fe8c12 | Athina Bekakou | can specify those under `normalizeHash`.
|
8267 | d0fe8c12 | Athina Bekakou | |
8268 | d0fe8c12 | Athina Bekakou | For example, given the following json where the the `IDs` under
|
8269 | d0fe8c12 | Athina Bekakou | `"comments"` are provided as `_id` instead of `id`.
|
8270 | d0fe8c12 | Athina Bekakou | |
8271 | d0fe8c12 | Athina Bekakou | ```javascript
|
8272 | d0fe8c12 | Athina Bekakou | {
|
8273 | d0fe8c12 | Athina Bekakou | "post": {
|
8274 | d0fe8c12 | Athina Bekakou | "id": 1,
|
8275 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
8276 | d0fe8c12 | Athina Bekakou | "comments": [ 1, 2 ]
|
8277 | d0fe8c12 | Athina Bekakou | },
|
8278 | d0fe8c12 | Athina Bekakou | "comments": [{
|
8279 | d0fe8c12 | Athina Bekakou | "_id": 1,
|
8280 | d0fe8c12 | Athina Bekakou | "body": "FIRST"
|
8281 | d0fe8c12 | Athina Bekakou | }, {
|
8282 | d0fe8c12 | Athina Bekakou | "_id": 2,
|
8283 | d0fe8c12 | Athina Bekakou | "body": "Rails is unagi"
|
8284 | d0fe8c12 | Athina Bekakou | }]
|
8285 | d0fe8c12 | Athina Bekakou | }
|
8286 | d0fe8c12 | Athina Bekakou | ```
|
8287 | d0fe8c12 | Athina Bekakou | |
8288 | d0fe8c12 | Athina Bekakou | You use `normalizeHash` to normalize just the comments:
|
8289 | d0fe8c12 | Athina Bekakou | |
8290 | d0fe8c12 | Athina Bekakou | ```javascript
|
8291 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.RESTSerializer.extend({
|
8292 | d0fe8c12 | Athina Bekakou | normalizeHash: {
|
8293 | d0fe8c12 | Athina Bekakou | comments: function(hash) {
|
8294 | d0fe8c12 | Athina Bekakou | hash.id = hash._id;
|
8295 | d0fe8c12 | Athina Bekakou | delete hash._id;
|
8296 | d0fe8c12 | Athina Bekakou | return hash;
|
8297 | d0fe8c12 | Athina Bekakou | }
|
8298 | d0fe8c12 | Athina Bekakou | }
|
8299 | d0fe8c12 | Athina Bekakou | });
|
8300 | d0fe8c12 | Athina Bekakou | ```
|
8301 | d0fe8c12 | Athina Bekakou | |
8302 | d0fe8c12 | Athina Bekakou | The key under `normalizeHash` is usually just the original key
|
8303 | d0fe8c12 | Athina Bekakou | that was in the original payload. However, key names will be
|
8304 | d0fe8c12 | Athina Bekakou | impacted by any modifications done in the `normalizePayload`
|
8305 | d0fe8c12 | Athina Bekakou | method. The `DS.RESTSerializer`'s default implemention makes no
|
8306 | d0fe8c12 | Athina Bekakou | changes to the payload keys.
|
8307 | d0fe8c12 | Athina Bekakou | |
8308 | d0fe8c12 | Athina Bekakou | @property normalizeHash
|
8309 | d0fe8c12 | Athina Bekakou | @type {Object}
|
8310 | d0fe8c12 | Athina Bekakou | @default undefined
|
8311 | d0fe8c12 | Athina Bekakou | */
|
8312 | d0fe8c12 | Athina Bekakou | |
8313 | d0fe8c12 | Athina Bekakou | /**
|
8314 | d0fe8c12 | Athina Bekakou | Normalizes a part of the JSON payload returned by
|
8315 | d0fe8c12 | Athina Bekakou | the server. You should override this method, munge the hash
|
8316 | d0fe8c12 | Athina Bekakou | and call super if you have generic normalization to do.
|
8317 | d0fe8c12 | Athina Bekakou | |
8318 | d0fe8c12 | Athina Bekakou | It takes the type of the record that is being normalized
|
8319 | d0fe8c12 | Athina Bekakou | (as a DS.Model class), the property where the hash was
|
8320 | d0fe8c12 | Athina Bekakou | originally found, and the hash to normalize.
|
8321 | d0fe8c12 | Athina Bekakou | |
8322 | d0fe8c12 | Athina Bekakou | For example, if you have a payload that looks like this:
|
8323 | d0fe8c12 | Athina Bekakou | |
8324 | d0fe8c12 | Athina Bekakou | ```js
|
8325 | d0fe8c12 | Athina Bekakou | {
|
8326 | d0fe8c12 | Athina Bekakou | "post": {
|
8327 | d0fe8c12 | Athina Bekakou | "id": 1,
|
8328 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
8329 | d0fe8c12 | Athina Bekakou | "comments": [ 1, 2 ]
|
8330 | d0fe8c12 | Athina Bekakou | },
|
8331 | d0fe8c12 | Athina Bekakou | "comments": [{
|
8332 | d0fe8c12 | Athina Bekakou | "id": 1,
|
8333 | d0fe8c12 | Athina Bekakou | "body": "FIRST"
|
8334 | d0fe8c12 | Athina Bekakou | }, {
|
8335 | d0fe8c12 | Athina Bekakou | "id": 2,
|
8336 | d0fe8c12 | Athina Bekakou | "body": "Rails is unagi"
|
8337 | d0fe8c12 | Athina Bekakou | }]
|
8338 | d0fe8c12 | Athina Bekakou | }
|
8339 | d0fe8c12 | Athina Bekakou | ```
|
8340 | d0fe8c12 | Athina Bekakou | |
8341 | d0fe8c12 | Athina Bekakou | The `normalize` method will be called three times:
|
8342 | d0fe8c12 | Athina Bekakou | |
8343 | d0fe8c12 | Athina Bekakou | * With `App.Post`, `"posts"` and `{ id: 1, title: "Rails is omakase", ... }`
|
8344 | d0fe8c12 | Athina Bekakou | * With `App.Comment`, `"comments"` and `{ id: 1, body: "FIRST" }`
|
8345 | d0fe8c12 | Athina Bekakou | * With `App.Comment`, `"comments"` and `{ id: 2, body: "Rails is unagi" }`
|
8346 | d0fe8c12 | Athina Bekakou | |
8347 | d0fe8c12 | Athina Bekakou | You can use this method, for example, to normalize underscored keys to camelized
|
8348 | d0fe8c12 | Athina Bekakou | or other general-purpose normalizations.
|
8349 | d0fe8c12 | Athina Bekakou | |
8350 | d0fe8c12 | Athina Bekakou | If you want to do normalizations specific to some part of the payload, you
|
8351 | d0fe8c12 | Athina Bekakou | can specify those under `normalizeHash`.
|
8352 | d0fe8c12 | Athina Bekakou | |
8353 | d0fe8c12 | Athina Bekakou | For example, if the `IDs` under `"comments"` are provided as `_id` instead of
|
8354 | d0fe8c12 | Athina Bekakou | `id`, you can specify how to normalize just the comments:
|
8355 | d0fe8c12 | Athina Bekakou | |
8356 | d0fe8c12 | Athina Bekakou | ```js
|
8357 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.RESTSerializer.extend({
|
8358 | d0fe8c12 | Athina Bekakou | normalizeHash: {
|
8359 | d0fe8c12 | Athina Bekakou | comments: function(hash) {
|
8360 | d0fe8c12 | Athina Bekakou | hash.id = hash._id;
|
8361 | d0fe8c12 | Athina Bekakou | delete hash._id;
|
8362 | d0fe8c12 | Athina Bekakou | return hash;
|
8363 | d0fe8c12 | Athina Bekakou | }
|
8364 | d0fe8c12 | Athina Bekakou | }
|
8365 | d0fe8c12 | Athina Bekakou | });
|
8366 | d0fe8c12 | Athina Bekakou | ```
|
8367 | d0fe8c12 | Athina Bekakou | |
8368 | d0fe8c12 | Athina Bekakou | The key under `normalizeHash` is just the original key that was in the original
|
8369 | d0fe8c12 | Athina Bekakou | payload.
|
8370 | d0fe8c12 | Athina Bekakou | |
8371 | d0fe8c12 | Athina Bekakou | @method normalize
|
8372 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8373 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
8374 | d0fe8c12 | Athina Bekakou | @param {String} prop
|
8375 | d0fe8c12 | Athina Bekakou | @returns {Object}
|
8376 | d0fe8c12 | Athina Bekakou | */
|
8377 | d0fe8c12 | Athina Bekakou | normalize: function(type, hash, prop) { |
8378 | d0fe8c12 | Athina Bekakou | this.normalizeId(hash);
|
8379 | d0fe8c12 | Athina Bekakou | this.normalizeAttributes(type, hash);
|
8380 | d0fe8c12 | Athina Bekakou | this.normalizeRelationships(type, hash);
|
8381 | d0fe8c12 | Athina Bekakou | |
8382 | d0fe8c12 | Athina Bekakou | this.normalizeUsingDeclaredMapping(type, hash);
|
8383 | d0fe8c12 | Athina Bekakou | |
8384 | d0fe8c12 | Athina Bekakou | if (this.normalizeHash && this.normalizeHash[prop]) { |
8385 | d0fe8c12 | Athina Bekakou | this.normalizeHash[prop](hash);
|
8386 | d0fe8c12 | Athina Bekakou | } |
8387 | d0fe8c12 | Athina Bekakou | |
8388 | d0fe8c12 | Athina Bekakou | return this._super(type, hash, prop); |
8389 | d0fe8c12 | Athina Bekakou | }, |
8390 | d0fe8c12 | Athina Bekakou | |
8391 | d0fe8c12 | Athina Bekakou | /**
|
8392 | d0fe8c12 | Athina Bekakou | You can use this method to normalize all payloads, regardless of whether they
|
8393 | d0fe8c12 | Athina Bekakou | represent single records or an array.
|
8394 | d0fe8c12 | Athina Bekakou | |
8395 | d0fe8c12 | Athina Bekakou | For example, you might want to remove some extraneous data from the payload:
|
8396 | d0fe8c12 | Athina Bekakou | |
8397 | d0fe8c12 | Athina Bekakou | ```js
|
8398 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.RESTSerializer.extend({
|
8399 | d0fe8c12 | Athina Bekakou | normalizePayload: function(type, payload) {
|
8400 | d0fe8c12 | Athina Bekakou | delete payload.version;
|
8401 | d0fe8c12 | Athina Bekakou | delete payload.status;
|
8402 | d0fe8c12 | Athina Bekakou | return payload;
|
8403 | d0fe8c12 | Athina Bekakou | }
|
8404 | d0fe8c12 | Athina Bekakou | });
|
8405 | d0fe8c12 | Athina Bekakou | ```
|
8406 | d0fe8c12 | Athina Bekakou | |
8407 | d0fe8c12 | Athina Bekakou | @method normalizePayload
|
8408 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8409 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
8410 | d0fe8c12 | Athina Bekakou | @returns {Object} the normalized payload
|
8411 | d0fe8c12 | Athina Bekakou | */
|
8412 | d0fe8c12 | Athina Bekakou | normalizePayload: function(type, payload) { |
8413 | d0fe8c12 | Athina Bekakou | return payload;
|
8414 | d0fe8c12 | Athina Bekakou | }, |
8415 | d0fe8c12 | Athina Bekakou | |
8416 | d0fe8c12 | Athina Bekakou | /**
|
8417 | d0fe8c12 | Athina Bekakou | @method normalizeId
|
8418 | d0fe8c12 | Athina Bekakou | @private
|
8419 | d0fe8c12 | Athina Bekakou | */
|
8420 | d0fe8c12 | Athina Bekakou | normalizeId: function(hash) { |
8421 | d0fe8c12 | Athina Bekakou | var primaryKey = get(this, 'primaryKey'); |
8422 | d0fe8c12 | Athina Bekakou | |
8423 | d0fe8c12 | Athina Bekakou | if (primaryKey === 'id') { return; } |
8424 | d0fe8c12 | Athina Bekakou | |
8425 | d0fe8c12 | Athina Bekakou | hash.id = hash[primaryKey]; |
8426 | d0fe8c12 | Athina Bekakou | delete hash[primaryKey];
|
8427 | d0fe8c12 | Athina Bekakou | }, |
8428 | d0fe8c12 | Athina Bekakou | |
8429 | d0fe8c12 | Athina Bekakou | /**
|
8430 | d0fe8c12 | Athina Bekakou | @method normalizeUsingDeclaredMapping
|
8431 | d0fe8c12 | Athina Bekakou | @private
|
8432 | d0fe8c12 | Athina Bekakou | */
|
8433 | d0fe8c12 | Athina Bekakou | normalizeUsingDeclaredMapping: function(type, hash) { |
8434 | d0fe8c12 | Athina Bekakou | var attrs = get(this, 'attrs'), payloadKey, key; |
8435 | d0fe8c12 | Athina Bekakou | |
8436 | d0fe8c12 | Athina Bekakou | if (attrs) {
|
8437 | d0fe8c12 | Athina Bekakou | for (key in attrs) { |
8438 | d0fe8c12 | Athina Bekakou | payloadKey = attrs[key]; |
8439 | d0fe8c12 | Athina Bekakou | if (payloadKey && payloadKey.key) {
|
8440 | d0fe8c12 | Athina Bekakou | payloadKey = payloadKey.key; |
8441 | d0fe8c12 | Athina Bekakou | } |
8442 | d0fe8c12 | Athina Bekakou | if (typeof payloadKey === 'string') { |
8443 | d0fe8c12 | Athina Bekakou | hash[key] = hash[payloadKey]; |
8444 | d0fe8c12 | Athina Bekakou | delete hash[payloadKey];
|
8445 | d0fe8c12 | Athina Bekakou | } |
8446 | d0fe8c12 | Athina Bekakou | } |
8447 | d0fe8c12 | Athina Bekakou | } |
8448 | d0fe8c12 | Athina Bekakou | }, |
8449 | d0fe8c12 | Athina Bekakou | |
8450 | d0fe8c12 | Athina Bekakou | /**
|
8451 | d0fe8c12 | Athina Bekakou | @method normalizeAttributes
|
8452 | d0fe8c12 | Athina Bekakou | @private
|
8453 | d0fe8c12 | Athina Bekakou | */
|
8454 | d0fe8c12 | Athina Bekakou | normalizeAttributes: function(type, hash) { |
8455 | d0fe8c12 | Athina Bekakou | var payloadKey, key;
|
8456 | d0fe8c12 | Athina Bekakou | |
8457 | d0fe8c12 | Athina Bekakou | if (this.keyForAttribute) { |
8458 | d0fe8c12 | Athina Bekakou | type.eachAttribute(function(key) {
|
8459 | d0fe8c12 | Athina Bekakou | payloadKey = this.keyForAttribute(key);
|
8460 | d0fe8c12 | Athina Bekakou | if (key === payloadKey) { return; } |
8461 | d0fe8c12 | Athina Bekakou | |
8462 | d0fe8c12 | Athina Bekakou | hash[key] = hash[payloadKey]; |
8463 | d0fe8c12 | Athina Bekakou | delete hash[payloadKey];
|
8464 | d0fe8c12 | Athina Bekakou | }, this);
|
8465 | d0fe8c12 | Athina Bekakou | } |
8466 | d0fe8c12 | Athina Bekakou | }, |
8467 | d0fe8c12 | Athina Bekakou | |
8468 | d0fe8c12 | Athina Bekakou | /**
|
8469 | d0fe8c12 | Athina Bekakou | @method normalizeRelationships
|
8470 | d0fe8c12 | Athina Bekakou | @private
|
8471 | d0fe8c12 | Athina Bekakou | */
|
8472 | d0fe8c12 | Athina Bekakou | normalizeRelationships: function(type, hash) { |
8473 | d0fe8c12 | Athina Bekakou | var payloadKey, key;
|
8474 | d0fe8c12 | Athina Bekakou | |
8475 | d0fe8c12 | Athina Bekakou | if (this.keyForRelationship) { |
8476 | d0fe8c12 | Athina Bekakou | type.eachRelationship(function(key, relationship) {
|
8477 | d0fe8c12 | Athina Bekakou | payloadKey = this.keyForRelationship(key, relationship.kind);
|
8478 | d0fe8c12 | Athina Bekakou | if (key === payloadKey) { return; } |
8479 | d0fe8c12 | Athina Bekakou | |
8480 | d0fe8c12 | Athina Bekakou | hash[key] = hash[payloadKey]; |
8481 | d0fe8c12 | Athina Bekakou | delete hash[payloadKey];
|
8482 | d0fe8c12 | Athina Bekakou | }, this);
|
8483 | d0fe8c12 | Athina Bekakou | } |
8484 | d0fe8c12 | Athina Bekakou | }, |
8485 | d0fe8c12 | Athina Bekakou | |
8486 | d0fe8c12 | Athina Bekakou | /**
|
8487 | d0fe8c12 | Athina Bekakou | Called when the server has returned a payload representing
|
8488 | d0fe8c12 | Athina Bekakou | a single record, such as in response to a `find` or `save`.
|
8489 | d0fe8c12 | Athina Bekakou | |
8490 | d0fe8c12 | Athina Bekakou | It is your opportunity to clean up the server's response into the normalized
|
8491 | d0fe8c12 | Athina Bekakou | form expected by Ember Data.
|
8492 | d0fe8c12 | Athina Bekakou | |
8493 | d0fe8c12 | Athina Bekakou | If you want, you can just restructure the top-level of your payload, and
|
8494 | d0fe8c12 | Athina Bekakou | do more fine-grained normalization in the `normalize` method.
|
8495 | d0fe8c12 | Athina Bekakou | |
8496 | d0fe8c12 | Athina Bekakou | For example, if you have a payload like this in response to a request for
|
8497 | d0fe8c12 | Athina Bekakou | post 1:
|
8498 | d0fe8c12 | Athina Bekakou | |
8499 | d0fe8c12 | Athina Bekakou | ```js
|
8500 | d0fe8c12 | Athina Bekakou | {
|
8501 | d0fe8c12 | Athina Bekakou | "id": 1,
|
8502 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
8503 | d0fe8c12 | Athina Bekakou | |
8504 | d0fe8c12 | Athina Bekakou | "_embedded": {
|
8505 | d0fe8c12 | Athina Bekakou | "comment": [{
|
8506 | d0fe8c12 | Athina Bekakou | "_id": 1,
|
8507 | d0fe8c12 | Athina Bekakou | "comment_title": "FIRST"
|
8508 | d0fe8c12 | Athina Bekakou | }, {
|
8509 | d0fe8c12 | Athina Bekakou | "_id": 2,
|
8510 | d0fe8c12 | Athina Bekakou | "comment_title": "Rails is unagi"
|
8511 | d0fe8c12 | Athina Bekakou | }]
|
8512 | d0fe8c12 | Athina Bekakou | }
|
8513 | d0fe8c12 | Athina Bekakou | }
|
8514 | d0fe8c12 | Athina Bekakou | ```
|
8515 | d0fe8c12 | Athina Bekakou | |
8516 | d0fe8c12 | Athina Bekakou | You could implement a serializer that looks like this to get your payload
|
8517 | d0fe8c12 | Athina Bekakou | into shape:
|
8518 | d0fe8c12 | Athina Bekakou | |
8519 | d0fe8c12 | Athina Bekakou | ```js
|
8520 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.RESTSerializer.extend({
|
8521 | d0fe8c12 | Athina Bekakou | // First, restructure the top-level so it's organized by type
|
8522 | d0fe8c12 | Athina Bekakou | extractSingle: function(store, type, payload, id, requestType) {
|
8523 | d0fe8c12 | Athina Bekakou | var comments = payload._embedded.comment;
|
8524 | d0fe8c12 | Athina Bekakou | delete payload._embedded;
|
8525 | d0fe8c12 | Athina Bekakou | |
8526 | d0fe8c12 | Athina Bekakou | payload = { comments: comments, post: payload };
|
8527 | d0fe8c12 | Athina Bekakou | return this._super(store, type, payload, id, requestType);
|
8528 | d0fe8c12 | Athina Bekakou | },
|
8529 | d0fe8c12 | Athina Bekakou | |
8530 | d0fe8c12 | Athina Bekakou | normalizeHash: {
|
8531 | d0fe8c12 | Athina Bekakou | // Next, normalize individual comments, which (after `extract`)
|
8532 | d0fe8c12 | Athina Bekakou | // are now located under `comments`
|
8533 | d0fe8c12 | Athina Bekakou | comments: function(hash) {
|
8534 | d0fe8c12 | Athina Bekakou | hash.id = hash._id;
|
8535 | d0fe8c12 | Athina Bekakou | hash.title = hash.comment_title;
|
8536 | d0fe8c12 | Athina Bekakou | delete hash._id;
|
8537 | d0fe8c12 | Athina Bekakou | delete hash.comment_title;
|
8538 | d0fe8c12 | Athina Bekakou | return hash;
|
8539 | d0fe8c12 | Athina Bekakou | }
|
8540 | d0fe8c12 | Athina Bekakou | }
|
8541 | d0fe8c12 | Athina Bekakou | })
|
8542 | d0fe8c12 | Athina Bekakou | ```
|
8543 | d0fe8c12 | Athina Bekakou | |
8544 | d0fe8c12 | Athina Bekakou | When you call super from your own implementation of `extractSingle`, the
|
8545 | d0fe8c12 | Athina Bekakou | built-in implementation will find the primary record in your normalized
|
8546 | d0fe8c12 | Athina Bekakou | payload and push the remaining records into the store.
|
8547 | d0fe8c12 | Athina Bekakou | |
8548 | d0fe8c12 | Athina Bekakou | The primary record is the single hash found under `post` or the first
|
8549 | d0fe8c12 | Athina Bekakou | element of the `posts` array.
|
8550 | d0fe8c12 | Athina Bekakou | |
8551 | d0fe8c12 | Athina Bekakou | The primary record has special meaning when the record is being created
|
8552 | d0fe8c12 | Athina Bekakou | for the first time or updated (`createRecord` or `updateRecord`). In
|
8553 | d0fe8c12 | Athina Bekakou | particular, it will update the properties of the record that was saved.
|
8554 | d0fe8c12 | Athina Bekakou | |
8555 | d0fe8c12 | Athina Bekakou | @method extractSingle
|
8556 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8557 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8558 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
8559 | d0fe8c12 | Athina Bekakou | @param {String} id
|
8560 | d0fe8c12 | Athina Bekakou | @param {'find'|'createRecord'|'updateRecord'|'deleteRecord'} requestType
|
8561 | d0fe8c12 | Athina Bekakou | @returns {Object} the primary response to the original request
|
8562 | d0fe8c12 | Athina Bekakou | */
|
8563 | d0fe8c12 | Athina Bekakou | extractSingle: function(store, primaryType, payload, recordId, requestType) { |
8564 | d0fe8c12 | Athina Bekakou | payload = this.normalizePayload(primaryType, payload);
|
8565 | d0fe8c12 | Athina Bekakou | |
8566 | d0fe8c12 | Athina Bekakou | var primaryTypeName = primaryType.typeKey,
|
8567 | d0fe8c12 | Athina Bekakou | primaryRecord; |
8568 | d0fe8c12 | Athina Bekakou | |
8569 | d0fe8c12 | Athina Bekakou | for (var prop in payload) { |
8570 | d0fe8c12 | Athina Bekakou | var typeName = this.typeForRoot(prop), |
8571 | d0fe8c12 | Athina Bekakou | isPrimary = typeName === primaryTypeName; |
8572 | d0fe8c12 | Athina Bekakou | |
8573 | d0fe8c12 | Athina Bekakou | // legacy support for singular resources
|
8574 | d0fe8c12 | Athina Bekakou | if (isPrimary && Ember.typeOf(payload[prop]) !== "array" ) { |
8575 | d0fe8c12 | Athina Bekakou | primaryRecord = this.normalize(primaryType, payload[prop], prop);
|
8576 | d0fe8c12 | Athina Bekakou | continue;
|
8577 | d0fe8c12 | Athina Bekakou | } |
8578 | d0fe8c12 | Athina Bekakou | |
8579 | d0fe8c12 | Athina Bekakou | var type = store.modelFor(typeName);
|
8580 | d0fe8c12 | Athina Bekakou | |
8581 | d0fe8c12 | Athina Bekakou | /*jshint loopfunc:true*/
|
8582 | d0fe8c12 | Athina Bekakou | forEach.call(payload[prop], function(hash) {
|
8583 | d0fe8c12 | Athina Bekakou | var typeName = this.typeForRoot(prop), |
8584 | d0fe8c12 | Athina Bekakou | type = store.modelFor(typeName), |
8585 | d0fe8c12 | Athina Bekakou | typeSerializer = store.serializerFor(type); |
8586 | d0fe8c12 | Athina Bekakou | |
8587 | d0fe8c12 | Athina Bekakou | hash = typeSerializer.normalize(type, hash, prop); |
8588 | d0fe8c12 | Athina Bekakou | |
8589 | d0fe8c12 | Athina Bekakou | var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord,
|
8590 | d0fe8c12 | Athina Bekakou | isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId; |
8591 | d0fe8c12 | Athina Bekakou | |
8592 | d0fe8c12 | Athina Bekakou | // find the primary record.
|
8593 | d0fe8c12 | Athina Bekakou | //
|
8594 | d0fe8c12 | Athina Bekakou | // It's either:
|
8595 | d0fe8c12 | Athina Bekakou | // * the record with the same ID as the original request
|
8596 | d0fe8c12 | Athina Bekakou | // * in the case of a newly created record that didn't have an ID, the first
|
8597 | d0fe8c12 | Athina Bekakou | // record in the Array
|
8598 | d0fe8c12 | Athina Bekakou | if (isFirstCreatedRecord || isUpdatedRecord) {
|
8599 | d0fe8c12 | Athina Bekakou | primaryRecord = hash; |
8600 | d0fe8c12 | Athina Bekakou | } else {
|
8601 | d0fe8c12 | Athina Bekakou | store.push(typeName, hash); |
8602 | d0fe8c12 | Athina Bekakou | } |
8603 | d0fe8c12 | Athina Bekakou | }, this);
|
8604 | d0fe8c12 | Athina Bekakou | } |
8605 | d0fe8c12 | Athina Bekakou | |
8606 | d0fe8c12 | Athina Bekakou | return primaryRecord;
|
8607 | d0fe8c12 | Athina Bekakou | }, |
8608 | d0fe8c12 | Athina Bekakou | |
8609 | d0fe8c12 | Athina Bekakou | /**
|
8610 | d0fe8c12 | Athina Bekakou | Called when the server has returned a payload representing
|
8611 | d0fe8c12 | Athina Bekakou | multiple records, such as in response to a `findAll` or `findQuery`.
|
8612 | d0fe8c12 | Athina Bekakou | |
8613 | d0fe8c12 | Athina Bekakou | It is your opportunity to clean up the server's response into the normalized
|
8614 | d0fe8c12 | Athina Bekakou | form expected by Ember Data.
|
8615 | d0fe8c12 | Athina Bekakou | |
8616 | d0fe8c12 | Athina Bekakou | If you want, you can just restructure the top-level of your payload, and
|
8617 | d0fe8c12 | Athina Bekakou | do more fine-grained normalization in the `normalize` method.
|
8618 | d0fe8c12 | Athina Bekakou | |
8619 | d0fe8c12 | Athina Bekakou | For example, if you have a payload like this in response to a request for
|
8620 | d0fe8c12 | Athina Bekakou | all posts:
|
8621 | d0fe8c12 | Athina Bekakou | |
8622 | d0fe8c12 | Athina Bekakou | ```js
|
8623 | d0fe8c12 | Athina Bekakou | {
|
8624 | d0fe8c12 | Athina Bekakou | "_embedded": {
|
8625 | d0fe8c12 | Athina Bekakou | "post": [{
|
8626 | d0fe8c12 | Athina Bekakou | "id": 1,
|
8627 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase"
|
8628 | d0fe8c12 | Athina Bekakou | }, {
|
8629 | d0fe8c12 | Athina Bekakou | "id": 2,
|
8630 | d0fe8c12 | Athina Bekakou | "title": "The Parley Letter"
|
8631 | d0fe8c12 | Athina Bekakou | }],
|
8632 | d0fe8c12 | Athina Bekakou | "comment": [{
|
8633 | d0fe8c12 | Athina Bekakou | "_id": 1,
|
8634 | d0fe8c12 | Athina Bekakou | "comment_title": "Rails is unagi"
|
8635 | d0fe8c12 | Athina Bekakou | "post_id": 1
|
8636 | d0fe8c12 | Athina Bekakou | }, {
|
8637 | d0fe8c12 | Athina Bekakou | "_id": 2,
|
8638 | d0fe8c12 | Athina Bekakou | "comment_title": "Don't tread on me",
|
8639 | d0fe8c12 | Athina Bekakou | "post_id": 2
|
8640 | d0fe8c12 | Athina Bekakou | }]
|
8641 | d0fe8c12 | Athina Bekakou | }
|
8642 | d0fe8c12 | Athina Bekakou | }
|
8643 | d0fe8c12 | Athina Bekakou | ```
|
8644 | d0fe8c12 | Athina Bekakou | |
8645 | d0fe8c12 | Athina Bekakou | You could implement a serializer that looks like this to get your payload
|
8646 | d0fe8c12 | Athina Bekakou | into shape:
|
8647 | d0fe8c12 | Athina Bekakou | |
8648 | d0fe8c12 | Athina Bekakou | ```js
|
8649 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.RESTSerializer.extend({
|
8650 | d0fe8c12 | Athina Bekakou | // First, restructure the top-level so it's organized by type
|
8651 | d0fe8c12 | Athina Bekakou | // and the comments are listed under a post's `comments` key.
|
8652 | d0fe8c12 | Athina Bekakou | extractArray: function(store, type, payload, id, requestType) {
|
8653 | d0fe8c12 | Athina Bekakou | var posts = payload._embedded.post;
|
8654 | d0fe8c12 | Athina Bekakou | var comments = [];
|
8655 | d0fe8c12 | Athina Bekakou | var postCache = {};
|
8656 | d0fe8c12 | Athina Bekakou | |
8657 | d0fe8c12 | Athina Bekakou | posts.forEach(function(post) {
|
8658 | d0fe8c12 | Athina Bekakou | post.comments = [];
|
8659 | d0fe8c12 | Athina Bekakou | postCache[post.id] = post;
|
8660 | d0fe8c12 | Athina Bekakou | });
|
8661 | d0fe8c12 | Athina Bekakou | |
8662 | d0fe8c12 | Athina Bekakou | payload._embedded.comment.forEach(function(comment) {
|
8663 | d0fe8c12 | Athina Bekakou | comments.push(comment);
|
8664 | d0fe8c12 | Athina Bekakou | postCache[comment.post_id].comments.push(comment);
|
8665 | d0fe8c12 | Athina Bekakou | delete comment.post_id;
|
8666 | d0fe8c12 | Athina Bekakou | }
|
8667 | d0fe8c12 | Athina Bekakou | |
8668 | d0fe8c12 | Athina Bekakou | payload = { comments: comments, posts: payload };
|
8669 | d0fe8c12 | Athina Bekakou | |
8670 | d0fe8c12 | Athina Bekakou | return this._super(store, type, payload, id, requestType);
|
8671 | d0fe8c12 | Athina Bekakou | },
|
8672 | d0fe8c12 | Athina Bekakou | |
8673 | d0fe8c12 | Athina Bekakou | normalizeHash: {
|
8674 | d0fe8c12 | Athina Bekakou | // Next, normalize individual comments, which (after `extract`)
|
8675 | d0fe8c12 | Athina Bekakou | // are now located under `comments`
|
8676 | d0fe8c12 | Athina Bekakou | comments: function(hash) {
|
8677 | d0fe8c12 | Athina Bekakou | hash.id = hash._id;
|
8678 | d0fe8c12 | Athina Bekakou | hash.title = hash.comment_title;
|
8679 | d0fe8c12 | Athina Bekakou | delete hash._id;
|
8680 | d0fe8c12 | Athina Bekakou | delete hash.comment_title;
|
8681 | d0fe8c12 | Athina Bekakou | return hash;
|
8682 | d0fe8c12 | Athina Bekakou | }
|
8683 | d0fe8c12 | Athina Bekakou | }
|
8684 | d0fe8c12 | Athina Bekakou | })
|
8685 | d0fe8c12 | Athina Bekakou | ```
|
8686 | d0fe8c12 | Athina Bekakou | |
8687 | d0fe8c12 | Athina Bekakou | When you call super from your own implementation of `extractArray`, the
|
8688 | d0fe8c12 | Athina Bekakou | built-in implementation will find the primary array in your normalized
|
8689 | d0fe8c12 | Athina Bekakou | payload and push the remaining records into the store.
|
8690 | d0fe8c12 | Athina Bekakou | |
8691 | d0fe8c12 | Athina Bekakou | The primary array is the array found under `posts`.
|
8692 | d0fe8c12 | Athina Bekakou | |
8693 | d0fe8c12 | Athina Bekakou | The primary record has special meaning when responding to `findQuery`
|
8694 | d0fe8c12 | Athina Bekakou | or `findHasMany`. In particular, the primary array will become the
|
8695 | d0fe8c12 | Athina Bekakou | list of records in the record array that kicked off the request.
|
8696 | d0fe8c12 | Athina Bekakou | |
8697 | d0fe8c12 | Athina Bekakou | If your primary array contains secondary (embedded) records of the same type,
|
8698 | d0fe8c12 | Athina Bekakou | you cannot place these into the primary array `posts`. Instead, place the
|
8699 | d0fe8c12 | Athina Bekakou | secondary items into an underscore prefixed property `_posts`, which will
|
8700 | d0fe8c12 | Athina Bekakou | push these items into the store and will not affect the resulting query.
|
8701 | d0fe8c12 | Athina Bekakou | |
8702 | d0fe8c12 | Athina Bekakou | @method extractArray
|
8703 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8704 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8705 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
8706 | d0fe8c12 | Athina Bekakou | @param {'findAll'|'findMany'|'findHasMany'|'findQuery'} requestType
|
8707 | d0fe8c12 | Athina Bekakou | @returns {Array} The primary array that was returned in response
|
8708 | d0fe8c12 | Athina Bekakou | to the original query.
|
8709 | d0fe8c12 | Athina Bekakou | */
|
8710 | d0fe8c12 | Athina Bekakou | extractArray: function(store, primaryType, payload) { |
8711 | d0fe8c12 | Athina Bekakou | payload = this.normalizePayload(primaryType, payload);
|
8712 | d0fe8c12 | Athina Bekakou | |
8713 | d0fe8c12 | Athina Bekakou | var primaryTypeName = primaryType.typeKey,
|
8714 | d0fe8c12 | Athina Bekakou | primaryArray; |
8715 | d0fe8c12 | Athina Bekakou | |
8716 | d0fe8c12 | Athina Bekakou | for (var prop in payload) { |
8717 | d0fe8c12 | Athina Bekakou | var typeKey = prop,
|
8718 | d0fe8c12 | Athina Bekakou | forcedSecondary = false;
|
8719 | d0fe8c12 | Athina Bekakou | |
8720 | d0fe8c12 | Athina Bekakou | if (prop.charAt(0) === '_') { |
8721 | d0fe8c12 | Athina Bekakou | forcedSecondary = true;
|
8722 | d0fe8c12 | Athina Bekakou | typeKey = prop.substr(1);
|
8723 | d0fe8c12 | Athina Bekakou | } |
8724 | d0fe8c12 | Athina Bekakou | |
8725 | d0fe8c12 | Athina Bekakou | var typeName = this.typeForRoot(typeKey), |
8726 | d0fe8c12 | Athina Bekakou | type = store.modelFor(typeName), |
8727 | d0fe8c12 | Athina Bekakou | typeSerializer = store.serializerFor(type), |
8728 | d0fe8c12 | Athina Bekakou | isPrimary = (!forcedSecondary && (typeName === primaryTypeName)); |
8729 | d0fe8c12 | Athina Bekakou | |
8730 | d0fe8c12 | Athina Bekakou | /*jshint loopfunc:true*/
|
8731 | d0fe8c12 | Athina Bekakou | var normalizedArray = map.call(payload[prop], function(hash) { |
8732 | d0fe8c12 | Athina Bekakou | return typeSerializer.normalize(type, hash, prop);
|
8733 | d0fe8c12 | Athina Bekakou | }, this);
|
8734 | d0fe8c12 | Athina Bekakou | |
8735 | d0fe8c12 | Athina Bekakou | if (isPrimary) {
|
8736 | d0fe8c12 | Athina Bekakou | primaryArray = normalizedArray; |
8737 | d0fe8c12 | Athina Bekakou | } else {
|
8738 | d0fe8c12 | Athina Bekakou | store.pushMany(typeName, normalizedArray); |
8739 | d0fe8c12 | Athina Bekakou | } |
8740 | d0fe8c12 | Athina Bekakou | } |
8741 | d0fe8c12 | Athina Bekakou | |
8742 | d0fe8c12 | Athina Bekakou | return primaryArray;
|
8743 | d0fe8c12 | Athina Bekakou | }, |
8744 | d0fe8c12 | Athina Bekakou | |
8745 | d0fe8c12 | Athina Bekakou | /**
|
8746 | d0fe8c12 | Athina Bekakou | This method allows you to push a payload containing top-level
|
8747 | d0fe8c12 | Athina Bekakou | collections of records organized per type.
|
8748 | d0fe8c12 | Athina Bekakou | |
8749 | d0fe8c12 | Athina Bekakou | ```js
|
8750 | d0fe8c12 | Athina Bekakou | {
|
8751 | d0fe8c12 | Athina Bekakou | "posts": [{
|
8752 | d0fe8c12 | Athina Bekakou | "id": "1",
|
8753 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
8754 | d0fe8c12 | Athina Bekakou | "author", "1",
|
8755 | d0fe8c12 | Athina Bekakou | "comments": [ "1" ]
|
8756 | d0fe8c12 | Athina Bekakou | }],
|
8757 | d0fe8c12 | Athina Bekakou | "comments": [{
|
8758 | d0fe8c12 | Athina Bekakou | "id": "1",
|
8759 | d0fe8c12 | Athina Bekakou | "body": "FIRST"
|
8760 | d0fe8c12 | Athina Bekakou | }],
|
8761 | d0fe8c12 | Athina Bekakou | "users": [{
|
8762 | d0fe8c12 | Athina Bekakou | "id": "1",
|
8763 | d0fe8c12 | Athina Bekakou | "name": "@d2h"
|
8764 | d0fe8c12 | Athina Bekakou | }]
|
8765 | d0fe8c12 | Athina Bekakou | }
|
8766 | d0fe8c12 | Athina Bekakou | ```
|
8767 | d0fe8c12 | Athina Bekakou | |
8768 | d0fe8c12 | Athina Bekakou | It will first normalize the payload, so you can use this to push
|
8769 | d0fe8c12 | Athina Bekakou | in data streaming in from your server structured the same way
|
8770 | d0fe8c12 | Athina Bekakou | that fetches and saves are structured.
|
8771 | d0fe8c12 | Athina Bekakou | |
8772 | d0fe8c12 | Athina Bekakou | @method pushPayload
|
8773 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
8774 | d0fe8c12 | Athina Bekakou | @param {Object} payload
|
8775 | d0fe8c12 | Athina Bekakou | */
|
8776 | d0fe8c12 | Athina Bekakou | pushPayload: function(store, payload) { |
8777 | d0fe8c12 | Athina Bekakou | payload = this.normalizePayload(null, payload); |
8778 | d0fe8c12 | Athina Bekakou | |
8779 | d0fe8c12 | Athina Bekakou | for (var prop in payload) { |
8780 | d0fe8c12 | Athina Bekakou | var typeName = this.typeForRoot(prop), |
8781 | d0fe8c12 | Athina Bekakou | type = store.modelFor(typeName); |
8782 | d0fe8c12 | Athina Bekakou | |
8783 | d0fe8c12 | Athina Bekakou | /*jshint loopfunc:true*/
|
8784 | d0fe8c12 | Athina Bekakou | var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) { |
8785 | d0fe8c12 | Athina Bekakou | return this.normalize(type, hash, prop); |
8786 | d0fe8c12 | Athina Bekakou | }, this);
|
8787 | d0fe8c12 | Athina Bekakou | |
8788 | d0fe8c12 | Athina Bekakou | store.pushMany(typeName, normalizedArray); |
8789 | d0fe8c12 | Athina Bekakou | } |
8790 | d0fe8c12 | Athina Bekakou | }, |
8791 | d0fe8c12 | Athina Bekakou | |
8792 | d0fe8c12 | Athina Bekakou | /**
|
8793 | d0fe8c12 | Athina Bekakou | You can use this method to normalize the JSON root keys returned
|
8794 | d0fe8c12 | Athina Bekakou | into the model type expected by your store.
|
8795 | d0fe8c12 | Athina Bekakou | |
8796 | d0fe8c12 | Athina Bekakou | For example, your server may return underscored root keys rather than
|
8797 | d0fe8c12 | Athina Bekakou | the expected camelcased versions.
|
8798 | d0fe8c12 | Athina Bekakou | |
8799 | d0fe8c12 | Athina Bekakou | ```js
|
8800 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.RESTSerializer.extend({
|
8801 | d0fe8c12 | Athina Bekakou | typeForRoot: function(root) {
|
8802 | d0fe8c12 | Athina Bekakou | var camelized = Ember.String.camelize(root);
|
8803 | d0fe8c12 | Athina Bekakou | return Ember.String.singularize(camelized);
|
8804 | d0fe8c12 | Athina Bekakou | }
|
8805 | d0fe8c12 | Athina Bekakou | });
|
8806 | d0fe8c12 | Athina Bekakou | ```
|
8807 | d0fe8c12 | Athina Bekakou | |
8808 | d0fe8c12 | Athina Bekakou | @method typeForRoot
|
8809 | d0fe8c12 | Athina Bekakou | @param {String} root
|
8810 | d0fe8c12 | Athina Bekakou | @returns {String} the model's typeKey
|
8811 | d0fe8c12 | Athina Bekakou | */
|
8812 | d0fe8c12 | Athina Bekakou | typeForRoot: function(root) { |
8813 | d0fe8c12 | Athina Bekakou | return Ember.String.singularize(root);
|
8814 | d0fe8c12 | Athina Bekakou | }, |
8815 | d0fe8c12 | Athina Bekakou | |
8816 | d0fe8c12 | Athina Bekakou | // SERIALIZE
|
8817 | d0fe8c12 | Athina Bekakou | |
8818 | d0fe8c12 | Athina Bekakou | /**
|
8819 | d0fe8c12 | Athina Bekakou | Called when a record is saved in order to convert the
|
8820 | d0fe8c12 | Athina Bekakou | record into JSON.
|
8821 | d0fe8c12 | Athina Bekakou | |
8822 | d0fe8c12 | Athina Bekakou | By default, it creates a JSON object with a key for
|
8823 | d0fe8c12 | Athina Bekakou | each attribute and belongsTo relationship.
|
8824 | d0fe8c12 | Athina Bekakou | |
8825 | d0fe8c12 | Athina Bekakou | For example, consider this model:
|
8826 | d0fe8c12 | Athina Bekakou | |
8827 | d0fe8c12 | Athina Bekakou | ```js
|
8828 | d0fe8c12 | Athina Bekakou | App.Comment = DS.Model.extend({
|
8829 | d0fe8c12 | Athina Bekakou | title: DS.attr(),
|
8830 | d0fe8c12 | Athina Bekakou | body: DS.attr(),
|
8831 | d0fe8c12 | Athina Bekakou | |
8832 | d0fe8c12 | Athina Bekakou | author: DS.belongsTo('user')
|
8833 | d0fe8c12 | Athina Bekakou | });
|
8834 | d0fe8c12 | Athina Bekakou | ```
|
8835 | d0fe8c12 | Athina Bekakou | |
8836 | d0fe8c12 | Athina Bekakou | The default serialization would create a JSON object like:
|
8837 | d0fe8c12 | Athina Bekakou | |
8838 | d0fe8c12 | Athina Bekakou | ```js
|
8839 | d0fe8c12 | Athina Bekakou | {
|
8840 | d0fe8c12 | Athina Bekakou | "title": "Rails is unagi",
|
8841 | d0fe8c12 | Athina Bekakou | "body": "Rails? Omakase? O_O",
|
8842 | d0fe8c12 | Athina Bekakou | "author": 12
|
8843 | d0fe8c12 | Athina Bekakou | }
|
8844 | d0fe8c12 | Athina Bekakou | ```
|
8845 | d0fe8c12 | Athina Bekakou | |
8846 | d0fe8c12 | Athina Bekakou | By default, attributes are passed through as-is, unless
|
8847 | d0fe8c12 | Athina Bekakou | you specified an attribute type (`DS.attr('date')`). If
|
8848 | d0fe8c12 | Athina Bekakou | you specify a transform, the JavaScript value will be
|
8849 | d0fe8c12 | Athina Bekakou | serialized when inserted into the JSON hash.
|
8850 | d0fe8c12 | Athina Bekakou | |
8851 | d0fe8c12 | Athina Bekakou | By default, belongs-to relationships are converted into
|
8852 | d0fe8c12 | Athina Bekakou | IDs when inserted into the JSON hash.
|
8853 | d0fe8c12 | Athina Bekakou | |
8854 | d0fe8c12 | Athina Bekakou | ## IDs
|
8855 | d0fe8c12 | Athina Bekakou | |
8856 | d0fe8c12 | Athina Bekakou | `serialize` takes an options hash with a single option:
|
8857 | d0fe8c12 | Athina Bekakou | `includeId`. If this option is `true`, `serialize` will,
|
8858 | d0fe8c12 | Athina Bekakou | by default include the ID in the JSON object it builds.
|
8859 | d0fe8c12 | Athina Bekakou | |
8860 | d0fe8c12 | Athina Bekakou | The adapter passes in `includeId: true` when serializing
|
8861 | d0fe8c12 | Athina Bekakou | a record for `createRecord`, but not for `updateRecord`.
|
8862 | d0fe8c12 | Athina Bekakou | |
8863 | d0fe8c12 | Athina Bekakou | ## Customization
|
8864 | d0fe8c12 | Athina Bekakou | |
8865 | d0fe8c12 | Athina Bekakou | Your server may expect a different JSON format than the
|
8866 | d0fe8c12 | Athina Bekakou | built-in serialization format.
|
8867 | d0fe8c12 | Athina Bekakou | |
8868 | d0fe8c12 | Athina Bekakou | In that case, you can implement `serialize` yourself and
|
8869 | d0fe8c12 | Athina Bekakou | return a JSON hash of your choosing.
|
8870 | d0fe8c12 | Athina Bekakou | |
8871 | d0fe8c12 | Athina Bekakou | ```js
|
8872 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.RESTSerializer.extend({
|
8873 | d0fe8c12 | Athina Bekakou | serialize: function(post, options) {
|
8874 | d0fe8c12 | Athina Bekakou | var json = {
|
8875 | d0fe8c12 | Athina Bekakou | POST_TTL: post.get('title'),
|
8876 | d0fe8c12 | Athina Bekakou | POST_BDY: post.get('body'),
|
8877 | d0fe8c12 | Athina Bekakou | POST_CMS: post.get('comments').mapProperty('id')
|
8878 | d0fe8c12 | Athina Bekakou | }
|
8879 | d0fe8c12 | Athina Bekakou | |
8880 | d0fe8c12 | Athina Bekakou | if (options.includeId) {
|
8881 | d0fe8c12 | Athina Bekakou | json.POST_ID_ = post.get('id');
|
8882 | d0fe8c12 | Athina Bekakou | }
|
8883 | d0fe8c12 | Athina Bekakou | |
8884 | d0fe8c12 | Athina Bekakou | return json;
|
8885 | d0fe8c12 | Athina Bekakou | }
|
8886 | d0fe8c12 | Athina Bekakou | });
|
8887 | d0fe8c12 | Athina Bekakou | ```
|
8888 | d0fe8c12 | Athina Bekakou | |
8889 | d0fe8c12 | Athina Bekakou | ## Customizing an App-Wide Serializer
|
8890 | d0fe8c12 | Athina Bekakou | |
8891 | d0fe8c12 | Athina Bekakou | If you want to define a serializer for your entire
|
8892 | d0fe8c12 | Athina Bekakou | application, you'll probably want to use `eachAttribute`
|
8893 | d0fe8c12 | Athina Bekakou | and `eachRelationship` on the record.
|
8894 | d0fe8c12 | Athina Bekakou | |
8895 | d0fe8c12 | Athina Bekakou | ```js
|
8896 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.RESTSerializer.extend({
|
8897 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) {
|
8898 | d0fe8c12 | Athina Bekakou | var json = {};
|
8899 | d0fe8c12 | Athina Bekakou | |
8900 | d0fe8c12 | Athina Bekakou | record.eachAttribute(function(name) {
|
8901 | d0fe8c12 | Athina Bekakou | json[serverAttributeName(name)] = record.get(name);
|
8902 | d0fe8c12 | Athina Bekakou | })
|
8903 | d0fe8c12 | Athina Bekakou | |
8904 | d0fe8c12 | Athina Bekakou | record.eachRelationship(function(name, relationship) {
|
8905 | d0fe8c12 | Athina Bekakou | if (relationship.kind === 'hasMany') {
|
8906 | d0fe8c12 | Athina Bekakou | json[serverHasManyName(name)] = record.get(name).mapBy('id');
|
8907 | d0fe8c12 | Athina Bekakou | }
|
8908 | d0fe8c12 | Athina Bekakou | });
|
8909 | d0fe8c12 | Athina Bekakou | |
8910 | d0fe8c12 | Athina Bekakou | if (options.includeId) {
|
8911 | d0fe8c12 | Athina Bekakou | json.ID_ = record.get('id');
|
8912 | d0fe8c12 | Athina Bekakou | }
|
8913 | d0fe8c12 | Athina Bekakou | |
8914 | d0fe8c12 | Athina Bekakou | return json;
|
8915 | d0fe8c12 | Athina Bekakou | }
|
8916 | d0fe8c12 | Athina Bekakou | });
|
8917 | d0fe8c12 | Athina Bekakou | |
8918 | d0fe8c12 | Athina Bekakou | function serverAttributeName(attribute) {
|
8919 | d0fe8c12 | Athina Bekakou | return attribute.underscore().toUpperCase();
|
8920 | d0fe8c12 | Athina Bekakou | }
|
8921 | d0fe8c12 | Athina Bekakou | |
8922 | d0fe8c12 | Athina Bekakou | function serverHasManyName(name) {
|
8923 | d0fe8c12 | Athina Bekakou | return serverAttributeName(name.singularize()) + "_IDS";
|
8924 | d0fe8c12 | Athina Bekakou | }
|
8925 | d0fe8c12 | Athina Bekakou | ```
|
8926 | d0fe8c12 | Athina Bekakou | |
8927 | d0fe8c12 | Athina Bekakou | This serializer will generate JSON that looks like this:
|
8928 | d0fe8c12 | Athina Bekakou | |
8929 | d0fe8c12 | Athina Bekakou | ```js
|
8930 | d0fe8c12 | Athina Bekakou | {
|
8931 | d0fe8c12 | Athina Bekakou | "TITLE": "Rails is omakase",
|
8932 | d0fe8c12 | Athina Bekakou | "BODY": "Yep. Omakase.",
|
8933 | d0fe8c12 | Athina Bekakou | "COMMENT_IDS": [ 1, 2, 3 ]
|
8934 | d0fe8c12 | Athina Bekakou | }
|
8935 | d0fe8c12 | Athina Bekakou | ```
|
8936 | d0fe8c12 | Athina Bekakou | |
8937 | d0fe8c12 | Athina Bekakou | ## Tweaking the Default JSON
|
8938 | d0fe8c12 | Athina Bekakou | |
8939 | d0fe8c12 | Athina Bekakou | If you just want to do some small tweaks on the default JSON,
|
8940 | d0fe8c12 | Athina Bekakou | you can call super first and make the tweaks on the returned
|
8941 | d0fe8c12 | Athina Bekakou | JSON.
|
8942 | d0fe8c12 | Athina Bekakou | |
8943 | d0fe8c12 | Athina Bekakou | ```js
|
8944 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.RESTSerializer.extend({
|
8945 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) {
|
8946 | d0fe8c12 | Athina Bekakou | var json = this._super(record, options);
|
8947 | d0fe8c12 | Athina Bekakou | |
8948 | d0fe8c12 | Athina Bekakou | json.subject = json.title;
|
8949 | d0fe8c12 | Athina Bekakou | delete json.title;
|
8950 | d0fe8c12 | Athina Bekakou | |
8951 | d0fe8c12 | Athina Bekakou | return json;
|
8952 | d0fe8c12 | Athina Bekakou | }
|
8953 | d0fe8c12 | Athina Bekakou | });
|
8954 | d0fe8c12 | Athina Bekakou | ```
|
8955 | d0fe8c12 | Athina Bekakou | |
8956 | d0fe8c12 | Athina Bekakou | @method serialize
|
8957 | d0fe8c12 | Athina Bekakou | @param record
|
8958 | d0fe8c12 | Athina Bekakou | @param options
|
8959 | d0fe8c12 | Athina Bekakou | */
|
8960 | d0fe8c12 | Athina Bekakou | serialize: function(record, options) { |
8961 | d0fe8c12 | Athina Bekakou | return this._super.apply(this, arguments); |
8962 | d0fe8c12 | Athina Bekakou | }, |
8963 | d0fe8c12 | Athina Bekakou | |
8964 | d0fe8c12 | Athina Bekakou | /**
|
8965 | d0fe8c12 | Athina Bekakou | You can use this method to customize the root keys serialized into the JSON.
|
8966 | d0fe8c12 | Athina Bekakou | By default the REST Serializer sends camelized root keys.
|
8967 | d0fe8c12 | Athina Bekakou | For example, your server may expect underscored root objects.
|
8968 | d0fe8c12 | Athina Bekakou | |
8969 | d0fe8c12 | Athina Bekakou | ```js
|
8970 | d0fe8c12 | Athina Bekakou | App.ApplicationSerializer = DS.RESTSerializer.extend({
|
8971 | d0fe8c12 | Athina Bekakou | serializeIntoHash: function(data, type, record, options) {
|
8972 | d0fe8c12 | Athina Bekakou | var root = Ember.String.decamelize(type.typeKey);
|
8973 | d0fe8c12 | Athina Bekakou | data[root] = this.serialize(record, options);
|
8974 | d0fe8c12 | Athina Bekakou | }
|
8975 | d0fe8c12 | Athina Bekakou | });
|
8976 | d0fe8c12 | Athina Bekakou | ```
|
8977 | d0fe8c12 | Athina Bekakou | |
8978 | d0fe8c12 | Athina Bekakou | @method serializeIntoHash
|
8979 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
8980 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
8981 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
8982 | d0fe8c12 | Athina Bekakou | @param {Object} options
|
8983 | d0fe8c12 | Athina Bekakou | */
|
8984 | d0fe8c12 | Athina Bekakou | serializeIntoHash: function(hash, type, record, options) { |
8985 | d0fe8c12 | Athina Bekakou | hash[type.typeKey] = this.serialize(record, options);
|
8986 | d0fe8c12 | Athina Bekakou | }, |
8987 | d0fe8c12 | Athina Bekakou | |
8988 | d0fe8c12 | Athina Bekakou | /**
|
8989 | d0fe8c12 | Athina Bekakou | You can use this method to customize how polymorphic objects are serialized.
|
8990 | d0fe8c12 | Athina Bekakou | By default the JSON Serializer creates the key by appending `Type` to
|
8991 | d0fe8c12 | Athina Bekakou | the attribute and value from the model's camelcased model name.
|
8992 | d0fe8c12 | Athina Bekakou | |
8993 | d0fe8c12 | Athina Bekakou | @method serializePolymorphicType
|
8994 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
8995 | d0fe8c12 | Athina Bekakou | @param {Object} json
|
8996 | d0fe8c12 | Athina Bekakou | @param {Object} relationship
|
8997 | d0fe8c12 | Athina Bekakou | */
|
8998 | d0fe8c12 | Athina Bekakou | serializePolymorphicType: function(record, json, relationship) { |
8999 | d0fe8c12 | Athina Bekakou | var key = relationship.key,
|
9000 | d0fe8c12 | Athina Bekakou | belongsTo = get(record, key); |
9001 | d0fe8c12 | Athina Bekakou | key = this.keyForAttribute ? this.keyForAttribute(key) : key; |
9002 | d0fe8c12 | Athina Bekakou | json[key + "Type"] = belongsTo.constructor.typeKey;
|
9003 | d0fe8c12 | Athina Bekakou | } |
9004 | d0fe8c12 | Athina Bekakou | }); |
9005 | d0fe8c12 | Athina Bekakou | |
9006 | d0fe8c12 | Athina Bekakou | })(); |
9007 | d0fe8c12 | Athina Bekakou | |
9008 | d0fe8c12 | Athina Bekakou | |
9009 | d0fe8c12 | Athina Bekakou | |
9010 | d0fe8c12 | Athina Bekakou | (function() {
|
9011 | d0fe8c12 | Athina Bekakou | /**
|
9012 | d0fe8c12 | Athina Bekakou | @module ember-data
|
9013 | d0fe8c12 | Athina Bekakou | */
|
9014 | d0fe8c12 | Athina Bekakou | |
9015 | d0fe8c12 | Athina Bekakou | var get = Ember.get, set = Ember.set;
|
9016 | d0fe8c12 | Athina Bekakou | var forEach = Ember.ArrayPolyfills.forEach;
|
9017 | d0fe8c12 | Athina Bekakou | |
9018 | d0fe8c12 | Athina Bekakou | /**
|
9019 | d0fe8c12 | Athina Bekakou | The REST adapter allows your store to communicate with an HTTP server by
|
9020 | d0fe8c12 | Athina Bekakou | transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
|
9021 | d0fe8c12 | Athina Bekakou | should use the REST adapter.
|
9022 | d0fe8c12 | Athina Bekakou | |
9023 | d0fe8c12 | Athina Bekakou | This adapter is designed around the idea that the JSON exchanged with
|
9024 | d0fe8c12 | Athina Bekakou | the server should be conventional.
|
9025 | d0fe8c12 | Athina Bekakou | |
9026 | d0fe8c12 | Athina Bekakou | ## JSON Structure
|
9027 | d0fe8c12 | Athina Bekakou | |
9028 | d0fe8c12 | Athina Bekakou | The REST adapter expects the JSON returned from your server to follow
|
9029 | d0fe8c12 | Athina Bekakou | these conventions.
|
9030 | d0fe8c12 | Athina Bekakou | |
9031 | d0fe8c12 | Athina Bekakou | ### Object Root
|
9032 | d0fe8c12 | Athina Bekakou | |
9033 | d0fe8c12 | Athina Bekakou | The JSON payload should be an object that contains the record inside a
|
9034 | d0fe8c12 | Athina Bekakou | root property. For example, in response to a `GET` request for
|
9035 | d0fe8c12 | Athina Bekakou | `/posts/1`, the JSON should look like this:
|
9036 | d0fe8c12 | Athina Bekakou | |
9037 | d0fe8c12 | Athina Bekakou | ```js
|
9038 | d0fe8c12 | Athina Bekakou | {
|
9039 | d0fe8c12 | Athina Bekakou | "post": {
|
9040 | d0fe8c12 | Athina Bekakou | "title": "I'm Running to Reform the W3C's Tag",
|
9041 | d0fe8c12 | Athina Bekakou | "author": "Yehuda Katz"
|
9042 | d0fe8c12 | Athina Bekakou | }
|
9043 | d0fe8c12 | Athina Bekakou | }
|
9044 | d0fe8c12 | Athina Bekakou | ```
|
9045 | d0fe8c12 | Athina Bekakou | |
9046 | d0fe8c12 | Athina Bekakou | ### Conventional Names
|
9047 | d0fe8c12 | Athina Bekakou | |
9048 | d0fe8c12 | Athina Bekakou | Attribute names in your JSON payload should be the camelCased versions of
|
9049 | d0fe8c12 | Athina Bekakou | the attributes in your Ember.js models.
|
9050 | d0fe8c12 | Athina Bekakou | |
9051 | d0fe8c12 | Athina Bekakou | For example, if you have a `Person` model:
|
9052 | d0fe8c12 | Athina Bekakou | |
9053 | d0fe8c12 | Athina Bekakou | ```js
|
9054 | d0fe8c12 | Athina Bekakou | App.Person = DS.Model.extend({
|
9055 | d0fe8c12 | Athina Bekakou | firstName: DS.attr('string'),
|
9056 | d0fe8c12 | Athina Bekakou | lastName: DS.attr('string'),
|
9057 | d0fe8c12 | Athina Bekakou | occupation: DS.attr('string')
|
9058 | d0fe8c12 | Athina Bekakou | });
|
9059 | d0fe8c12 | Athina Bekakou | ```
|
9060 | d0fe8c12 | Athina Bekakou | |
9061 | d0fe8c12 | Athina Bekakou | The JSON returned should look like this:
|
9062 | d0fe8c12 | Athina Bekakou | |
9063 | d0fe8c12 | Athina Bekakou | ```js
|
9064 | d0fe8c12 | Athina Bekakou | {
|
9065 | d0fe8c12 | Athina Bekakou | "person": {
|
9066 | d0fe8c12 | Athina Bekakou | "firstName": "Barack",
|
9067 | d0fe8c12 | Athina Bekakou | "lastName": "Obama",
|
9068 | d0fe8c12 | Athina Bekakou | "occupation": "President"
|
9069 | d0fe8c12 | Athina Bekakou | }
|
9070 | d0fe8c12 | Athina Bekakou | }
|
9071 | d0fe8c12 | Athina Bekakou | ```
|
9072 | d0fe8c12 | Athina Bekakou | |
9073 | d0fe8c12 | Athina Bekakou | ## Customization
|
9074 | d0fe8c12 | Athina Bekakou | |
9075 | d0fe8c12 | Athina Bekakou | ### Endpoint path customization
|
9076 | d0fe8c12 | Athina Bekakou | |
9077 | d0fe8c12 | Athina Bekakou | Endpoint paths can be prefixed with a `namespace` by setting the namespace
|
9078 | d0fe8c12 | Athina Bekakou | property on the adapter:
|
9079 | d0fe8c12 | Athina Bekakou | |
9080 | d0fe8c12 | Athina Bekakou | ```js
|
9081 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9082 | d0fe8c12 | Athina Bekakou | namespace: 'api/1'
|
9083 | d0fe8c12 | Athina Bekakou | });
|
9084 | d0fe8c12 | Athina Bekakou | ```
|
9085 | d0fe8c12 | Athina Bekakou | Requests for `App.Person` would now target `/api/1/people/1`.
|
9086 | d0fe8c12 | Athina Bekakou | |
9087 | d0fe8c12 | Athina Bekakou | ### Host customization
|
9088 | d0fe8c12 | Athina Bekakou | |
9089 | d0fe8c12 | Athina Bekakou | An adapter can target other hosts by setting the `host` property.
|
9090 | d0fe8c12 | Athina Bekakou | |
9091 | d0fe8c12 | Athina Bekakou | ```js
|
9092 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9093 | d0fe8c12 | Athina Bekakou | host: 'https://api.example.com'
|
9094 | d0fe8c12 | Athina Bekakou | });
|
9095 | d0fe8c12 | Athina Bekakou | ```
|
9096 | d0fe8c12 | Athina Bekakou | |
9097 | d0fe8c12 | Athina Bekakou | ### Headers customization
|
9098 | d0fe8c12 | Athina Bekakou | |
9099 | d0fe8c12 | Athina Bekakou | Some APIs require HTTP headers, e.g. to provide an API key. An array of
|
9100 | d0fe8c12 | Athina Bekakou | headers can be added to the adapter which are passed with every request:
|
9101 | d0fe8c12 | Athina Bekakou | |
9102 | d0fe8c12 | Athina Bekakou | ```js
|
9103 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9104 | d0fe8c12 | Athina Bekakou | headers: {
|
9105 | d0fe8c12 | Athina Bekakou | "API_KEY": "secret key",
|
9106 | d0fe8c12 | Athina Bekakou | "ANOTHER_HEADER": "Some header value"
|
9107 | d0fe8c12 | Athina Bekakou | }
|
9108 | d0fe8c12 | Athina Bekakou | });
|
9109 | d0fe8c12 | Athina Bekakou | ```
|
9110 | d0fe8c12 | Athina Bekakou | |
9111 | d0fe8c12 | Athina Bekakou | @class RESTAdapter
|
9112 | d0fe8c12 | Athina Bekakou | @constructor
|
9113 | d0fe8c12 | Athina Bekakou | @namespace DS
|
9114 | d0fe8c12 | Athina Bekakou | @extends DS.Adapter
|
9115 | d0fe8c12 | Athina Bekakou | */
|
9116 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter = DS.Adapter.extend({ |
9117 | d0fe8c12 | Athina Bekakou | defaultSerializer: '_rest', |
9118 | d0fe8c12 | Athina Bekakou | |
9119 | d0fe8c12 | Athina Bekakou | |
9120 | d0fe8c12 | Athina Bekakou | /**
|
9121 | d0fe8c12 | Athina Bekakou | Endpoint paths can be prefixed with a `namespace` by setting the namespace
|
9122 | d0fe8c12 | Athina Bekakou | property on the adapter:
|
9123 | d0fe8c12 | Athina Bekakou | |
9124 | d0fe8c12 | Athina Bekakou | ```javascript
|
9125 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9126 | d0fe8c12 | Athina Bekakou | namespace: 'api/1'
|
9127 | d0fe8c12 | Athina Bekakou | });
|
9128 | d0fe8c12 | Athina Bekakou | ```
|
9129 | d0fe8c12 | Athina Bekakou | |
9130 | d0fe8c12 | Athina Bekakou | Requests for `App.Post` would now target `/api/1/post/`.
|
9131 | d0fe8c12 | Athina Bekakou | |
9132 | d0fe8c12 | Athina Bekakou | @property namespace
|
9133 | d0fe8c12 | Athina Bekakou | @type {String}
|
9134 | d0fe8c12 | Athina Bekakou | */
|
9135 | d0fe8c12 | Athina Bekakou | |
9136 | d0fe8c12 | Athina Bekakou | /**
|
9137 | d0fe8c12 | Athina Bekakou | An adapter can target other hosts by setting the `host` property.
|
9138 | d0fe8c12 | Athina Bekakou | |
9139 | d0fe8c12 | Athina Bekakou | ```javascript
|
9140 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9141 | d0fe8c12 | Athina Bekakou | host: 'https://api.example.com'
|
9142 | d0fe8c12 | Athina Bekakou | });
|
9143 | d0fe8c12 | Athina Bekakou | ```
|
9144 | d0fe8c12 | Athina Bekakou | |
9145 | d0fe8c12 | Athina Bekakou | Requests for `App.Post` would now target `https://api.example.com/post/`.
|
9146 | d0fe8c12 | Athina Bekakou | |
9147 | d0fe8c12 | Athina Bekakou | @property host
|
9148 | d0fe8c12 | Athina Bekakou | @type {String}
|
9149 | d0fe8c12 | Athina Bekakou | */
|
9150 | d0fe8c12 | Athina Bekakou | |
9151 | d0fe8c12 | Athina Bekakou | /**
|
9152 | d0fe8c12 | Athina Bekakou | Some APIs require HTTP headers, e.g. to provide an API key. An array of
|
9153 | d0fe8c12 | Athina Bekakou | headers can be added to the adapter which are passed with every request:
|
9154 | d0fe8c12 | Athina Bekakou | |
9155 | d0fe8c12 | Athina Bekakou | ```javascript
|
9156 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9157 | d0fe8c12 | Athina Bekakou | headers: {
|
9158 | d0fe8c12 | Athina Bekakou | "API_KEY": "secret key",
|
9159 | d0fe8c12 | Athina Bekakou | "ANOTHER_HEADER": "Some header value"
|
9160 | d0fe8c12 | Athina Bekakou | }
|
9161 | d0fe8c12 | Athina Bekakou | });
|
9162 | d0fe8c12 | Athina Bekakou | ```
|
9163 | d0fe8c12 | Athina Bekakou | |
9164 | d0fe8c12 | Athina Bekakou | @property headers
|
9165 | d0fe8c12 | Athina Bekakou | @type {Object}
|
9166 | d0fe8c12 | Athina Bekakou | */
|
9167 | d0fe8c12 | Athina Bekakou | |
9168 | d0fe8c12 | Athina Bekakou | /**
|
9169 | d0fe8c12 | Athina Bekakou | Called by the store in order to fetch the JSON for a given
|
9170 | d0fe8c12 | Athina Bekakou | type and ID.
|
9171 | d0fe8c12 | Athina Bekakou | |
9172 | d0fe8c12 | Athina Bekakou | The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a
|
9173 | d0fe8c12 | Athina Bekakou | promise for the resulting payload.
|
9174 | d0fe8c12 | Athina Bekakou | |
9175 | d0fe8c12 | Athina Bekakou | This method performs an HTTP `GET` request with the id provided as part of the querystring.
|
9176 | d0fe8c12 | Athina Bekakou | |
9177 | d0fe8c12 | Athina Bekakou | @method find
|
9178 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9179 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9180 | d0fe8c12 | Athina Bekakou | @param {String} id
|
9181 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9182 | d0fe8c12 | Athina Bekakou | */
|
9183 | d0fe8c12 | Athina Bekakou | find: function(store, type, id) { |
9184 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey, id), 'GET'); |
9185 | d0fe8c12 | Athina Bekakou | }, |
9186 | d0fe8c12 | Athina Bekakou | |
9187 | d0fe8c12 | Athina Bekakou | /**
|
9188 | d0fe8c12 | Athina Bekakou | Called by the store in order to fetch a JSON array for all
|
9189 | d0fe8c12 | Athina Bekakou | of the records for a given type.
|
9190 | d0fe8c12 | Athina Bekakou | |
9191 | d0fe8c12 | Athina Bekakou | The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
|
9192 | d0fe8c12 | Athina Bekakou | promise for the resulting payload.
|
9193 | d0fe8c12 | Athina Bekakou | |
9194 | d0fe8c12 | Athina Bekakou | @private
|
9195 | d0fe8c12 | Athina Bekakou | @method findAll
|
9196 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9197 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9198 | d0fe8c12 | Athina Bekakou | @param {String} sinceToken
|
9199 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9200 | d0fe8c12 | Athina Bekakou | */
|
9201 | d0fe8c12 | Athina Bekakou | findAll: function(store, type, sinceToken) { |
9202 | d0fe8c12 | Athina Bekakou | var query;
|
9203 | d0fe8c12 | Athina Bekakou | |
9204 | d0fe8c12 | Athina Bekakou | if (sinceToken) {
|
9205 | d0fe8c12 | Athina Bekakou | query = { since: sinceToken };
|
9206 | d0fe8c12 | Athina Bekakou | } |
9207 | d0fe8c12 | Athina Bekakou | |
9208 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); |
9209 | d0fe8c12 | Athina Bekakou | }, |
9210 | d0fe8c12 | Athina Bekakou | |
9211 | d0fe8c12 | Athina Bekakou | /**
|
9212 | d0fe8c12 | Athina Bekakou | Called by the store in order to fetch a JSON array for
|
9213 | d0fe8c12 | Athina Bekakou | the records that match a particular query.
|
9214 | d0fe8c12 | Athina Bekakou | |
9215 | d0fe8c12 | Athina Bekakou | The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
|
9216 | d0fe8c12 | Athina Bekakou | promise for the resulting payload.
|
9217 | d0fe8c12 | Athina Bekakou | |
9218 | d0fe8c12 | Athina Bekakou | The `query` argument is a simple JavaScript object that will be passed directly
|
9219 | d0fe8c12 | Athina Bekakou | to the server as parameters.
|
9220 | d0fe8c12 | Athina Bekakou | |
9221 | d0fe8c12 | Athina Bekakou | @private
|
9222 | d0fe8c12 | Athina Bekakou | @method findQuery
|
9223 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9224 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9225 | d0fe8c12 | Athina Bekakou | @param {Object} query
|
9226 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9227 | d0fe8c12 | Athina Bekakou | */
|
9228 | d0fe8c12 | Athina Bekakou | findQuery: function(store, type, query) { |
9229 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); |
9230 | d0fe8c12 | Athina Bekakou | }, |
9231 | d0fe8c12 | Athina Bekakou | |
9232 | d0fe8c12 | Athina Bekakou | /**
|
9233 | d0fe8c12 | Athina Bekakou | Called by the store in order to fetch a JSON array for
|
9234 | d0fe8c12 | Athina Bekakou | the unloaded records in a has-many relationship that were originally
|
9235 | d0fe8c12 | Athina Bekakou | specified as IDs.
|
9236 | d0fe8c12 | Athina Bekakou | |
9237 | d0fe8c12 | Athina Bekakou | For example, if the original payload looks like:
|
9238 | d0fe8c12 | Athina Bekakou | |
9239 | d0fe8c12 | Athina Bekakou | ```js
|
9240 | d0fe8c12 | Athina Bekakou | {
|
9241 | d0fe8c12 | Athina Bekakou | "id": 1,
|
9242 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
9243 | d0fe8c12 | Athina Bekakou | "comments": [ 1, 2, 3 ]
|
9244 | d0fe8c12 | Athina Bekakou | }
|
9245 | d0fe8c12 | Athina Bekakou | ```
|
9246 | d0fe8c12 | Athina Bekakou | |
9247 | d0fe8c12 | Athina Bekakou | The IDs will be passed as a URL-encoded Array of IDs, in this form:
|
9248 | d0fe8c12 | Athina Bekakou | |
9249 | d0fe8c12 | Athina Bekakou | ```
|
9250 | d0fe8c12 | Athina Bekakou | ids[]=1&ids[]=2&ids[]=3
|
9251 | d0fe8c12 | Athina Bekakou | ```
|
9252 | d0fe8c12 | Athina Bekakou | |
9253 | d0fe8c12 | Athina Bekakou | Many servers, such as Rails and PHP, will automatically convert this URL-encoded array
|
9254 | d0fe8c12 | Athina Bekakou | into an Array for you on the server-side. If you want to encode the
|
9255 | d0fe8c12 | Athina Bekakou | IDs, differently, just override this (one-line) method.
|
9256 | d0fe8c12 | Athina Bekakou | |
9257 | d0fe8c12 | Athina Bekakou | The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a
|
9258 | d0fe8c12 | Athina Bekakou | promise for the resulting payload.
|
9259 | d0fe8c12 | Athina Bekakou | |
9260 | d0fe8c12 | Athina Bekakou | @method findMany
|
9261 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9262 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9263 | d0fe8c12 | Athina Bekakou | @param {Array} ids
|
9264 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9265 | d0fe8c12 | Athina Bekakou | */
|
9266 | d0fe8c12 | Athina Bekakou | findMany: function(store, type, ids) { |
9267 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } }); |
9268 | d0fe8c12 | Athina Bekakou | }, |
9269 | d0fe8c12 | Athina Bekakou | |
9270 | d0fe8c12 | Athina Bekakou | /**
|
9271 | d0fe8c12 | Athina Bekakou | Called by the store in order to fetch a JSON array for
|
9272 | d0fe8c12 | Athina Bekakou | the unloaded records in a has-many relationship that were originally
|
9273 | d0fe8c12 | Athina Bekakou | specified as a URL (inside of `links`).
|
9274 | d0fe8c12 | Athina Bekakou | |
9275 | d0fe8c12 | Athina Bekakou | For example, if your original payload looks like this:
|
9276 | d0fe8c12 | Athina Bekakou | |
9277 | d0fe8c12 | Athina Bekakou | ```js
|
9278 | d0fe8c12 | Athina Bekakou | {
|
9279 | d0fe8c12 | Athina Bekakou | "post": {
|
9280 | d0fe8c12 | Athina Bekakou | "id": 1,
|
9281 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
9282 | d0fe8c12 | Athina Bekakou | "links": { "comments": "/posts/1/comments" }
|
9283 | d0fe8c12 | Athina Bekakou | }
|
9284 | d0fe8c12 | Athina Bekakou | }
|
9285 | d0fe8c12 | Athina Bekakou | ```
|
9286 | d0fe8c12 | Athina Bekakou | |
9287 | d0fe8c12 | Athina Bekakou | This method will be called with the parent record and `/posts/1/comments`.
|
9288 | d0fe8c12 | Athina Bekakou | |
9289 | d0fe8c12 | Athina Bekakou | The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL.
|
9290 | d0fe8c12 | Athina Bekakou | If the URL is host-relative (starting with a single slash), the
|
9291 | d0fe8c12 | Athina Bekakou | request will use the host specified on the adapter (if any).
|
9292 | d0fe8c12 | Athina Bekakou | |
9293 | d0fe8c12 | Athina Bekakou | @method findHasMany
|
9294 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9295 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
9296 | d0fe8c12 | Athina Bekakou | @param {String} url
|
9297 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9298 | d0fe8c12 | Athina Bekakou | */
|
9299 | d0fe8c12 | Athina Bekakou | findHasMany: function(store, record, url) { |
9300 | d0fe8c12 | Athina Bekakou | var host = get(this, 'host'), |
9301 | d0fe8c12 | Athina Bekakou | id = get(record, 'id'),
|
9302 | d0fe8c12 | Athina Bekakou | type = record.constructor.typeKey; |
9303 | d0fe8c12 | Athina Bekakou | |
9304 | d0fe8c12 | Athina Bekakou | if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') { |
9305 | d0fe8c12 | Athina Bekakou | url = host + url; |
9306 | d0fe8c12 | Athina Bekakou | } |
9307 | d0fe8c12 | Athina Bekakou | |
9308 | d0fe8c12 | Athina Bekakou | return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); |
9309 | d0fe8c12 | Athina Bekakou | }, |
9310 | d0fe8c12 | Athina Bekakou | |
9311 | d0fe8c12 | Athina Bekakou | /**
|
9312 | d0fe8c12 | Athina Bekakou | Called by the store in order to fetch a JSON array for
|
9313 | d0fe8c12 | Athina Bekakou | the unloaded records in a belongs-to relationship that were originally
|
9314 | d0fe8c12 | Athina Bekakou | specified as a URL (inside of `links`).
|
9315 | d0fe8c12 | Athina Bekakou | |
9316 | d0fe8c12 | Athina Bekakou | For example, if your original payload looks like this:
|
9317 | d0fe8c12 | Athina Bekakou | |
9318 | d0fe8c12 | Athina Bekakou | ```js
|
9319 | d0fe8c12 | Athina Bekakou | {
|
9320 | d0fe8c12 | Athina Bekakou | "person": {
|
9321 | d0fe8c12 | Athina Bekakou | "id": 1,
|
9322 | d0fe8c12 | Athina Bekakou | "name": "Tom Dale",
|
9323 | d0fe8c12 | Athina Bekakou | "links": { "group": "/people/1/group" }
|
9324 | d0fe8c12 | Athina Bekakou | }
|
9325 | d0fe8c12 | Athina Bekakou | }
|
9326 | d0fe8c12 | Athina Bekakou | ```
|
9327 | d0fe8c12 | Athina Bekakou | |
9328 | d0fe8c12 | Athina Bekakou | This method will be called with the parent record and `/people/1/group`.
|
9329 | d0fe8c12 | Athina Bekakou | |
9330 | d0fe8c12 | Athina Bekakou | The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL.
|
9331 | d0fe8c12 | Athina Bekakou | |
9332 | d0fe8c12 | Athina Bekakou | @method findBelongsTo
|
9333 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9334 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
9335 | d0fe8c12 | Athina Bekakou | @param {String} url
|
9336 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9337 | d0fe8c12 | Athina Bekakou | */
|
9338 | d0fe8c12 | Athina Bekakou | findBelongsTo: function(store, record, url) { |
9339 | d0fe8c12 | Athina Bekakou | var id = get(record, 'id'), |
9340 | d0fe8c12 | Athina Bekakou | type = record.constructor.typeKey; |
9341 | d0fe8c12 | Athina Bekakou | |
9342 | d0fe8c12 | Athina Bekakou | return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); |
9343 | d0fe8c12 | Athina Bekakou | }, |
9344 | d0fe8c12 | Athina Bekakou | |
9345 | d0fe8c12 | Athina Bekakou | /**
|
9346 | d0fe8c12 | Athina Bekakou | Called by the store when a newly created record is
|
9347 | d0fe8c12 | Athina Bekakou | saved via the `save` method on a model record instance.
|
9348 | d0fe8c12 | Athina Bekakou | |
9349 | d0fe8c12 | Athina Bekakou | The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request
|
9350 | d0fe8c12 | Athina Bekakou | to a URL computed by `buildURL`.
|
9351 | d0fe8c12 | Athina Bekakou | |
9352 | d0fe8c12 | Athina Bekakou | See `serialize` for information on how to customize the serialized form
|
9353 | d0fe8c12 | Athina Bekakou | of a record.
|
9354 | d0fe8c12 | Athina Bekakou | |
9355 | d0fe8c12 | Athina Bekakou | @method createRecord
|
9356 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9357 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9358 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
9359 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9360 | d0fe8c12 | Athina Bekakou | */
|
9361 | d0fe8c12 | Athina Bekakou | createRecord: function(store, type, record) { |
9362 | d0fe8c12 | Athina Bekakou | var data = {};
|
9363 | d0fe8c12 | Athina Bekakou | var serializer = store.serializerFor(type.typeKey);
|
9364 | d0fe8c12 | Athina Bekakou | |
9365 | d0fe8c12 | Athina Bekakou | serializer.serializeIntoHash(data, type, record, { includeId: true }); |
9366 | d0fe8c12 | Athina Bekakou | |
9367 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey), "POST", { data: data }); |
9368 | d0fe8c12 | Athina Bekakou | }, |
9369 | d0fe8c12 | Athina Bekakou | |
9370 | d0fe8c12 | Athina Bekakou | /**
|
9371 | d0fe8c12 | Athina Bekakou | Called by the store when an existing record is saved
|
9372 | d0fe8c12 | Athina Bekakou | via the `save` method on a model record instance.
|
9373 | d0fe8c12 | Athina Bekakou | |
9374 | d0fe8c12 | Athina Bekakou | The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request
|
9375 | d0fe8c12 | Athina Bekakou | to a URL computed by `buildURL`.
|
9376 | d0fe8c12 | Athina Bekakou | |
9377 | d0fe8c12 | Athina Bekakou | See `serialize` for information on how to customize the serialized form
|
9378 | d0fe8c12 | Athina Bekakou | of a record.
|
9379 | d0fe8c12 | Athina Bekakou | |
9380 | d0fe8c12 | Athina Bekakou | @method updateRecord
|
9381 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9382 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9383 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
9384 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9385 | d0fe8c12 | Athina Bekakou | */
|
9386 | d0fe8c12 | Athina Bekakou | updateRecord: function(store, type, record) { |
9387 | d0fe8c12 | Athina Bekakou | var data = {};
|
9388 | d0fe8c12 | Athina Bekakou | var serializer = store.serializerFor(type.typeKey);
|
9389 | d0fe8c12 | Athina Bekakou | |
9390 | d0fe8c12 | Athina Bekakou | serializer.serializeIntoHash(data, type, record); |
9391 | d0fe8c12 | Athina Bekakou | |
9392 | d0fe8c12 | Athina Bekakou | var id = get(record, 'id'); |
9393 | d0fe8c12 | Athina Bekakou | |
9394 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data }); |
9395 | d0fe8c12 | Athina Bekakou | }, |
9396 | d0fe8c12 | Athina Bekakou | |
9397 | d0fe8c12 | Athina Bekakou | /**
|
9398 | d0fe8c12 | Athina Bekakou | Called by the store when a record is deleted.
|
9399 | d0fe8c12 | Athina Bekakou | |
9400 | d0fe8c12 | Athina Bekakou | The `deleteRecord` method makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`.
|
9401 | d0fe8c12 | Athina Bekakou | |
9402 | d0fe8c12 | Athina Bekakou | @method deleteRecord
|
9403 | d0fe8c12 | Athina Bekakou | @param {DS.Store} store
|
9404 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
9405 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
9406 | d0fe8c12 | Athina Bekakou | @returns {Promise} promise
|
9407 | d0fe8c12 | Athina Bekakou | */
|
9408 | d0fe8c12 | Athina Bekakou | deleteRecord: function(store, type, record) { |
9409 | d0fe8c12 | Athina Bekakou | var id = get(record, 'id'); |
9410 | d0fe8c12 | Athina Bekakou | |
9411 | d0fe8c12 | Athina Bekakou | return this.ajax(this.buildURL(type.typeKey, id), "DELETE"); |
9412 | d0fe8c12 | Athina Bekakou | }, |
9413 | d0fe8c12 | Athina Bekakou | |
9414 | d0fe8c12 | Athina Bekakou | /**
|
9415 | d0fe8c12 | Athina Bekakou | Builds a URL for a given type and optional ID.
|
9416 | d0fe8c12 | Athina Bekakou | |
9417 | d0fe8c12 | Athina Bekakou | By default, it pluralizes the type's name (for example, 'post'
|
9418 | d0fe8c12 | Athina Bekakou | becomes 'posts' and 'person' becomes 'people'). To override the
|
9419 | d0fe8c12 | Athina Bekakou | pluralization see [pathForType](#method_pathForType).
|
9420 | d0fe8c12 | Athina Bekakou | |
9421 | d0fe8c12 | Athina Bekakou | If an ID is specified, it adds the ID to the path generated
|
9422 | d0fe8c12 | Athina Bekakou | for the type, separated by a `/`.
|
9423 | d0fe8c12 | Athina Bekakou | |
9424 | d0fe8c12 | Athina Bekakou | @method buildURL
|
9425 | d0fe8c12 | Athina Bekakou | @param {String} type
|
9426 | d0fe8c12 | Athina Bekakou | @param {String} id
|
9427 | d0fe8c12 | Athina Bekakou | @returns {String} url
|
9428 | d0fe8c12 | Athina Bekakou | */
|
9429 | d0fe8c12 | Athina Bekakou | buildURL: function(type, id) { |
9430 | d0fe8c12 | Athina Bekakou | var url = [],
|
9431 | d0fe8c12 | Athina Bekakou | host = get(this, 'host'), |
9432 | d0fe8c12 | Athina Bekakou | prefix = this.urlPrefix();
|
9433 | d0fe8c12 | Athina Bekakou | |
9434 | d0fe8c12 | Athina Bekakou | if (type) { url.push(this.pathForType(type)); } |
9435 | d0fe8c12 | Athina Bekakou | if (id) { url.push(id); }
|
9436 | d0fe8c12 | Athina Bekakou | |
9437 | d0fe8c12 | Athina Bekakou | if (prefix) { url.unshift(prefix); }
|
9438 | d0fe8c12 | Athina Bekakou | |
9439 | d0fe8c12 | Athina Bekakou | url = url.join('/');
|
9440 | d0fe8c12 | Athina Bekakou | if (!host && url) { url = '/' + url; } |
9441 | d0fe8c12 | Athina Bekakou | |
9442 | d0fe8c12 | Athina Bekakou | return url;
|
9443 | d0fe8c12 | Athina Bekakou | }, |
9444 | d0fe8c12 | Athina Bekakou | |
9445 | d0fe8c12 | Athina Bekakou | /**
|
9446 | d0fe8c12 | Athina Bekakou | @method urlPrefix
|
9447 | d0fe8c12 | Athina Bekakou | @private
|
9448 | d0fe8c12 | Athina Bekakou | @param {String} path
|
9449 | d0fe8c12 | Athina Bekakou | @param {String} parentUrl
|
9450 | d0fe8c12 | Athina Bekakou | @return {String} urlPrefix
|
9451 | d0fe8c12 | Athina Bekakou | */
|
9452 | d0fe8c12 | Athina Bekakou | urlPrefix: function(path, parentURL) { |
9453 | d0fe8c12 | Athina Bekakou | var host = get(this, 'host'), |
9454 | d0fe8c12 | Athina Bekakou | namespace = get(this, 'namespace'), |
9455 | d0fe8c12 | Athina Bekakou | url = []; |
9456 | d0fe8c12 | Athina Bekakou | |
9457 | d0fe8c12 | Athina Bekakou | if (path) {
|
9458 | d0fe8c12 | Athina Bekakou | // Absolute path
|
9459 | d0fe8c12 | Athina Bekakou | if (path.charAt(0) === '/') { |
9460 | d0fe8c12 | Athina Bekakou | if (host) {
|
9461 | d0fe8c12 | Athina Bekakou | path = path.slice(1);
|
9462 | d0fe8c12 | Athina Bekakou | url.push(host); |
9463 | d0fe8c12 | Athina Bekakou | } |
9464 | d0fe8c12 | Athina Bekakou | // Relative path
|
9465 | d0fe8c12 | Athina Bekakou | } else if (!/^http(s)?:\/\//.test(path)) { |
9466 | d0fe8c12 | Athina Bekakou | url.push(parentURL); |
9467 | d0fe8c12 | Athina Bekakou | } |
9468 | d0fe8c12 | Athina Bekakou | } else {
|
9469 | d0fe8c12 | Athina Bekakou | if (host) { url.push(host); }
|
9470 | d0fe8c12 | Athina Bekakou | if (namespace) { url.push(namespace); }
|
9471 | d0fe8c12 | Athina Bekakou | } |
9472 | d0fe8c12 | Athina Bekakou | |
9473 | d0fe8c12 | Athina Bekakou | if (path) {
|
9474 | d0fe8c12 | Athina Bekakou | url.push(path); |
9475 | d0fe8c12 | Athina Bekakou | } |
9476 | d0fe8c12 | Athina Bekakou | |
9477 | d0fe8c12 | Athina Bekakou | return url.join('/'); |
9478 | d0fe8c12 | Athina Bekakou | }, |
9479 | d0fe8c12 | Athina Bekakou | |
9480 | d0fe8c12 | Athina Bekakou | /**
|
9481 | d0fe8c12 | Athina Bekakou | Determines the pathname for a given type.
|
9482 | d0fe8c12 | Athina Bekakou | |
9483 | d0fe8c12 | Athina Bekakou | By default, it pluralizes the type's name (for example,
|
9484 | d0fe8c12 | Athina Bekakou | 'post' becomes 'posts' and 'person' becomes 'people').
|
9485 | d0fe8c12 | Athina Bekakou | |
9486 | d0fe8c12 | Athina Bekakou | ### Pathname customization
|
9487 | d0fe8c12 | Athina Bekakou | |
9488 | d0fe8c12 | Athina Bekakou | For example if you have an object LineItem with an
|
9489 | d0fe8c12 | Athina Bekakou | endpoint of "/line_items/".
|
9490 | d0fe8c12 | Athina Bekakou | |
9491 | d0fe8c12 | Athina Bekakou | ```js
|
9492 | d0fe8c12 | Athina Bekakou | DS.RESTAdapter.reopen({
|
9493 | d0fe8c12 | Athina Bekakou | pathForType: function(type) {
|
9494 | d0fe8c12 | Athina Bekakou | var decamelized = Ember.String.decamelize(type);
|
9495 | d0fe8c12 | Athina Bekakou | return Ember.String.pluralize(decamelized);
|
9496 | d0fe8c12 | Athina Bekakou | };
|
9497 | d0fe8c12 | Athina Bekakou | });
|
9498 | d0fe8c12 | Athina Bekakou | ```
|
9499 | d0fe8c12 | Athina Bekakou | |
9500 | d0fe8c12 | Athina Bekakou | @method pathForType
|
9501 | d0fe8c12 | Athina Bekakou | @param {String} type
|
9502 | d0fe8c12 | Athina Bekakou | @returns {String} path
|
9503 | d0fe8c12 | Athina Bekakou | **/
|
9504 | d0fe8c12 | Athina Bekakou | pathForType: function(type) { |
9505 | d0fe8c12 | Athina Bekakou | return Ember.String.pluralize(type);
|
9506 | d0fe8c12 | Athina Bekakou | }, |
9507 | d0fe8c12 | Athina Bekakou | |
9508 | d0fe8c12 | Athina Bekakou | /**
|
9509 | d0fe8c12 | Athina Bekakou | Takes an ajax response, and returns a relavant error.
|
9510 | d0fe8c12 | Athina Bekakou | |
9511 | d0fe8c12 | Athina Bekakou | Returning a `DS.InvalidError` from this method will cause the
|
9512 | d0fe8c12 | Athina Bekakou | record to transition into the `invalid` state and make the
|
9513 | d0fe8c12 | Athina Bekakou | `errors` object available on the record.
|
9514 | d0fe8c12 | Athina Bekakou | |
9515 | d0fe8c12 | Athina Bekakou | ```javascript
|
9516 | d0fe8c12 | Athina Bekakou | App.ApplicationAdapter = DS.RESTAdapter.extend({
|
9517 | d0fe8c12 | Athina Bekakou | ajaxError: function(jqXHR) {
|
9518 | d0fe8c12 | Athina Bekakou | var error = this._super(jqXHR);
|
9519 | d0fe8c12 | Athina Bekakou | |
9520 | d0fe8c12 | Athina Bekakou | if (jqXHR && jqXHR.status === 422) {
|
9521 | d0fe8c12 | Athina Bekakou | var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"];
|
9522 | d0fe8c12 | Athina Bekakou | |
9523 | d0fe8c12 | Athina Bekakou | return new DS.InvalidError(jsonErrors);
|
9524 | d0fe8c12 | Athina Bekakou | } else {
|
9525 | d0fe8c12 | Athina Bekakou | return error;
|
9526 | d0fe8c12 | Athina Bekakou | }
|
9527 | d0fe8c12 | Athina Bekakou | }
|
9528 | d0fe8c12 | Athina Bekakou | });
|
9529 | d0fe8c12 | Athina Bekakou | ```
|
9530 | d0fe8c12 | Athina Bekakou | |
9531 | d0fe8c12 | Athina Bekakou | Note: As a correctness optimization, the default implementation of
|
9532 | d0fe8c12 | Athina Bekakou | the `ajaxError` method strips out the `then` method from jquery's
|
9533 | d0fe8c12 | Athina Bekakou | ajax response (jqXHR). This is important because the jqXHR's
|
9534 | d0fe8c12 | Athina Bekakou | `then` method fulfills the promise with itself resulting in a
|
9535 | d0fe8c12 | Athina Bekakou | circular "thenable" chain which may cause problems for some
|
9536 | d0fe8c12 | Athina Bekakou | promise libraries.
|
9537 | d0fe8c12 | Athina Bekakou | |
9538 | d0fe8c12 | Athina Bekakou | @method ajaxError
|
9539 | d0fe8c12 | Athina Bekakou | @param {Object} jqXHR
|
9540 | d0fe8c12 | Athina Bekakou | @return {Object} jqXHR
|
9541 | d0fe8c12 | Athina Bekakou | */
|
9542 | d0fe8c12 | Athina Bekakou | ajaxError: function(jqXHR) { |
9543 | d0fe8c12 | Athina Bekakou | if (jqXHR) {
|
9544 | d0fe8c12 | Athina Bekakou | jqXHR.then = null;
|
9545 | d0fe8c12 | Athina Bekakou | } |
9546 | d0fe8c12 | Athina Bekakou | |
9547 | d0fe8c12 | Athina Bekakou | return jqXHR;
|
9548 | d0fe8c12 | Athina Bekakou | }, |
9549 | d0fe8c12 | Athina Bekakou | |
9550 | d0fe8c12 | Athina Bekakou | /**
|
9551 | d0fe8c12 | Athina Bekakou | Takes a URL, an HTTP method and a hash of data, and makes an
|
9552 | d0fe8c12 | Athina Bekakou | HTTP request.
|
9553 | d0fe8c12 | Athina Bekakou | |
9554 | d0fe8c12 | Athina Bekakou | When the server responds with a payload, Ember Data will call into `extractSingle`
|
9555 | d0fe8c12 | Athina Bekakou | or `extractArray` (depending on whether the original query was for one record or
|
9556 | d0fe8c12 | Athina Bekakou | many records).
|
9557 | d0fe8c12 | Athina Bekakou | |
9558 | d0fe8c12 | Athina Bekakou | By default, `ajax` method has the following behavior:
|
9559 | d0fe8c12 | Athina Bekakou | |
9560 | d0fe8c12 | Athina Bekakou | * It sets the response `dataType` to `"json"`
|
9561 | d0fe8c12 | Athina Bekakou | * If the HTTP method is not `"GET"`, it sets the `Content-Type` to be
|
9562 | d0fe8c12 | Athina Bekakou | `application/json; charset=utf-8`
|
9563 | d0fe8c12 | Athina Bekakou | * If the HTTP method is not `"GET"`, it stringifies the data passed in. The
|
9564 | d0fe8c12 | Athina Bekakou | data is the serialized record in the case of a save.
|
9565 | d0fe8c12 | Athina Bekakou | * Registers success and failure handlers.
|
9566 | d0fe8c12 | Athina Bekakou | |
9567 | d0fe8c12 | Athina Bekakou | @method ajax
|
9568 | d0fe8c12 | Athina Bekakou | @private
|
9569 | d0fe8c12 | Athina Bekakou | @param {String} url
|
9570 | d0fe8c12 | Athina Bekakou | @param {String} type The request type GET, POST, PUT, DELETE ect.
|
9571 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
9572 | d0fe8c12 | Athina Bekakou | @return {Promise} promise
|
9573 | d0fe8c12 | Athina Bekakou | */
|
9574 | d0fe8c12 | Athina Bekakou | ajax: function(url, type, hash) { |
9575 | d0fe8c12 | Athina Bekakou | var adapter = this; |
9576 | d0fe8c12 | Athina Bekakou | |
9577 | d0fe8c12 | Athina Bekakou | return new Ember.RSVP.Promise(function(resolve, reject) { |
9578 | d0fe8c12 | Athina Bekakou | hash = adapter.ajaxOptions(url, type, hash); |
9579 | d0fe8c12 | Athina Bekakou | |
9580 | d0fe8c12 | Athina Bekakou | hash.success = function(json) { |
9581 | d0fe8c12 | Athina Bekakou | Ember.run(null, resolve, json);
|
9582 | d0fe8c12 | Athina Bekakou | }; |
9583 | d0fe8c12 | Athina Bekakou | |
9584 | d0fe8c12 | Athina Bekakou | hash.error = function(jqXHR, textStatus, errorThrown) { |
9585 | d0fe8c12 | Athina Bekakou | Ember.run(null, reject, adapter.ajaxError(jqXHR));
|
9586 | d0fe8c12 | Athina Bekakou | }; |
9587 | d0fe8c12 | Athina Bekakou | |
9588 | d0fe8c12 | Athina Bekakou | Ember.$.ajax(hash);
|
9589 | d0fe8c12 | Athina Bekakou | }, "DS: RestAdapter#ajax " + type + " to " + url); |
9590 | d0fe8c12 | Athina Bekakou | }, |
9591 | d0fe8c12 | Athina Bekakou | |
9592 | d0fe8c12 | Athina Bekakou | /**
|
9593 | d0fe8c12 | Athina Bekakou | @method ajaxOptions
|
9594 | d0fe8c12 | Athina Bekakou | @private
|
9595 | d0fe8c12 | Athina Bekakou | @param {String} url
|
9596 | d0fe8c12 | Athina Bekakou | @param {String} type The request type GET, POST, PUT, DELETE ect.
|
9597 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
9598 | d0fe8c12 | Athina Bekakou | @return {Object} hash
|
9599 | d0fe8c12 | Athina Bekakou | */
|
9600 | d0fe8c12 | Athina Bekakou | ajaxOptions: function(url, type, hash) { |
9601 | d0fe8c12 | Athina Bekakou | hash = hash || {}; |
9602 | d0fe8c12 | Athina Bekakou | hash.url = url; |
9603 | d0fe8c12 | Athina Bekakou | hash.type = type; |
9604 | d0fe8c12 | Athina Bekakou | hash.dataType = 'json';
|
9605 | d0fe8c12 | Athina Bekakou | hash.context = this;
|
9606 | d0fe8c12 | Athina Bekakou | |
9607 | d0fe8c12 | Athina Bekakou | if (hash.data && type !== 'GET') { |
9608 | d0fe8c12 | Athina Bekakou | hash.contentType = 'application/json; charset=utf-8';
|
9609 | d0fe8c12 | Athina Bekakou | hash.data = JSON.stringify(hash.data); |
9610 | d0fe8c12 | Athina Bekakou | } |
9611 | d0fe8c12 | Athina Bekakou | |
9612 | d0fe8c12 | Athina Bekakou | if (this.headers !== undefined) { |
9613 | d0fe8c12 | Athina Bekakou | var headers = this.headers; |
9614 | d0fe8c12 | Athina Bekakou | hash.beforeSend = function (xhr) { |
9615 | d0fe8c12 | Athina Bekakou | forEach.call(Ember.keys(headers), function(key) {
|
9616 | d0fe8c12 | Athina Bekakou | xhr.setRequestHeader(key, headers[key]); |
9617 | d0fe8c12 | Athina Bekakou | }); |
9618 | d0fe8c12 | Athina Bekakou | }; |
9619 | d0fe8c12 | Athina Bekakou | } |
9620 | d0fe8c12 | Athina Bekakou | |
9621 | d0fe8c12 | Athina Bekakou | |
9622 | d0fe8c12 | Athina Bekakou | return hash;
|
9623 | d0fe8c12 | Athina Bekakou | } |
9624 | d0fe8c12 | Athina Bekakou | |
9625 | d0fe8c12 | Athina Bekakou | }); |
9626 | d0fe8c12 | Athina Bekakou | |
9627 | d0fe8c12 | Athina Bekakou | })(); |
9628 | d0fe8c12 | Athina Bekakou | |
9629 | d0fe8c12 | Athina Bekakou | |
9630 | d0fe8c12 | Athina Bekakou | |
9631 | d0fe8c12 | Athina Bekakou | (function() {
|
9632 | d0fe8c12 | Athina Bekakou | /**
|
9633 | d0fe8c12 | Athina Bekakou | @module ember-data
|
9634 | d0fe8c12 | Athina Bekakou | */
|
9635 | d0fe8c12 | Athina Bekakou | |
9636 | d0fe8c12 | Athina Bekakou | })(); |
9637 | d0fe8c12 | Athina Bekakou | |
9638 | d0fe8c12 | Athina Bekakou | |
9639 | d0fe8c12 | Athina Bekakou | |
9640 | d0fe8c12 | Athina Bekakou | (function() {
|
9641 | d0fe8c12 | Athina Bekakou | DS.Model.reopen({ |
9642 | d0fe8c12 | Athina Bekakou | |
9643 | d0fe8c12 | Athina Bekakou | /**
|
9644 | d0fe8c12 | Athina Bekakou | Provides info about the model for debugging purposes
|
9645 | d0fe8c12 | Athina Bekakou | by grouping the properties into more semantic groups.
|
9646 | d0fe8c12 | Athina Bekakou | |
9647 | d0fe8c12 | Athina Bekakou | Meant to be used by debugging tools such as the Chrome Ember Extension.
|
9648 | d0fe8c12 | Athina Bekakou | |
9649 | d0fe8c12 | Athina Bekakou | - Groups all attributes in "Attributes" group.
|
9650 | d0fe8c12 | Athina Bekakou | - Groups all belongsTo relationships in "Belongs To" group.
|
9651 | d0fe8c12 | Athina Bekakou | - Groups all hasMany relationships in "Has Many" group.
|
9652 | d0fe8c12 | Athina Bekakou | - Groups all flags in "Flags" group.
|
9653 | d0fe8c12 | Athina Bekakou | - Flags relationship CPs as expensive properties.
|
9654 | d0fe8c12 | Athina Bekakou | |
9655 | d0fe8c12 | Athina Bekakou | @method _debugInfo
|
9656 | d0fe8c12 | Athina Bekakou | @for DS.Model
|
9657 | d0fe8c12 | Athina Bekakou | @private
|
9658 | d0fe8c12 | Athina Bekakou | */
|
9659 | d0fe8c12 | Athina Bekakou | _debugInfo: function() { |
9660 | d0fe8c12 | Athina Bekakou | var attributes = ['id'], |
9661 | d0fe8c12 | Athina Bekakou | relationships = { belongsTo: [], hasMany: [] }, |
9662 | d0fe8c12 | Athina Bekakou | expensiveProperties = []; |
9663 | d0fe8c12 | Athina Bekakou | |
9664 | d0fe8c12 | Athina Bekakou | this.eachAttribute(function(name, meta) { |
9665 | d0fe8c12 | Athina Bekakou | attributes.push(name); |
9666 | d0fe8c12 | Athina Bekakou | }, this);
|
9667 | d0fe8c12 | Athina Bekakou | |
9668 | d0fe8c12 | Athina Bekakou | this.eachRelationship(function(name, relationship) { |
9669 | d0fe8c12 | Athina Bekakou | relationships[relationship.kind].push(name); |
9670 | d0fe8c12 | Athina Bekakou | expensiveProperties.push(name); |
9671 | d0fe8c12 | Athina Bekakou | }); |
9672 | d0fe8c12 | Athina Bekakou | |
9673 | d0fe8c12 | Athina Bekakou | var groups = [
|
9674 | d0fe8c12 | Athina Bekakou | { |
9675 | d0fe8c12 | Athina Bekakou | name: 'Attributes', |
9676 | d0fe8c12 | Athina Bekakou | properties: attributes,
|
9677 | d0fe8c12 | Athina Bekakou | expand: true |
9678 | d0fe8c12 | Athina Bekakou | }, |
9679 | d0fe8c12 | Athina Bekakou | { |
9680 | d0fe8c12 | Athina Bekakou | name: 'Belongs To', |
9681 | d0fe8c12 | Athina Bekakou | properties: relationships.belongsTo,
|
9682 | d0fe8c12 | Athina Bekakou | expand: true |
9683 | d0fe8c12 | Athina Bekakou | }, |
9684 | d0fe8c12 | Athina Bekakou | { |
9685 | d0fe8c12 | Athina Bekakou | name: 'Has Many', |
9686 | d0fe8c12 | Athina Bekakou | properties: relationships.hasMany,
|
9687 | d0fe8c12 | Athina Bekakou | expand: true |
9688 | d0fe8c12 | Athina Bekakou | }, |
9689 | d0fe8c12 | Athina Bekakou | { |
9690 | d0fe8c12 | Athina Bekakou | name: 'Flags', |
9691 | d0fe8c12 | Athina Bekakou | properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] |
9692 | d0fe8c12 | Athina Bekakou | } |
9693 | d0fe8c12 | Athina Bekakou | ]; |
9694 | d0fe8c12 | Athina Bekakou | |
9695 | d0fe8c12 | Athina Bekakou | return {
|
9696 | d0fe8c12 | Athina Bekakou | propertyInfo: {
|
9697 | d0fe8c12 | Athina Bekakou | // include all other mixins / properties (not just the grouped ones)
|
9698 | d0fe8c12 | Athina Bekakou | includeOtherProperties: true, |
9699 | d0fe8c12 | Athina Bekakou | groups: groups,
|
9700 | d0fe8c12 | Athina Bekakou | // don't pre-calculate unless cached
|
9701 | d0fe8c12 | Athina Bekakou | expensiveProperties: expensiveProperties
|
9702 | d0fe8c12 | Athina Bekakou | } |
9703 | d0fe8c12 | Athina Bekakou | }; |
9704 | d0fe8c12 | Athina Bekakou | } |
9705 | d0fe8c12 | Athina Bekakou | |
9706 | d0fe8c12 | Athina Bekakou | }); |
9707 | d0fe8c12 | Athina Bekakou | |
9708 | d0fe8c12 | Athina Bekakou | })(); |
9709 | d0fe8c12 | Athina Bekakou | |
9710 | d0fe8c12 | Athina Bekakou | |
9711 | d0fe8c12 | Athina Bekakou | |
9712 | d0fe8c12 | Athina Bekakou | (function() {
|
9713 | d0fe8c12 | Athina Bekakou | /**
|
9714 | d0fe8c12 | Athina Bekakou | @module ember-data
|
9715 | d0fe8c12 | Athina Bekakou | */
|
9716 | d0fe8c12 | Athina Bekakou | |
9717 | d0fe8c12 | Athina Bekakou | })(); |
9718 | d0fe8c12 | Athina Bekakou | |
9719 | d0fe8c12 | Athina Bekakou | |
9720 | d0fe8c12 | Athina Bekakou | |
9721 | d0fe8c12 | Athina Bekakou | (function() {
|
9722 | d0fe8c12 | Athina Bekakou | /**
|
9723 | d0fe8c12 | Athina Bekakou | Ember Data
|
9724 | d0fe8c12 | Athina Bekakou | |
9725 | d0fe8c12 | Athina Bekakou | @module ember-data
|
9726 | d0fe8c12 | Athina Bekakou | @main ember-data
|
9727 | d0fe8c12 | Athina Bekakou | */
|
9728 | d0fe8c12 | Athina Bekakou | |
9729 | d0fe8c12 | Athina Bekakou | })(); |
9730 | d0fe8c12 | Athina Bekakou | |
9731 | d0fe8c12 | Athina Bekakou | (function() {
|
9732 | d0fe8c12 | Athina Bekakou | Ember.String.pluralize = function(word) { |
9733 | d0fe8c12 | Athina Bekakou | return Ember.Inflector.inflector.pluralize(word);
|
9734 | d0fe8c12 | Athina Bekakou | }; |
9735 | d0fe8c12 | Athina Bekakou | |
9736 | d0fe8c12 | Athina Bekakou | Ember.String.singularize = function(word) { |
9737 | d0fe8c12 | Athina Bekakou | return Ember.Inflector.inflector.singularize(word);
|
9738 | d0fe8c12 | Athina Bekakou | }; |
9739 | d0fe8c12 | Athina Bekakou | |
9740 | d0fe8c12 | Athina Bekakou | })(); |
9741 | d0fe8c12 | Athina Bekakou | |
9742 | d0fe8c12 | Athina Bekakou | |
9743 | d0fe8c12 | Athina Bekakou | |
9744 | d0fe8c12 | Athina Bekakou | (function() {
|
9745 | d0fe8c12 | Athina Bekakou | var BLANK_REGEX = /^\s*$/; |
9746 | d0fe8c12 | Athina Bekakou | |
9747 | d0fe8c12 | Athina Bekakou | function loadUncountable(rules, uncountable) { |
9748 | d0fe8c12 | Athina Bekakou | for (var i = 0, length = uncountable.length; i < length; i++) { |
9749 | d0fe8c12 | Athina Bekakou | rules.uncountable[uncountable[i].toLowerCase()] = true;
|
9750 | d0fe8c12 | Athina Bekakou | } |
9751 | d0fe8c12 | Athina Bekakou | } |
9752 | d0fe8c12 | Athina Bekakou | |
9753 | d0fe8c12 | Athina Bekakou | function loadIrregular(rules, irregularPairs) { |
9754 | d0fe8c12 | Athina Bekakou | var pair;
|
9755 | d0fe8c12 | Athina Bekakou | |
9756 | d0fe8c12 | Athina Bekakou | for (var i = 0, length = irregularPairs.length; i < length; i++) { |
9757 | d0fe8c12 | Athina Bekakou | pair = irregularPairs[i]; |
9758 | d0fe8c12 | Athina Bekakou | |
9759 | d0fe8c12 | Athina Bekakou | rules.irregular[pair[0].toLowerCase()] = pair[1]; |
9760 | d0fe8c12 | Athina Bekakou | rules.irregularInverse[pair[1].toLowerCase()] = pair[0]; |
9761 | d0fe8c12 | Athina Bekakou | } |
9762 | d0fe8c12 | Athina Bekakou | } |
9763 | d0fe8c12 | Athina Bekakou | |
9764 | d0fe8c12 | Athina Bekakou | /**
|
9765 | d0fe8c12 | Athina Bekakou | Inflector.Ember provides a mechanism for supplying inflection rules for your
|
9766 | d0fe8c12 | Athina Bekakou | application. Ember includes a default set of inflection rules, and provides an
|
9767 | d0fe8c12 | Athina Bekakou | API for providing additional rules.
|
9768 | d0fe8c12 | Athina Bekakou | |
9769 | d0fe8c12 | Athina Bekakou | Examples:
|
9770 | d0fe8c12 | Athina Bekakou | |
9771 | d0fe8c12 | Athina Bekakou | Creating an inflector with no rules.
|
9772 | d0fe8c12 | Athina Bekakou | |
9773 | d0fe8c12 | Athina Bekakou | ```js
|
9774 | d0fe8c12 | Athina Bekakou | var inflector = new Ember.Inflector();
|
9775 | d0fe8c12 | Athina Bekakou | ```
|
9776 | d0fe8c12 | Athina Bekakou | |
9777 | d0fe8c12 | Athina Bekakou | Creating an inflector with the default ember ruleset.
|
9778 | d0fe8c12 | Athina Bekakou | |
9779 | d0fe8c12 | Athina Bekakou | ```js
|
9780 | d0fe8c12 | Athina Bekakou | var inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
|
9781 | d0fe8c12 | Athina Bekakou | |
9782 | d0fe8c12 | Athina Bekakou | inflector.pluralize('cow') //=> 'kine'
|
9783 | d0fe8c12 | Athina Bekakou | inflector.singularize('kine') //=> 'cow'
|
9784 | d0fe8c12 | Athina Bekakou | ```
|
9785 | d0fe8c12 | Athina Bekakou | |
9786 | d0fe8c12 | Athina Bekakou | Creating an inflector and adding rules later.
|
9787 | d0fe8c12 | Athina Bekakou | |
9788 | d0fe8c12 | Athina Bekakou | ```javascript
|
9789 | d0fe8c12 | Athina Bekakou | var inflector = Ember.Inflector.inflector;
|
9790 | d0fe8c12 | Athina Bekakou | |
9791 | d0fe8c12 | Athina Bekakou | inflector.pluralize('advice') // => 'advices'
|
9792 | d0fe8c12 | Athina Bekakou | inflector.uncountable('advice');
|
9793 | d0fe8c12 | Athina Bekakou | inflector.pluralize('advice') // => 'advice'
|
9794 | d0fe8c12 | Athina Bekakou | |
9795 | d0fe8c12 | Athina Bekakou | inflector.pluralize('formula') // => 'formulas'
|
9796 | d0fe8c12 | Athina Bekakou | inflector.irregular('formula', 'formulae');
|
9797 | d0fe8c12 | Athina Bekakou | inflector.pluralize('formula') // => 'formulae'
|
9798 | d0fe8c12 | Athina Bekakou | |
9799 | d0fe8c12 | Athina Bekakou | // you would not need to add these as they are the default rules
|
9800 | d0fe8c12 | Athina Bekakou | inflector.plural(/$/, 's');
|
9801 | d0fe8c12 | Athina Bekakou | inflector.singular(/s$/i, '');
|
9802 | d0fe8c12 | Athina Bekakou | ```
|
9803 | d0fe8c12 | Athina Bekakou | |
9804 | d0fe8c12 | Athina Bekakou | Creating an inflector with a nondefault ruleset.
|
9805 | d0fe8c12 | Athina Bekakou | |
9806 | d0fe8c12 | Athina Bekakou | ```javascript
|
9807 | d0fe8c12 | Athina Bekakou | var rules = {
|
9808 | d0fe8c12 | Athina Bekakou | plurals: [ /$/, 's' ],
|
9809 | d0fe8c12 | Athina Bekakou | singular: [ /\s$/, '' ],
|
9810 | d0fe8c12 | Athina Bekakou | irregularPairs: [
|
9811 | d0fe8c12 | Athina Bekakou | [ 'cow', 'kine' ]
|
9812 | d0fe8c12 | Athina Bekakou | ],
|
9813 | d0fe8c12 | Athina Bekakou | uncountable: [ 'fish' ]
|
9814 | d0fe8c12 | Athina Bekakou | };
|
9815 | d0fe8c12 | Athina Bekakou | |
9816 | d0fe8c12 | Athina Bekakou | var inflector = new Ember.Inflector(rules);
|
9817 | d0fe8c12 | Athina Bekakou | ```
|
9818 | d0fe8c12 | Athina Bekakou | |
9819 | d0fe8c12 | Athina Bekakou | @class Inflector
|
9820 | d0fe8c12 | Athina Bekakou | @namespace Ember
|
9821 | d0fe8c12 | Athina Bekakou | */
|
9822 | d0fe8c12 | Athina Bekakou | function Inflector(ruleSet) { |
9823 | d0fe8c12 | Athina Bekakou | ruleSet = ruleSet || {}; |
9824 | d0fe8c12 | Athina Bekakou | ruleSet.uncountable = ruleSet.uncountable || {}; |
9825 | d0fe8c12 | Athina Bekakou | ruleSet.irregularPairs = ruleSet.irregularPairs || {}; |
9826 | d0fe8c12 | Athina Bekakou | |
9827 | d0fe8c12 | Athina Bekakou | var rules = this.rules = { |
9828 | d0fe8c12 | Athina Bekakou | plurals: ruleSet.plurals || [],
|
9829 | d0fe8c12 | Athina Bekakou | singular: ruleSet.singular || [],
|
9830 | d0fe8c12 | Athina Bekakou | irregular: {},
|
9831 | d0fe8c12 | Athina Bekakou | irregularInverse: {},
|
9832 | d0fe8c12 | Athina Bekakou | uncountable: {}
|
9833 | d0fe8c12 | Athina Bekakou | }; |
9834 | d0fe8c12 | Athina Bekakou | |
9835 | d0fe8c12 | Athina Bekakou | loadUncountable(rules, ruleSet.uncountable); |
9836 | d0fe8c12 | Athina Bekakou | loadIrregular(rules, ruleSet.irregularPairs); |
9837 | d0fe8c12 | Athina Bekakou | } |
9838 | d0fe8c12 | Athina Bekakou | |
9839 | d0fe8c12 | Athina Bekakou | Inflector.prototype = { |
9840 | d0fe8c12 | Athina Bekakou | /**
|
9841 | d0fe8c12 | Athina Bekakou | @method plural
|
9842 | d0fe8c12 | Athina Bekakou | @param {RegExp} regex
|
9843 | d0fe8c12 | Athina Bekakou | @param {String} string
|
9844 | d0fe8c12 | Athina Bekakou | */
|
9845 | d0fe8c12 | Athina Bekakou | plural: function(regex, string) { |
9846 | d0fe8c12 | Athina Bekakou | this.rules.plurals.push([regex, string.toLowerCase()]);
|
9847 | d0fe8c12 | Athina Bekakou | }, |
9848 | d0fe8c12 | Athina Bekakou | |
9849 | d0fe8c12 | Athina Bekakou | /**
|
9850 | d0fe8c12 | Athina Bekakou | @method singular
|
9851 | d0fe8c12 | Athina Bekakou | @param {RegExp} regex
|
9852 | d0fe8c12 | Athina Bekakou | @param {String} string
|
9853 | d0fe8c12 | Athina Bekakou | */
|
9854 | d0fe8c12 | Athina Bekakou | singular: function(regex, string) { |
9855 | d0fe8c12 | Athina Bekakou | this.rules.singular.push([regex, string.toLowerCase()]);
|
9856 | d0fe8c12 | Athina Bekakou | }, |
9857 | d0fe8c12 | Athina Bekakou | |
9858 | d0fe8c12 | Athina Bekakou | /**
|
9859 | d0fe8c12 | Athina Bekakou | @method uncountable
|
9860 | d0fe8c12 | Athina Bekakou | @param {String} regex
|
9861 | d0fe8c12 | Athina Bekakou | */
|
9862 | d0fe8c12 | Athina Bekakou | uncountable: function(string) { |
9863 | d0fe8c12 | Athina Bekakou | loadUncountable(this.rules, [string.toLowerCase()]);
|
9864 | d0fe8c12 | Athina Bekakou | }, |
9865 | d0fe8c12 | Athina Bekakou | |
9866 | d0fe8c12 | Athina Bekakou | /**
|
9867 | d0fe8c12 | Athina Bekakou | @method irregular
|
9868 | d0fe8c12 | Athina Bekakou | @param {String} singular
|
9869 | d0fe8c12 | Athina Bekakou | @param {String} plural
|
9870 | d0fe8c12 | Athina Bekakou | */
|
9871 | d0fe8c12 | Athina Bekakou | irregular: function (singular, plural) { |
9872 | d0fe8c12 | Athina Bekakou | loadIrregular(this.rules, [[singular, plural]]);
|
9873 | d0fe8c12 | Athina Bekakou | }, |
9874 | d0fe8c12 | Athina Bekakou | |
9875 | d0fe8c12 | Athina Bekakou | /**
|
9876 | d0fe8c12 | Athina Bekakou | @method pluralize
|
9877 | d0fe8c12 | Athina Bekakou | @param {String} word
|
9878 | d0fe8c12 | Athina Bekakou | */
|
9879 | d0fe8c12 | Athina Bekakou | pluralize: function(word) { |
9880 | d0fe8c12 | Athina Bekakou | return this.inflect(word, this.rules.plurals, this.rules.irregular); |
9881 | d0fe8c12 | Athina Bekakou | }, |
9882 | d0fe8c12 | Athina Bekakou | |
9883 | d0fe8c12 | Athina Bekakou | /**
|
9884 | d0fe8c12 | Athina Bekakou | @method singularize
|
9885 | d0fe8c12 | Athina Bekakou | @param {String} word
|
9886 | d0fe8c12 | Athina Bekakou | */
|
9887 | d0fe8c12 | Athina Bekakou | singularize: function(word) { |
9888 | d0fe8c12 | Athina Bekakou | return this.inflect(word, this.rules.singular, this.rules.irregularInverse); |
9889 | d0fe8c12 | Athina Bekakou | }, |
9890 | d0fe8c12 | Athina Bekakou | |
9891 | d0fe8c12 | Athina Bekakou | /**
|
9892 | d0fe8c12 | Athina Bekakou | @protected
|
9893 | d0fe8c12 | Athina Bekakou | |
9894 | d0fe8c12 | Athina Bekakou | @method inflect
|
9895 | d0fe8c12 | Athina Bekakou | @param {String} word
|
9896 | d0fe8c12 | Athina Bekakou | @param {Object} typeRules
|
9897 | d0fe8c12 | Athina Bekakou | @param {Object} irregular
|
9898 | d0fe8c12 | Athina Bekakou | */
|
9899 | d0fe8c12 | Athina Bekakou | inflect: function(word, typeRules, irregular) { |
9900 | d0fe8c12 | Athina Bekakou | var inflection, substitution, result, lowercase, isBlank,
|
9901 | d0fe8c12 | Athina Bekakou | isUncountable, isIrregular, isIrregularInverse, rule; |
9902 | d0fe8c12 | Athina Bekakou | |
9903 | d0fe8c12 | Athina Bekakou | isBlank = BLANK_REGEX.test(word); |
9904 | d0fe8c12 | Athina Bekakou | |
9905 | d0fe8c12 | Athina Bekakou | if (isBlank) {
|
9906 | d0fe8c12 | Athina Bekakou | return word;
|
9907 | d0fe8c12 | Athina Bekakou | } |
9908 | d0fe8c12 | Athina Bekakou | |
9909 | d0fe8c12 | Athina Bekakou | lowercase = word.toLowerCase(); |
9910 | d0fe8c12 | Athina Bekakou | |
9911 | d0fe8c12 | Athina Bekakou | isUncountable = this.rules.uncountable[lowercase];
|
9912 | d0fe8c12 | Athina Bekakou | |
9913 | d0fe8c12 | Athina Bekakou | if (isUncountable) {
|
9914 | d0fe8c12 | Athina Bekakou | return word;
|
9915 | d0fe8c12 | Athina Bekakou | } |
9916 | d0fe8c12 | Athina Bekakou | |
9917 | d0fe8c12 | Athina Bekakou | isIrregular = irregular && irregular[lowercase]; |
9918 | d0fe8c12 | Athina Bekakou | |
9919 | d0fe8c12 | Athina Bekakou | if (isIrregular) {
|
9920 | d0fe8c12 | Athina Bekakou | return isIrregular;
|
9921 | d0fe8c12 | Athina Bekakou | } |
9922 | d0fe8c12 | Athina Bekakou | |
9923 | d0fe8c12 | Athina Bekakou | for (var i = typeRules.length, min = 0; i > min; i--) { |
9924 | d0fe8c12 | Athina Bekakou | inflection = typeRules[i-1];
|
9925 | d0fe8c12 | Athina Bekakou | rule = inflection[0];
|
9926 | d0fe8c12 | Athina Bekakou | |
9927 | d0fe8c12 | Athina Bekakou | if (rule.test(word)) {
|
9928 | d0fe8c12 | Athina Bekakou | break;
|
9929 | d0fe8c12 | Athina Bekakou | } |
9930 | d0fe8c12 | Athina Bekakou | } |
9931 | d0fe8c12 | Athina Bekakou | |
9932 | d0fe8c12 | Athina Bekakou | inflection = inflection || []; |
9933 | d0fe8c12 | Athina Bekakou | |
9934 | d0fe8c12 | Athina Bekakou | rule = inflection[0];
|
9935 | d0fe8c12 | Athina Bekakou | substitution = inflection[1];
|
9936 | d0fe8c12 | Athina Bekakou | |
9937 | d0fe8c12 | Athina Bekakou | result = word.replace(rule, substitution); |
9938 | d0fe8c12 | Athina Bekakou | |
9939 | d0fe8c12 | Athina Bekakou | return result;
|
9940 | d0fe8c12 | Athina Bekakou | } |
9941 | d0fe8c12 | Athina Bekakou | }; |
9942 | d0fe8c12 | Athina Bekakou | |
9943 | d0fe8c12 | Athina Bekakou | Ember.Inflector = Inflector; |
9944 | d0fe8c12 | Athina Bekakou | |
9945 | d0fe8c12 | Athina Bekakou | })(); |
9946 | d0fe8c12 | Athina Bekakou | |
9947 | d0fe8c12 | Athina Bekakou | |
9948 | d0fe8c12 | Athina Bekakou | |
9949 | d0fe8c12 | Athina Bekakou | (function() {
|
9950 | d0fe8c12 | Athina Bekakou | Ember.Inflector.defaultRules = { |
9951 | d0fe8c12 | Athina Bekakou | plurals: [
|
9952 | d0fe8c12 | Athina Bekakou | [/$/, 's'], |
9953 | d0fe8c12 | Athina Bekakou | [/s$/i, 's'], |
9954 | d0fe8c12 | Athina Bekakou | [/^(ax|test)is$/i, '$1es'], |
9955 | d0fe8c12 | Athina Bekakou | [/(octop|vir)us$/i, '$1i'], |
9956 | d0fe8c12 | Athina Bekakou | [/(octop|vir)i$/i, '$1i'], |
9957 | d0fe8c12 | Athina Bekakou | [/(alias|status)$/i, '$1es'], |
9958 | d0fe8c12 | Athina Bekakou | [/(bu)s$/i, '$1ses'], |
9959 | d0fe8c12 | Athina Bekakou | [/(buffal|tomat)o$/i, '$1oes'], |
9960 | d0fe8c12 | Athina Bekakou | [/([ti])um$/i, '$1a'], |
9961 | d0fe8c12 | Athina Bekakou | [/([ti])a$/i, '$1a'], |
9962 | d0fe8c12 | Athina Bekakou | [/sis$/i, 'ses'], |
9963 | d0fe8c12 | Athina Bekakou | [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'], |
9964 | d0fe8c12 | Athina Bekakou | [/(hive)$/i, '$1s'], |
9965 | d0fe8c12 | Athina Bekakou | [/([^aeiouy]|qu)y$/i, '$1ies'], |
9966 | d0fe8c12 | Athina Bekakou | [/(x|ch|ss|sh)$/i, '$1es'], |
9967 | d0fe8c12 | Athina Bekakou | [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'], |
9968 | d0fe8c12 | Athina Bekakou | [/^(m|l)ouse$/i, '$1ice'], |
9969 | d0fe8c12 | Athina Bekakou | [/^(m|l)ice$/i, '$1ice'], |
9970 | d0fe8c12 | Athina Bekakou | [/^(ox)$/i, '$1en'], |
9971 | d0fe8c12 | Athina Bekakou | [/^(oxen)$/i, '$1'], |
9972 | d0fe8c12 | Athina Bekakou | [/(quiz)$/i, '$1zes'] |
9973 | d0fe8c12 | Athina Bekakou | ], |
9974 | d0fe8c12 | Athina Bekakou | |
9975 | d0fe8c12 | Athina Bekakou | singular: [
|
9976 | d0fe8c12 | Athina Bekakou | [/s$/i, ''], |
9977 | d0fe8c12 | Athina Bekakou | [/(ss)$/i, '$1'], |
9978 | d0fe8c12 | Athina Bekakou | [/(n)ews$/i, '$1ews'], |
9979 | d0fe8c12 | Athina Bekakou | [/([ti])a$/i, '$1um'], |
9980 | d0fe8c12 | Athina Bekakou | [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'], |
9981 | d0fe8c12 | Athina Bekakou | [/(^analy)(sis|ses)$/i, '$1sis'], |
9982 | d0fe8c12 | Athina Bekakou | [/([^f])ves$/i, '$1fe'], |
9983 | d0fe8c12 | Athina Bekakou | [/(hive)s$/i, '$1'], |
9984 | d0fe8c12 | Athina Bekakou | [/(tive)s$/i, '$1'], |
9985 | d0fe8c12 | Athina Bekakou | [/([lr])ves$/i, '$1f'], |
9986 | d0fe8c12 | Athina Bekakou | [/([^aeiouy]|qu)ies$/i, '$1y'], |
9987 | d0fe8c12 | Athina Bekakou | [/(s)eries$/i, '$1eries'], |
9988 | d0fe8c12 | Athina Bekakou | [/(m)ovies$/i, '$1ovie'], |
9989 | d0fe8c12 | Athina Bekakou | [/(x|ch|ss|sh)es$/i, '$1'], |
9990 | d0fe8c12 | Athina Bekakou | [/^(m|l)ice$/i, '$1ouse'], |
9991 | d0fe8c12 | Athina Bekakou | [/(bus)(es)?$/i, '$1'], |
9992 | d0fe8c12 | Athina Bekakou | [/(o)es$/i, '$1'], |
9993 | d0fe8c12 | Athina Bekakou | [/(shoe)s$/i, '$1'], |
9994 | d0fe8c12 | Athina Bekakou | [/(cris|test)(is|es)$/i, '$1is'], |
9995 | d0fe8c12 | Athina Bekakou | [/^(a)x[ie]s$/i, '$1xis'], |
9996 | d0fe8c12 | Athina Bekakou | [/(octop|vir)(us|i)$/i, '$1us'], |
9997 | d0fe8c12 | Athina Bekakou | [/(alias|status)(es)?$/i, '$1'], |
9998 | d0fe8c12 | Athina Bekakou | [/^(ox)en/i, '$1'], |
9999 | d0fe8c12 | Athina Bekakou | [/(vert|ind)ices$/i, '$1ex'], |
10000 | d0fe8c12 | Athina Bekakou | [/(matr)ices$/i, '$1ix'], |
10001 | d0fe8c12 | Athina Bekakou | [/(quiz)zes$/i, '$1'], |
10002 | d0fe8c12 | Athina Bekakou | [/(database)s$/i, '$1'] |
10003 | d0fe8c12 | Athina Bekakou | ], |
10004 | d0fe8c12 | Athina Bekakou | |
10005 | d0fe8c12 | Athina Bekakou | irregularPairs: [
|
10006 | d0fe8c12 | Athina Bekakou | ['person', 'people'], |
10007 | d0fe8c12 | Athina Bekakou | ['man', 'men'], |
10008 | d0fe8c12 | Athina Bekakou | ['child', 'children'], |
10009 | d0fe8c12 | Athina Bekakou | ['sex', 'sexes'], |
10010 | d0fe8c12 | Athina Bekakou | ['move', 'moves'], |
10011 | d0fe8c12 | Athina Bekakou | ['cow', 'kine'], |
10012 | d0fe8c12 | Athina Bekakou | ['zombie', 'zombies'] |
10013 | d0fe8c12 | Athina Bekakou | ], |
10014 | d0fe8c12 | Athina Bekakou | |
10015 | d0fe8c12 | Athina Bekakou | uncountable: [
|
10016 | d0fe8c12 | Athina Bekakou | 'equipment',
|
10017 | d0fe8c12 | Athina Bekakou | 'information',
|
10018 | d0fe8c12 | Athina Bekakou | 'rice',
|
10019 | d0fe8c12 | Athina Bekakou | 'money',
|
10020 | d0fe8c12 | Athina Bekakou | 'species',
|
10021 | d0fe8c12 | Athina Bekakou | 'series',
|
10022 | d0fe8c12 | Athina Bekakou | 'fish',
|
10023 | d0fe8c12 | Athina Bekakou | 'sheep',
|
10024 | d0fe8c12 | Athina Bekakou | 'jeans',
|
10025 | d0fe8c12 | Athina Bekakou | 'police'
|
10026 | d0fe8c12 | Athina Bekakou | ] |
10027 | d0fe8c12 | Athina Bekakou | }; |
10028 | d0fe8c12 | Athina Bekakou | |
10029 | d0fe8c12 | Athina Bekakou | })(); |
10030 | d0fe8c12 | Athina Bekakou | |
10031 | d0fe8c12 | Athina Bekakou | |
10032 | d0fe8c12 | Athina Bekakou | |
10033 | d0fe8c12 | Athina Bekakou | (function() {
|
10034 | d0fe8c12 | Athina Bekakou | if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { |
10035 | d0fe8c12 | Athina Bekakou | /**
|
10036 | d0fe8c12 | Athina Bekakou | See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
|
10037 | d0fe8c12 | Athina Bekakou | |
10038 | d0fe8c12 | Athina Bekakou | @method pluralize
|
10039 | d0fe8c12 | Athina Bekakou | @for String
|
10040 | d0fe8c12 | Athina Bekakou | */
|
10041 | d0fe8c12 | Athina Bekakou | String.prototype.pluralize = function() { |
10042 | d0fe8c12 | Athina Bekakou | return Ember.String.pluralize(this); |
10043 | d0fe8c12 | Athina Bekakou | }; |
10044 | d0fe8c12 | Athina Bekakou | |
10045 | d0fe8c12 | Athina Bekakou | /**
|
10046 | d0fe8c12 | Athina Bekakou | See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
|
10047 | d0fe8c12 | Athina Bekakou | |
10048 | d0fe8c12 | Athina Bekakou | @method singularize
|
10049 | d0fe8c12 | Athina Bekakou | @for String
|
10050 | d0fe8c12 | Athina Bekakou | */
|
10051 | d0fe8c12 | Athina Bekakou | String.prototype.singularize = function() { |
10052 | d0fe8c12 | Athina Bekakou | return Ember.String.singularize(this); |
10053 | d0fe8c12 | Athina Bekakou | }; |
10054 | d0fe8c12 | Athina Bekakou | } |
10055 | d0fe8c12 | Athina Bekakou | |
10056 | d0fe8c12 | Athina Bekakou | })(); |
10057 | d0fe8c12 | Athina Bekakou | |
10058 | d0fe8c12 | Athina Bekakou | |
10059 | d0fe8c12 | Athina Bekakou | |
10060 | d0fe8c12 | Athina Bekakou | (function() {
|
10061 | d0fe8c12 | Athina Bekakou | Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
|
10062 | d0fe8c12 | Athina Bekakou | |
10063 | d0fe8c12 | Athina Bekakou | })(); |
10064 | d0fe8c12 | Athina Bekakou | |
10065 | d0fe8c12 | Athina Bekakou | |
10066 | d0fe8c12 | Athina Bekakou | |
10067 | d0fe8c12 | Athina Bekakou | (function() {
|
10068 | d0fe8c12 | Athina Bekakou | |
10069 | d0fe8c12 | Athina Bekakou | })(); |
10070 | d0fe8c12 | Athina Bekakou | |
10071 | d0fe8c12 | Athina Bekakou | (function() {
|
10072 | d0fe8c12 | Athina Bekakou | /**
|
10073 | d0fe8c12 | Athina Bekakou | @module ember-data
|
10074 | d0fe8c12 | Athina Bekakou | */
|
10075 | d0fe8c12 | Athina Bekakou | |
10076 | d0fe8c12 | Athina Bekakou | var get = Ember.get;
|
10077 | d0fe8c12 | Athina Bekakou | var forEach = Ember.EnumerableUtils.forEach;
|
10078 | d0fe8c12 | Athina Bekakou | |
10079 | d0fe8c12 | Athina Bekakou | DS.ActiveModelSerializer = DS.RESTSerializer.extend({ |
10080 | d0fe8c12 | Athina Bekakou | // SERIALIZE
|
10081 | d0fe8c12 | Athina Bekakou | |
10082 | d0fe8c12 | Athina Bekakou | /**
|
10083 | d0fe8c12 | Athina Bekakou | Converts camelcased attributes to underscored when serializing.
|
10084 | d0fe8c12 | Athina Bekakou | |
10085 | d0fe8c12 | Athina Bekakou | @method keyForAttribute
|
10086 | d0fe8c12 | Athina Bekakou | @param {String} attribute
|
10087 | d0fe8c12 | Athina Bekakou | @returns String
|
10088 | d0fe8c12 | Athina Bekakou | */
|
10089 | d0fe8c12 | Athina Bekakou | keyForAttribute: function(attr) { |
10090 | d0fe8c12 | Athina Bekakou | return Ember.String.decamelize(attr);
|
10091 | d0fe8c12 | Athina Bekakou | }, |
10092 | d0fe8c12 | Athina Bekakou | |
10093 | d0fe8c12 | Athina Bekakou | /**
|
10094 | d0fe8c12 | Athina Bekakou | Underscores relationship names and appends "_id" or "_ids" when serializing
|
10095 | d0fe8c12 | Athina Bekakou | relationship keys.
|
10096 | d0fe8c12 | Athina Bekakou | |
10097 | d0fe8c12 | Athina Bekakou | @method keyForRelationship
|
10098 | d0fe8c12 | Athina Bekakou | @param {String} key
|
10099 | d0fe8c12 | Athina Bekakou | @param {String} kind
|
10100 | d0fe8c12 | Athina Bekakou | @returns String
|
10101 | d0fe8c12 | Athina Bekakou | */
|
10102 | d0fe8c12 | Athina Bekakou | keyForRelationship: function(key, kind) { |
10103 | d0fe8c12 | Athina Bekakou | key = Ember.String.decamelize(key); |
10104 | d0fe8c12 | Athina Bekakou | if (kind === "belongsTo") { |
10105 | d0fe8c12 | Athina Bekakou | return key + "_id"; |
10106 | d0fe8c12 | Athina Bekakou | } else if (kind === "hasMany") { |
10107 | d0fe8c12 | Athina Bekakou | return Ember.String.singularize(key) + "_ids"; |
10108 | d0fe8c12 | Athina Bekakou | } else {
|
10109 | d0fe8c12 | Athina Bekakou | return key;
|
10110 | d0fe8c12 | Athina Bekakou | } |
10111 | d0fe8c12 | Athina Bekakou | }, |
10112 | d0fe8c12 | Athina Bekakou | |
10113 | d0fe8c12 | Athina Bekakou | /**
|
10114 | d0fe8c12 | Athina Bekakou | Does not serialize hasMany relationships by default.
|
10115 | d0fe8c12 | Athina Bekakou | */
|
10116 | d0fe8c12 | Athina Bekakou | serializeHasMany: Ember.K,
|
10117 | d0fe8c12 | Athina Bekakou | |
10118 | d0fe8c12 | Athina Bekakou | /**
|
10119 | d0fe8c12 | Athina Bekakou | Underscores the JSON root keys when serializing.
|
10120 | d0fe8c12 | Athina Bekakou | |
10121 | d0fe8c12 | Athina Bekakou | @method serializeIntoHash
|
10122 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
10123 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
10124 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
10125 | d0fe8c12 | Athina Bekakou | @param {Object} options
|
10126 | d0fe8c12 | Athina Bekakou | */
|
10127 | d0fe8c12 | Athina Bekakou | serializeIntoHash: function(data, type, record, options) { |
10128 | d0fe8c12 | Athina Bekakou | var root = Ember.String.decamelize(type.typeKey);
|
10129 | d0fe8c12 | Athina Bekakou | data[root] = this.serialize(record, options);
|
10130 | d0fe8c12 | Athina Bekakou | }, |
10131 | d0fe8c12 | Athina Bekakou | |
10132 | d0fe8c12 | Athina Bekakou | /**
|
10133 | d0fe8c12 | Athina Bekakou | Serializes a polymorphic type as a fully capitalized model name.
|
10134 | d0fe8c12 | Athina Bekakou | |
10135 | d0fe8c12 | Athina Bekakou | @method serializePolymorphicType
|
10136 | d0fe8c12 | Athina Bekakou | @param {DS.Model} record
|
10137 | d0fe8c12 | Athina Bekakou | @param {Object} json
|
10138 | d0fe8c12 | Athina Bekakou | @param relationship
|
10139 | d0fe8c12 | Athina Bekakou | */
|
10140 | d0fe8c12 | Athina Bekakou | serializePolymorphicType: function(record, json, relationship) { |
10141 | d0fe8c12 | Athina Bekakou | var key = relationship.key,
|
10142 | d0fe8c12 | Athina Bekakou | belongsTo = get(record, key); |
10143 | d0fe8c12 | Athina Bekakou | key = this.keyForAttribute(key);
|
10144 | d0fe8c12 | Athina Bekakou | json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
|
10145 | d0fe8c12 | Athina Bekakou | }, |
10146 | d0fe8c12 | Athina Bekakou | |
10147 | d0fe8c12 | Athina Bekakou | // EXTRACT
|
10148 | d0fe8c12 | Athina Bekakou | |
10149 | d0fe8c12 | Athina Bekakou | /**
|
10150 | d0fe8c12 | Athina Bekakou | Extracts the model typeKey from underscored root objects.
|
10151 | d0fe8c12 | Athina Bekakou | |
10152 | d0fe8c12 | Athina Bekakou | @method typeForRoot
|
10153 | d0fe8c12 | Athina Bekakou | @param {String} root
|
10154 | d0fe8c12 | Athina Bekakou | @returns String the model's typeKey
|
10155 | d0fe8c12 | Athina Bekakou | */
|
10156 | d0fe8c12 | Athina Bekakou | typeForRoot: function(root) { |
10157 | d0fe8c12 | Athina Bekakou | var camelized = Ember.String.camelize(root);
|
10158 | d0fe8c12 | Athina Bekakou | return Ember.String.singularize(camelized);
|
10159 | d0fe8c12 | Athina Bekakou | }, |
10160 | d0fe8c12 | Athina Bekakou | |
10161 | d0fe8c12 | Athina Bekakou | /**
|
10162 | d0fe8c12 | Athina Bekakou | Add extra step to `DS.RESTSerializer.normalize` so links are
|
10163 | d0fe8c12 | Athina Bekakou | normalized.
|
10164 | d0fe8c12 | Athina Bekakou | |
10165 | d0fe8c12 | Athina Bekakou | If your payload looks like this
|
10166 | d0fe8c12 | Athina Bekakou | |
10167 | d0fe8c12 | Athina Bekakou | ```js
|
10168 | d0fe8c12 | Athina Bekakou | {
|
10169 | d0fe8c12 | Athina Bekakou | "post": {
|
10170 | d0fe8c12 | Athina Bekakou | "id": 1,
|
10171 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
10172 | d0fe8c12 | Athina Bekakou | "links": { "flagged_comments": "api/comments/flagged" }
|
10173 | d0fe8c12 | Athina Bekakou | }
|
10174 | d0fe8c12 | Athina Bekakou | }
|
10175 | d0fe8c12 | Athina Bekakou | ```
|
10176 | d0fe8c12 | Athina Bekakou | The normalized version would look like this
|
10177 | d0fe8c12 | Athina Bekakou | |
10178 | d0fe8c12 | Athina Bekakou | ```js
|
10179 | d0fe8c12 | Athina Bekakou | {
|
10180 | d0fe8c12 | Athina Bekakou | "post": {
|
10181 | d0fe8c12 | Athina Bekakou | "id": 1,
|
10182 | d0fe8c12 | Athina Bekakou | "title": "Rails is omakase",
|
10183 | d0fe8c12 | Athina Bekakou | "links": { "flaggedComments": "api/comments/flagged" }
|
10184 | d0fe8c12 | Athina Bekakou | }
|
10185 | d0fe8c12 | Athina Bekakou | }
|
10186 | d0fe8c12 | Athina Bekakou | ```
|
10187 | d0fe8c12 | Athina Bekakou | |
10188 | d0fe8c12 | Athina Bekakou | @method normalize
|
10189 | d0fe8c12 | Athina Bekakou | @param {subclass of DS.Model} type
|
10190 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
10191 | d0fe8c12 | Athina Bekakou | @param {String} prop
|
10192 | d0fe8c12 | Athina Bekakou | @returns Object
|
10193 | d0fe8c12 | Athina Bekakou | */
|
10194 | d0fe8c12 | Athina Bekakou | |
10195 | d0fe8c12 | Athina Bekakou | normalize: function(type, hash, prop) { |
10196 | d0fe8c12 | Athina Bekakou | this.normalizeLinks(hash);
|
10197 | d0fe8c12 | Athina Bekakou | |
10198 | d0fe8c12 | Athina Bekakou | return this._super(type, hash, prop); |
10199 | d0fe8c12 | Athina Bekakou | }, |
10200 | d0fe8c12 | Athina Bekakou | |
10201 | d0fe8c12 | Athina Bekakou | /**
|
10202 | d0fe8c12 | Athina Bekakou | Convert `snake_cased` links to `camelCase`
|
10203 | d0fe8c12 | Athina Bekakou | |
10204 | d0fe8c12 | Athina Bekakou | @method normalizeLinks
|
10205 | d0fe8c12 | Athina Bekakou | @param {Object} hash
|
10206 | d0fe8c12 | Athina Bekakou | */
|
10207 | d0fe8c12 | Athina Bekakou | |
10208 | d0fe8c12 | Athina Bekakou | normalizeLinks: function(data){ |
10209 | d0fe8c12 | Athina Bekakou | if (data.links) {
|
10210 | d0fe8c12 | Athina Bekakou | var links = data.links;
|
10211 | d0fe8c12 | Athina Bekakou | |
10212 | d0fe8c12 | Athina Bekakou | for (var link in links) { |
10213 | d0fe8c12 | Athina Bekakou | var camelizedLink = Ember.String.camelize(link);
|
10214 | d0fe8c12 | Athina Bekakou | |
10215 | d0fe8c12 | Athina Bekakou | if (camelizedLink !== link) {
|
10216 | d0fe8c12 | Athina Bekakou | links[camelizedLink] = links[link]; |
10217 | d0fe8c12 | Athina Bekakou | delete links[link];
|
10218 | d0fe8c12 | Athina Bekakou | } |
10219 | d0fe8c12 | Athina Bekakou | } |
10220 | d0fe8c12 | Athina Bekakou | } |
10221 | d0fe8c12 | Athina Bekakou | }, |
10222 | d0fe8c12 | Athina Bekakou | |
10223 | d0fe8c12 | Athina Bekakou | /**
|
10224 | d0fe8c12 | Athina Bekakou | Normalize the polymorphic type from the JSON.
|
10225 | d0fe8c12 | Athina Bekakou | |
10226 | d0fe8c12 | Athina Bekakou | Normalize:
|
10227 | d0fe8c12 | Athina Bekakou | ```js
|
10228 | d0fe8c12 | Athina Bekakou | {
|
10229 | d0fe8c12 | Athina Bekakou | id: "1"
|
10230 | d0fe8c12 | Athina Bekakou | minion: { type: "evil_minion", id: "12"}
|
10231 | d0fe8c12 | Athina Bekakou | }
|
10232 | d0fe8c12 | Athina Bekakou | ```
|
10233 | d0fe8c12 | Athina Bekakou | |
10234 | d0fe8c12 | Athina Bekakou | To:
|
10235 | d0fe8c12 | Athina Bekakou | ```js
|
10236 | d0fe8c12 | Athina Bekakou | {
|
10237 | d0fe8c12 | Athina Bekakou | id: "1"
|
10238 | d0fe8c12 | Athina Bekakou | minion: { type: "evilMinion", id: "12"}
|
10239 | d0fe8c12 | Athina Bekakou | }
|
10240 | d0fe8c12 | Athina Bekakou | ```
|
10241 | d0fe8c12 | Athina Bekakou | |
10242 | d0fe8c12 | Athina Bekakou | @method normalizeRelationships
|
10243 | d0fe8c12 | Athina Bekakou | @private
|
10244 | d0fe8c12 | Athina Bekakou | */
|
10245 | d0fe8c12 | Athina Bekakou | normalizeRelationships: function(type, hash) { |
10246 | d0fe8c12 | Athina Bekakou | var payloadKey, payload;
|
10247 | d0fe8c12 | Athina Bekakou | |
10248 | d0fe8c12 | Athina Bekakou | if (this.keyForRelationship) { |
10249 | d0fe8c12 | Athina Bekakou | type.eachRelationship(function(key, relationship) {
|
10250 | d0fe8c12 | Athina Bekakou | if (relationship.options.polymorphic) {
|
10251 | d0fe8c12 | Athina Bekakou | payloadKey = this.keyForAttribute(key);
|
10252 | d0fe8c12 | Athina Bekakou | payload = hash[payloadKey]; |
10253 | d0fe8c12 | Athina Bekakou | if (payload && payload.type) {
|
10254 | d0fe8c12 | Athina Bekakou | payload.type = this.typeForRoot(payload.type);
|
10255 | d0fe8c12 | Athina Bekakou | } else if (payload && relationship.kind === "hasMany") { |
10256 | d0fe8c12 | Athina Bekakou | var self = this; |
10257 | d0fe8c12 | Athina Bekakou | forEach(payload, function(single) {
|
10258 | d0fe8c12 | Athina Bekakou | single.type = self.typeForRoot(single.type); |
10259 | d0fe8c12 | Athina Bekakou | }); |
10260 | d0fe8c12 | Athina Bekakou | } |
10261 | d0fe8c12 | Athina Bekakou | } else {
|
10262 | d0fe8c12 | Athina Bekakou | payloadKey = this.keyForRelationship(key, relationship.kind);
|
10263 | d0fe8c12 | Athina Bekakou | payload = hash[payloadKey]; |
10264 | d0fe8c12 | Athina Bekakou | } |
10265 | d0fe8c12 | Athina Bekakou | |
10266 | d0fe8c12 | Athina Bekakou | hash[key] = payload; |
10267 | d0fe8c12 | Athina Bekakou | |
10268 | d0fe8c12 | Athina Bekakou | if (key !== payloadKey) {
|
10269 | d0fe8c12 | Athina Bekakou | delete hash[payloadKey];
|
10270 | d0fe8c12 | Athina Bekakou | } |
10271 | d0fe8c12 | Athina Bekakou | }, this);
|
10272 | d0fe8c12 | Athina Bekakou | } |
10273 | d0fe8c12 | Athina Bekakou | } |
10274 | d0fe8c12 | Athina Bekakou | }); |
10275 | d0fe8c12 | Athina Bekakou | |
10276 | d0fe8c12 | Athina Bekakou | })(); |
10277 | d0fe8c12 | Athina Bekakou | |
10278 | d0fe8c12 | Athina Bekakou | |
10279 | d0fe8c12 | Athina Bekakou | |
10280 | d0fe8c12 | Athina Bekakou | (function() {
|
10281 | d0fe8c12 | Athina Bekakou | var get = Ember.get;
|
10282 | d0fe8c12 | Athina Bekakou | var forEach = Ember.EnumerableUtils.forEach;
|
10283 | d0fe8c12 | Athina Bekakou | |
10284 | d0fe8c12 | Athina Bekakou | /**
|
10285 | d0fe8c12 | Athina Bekakou | The EmbeddedRecordsMixin allows you to add embedded record support to your
|
10286 | d0fe8c12 | Athina Bekakou | serializers.
|
10287 | d0fe8c12 | Athina Bekakou | To set up embedded records, you include the mixin into the serializer and then
|
10288 | d0fe8c12 | Athina Bekakou | define your embedded relations.
|
10289 | d0fe8c12 | Athina Bekakou | |
10290 | d0fe8c12 | Athina Bekakou | ```js
|
10291 | d0fe8c12 | Athina Bekakou | App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
|
10292 | d0fe8c12 | Athina Bekakou | attrs: {
|
10293 | d0fe8c12 | Athina Bekakou | comments: {embedded: 'always'}
|
10294 | d0fe8c12 | Athina Bekakou | }
|
10295 | d0fe8c12 | Athina Bekakou | })
|
10296 | d0fe8c12 | Athina Bekakou | ```
|
10297 | d0fe8c12 | Athina Bekakou | |
10298 | d0fe8c12 | Athina Bekakou | Currently only `{embedded: 'always'}` records are supported.
|
10299 | d0fe8c12 | Athina Bekakou | |
10300 | d0fe8c12 | Athina Bekakou | @class EmbeddedRecordsMixin
|
10301 | d0fe8c12 | Athina Bekakou | @namespace DS
|
10302 | d0fe8c12 | Athina Bekakou | */
|
10303 | d0fe8c12 | Athina Bekakou | DS.EmbeddedRecordsMixin = Ember.Mixin.create({ |
10304 | d0fe8c12 | Athina Bekakou | |
10305 | d0fe8c12 | Athina Bekakou | /**
|
10306 | d0fe8c12 | Athina Bekakou | Serialize has-may relationship when it is configured as embedded objects.
|
10307 | d0fe8c12 | Athina Bekakou | |
10308 | d0fe8c12 | Athina Bekakou | @method serializeHasMany
|
10309 | d0fe8c12 | Athina Bekakou | */
|
10310 | d0fe8c12 | Athina Bekakou | serializeHasMany: function(record, json, relationship) { |
10311 | d0fe8c12 | Athina Bekakou | var key = relationship.key,
|
10312 | d0fe8c12 | Athina Bekakou | attrs = get(this, 'attrs'), |
10313 | d0fe8c12 | Athina Bekakou | embed = attrs && attrs[key] && attrs[key].embedded === 'always';
|
10314 | d0fe8c12 | Athina Bekakou | |
10315 | d0fe8c12 | Athina Bekakou | if (embed) {
|
10316 | d0fe8c12 | Athina Bekakou | json[this.keyForAttribute(key)] = get(record, key).map(function(relation) { |
10317 | d0fe8c12 | Athina Bekakou | var data = relation.serialize(),
|
10318 | d0fe8c12 | Athina Bekakou | primaryKey = get(this, 'primaryKey'); |
10319 | d0fe8c12 | Athina Bekakou | |
10320 | d0fe8c12 | Athina Bekakou | data[primaryKey] = get(relation, primaryKey); |
10321 | d0fe8c12 | Athina Bekakou | |
10322 | d0fe8c12 | Athina Bekakou | return data;
|
10323 | d0fe8c12 | Athina Bekakou | }, this);
|
10324 | d0fe8c12 | Athina Bekakou | } |
10325 | d0fe8c12 | Athina Bekakou | }, |
10326 | d0fe8c12 | Athina Bekakou | |
10327 | d0fe8c12 | Athina Bekakou | /**
|
10328 | d0fe8c12 | Athina Bekakou | Extract embedded objects out of the payload for a single object
|
10329 | d0fe8c12 | Athina Bekakou | and add them as sideloaded objects instead.
|
10330 | d0fe8c12 | Athina Bekakou | |
10331 | d0fe8c12 | Athina Bekakou | @method extractSingle
|
10332 | d0fe8c12 | Athina Bekakou | */
|
10333 | d0fe8c12 | Athina Bekakou | extractSingle: function(store, primaryType, payload, recordId, requestType) { |
10334 | d0fe8c12 | Athina Bekakou | var root = this.keyForAttribute(primaryType.typeKey), |
10335 | d0fe8c12 | Athina Bekakou | partial = payload[root]; |
10336 | d0fe8c12 | Athina Bekakou | |
10337 | d0fe8c12 | Athina Bekakou | updatePayloadWithEmbedded(store, this, primaryType, partial, payload);
|
10338 | d0fe8c12 | Athina Bekakou | |
10339 | d0fe8c12 | Athina Bekakou | return this._super(store, primaryType, payload, recordId, requestType); |
10340 | d0fe8c12 | Athina Bekakou | }, |
10341 | d0fe8c12 | Athina Bekakou | |
10342 | d0fe8c12 | Athina Bekakou | /**
|
10343 | d0fe8c12 | Athina Bekakou | Extract embedded objects out of a standard payload
|
10344 | d0fe8c12 | Athina Bekakou | and add them as sideloaded objects instead.
|
10345 | d0fe8c12 | Athina Bekakou | |
10346 | d0fe8c12 | Athina Bekakou | @method extractArray
|
10347 | d0fe8c12 | Athina Bekakou | */
|
10348 | d0fe8c12 | Athina Bekakou | extractArray: function(store, type, payload) { |
10349 | d0fe8c12 | Athina Bekakou | var root = this.keyForAttribute(type.typeKey), |
10350 | d0fe8c12 | Athina Bekakou | partials = payload[Ember.String.pluralize(root)]; |
10351 | d0fe8c12 | Athina Bekakou | |
10352 | d0fe8c12 | Athina Bekakou | forEach(partials, function(partial) {
|
10353 | d0fe8c12 | Athina Bekakou | updatePayloadWithEmbedded(store, this, type, partial, payload);
|
10354 | d0fe8c12 | Athina Bekakou | }, this);
|
10355 | d0fe8c12 | Athina Bekakou | |
10356 | d0fe8c12 | Athina Bekakou | return this._super(store, type, payload); |
10357 | d0fe8c12 | Athina Bekakou | } |
10358 | d0fe8c12 | Athina Bekakou | }); |
10359 | d0fe8c12 | Athina Bekakou | |
10360 | d0fe8c12 | Athina Bekakou | function updatePayloadWithEmbedded(store, serializer, type, partial, payload) { |
10361 | d0fe8c12 | Athina Bekakou | var attrs = get(serializer, 'attrs'); |
10362 | d0fe8c12 | Athina Bekakou | |
10363 | d0fe8c12 | Athina Bekakou | if (!attrs) {
|
10364 | d0fe8c12 | Athina Bekakou | return;
|
10365 | d0fe8c12 | Athina Bekakou | } |
10366 | d0fe8c12 | Athina Bekakou | |
10367 | d0fe8c12 | Athina Bekakou | type.eachRelationship(function(key, relationship) {
|
10368 | d0fe8c12 | Athina Bekakou | var expandedKey, embeddedTypeKey, attribute, ids,
|
10369 | d0fe8c12 | Athina Bekakou | config = attrs[key], |
10370 | d0fe8c12 | Athina Bekakou | serializer = store.serializerFor(relationship.type.typeKey), |
10371 | d0fe8c12 | Athina Bekakou | primaryKey = get(serializer, "primaryKey");
|
10372 | d0fe8c12 | Athina Bekakou | |
10373 | d0fe8c12 | Athina Bekakou | if (relationship.kind !== "hasMany") { |
10374 | d0fe8c12 | Athina Bekakou | return;
|
10375 | d0fe8c12 | Athina Bekakou | } |
10376 | d0fe8c12 | Athina Bekakou | |
10377 | d0fe8c12 | Athina Bekakou | if (config && (config.embedded === 'always' || config.embedded === 'load')) { |
10378 | d0fe8c12 | Athina Bekakou | // underscore forces the embedded records to be side loaded.
|
10379 | d0fe8c12 | Athina Bekakou | // it is needed when main type === relationship.type
|
10380 | d0fe8c12 | Athina Bekakou | embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey);
|
10381 | d0fe8c12 | Athina Bekakou | expandedKey = this.keyForRelationship(key, relationship.kind);
|
10382 | d0fe8c12 | Athina Bekakou | attribute = this.keyForAttribute(key);
|
10383 | d0fe8c12 | Athina Bekakou | ids = []; |
10384 | d0fe8c12 | Athina Bekakou | |
10385 | d0fe8c12 | Athina Bekakou | if (!partial[attribute]) {
|
10386 | d0fe8c12 | Athina Bekakou | return;
|
10387 | d0fe8c12 | Athina Bekakou | } |
10388 | d0fe8c12 | Athina Bekakou | |
10389 | d0fe8c12 | Athina Bekakou | payload[embeddedTypeKey] = payload[embeddedTypeKey] || []; |
10390 | d0fe8c12 | Athina Bekakou | |
10391 | d0fe8c12 | Athina Bekakou | forEach(partial[attribute], function(data) {
|
10392 | d0fe8c12 | Athina Bekakou | var embeddedType = store.modelFor(relationship.type.typeKey);
|
10393 | d0fe8c12 | Athina Bekakou | updatePayloadWithEmbedded(store, serializer, embeddedType, data, payload); |
10394 | d0fe8c12 | Athina Bekakou | ids.push(data[primaryKey]); |
10395 | d0fe8c12 | Athina Bekakou | payload[embeddedTypeKey].push(data); |
10396 | d0fe8c12 | Athina Bekakou | }); |
10397 | d0fe8c12 | Athina Bekakou | |
10398 | d0fe8c12 | Athina Bekakou | partial[expandedKey] = ids; |
10399 | d0fe8c12 | Athina Bekakou | delete partial[attribute];
|
10400 | d0fe8c12 | Athina Bekakou | } |
10401 | d0fe8c12 | Athina Bekakou | }, serializer); |
10402 | d0fe8c12 | Athina Bekakou | } |
10403 | d0fe8c12 | Athina Bekakou | })(); |
10404 | d0fe8c12 | Athina Bekakou | |
10405 | d0fe8c12 | Athina Bekakou | |
10406 | d0fe8c12 | Athina Bekakou | |
10407 | d0fe8c12 | Athina Bekakou | (function() {
|
10408 | d0fe8c12 | Athina Bekakou | /**
|
10409 | d0fe8c12 | Athina Bekakou | @module ember-data
|
10410 | d0fe8c12 | Athina Bekakou | */
|
10411 | d0fe8c12 | Athina Bekakou | |
10412 | d0fe8c12 | Athina Bekakou | var forEach = Ember.EnumerableUtils.forEach;
|
10413 | d0fe8c12 | Athina Bekakou | |
10414 | d0fe8c12 | Athina Bekakou | /**
|
10415 | d0fe8c12 | Athina Bekakou | The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
|
10416 | d0fe8c12 | Athina Bekakou | with a JSON API that uses an underscored naming convention instead of camelcasing.
|
10417 | d0fe8c12 | Athina Bekakou | It has been designed to work out of the box with the
|
10418 | d0fe8c12 | Athina Bekakou | [active_model_serializers](http://github.com/rails-api/active_model_serializers)
|
10419 | d0fe8c12 | Athina Bekakou | Ruby gem.
|
10420 | d0fe8c12 | Athina Bekakou | |
10421 | d0fe8c12 | Athina Bekakou | This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
|
10422 | d0fe8c12 | Athina Bekakou | decamelization and pluralization methods to normalize the serialized JSON into a
|
10423 | d0fe8c12 | Athina Bekakou | format that is compatible with a conventional Rails backend and Ember Data.
|
10424 | d0fe8c12 | Athina Bekakou | |
10425 | d0fe8c12 | Athina Bekakou | ## JSON Structure
|
10426 | d0fe8c12 | Athina Bekakou | |
10427 | d0fe8c12 | Athina Bekakou | The ActiveModelAdapter expects the JSON returned from your server to follow
|
10428 | d0fe8c12 | Athina Bekakou | the REST adapter conventions substituting underscored keys for camelcased ones.
|
10429 | d0fe8c12 | Athina Bekakou | |
10430 | d0fe8c12 | Athina Bekakou | ### Conventional Names
|
10431 | d0fe8c12 | Athina Bekakou | |
10432 | d0fe8c12 | Athina Bekakou | Attribute names in your JSON payload should be the underscored versions of
|
10433 | d0fe8c12 | Athina Bekakou | the attributes in your Ember.js models.
|
10434 | d0fe8c12 | Athina Bekakou | |
10435 | d0fe8c12 | Athina Bekakou | For example, if you have a `Person` model:
|
10436 | d0fe8c12 | Athina Bekakou | |
10437 | d0fe8c12 | Athina Bekakou | ```js
|
10438 | d0fe8c12 | Athina Bekakou | App.FamousPerson = DS.Model.extend({
|
10439 | d0fe8c12 | Athina Bekakou | firstName: DS.attr('string'),
|
10440 | d0fe8c12 | Athina Bekakou | lastName: DS.attr('string'),
|
10441 | d0fe8c12 | Athina Bekakou | occupation: DS.attr('string')
|
10442 | d0fe8c12 | Athina Bekakou | });
|
10443 | d0fe8c12 | Athina Bekakou | ```
|
10444 | d0fe8c12 | Athina Bekakou | |
10445 | d0fe8c12 | Athina Bekakou | The JSON returned should look like this:
|
10446 | d0fe8c12 | Athina Bekakou | |
10447 | d0fe8c12 | Athina Bekakou | ```js
|
10448 | d0fe8c12 | Athina Bekakou | {
|
10449 | d0fe8c12 | Athina Bekakou | "famous_person": {
|
10450 | d0fe8c12 | Athina Bekakou | "first_name": "Barack",
|
10451 | d0fe8c12 | Athina Bekakou | "last_name": "Obama",
|
10452 | d0fe8c12 | Athina Bekakou | "occupation": "President"
|
10453 | d0fe8c12 | Athina Bekakou | }
|
10454 | d0fe8c12 | Athina Bekakou | }
|
10455 | d0fe8c12 | Athina Bekakou | ```
|
10456 | d0fe8c12 | Athina Bekakou | |
10457 | d0fe8c12 | Athina Bekakou | @class ActiveModelAdapter
|
10458 | d0fe8c12 | Athina Bekakou | @constructor
|
10459 | d0fe8c12 | Athina Bekakou | @namespace DS
|
10460 | d0fe8c12 | Athina Bekakou | @extends DS.Adapter
|
10461 | d0fe8c12 | Athina Bekakou | **/
|
10462 | d0fe8c12 | Athina Bekakou | |
10463 | d0fe8c12 | Athina Bekakou | DS.ActiveModelAdapter = DS.RESTAdapter.extend({ |
10464 | d0fe8c12 | Athina Bekakou | defaultSerializer: '_ams', |
10465 | d0fe8c12 | Athina Bekakou | /**
|
10466 | d0fe8c12 | Athina Bekakou | The ActiveModelAdapter overrides the `pathForType` method to build
|
10467 | d0fe8c12 | Athina Bekakou | underscored URLs by decamelizing and pluralizing the object type name.
|
10468 | d0fe8c12 | Athina Bekakou | |
10469 | d0fe8c12 | Athina Bekakou | ```js
|
10470 | d0fe8c12 | Athina Bekakou | this.pathForType("famousPerson");
|
10471 | d0fe8c12 | Athina Bekakou | //=> "famous_people"
|
10472 | d0fe8c12 | Athina Bekakou | ```
|
10473 | d0fe8c12 | Athina Bekakou | |
10474 | d0fe8c12 | Athina Bekakou | @method pathForType
|
10475 | d0fe8c12 | Athina Bekakou | @param {String} type
|
10476 | d0fe8c12 | Athina Bekakou | @returns String
|
10477 | d0fe8c12 | Athina Bekakou | */
|
10478 | d0fe8c12 | Athina Bekakou | pathForType: function(type) { |
10479 | d0fe8c12 | Athina Bekakou | var decamelized = Ember.String.decamelize(type);
|
10480 | d0fe8c12 | Athina Bekakou | return Ember.String.pluralize(decamelized);
|
10481 | d0fe8c12 | Athina Bekakou | }, |
10482 | d0fe8c12 | Athina Bekakou | |
10483 | d0fe8c12 | Athina Bekakou | /**
|
10484 | d0fe8c12 | Athina Bekakou | The ActiveModelAdapter overrides the `ajaxError` method
|
10485 | d0fe8c12 | Athina Bekakou | to return a DS.InvalidError for all 422 Unprocessable Entity
|
10486 | d0fe8c12 | Athina Bekakou | responses.
|
10487 | d0fe8c12 | Athina Bekakou | |
10488 | d0fe8c12 | Athina Bekakou | A 422 HTTP response from the server generally implies that the request
|
10489 | d0fe8c12 | Athina Bekakou | was well formed but the API was unable to process it because the
|
10490 | d0fe8c12 | Athina Bekakou | content was not semantically correct or meaningful per the API.
|
10491 | d0fe8c12 | Athina Bekakou | |
10492 | d0fe8c12 | Athina Bekakou | For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
|
10493 | d0fe8c12 | Athina Bekakou | https://tools.ietf.org/html/rfc4918#section-11.2
|
10494 | d0fe8c12 | Athina Bekakou | |
10495 | d0fe8c12 | Athina Bekakou | @method ajaxError
|
10496 | d0fe8c12 | Athina Bekakou | @param jqXHR
|
10497 | d0fe8c12 | Athina Bekakou | @returns error
|
10498 | d0fe8c12 | Athina Bekakou | */
|
10499 | d0fe8c12 | Athina Bekakou | ajaxError: function(jqXHR) { |
10500 | d0fe8c12 | Athina Bekakou | var error = this._super(jqXHR); |
10501 | d0fe8c12 | Athina Bekakou | |
10502 | d0fe8c12 | Athina Bekakou | if (jqXHR && jqXHR.status === 422) { |
10503 | d0fe8c12 | Athina Bekakou | var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"], |
10504 | d0fe8c12 | Athina Bekakou | errors = {}; |
10505 | d0fe8c12 | Athina Bekakou | |
10506 | d0fe8c12 | Athina Bekakou | forEach(Ember.keys(jsonErrors), function(key) {
|
10507 | d0fe8c12 | Athina Bekakou | errors[Ember.String.camelize(key)] = jsonErrors[key]; |
10508 | d0fe8c12 | Athina Bekakou | }); |
10509 | d0fe8c12 | Athina Bekakou | |
10510 | d0fe8c12 | Athina Bekakou | return new DS.InvalidError(errors); |
10511 | d0fe8c12 | Athina Bekakou | } else {
|
10512 | d0fe8c12 | Athina Bekakou | return error;
|
10513 | d0fe8c12 | Athina Bekakou | } |
10514 | d0fe8c12 | Athina Bekakou | } |
10515 | d0fe8c12 | Athina Bekakou | }); |
10516 | d0fe8c12 | Athina Bekakou | |
10517 | d0fe8c12 | Athina Bekakou | })(); |
10518 | d0fe8c12 | Athina Bekakou | |
10519 | d0fe8c12 | Athina Bekakou | |
10520 | d0fe8c12 | Athina Bekakou | |
10521 | d0fe8c12 | Athina Bekakou | (function() {
|
10522 | d0fe8c12 | Athina Bekakou | |
10523 | d0fe8c12 | Athina Bekakou | })(); |
10524 | d0fe8c12 | Athina Bekakou | |
10525 | d0fe8c12 | Athina Bekakou | |
10526 | d0fe8c12 | Athina Bekakou | |
10527 | d0fe8c12 | Athina Bekakou | (function() {
|
10528 | d0fe8c12 | Athina Bekakou | Ember.onLoad('Ember.Application', function(Application) { |
10529 | d0fe8c12 | Athina Bekakou | Application.initializer({ |
10530 | d0fe8c12 | Athina Bekakou | name: "activeModelAdapter", |
10531 | d0fe8c12 | Athina Bekakou | |
10532 | d0fe8c12 | Athina Bekakou | initialize: function(container, application) { |
10533 | d0fe8c12 | Athina Bekakou | application.register('serializer:_ams', DS.ActiveModelSerializer);
|
10534 | d0fe8c12 | Athina Bekakou | application.register('adapter:_ams', DS.ActiveModelAdapter);
|
10535 | d0fe8c12 | Athina Bekakou | } |
10536 | d0fe8c12 | Athina Bekakou | }); |
10537 | d0fe8c12 | Athina Bekakou | }); |
10538 | d0fe8c12 | Athina Bekakou | |
10539 | d0fe8c12 | Athina Bekakou | })(); |
10540 | d0fe8c12 | Athina Bekakou | |
10541 | d0fe8c12 | Athina Bekakou | |
10542 | d0fe8c12 | Athina Bekakou | |
10543 | d0fe8c12 | Athina Bekakou | (function() {
|
10544 | d0fe8c12 | Athina Bekakou | |
10545 | d0fe8c12 | Athina Bekakou | })(); |
10546 | d0fe8c12 | Athina Bekakou | |
10547 | d0fe8c12 | Athina Bekakou | |
10548 | d0fe8c12 | Athina Bekakou | })(); |