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 +15 -0
- data/CHANGELOG.md +8 -0
- data/EXAMPLES.md +47 -3
- data/README.md +35 -13
- data/karma.conf.js +21 -21
- data/lib/angularjs-rails-resource/version.rb +1 -1
- data/package.json +8 -3
- data/test/unit/angularjs/rails/interceptorsSpec.js +175 -45
- data/vendor/assets/javascripts/angularjs/rails/resource/resource.js +51 -11
- data/vendor/assets/javascripts/angularjs/rails/resource/serialization.js +1 -1
- metadata +5 -7
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('
|
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.
|
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
|
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.
|
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
|
207
|
-
|
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
|
211
|
-
* **
|
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.
|
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.
|
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
|
327
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
};
|
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
|
-
|
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
|
-
|
37
|
-
|
51
|
+
angular.copy(config, testConfig);
|
52
|
+
testConfig.responseInterceptors = ['railsTestInterceptor'];
|
53
|
+
Resource = factory(testConfig);
|
38
54
|
|
39
|
-
|
55
|
+
expect(promise = Resource.get(123)).toBeDefined();
|
40
56
|
|
41
|
-
|
42
|
-
|
43
|
-
|
57
|
+
promise.then(function (response) {
|
58
|
+
result = response;
|
59
|
+
});
|
44
60
|
|
45
|
-
|
61
|
+
$httpBackend.flush();
|
46
62
|
|
47
|
-
|
48
|
-
result
|
63
|
+
expect(result).toBeInstanceOf(Resource);
|
64
|
+
expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
|
49
65
|
});
|
50
66
|
|
51
|
-
|
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
|
-
|
54
|
-
|
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
|
-
|
58
|
-
|
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
|
-
|
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
|
-
|
64
|
-
testConfig.responseInterceptors = [testInterceptor];
|
65
|
-
Resource = factory(testConfig);
|
151
|
+
expect(promise = Resource.get(123)).toBeDefined();
|
66
152
|
|
67
|
-
|
153
|
+
promise.then(function (response) {
|
154
|
+
result = response;
|
155
|
+
});
|
68
156
|
|
69
|
-
|
70
|
-
|
157
|
+
$httpBackend.flush();
|
158
|
+
|
159
|
+
expect(result).toBeInstanceOf(Resource);
|
160
|
+
expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
|
71
161
|
});
|
72
162
|
|
73
|
-
|
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
|
-
|
76
|
-
|
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
|
-
|
173
|
+
expect(promise = Resource.get(123)).toBeDefined();
|
83
174
|
|
84
|
-
|
175
|
+
promise.then(function (response) {
|
176
|
+
result = response;
|
177
|
+
});
|
85
178
|
|
86
|
-
|
87
|
-
|
88
|
-
expect(
|
89
|
-
|
179
|
+
$httpBackend.flush();
|
180
|
+
|
181
|
+
expect(result).toBeInstanceOf(Resource);
|
182
|
+
expect(result).toEqualData({interceptorAdded: 'x', id: 123, abcDef: 'xyz'});
|
90
183
|
});
|
91
184
|
|
92
|
-
|
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
|
-
|
95
|
-
|
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
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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) {
|
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.
|
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-
|
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:
|
81
|
+
rubygems_version: 2.0.6
|
84
82
|
signing_key:
|
85
|
-
specification_version:
|
83
|
+
specification_version: 4
|
86
84
|
summary: ''
|
87
85
|
test_files:
|
88
86
|
- test/lib/angular/angular-bootstrap-prettify.js
|