angularjs-rails-resource 1.0.0.pre.2 → 1.0.0.pre.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -303,9 +303,9 @@
303
303
  Serializer.prototype.getDeserializedAttributeName = function (attributeName) {
304
304
  var camelizedName = this.camelize(attributeName);
305
305
 
306
- camelizedName = this.deserializeMappings[attributeName]
307
- || this.deserializeMappings[camelizedName]
308
- || camelizedName;
306
+ camelizedName = this.deserializeMappings[attributeName] ||
307
+ this.deserializeMappings[camelizedName] ||
308
+ camelizedName;
309
309
 
310
310
  if (this.isExcludedFromDeserialization(attributeName) || this.isExcludedFromDeserialization(camelizedName)) {
311
311
  return undefined;
@@ -527,7 +527,7 @@
527
527
  return this.options.camelize(value);
528
528
  }
529
529
  return value;
530
- }
530
+ };
531
531
 
532
532
  return Serializer;
533
533
  }
@@ -31,6 +31,6 @@
31
31
  camelize: camelize,
32
32
  underscore: underscore,
33
33
  pluralize: pluralize
34
- }
34
+ };
35
35
  });
36
36
  }());
@@ -9,7 +9,7 @@
9
9
  */
10
10
  function getDependency(dependency) {
11
11
  if (dependency) {
12
- return angular.isString(dependency) ? $injector.get(dependency) : dependency
12
+ return angular.isString(dependency) ? $injector.get(dependency) : dependency;
13
13
  }
14
14
 
15
15
  return undefined;
@@ -17,9 +17,9 @@
17
17
 
18
18
  /**
19
19
  * Looks up and instantiates an instance of the requested service. If the service is not a string then it is
20
- * assumed to be a constuctor function.
20
+ * assumed to be a constructor function.
21
21
  *
22
- * @param service (string | function) The service to instantiate
22
+ * @param {String|function|Object} service The service to instantiate
23
23
  * @returns {*} A new instance of the requested service
24
24
  */
25
25
  function createService(service) {
@@ -30,9 +30,26 @@
30
30
  return undefined;
31
31
  }
32
32
 
33
+ /**
34
+ * Looks up and instantiates an instance of the requested service if .
35
+ * @param {String|function|Object} service The service to instantiate
36
+ * @returns {*}
37
+ */
38
+ function getService(service) {
39
+ // strings and functions are not considered objects by angular.isObject()
40
+ if (angular.isObject(service)) {
41
+ return service;
42
+ } else if (service) {
43
+ return createService(service);
44
+ }
45
+
46
+ return undefined;
47
+ }
48
+
33
49
  return {
34
50
  createService: createService,
51
+ getService: getService,
35
52
  getDependency: getDependency
36
- }
53
+ };
37
54
  }]);
38
55
  }());
@@ -53,6 +53,5 @@
53
53
  return url;
54
54
  };
55
55
  };
56
-
57
- }])
56
+ }]);
58
57
  }());
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: angularjs-rails-resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.2
4
+ version: 1.0.0.pre.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tommy Odom
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-24 00:00:00.000000000 Z
12
+ date: 2013-12-22 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A small AngularJS add-on for integrating with Rails via JSON more easily.
15
15
  email:
@@ -29,10 +29,8 @@ files:
29
29
  - README.md
30
30
  - Rakefile
31
31
  - angularjs-rails-resource.gemspec
32
- - angularjs-rails-resource.js
33
- - angularjs-rails-resource.min.js
34
- - angularjs-rails-resource.zip
35
32
  - bower.json
33
+ - changelog.js
36
34
  - karma.conf.js
37
35
  - lib/angularjs-rails-resource.rb
38
36
  - lib/angularjs-rails-resource/version.rb
@@ -48,6 +46,7 @@ files:
48
46
  - test/lib/angular/angular-sanitize.js
49
47
  - test/lib/angular/angular-scenario.js
50
48
  - test/lib/angular/angular.js
49
+ - test/unit/angularjs/rails/extensions/snapshotsSpec.js
51
50
  - test/unit/angularjs/rails/httpSettingsSpec.js
52
51
  - test/unit/angularjs/rails/interceptorsSpec.js
53
52
  - test/unit/angularjs/rails/nestedUrlsSpec.js
@@ -59,6 +58,7 @@ files:
59
58
  - test/unit/angularjs/rails/transformersSpec.js
60
59
  - test/unit/angularjs/rails/utils/urlBuilderSpec.js
61
60
  - test/unit/helpers/spec_helper.js
61
+ - vendor/assets/javascripts/angularjs/rails/resource/extensions/snapshots.js
62
62
  - vendor/assets/javascripts/angularjs/rails/resource/index.js
63
63
  - vendor/assets/javascripts/angularjs/rails/resource/resource.js
64
64
  - vendor/assets/javascripts/angularjs/rails/resource/serialization.js
@@ -85,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
85
  version: 1.3.1
86
86
  requirements: []
87
87
  rubyforge_project:
88
- rubygems_version: 2.0.6
88
+ rubygems_version: 2.1.9
89
89
  signing_key:
90
90
  specification_version: 4
91
91
  summary: AngularJS add-on resource add-on for integrating with Rails
@@ -101,6 +101,7 @@ test_files:
101
101
  - test/lib/angular/angular-sanitize.js
102
102
  - test/lib/angular/angular-scenario.js
103
103
  - test/lib/angular/angular.js
104
+ - test/unit/angularjs/rails/extensions/snapshotsSpec.js
104
105
  - test/unit/angularjs/rails/httpSettingsSpec.js
105
106
  - test/unit/angularjs/rails/interceptorsSpec.js
106
107
  - test/unit/angularjs/rails/nestedUrlsSpec.js
@@ -1,1117 +0,0 @@
1
- /**
2
- * A resource factory inspired by $resource from AngularJS
3
- * @version v1.0.0-pre.2 - 2013-11-24
4
- * @link https://github.com/FineLinePrototyping/angularjs-rails-resource.git
5
- * @author
6
- */
7
-
8
- (function (undefined) {
9
- angular.module('rails', ['ng']);
10
- }());
11
-
12
-
13
-
14
- (function (undefined) {
15
- angular.module('rails').factory('RailsInflector', function() {
16
- function camelize(key) {
17
- if (!angular.isString(key)) {
18
- return key;
19
- }
20
-
21
- // should this match more than word and digit characters?
22
- return key.replace(/_[\w\d]/g, function (match, index, string) {
23
- return index === 0 ? match : string.charAt(index + 1).toUpperCase();
24
- });
25
- }
26
-
27
- function underscore(key) {
28
- if (!angular.isString(key)) {
29
- return key;
30
- }
31
-
32
- // TODO match the latest logic from Active Support
33
- return key.replace(/[A-Z]/g, function (match, index) {
34
- return index === 0 ? match : '_' + match.toLowerCase();
35
- });
36
- }
37
-
38
- function pluralize(value) {
39
- // TODO match Active Support
40
- return value + 's';
41
- }
42
-
43
- return {
44
- camelize: camelize,
45
- underscore: underscore,
46
- pluralize: pluralize
47
- }
48
- });
49
- }());
50
- (function (undefined) {
51
- angular.module('rails').factory('RailsResourceInjector', ['$injector', function($injector) {
52
- /**
53
- * Allow dependencies to be referenced by name or instance. If referenced by name AngularJS $injector
54
- * is used to retrieve the dependency.
55
- *
56
- * @param dependency (string | function) The dependency to retrieve
57
- * @returns {*} The dependency
58
- */
59
- function getDependency(dependency) {
60
- if (dependency) {
61
- return angular.isString(dependency) ? $injector.get(dependency) : dependency
62
- }
63
-
64
- return undefined;
65
- }
66
-
67
- /**
68
- * Looks up and instantiates an instance of the requested service. If the service is not a string then it is
69
- * assumed to be a constuctor function.
70
- *
71
- * @param service (string | function) The service to instantiate
72
- * @returns {*} A new instance of the requested service
73
- */
74
- function createService(service) {
75
- if (service) {
76
- return $injector.instantiate(getDependency(service));
77
- }
78
-
79
- return undefined;
80
- }
81
-
82
- return {
83
- createService: createService,
84
- getDependency: getDependency
85
- }
86
- }]);
87
- }());
88
- /**
89
- * @ngdoc function
90
- * @name rails.railsUrlBuilder
91
- * @function
92
- * @requires $interpolate
93
- *
94
- * @description
95
- *
96
- * Compiles a URL template string into an interpolation function using $interpolate. If no interpolation bindings
97
- * found then {{id}} is appended to the url string.
98
- *
99
- <pre>
100
- expect(railsUrlBuilder('/books')()).toEqual('/books')
101
- expect(railsUrlBuilder('/books')({id: 1})).toEqual('/books/1')
102
- expect(railsUrlBuilder('/authors/{{authorId}}/books/{{id}}')({id: 1, authorId: 2})).toEqual('/authors/2/books/1')
103
- </pre>
104
- *
105
- * If the $interpolate startSymbol and endSymbol have been customized those values should be used instead of {{ and }}
106
- *
107
- * @param {string|function} url If the url is a function then that function is returned. Otherwise the url string
108
- * is passed to $interpolate as an expression.
109
- *
110
- * @returns {function(context)} As stated by $interpolate documentation:
111
- * An interpolation function which is used to compute the interpolated
112
- * string. The function has these parameters:
113
- *
114
- * * `context`: an object against which any expressions embedded in the strings are evaluated
115
- * against.
116
- *
117
- */
118
- (function (undefined) {
119
- angular.module('rails').factory('railsUrlBuilder', ['$interpolate', function($interpolate) {
120
- return function (url) {
121
- var expression;
122
-
123
- if (angular.isFunction(url) || angular.isUndefined(url)) {
124
- return url;
125
- }
126
-
127
- if (url.indexOf($interpolate.startSymbol()) === -1) {
128
- url = url + '/' + $interpolate.startSymbol() + 'id' + $interpolate.endSymbol();
129
- }
130
-
131
- expression = $interpolate(url);
132
-
133
- return function (params) {
134
- url = expression(params);
135
-
136
- if (url.charAt(url.length - 1) === '/') {
137
- url = url.substr(0, url.length - 1);
138
- }
139
-
140
- return url;
141
- };
142
- };
143
-
144
- }])
145
- }());
146
- (function (undefined) {
147
- angular.module('rails').provider('railsSerializer', function() {
148
- var defaultOptions = {
149
- underscore: undefined,
150
- camelize: undefined,
151
- pluralize: undefined,
152
- exclusionMatchers: []
153
- };
154
-
155
- /**
156
- * Configures the underscore method used by the serializer. If not defined then <code>RailsInflector.underscore</code>
157
- * will be used.
158
- *
159
- * @param {function(string):string} fn The function to use for underscore conversion
160
- * @returns {railsSerializerProvider} The provider for chaining
161
- */
162
- this.underscore = function(fn) {
163
- defaultOptions.underscore = fn;
164
- return this;
165
- };
166
-
167
- /**
168
- * Configures the camelize method used by the serializer. If not defined then <code>RailsInflector.camelize</code>
169
- * will be used.
170
- *
171
- * @param {function(string):string} fn The function to use for camelize conversion
172
- * @returns {railsSerializerProvider} The provider for chaining
173
- */
174
- this.camelize = function(fn) {
175
- defaultOptions.camelize = fn;
176
- return this;
177
- };
178
-
179
- /**
180
- * Configures the pluralize method used by the serializer. If not defined then <code>RailsInflector.pluralize</code>
181
- * will be used.
182
- *
183
- * @param {function(string):string} fn The function to use for pluralizing strings.
184
- * @returns {railsSerializerProvider} The provider for chaining
185
- */
186
- this.pluralize = function(fn) {
187
- defaultOptions.pluralize = fn;
188
- return this;
189
- };
190
-
191
- /**
192
- * Configures the array exclusion matchers by the serializer. Exclusion matchers can be one of the following:
193
- * * string - Defines a prefix that is used to test for exclusion
194
- * * RegExp - A custom regular expression that is tested against the attribute name
195
- * * function - A custom function that accepts a string argument and returns a boolean with true indicating exclusion.
196
- *
197
- * @param {Array.<string|function(string):boolean|RegExp} exclusions An array of exclusion matchers
198
- * @returns {railsSerializerProvider} The provider for chaining
199
- */
200
- this.exclusionMatchers = function(exclusions) {
201
- defaultOptions.exclusionMatchers = exclusions;
202
- return this;
203
- };
204
-
205
- this.$get = ['$injector', 'RailsInflector', 'RailsResourceInjector', function ($injector, RailsInflector, RailsResourceInjector) {
206
- defaultOptions.underscore = defaultOptions.underscore || RailsInflector.underscore;
207
- defaultOptions.camelize = defaultOptions.camelize || RailsInflector.camelize;
208
- defaultOptions.pluralize = defaultOptions.pluralize || RailsInflector.pluralize;
209
-
210
- function railsSerializer(options, customizer) {
211
-
212
- function Serializer() {
213
- if (angular.isFunction(options)) {
214
- customizer = options;
215
- options = {};
216
- }
217
-
218
- this.exclusions = {};
219
- this.inclusions = {};
220
- this.serializeMappings = {};
221
- this.deserializeMappings = {};
222
- this.customSerializedAttributes = {};
223
- this.preservedAttributes = {};
224
- this.customSerializers = {};
225
- this.nestedResources = {};
226
- this.options = angular.extend({excludeByDefault: false}, defaultOptions, options || {});
227
-
228
- if (customizer) {
229
- customizer.call(this, this);
230
- }
231
- }
232
-
233
- /**
234
- * Accepts a variable list of attribute names to exclude from JSON serialization.
235
- *
236
- * @param attributeNames... {string} Variable number of attribute name parameters
237
- * @returns {Serializer} this for chaining support
238
- */
239
- Serializer.prototype.exclude = function () {
240
- var exclusions = this.exclusions;
241
-
242
- angular.forEach(arguments, function (attributeName) {
243
- exclusions[attributeName] = false;
244
- });
245
-
246
- return this;
247
- };
248
-
249
- /**
250
- * Accepts a variable list of attribute names that should be included in JSON serialization.
251
- * Using this method will by default exclude all other attributes and only the ones explicitly included using <code>only</code> will be serialized.
252
- * @param attributeNames... {string} Variable number of attribute name parameters
253
- * @returns {Serializer} this for chaining support
254
- */
255
- Serializer.prototype.only = function () {
256
- var inclusions = this.inclusions;
257
- this.options.excludeByDefault = true;
258
-
259
- angular.forEach(arguments, function (attributeName) {
260
- inclusions[attributeName] = true;
261
- });
262
-
263
- return this;
264
- };
265
-
266
- /**
267
- * This is a shortcut for rename that allows you to specify a variable number of attributes that should all be renamed to
268
- * <code>{attributeName}_attributes</code> to work with the Rails nested_attributes feature.
269
- * @param attributeNames... {string} Variable number of attribute name parameters
270
- * @returns {Serializer} this for chaining support
271
- */
272
- Serializer.prototype.nestedAttribute = function () {
273
- var self = this;
274
-
275
- angular.forEach(arguments, function (attributeName) {
276
- self.rename(attributeName, attributeName + '_attributes');
277
- });
278
-
279
- return this;
280
- };
281
-
282
- /**
283
- * Specifies an attribute that is a nested resource within the parent object.
284
- * Nested resources do not imply nested attributes, if you want both you still have to specify call <code>nestedAttribute</code> as well.
285
- *
286
- * A nested resource serves two purposes. First, it defines the resource that should be used when constructing resources from the server.
287
- * Second, it specifies how the nested object should be serialized.
288
- *
289
- * An optional third parameter <code>serializer</code> is available to override the serialization logic
290
- * of the resource in case you need to serialize it differently in multiple contexts.
291
- *
292
- * @param attributeName {string} The name of the attribute that is a nested resource
293
- * @param resource {string | Resource} A reference to the resource that the attribute is a type of.
294
- * @param serializer {string | Serializer} (optional) An optional serializer reference to override the nested resource's default serializer
295
- * @returns {Serializer} this for chaining support
296
- */
297
- Serializer.prototype.resource = function (attributeName, resource, serializer) {
298
- this.nestedResources[attributeName] = resource;
299
-
300
- if (serializer) {
301
- this.serializeWith(attributeName, serializer);
302
- }
303
-
304
- return this;
305
- };
306
-
307
- /**
308
- * Specifies a custom name mapping for an attribute.
309
- * On serializing to JSON the jsonName will be used.
310
- * On deserialization, if jsonName is seen then it will be renamed as javascriptName in the resulting resource.
311
- *
312
- * @param javascriptName {string} The attribute name as it appears in the JavaScript object
313
- * @param jsonName {string} The attribute name as it should appear in JSON
314
- * @param bidirectional {boolean} (optional) Allows turning off the bidirectional renaming, defaults to true.
315
- * @returns {Serializer} this for chaining support
316
- */
317
- Serializer.prototype.rename = function (javascriptName, jsonName, bidirectional) {
318
- this.serializeMappings[javascriptName] = jsonName;
319
-
320
- if (bidirectional || bidirectional === undefined) {
321
- this.deserializeMappings[jsonName] = javascriptName;
322
- }
323
- return this;
324
- };
325
-
326
- /**
327
- * Allows custom attribute creation as part of the serialization to JSON.
328
- *
329
- * @param attributeName {string} The name of the attribute to add
330
- * @param value {*} The value to add, if specified as a function then the function will be called during serialization
331
- * and should return the value to add.
332
- * @returns {Serializer} this for chaining support
333
- */
334
- Serializer.prototype.add = function (attributeName, value) {
335
- this.customSerializedAttributes[attributeName] = value;
336
- return this;
337
- };
338
-
339
-
340
- /**
341
- * Allows the attribute to be preserved unmodified in the resulting object.
342
- *
343
- * @param attributeName {string} The name of the attribute to add
344
- * @returns {Serializer} this for chaining support
345
- */
346
- Serializer.prototype.preserve = function(attributeName) {
347
- this.preservedAttributes[attributeName] = true;
348
- return this;
349
- };
350
-
351
- /**
352
- * Specify a custom serializer to use for an attribute.
353
- *
354
- * @param attributeName {string} The name of the attribute
355
- * @param serializer {string | function} A reference to the custom serializer to use for the attribute.
356
- * @returns {Serializer} this for chaining support
357
- */
358
- Serializer.prototype.serializeWith = function (attributeName, serializer) {
359
- this.customSerializers[attributeName] = serializer;
360
- return this;
361
- };
362
-
363
- /**
364
- * Determines whether or not an attribute should be excluded.
365
- *
366
- * If the option excludeByDefault has been set then attributes will default to excluded and will only
367
- * be included if they have been included using the "only" customization function.
368
- *
369
- * If the option excludeByDefault has not been set then attributes must be explicitly excluded using the "exclude"
370
- * customization function or must be matched by one of the exclusionMatchers.
371
- *
372
- * @param attributeName The name of the attribute to check for exclusion
373
- * @returns {boolean} true if excluded, false otherwise
374
- */
375
- Serializer.prototype.isExcludedFromSerialization = function (attributeName) {
376
- if ((this.options.excludeByDefault && !this.inclusions.hasOwnProperty(attributeName)) || this.exclusions.hasOwnProperty(attributeName)) {
377
- return true;
378
- }
379
-
380
- if (this.options.exclusionMatchers) {
381
- var excluded = false;
382
-
383
- angular.forEach(this.options.exclusionMatchers, function (matcher) {
384
- if (angular.isString(matcher)) {
385
- excluded = excluded || attributeName.indexOf(matcher) === 0;
386
- } else if (angular.isFunction(matcher)) {
387
- excluded = excluded || matcher.call(undefined, attributeName);
388
- } else if (matcher instanceof RegExp) {
389
- excluded = excluded || matcher.test(attributeName);
390
- }
391
- });
392
-
393
- return excluded;
394
- }
395
-
396
- return false;
397
- };
398
-
399
- /**
400
- * Remaps the attribute name to the serialized form which includes:
401
- * - checking for exclusion
402
- * - remapping to a custom value specified by the rename customization function
403
- * - underscoring the name
404
- *
405
- * @param attributeName The current attribute name
406
- * @returns {*} undefined if the attribute should be excluded or the mapped attribute name
407
- */
408
- Serializer.prototype.getSerializedAttributeName = function (attributeName) {
409
- var mappedName = this.serializeMappings[attributeName] || attributeName;
410
-
411
- var mappedNameExcluded = this.isExcludedFromSerialization(mappedName),
412
- attributeNameExcluded = this.isExcludedFromSerialization(attributeName);
413
-
414
- if(this.options.excludeByDefault) {
415
- if(mappedNameExcluded && attributeNameExcluded) {
416
- return undefined;
417
- }
418
- } else {
419
- if (mappedNameExcluded || attributeNameExcluded) {
420
- return undefined;
421
- }
422
- }
423
-
424
- return this.underscore(mappedName);
425
- };
426
-
427
- /**
428
- * Determines whether or not an attribute should be excluded from deserialization.
429
- *
430
- * By default, we do not exclude any attributes from deserialization.
431
- *
432
- * @param attributeName The name of the attribute to check for exclusion
433
- * @returns {boolean} true if excluded, false otherwise
434
- */
435
- Serializer.prototype.isExcludedFromDeserialization = function (attributeName) {
436
- return false;
437
- };
438
-
439
- /**
440
- * Remaps the attribute name to the deserialized form which includes:
441
- * - camelizing the name
442
- * - checking for exclusion
443
- * - remapping to a custom value specified by the rename customization function
444
- *
445
- * @param attributeName The current attribute name
446
- * @returns {*} undefined if the attribute should be excluded or the mapped attribute name
447
- */
448
- Serializer.prototype.getDeserializedAttributeName = function (attributeName) {
449
- var camelizedName = this.camelize(attributeName);
450
-
451
- camelizedName = this.deserializeMappings[attributeName]
452
- || this.deserializeMappings[camelizedName]
453
- || camelizedName;
454
-
455
- if (this.isExcludedFromDeserialization(attributeName) || this.isExcludedFromDeserialization(camelizedName)) {
456
- return undefined;
457
- }
458
-
459
- return camelizedName;
460
- };
461
-
462
- /**
463
- * Returns a reference to the nested resource that has been specified for the attribute.
464
- * @param attributeName The attribute name
465
- * @returns {*} undefined if no nested resource has been specified or a reference to the nested resource class
466
- */
467
- Serializer.prototype.getNestedResource = function (attributeName) {
468
- return RailsResourceInjector.getDependency(this.nestedResources[attributeName]);
469
- };
470
-
471
- /**
472
- * Returns a custom serializer for the attribute if one has been specified. Custom serializers can be specified
473
- * in one of two ways. The serializeWith customization method allows specifying a custom serializer for any attribute.
474
- * Or an attribute could have been specified as a nested resource in which case the nested resource's serializer
475
- * is used. Custom serializers specified using serializeWith take precedence over the nested resource serializer.
476
- *
477
- * @param attributeName The attribute name
478
- * @returns {*} undefined if no custom serializer has been specified or an instance of the Serializer
479
- */
480
- Serializer.prototype.getAttributeSerializer = function (attributeName) {
481
- var resource = this.getNestedResource(attributeName),
482
- serializer = this.customSerializers[attributeName];
483
-
484
- // custom serializer takes precedence over resource serializer
485
- if (serializer) {
486
- return RailsResourceInjector.createService(serializer);
487
- } else if (resource) {
488
- return resource.config.serializer;
489
- }
490
-
491
- return undefined;
492
- };
493
-
494
-
495
- /**
496
- * Prepares the data for serialization to JSON.
497
- *
498
- * @param data The data to prepare
499
- * @returns {*} A new object or array that is ready for JSON serialization
500
- */
501
- Serializer.prototype.serializeValue = function (data) {
502
- var result = data,
503
- self = this;
504
-
505
- if (angular.isArray(data)) {
506
- result = [];
507
-
508
- angular.forEach(data, function (value) {
509
- result.push(self.serializeValue(value));
510
- });
511
- } else if (angular.isObject(data)) {
512
- if (angular.isDate(data)) {
513
- return data;
514
- }
515
- result = {};
516
-
517
- angular.forEach(data, function (value, key) {
518
- // if the value is a function then it can't be serialized to JSON so we'll just skip it
519
- if (!angular.isFunction(value)) {
520
- self.serializeAttribute(result, key, value);
521
- }
522
- });
523
- }
524
-
525
- return result;
526
- };
527
-
528
- /**
529
- * Transforms an attribute and its value and stores it on the parent data object. The attribute will be
530
- * renamed as needed and the value itself will be serialized as well.
531
- *
532
- * @param data The object that the attribute will be added to
533
- * @param attribute The attribute to transform
534
- * @param value The current value of the attribute
535
- */
536
- Serializer.prototype.serializeAttribute = function (data, attribute, value) {
537
- var serializer = this.getAttributeSerializer(attribute),
538
- serializedAttributeName = this.getSerializedAttributeName(attribute);
539
-
540
- // undefined means the attribute should be excluded from serialization
541
- if (serializedAttributeName === undefined) {
542
- return;
543
- }
544
-
545
- data[serializedAttributeName] = serializer ? serializer.serialize(value) : this.serializeValue(value);
546
- };
547
-
548
- /**
549
- * Serializes the data by applying various transformations such as:
550
- * - Underscoring attribute names
551
- * - attribute renaming
552
- * - attribute exclusion
553
- * - custom attribute addition
554
- *
555
- * @param data The data to prepare
556
- * @returns {*} A new object or array that is ready for JSON serialization
557
- */
558
- Serializer.prototype.serialize = function (data) {
559
- var result = this.serializeValue(data),
560
- self = this;
561
-
562
- if (angular.isObject(result)) {
563
- angular.forEach(this.customSerializedAttributes, function (value, key) {
564
- if (angular.isFunction(value)) {
565
- value = value.call(data, data);
566
- }
567
-
568
- self.serializeAttribute(result, key, value);
569
- });
570
- }
571
-
572
- return result;
573
- };
574
-
575
- /**
576
- * Iterates over the data deserializing each entry on arrays and each key/value on objects.
577
- *
578
- * @param data The object to deserialize
579
- * @param Resource (optional) The resource type to deserialize the result into
580
- * @returns {*} A new object or an instance of Resource populated with deserialized data.
581
- */
582
- Serializer.prototype.deserializeValue = function (data, Resource) {
583
- var result = data,
584
- self = this;
585
-
586
- if (angular.isArray(data)) {
587
- result = [];
588
-
589
- angular.forEach(data, function (value) {
590
- result.push(self.deserializeValue(value, Resource));
591
- });
592
- } else if (angular.isObject(data)) {
593
- if (angular.isDate(data)) {
594
- return data;
595
- }
596
-
597
- result = {};
598
-
599
- if (Resource) {
600
- result = new Resource.config.resourceConstructor();
601
- }
602
-
603
- angular.forEach(data, function (value, key) {
604
- self.deserializeAttribute(result, key, value);
605
- });
606
- }
607
-
608
- return result;
609
- };
610
-
611
- /**
612
- * Transforms an attribute and its value and stores it on the parent data object. The attribute will be
613
- * renamed as needed and the value itself will be deserialized as well.
614
- *
615
- * @param data The object that the attribute will be added to
616
- * @param attribute The attribute to transform
617
- * @param value The current value of the attribute
618
- */
619
- Serializer.prototype.deserializeAttribute = function (data, attribute, value) {
620
- var serializer,
621
- NestedResource,
622
- attributeName = this.getDeserializedAttributeName(attribute);
623
-
624
- // undefined means the attribute should be excluded from serialization
625
- if (attributeName === undefined) {
626
- return;
627
- }
628
-
629
- serializer = this.getAttributeSerializer(attributeName);
630
- NestedResource = this.getNestedResource(attributeName);
631
-
632
- // preserved attributes are assigned unmodified
633
- if (this.preservedAttributes[attributeName]) {
634
- data[attributeName] = value;
635
- } else {
636
- data[attributeName] = serializer ? serializer.deserialize(value, NestedResource) : this.deserializeValue(value, NestedResource);
637
- }
638
- };
639
-
640
- /**
641
- * Deserializes the data by applying various transformations such as:
642
- * - Camelizing attribute names
643
- * - attribute renaming
644
- * - attribute exclusion
645
- * - nested resource creation
646
- *
647
- * @param data The object to deserialize
648
- * @param Resource (optional) The resource type to deserialize the result into
649
- * @returns {*} A new object or an instance of Resource populated with deserialized data
650
- */
651
- Serializer.prototype.deserialize = function (data, Resource) {
652
- // just calls deserializeValue for now so we can more easily add on custom attribute logic for deserialize too
653
- return this.deserializeValue(data, Resource);
654
- };
655
-
656
- Serializer.prototype.pluralize = function (value) {
657
- if (this.options.pluralize) {
658
- return this.options.pluralize(value);
659
- }
660
- return value;
661
- };
662
-
663
- Serializer.prototype.underscore = function (value) {
664
- if (this.options.underscore) {
665
- return this.options.underscore(value);
666
- }
667
- return value;
668
- };
669
-
670
- Serializer.prototype.camelize = function (value) {
671
- if (this.options.camelize) {
672
- return this.options.camelize(value);
673
- }
674
- return value;
675
- }
676
-
677
- return Serializer;
678
- }
679
-
680
- railsSerializer.defaultOptions = defaultOptions;
681
- return railsSerializer;
682
- }];
683
- });
684
- }());
685
-
686
- (function (undefined) {
687
- angular.module('rails').factory('railsRootWrappingTransformer', function () {
688
- return function (data, resource) {
689
- var result = {};
690
- result[angular.isArray(data) ? resource.config.pluralName : resource.config.name] = data;
691
- return result;
692
- };
693
- });
694
-
695
- angular.module('rails').factory('railsRootWrappingInterceptor', function () {
696
- return function (promise) {
697
- var resource = promise.resource;
698
-
699
- if (!resource) {
700
- return promise;
701
- }
702
-
703
- return promise.then(function (response) {
704
- if (response.data && response.data.hasOwnProperty(resource.config.name)) {
705
- response.data = response.data[resource.config.name];
706
- } else if (response.data && response.data.hasOwnProperty(resource.config.pluralName)) {
707
- response.data = response.data[resource.config.pluralName];
708
- }
709
-
710
- return response;
711
- });
712
- };
713
- });
714
-
715
- angular.module('rails').provider('RailsResource', function () {
716
- var defaultOptions = {
717
- rootWrapping: true,
718
- updateMethod: 'put',
719
- httpConfig: {},
720
- defaultParams: undefined
721
- };
722
-
723
- this.rootWrapping = function (value) {
724
- defaultOptions.rootWrapping = value;
725
- return this;
726
- };
727
-
728
- this.updateMethod = function (value) {
729
- defaultOptions.updateMethod = value;
730
- return this;
731
- };
732
-
733
- this.httpConfig = function (value) {
734
- defaultOptions.httpConfig = value;
735
- return this;
736
- };
737
-
738
- this.defaultParams = function (value) {
739
- defaultOptions.defaultParams = value;
740
- return this;
741
- };
742
-
743
- this.$get = ['$http', '$q', 'railsUrlBuilder', 'railsSerializer', 'railsRootWrappingTransformer', 'railsRootWrappingInterceptor', 'RailsResourceInjector',
744
- function ($http, $q, railsUrlBuilder, railsSerializer, railsRootWrappingTransformer, railsRootWrappingInterceptor, RailsResourceInjector) {
745
-
746
- function appendPath(url, path) {
747
- if (path) {
748
- if (path[0] !== '/') {
749
- url += '/';
750
- }
751
-
752
- url += path;
753
- }
754
-
755
- return url;
756
- }
757
-
758
- function forEachDependency(list, callback) {
759
- var dependency;
760
-
761
- for (var i = 0, len = list.length; i < len; i++) {
762
- dependency = list[i];
763
-
764
- if (angular.isString(dependency)) {
765
- dependency = list[i] = RailsResourceInjector.getDependency(dependency);
766
- }
767
-
768
- callback(dependency);
769
- }
770
- }
771
-
772
- function RailsResource(value) {
773
- var instance = this;
774
- if (value) {
775
- var immediatePromise = function (data) {
776
- return {
777
- resource: RailsResource,
778
- context: instance,
779
- response: data,
780
- then: function (callback) {
781
- this.response = callback(this.response, this.resource, this.context);
782
- return immediatePromise(this.response);
783
- }
784
- }
785
- };
786
-
787
- var data = this.constructor.callInterceptors(immediatePromise({data: value}), this).response.data;
788
- angular.extend(this, data);
789
- }
790
- }
791
-
792
- RailsResource.extend = function (child) {
793
- // Extend logic copied from CoffeeScript generated code
794
- var __hasProp = {}.hasOwnProperty, parent = this;
795
- for (var key in parent) {
796
- if (__hasProp.call(parent, key)) child[key] = parent[key];
797
- }
798
-
799
- function ctor() {
800
- this.constructor = child;
801
- }
802
-
803
- ctor.prototype = parent.prototype;
804
- child.prototype = new ctor();
805
- child.__super__ = parent.prototype;
806
- return child;
807
- };
808
-
809
- // allow calling configure multiple times to set configuration options and override values from inherited resources
810
- RailsResource.configure = function (cfg) {
811
- cfg = cfg || {};
812
-
813
- if (this.config) {
814
- cfg = angular.extend({}, this.config, cfg);
815
- }
816
-
817
- this.config = {};
818
- this.config.url = cfg.url;
819
- this.config.rootWrapping = cfg.rootWrapping === undefined ? defaultOptions.rootWrapping : cfg.rootWrapping; // using undefined check because config.rootWrapping || true would be true when config.rootWrapping === false
820
- this.config.httpConfig = cfg.httpConfig || defaultOptions.httpConfig;
821
- this.config.httpConfig.headers = angular.extend({'Accept': 'application/json', 'Content-Type': 'application/json'}, this.config.httpConfig.headers || {});
822
- this.config.defaultParams = cfg.defaultParams || defaultOptions.defaultParams;
823
- this.config.updateMethod = (cfg.updateMethod || defaultOptions.updateMethod).toLowerCase();
824
-
825
- this.config.requestTransformers = cfg.requestTransformers ? cfg.requestTransformers.slice(0) : [];
826
- this.config.responseInterceptors = cfg.responseInterceptors ? cfg.responseInterceptors.slice(0) : [];
827
- this.config.afterResponseInterceptors = cfg.afterResponseInterceptors ? cfg.afterResponseInterceptors.slice(0) : [];
828
-
829
- // strings and functions are not considered objects by angular.isObject()
830
- if (angular.isObject(cfg.serializer)) {
831
- this.config.serializer = cfg.serializer;
832
- } else {
833
- this.config.serializer = RailsResourceInjector.createService(cfg.serializer || railsSerializer());
834
- }
835
-
836
- this.config.name = this.config.serializer.underscore(cfg.name);
837
-
838
- // we don't want to turn undefined name into "undefineds" then the plural name won't update when the name is set
839
- if (this.config.name) {
840
- this.config.pluralName = this.config.serializer.underscore(cfg.pluralName || this.config.serializer.pluralize(this.config.name));
841
- }
842
-
843
- this.config.urlBuilder = railsUrlBuilder(this.config.url);
844
- this.config.resourceConstructor = this;
845
- };
846
-
847
- RailsResource.configure({});
848
-
849
- RailsResource.setUrl = function (url) {
850
- this.configure({url: url});
851
- };
852
-
853
- RailsResource.buildUrl = function (context) {
854
- return this.config.urlBuilder(context);
855
- };
856
-
857
- /**
858
- * Add a callback to run on response and construction.
859
- * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
860
- * constructor is the resource class calling the function,
861
- * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
862
- */
863
- RailsResource.beforeResponse = function (fn) {
864
- fn = RailsResourceInjector.getDependency(fn);
865
- this.config.responseInterceptors.push(function (promise) {
866
- return promise.then(function (response) {
867
- fn(response.data, promise.resource.config.resourceConstructor, promise.context);
868
- return response;
869
- });
870
- });
871
- };
872
-
873
- /**
874
- * Add a callback to run after response has been processed. These callbacks are not called on object construction.
875
- * @param fn(response data, constructor) - response data is either the resource instance returned or an array of resource instances and constructor is the resource class calling the function
876
- */
877
- RailsResource.afterResponse = function (fn) {
878
- fn = RailsResourceInjector.getDependency(fn);
879
- this.config.afterResponseInterceptors.push(function (promise) {
880
- return promise.then(function (response) {
881
- fn(response, promise.resource.config.resourceConstructor);
882
- return response;
883
- });
884
- });
885
- };
886
-
887
- /**
888
- * Adds a function to run after serializing the data to send to the server, but before root-wrapping it.
889
- * @param fn (data, constructor) - data object is the serialized resource instance, and constructor the resource class calling the function
890
- */
891
- RailsResource.beforeRequest = function (fn) {
892
- fn = RailsResourceInjector.getDependency(fn);
893
- this.config.requestTransformers.push(function (data, resource) {
894
- return fn(data, resource.config.resourceConstructor) || data;
895
- });
896
- };
897
-
898
- // transform data for request:
899
- RailsResource.transformData = function (data) {
900
- var config = this.config;
901
- data = config.serializer.serialize(data);
902
-
903
- forEachDependency(this.config.requestTransformers, function (transformer) {
904
- data = transformer(data, config.resourceConstructor);
905
- });
906
-
907
- if (config.rootWrapping) {
908
- data = railsRootWrappingTransformer(data, config.resourceConstructor);
909
- }
910
-
911
- return data;
912
- };
913
-
914
- // transform data on response:
915
- RailsResource.callInterceptors = function (promise, context) {
916
- var config = this.config;
917
-
918
- promise = promise.then(function (response) {
919
- // store off the data in case something (like our root unwrapping) assigns data as a new object
920
- response.originalData = response.data;
921
- return response;
922
- });
923
-
924
- if (config.rootWrapping) {
925
- promise.resource = config.resourceConstructor;
926
- promise = railsRootWrappingInterceptor(promise);
927
- }
928
-
929
- promise.then(function (response) {
930
- response.data = config.serializer.deserialize(response.data, config.resourceConstructor);
931
- return response;
932
- });
933
-
934
- // data is now deserialized. call response interceptors including beforeResponse
935
- forEachDependency(config.responseInterceptors, function (interceptor) {
936
- promise.resource = config.resourceConstructor;
937
- promise.context = context;
938
- promise = interceptor(promise);
939
- });
940
-
941
- return promise;
942
- };
943
-
944
- // transform data after response has been converted to a resource instance:
945
- RailsResource.callAfterInterceptors = function (promise) {
946
- var config = this.config;
947
- // data is now deserialized. call response interceptors including afterResponse
948
- forEachDependency(config.afterResponseInterceptors, function (interceptor) {
949
- promise.resource = config.resourceConstructor;
950
- promise = interceptor(promise);
951
- });
952
-
953
- return promise;
954
- };
955
-
956
- RailsResource.processResponse = function (promise) {
957
- promise = this.callInterceptors(promise).then(function (response) {
958
- return response.data;
959
- });
960
-
961
- return this.callAfterInterceptors(promise);
962
- };
963
-
964
- RailsResource.getParameters = function (queryParams) {
965
- var params;
966
-
967
- if (this.config.defaultParams) {
968
- params = this.config.defaultParams;
969
- }
970
-
971
- if (angular.isObject(queryParams)) {
972
- params = angular.extend(params || {}, queryParams);
973
- }
974
-
975
- return params;
976
- };
977
-
978
- RailsResource.getHttpConfig = function (queryParams) {
979
- var params = this.getParameters(queryParams);
980
-
981
- if (params) {
982
- return angular.extend({params: params}, this.config.httpConfig);
983
- }
984
-
985
- return angular.copy(this.config.httpConfig);
986
- };
987
-
988
- /**
989
- * Returns a URL from the given parameters. You can override this method on your resource definitions to provide
990
- * custom logic for building your URLs or you can utilize the parameterized url strings to substitute values in the
991
- * URL string.
992
- *
993
- * The parameters in the URL string follow the normal Angular binding expression using {{ and }} for the start/end symbols.
994
- *
995
- * If the context is a number and the URL string does not contain an id parameter then the number is appended
996
- * to the URL string.
997
- *
998
- * If the context is a number and the URL string does
999
- * @param context
1000
- * @param path {string} (optional) An additional path to append to the URL
1001
- * @return {string}
1002
- */
1003
- RailsResource.$url = RailsResource.resourceUrl = function (context, path) {
1004
- if (!angular.isObject(context)) {
1005
- context = {id: context};
1006
- }
1007
-
1008
- return appendPath(this.buildUrl(context || {}), path);
1009
- };
1010
-
1011
- RailsResource.$get = function (url, queryParams) {
1012
- return this.processResponse($http.get(url, this.getHttpConfig(queryParams)));
1013
- };
1014
-
1015
- RailsResource.query = function (queryParams, context) {
1016
- return this.$get(this.resourceUrl(context), queryParams);
1017
- };
1018
-
1019
- RailsResource.get = function (context, queryParams) {
1020
- return this.$get(this.resourceUrl(context), queryParams);
1021
- };
1022
-
1023
- /**
1024
- * Returns the URL for this resource.
1025
- *
1026
- * @param path {string} (optional) An additional path to append to the URL
1027
- * @returns {string} The URL for the resource
1028
- */
1029
- RailsResource.prototype.$url = function (path) {
1030
- return appendPath(this.constructor.resourceUrl(this), path);
1031
- };
1032
-
1033
- RailsResource.prototype.processResponse = function (promise) {
1034
- promise = this.constructor.callInterceptors(promise, this);
1035
-
1036
- promise = promise.then(angular.bind(this, function (response) {
1037
- // we may not have response data
1038
- if (response.hasOwnProperty('data') && angular.isObject(response.data)) {
1039
- angular.extend(this, response.data);
1040
- }
1041
-
1042
- return this;
1043
- }));
1044
-
1045
- return this.constructor.callAfterInterceptors(promise);
1046
- };
1047
-
1048
- angular.forEach(['post', 'put', 'patch'], function (method) {
1049
- RailsResource['$' + method] = function (url, data) {
1050
- var config;
1051
- // clone so we can manipulate w/o modifying the actual instance
1052
- data = this.transformData(angular.copy(data));
1053
- config = angular.extend({method: method, url: url, data: data}, this.getHttpConfig());
1054
- return this.processResponse($http(config));
1055
- };
1056
-
1057
- RailsResource.prototype['$' + method] = function (url) {
1058
- var data, config;
1059
- // clone so we can manipulate w/o modifying the actual instance
1060
- data = this.constructor.transformData(angular.copy(this, {}));
1061
- config = angular.extend({method: method, url: url, data: data}, this.constructor.getHttpConfig());
1062
- return this.processResponse($http(config));
1063
-
1064
- };
1065
- });
1066
-
1067
- RailsResource.prototype.create = function () {
1068
- return this.$post(this.$url(), this);
1069
- };
1070
-
1071
- RailsResource.prototype.update = function () {
1072
- return this['$' + this.constructor.config.updateMethod](this.$url(), this);
1073
- };
1074
-
1075
- RailsResource.prototype.isNew = function () {
1076
- return this.id == null;
1077
- };
1078
-
1079
- RailsResource.prototype.save = function () {
1080
- if (this.isNew()) {
1081
- return this.create();
1082
- } else {
1083
- return this.update();
1084
- }
1085
- };
1086
-
1087
- RailsResource['$delete'] = function (url) {
1088
- return this.processResponse($http['delete'](url, this.getHttpConfig()));
1089
- };
1090
-
1091
- RailsResource.prototype['$delete'] = function (url) {
1092
- return this.processResponse($http['delete'](url, this.constructor.getHttpConfig()));
1093
- };
1094
-
1095
- //using ['delete'] instead of .delete for IE7/8 compatibility
1096
- RailsResource.prototype.remove = RailsResource.prototype['delete'] = function () {
1097
- return this.$delete(this.$url());
1098
- };
1099
-
1100
- return RailsResource;
1101
- }];
1102
- });
1103
-
1104
- angular.module('rails').factory('railsResourceFactory', ['RailsResource', function (RailsResource) {
1105
- return function (config) {
1106
- function Resource() {
1107
- Resource.__super__.constructor.apply(this, arguments);
1108
- }
1109
-
1110
- RailsResource.extend(Resource);
1111
- Resource.configure(config);
1112
-
1113
- return Resource;
1114
- }
1115
- }]);
1116
-
1117
- }());