angularjs-rails-resource 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjM2MTM3OTRmYjQzZDE1ZGNhZjk1YTM2MDk4ZmUxNTJjMzBmYzBmOA==
5
+ data.tar.gz: !binary |-
6
+ NjAyYWZmZGEwNzQ2MjhmODk1OWIyMTRiMWY1NDBmOWQxN2VkMGQ3Ng==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MWU4ZDAzMjZhMGM5NTIyZmZlMzY4YjQ3M2I3MzJmMmI1MDI4ZWI2ZjE5NmZh
10
+ Njc5NzU0YzNkNjFlYjI2OGM1NzJlNDczOTQxOGU4ZDgwNDgzYzVjZjE2Yzgy
11
+ ODQyYWI4NTM3NGYyMWY0MDlmNjkyM2M0ZGQyOWY0NTEwM2E2Njc=
12
+ data.tar.gz: !binary |-
13
+ ODkyMTcyNmZhYjVjYmNlZWM3NTAzNjY4ZDYzYmU0Yjk3NGQxYjQyNzAxMWQy
14
+ ODE5NjQ4YmNiMmNhOTRjZGQyOGZjNjZkOTUyYzg3NWI5MmRiYjQ5ZDZjM2Fk
15
+ MGY5ZDM2YzZiNGZiZDNlYTdhMjA4M2UyNTZhMTYyMmQ4ODEwM2Q=
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ <a name="0.2.1"></a>
2
+ # 0.2.1
3
+ ## Bug Fixes
4
+
5
+ ## Features
6
+ - Added context property to before response interceptors to have access to the calling resource instance in the case of create/update/delete.
7
+ - Added after response interceptors to be able to define custom callbacks that execute on all resources after method completion.
8
+
1
9
  <a name="0.2.0"></a>
2
10
  # 0.2.0
3
11
  ## Breaking Changes
data/EXAMPLES.md CHANGED
@@ -13,12 +13,12 @@ a resource instance for the book.
13
13
  return railsResourceFactory({url: '/books', name: 'book'});
14
14
  }]);
15
15
 
16
- angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
16
+ angular.module('book.services').factory('Author', ['railsResourceFactory', 'railsSerializer', function (railsResourceFactory, railsSerializer) {
17
17
  return railsResourceFactory({
18
18
  url: '/authors',
19
19
  name: 'author',
20
20
  serializer: railsSerializer(function () {
21
- this.nestedResource('books', 'Book');
21
+ this.resource('books', 'Book');
22
22
  });
23
23
  });
24
24
  }]);
@@ -32,6 +32,50 @@ a resource instance for the book.
32
32
  }
33
33
  }]);
34
34
 
35
+ # Nested attributes
36
+ While we don't have logic for full nested attributes support, the new serializer does allow you to specify which fields
37
+ should be passed with the <code>_attributes</code> suffix.
38
+
39
+ angular.module('book.services').factory('Book', ['railsResourceFactory', 'railsSerializer', function (railsResourceFactory, railsSerializer) {
40
+ return railsResourceFactory({
41
+ url: '/books',
42
+ name: 'book',
43
+ serializer: railsSerializer(function () {
44
+ this.nestedAttribute('author');
45
+ });
46
+ });
47
+ }]);
48
+
49
+ # Excluding attributes from serialization
50
+ Sometimes you don't want to serialize certain fields when updating an object. Take for instance the case of the author on a book.
51
+ We know that we don't accept nested attributes for the author on the server so we want to exclude it from the JSON to reduce
52
+ the amount of data being sent to the server.
53
+
54
+ angular.module('book.services').factory('Book', ['railsResourceFactory', 'railsSerializer', function (railsResourceFactory railsSerializer) {
55
+ return railsResourceFactory({
56
+ url: '/books',
57
+ name: 'book',
58
+ serializer: railsSerializer(function () {
59
+ this.exclude('author');
60
+ });
61
+ });
62
+ }]);
63
+
64
+
65
+ # Only allowing specific attributes for serialization
66
+ You can also be very restrictive and only include specific attributes that you want to send to the server. All other attribtues
67
+ would be excluded by default.
68
+
69
+ angular.module('book.services').factory('Book', ['railsResourceFactory', 'railsSerializer', function (railsResourceFactory. railsSerializer) {
70
+ return railsResourceFactory({
71
+ url: '/books',
72
+ name: 'book',
73
+ serializer: railsSerializer(function () {
74
+ this.only('id', 'isbn', 'publicationDate');
75
+ });
76
+ });
77
+ }]);
78
+
35
79
 
36
80
  # Adding custom methods to a resource
37
81
  You can add additional "class" or "instance" methods by modifying the resource returned from the factory call.
@@ -97,7 +141,7 @@ passes the resulting promise to the processResponse method which will perform th
97
141
  }]);
98
142
 
99
143
  # Specifying Transformer
100
- Transformers can be by specifying an array of transformers in the configuration options passed to railsResourceFactory.
144
+ Transformers can be specified by an array of transformers in the configuration options passed to railsResourceFactory.
101
145
  However, a cleaner eway to write it is to use the <code>beforeRequest</code> which can take a new anonymous function or
102
146
  a function returned by a factory if you want to share a transformer across multiple resources.
103
147
 
data/README.md CHANGED
@@ -87,7 +87,7 @@ When defining a resource, you can pass a custom [serializer](#serializers) using
87
87
  serializer: railsSerializer(function () {
88
88
  this.exclude('birthDate', 'books');
89
89
  this.nestedAttribute('books');
90
- this.nestedResource('books', 'Book');
90
+ this.resource('books', 'Book');
91
91
  })
92
92
  });
93
93
 
@@ -134,6 +134,7 @@ The following options are available for the config object passed to the factory
134
134
  * **defaultParams** *(optional)* - If the resource expects a default set of query params on every call you can specify them here.
135
135
  * **requestTransformers** *(optional) - See [Transformers / Interceptors](#transformers--interceptors)
136
136
  * **responseInterceptors** *(optional)* - See [Transformers / Interceptors](#transformers--interceptors)
137
+ * **afterResponseInterceptors** *(optional)* - See [Transformers / Interceptors](#transformers--interceptors)
137
138
 
138
139
  **NOTE:** The names should be specified using camel case when using the key transformations because that happens before the root wrapping by default.
139
140
  For example, you should specify "publishingCompany" and "publishingCompanies" instead of "publishing_company" and "publishing_companies".
@@ -203,12 +204,16 @@ passed to the function is still a Resource instance as long as another transform
203
204
  * **resource** {Resource class} - The Resource class that is calling the function
204
205
  * **returns** {object | undefined} - If the function returns a new object that object will instead be used for serialization.
205
206
 
206
- * beforeResponse(fn(data, resource)) - See [Interceptors](#interceptors) for more information. The function is called prior to deserialization so the data represents the
207
- raw data returned from the server. Since the data has not been deserialized into a Resource instance yet, none of the [Resource instance methods](#instance-methods) are available.
208
- * fn(data, resource) {function} - The function to add as an interceptor
207
+ * beforeResponse(fn(data, resource, context)) - See [Interceptors](#interceptors) for more information. The function is called after the response data has been deserialized.
208
+ * fn(data, resource, context) {function} - The function to add as an interceptor
209
209
  * **data** {object} - The data received from the server
210
- * **resource** {Resource class} - The Resource class that is calling the function
211
- * **returns** {object | undefined} - If the function returns a new object that object will instead be used for serialization.
210
+ * **resource** {Resource function} - The Resource constructor that is calling the function
211
+ * **context** {Resource|undefined} - The Resource instance that is calling the function or undefined if called from a class level method (get, query).
212
+
213
+ * afterResponse(fn(data, resource, context)) - See [Interceptors](#interceptors) for more information. This function is called after all internal processing and beforeResponse callbacks have been completed.
214
+ * fn(data, resource) {function} - The function to add as an interceptor
215
+ * **data** {object} - The result, either an array of resource instances or a single resource instance.
216
+ * **resource** {Resource function} - The Resource constructor that is calling the function
212
217
 
213
218
  ### Instance Methods
214
219
  The instance methods can be used on any instance (created manually or returned in a promise response) of a resource.
@@ -244,6 +249,8 @@ that dictate how serialization and deserialization is performed. Users can: ren
244
249
  with the ability to exclude all attributes by default and only serialize ones explicitly allowed, specify other serializers to use
245
250
  for an attribute and even specify that an attribute is a nested resource.
246
251
 
252
+ AngularJS automatically excludes all attribute keys that begin with $ in their toJson code.
253
+
247
254
  ### railsSerializer
248
255
  * railsSerializer(options, customizer) - Builds a Serializer constructor function using the configuration options specified.
249
256
  * **options** {object} (optional) - Configuration options to alter the default operation of the serializers. This parameter can be excluded and the
@@ -274,7 +281,7 @@ Serializers have the following available configuration options:
274
281
  * **returns** {string} - The name as it should appear in the resource
275
282
  * excludeByDefault {boolean} - Specifies whether or not JSON serialization should exclude all attributes from serialization by default.
276
283
  * default: false
277
- * exclusionMatchers {array} - An list of rules that should be applied to determine whether or not an attribute should be excluded. For instance, $resource excludes all variables that start with $. The values in the array can be one of the following types:
284
+ * exclusionMatchers {array} - An list of rules that should be applied to determine whether or not an attribute should be excluded. The values in the array can be one of the following types:
278
285
  * string - Defines a prefix that is used to test for exclusion
279
286
  * RegExp - A custom regular expression that is tested against the attribute name
280
287
  * function - A custom function that accepts a string argument and returns a boolean with true indicating exclusion.
@@ -292,7 +299,7 @@ The customizer function passed to the railsSerializer has available to it the fo
292
299
 
293
300
  * resource (attributeName, resource, serializer) - Specifies an attribute that is a nested resource within the parent object. Nested resources do not imply nested attributes, if you want both you still have to specify call <code>nestedAttribute</code> as well. A nested resource serves two purposes. First, it defines the resource that should be used when constructing resources from the server. Second, it specifies how the nested object should be serialized. An optional third parameter <code>serializer</code> is available to override the serialization logic of the resource in case you need to serialize it differently in multiple contexts.
294
301
 
295
- * add (attributeName, value) - Allows custom attribute creation as part of the serialization to JSON. This method may be renamed to allow for specifying different custom attributes during serialization to allow for custom attributes during deserialization as well. The parameter <code>value</code> can be defined as function that takes a parameter of the containing object and returns a value that should be included in the JSON.
302
+ * add (attributeName, value) - Allows custom attribute creation as part of the serialization to JSON. The parameter <code>value</code> can be defined as function that takes a parameter of the containing object and returns a value that should be included in the JSON.
296
303
 
297
304
  * serializeWith (attributeName, serializer) - Specifies a custom serializer that should be used for the attribute. The serializer can be specified either as a <code>string</code> reference to a registered service or as a Serializer constructor returned from <code>railsSerializer</code>
298
305
 
@@ -306,7 +313,7 @@ The transformers and interceptors can be specified using an array containing tra
306
313
  that can be resolved using Angular's DI. The transformers / interceptors concept was prior to the [serializers](#serializers) but
307
314
  we kept the API available because there may be use cases that can be accomplished with these but not the serializers.
308
315
 
309
- ### Transformers
316
+ ### Request Transformers
310
317
  Transformer functions are called to transform the data before we send it to $http for POST/PUT.
311
318
 
312
319
  A transformer function is called with two parameters:
@@ -319,17 +326,32 @@ The resource also exposes a class method <code>beforeRequest(fn)</code> that acc
319
326
  to the list of transformers for the resource class. The function passed to <code>beforeRequest</code> is called with the same two parameters. One difference
320
327
  is that the functions are not required to return the data, though they still can if they need to return a new object. See [example](EXAMPLES.md#specifying-transformer).
321
328
 
322
- ### Interceptors
329
+ ### Response Interceptors
323
330
  Interceptor functions utilize [$q promises](http://docs.angularjs.org/api/ng.$q) to process the data returned from the server.
324
331
 
325
332
  The interceptor is called with the promise returned from $http and is expected to return a promise for chaining. The promise passed to each
326
- interceptor contains a reference to the resource to expose the configured options of the resource. Each interceptor promise
327
- is expected to return the response or a $q.reject. See [Promises](#promises) for more information about the promise data.
333
+ interceptor contains two additional properties:
334
+ * **resource** - The Resource constructor function for the resource calling the interceptor
335
+ * **context** - The Resource instance if applicable (create, update, delete) calling the interceptor
336
+
337
+ Each interceptor promise is expected to return the response or a $q.reject. See [Promises](#promises) for more information about the promise data.
328
338
 
329
339
  The resource also exposes a class method <code>beforeResponse(fn)</code> that accepts a function to execute and automatically wraps it as an interceptor and appends it
330
- to the list of interceptors for the resource class. Functions added with beforeResponse don't need to know anything about promises since they are automatically wrapped
340
+ to the list of interceptors for the resource class. Functions added with <code>beforeResponse</code> don't need to know anything about promises since they are automatically wrapped
331
341
  as an interceptor.
332
342
 
343
+ ### After Response Interceptors
344
+ After response interceptors are called after all processing and response interceptors have completed. An after response interceptor is analogous to
345
+ chaining a promise after the resource method call but is instead for all methods.
346
+
347
+ The after response interceptors are called with the final processing promise and is expected to return a promise for chaining. The promise is resolved
348
+ with the result of the operation which will be either a resource instance or an array of resource instances. The promise passed to the interceptor
349
+ has the following additional property:
350
+ * **resource** - The Resource constructor function for the resource calling the interceptor
351
+
352
+ The resource also exposes a class method <code>afterResponse(fn)</code> that accepts a function to execute and automatically wraps it as an interceptor and appends it
353
+ to the list of after response interceptors for the resource class. Functions added with <code>afterResponse</code> don't need to know anything about promises since they are automatically wrapped
354
+ as an interceptor.
333
355
 
334
356
  ## Tests
335
357
  The tests are written using [Jasmine](http://pivotal.github.com/jasmine/) and are run using [Karma](https://github.com/karma-runner/karma).
data/karma.conf.js CHANGED
@@ -1,22 +1,22 @@
1
- basePath = './';
2
-
3
- files = [
4
- JASMINE,
5
- JASMINE_ADAPTER,
6
- 'test/lib/angular/angular.js',
7
- 'test/lib/angular/angular-bootstrap.js',
8
- 'test/lib/angular/angular-loader.js',
9
- 'test/lib/angular/angular-sanitize.js',
10
- 'test/lib/angular/angular-mocks.js',
11
- 'vendor/assets/javascripts/**/*.js',
12
- 'test/unit/**/*.js'
13
- ];
14
-
15
- autoWatch = true;
16
-
17
- browsers = ['Chrome'];
18
-
19
- junitReporter = {
20
- outputFile: 'test_out/unit.xml',
21
- suite: 'unit'
1
+ module.exports = function(config) {
2
+ config.set({
3
+ basePath: '.',
4
+ autoWatch: true,
5
+ browsers: ['Chrome'],
6
+ frameworks: ['jasmine'],
7
+ reporters: ['progress', 'junit'],
8
+ files: [
9
+ 'test/lib/angular/angular.js',
10
+ 'test/lib/angular/angular-bootstrap.js',
11
+ 'test/lib/angular/angular-loader.js',
12
+ 'test/lib/angular/angular-sanitize.js',
13
+ 'test/lib/angular/angular-mocks.js',
14
+ 'vendor/assets/javascripts/**/*.js',
15
+ 'test/unit/**/*.js'
16
+ ],
17
+ junitReporter: {
18
+ outputFile: 'test_out/unit.xml',
19
+ suite: 'unit'
20
+ }
21
+ })
22
22
  };
@@ -1,7 +1,7 @@
1
1
  module Angularjs
2
2
  module Rails
3
3
  module Resource
4
- VERSION = "0.2.0"
4
+ VERSION = '0.2.1'
5
5
  end
6
6
  end
7
7
  end
data/package.json CHANGED
@@ -2,10 +2,15 @@
2
2
  "name": "angularjs-rails-resource",
3
3
  "version": "0.0.0",
4
4
  "dependencies": {
5
- "karma": "~0.8"
6
5
  },
7
6
  "scripts": {
8
- "test": "./node_modules/.bin/karma start --single-run --browsers PhantomJS"
7
+ "test": "./node_modules/.bin/karma start --single-run --browsers PhantomJS"
8
+ },
9
+ "devDependencies": {
10
+ "karma": "~0.10",
11
+ "karma-jasmine": "~0.1",
12
+ "karma-chrome-launcher": "~0.1",
13
+ "karma-phantomjs-launcher": "~0.1",
14
+ "karma-junit-reporter": "~0.1"
9
15
  }
10
16
  }
11
-
@@ -1,12 +1,12 @@
1
1
  describe('transformers', function () {
2
2
  'use strict';
3
- var $httpBackend, $rootScope, factory, Test, testInterceptor,
3
+ var $httpBackend, $rootScope, factory, Test, testInterceptor, testAfterInterceptor,
4
4
  config = {
5
5
  url: '/test',
6
6
  name: 'test'
7
7
  };
8
8
 
9
- beforeEach(function() {
9
+ beforeEach(function () {
10
10
  module('rails');
11
11
 
12
12
  angular.module('rails').factory('railsTestInterceptor', function () {
@@ -17,89 +17,219 @@ describe('transformers', function () {
17
17
  });
18
18
  }
19
19
  });
20
+
21
+ angular.module('rails').factory('railsTestAfterInterceptor', function () {
22
+ return function (promise) {
23
+ return promise.then(function (resource) {
24
+ resource.interceptorAdded = 'x';
25
+ return resource;
26
+ });
27
+ }
28
+ });
20
29
  });
21
30
 
22
- beforeEach(inject(function (_$httpBackend_, _$rootScope_, railsResourceFactory, railsTestInterceptor) {
31
+ beforeEach(inject(function (_$httpBackend_, _$rootScope_, railsResourceFactory, railsTestInterceptor, railsTestAfterInterceptor) {
23
32
  $httpBackend = _$httpBackend_;
24
33
  $rootScope = _$rootScope_;
25
34
  factory = railsResourceFactory;
26
35
  Test = railsResourceFactory(config);
27
36
  testInterceptor = railsTestInterceptor;
37
+ testAfterInterceptor = railsTestAfterInterceptor;
28
38
  }));
29
39
 
30
- afterEach(function() {
40
+ afterEach(function () {
31
41
  $httpBackend.verifyNoOutstandingExpectation();
32
42
  $httpBackend.verifyNoOutstandingRequest();
33
43
  });
34
44
 
45
+ describe('before response', function () {
46
+ it('should be able to reference interceptor using name', function () {
47
+ var promise, result, Resource, testConfig = {};
48
+
49
+ $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
35
50
 
36
- it('should be able to reference interceptor using name', function() {
37
- var promise, result, Resource, testConfig = {};
51
+ angular.copy(config, testConfig);
52
+ testConfig.responseInterceptors = ['railsTestInterceptor'];
53
+ Resource = factory(testConfig);
38
54
 
39
- $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
55
+ expect(promise = Resource.get(123)).toBeDefined();
40
56
 
41
- angular.copy(config, testConfig);
42
- testConfig.responseInterceptors = ['railsTestInterceptor'];
43
- Resource = factory(testConfig);
57
+ promise.then(function (response) {
58
+ result = response;
59
+ });
44
60
 
45
- expect(promise = Resource.get(123)).toBeDefined();
61
+ $httpBackend.flush();
46
62
 
47
- promise.then(function (response) {
48
- result = response;
63
+ expect(result).toBeInstanceOf(Resource);
64
+ expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
49
65
  });
50
66
 
51
- $httpBackend.flush();
67
+ it('should be able to add interceptor using reference', function () {
68
+ var promise, result, Resource, testConfig = {};
69
+
70
+ $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
71
+
72
+
73
+ angular.copy(config, testConfig);
74
+ testConfig.responseInterceptors = [testInterceptor];
75
+ Resource = factory(testConfig);
76
+
77
+ expect(promise = Resource.get(123)).toBeDefined();
78
+
79
+ promise.then(function (response) {
80
+ result = response;
81
+ });
82
+
83
+ $httpBackend.flush();
84
+
85
+ expect(result).toBeInstanceOf(Resource);
86
+ expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
87
+ });
88
+
89
+ it('should be able to add interceptor using beforeResponse', function () {
90
+ var promise, result, Resource, interceptorCalled = false;
91
+
92
+ $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
93
+
94
+ Resource = factory(config);
95
+
96
+ Resource.beforeResponse(function (resource, constructor, context) {
97
+ expect(resource).toEqualData({id: 123, abcDef: 'xyz'});
98
+ expect(constructor).toEqual(Resource);
99
+ expect(context).toBeUndefined();
100
+ interceptorCalled = true;
101
+ });
102
+
103
+ expect(promise = Resource.get(123)).toBeDefined();
104
+
105
+ promise.then(function (response) {
106
+ result = response;
107
+ });
108
+
109
+ $httpBackend.flush();
110
+
111
+ expect(result).toBeInstanceOf(Resource);
112
+ expect(result).toEqualData({id: 123, abcDef: 'xyz'});
113
+ expect(interceptorCalled).toBeTruthy();
114
+ });
115
+
116
+ it('should set context to resource instance', function () {
117
+ var instance, Resource, interceptorCalled = false;
118
+
119
+ $httpBackend.expectPOST('/test').respond(200, {id: 123, abc_def: 'xyz'});
52
120
 
53
- expect(result).toBeInstanceOf(Resource);
54
- expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
121
+ Resource = factory(config);
122
+
123
+ instance = new Resource({abcDef: 'xyz'});
124
+
125
+ Resource.beforeResponse(function (resource, constructor, context) {
126
+ expect(resource).toEqualData({id: 123, abcDef: 'xyz'});
127
+ expect(constructor).toEqual(Resource);
128
+ expect(context).toBeInstanceOf(Resource);
129
+ expect(context).toEqualData(instance);
130
+ interceptorCalled = true;
131
+ });
132
+
133
+ instance.save();
134
+
135
+ $httpBackend.flush();
136
+
137
+ expect(interceptorCalled).toBeTruthy();
138
+ });
55
139
  });
56
140
 
57
- it('should be able to add interceptor using reference', function() {
58
- var promise, result, Resource, testConfig = {};
141
+ describe('after response', function () {
142
+ it('should be able to reference interceptor using name', function () {
143
+ var promise, result, Resource, testConfig = {};
59
144
 
60
- $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
145
+ $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
61
146
 
147
+ angular.copy(config, testConfig);
148
+ testConfig.afterResponseInterceptors = ['railsTestAfterInterceptor'];
149
+ Resource = factory(testConfig);
62
150
 
63
- angular.copy(config, testConfig);
64
- testConfig.responseInterceptors = [testInterceptor];
65
- Resource = factory(testConfig);
151
+ expect(promise = Resource.get(123)).toBeDefined();
66
152
 
67
- expect(promise = Resource.get(123)).toBeDefined();
153
+ promise.then(function (response) {
154
+ result = response;
155
+ });
68
156
 
69
- promise.then(function (response) {
70
- result = response;
157
+ $httpBackend.flush();
158
+
159
+ expect(result).toBeInstanceOf(Resource);
160
+ expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
71
161
  });
72
162
 
73
- $httpBackend.flush();
163
+ it('should be able to add interceptor using reference', function () {
164
+ var promise, result, Resource, testConfig = {};
165
+
166
+ $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
167
+
74
168
 
75
- expect(result).toBeInstanceOf(Resource);
76
- expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
77
- });
78
-
79
- it('should be able to add interceptor using beforeResponse', function() {
80
- var promise, result, Resource, interceptorCalled = false;
169
+ angular.copy(config, testConfig);
170
+ testConfig.afterResponseInterceptors = [testAfterInterceptor];
171
+ Resource = factory(testConfig);
81
172
 
82
- $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
173
+ expect(promise = Resource.get(123)).toBeDefined();
83
174
 
84
- Resource = factory(config);
175
+ promise.then(function (response) {
176
+ result = response;
177
+ });
85
178
 
86
- Resource.beforeResponse(function (resource, constructor) {
87
- expect(resource).toEqualData({id: 123, abcDef: 'xyz'});
88
- expect(constructor).toEqual(Resource);
89
- interceptorCalled = true;
179
+ $httpBackend.flush();
180
+
181
+ expect(result).toBeInstanceOf(Resource);
182
+ expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
90
183
  });
91
184
 
92
- expect(promise = Resource.get(123)).toBeDefined();
185
+ it('should be able to add interceptor using afterResponse', function () {
186
+ var promise, result, Resource, interceptorCalled = false;
187
+
188
+ $httpBackend.expectGET('/test/123').respond(200, {id: 123, abc_def: 'xyz'});
189
+
190
+ Resource = factory(config);
93
191
 
94
- promise.then(function (response) {
95
- result = response;
192
+ Resource.afterResponse(function (resource, constructor, context) {
193
+ expect(resource).toEqualData({id: 123, abcDef: 'xyz'});
194
+ expect(constructor).toEqual(Resource);
195
+ interceptorCalled = true;
196
+ });
197
+
198
+ expect(promise = Resource.get(123)).toBeDefined();
199
+
200
+ promise.then(function (response) {
201
+ result = response;
202
+ });
203
+
204
+ $httpBackend.flush();
205
+
206
+ expect(result).toBeInstanceOf(Resource);
207
+ expect(result).toEqualData({id: 123, abcDef: 'xyz'});
208
+ expect(interceptorCalled).toBeTruthy();
96
209
  });
97
210
 
98
- $httpBackend.flush();
211
+ it('should set context to resource instance', function () {
212
+ var instance, Resource, interceptorCalled = false;
213
+
214
+ $httpBackend.expectPOST('/test').respond(200, {id: 123, abc_def: 'xyz'});
215
+
216
+ Resource = factory(config);
99
217
 
100
- expect(result).toBeInstanceOf(Resource);
101
- expect(result).toEqualData({id: 123, abcDef: 'xyz'});
102
- expect(interceptorCalled).toBeTruthy();
218
+ instance = new Resource({abcDef: 'xyz'});
219
+
220
+ Resource.afterResponse(function (resource, constructor, context) {
221
+ expect(resource).toEqualData({id: 123, abcDef: 'xyz'});
222
+ expect(constructor).toEqual(Resource);
223
+ interceptorCalled = true;
224
+ });
225
+
226
+ instance.save();
227
+
228
+ $httpBackend.flush();
229
+
230
+ expect(interceptorCalled).toBeTruthy();
231
+ });
103
232
  });
104
233
 
234
+
105
235
  });
@@ -32,7 +32,8 @@
32
32
 
33
33
  function railsResourceFactory(config) {
34
34
  var transformers = config.requestTransformers,
35
- interceptors = config.responseInterceptors;
35
+ interceptors = config.responseInterceptors,
36
+ afterInterceptors = config.afterResponseInterceptors;
36
37
 
37
38
  function appendPath(url, path) {
38
39
  if (path) {
@@ -47,19 +48,21 @@
47
48
  }
48
49
 
49
50
  function RailsResource(value) {
51
+ var instance = this;
50
52
  if (value) {
51
53
  var immediatePromise = function(data) {
52
54
  return {
53
55
  resource: RailsResource,
56
+ context: instance,
54
57
  response: data,
55
58
  then: function(callback) {
56
- this.response = callback(this.response, this.resource);
59
+ this.response = callback(this.response, this.resource, this.context);
57
60
  return immediatePromise(this.response);
58
61
  }
59
62
  }
60
63
  };
61
64
 
62
- var data = RailsResource.callInterceptors(immediatePromise({data: value})).response.data;
65
+ var data = RailsResource.callInterceptors(immediatePromise({data: value}), this).response.data;
63
66
  angular.extend(this, data);
64
67
  }
65
68
  }
@@ -74,6 +77,7 @@
74
77
  RailsResource.httpConfig.headers = angular.extend({'Accept': 'application/json', 'Content-Type': 'application/json'}, RailsResource.httpConfig.headers || {});
75
78
  RailsResource.requestTransformers = [];
76
79
  RailsResource.responseInterceptors = [];
80
+ RailsResource.afterResponseInterceptors = [];
77
81
  RailsResource.defaultParams = config.defaultParams;
78
82
  RailsResource.serializer = RailsResourceInjector.createService(config.serializer || railsSerializer());
79
83
  RailsResource.rootName = RailsResource.serializer.underscore(config.name);
@@ -81,13 +85,29 @@
81
85
 
82
86
  /**
83
87
  * Add a callback to run on response and construction.
84
- * @param fn(resource, constructor) - resource is a resource instance, and constructor is the resource class calling the function
88
+ * @param fn(response data, constructor, context) - response data is either the resource instance returned or an array of resource instances,
89
+ * constructor is the resource class calling the function,
90
+ * context is the resource instance of the calling method (create, update, delete) or undefined if the method was a class method (get, query)
85
91
  */
86
92
  RailsResource.beforeResponse = function(fn) {
87
- var fn = RailsResourceInjector.getDependency(fn);
93
+ fn = RailsResourceInjector.getDependency(fn);
88
94
  RailsResource.responseInterceptors.push(function(promise) {
89
95
  return promise.then(function(response) {
90
- fn(response.data, promise.resource);
96
+ fn(response.data, promise.resource, promise.context);
97
+ return response;
98
+ });
99
+ });
100
+ };
101
+
102
+ /**
103
+ * Add a callback to run after response has been processed. These callbacks are not called on object construction.
104
+ * @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
105
+ */
106
+ RailsResource.afterResponse = function(fn) {
107
+ fn = RailsResourceInjector.getDependency(fn);
108
+ RailsResource.afterResponseInterceptors.push(function(promise) {
109
+ return promise.then(function(response) {
110
+ fn(response, promise.resource);
91
111
  return response;
92
112
  });
93
113
  });
@@ -98,7 +118,7 @@
98
118
  * @param fn (data, constructor) - data object is the serialized resource instance, and constructor the resource class calling the function
99
119
  */
100
120
  RailsResource.beforeRequest = function(fn) {
101
- var fn = RailsResourceInjector.getDependency(fn);
121
+ fn = RailsResourceInjector.getDependency(fn);
102
122
  RailsResource.requestTransformers.push(function(data, resource) {
103
123
  return fn(data, resource) || data;
104
124
  });
@@ -109,6 +129,10 @@
109
129
  RailsResource.responseInterceptors.push(RailsResourceInjector.getDependency(interceptor));
110
130
  });
111
131
 
132
+ angular.forEach(afterInterceptors, function (interceptor) {
133
+ RailsResource.afterResponseInterceptors.push(RailsResourceInjector.getDependency(interceptor));
134
+ });
135
+
112
136
  angular.forEach(transformers, function (transformer) {
113
137
  RailsResource.requestTransformers.push(RailsResourceInjector.getDependency(transformer));
114
138
  });
@@ -131,7 +155,7 @@
131
155
  };
132
156
 
133
157
  // transform data on response:
134
- RailsResource.callInterceptors = function (promise) {
158
+ RailsResource.callInterceptors = function (promise, context) {
135
159
  promise = promise.then(function (response) {
136
160
  // store off the data in case something (like our root unwrapping) assigns data as a new object
137
161
  response.originalData = response.data;
@@ -150,6 +174,18 @@
150
174
 
151
175
  // data is now deserialized. call response interceptors including beforeResponse
152
176
  angular.forEach(RailsResource.responseInterceptors, function (interceptor) {
177
+ promise.resource = RailsResource;
178
+ promise.context = context;
179
+ promise = interceptor(promise);
180
+ });
181
+
182
+ return promise;
183
+ };
184
+
185
+ // transform data after response has been converted to a resource instance:
186
+ RailsResource.callAfterInterceptors = function (promise) {
187
+ // data is now deserialized. call response interceptors including afterResponse
188
+ angular.forEach(RailsResource.afterResponseInterceptors, function (interceptor) {
153
189
  promise.resource = RailsResource;
154
190
  promise = interceptor(promise);
155
191
  });
@@ -158,9 +194,11 @@
158
194
  };
159
195
 
160
196
  RailsResource.processResponse = function (promise) {
161
- return RailsResource.callInterceptors(promise).then(function (response) {
197
+ promise = RailsResource.callInterceptors(promise).then(function (response) {
162
198
  return response.data;
163
199
  });
200
+
201
+ return RailsResource.callAfterInterceptors(promise);
164
202
  };
165
203
 
166
204
  RailsResource.getParameters = function (queryParams) {
@@ -233,9 +271,9 @@
233
271
  };
234
272
 
235
273
  RailsResource.prototype.processResponse = function (promise) {
236
- promise = RailsResource.callInterceptors(promise);
274
+ promise = RailsResource.callInterceptors(promise, this);
237
275
 
238
- return promise.then(angular.bind(this, function (response) {
276
+ promise = promise.then(angular.bind(this, function (response) {
239
277
  // we may not have response data
240
278
  if (response.hasOwnProperty('data') && angular.isObject(response.data)) {
241
279
  angular.extend(this, response.data);
@@ -243,6 +281,8 @@
243
281
 
244
282
  return this;
245
283
  }));
284
+
285
+ return RailsResource.callAfterInterceptors(promise);
246
286
  };
247
287
 
248
288
  angular.forEach(['post', 'put', 'patch'], function (method) {
@@ -4,7 +4,7 @@
4
4
  underscore: RailsInflector.underscore,
5
5
  camelize: RailsInflector.camelize,
6
6
  pluralize: RailsInflector.pluralize,
7
- exclusionMatchers: ['$'],
7
+ exclusionMatchers: [],
8
8
  excludeByDefault: false
9
9
  };
10
10
 
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: angularjs-rails-resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.2.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tommy Odom
@@ -10,7 +9,7 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2013-07-06 00:00:00.000000000 Z
12
+ date: 2013-08-08 00:00:00.000000000 Z
14
13
  dependencies: []
15
14
  description: A small AngularJS add-on for integrating with Rails via JSON more easily.
16
15
  email:
@@ -62,27 +61,26 @@ files:
62
61
  - vendor/assets/javascripts/angularjs/rails/resource/utils/url_builder.js
63
62
  homepage: https://github.com/finelineprototyping/angularjs-rails-resource
64
63
  licenses: []
64
+ metadata: {}
65
65
  post_install_message:
66
66
  rdoc_options: []
67
67
  require_paths:
68
68
  - lib
69
69
  required_ruby_version: !ruby/object:Gem::Requirement
70
- none: false
71
70
  requirements:
72
71
  - - ! '>='
73
72
  - !ruby/object:Gem::Version
74
73
  version: '0'
75
74
  required_rubygems_version: !ruby/object:Gem::Requirement
76
- none: false
77
75
  requirements:
78
76
  - - ! '>='
79
77
  - !ruby/object:Gem::Version
80
78
  version: '0'
81
79
  requirements: []
82
80
  rubyforge_project:
83
- rubygems_version: 1.8.25
81
+ rubygems_version: 2.0.6
84
82
  signing_key:
85
- specification_version: 3
83
+ specification_version: 4
86
84
  summary: ''
87
85
  test_files:
88
86
  - test/lib/angular/angular-bootstrap-prettify.js