ember-resource 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +36 -0
- data/Rakefile +2 -0
- data/ember-resource.gemspec +17 -0
- data/lib/ember-resource/version.rb +5 -0
- data/lib/ember-resource.rb +6 -0
- data/vendor/assets/javascripts/ember-resource.js +1175 -0
- metadata +55 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Hedtek Ltd.; Mick Staugaard; Shajith Chacko; James A. Rosen; Zendesk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Ember Resource asset pipeline
|
2
|
+
|
3
|
+
A version of the ember-resource javascript library packaged for the asset pipeline in rails.
|
4
|
+
|
5
|
+
Original project is https://github.com/staugaard/ember-resource
|
6
|
+
|
7
|
+
Version taken at commit 3a67468faf03a2866303b640e75daceb4a60ac11
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'ember-resource'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install ember-resource
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
Simply add to your assets group in a rails application and then add the line
|
27
|
+
//= require ember-resource
|
28
|
+
to your application's javascript manifest
|
29
|
+
|
30
|
+
## Contributing
|
31
|
+
|
32
|
+
1. Fork it
|
33
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
34
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
35
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
36
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/ember-resource/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["David Workman", "Hedtek Ltd."]
|
6
|
+
gem.email = ["gems@hedtek.com"]
|
7
|
+
gem.description = %q{Ember resource asset pipeline}
|
8
|
+
gem.summary = %q{Ember resource asset pipeline}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "ember-resource"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Ember::Resource::VERSION
|
17
|
+
end
|
@@ -0,0 +1,1175 @@
|
|
1
|
+
(function(undefined) {
|
2
|
+
|
3
|
+
window.Ember = window.Ember || window.SC;
|
4
|
+
|
5
|
+
var expandSchema, expandSchemaItem, createSchemaProperties,
|
6
|
+
mergeSchemas;
|
7
|
+
|
8
|
+
function isString(obj) {
|
9
|
+
return !!(obj === '' || (obj && obj !== String && obj.charCodeAt && obj.substr));
|
10
|
+
}
|
11
|
+
|
12
|
+
function isObject(obj) {
|
13
|
+
return obj === Object(obj);
|
14
|
+
}
|
15
|
+
|
16
|
+
function K() {};
|
17
|
+
|
18
|
+
Ember.Resource = Ember.Object.extend({
|
19
|
+
resourcePropertyWillChange: K,
|
20
|
+
resourcePropertyDidChange: K
|
21
|
+
});
|
22
|
+
|
23
|
+
Ember.Resource.deepSet = function(obj, path, value) {
|
24
|
+
if (Ember.typeOf(path) === 'string') {
|
25
|
+
Ember.Resource.deepSet(obj, path.split('.'), value);
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
|
29
|
+
var key = path.shift();
|
30
|
+
|
31
|
+
if (path.length === 0) {
|
32
|
+
Ember.set(obj, key, value);
|
33
|
+
} else {
|
34
|
+
var newObj = Ember.get(obj, key);
|
35
|
+
|
36
|
+
if (newObj === null || newObj === undefined) {
|
37
|
+
newObj = {};
|
38
|
+
Ember.set(obj, key, newObj);
|
39
|
+
}
|
40
|
+
|
41
|
+
Ember.Resource.deepSet(newObj, path, value);
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
45
|
+
Ember.Resource.deepMerge = function(objA, objB) {
|
46
|
+
var oldValue, newValue;
|
47
|
+
|
48
|
+
for (var key in objB) {
|
49
|
+
if (objB.hasOwnProperty(key)) {
|
50
|
+
oldValue = Ember.get(objA, key);
|
51
|
+
newValue = Ember.get(objB, key);
|
52
|
+
|
53
|
+
if (Ember.typeOf(newValue) === 'object' && Ember.typeOf(oldValue) === 'object') {
|
54
|
+
Ember.Resource.deepMerge(oldValue, newValue);
|
55
|
+
} else {
|
56
|
+
Ember.set(objA, key, newValue);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
};
|
61
|
+
|
62
|
+
Ember.Resource.AbstractSchemaItem = Ember.Object.extend({
|
63
|
+
name: Ember.required(String),
|
64
|
+
fetchable: Ember.required(Boolean),
|
65
|
+
getValue: Ember.required(Function),
|
66
|
+
setValue: Ember.required(Function),
|
67
|
+
|
68
|
+
dependencies: function() {
|
69
|
+
return ['data.' + this.get('path'), 'isExpired'];
|
70
|
+
}.property('path'),
|
71
|
+
|
72
|
+
data: function(instance) {
|
73
|
+
return Ember.get(instance, 'data');
|
74
|
+
},
|
75
|
+
|
76
|
+
type: function() {
|
77
|
+
var type = this.get('theType');
|
78
|
+
if (isString(type)) {
|
79
|
+
type = Ember.getPath(type);
|
80
|
+
if (type) {
|
81
|
+
this.set('theType', type);
|
82
|
+
} else {
|
83
|
+
type = this.get('theType');
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return type;
|
87
|
+
}.property('theType'),
|
88
|
+
|
89
|
+
propertyFunction: function(name, value) {
|
90
|
+
var schemaItem = this.constructor.schema[name];
|
91
|
+
if (arguments.length === 2) {
|
92
|
+
this.resourcePropertyWillChange(name, value);
|
93
|
+
schemaItem.setValue.call(schemaItem, this, value);
|
94
|
+
value = schemaItem.getValue.call(schemaItem, this);
|
95
|
+
this.resourcePropertyDidChange(name, value);
|
96
|
+
} else {
|
97
|
+
value = schemaItem.getValue.call(schemaItem, this);
|
98
|
+
if ((value === undefined || Ember.get(this, 'isExpired')) && schemaItem.get('fetchable')) {
|
99
|
+
this.scheduleFetch();
|
100
|
+
}
|
101
|
+
}
|
102
|
+
return value;
|
103
|
+
},
|
104
|
+
|
105
|
+
property: function() {
|
106
|
+
return this.propertyFunction.property.apply(this.propertyFunction, this.get('dependencies')).cacheable();
|
107
|
+
},
|
108
|
+
|
109
|
+
toJSON: function(instance) {
|
110
|
+
return undefined;
|
111
|
+
}
|
112
|
+
});
|
113
|
+
Ember.Resource.AbstractSchemaItem.reopenClass({
|
114
|
+
create: function(name, schema) {
|
115
|
+
var instance = this._super.apply(this);
|
116
|
+
instance.set('name', name);
|
117
|
+
return instance;
|
118
|
+
}
|
119
|
+
});
|
120
|
+
|
121
|
+
|
122
|
+
Ember.Resource.SchemaItem = Ember.Resource.AbstractSchemaItem.extend({});
|
123
|
+
|
124
|
+
Ember.Resource.SchemaItem.reopenClass({
|
125
|
+
create: function(name, schema) {
|
126
|
+
var definition = schema[name];
|
127
|
+
|
128
|
+
if (definition instanceof Ember.Resource.AbstractSchemaItem) { return definition; }
|
129
|
+
|
130
|
+
var type;
|
131
|
+
if (definition === Number || definition === String || definition === Boolean || definition === Date || definition === Object) {
|
132
|
+
definition = {type: definition};
|
133
|
+
schema[name] = definition;
|
134
|
+
}
|
135
|
+
|
136
|
+
if(isObject(definition)) {
|
137
|
+
type = definition.type;
|
138
|
+
}
|
139
|
+
|
140
|
+
if (type) {
|
141
|
+
if (type.isEmberResource || Ember.typeOf(type) === 'string') { // a has-one association
|
142
|
+
return Ember.Resource.HasOneSchemaItem.create(name, schema);
|
143
|
+
} else if(type.isEmberResourceCollection) { // a has-many association
|
144
|
+
return Ember.Resource.HasManySchemaItem.create(name, schema);
|
145
|
+
} else { // a regular attribute
|
146
|
+
return Ember.Resource.AttributeSchemaItem.create(name, schema);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
});
|
151
|
+
|
152
|
+
Ember.Resource.AttributeSchemaItem = Ember.Resource.AbstractSchemaItem.extend({
|
153
|
+
fetchable: true,
|
154
|
+
theType: Object,
|
155
|
+
path: Ember.required(String),
|
156
|
+
|
157
|
+
getValue: function(instance) {
|
158
|
+
var value;
|
159
|
+
var data = this.data(instance);
|
160
|
+
if (data) {
|
161
|
+
value = Ember.getPath(data, this.get('path'));
|
162
|
+
}
|
163
|
+
|
164
|
+
if (this.typeCast) {
|
165
|
+
value = this.typeCast(value);
|
166
|
+
}
|
167
|
+
|
168
|
+
return value;
|
169
|
+
},
|
170
|
+
|
171
|
+
setValue: function(instance, value) {
|
172
|
+
var data = this.data(instance);
|
173
|
+
if (!data) return;
|
174
|
+
|
175
|
+
if (this.typeCast) {
|
176
|
+
value = this.typeCast(value);
|
177
|
+
}
|
178
|
+
if (value !== null && value !== undefined && Ember.typeOf(value.toJSON) == 'function') {
|
179
|
+
value = value.toJSON();
|
180
|
+
}
|
181
|
+
Ember.Resource.deepSet(data, this.get('path'), value);
|
182
|
+
},
|
183
|
+
|
184
|
+
toJSON: function(instance) {
|
185
|
+
return Ember.get(instance, this.name);
|
186
|
+
}
|
187
|
+
});
|
188
|
+
|
189
|
+
Ember.Resource.AttributeSchemaItem.reopenClass({
|
190
|
+
create: function(name, schema) {
|
191
|
+
var definition = schema[name];
|
192
|
+
var instance;
|
193
|
+
|
194
|
+
if (this === Ember.Resource.AttributeSchemaItem) {
|
195
|
+
switch (definition.type) {
|
196
|
+
case Number:
|
197
|
+
return Ember.Resource.NumberAttributeSchemaItem.create(name, schema);
|
198
|
+
case String:
|
199
|
+
return Ember.Resource.StringAttributeSchemaItem.create(name, schema);
|
200
|
+
case Boolean:
|
201
|
+
return Ember.Resource.BooleanAttributeSchemaItem.create(name, schema);
|
202
|
+
case Date:
|
203
|
+
return Ember.Resource.DateAttributeSchemaItem.create(name, schema);
|
204
|
+
default:
|
205
|
+
instance = this._super.apply(this, arguments);
|
206
|
+
instance.set('fetchable', name !== 'id');
|
207
|
+
instance.set('path', definition.path || name);
|
208
|
+
return instance;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
else {
|
212
|
+
instance = this._super.apply(this, arguments);
|
213
|
+
instance.set('fetchable', name !== 'id');
|
214
|
+
instance.set('path', definition.path || name);
|
215
|
+
return instance;
|
216
|
+
}
|
217
|
+
}
|
218
|
+
});
|
219
|
+
|
220
|
+
Ember.Resource.NumberAttributeSchemaItem = Ember.Resource.AttributeSchemaItem.extend({
|
221
|
+
theType: Number,
|
222
|
+
typeCast: function(value) {
|
223
|
+
if (isNaN(value)) {
|
224
|
+
value = undefined;
|
225
|
+
}
|
226
|
+
|
227
|
+
if (value === undefined || value === null || Ember.typeOf(value) === 'number') {
|
228
|
+
return value;
|
229
|
+
} else {
|
230
|
+
return Number(value);
|
231
|
+
}
|
232
|
+
}
|
233
|
+
});
|
234
|
+
|
235
|
+
Ember.Resource.StringAttributeSchemaItem = Ember.Resource.AttributeSchemaItem.extend({
|
236
|
+
theType: String,
|
237
|
+
typeCast: function(value) {
|
238
|
+
if (value === undefined || value === null || Ember.typeOf(value) === 'string') {
|
239
|
+
return value;
|
240
|
+
} else {
|
241
|
+
return '' + value;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
});
|
245
|
+
|
246
|
+
Ember.Resource.BooleanAttributeSchemaItem = Ember.Resource.AttributeSchemaItem.extend({
|
247
|
+
theType: Boolean,
|
248
|
+
typeCast: function(value) {
|
249
|
+
if (value === undefined || value === null || Ember.typeOf(value) === 'boolean') {
|
250
|
+
return value;
|
251
|
+
} else {
|
252
|
+
return value === 'true';
|
253
|
+
}
|
254
|
+
}
|
255
|
+
});
|
256
|
+
|
257
|
+
Ember.Resource.DateAttributeSchemaItem = Ember.Resource.AttributeSchemaItem.extend({
|
258
|
+
theType: Date,
|
259
|
+
typeCast: function(value) {
|
260
|
+
if (value === undefined || value === null || Ember.typeOf(value) === 'date') {
|
261
|
+
return value;
|
262
|
+
} else {
|
263
|
+
return new Date(value);
|
264
|
+
}
|
265
|
+
},
|
266
|
+
toJSON: function(instance) {
|
267
|
+
var value = Ember.get(instance, this.name);
|
268
|
+
return value ? value.toJSON() : value;
|
269
|
+
}
|
270
|
+
});
|
271
|
+
|
272
|
+
Ember.Resource.HasOneSchemaItem = Ember.Resource.AbstractSchemaItem.extend({
|
273
|
+
fetchable: true
|
274
|
+
});
|
275
|
+
Ember.Resource.HasOneSchemaItem.reopenClass({
|
276
|
+
create: function(name, schema) {
|
277
|
+
var definition = schema[name];
|
278
|
+
if (this === Ember.Resource.HasOneSchemaItem) {
|
279
|
+
if (definition.nested) {
|
280
|
+
return Ember.Resource.HasOneNestedSchemaItem.create(name, schema);
|
281
|
+
} else {
|
282
|
+
return Ember.Resource.HasOneRemoteSchemaItem.create(name, schema);
|
283
|
+
}
|
284
|
+
}
|
285
|
+
else {
|
286
|
+
var instance = this._super.apply(this, arguments);
|
287
|
+
instance.set('theType', definition.type);
|
288
|
+
if (definition.parse) {
|
289
|
+
instance.set('parse', definition.parse);
|
290
|
+
}
|
291
|
+
return instance;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
});
|
295
|
+
|
296
|
+
Ember.Resource.HasOneNestedSchemaItem = Ember.Resource.HasOneSchemaItem.extend({
|
297
|
+
getValue: function(instance) {
|
298
|
+
var data = this.data(instance);
|
299
|
+
if (!data) return;
|
300
|
+
var type = this.get('type');
|
301
|
+
var value = Ember.getPath(data, this.get('path'));
|
302
|
+
if (value) {
|
303
|
+
value = (this.get('parse') || type.parse).call(type, Ember.copy(value));
|
304
|
+
return type.create({}, value);
|
305
|
+
}
|
306
|
+
return value;
|
307
|
+
},
|
308
|
+
|
309
|
+
setValue: function(instance, value) {
|
310
|
+
var data = this.data(instance);
|
311
|
+
if (!data) return;
|
312
|
+
|
313
|
+
if (value instanceof this.get('type')) {
|
314
|
+
value = Ember.get(value, 'data');
|
315
|
+
}
|
316
|
+
|
317
|
+
Ember.Resource.deepSet(data, this.get('path'), value);
|
318
|
+
},
|
319
|
+
|
320
|
+
toJSON: function(instance) {
|
321
|
+
var value = Ember.get(instance, this.name);
|
322
|
+
return value ? value.toJSON() : value;
|
323
|
+
}
|
324
|
+
});
|
325
|
+
Ember.Resource.HasOneNestedSchemaItem.reopenClass({
|
326
|
+
create: function(name, schema) {
|
327
|
+
var definition = schema[name];
|
328
|
+
var instance = this._super.apply(this, arguments);
|
329
|
+
instance.set('path', definition.path || name);
|
330
|
+
|
331
|
+
var id_name = name + '_id';
|
332
|
+
if (!schema[id_name]) {
|
333
|
+
schema[id_name] = {type: Number, association: instance };
|
334
|
+
schema[id_name] = Ember.Resource.HasOneNestedIdSchemaItem.create(id_name, schema);
|
335
|
+
}
|
336
|
+
|
337
|
+
return instance;
|
338
|
+
}
|
339
|
+
});
|
340
|
+
Ember.Resource.HasOneNestedIdSchemaItem = Ember.Resource.AbstractSchemaItem.extend({
|
341
|
+
fetchable: true,
|
342
|
+
theType: Number,
|
343
|
+
getValue: function(instance) {
|
344
|
+
return instance.getPath(this.get('path'));
|
345
|
+
},
|
346
|
+
setValue: function(instance, value) {
|
347
|
+
Ember.set(instance, this.getPath('association.name'), {id: value});
|
348
|
+
}
|
349
|
+
});
|
350
|
+
Ember.Resource.HasOneNestedIdSchemaItem.reopenClass({
|
351
|
+
create: function(name, schema) {
|
352
|
+
var definition = schema[name];
|
353
|
+
var instance = this._super.apply(this, arguments);
|
354
|
+
instance.set('association', definition.association);
|
355
|
+
instance.set('path', definition.association.get('path') + '.id');
|
356
|
+
return instance;
|
357
|
+
}
|
358
|
+
});
|
359
|
+
|
360
|
+
|
361
|
+
Ember.Resource.HasOneRemoteSchemaItem = Ember.Resource.HasOneSchemaItem.extend({
|
362
|
+
getValue: function(instance) {
|
363
|
+
var data = this.data(instance);
|
364
|
+
if (!data) return;
|
365
|
+
var id = Ember.getPath(data, this.get('path'));
|
366
|
+
if (id) {
|
367
|
+
return this.get('type').create({}, {id: id});
|
368
|
+
}
|
369
|
+
},
|
370
|
+
|
371
|
+
setValue: function(instance, value) {
|
372
|
+
var data = this.data(instance);
|
373
|
+
if (!data) return;
|
374
|
+
var id = Ember.get(value || {}, 'id');
|
375
|
+
Ember.Resource.deepSet(data, this.get('path'), id);
|
376
|
+
}
|
377
|
+
});
|
378
|
+
Ember.Resource.HasOneRemoteSchemaItem.reopenClass({
|
379
|
+
create: function(name, schema) {
|
380
|
+
var definition = schema[name];
|
381
|
+
var instance = this._super.apply(this, arguments);
|
382
|
+
var path = definition.path || name + '_id';
|
383
|
+
instance.set('path', path);
|
384
|
+
|
385
|
+
if (!schema[path]) {
|
386
|
+
schema[path] = Number;
|
387
|
+
schema[path] = Ember.Resource.SchemaItem.create(path, schema);
|
388
|
+
}
|
389
|
+
|
390
|
+
return instance;
|
391
|
+
}
|
392
|
+
});
|
393
|
+
|
394
|
+
|
395
|
+
Ember.Resource.HasManySchemaItem = Ember.Resource.AbstractSchemaItem.extend({
|
396
|
+
itemType: function() {
|
397
|
+
var type = this.get('theItemType');
|
398
|
+
if (isString(type)) {
|
399
|
+
type = Ember.getPath(type);
|
400
|
+
if (type) {
|
401
|
+
this.set('theItemType', type);
|
402
|
+
} else {
|
403
|
+
type = this.get('theItemType');
|
404
|
+
}
|
405
|
+
}
|
406
|
+
return type;
|
407
|
+
}.property('theItemType')
|
408
|
+
});
|
409
|
+
Ember.Resource.HasManySchemaItem.reopenClass({
|
410
|
+
create: function(name, schema) {
|
411
|
+
var definition = schema[name];
|
412
|
+
if (this === Ember.Resource.HasManySchemaItem) {
|
413
|
+
if (definition.url) {
|
414
|
+
return Ember.Resource.HasManyRemoteSchemaItem.create(name, schema);
|
415
|
+
} else if (definition.nested) {
|
416
|
+
return Ember.Resource.HasManyNestedSchemaItem.create(name, schema);
|
417
|
+
} else {
|
418
|
+
return Ember.Resource.HasManyInArraySchemaItem.create(name, schema);
|
419
|
+
}
|
420
|
+
} else {
|
421
|
+
var instance = this._super.apply(this, arguments);
|
422
|
+
instance.set('theType', definition.type);
|
423
|
+
instance.set('theItemType', definition.itemType);
|
424
|
+
if (definition.parse) {
|
425
|
+
instance.set('parse', definition.parse);
|
426
|
+
}
|
427
|
+
return instance;
|
428
|
+
}
|
429
|
+
}
|
430
|
+
});
|
431
|
+
|
432
|
+
Ember.Resource.HasManyRemoteSchemaItem = Ember.Resource.HasManySchemaItem.extend({
|
433
|
+
fetchable: false,
|
434
|
+
dependencies: ['id', 'isInitializing'],
|
435
|
+
getValue: function(instance) {
|
436
|
+
if (Ember.get(instance, 'isInitializing')) return;
|
437
|
+
|
438
|
+
var options = {
|
439
|
+
type: this.get('itemType')
|
440
|
+
};
|
441
|
+
|
442
|
+
if (this.get('parse')) options.parse = this.get('parse');
|
443
|
+
|
444
|
+
var url = this.url(instance);
|
445
|
+
if (url) {
|
446
|
+
options.url = url;
|
447
|
+
} else {
|
448
|
+
options.content = [];
|
449
|
+
}
|
450
|
+
|
451
|
+
return this.get('type').create(options);
|
452
|
+
},
|
453
|
+
|
454
|
+
setValue: function(instance, value) {
|
455
|
+
throw('you can not set a remote has many association');
|
456
|
+
}
|
457
|
+
});
|
458
|
+
Ember.Resource.HasManyRemoteSchemaItem.reopenClass({
|
459
|
+
create: function(name, schema) {
|
460
|
+
var definition = schema[name];
|
461
|
+
|
462
|
+
var instance = this._super.apply(this, arguments);
|
463
|
+
|
464
|
+
if (Ember.typeOf(definition.url) === 'function') {
|
465
|
+
instance.url = definition.url;
|
466
|
+
} else {
|
467
|
+
instance.url = function(obj) {
|
468
|
+
var id = obj.get('id');
|
469
|
+
if (id) {
|
470
|
+
return definition.url.fmt(id);
|
471
|
+
}
|
472
|
+
};
|
473
|
+
}
|
474
|
+
|
475
|
+
return instance;
|
476
|
+
}
|
477
|
+
});
|
478
|
+
|
479
|
+
Ember.Resource.HasManyNestedSchemaItem = Ember.Resource.HasManySchemaItem.extend({
|
480
|
+
fetchable: true,
|
481
|
+
getValue: function(instance) {
|
482
|
+
var data = this.data(instance);
|
483
|
+
if (!data) return;
|
484
|
+
data = Ember.getPath(data, this.get('path'));
|
485
|
+
if (data === undefined || data === null) return data;
|
486
|
+
data = Ember.copy(data);
|
487
|
+
|
488
|
+
var options = {
|
489
|
+
type: this.get('itemType'),
|
490
|
+
content: data
|
491
|
+
};
|
492
|
+
|
493
|
+
if (this.get('parse')) options.parse = this.get('parse');
|
494
|
+
|
495
|
+
return this.get('type').create(options);
|
496
|
+
},
|
497
|
+
|
498
|
+
setValue: function(instance, value) {
|
499
|
+
},
|
500
|
+
|
501
|
+
toJSON: function(instance) {
|
502
|
+
var value = Ember.get(instance, this.name);
|
503
|
+
return value ? value.toJSON() : value;
|
504
|
+
}
|
505
|
+
});
|
506
|
+
Ember.Resource.HasManyNestedSchemaItem.reopenClass({
|
507
|
+
create: function(name, schema) {
|
508
|
+
var definition = schema[name];
|
509
|
+
|
510
|
+
var instance = this._super.apply(this, arguments);
|
511
|
+
instance.set('path', definition.path || name);
|
512
|
+
|
513
|
+
return instance;
|
514
|
+
}
|
515
|
+
});
|
516
|
+
|
517
|
+
Ember.Resource.HasManyInArraySchemaItem = Ember.Resource.HasManySchemaItem.extend({
|
518
|
+
fetchable: true,
|
519
|
+
getValue: function(instance) {
|
520
|
+
var data = this.data(instance);
|
521
|
+
if (!data) return;
|
522
|
+
data = Ember.getPath(data, this.get('path'));
|
523
|
+
if (data === undefined || data === null) return data;
|
524
|
+
|
525
|
+
|
526
|
+
return this.get('type').create({
|
527
|
+
type: this.get('itemType'),
|
528
|
+
content: data.map(function(id) { return {id: id}; })
|
529
|
+
});
|
530
|
+
},
|
531
|
+
|
532
|
+
setValue: function(instance, value) {
|
533
|
+
},
|
534
|
+
|
535
|
+
toJSON: function(instance) {
|
536
|
+
var value = Ember.get(instance, this.name);
|
537
|
+
return value ? value.mapProperty('id') : value;
|
538
|
+
}
|
539
|
+
});
|
540
|
+
Ember.Resource.HasManyInArraySchemaItem.reopenClass({
|
541
|
+
create: function(name, schema) {
|
542
|
+
var definition = schema[name];
|
543
|
+
|
544
|
+
var instance = this._super.apply(this, arguments);
|
545
|
+
instance.set('path', definition.path || name + '_ids');
|
546
|
+
|
547
|
+
return instance;
|
548
|
+
}
|
549
|
+
});
|
550
|
+
|
551
|
+
|
552
|
+
// Gives custom error handlers access to the resource object.
|
553
|
+
// 1. `this` will refer to the Ember.Resource object.
|
554
|
+
// 2. `resource` will be passed as the last argument
|
555
|
+
//
|
556
|
+
// function errorHandler() {
|
557
|
+
// this; // the Ember.Resource
|
558
|
+
// }
|
559
|
+
//
|
560
|
+
// function errorHandler(jqXHR, textStatus, errorThrown, resource) {
|
561
|
+
// resource; // another way to reference the resource object
|
562
|
+
// }
|
563
|
+
//
|
564
|
+
var errorHandlerWithModel = function(errorHandler, resource) {
|
565
|
+
return function() {
|
566
|
+
var args = Array.prototype.slice.call(arguments, 0);
|
567
|
+
args.push(resource);
|
568
|
+
errorHandler.apply(resource, args);
|
569
|
+
};
|
570
|
+
};
|
571
|
+
|
572
|
+
Ember.Resource.ajax = function(options) {
|
573
|
+
options.dataType = options.dataType || 'json';
|
574
|
+
options.type = options.type || 'GET';
|
575
|
+
|
576
|
+
if (!options.error && Ember.Resource.errorHandler) {
|
577
|
+
if (options.resource) {
|
578
|
+
options.error = errorHandlerWithModel(Ember.Resource.errorHandler, options.resource);
|
579
|
+
delete options.resource;
|
580
|
+
} else {
|
581
|
+
options.error = Ember.Resource.errorHandler;
|
582
|
+
}
|
583
|
+
}
|
584
|
+
|
585
|
+
return $.ajax(options);
|
586
|
+
};
|
587
|
+
|
588
|
+
Ember.Resource.Lifecycle = {
|
589
|
+
INITIALIZING: 0,
|
590
|
+
UNFETCHED: 10,
|
591
|
+
EXPIRING: 20,
|
592
|
+
EXPIRED: 30,
|
593
|
+
FETCHING: 40,
|
594
|
+
FETCHED: 50,
|
595
|
+
SAVING: 60,
|
596
|
+
DESTROYING: 70,
|
597
|
+
DESTROYED: 80,
|
598
|
+
|
599
|
+
clock: Ember.Object.create({
|
600
|
+
now: new Date(),
|
601
|
+
|
602
|
+
tick: function() {
|
603
|
+
Ember.Resource.Lifecycle.clock.set('now', new Date());
|
604
|
+
},
|
605
|
+
|
606
|
+
start: function() {
|
607
|
+
this.stop();
|
608
|
+
Ember.Resource.Lifecycle.clock.set('timer', setInterval(Ember.Resource.Lifecycle.clock.tick, 10000));
|
609
|
+
},
|
610
|
+
|
611
|
+
stop: function() {
|
612
|
+
var timer = Ember.Resource.Lifecycle.clock.get('timer');
|
613
|
+
if (timer) {
|
614
|
+
clearInterval(timer);
|
615
|
+
}
|
616
|
+
}
|
617
|
+
}),
|
618
|
+
|
619
|
+
classMixin: Ember.Mixin.create({
|
620
|
+
create: function(options, data) {
|
621
|
+
options = options || {};
|
622
|
+
options.resourceState = Ember.Resource.Lifecycle.INITIALIZING;
|
623
|
+
|
624
|
+
var instance = this._super.apply(this, arguments);
|
625
|
+
|
626
|
+
if (Ember.get(instance, 'resourceState') === Ember.Resource.Lifecycle.INITIALIZING) {
|
627
|
+
Ember.set(instance, 'resourceState', Ember.Resource.Lifecycle.UNFETCHED);
|
628
|
+
}
|
629
|
+
|
630
|
+
return instance;
|
631
|
+
}
|
632
|
+
}),
|
633
|
+
|
634
|
+
prototypeMixin: Ember.Mixin.create({
|
635
|
+
expireIn: 60 * 5,
|
636
|
+
resourceState: 0,
|
637
|
+
autoFetch: true,
|
638
|
+
|
639
|
+
init: function() {
|
640
|
+
this._super.apply(this, arguments);
|
641
|
+
|
642
|
+
var self = this;
|
643
|
+
|
644
|
+
var updateExpiry = function() {
|
645
|
+
var expireAt = new Date();
|
646
|
+
expireAt.setSeconds(expireAt.getSeconds() + Ember.get(self, 'expireIn'));
|
647
|
+
Ember.set(self, 'expireAt', expireAt);
|
648
|
+
};
|
649
|
+
|
650
|
+
Ember.addListener(this, 'willFetch', this, function() {
|
651
|
+
Ember.set(self, 'resourceState', Ember.Resource.Lifecycle.FETCHING);
|
652
|
+
updateExpiry();
|
653
|
+
});
|
654
|
+
|
655
|
+
Ember.addListener(this, 'didFetch', this, function() {
|
656
|
+
Ember.set(self, 'resourceState', Ember.Resource.Lifecycle.FETCHED);
|
657
|
+
updateExpiry();
|
658
|
+
});
|
659
|
+
|
660
|
+
var resourceStateBeforeSave;
|
661
|
+
Ember.addListener(this, 'willSave', this, function() {
|
662
|
+
resourceStateBeforeSave = Ember.get(self, 'resourceState');
|
663
|
+
Ember.set(self, 'resourceState', Ember.Resource.Lifecycle.SAVING);
|
664
|
+
});
|
665
|
+
|
666
|
+
Ember.addListener(this, 'didSave', this, function() {
|
667
|
+
Ember.set(self, 'resourceState', resourceStateBeforeSave || Ember.Resource.Lifecycle.UNFETCHED);
|
668
|
+
});
|
669
|
+
},
|
670
|
+
|
671
|
+
isFetchable: function() {
|
672
|
+
var state = Ember.get(this, 'resourceState');
|
673
|
+
return state == Ember.Resource.Lifecycle.UNFETCHED || state === Ember.Resource.Lifecycle.EXPIRED;
|
674
|
+
}.property('resourceState').cacheable(),
|
675
|
+
|
676
|
+
isAutoFetchable: function() {
|
677
|
+
return this.get('isFetchable') && this.get('autoFetch');
|
678
|
+
}.property('isFetchable', 'autoFetch').cacheable(),
|
679
|
+
|
680
|
+
isInitializing: function() {
|
681
|
+
return (Ember.get(this, 'resourceState') || Ember.Resource.Lifecycle.INITIALIZING) === Ember.Resource.Lifecycle.INITIALIZING;
|
682
|
+
}.property('resourceState').cacheable(),
|
683
|
+
|
684
|
+
isFetching: function() {
|
685
|
+
return (Ember.get(this, 'resourceState')) === Ember.Resource.Lifecycle.FETCHING;
|
686
|
+
}.property('resourceState').cacheable(),
|
687
|
+
|
688
|
+
isFetched: function() {
|
689
|
+
return (Ember.get(this, 'resourceState')) === Ember.Resource.Lifecycle.FETCHED;
|
690
|
+
}.property('resourceState').cacheable(),
|
691
|
+
|
692
|
+
isSavable: function() {
|
693
|
+
var state = Ember.get(this, 'resourceState');
|
694
|
+
var unsavableState = [
|
695
|
+
Ember.Resource.Lifecycle.INITIALIZING,
|
696
|
+
Ember.Resource.Lifecycle.FETCHING,
|
697
|
+
Ember.Resource.Lifecycle.SAVING,
|
698
|
+
Ember.Resource.Lifecycle.DESTROYING
|
699
|
+
];
|
700
|
+
|
701
|
+
return state && !unsavableState.contains(state);
|
702
|
+
}.property('resourceState').cacheable(),
|
703
|
+
|
704
|
+
scheduleFetch: function() {
|
705
|
+
if (Ember.get(this, 'isAutoFetchable')) {
|
706
|
+
Ember.run.next(this, this.fetch);
|
707
|
+
}
|
708
|
+
},
|
709
|
+
|
710
|
+
expire: function() {
|
711
|
+
Ember.run.next(this, function() {
|
712
|
+
Ember.set(this, 'expireAt', new Date());
|
713
|
+
Ember.Resource.Lifecycle.clock.tick();
|
714
|
+
});
|
715
|
+
},
|
716
|
+
|
717
|
+
updateIsExpired: function() {
|
718
|
+
var isExpired = Ember.get(this, 'resourceState') === Ember.Resource.Lifecycle.EXPIRED;
|
719
|
+
if (isExpired) return true;
|
720
|
+
|
721
|
+
var expireAt = Ember.get(this, 'expireAt');
|
722
|
+
if (expireAt) {
|
723
|
+
var now = Ember.Resource.Lifecycle.clock.get('now');
|
724
|
+
isExpired = expireAt.getTime() <= now.getTime();
|
725
|
+
}
|
726
|
+
|
727
|
+
if (isExpired !== Ember.get(this, 'isExpired')) {
|
728
|
+
Ember.set(this, 'isExpired', isExpired);
|
729
|
+
}
|
730
|
+
}.observes('Ember.Resource.Lifecycle.clock.now', 'expireAt', 'resourceState'),
|
731
|
+
|
732
|
+
isExpired: function(name, value) {
|
733
|
+
if (value) {
|
734
|
+
Ember.set(this, 'resourceState', Ember.Resource.Lifecycle.EXPIRED);
|
735
|
+
}
|
736
|
+
return value;
|
737
|
+
}.property().cacheable()
|
738
|
+
})
|
739
|
+
};
|
740
|
+
Ember.Resource.Lifecycle.clock.start();
|
741
|
+
|
742
|
+
Ember.Resource.reopen({
|
743
|
+
isEmberResource: true,
|
744
|
+
|
745
|
+
updateWithApiData: function(json) {
|
746
|
+
var data = Ember.get(this, 'data');
|
747
|
+
Ember.beginPropertyChanges(data);
|
748
|
+
Ember.Resource.deepMerge(data, this.constructor.parse(json));
|
749
|
+
Ember.endPropertyChanges(data);
|
750
|
+
},
|
751
|
+
|
752
|
+
willFetch: function() {},
|
753
|
+
didFetch: function() {},
|
754
|
+
willSave: function() {},
|
755
|
+
didSave: function() {},
|
756
|
+
|
757
|
+
fetch: function() {
|
758
|
+
if (!Ember.get(this, 'isFetchable')) return $.when();
|
759
|
+
|
760
|
+
var url = this.resourceURL();
|
761
|
+
|
762
|
+
if (!url) return;
|
763
|
+
|
764
|
+
var self = this;
|
765
|
+
|
766
|
+
if (this.deferedFetch && !Ember.get(this, 'isExpired')) return this.deferedFetch;
|
767
|
+
|
768
|
+
self.willFetch.call(self);
|
769
|
+
Ember.sendEvent(self, 'willFetch');
|
770
|
+
|
771
|
+
this.deferedFetch = Ember.Resource.ajax({
|
772
|
+
url: url,
|
773
|
+
success: function(json) {
|
774
|
+
self.updateWithApiData(json);
|
775
|
+
}
|
776
|
+
});
|
777
|
+
|
778
|
+
this.deferedFetch.always(function() {
|
779
|
+
self.didFetch.call(self);
|
780
|
+
Ember.sendEvent(self, 'didFetch');
|
781
|
+
});
|
782
|
+
|
783
|
+
return this.deferedFetch;
|
784
|
+
},
|
785
|
+
|
786
|
+
resourceURL: function() {
|
787
|
+
return this.constructor.resourceURL(this);
|
788
|
+
},
|
789
|
+
|
790
|
+
// Turn this resource into a JSON object to be saved via AJAX. Override
|
791
|
+
// this method to produce different syncing behavior.
|
792
|
+
toJSON: function() {
|
793
|
+
var json = {};
|
794
|
+
var schemaItem, path, value;
|
795
|
+
for (var name in this.constructor.schema) {
|
796
|
+
if (this.constructor.schema.hasOwnProperty(name)) {
|
797
|
+
schemaItem = this.constructor.schema[name];
|
798
|
+
if (schemaItem instanceof Ember.Resource.AbstractSchemaItem) {
|
799
|
+
path = schemaItem.get('path');
|
800
|
+
value = schemaItem.toJSON(this);
|
801
|
+
if (value !== undefined) {
|
802
|
+
Ember.Resource.deepSet(json, path, value);
|
803
|
+
}
|
804
|
+
}
|
805
|
+
}
|
806
|
+
}
|
807
|
+
|
808
|
+
return json;
|
809
|
+
},
|
810
|
+
|
811
|
+
isNew: function() {
|
812
|
+
return !Ember.get(this, 'id');
|
813
|
+
}.property('id').cacheable(),
|
814
|
+
|
815
|
+
save: function(options) {
|
816
|
+
options = options || {};
|
817
|
+
if (!Ember.get(this, 'isSavable')) return false;
|
818
|
+
|
819
|
+
var ajaxOptions = {
|
820
|
+
contentType: 'application/json',
|
821
|
+
data: JSON.stringify(this.toJSON()),
|
822
|
+
resource: this
|
823
|
+
};
|
824
|
+
|
825
|
+
if (Ember.get(this, 'isNew')) {
|
826
|
+
ajaxOptions.type = 'POST';
|
827
|
+
ajaxOptions.url = this.constructor.resourceURL();
|
828
|
+
} else {
|
829
|
+
ajaxOptions.type = 'PUT';
|
830
|
+
ajaxOptions.url = this.resourceURL();
|
831
|
+
}
|
832
|
+
|
833
|
+
var self = this;
|
834
|
+
|
835
|
+
self.willSave.call(self);
|
836
|
+
Ember.sendEvent(self, 'willSave');
|
837
|
+
|
838
|
+
var deferedSave = Ember.Resource.ajax(ajaxOptions);
|
839
|
+
|
840
|
+
deferedSave.done(function(data, status, response) {
|
841
|
+
var location = response.getResponseHeader('Location');
|
842
|
+
if (location) {
|
843
|
+
var id = self.constructor.idFromURL(location);
|
844
|
+
if (id) {
|
845
|
+
Ember.set(self, 'id', id);
|
846
|
+
}
|
847
|
+
}
|
848
|
+
|
849
|
+
if (options.update !== false && Ember.typeOf(data) === 'object') {
|
850
|
+
self.updateWithApiData(data);
|
851
|
+
}
|
852
|
+
});
|
853
|
+
|
854
|
+
deferedSave.always(function() {
|
855
|
+
self.didSave.call(self);
|
856
|
+
Ember.sendEvent(self, 'didSave');
|
857
|
+
});
|
858
|
+
|
859
|
+
return deferedSave;
|
860
|
+
},
|
861
|
+
|
862
|
+
destroy: function() {
|
863
|
+
var previousState = Ember.get(this, 'resourceState'), self = this;
|
864
|
+
Ember.set(this, 'resourceState', Ember.Resource.Lifecycle.DESTROYING);
|
865
|
+
return Ember.Resource.ajax({
|
866
|
+
type: 'DELETE',
|
867
|
+
url: this.resourceURL(),
|
868
|
+
resource: this
|
869
|
+
}).done(function() {
|
870
|
+
Ember.set(self, 'resourceState', Ember.Resource.Lifecycle.DESTROYED);
|
871
|
+
}).fail(function() {
|
872
|
+
Ember.set(self, 'resourceState', previousState);
|
873
|
+
});
|
874
|
+
}
|
875
|
+
}, Ember.Resource.Lifecycle.prototypeMixin);
|
876
|
+
|
877
|
+
expandSchema = function(schema) {
|
878
|
+
for (var name in schema) {
|
879
|
+
if (schema.hasOwnProperty(name)) {
|
880
|
+
schema[name] = Ember.Resource.SchemaItem.create(name, schema);
|
881
|
+
}
|
882
|
+
}
|
883
|
+
|
884
|
+
return schema;
|
885
|
+
};
|
886
|
+
|
887
|
+
mergeSchemas = function(childSchema, parentSchema) {
|
888
|
+
var schema = Ember.copy(parentSchema || {});
|
889
|
+
|
890
|
+
for (var name in childSchema) {
|
891
|
+
if (childSchema.hasOwnProperty(name)) {
|
892
|
+
if (schema.hasOwnProperty(name)) {
|
893
|
+
throw("Schema item '" + name + "' is already defined");
|
894
|
+
}
|
895
|
+
|
896
|
+
schema[name] = childSchema[name];
|
897
|
+
}
|
898
|
+
}
|
899
|
+
|
900
|
+
return schema;
|
901
|
+
};
|
902
|
+
|
903
|
+
createSchemaProperties = function(schema) {
|
904
|
+
var properties = {}, schemaItem;
|
905
|
+
|
906
|
+
for (var propertyName in schema) {
|
907
|
+
if (schema.hasOwnProperty(propertyName)) {
|
908
|
+
properties[propertyName] = schema[propertyName].property();
|
909
|
+
}
|
910
|
+
}
|
911
|
+
|
912
|
+
return properties;
|
913
|
+
};
|
914
|
+
|
915
|
+
Ember.Resource.reopenClass({
|
916
|
+
isEmberResource: true,
|
917
|
+
schema: {},
|
918
|
+
|
919
|
+
baseClass: function() {
|
920
|
+
if (this === Ember.Resource) {
|
921
|
+
return null;
|
922
|
+
} else {
|
923
|
+
return this.baseResourceClass || this;
|
924
|
+
}
|
925
|
+
},
|
926
|
+
|
927
|
+
subclassFor: function(options, data) {
|
928
|
+
return this;
|
929
|
+
},
|
930
|
+
|
931
|
+
// Create an instance of this resource. If `options` includes an
|
932
|
+
// `id`, first check the identity map and return the existing resource
|
933
|
+
// with that ID if found.
|
934
|
+
create: function(options, data) {
|
935
|
+
data = data || {};
|
936
|
+
options = options || {};
|
937
|
+
|
938
|
+
var klass = this.subclassFor(options, data);
|
939
|
+
|
940
|
+
if (klass === this) {
|
941
|
+
var instance;
|
942
|
+
this.identityMap = this.identityMap || {};
|
943
|
+
|
944
|
+
var id = data.id || options.id;
|
945
|
+
if (id && !options.skipIdentityMap) {
|
946
|
+
id = id.toString();
|
947
|
+
instance = this.identityMap[id];
|
948
|
+
|
949
|
+
if (!instance) {
|
950
|
+
this.identityMap[id] = instance = this._super.call(this, { data: data });
|
951
|
+
} else {
|
952
|
+
instance.updateWithApiData(data);
|
953
|
+
}
|
954
|
+
} else {
|
955
|
+
instance = this._super.call(this, { data: data });
|
956
|
+
}
|
957
|
+
|
958
|
+
delete options.data;
|
959
|
+
|
960
|
+
Ember.beginPropertyChanges(instance);
|
961
|
+
var mixin = {};
|
962
|
+
var hasMixin = false;
|
963
|
+
for (var name in options) {
|
964
|
+
if (options.hasOwnProperty(name)) {
|
965
|
+
if (this.schema[name]) {
|
966
|
+
instance.set(name, options[name]);
|
967
|
+
} else {
|
968
|
+
mixin[name] = options[name];
|
969
|
+
hasMixin = true;
|
970
|
+
}
|
971
|
+
}
|
972
|
+
}
|
973
|
+
if (hasMixin) {
|
974
|
+
instance.reopen(mixin);
|
975
|
+
}
|
976
|
+
Ember.endPropertyChanges(instance);
|
977
|
+
|
978
|
+
return instance;
|
979
|
+
} else {
|
980
|
+
return klass.create(options, data);
|
981
|
+
}
|
982
|
+
},
|
983
|
+
|
984
|
+
// Parse JSON -- likely returned from an AJAX call -- into the
|
985
|
+
// properties for an instance of this resource. Override this method
|
986
|
+
// to produce different parsing behavior.
|
987
|
+
parse: function(json) {
|
988
|
+
return json;
|
989
|
+
},
|
990
|
+
|
991
|
+
// Define a resource class.
|
992
|
+
//
|
993
|
+
// Parameters:
|
994
|
+
//
|
995
|
+
// * `schema` -- the properties schema for the resource class
|
996
|
+
// * `url` -- either a function that returns the URL for syncing
|
997
|
+
// resources or a string. If the latter, a string of the form
|
998
|
+
// "/widgets" is turned into a function that returns "/widgets" if
|
999
|
+
// the Widget's ID is not present and "/widgets/{id}" if it is.
|
1000
|
+
// * `parse` -- the function used to parse JSON returned from AJAX
|
1001
|
+
// calls into the resource properties. By default, simply returns
|
1002
|
+
// the JSON.
|
1003
|
+
define: function(options) {
|
1004
|
+
options = options || {};
|
1005
|
+
var schema = expandSchema(options.schema);
|
1006
|
+
schema = mergeSchemas(schema, this.schema);
|
1007
|
+
|
1008
|
+
var klass = this.extend(createSchemaProperties(schema));
|
1009
|
+
|
1010
|
+
var classOptions = {
|
1011
|
+
schema: schema
|
1012
|
+
};
|
1013
|
+
|
1014
|
+
if (this !== Ember.Resource) {
|
1015
|
+
classOptions.baseResourceClass = this.baseClass() || this;
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
if (options.url) {
|
1019
|
+
classOptions.url = options.url;
|
1020
|
+
}
|
1021
|
+
|
1022
|
+
if (options.parse) {
|
1023
|
+
classOptions.parse = options.parse;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
klass.reopenClass(classOptions);
|
1027
|
+
|
1028
|
+
return klass;
|
1029
|
+
},
|
1030
|
+
|
1031
|
+
resourceURL: function(instance) {
|
1032
|
+
if (Ember.typeOf(this.url) == 'function') {
|
1033
|
+
return this.url(instance);
|
1034
|
+
} else if (this.url) {
|
1035
|
+
if (instance) {
|
1036
|
+
var id = Ember.get(instance, 'id');
|
1037
|
+
if (id && (Ember.typeOf(id) !== 'number' || id > 0)) {
|
1038
|
+
return this.url + '/' + id;
|
1039
|
+
}
|
1040
|
+
} else {
|
1041
|
+
return this.url;
|
1042
|
+
}
|
1043
|
+
}
|
1044
|
+
},
|
1045
|
+
|
1046
|
+
idFromURL: function(url) {
|
1047
|
+
var regex;
|
1048
|
+
if (!this.schema.id) return;
|
1049
|
+
|
1050
|
+
if (this.schema.id.get('type') === Number) {
|
1051
|
+
regex = /\/(\d+)(\.\w+)?$/;
|
1052
|
+
} else {
|
1053
|
+
regex = /\/([^\/\.]+)(\.\w+)?$/;
|
1054
|
+
}
|
1055
|
+
|
1056
|
+
var match = (url || '').match(regex);
|
1057
|
+
if (match) {
|
1058
|
+
return match[1];
|
1059
|
+
}
|
1060
|
+
}
|
1061
|
+
}, Ember.Resource.Lifecycle.classMixin);
|
1062
|
+
|
1063
|
+
Ember.ResourceCollection = Ember.ArrayProxy.extend({
|
1064
|
+
isEmberResourceCollection: true,
|
1065
|
+
type: Ember.required(),
|
1066
|
+
fetch: function() {
|
1067
|
+
if (!Ember.get(this, 'isFetchable')) return $.when();
|
1068
|
+
|
1069
|
+
if (!this.prePopulated) {
|
1070
|
+
var self = this;
|
1071
|
+
|
1072
|
+
if (this.deferedFetch && !Ember.get(this, 'isExpired')) return this.deferedFetch;
|
1073
|
+
|
1074
|
+
Ember.sendEvent(self, 'willFetch');
|
1075
|
+
|
1076
|
+
this.deferedFetch = this._fetch(function(json) {
|
1077
|
+
Ember.set(self, 'content', self.parse(json));
|
1078
|
+
});
|
1079
|
+
|
1080
|
+
this.deferedFetch.always(function() {
|
1081
|
+
Ember.sendEvent(self, 'didFetch');
|
1082
|
+
});
|
1083
|
+
}
|
1084
|
+
return this.deferedFetch;
|
1085
|
+
},
|
1086
|
+
_resolveType: function() {
|
1087
|
+
if (isString(this.type)) {
|
1088
|
+
var type = Ember.getPath(this.type);
|
1089
|
+
if (type) this.type = type;
|
1090
|
+
}
|
1091
|
+
},
|
1092
|
+
_fetch: function(callback) {
|
1093
|
+
this._resolveType();
|
1094
|
+
return Ember.Resource.ajax({
|
1095
|
+
url: this.resolveUrl(),
|
1096
|
+
success: callback
|
1097
|
+
});
|
1098
|
+
},
|
1099
|
+
resolveUrl: function() {
|
1100
|
+
return this.get('url') || this.type.resourceURL();
|
1101
|
+
},
|
1102
|
+
instantiateItems: function(items) {
|
1103
|
+
this._resolveType();
|
1104
|
+
return items.map(function(item) {
|
1105
|
+
if (item instanceof this.type) {
|
1106
|
+
return item;
|
1107
|
+
} else {
|
1108
|
+
return this.type.create({}, item);
|
1109
|
+
}
|
1110
|
+
}, this);
|
1111
|
+
},
|
1112
|
+
parse: function(json) {
|
1113
|
+
this._resolveType();
|
1114
|
+
if (Ember.typeOf(this.type.parse) == 'function') {
|
1115
|
+
return json.map(this.type.parse);
|
1116
|
+
}
|
1117
|
+
else {
|
1118
|
+
return json;
|
1119
|
+
}
|
1120
|
+
},
|
1121
|
+
length: function() {
|
1122
|
+
var content = Ember.get(this, 'content');
|
1123
|
+
var length = content ? Ember.get(content, 'length') : 0;
|
1124
|
+
if (length === 0 || Ember.get(this, 'isExpired')) this.scheduleFetch();
|
1125
|
+
return length;
|
1126
|
+
}.property('content.length', 'resourceState', 'isExpired').cacheable(),
|
1127
|
+
content: function(name, value) {
|
1128
|
+
if (arguments.length === 2) { // setter
|
1129
|
+
return this.instantiateItems(value);
|
1130
|
+
}
|
1131
|
+
}.property().cacheable(),
|
1132
|
+
|
1133
|
+
autoFetchOnExpiry: function() {
|
1134
|
+
if (Ember.get(this, 'isExpired') && Ember.get(this, 'hasArrayObservers')) {
|
1135
|
+
this.fetch();
|
1136
|
+
}
|
1137
|
+
}.observes('isExpired', 'hasArrayObservers'),
|
1138
|
+
|
1139
|
+
toJSON: function() {
|
1140
|
+
return this.map(function(item) {
|
1141
|
+
return item.toJSON();
|
1142
|
+
})
|
1143
|
+
}
|
1144
|
+
}, Ember.Resource.Lifecycle.prototypeMixin);
|
1145
|
+
|
1146
|
+
Ember.ResourceCollection.reopenClass({
|
1147
|
+
isEmberResourceCollection: true,
|
1148
|
+
create: function(options) {
|
1149
|
+
options = options || {};
|
1150
|
+
var content = options.content;
|
1151
|
+
delete options.content;
|
1152
|
+
|
1153
|
+
options.prePopulated = !! content;
|
1154
|
+
|
1155
|
+
var instance;
|
1156
|
+
|
1157
|
+
if (!options.prePopulated && options.url) {
|
1158
|
+
this.identityMap = this.identityMap || {};
|
1159
|
+
var identity = [options.type, options.url];
|
1160
|
+
instance = this.identityMap[identity] || this._super.call(this, options);
|
1161
|
+
this.identityMap[identity] = instance;
|
1162
|
+
}
|
1163
|
+
|
1164
|
+
if (!instance) {
|
1165
|
+
instance = this._super.call(this, options);
|
1166
|
+
|
1167
|
+
if (content) {
|
1168
|
+
Ember.set(instance, 'content', instance.parse(content));
|
1169
|
+
}
|
1170
|
+
}
|
1171
|
+
|
1172
|
+
return instance;
|
1173
|
+
}
|
1174
|
+
}, Ember.Resource.Lifecycle.classMixin);
|
1175
|
+
}());
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ember-resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Workman
|
9
|
+
- Hedtek Ltd.
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-03-26 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Ember resource asset pipeline
|
16
|
+
email:
|
17
|
+
- gems@hedtek.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE
|
25
|
+
- README.md
|
26
|
+
- Rakefile
|
27
|
+
- ember-resource.gemspec
|
28
|
+
- lib/ember-resource.rb
|
29
|
+
- lib/ember-resource/version.rb
|
30
|
+
- vendor/assets/javascripts/ember-resource.js
|
31
|
+
homepage: ''
|
32
|
+
licenses: []
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.16
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: Ember resource asset pipeline
|
55
|
+
test_files: []
|