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

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.
@@ -478,6 +478,25 @@ describe('railsResourceFactory', function () {
478
478
  $httpBackend.flush();
479
479
  });
480
480
 
481
+ it('should not require query params on $delete', function () {
482
+ $httpBackend.expectDELETE('/test').respond(200);
483
+ Test.$delete('/test');
484
+ $httpBackend.flush();
485
+ });
486
+
487
+ it('should add query params passed to class $delete', function () {
488
+ $httpBackend.expectDELETE('/test?a=1').respond(200);
489
+ Test.$delete('/test', {a: 1});
490
+ $httpBackend.flush();
491
+ });
492
+
493
+ it('should add query params passed to instance $delete', function () {
494
+ var data = new Test({abcDef: 'xyz'});
495
+
496
+ $httpBackend.expectDELETE('/test?a=1').respond(200);
497
+ data.$delete('/test', {a: 1});
498
+ $httpBackend.flush();
499
+ });
481
500
  });
482
501
 
483
502
  describe('plural', function() {
@@ -3,33 +3,13 @@ describe('root wrapping', function () {
3
3
 
4
4
  beforeEach(module('rails'));
5
5
 
6
- var q, rootScope,
7
- transformer, interceptor,
8
- config = {config: {name: 'test', pluralName: 'tests'}};
6
+ var q, rootScope, railsRootWrapper, Resource;
9
7
 
10
-
11
- function testTransform(wrappedData, unwrappedData) {
12
- var result, resultPromise,
13
- deferred = q.defer();
14
-
15
- expect(transformer(unwrappedData, config)).toEqualData(wrappedData);
16
- deferred.promise.resource = config;
17
- expect(resultPromise = interceptor(deferred.promise)).toBeDefined();
18
-
19
- resultPromise.then(function (response) {
20
- result = response;
21
- });
22
-
23
- deferred.resolve({data: wrappedData});
24
- rootScope.$digest(); // needed for $q to actually run callbacks
25
- expect(result).toEqualData({data: unwrappedData});
26
- }
27
-
28
- beforeEach(inject(function ($rootScope, $q, railsRootWrappingTransformer, railsRootWrappingInterceptor) {
8
+ beforeEach(inject(function ($rootScope, $q, railsResourceFactory, _railsRootWrapper_) {
29
9
  q = $q;
30
10
  rootScope = $rootScope;
31
- transformer = railsRootWrappingTransformer;
32
- interceptor = railsRootWrappingInterceptor;
11
+ Resource = railsResourceFactory({name: 'test', pluralName: 'tests'});
12
+ railsRootWrapper = _railsRootWrapper_;
33
13
  }));
34
14
 
35
15
  it('should handle null root', function() {
@@ -43,4 +23,9 @@ describe('root wrapping', function () {
43
23
  it('should transform object', function() {
44
24
  testTransform({test: {abc: 'xyz', def: 'abc'}}, {abc: 'xyz', def: 'abc'});
45
25
  });
26
+
27
+ function testTransform(wrappedData, unwrappedData) {
28
+ expect(railsRootWrapper.wrap(unwrappedData, Resource)).toEqualData(wrappedData);
29
+ expect(railsRootWrapper.unwrap({data: wrappedData}, Resource)).toEqualData({data: unwrappedData});
30
+ }
46
31
  });
@@ -1,4 +1,4 @@
1
- describe('transformers', function () {
1
+ describe('deprecated request transformers', function () {
2
2
  'use strict';
3
3
  var $httpBackend, $rootScope, factory, Test, testTransformer,
4
4
  config = {
@@ -30,7 +30,6 @@ describe('transformers', function () {
30
30
  $httpBackend.verifyNoOutstandingRequest();
31
31
  });
32
32
 
33
-
34
33
  it('should be able to add transformer using name', function() {
35
34
  var Resource, testConfig = {};
36
35
 
@@ -8,9 +8,9 @@
8
8
  };
9
9
 
10
10
  RailsResourceSnapshotsMixin.extended = function (Resource) {
11
- Resource.afterResponse(function (result) {
12
- if (result.hasOwnProperty('$snapshots') && angular.isArray(result.$snapshots)) {
13
- result.$snapshots.length = 0;
11
+ Resource.intercept('afterResponse', function (result, resource, context) {
12
+ if (context && context.hasOwnProperty('$snapshots') && angular.isArray(context.$snapshots)) {
13
+ context.$snapshots.length = 0;
14
14
  }
15
15
  });
16
16
 
@@ -109,7 +109,10 @@
109
109
  numVersions = snapshotsLength;
110
110
  }
111
111
 
112
- this.rollbackTo(this.$snapshots.length - numVersions);
112
+ if (snapshotsLength) {
113
+ this.rollbackTo(this.$snapshots.length - numVersions);
114
+ }
115
+
113
116
  return true;
114
117
  }
115
118
 
@@ -1,21 +1,12 @@
1
1
  (function (undefined) {
2
- angular.module('rails').factory('railsRootWrappingTransformer', function () {
3
- return function (data, resource) {
4
- var result = {};
5
- result[angular.isArray(data) ? resource.config.pluralName : resource.config.name] = data;
6
- return result;
7
- };
8
- });
9
-
10
- angular.module('rails').factory('railsRootWrappingInterceptor', function () {
11
- return function (promise) {
12
- var resource = promise.resource;
13
-
14
- if (!resource) {
15
- return promise;
16
- }
17
-
18
- return promise.then(function (response) {
2
+ angular.module('rails').factory('railsRootWrapper', function () {
3
+ return {
4
+ wrap: function (data, resource) {
5
+ var result = {};
6
+ result[angular.isArray(data) ? resource.config.pluralName : resource.config.name] = data;
7
+ return result;
8
+ },
9
+ unwrap: function (response, resource) {
19
10
  if (response.data && response.data.hasOwnProperty(resource.config.name)) {
20
11
  response.data = response.data[resource.config.name];
21
12
  } else if (response.data && response.data.hasOwnProperty(resource.config.pluralName)) {
@@ -23,7 +14,7 @@
23
14
  }
24
15
 
25
16
  return response;
26
- });
17
+ }
27
18
  };
28
19
  });
29
20
 
@@ -34,6 +25,7 @@
34
25
  httpConfig: {},
35
26
  defaultParams: undefined,
36
27
  underscoreParams: true,
28
+ fullResponse: false,
37
29
  extensions: []
38
30
  };
39
31
 
@@ -91,6 +83,16 @@
91
83
  return this;
92
84
  };
93
85
 
86
+ /**
87
+ * Configures whether the full response from $http is returned or just the result data.
88
+ * @param {boolean} value true to return full $http response. Defaults to false.
89
+ * @returns {RailsResourceProvider} The provider instance
90
+ */
91
+ this.fullResponse = function (value) {
92
+ defaultOptions.fullResponse = value;
93
+ return this;
94
+ };
95
+
94
96
  /**
95
97
  * List of RailsResource extensions to include by default.
96
98
  *
@@ -105,28 +107,16 @@
105
107
  return this;
106
108
  };
107
109
 
108
- this.$get = ['$http', '$q', 'railsUrlBuilder', 'railsSerializer', 'railsRootWrappingTransformer', 'railsRootWrappingInterceptor', 'RailsResourceInjector', 'RailsInflector',
109
- function ($http, $q, railsUrlBuilder, railsSerializer, railsRootWrappingTransformer, railsRootWrappingInterceptor, RailsResourceInjector, RailsInflector) {
110
+ this.$get = ['$http', '$q', 'railsUrlBuilder', 'railsSerializer', 'railsRootWrapper', 'RailsResourceInjector',
111
+ function ($http, $q, railsUrlBuilder, railsSerializer, railsRootWrapper, RailsResourceInjector) {
110
112
 
111
113
  function RailsResource(value) {
112
- var instance = this;
113
- this.$snapshots = [];
114
-
115
114
  if (value) {
116
- var immediatePromise = function (data) {
117
- return {
118
- resource: RailsResource,
119
- context: instance,
120
- response: data,
121
- then: function (callback) {
122
- this.response = callback(this.response, this.resource, this.context);
123
- return immediatePromise(this.response);
124
- }
125
- };
126
- };
127
-
128
- var data = this.constructor.callInterceptors(immediatePromise({data: value}), this).response.data;
129
- angular.extend(this, data);
115
+ var response = this.constructor.deserialize({data: value});
116
+ if (this.constructor.config.rootWrapping) {
117
+ response = railsRootWrapper.unwrap(response, this.constructor);
118
+ }
119
+ angular.extend(this, response.data);
130
120
  }
131
121
  }
132
122
 
@@ -226,10 +216,12 @@
226
216
  this.config.defaultParams = cfg.defaultParams || defaultOptions.defaultParams;
227
217
  this.config.underscoreParams = booleanParam(cfg.underscoreParams, defaultOptions.underscoreParams);
228
218
  this.config.updateMethod = (cfg.updateMethod || defaultOptions.updateMethod).toLowerCase();
219
+ this.config.fullResponse = booleanParam(cfg.fullResponse, defaultOptions.fullResponse);
229
220
 
230
221
  this.config.requestTransformers = cfg.requestTransformers ? cfg.requestTransformers.slice(0) : [];
231
222
  this.config.responseInterceptors = cfg.responseInterceptors ? cfg.responseInterceptors.slice(0) : [];
232
223
  this.config.afterResponseInterceptors = cfg.afterResponseInterceptors ? cfg.afterResponseInterceptors.slice(0) : [];
224
+ this.config.interceptors = cfg.interceptors ? cfg.interceptors.slice(0) : [];
233
225
 
234
226
  this.config.serializer = RailsResourceInjector.getService(cfg.serializer || railsSerializer());
235
227
 
@@ -265,94 +257,241 @@
265
257
  };
266
258
 
267
259
  /**
268
- * Add a callback to run on response and construction.
260
+ * Interceptors utilize $q promises to allow for both synchronous and asynchronous processing during
261
+ * a request / response cycle.
262
+ *
263
+ * Interceptors can be added as a service factory name or as an object with properties matching one
264
+ * or more of the phases. Each property should have a value of a function to be called during that phase.
265
+ *
266
+ * There are multiple phases for both request and response. In addition, each phase has a corresponding
267
+ * error phase to handle promise rejections.
268
+ *
269
+ * Each request phase interceptor is called with the $http config object, the resource constructor, and if
270
+ * applicable the resource instance. The interceptor is free to modify the config or create a new one.
271
+ * The interceptor function must return a valid $http config or a promise that will eventually resolve
272
+ * to a config object.
273
+ *
274
+ * The valid request phases are:
275
+ *
276
+ * * beforeRequest: Interceptors are called prior to any data serialization or root wrapping.
277
+ * * beforeRequestError: Interceptors get called when a previous interceptor threw an error or
278
+ * resolved with a rejection.
279
+ * * beforeRequestWrapping: Interceptors are called after data serialization but before root wrapping.
280
+ * * beforeRequestWrappingError: Interceptors get called when a previous interceptor threw an error or
281
+ * resolved with a rejection.
282
+ * * request: Interceptors are called after any data serialization and root wrapping have been performed.
283
+ * * requestError: Interceptors get called when a previous interceptor threw an error or
284
+ * resolved with a rejection.
285
+ *
286
+ * The beforeResponse and response interceptors are called with the $http response object,
287
+ * the resource constructor, and if applicable the resource instance. The afterResponse interceptors
288
+ * are typically called with the response data instead of the full response object unless the config option
289
+ * fullResponse has been set to true. Like the request interceptor callbacks the response callbacks can
290
+ * manipulate the data or return new data. The interceptor function must return
291
+ *
292
+ * The valid response phases are:
293
+ *
294
+ * * beforeResponse: Interceptors are called prior to any data processing.
295
+ * * beforeResponseError: Interceptors get called when a previous interceptor threw an error or
296
+ * resolved with a rejection.
297
+ * * beforeResponseDeserialize: Interceptors are called after root unwrapping but prior to data deserializing.
298
+ * * beforeResponseDeserializeError: Interceptors get called when a previous interceptor threw an error or
299
+ * resolved with a rejection.
300
+ * * response: Interceptors are called after the data has been deserialized and root unwrapped but
301
+ * prior to the data being copied to the resource instance if applicable.
302
+ * * responseError: Interceptors get called when a previous interceptor threw an error or
303
+ * resolved with a rejection.
304
+ * * afterResponse: Interceptors are called at the very end of the response chain after all processing
305
+ * has been completed. The value of the first parameter is one of the following:
306
+ * - resource instance: When fullResponse is false and the operation was called on a resource instance.
307
+ * - response data: When fullResponse is false and the operation was called on the resource class.
308
+ * - $http response: When fullResponse is true
309
+ * * afterResponseError: Interceptors get called when a previous interceptor threw an error or
310
+ * resolved with a rejection.
311
+ *
312
+ * @param {String | Object} interceptor
313
+ */
314
+ RailsResource.addInterceptor = function (interceptor) {
315
+ this.config.interceptors.push(interceptor);
316
+ };
317
+
318
+ /**
319
+ * Adds an interceptor callback function for the specified phase.
320
+ * @param {String} phase The interceptor phase, one of:
321
+ * beforeRequest, request, beforeResponse, response, afterResponse
322
+ * @param fn The function to call.
323
+ */
324
+ RailsResource.intercept = function (phase, fn) {
325
+ var interceptor = {};
326
+ fn = RailsResourceInjector.getDependency(fn);
327
+
328
+ interceptor[phase] = function (value, resourceConstructor, context) {
329
+ return fn(value, resourceConstructor, context) || value;
330
+ };
331
+
332
+ this.addInterceptor(interceptor);
333
+ };
334
+
335
+ /**
336
+ * Adds interceptor on 'beforeRequest' phase.
337
+ * @param fn(httpConfig, constructor, context) - httpConfig is the config object to pass to $http,
338
+ * constructor is the resource class calling the function,
339
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
340
+ */
341
+ RailsResource.interceptBeforeRequest = function (fn) {
342
+ this.intercept('beforeRequest', fn);
343
+ };
344
+
345
+ /**
346
+ * Adds interceptor on 'beforeRequestWrapping' phase.
347
+ * @param fn(httpConfig, constructor, context) - httpConfig is the config object to pass to $http,
348
+ * constructor is the resource class calling the function,
349
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
350
+ */
351
+ RailsResource.interceptBeforeRequestWrapping = function (fn) {
352
+ this.intercept('beforeRequestWrapping', fn);
353
+ };
354
+
355
+ /**
356
+ * Adds interceptor on 'request' phase.
357
+ * @param fn(httpConfig, constructor, context) - httpConfig is the config object to pass to $http,
358
+ * constructor is the resource class calling the function,
359
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
360
+ */
361
+ RailsResource.interceptRequest = function (fn) {
362
+ this.intercept('request', fn);
363
+ };
364
+
365
+ /**
366
+ * Adds interceptor on 'beforeResponse' phase.
367
+ * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
368
+ * constructor is the resource class calling the function,
369
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
370
+ */
371
+ RailsResource.interceptBeforeResponse = function (fn) {
372
+ this.intercept('beforeResponse', fn);
373
+ };
374
+
375
+ /**
376
+ * Adds interceptor on 'beforeResponseDeserialize' phase.
377
+ * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
378
+ * constructor is the resource class calling the function,
379
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
380
+ */
381
+ RailsResource.interceptBeforeResponseDeserialize = function (fn) {
382
+ this.intercept('beforeResponseDeserialize', fn);
383
+ };
384
+
385
+ /**
386
+ * Adds interceptor on 'response' phase.
387
+ * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
388
+ * constructor is the resource class calling the function,
389
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
390
+ */
391
+ RailsResource.interceptResponse = function (fn) {
392
+ this.intercept('response', fn);
393
+ };
394
+
395
+ /**
396
+ * Adds interceptor on 'afterResponse' phase.
397
+ * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
398
+ * constructor is the resource class calling the function,
399
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
400
+ */
401
+ RailsResource.interceptAfterResponse = function (fn) {
402
+ this.intercept('afterResponse', fn);
403
+ };
404
+
405
+ /**
406
+ * Deprecated, see interceptors
407
+ * Add a callback to run on response.
408
+ * @deprecated since version 1.0.0, use interceptResponse instead
269
409
  * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
270
410
  * constructor is the resource class calling the function,
271
411
  * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
272
412
  */
273
413
  RailsResource.beforeResponse = function (fn) {
274
414
  fn = RailsResourceInjector.getDependency(fn);
275
- this.config.responseInterceptors.push(function (promise) {
276
- return promise.then(function (response) {
277
- fn(response.data, promise.resource.config.resourceConstructor, promise.context);
278
- return response;
279
- });
415
+ this.interceptResponse(function (response, resource, context) {
416
+ fn(response.data, resource.config.resourceConstructor, context);
417
+ return response;
280
418
  });
281
419
  };
282
420
 
283
421
  /**
422
+ * Deprecated, see interceptors
284
423
  * Add a callback to run after response has been processed. These callbacks are not called on object construction.
424
+ * @deprecated since version 1.0.0, use interceptAfterResponse instead
285
425
  * @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
286
426
  */
287
427
  RailsResource.afterResponse = function (fn) {
288
428
  fn = RailsResourceInjector.getDependency(fn);
289
- this.config.afterResponseInterceptors.push(function (promise) {
290
- return promise.then(function (response) {
291
- fn(response, promise.resource.config.resourceConstructor);
292
- return response;
293
- });
429
+ this.interceptAfterResponse(function (response, resource, context) {
430
+ fn(response, resource.config.resourceConstructor, context);
431
+ return response;
294
432
  });
295
433
  };
296
434
 
297
435
  /**
436
+ * Deprecated, see interceptors
298
437
  * Adds a function to run after serializing the data to send to the server, but before root-wrapping it.
438
+ * @deprecated since version 1.0.0, use interceptBeforeRequestWrapping instead
299
439
  * @param fn (data, constructor) - data object is the serialized resource instance, and constructor the resource class calling the function
300
440
  */
301
441
  RailsResource.beforeRequest = function (fn) {
302
442
  fn = RailsResourceInjector.getDependency(fn);
303
- this.config.requestTransformers.push(function (data, resource) {
304
- return fn(data, resource.config.resourceConstructor) || data;
443
+ this.interceptBeforeRequestWrapping(function (httpConfig, resource) {
444
+ httpConfig.data = fn(httpConfig.data, resource.config.resourceConstructor) || httpConfig.data;
445
+ return httpConfig;
305
446
  });
306
447
  };
307
448
 
308
- // transform data for request:
309
- RailsResource.transformData = function (data) {
310
- var config = this.config;
311
- data = config.serializer.serialize(data);
312
-
313
- forEachDependency(this.config.requestTransformers, function (transformer) {
314
- data = transformer(data, config.resourceConstructor);
315
- });
316
-
317
- if (config.rootWrapping) {
318
- data = railsRootWrappingTransformer(data, config.resourceConstructor);
449
+ RailsResource.serialize = function (httpConfig) {
450
+ if (httpConfig.data) {
451
+ httpConfig.data = this.config.serializer.serialize(httpConfig.data);
319
452
  }
320
453
 
321
- return data;
454
+ return httpConfig;
322
455
  };
323
456
 
324
- // transform data on response:
325
- RailsResource.callInterceptors = function (promise, context) {
326
- var config = this.config;
327
-
328
- promise = promise.then(function (response) {
329
- // store off the data in case something (like our root unwrapping) assigns data as a new object
330
- response.originalData = response.data;
331
- return response;
332
- });
333
-
334
- if (config.rootWrapping) {
335
- promise.resource = config.resourceConstructor;
336
- promise = railsRootWrappingInterceptor(promise);
337
- }
338
-
339
- promise.then(function (response) {
340
- response.data = config.serializer.deserialize(response.data, config.resourceConstructor);
341
- return response;
342
- });
457
+ /**
458
+ * Deserializes the response data on the $http response. Stores the original version of the data
459
+ * on the response as "originalData" and sets the deserialized data in the "data" property.
460
+ * @param response The $http response object
461
+ * @returns {*} The $http response
462
+ */
463
+ RailsResource.deserialize = function (response) {
464
+ // store off the data so we don't lose access to it after deserializing and unwrapping
465
+ response.originalData = response.data;
466
+ response.data = this.config.serializer.deserialize(response.data, this.config.resourceConstructor);
467
+ return response;
468
+ };
343
469
 
344
- // data is now deserialized. call response interceptors including beforeResponse
470
+ /**
471
+ * Deprecated, see interceptors
472
+ * Transform data after response has been converted to a resource instance
473
+ * @deprecated
474
+ * @param promise
475
+ * @param context
476
+ */
477
+ RailsResource.callResponseInterceptors = function (promise, context) {
478
+ var config = this.config;
345
479
  forEachDependency(config.responseInterceptors, function (interceptor) {
346
480
  promise.resource = config.resourceConstructor;
347
481
  promise.context = context;
348
482
  promise = interceptor(promise);
349
483
  });
350
-
351
484
  return promise;
352
485
  };
353
486
 
354
- // transform data after response has been converted to a resource instance:
355
- RailsResource.callAfterInterceptors = function (promise) {
487
+ /**
488
+ * Deprecated, see interceptors
489
+ * Transform data after response has been converted to a resource instance
490
+ * @deprecated
491
+ * @param promise
492
+ * @param context
493
+ */
494
+ RailsResource.callAfterResponseInterceptors = function (promise) {
356
495
  var config = this.config;
357
496
  // data is now deserialized. call response interceptors including afterResponse
358
497
  forEachDependency(config.afterResponseInterceptors, function (interceptor) {
@@ -363,12 +502,100 @@
363
502
  return promise;
364
503
  };
365
504
 
366
- RailsResource.processResponse = function (promise) {
367
- promise = this.callInterceptors(promise).then(function (response) {
368
- return response.data;
505
+ RailsResource.runInterceptorPhase = function (phase, context, promise) {
506
+ var config = this.config, chain = [];
507
+
508
+ forEachDependency(config.interceptors, function (interceptor) {
509
+ if (interceptor[phase] || interceptor[phase + 'Error']) {
510
+ chain.push(interceptor[phase], interceptor[phase + 'Error']);
511
+ }
512
+ });
513
+
514
+ while (chain.length) {
515
+ var thenFn = chain.shift();
516
+ var rejectFn = chain.shift();
517
+
518
+ promise = promise.then(createInterceptorSuccessCallback(thenFn, config.resourceConstructor, context),
519
+ createInterceptorRejectionCallback(rejectFn, config.resourceConstructor, context));
520
+ }
521
+
522
+ return promise;
523
+ };
524
+
525
+ /**
526
+ * Executes an HTTP request using $http.
527
+ *
528
+ * This method is used by all RailsResource operations that execute HTTP requests. Handles serializing
529
+ * the request data using the resource serializer, root wrapping (if enabled), deserializing the response
530
+ * data using the resource serializer, root unwrapping (if enabled), and copying the result back into the
531
+ * resource context if applicable. Executes interceptors at each phase of the request / response to allow
532
+ * users to build synchronous & asynchronous customizations to manipulate the data as necessary.
533
+ *
534
+ * @param httpConfig The config to pass to $http, see $http docs for details
535
+ * @param context An optional reference to the resource instance that is the context for the operation.
536
+ * If specified, the result data will be copied into the context during the response handling.
537
+ * @param resourceConfigOverrides An optional set of RailsResource configuration options overrides.
538
+ * These overrides allow users to build custom operations more easily with different resource settings.
539
+ * @returns {Promise} The promise that will eventually be resolved after all request / response handling
540
+ * has completed.
541
+ */
542
+ RailsResource.$http = function (httpConfig, context, resourceConfigOverrides) {
543
+ var config = angular.extend(angular.copy(this.config), resourceConfigOverrides || {}),
544
+ resourceConstructor = config.resourceConstructor,
545
+ promise = $q.when(httpConfig);
546
+
547
+ promise = this.runInterceptorPhase('beforeRequest', context, promise).then(function (httpConfig) {
548
+ httpConfig = resourceConstructor.serialize(httpConfig);
549
+
550
+ forEachDependency(config.requestTransformers, function (transformer) {
551
+ httpConfig.data = transformer(httpConfig.data, config.resourceConstructor);
552
+ });
553
+
554
+ return httpConfig;
369
555
  });
370
556
 
371
- return this.callAfterInterceptors(promise);
557
+ promise = this.runInterceptorPhase('beforeRequestWrapping', context, promise);
558
+
559
+ if (config.rootWrapping) {
560
+ promise = promise.then(function (httpConfig) {
561
+ httpConfig.data = railsRootWrapper.wrap(httpConfig.data, config.resourceConstructor);
562
+ return httpConfig;
563
+ });
564
+ }
565
+
566
+ promise = this.runInterceptorPhase('request', context, promise).then(function (httpConfig) {
567
+ return $http(httpConfig);
568
+ });
569
+
570
+ promise = this.runInterceptorPhase('beforeResponse', context, promise);
571
+
572
+ if (config.rootWrapping) {
573
+ promise = promise.then(function (response) {
574
+ return railsRootWrapper.unwrap(response, config.resourceConstructor);
575
+ });
576
+ }
577
+
578
+ promise = this.runInterceptorPhase('beforeResponseDeserialize', context, promise).then(function (response) {
579
+ return resourceConstructor.deserialize(response);
580
+ });
581
+
582
+ promise = this.callResponseInterceptors(promise, context);
583
+ promise = this.runInterceptorPhase('response', context, promise).then(function (response) {
584
+ if (context) {
585
+ // we may not have response data
586
+ if (response.hasOwnProperty('data') && angular.isObject(response.data)) {
587
+ angular.extend(context, response.data);
588
+ }
589
+ }
590
+
591
+ return config.fullResponse ? response : (context || response.data);
592
+ });
593
+
594
+ promise = this.callAfterResponseInterceptors(promise, context);
595
+ promise = this.runInterceptorPhase('afterResponse', context, promise);
596
+ promise.resource = config.resourceConstructor;
597
+ promise.context = context;
598
+ return promise;
372
599
  };
373
600
 
374
601
  /**
@@ -442,7 +669,7 @@
442
669
  };
443
670
 
444
671
  RailsResource.$get = function (url, queryParams) {
445
- return this.processResponse($http.get(url, this.getHttpConfig(queryParams)));
672
+ return this.$http(angular.extend({method: 'get', url: url}, this.getHttpConfig(queryParams)));
446
673
  };
447
674
 
448
675
  RailsResource.query = function (queryParams, context) {
@@ -463,36 +690,32 @@
463
690
  return appendPath(this.constructor.resourceUrl(this), path);
464
691
  };
465
692
 
466
- RailsResource.prototype.processResponse = function (promise) {
467
- promise = this.constructor.callInterceptors(promise, this);
468
-
469
- promise = promise.then(angular.bind(this, function (response) {
470
- // we may not have response data
471
- if (response.hasOwnProperty('data') && angular.isObject(response.data)) {
472
- angular.extend(this, response.data);
473
- }
474
-
475
- return this;
476
- }));
477
-
478
- return this.constructor.callAfterInterceptors(promise);
693
+ /**
694
+ * Executes $http with the resource instance as the context.
695
+ *
696
+ * @param httpConfig The config to pass to $http, see $http docs for details
697
+ * @param context An optional reference to the resource instance that is the context for the operation.
698
+ * If specified, the result data will be copied into the context during the response handling.
699
+ * @param resourceConfigOverrides An optional set of RailsResource configuration options overrides.
700
+ * These overrides allow users to build custom operations more easily with different resource settings.
701
+ * @returns {Promise} The promise that will eventually be resolved after all request / response handling
702
+ * has completed.
703
+ */
704
+ RailsResource.prototype.$http = function (httpConfig, resourceConfigOverrides) {
705
+ return this.constructor.$http(httpConfig, this, resourceConfigOvverides);
479
706
  };
480
707
 
481
708
  angular.forEach(['post', 'put', 'patch'], function (method) {
482
709
  RailsResource['$' + method] = function (url, data) {
483
- var config;
484
710
  // clone so we can manipulate w/o modifying the actual instance
485
- data = this.transformData(angular.copy(data));
486
- config = angular.extend({method: method, url: url, data: data}, this.getHttpConfig());
487
- return this.processResponse($http(config));
711
+ data = angular.copy(data);
712
+ return this.$http(angular.extend({method: method, url: url, data: data}, this.getHttpConfig()));
488
713
  };
489
714
 
490
715
  RailsResource.prototype['$' + method] = function (url) {
491
- var data, config;
492
716
  // clone so we can manipulate w/o modifying the actual instance
493
- data = this.constructor.transformData(angular.copy(this, {}));
494
- config = angular.extend({method: method, url: url, data: data}, this.constructor.getHttpConfig());
495
- return this.processResponse($http(config));
717
+ var data = angular.copy(this, {});
718
+ return this.constructor.$http(angular.extend({method: method, url: url, data: data}, this.constructor.getHttpConfig()), this);
496
719
 
497
720
  };
498
721
  });
@@ -518,12 +741,12 @@
518
741
  }
519
742
  };
520
743
 
521
- RailsResource.$delete = function (url) {
522
- return this.processResponse($http['delete'](url, this.getHttpConfig()));
744
+ RailsResource.$delete = function (url, queryParams) {
745
+ return this.$http(angular.extend({method: 'delete', url: url}, this.getHttpConfig(queryParams)));
523
746
  };
524
747
 
525
- RailsResource.prototype.$delete = function (url) {
526
- return this.processResponse($http['delete'](url, this.constructor.getHttpConfig()));
748
+ RailsResource.prototype.$delete = function (url, queryParams) {
749
+ return this.constructor.$http(angular.extend({method: 'delete', url: url}, this.constructor.getHttpConfig(queryParams)), this);
527
750
  };
528
751
 
529
752
  //using ['delete'] instead of .delete for IE7/8 compatibility
@@ -600,6 +823,19 @@
600
823
  function booleanParam(value, defaultValue) {
601
824
  return angular.isUndefined(value) ? defaultValue : value;
602
825
  }
826
+
827
+ function createInterceptorSuccessCallback(thenFn, resourceConstructor, context) {
828
+ return function (data) {
829
+ return (thenFn || angular.identity)(data, resourceConstructor, context);
830
+ };
831
+ }
832
+
833
+ function createInterceptorRejectionCallback(rejectFn, resourceConstructor, context) {
834
+ return function (rejection) {
835
+ // can't use identity because we need to return a rejected promise to keep the error chain going
836
+ return rejectFn ? rejectFn(rejection, resourceConstructor, context) : $q.reject(rejection);
837
+ };
838
+ }
603
839
  }];
604
840
  });
605
841