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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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