angularjs-rails-resource 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ <a name="0.2.0"></a>
2
+ # 0.2.0
3
+ ## Breaking Changes
4
+ - Removed default transformers and interceptors
5
+ - railsFieldRenamingTransformer and railsFieldRenamingInterceptor have been removed completely and replaced by the serializers
6
+ - railsRootWrappingTransformer/Interceptor are no longer configured by the transformers/interceptors configuration option and is instead
7
+ configured by the <code>enableRootWrapping</code> option.
8
+ - Interceptors added using <code>beforeRequest</code> are run before any deserialization so the fields names have not been camelized.
9
+
10
+ ## Bug Fixes
11
+
12
+ ## Features
13
+ - Added serializers to replace old field renaming logic and to give users a lot more flexibility in customizing the (de)serialization process
14
+ - Added <code>enableRootWrapping</code> configuration option to be able to turn off root wrapping instead
15
+ - Added path option to <code>$url</code> methods to make it easier to construct a nested url.
16
+
17
+ <a name="0.1.7"></a>
18
+ # 0.1.7
19
+ ## Bug Fixes
20
+
21
+ ## Features
22
+ - New <code>save</code> instance method added to resources
23
+
24
+ <a name="0.1.6"></a>
25
+ # 0.1.6
26
+ ## Bug Fixes
27
+
28
+ ## Features
29
+ - Added beforeRequest and beforeResponse methods that wrap a given function as a transformer/interceptor to more easily add customizations
30
+
31
+ <a name="0.1.5"></a>
32
+ # 0.1.5
33
+
34
+ ## Bug Fixes
35
+
36
+ ## Features
37
+ - Added setUrl method to allow reconfiguring a resource's url after creation
38
+ - Added $url instance method to more easily reference the instance's URL
39
+ - Added instance and class methods for generic HTTP operations of $get, $post, $put, $delete
40
+
41
+
42
+
data/EXAMPLES.md ADDED
@@ -0,0 +1,127 @@
1
+ # Defining a nested resource
2
+ If a resource returns nested objects then those are normally treated as just basic JavaScript objects stored on the resource instance.
3
+ Using the serializers we can specify that a nested object is in fact a resouce and the object(s) will be constructed as a resource
4
+ instance during deserialization.
5
+
6
+ For example, suppose we have an Author that serializes all of the books that author has written. If we specify that the books
7
+ are a nested resource that allows us to more easily perform updates against those books without having to worry about creating
8
+ a resource instance for the book.
9
+
10
+ angular.module('book.services', ['rails']);
11
+
12
+ angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
13
+ return railsResourceFactory({url: '/books', name: 'book'});
14
+ }]);
15
+
16
+ angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
17
+ return railsResourceFactory({
18
+ url: '/authors',
19
+ name: 'author',
20
+ serializer: railsSerializer(function () {
21
+ this.nestedResource('books', 'Book');
22
+ });
23
+ });
24
+ }]);
25
+
26
+ angular.module('book.controllers').controller('AuthorCtrl', ['$scope', 'Author', function ($scope, Author) {
27
+ $scope.author = Author.get(123);
28
+
29
+ // allow the view to trigger an update to a book from $scope.author.books
30
+ $scope.updateBook = function (book) {
31
+ book.update();
32
+ }
33
+ }]);
34
+
35
+
36
+ # Adding custom methods to a resource
37
+ You can add additional "class" or "instance" methods by modifying the resource returned from the factory call.
38
+
39
+ ## Custom class-level find
40
+ For instance, if you wanted to add a method that would search for Books by the title without having to construct the query params
41
+ each time you could add a new <code>findByTitle</code> class function.
42
+
43
+ angular.module('book.services', ['rails']);
44
+ angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
45
+ var resource = railsResourceFactory({url: '/books', name: 'book'});
46
+ resource.findByTitle = function (title) {
47
+ return resource.query({title: title});
48
+ };
49
+ return resource;
50
+ }]);
51
+
52
+ angular.module('book.controllers').controller('BookShelfCtrl', ['$scope', 'Book', function ($scope, Book) {
53
+ $scope.searching = true;
54
+ // Find all books matching the title
55
+ $scope.books = Book.findByTitle({title: title});
56
+ $scope.books.then(function(results) {
57
+ $scope.searching = false;
58
+ }, function (error) {
59
+ $scope.searching = false;
60
+ });
61
+ }]);
62
+
63
+ ## Get related object
64
+ You can also add additional methods on the object prototype chain so all instances of the resource have that function available.
65
+ The following example exposes a <code>getAuthor</code> instance method that would be accessible on all Book instances.
66
+
67
+ angular.module('book.services', ['rails']);
68
+ angular.module('book.services').factory('Author', ['railsResourceFactory', function (railsResourceFactory) {
69
+ return railsResourceFactory({url: '/authors', name: 'author'});
70
+ }]);
71
+ angular.module('book.services').factory('Book', ['railsResourceFactory', 'Author', function (railsResourceFactory, Author) {
72
+ var resource = railsResourceFactory({url: '/books', name: 'book'});
73
+ resource.prototype.getAuthor = function () {
74
+ return Author.get(this.authorId);
75
+ };
76
+ }]);
77
+ angular.module('book.controllers').controller('BookShelfCtrl', ['$scope', 'Book', function ($scope, Book) {
78
+ $scope.getAuthorDetails = function (book) {
79
+ $scope.author = book.getAuthor();
80
+ };
81
+ }]);
82
+
83
+ ## Nested URL
84
+ Or say you instead had a nested "references" service call that returned a list of referenced books for a given book instance. In that case you can add your own addition method that calls $http.get and then
85
+ passes the resulting promise to the processResponse method which will perform the same transformations and handling that the get or query would use.
86
+
87
+ angular.module('book.services', ['rails']);
88
+ angular.module('book.services').factory('Book', ['railsResourceFactory', '$http', function (railsResourceFactory, $http) {
89
+ var resource = railsResourceFactory({url: '/books', name: 'book'});
90
+ resource.prototype.getReferences = function () {
91
+ var self = this;
92
+ return resource.$get(self.$url('references'))).then(function (references) {
93
+ self.references = references;
94
+ return self.references;
95
+ });
96
+ };
97
+ }]);
98
+
99
+ # Specifying Transformer
100
+ Transformers can be by specifying an array of transformers in the configuration options passed to railsResourceFactory.
101
+ However, a cleaner eway to write it is to use the <code>beforeRequest</code> which can take a new anonymous function or
102
+ a function returned by a factory if you want to share a transformer across multiple resources.
103
+
104
+ Both of these examples can be accomplished using the serializers now.
105
+
106
+ angular.module('test').factory('excludePrivateKeysTransformer', function () {
107
+ return function (data) {
108
+ angular.forEach(data, function (value, key) {
109
+ if (key[0] === '_') {
110
+ delete data[key];
111
+ }
112
+ });
113
+ });
114
+ });
115
+
116
+ angular.module('test').factory('Book', function (railsResourceFactory, excludePrivateKeysTransformer) {
117
+ var Book = railsResourceFactory({url: '/books', name: 'book'});
118
+ Book.beforeRequest(excludePrivateKeysTransformer);
119
+ Book.beforeRequest(function (data) {
120
+ data['release_date'] = data['publicationDate'];
121
+ delete data['publicationDate'];
122
+ });
123
+
124
+ return Book;
125
+ });
126
+
127
+
data/README.md CHANGED
@@ -19,11 +19,14 @@ The resource object created by this factory simplifies access to those models by
19
19
 
20
20
  This module is being used for applications we are writing and we expect that over time that we will be adding additional functionality but we welcome contributions and suggestions.
21
21
 
22
+ ## Changes
23
+ Make sure to check the [CHANGELOG](CHANGELOG.md) for any breaking changes between releases.
24
+
22
25
  ## Installation
23
26
 
24
27
  Add this line to your application's Gemfile:
25
28
 
26
- gem 'angularjs-rails-resource'
29
+ gem 'angularjs-rails-resource', '~> 0.2.0'
27
30
 
28
31
  Include the javascript somewhere in your asset pipeline:
29
32
 
@@ -38,23 +41,92 @@ Since this is an [AngularJS](http://angularjs.org) module it of course depends o
38
41
  * [$injector](http://docs.angularjs.org/api/AUTO.$injector)
39
42
  * [$interpolate](http://docs.angularjs.org/api/ng.$interpolate)
40
43
 
44
+ ## Usage
45
+ There are a lot of different ways that you can use the resources and we try not to force you into any specific pattern
46
+
47
+ There are more examples available in [EXAMPLES.md](EXAMPLES.md). We also published a complete working example (including the rails side)
48
+ at [Employee Training Tracker](https://github.com/FineLinePrototyping/employee-training-tracker) application.
49
+
50
+
51
+ ### Basic Example
52
+ In order to create a Book resource, we would first define the factory within a module.
53
+
54
+ angular.module('book.services', ['rails']);
55
+ angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
56
+ return railsResourceFactory({url: '/books', name: 'book'});
57
+ }]);
58
+
59
+ We would then inject that service into a controller:
60
+
61
+ angular.module('book.controllers').controller('BookShelfCtrl', ['$scope', 'Book', function ($scope, Book) {
62
+ $scope.searching = true;
63
+ // Find all books matching the title
64
+ $scope.books = Book.query({title: title});
65
+ $scope.books.then(function(results) {
66
+ $scope.searching = false;
67
+ }, function (error) {
68
+ $scope.searching = false;
69
+ });
70
+
71
+ // Find a single book and update it
72
+ Book.get(1234).then(function (book) {
73
+ book.lastViewed = new Date();
74
+ book.update();
75
+ });
76
+
77
+ // Create a book and save it
78
+ new Book({title: 'Gardens of the Moon', author: 'Steven Erikson', isbn: '0-553-81957-7'}).create();
79
+ }]);
80
+
81
+ ### Serializer
82
+ When defining a resource, you can pass a custom [serializer](#serializers) using the <code>serializer</code> configuration option.
83
+
84
+ Author = railsResourceFactory({
85
+ url: '/authors',
86
+ name: 'author',
87
+ serializer: railsSerializer(function () {
88
+ this.exclude('birthDate', 'books');
89
+ this.nestedAttribute('books');
90
+ this.nestedResource('books', 'Book');
91
+ })
92
+ });
93
+
94
+ You can also specify a serializer as a factory and inject it as a dependency.
95
+
96
+ angular.module('rails').factory('BookSerializer', function(railsSerializer) {
97
+ return railsSerializer(function () {
98
+ this.exclude('publicationDate', 'relatedBooks');
99
+ this.rename('ISBN', 'isbn');
100
+ this.nestedAttribute('chapters', 'notes');
101
+ this.serializeWith('chapters', 'ChapterSerializer');
102
+ this.add('numChapters', function (book) {
103
+ return book.chapters.length;
104
+ });
105
+ });
106
+ });
107
+
108
+ Book = railsResourceFactory({
109
+ url: '/books',
110
+ name: 'book',
111
+ serializer: 'BookSerializer'
112
+ });
113
+
114
+
41
115
  ## Resource Creation
42
- Creating a resource using this factory is similar to using $resource, you just call the factory with the config options and it returns a new resource function.
116
+ Creating a resource using this factory is similar to using $resource, you just call <code>railsResourceFactory</code> with the config options and it returns a new resource function.
43
117
  The resource function serves two purposes. First is that you can use (or define new) "class" methods directly accessible such as query and get to retrieve
44
118
  instances from the backend rails service. The second is that it allows you to use it as a constructor to create new instances of that resource giving you access
45
119
  to create, update, and delete instance methods (or any others you add).
46
120
 
47
- The typical use case is to define the resource as an AngularJS factory within a module and then inject that into a controller or directive.
48
- See [Examples](#examples) below for more information on creating and injecting the resource.
49
-
50
121
 
51
122
  ### Config Options
52
123
  The following options are available for the config object passed to the factory function.
53
124
 
54
125
  * **url** - This is the url of the service. See [Resource URLs](#resource-urls) below for more information.
126
+ * **enableRootWrapping** - (Default: true) Turns on/off root wrapping on JSON (de)serialization.
55
127
  * **name** - This is the name used for root wrapping when dealing with singular instances.
56
- * **pluralName** *(optional)* - If specified this name will be used for unwrapping query results,
57
- if not specified the singular name with an appended 's' will be used.
128
+ * **pluralName** *(optional)* - If specified this name will be used for unwrapping array results. If not specified then the serializer's [pluralize](#serializers) method is used to calculate
129
+ the plural name from the singular name.
58
130
  * **httpConfig** *(optional)* - By default we will add the following headers to ensure that the request is processed as JSON by Rails. You can specify additional http config options or override any of the defaults by setting this property. See the [AngularJS $http API](http://docs.angularjs.org/api/ng.$http) for more information.
59
131
  * **headers**
60
132
  * **Accept** - application/json
@@ -81,35 +153,6 @@ The URL can be specified as one of three ways:
81
153
  new Item({store: 123}).create() would generate a POST to /stores/123/items
82
154
  new Item({id: 1, storeId: 123}).update() would generate a PUT to /stores/123/items/1
83
155
 
84
-
85
- ## Transformers / Interceptors
86
- The transformers and interceptors can be specified using an array containing transformer/interceptor functions or strings
87
- that can be resolved using Angular's DI.
88
-
89
- The root wrapping and snake case to camel case conversions are implemented as transformers and interceptors. So if you override
90
- the default transformers and interceptors you will have to include those in the array as well (assuming you want that functionality).
91
-
92
- That also means that if you don't want root wrapping and key conversions then you can just pass an emptry array for each
93
- and no processing will be done on the data.
94
-
95
- ### Transformers
96
- Transformer functions are called to transform the data before we send it to $http for POST/PUT.
97
-
98
- The transformer functions will be called with the following signature
99
-
100
- function (data, resource)
101
-
102
- The return value of the function must be the transformed data.
103
-
104
- ### Interceptors
105
- Interceptor functions utilize [$q promises](http://docs.angularjs.org/api/ng.$q) to process the data returned from the server.
106
-
107
- The interceptor is called with the promise returned from $http and is expected to return a promise for chaining.
108
-
109
- The promise passed to each interceptor contains a reference to the resource to expose the configured options of the resource.
110
-
111
- Each interceptor promise is expected to return the response or a $q.reject. See [Promises](#promises) below for more information about the promise data.
112
-
113
156
  ## Promises
114
157
  [$http documentation](http://docs.angularjs.org/api/ng.$http) describes the promise data very well so I highly recommend reading that.
115
158
 
@@ -119,225 +162,174 @@ that if response.data is reassigned that there's still a pointer to the original
119
162
 
120
163
 
121
164
  ## Resource Methods
122
- Resources created using this factory have the following methods available and each one (except the constructor) returns a [Promise](#promises).
123
-
124
- ### $url
125
- ***
126
-
127
- Returns the resource URL using the given context.
128
-
129
- ####Parameters
130
-
131
- * **context** - The context to use when building the url. See [Resource URLs](#resource-urls) above for more information.
132
-
133
- ### constructor
134
- ***
135
-
136
- The constructor is the function returned by the railsResourceFactory and can be used with the "new" keyword.
137
-
138
- ####Parameters
139
- * **data** *(optional)* - An object containing the data to be stored in the instance.
140
-
141
- ### $get
142
- ***
143
-
144
- Executes a GET request against the given URL and returns a promise that will be resolved with a new Resource instance (or instances in the case of an array response).
145
-
146
- ####Parameters
147
- * **url** - The url to GET
148
- * **queryParams** - The set of query parameters to include in the GET request
149
-
150
- ### query
151
- ***
152
-
153
- Executes a GET request against the resource's base url and returns a promise that will be resolved with an array of new Resource instances.
154
-
155
- ####Parameters
156
- * **query params** - An map of strings or objects that are passed to $http to be turned into query parameters
157
- * **context** - A context object that is used during url evaluation to resolve expression variables
158
-
159
-
160
- ### get
161
- ***
162
-
163
- Executs a GET request against the resource's url and returns a promise that will be resolved with a new instance of the Resource.
164
-
165
- ####Parameters
166
- * **context** - A context object that is used during url evaluation to resolve expression variables. If you are using a basic url this can be an id number to append to the url.
167
-
168
-
169
- ### $post, $put, $patch
170
- ***
171
-
172
- Transforms the given data and submits it using a POST/PUT/PATCH to the given URL and returns a promise that will be resolved with a new Resource instance (or instances in the case of an array response).
173
-
174
- ####Parameters
175
- * **url** - The url to POST/PUT/PATCH
176
- * **data** - The data to transform and submit to the server
177
-
178
- ### $delete
179
- ***
180
-
181
- Executes a DELETE against the given URL and returns a promise that will be resolved with a new Resource instance (if the server returns a body).
182
-
183
- ####Parameters
184
- * **url** - The url to POST/PUT/PATCH
185
-
186
- ## Resource Instance Methods
165
+ Resources created using <code>railsResourceFactory</code> have the following class and instance methods available.
166
+
167
+ ### Class Methods
168
+ * Constructor(data) - The Resource object can act as a constructor function for use with the JavaScript <code>new</code> keyword.
169
+ * **data** {object} (optional) - Optional data to set on the new instance
170
+
171
+ * $url(context, path) - Returns the resource URL using the given context with the optional path appended if provided.
172
+ * **context** {*} - The context to use when building the url. See [Resource URLs](#resource-urls) above for more information.
173
+ * **path** {string} (optional) - A path to append to the resource's URL.
174
+ * **returns** {string} - The resource URL
175
+
176
+ * query(queryParams, context) - Executes a GET request against the resource's base url (e.g. /books).
177
+ * **query params** {object} (optional) - An map of strings or objects that are passed to $http to be turned into query parameters
178
+ * **context** {*} (optional) - A context object that is used during url evaluation to resolve expression variables
179
+ * **returns** {promise} - A promise that will be resolved with an array of new Resource instances
180
+
181
+ * get(context) - Executs a GET request against the resource's url (e.g. /books/1234).
182
+ * **context** {*} - A context object that is used during url evaluation to resolve expression variables. If you are using a basic url this can be an id number to append to the url.
183
+ * **returns** {promise} A promise that will be resolved with a new instance of the Resource
184
+
185
+ * $get(customUrl, queryParams) - Executes a GET request against the given URL.
186
+ * **customUrl** {string} - The url to GET
187
+ * **queryParams** {object} (optional) - The set of query parameters to include in the GET request
188
+ * **returns** {promise} A promise that will be resolved with a new Resource instance (or instances in the case of an array response).
189
+
190
+ * $post(customUrl, data), $put(customUrl, data), $patch(customUrl, data) - Serializes the data parameter using the Resource's normal serialization process and submits the result as a POST / PUT / PATCH to the given URL.
191
+ * **customUrl** {string} - The url to POST / PUT / PATCH to
192
+ * **data** {object} - The data to serialize and POST / PUT / PATCH
193
+ * **returns** {promise} A promise that will be resolved with a new Resource instance (or instances in the case of an array response).
194
+
195
+ * $delete(customUrl) - Executes a DELETE to a custom URL. The main difference between this and $http.delete is that a server response that contains a body will be deserialized using the normal Resource deserialization process.
196
+ * **customUrl** {string} - The url to DELETE to
197
+ * **returns** {promise} A promise that will be resolved with a new Resource instance (or instances in the case of an array response) if the server includes a response body.
198
+
199
+ * beforeRequest(fn(data, resource)) - See [Transformers](#transformers) for more information. The function is called prior to the serialization process so the data
200
+ passed to the function is still a Resource instance as long as another transformation function has not returned a new object to serialize.
201
+ * fn(data, resource) {function} - The function to add as a transformer.
202
+ * **data** {object} - The data being serialized
203
+ * **resource** {Resource class} - The Resource class that is calling the function
204
+ * **returns** {object | undefined} - If the function returns a new object that object will instead be used for serialization.
205
+
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
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.
212
+
213
+ ### Instance Methods
187
214
  The instance methods can be used on any instance (created manually or returned in a promise response) of a resource.
188
215
  All of the instance methods will update the instance in-place on response and will resolve the promise with the current instance.
189
216
 
190
- ### $url
191
- ***
192
-
193
- Returns the url for the instance.
217
+ * $url(path) - Returns this Resource instance's URL with the optional path appended if provided.
218
+ * **path** {string} (optional) - A path to append to the resource's URL.
194
219
 
195
- ####Parameters
220
+ * create() - Submits the resource instance to the resource's base URL (e.g. /books) using a POST
221
+ * **returns** {promise} - A promise that will be resolved with the instance itself
196
222
 
197
- None
223
+ * update() - Submits the resource instance to the resource's URL (e.g. /books/1234) using a PUT
224
+ * **returns** {promise} - A promise that will be resolved with the instance itself
198
225
 
199
- ### $post, $put, $patch
200
- ***
226
+ * save() - Calls <code>create</code> if <code>isNew</code> returns true, otherwise it calls <code>update</code>.
201
227
 
202
- Transforms the instance and submits it using POST/PUT/PATCH to the given URL and returns a promise that will be resolved with a new Resource instance (or instances in the case of an array response).
228
+ * remove(), delete() - Executes an HTTP DELETE against the resource's URL (e.g. /books/1234)
229
+ * **returns** {promise} - A promise that will be resolved with the instance itself
203
230
 
204
- ####Parameters
205
- * **url** - The url to POST/PUT/PATCH/DELETE
231
+ * $post(customUrl), $put(customUrl), $patch(customUrl) - Serializes and submits the instance using an HTTP POST/PUT/PATCH to the given URL.
232
+ * **customUrl** {string} - The url to POST / PUT / PATCH to
233
+ * **returns** {promise} - A promise that will be resolved with the instance itself
206
234
 
207
- ### create
208
- ***
235
+ * $delete(customUrl) - Executes a DELETE to a custom URL. The main difference between this and $http.delete is that a server response that contains a body will be deserialized using the normal Resource deserialization process.
236
+ * **customUrl** {string} - The url to DELETE to
237
+ * **returns** {promise} - A promise that will be resolved with the instance itself
209
238
 
210
- Transforms and submits the instance using a POST to the resource base URL.
211
239
 
212
- ####Parameters
240
+ ## Serializers
241
+ Out of the box, resources serialize all available keys and transform key names between camel case and underscores to match Ruby conventions.
242
+ However, that basic serialization often isn't ideal in every situation. With the serializers users can define customizations
243
+ that dictate how serialization and deserialization is performed. Users can: rename attributes, specify extra attributes, exclude attributes
244
+ with the ability to exclude all attributes by default and only serialize ones explicitly allowed, specify other serializers to use
245
+ for an attribute and even specify that an attribute is a nested resource.
213
246
 
214
- None
247
+ ### railsSerializer
248
+ * railsSerializer(options, customizer) - Builds a Serializer constructor function using the configuration options specified.
249
+ * **options** {object} (optional) - Configuration options to alter the default operation of the serializers. This parameter can be excluded and the
250
+ customizer function specified as the first argument instead.
251
+ * **customizer** {function} (optional) - A function that will be called to customize the serialization logic.
252
+ * **returns** {Serializer} - A Serializer constructor function
215
253
 
254
+ ### Configuration
255
+ The <code>railsSerializer</code> function takes a customizer function that is called on create within the context of the constructed Serializer. From within the customizer function you can call customization functions that affect what gets serialized and how or override the default options.
256
+ In addition, <code>railsSerializer</code> exposes a field <code>defaultOptions</code> that allows you to globally override the defaults for the configuration options.
216
257
 
217
- ### update
218
- ***
258
+ #### Configuration Options
259
+ Serializers have the following available configuration options:
260
+ * underscore - (function) Allows users to supply their own custom underscore conversion logic.
261
+ * **default**: RailsInflector.underscore
262
+ * parameters
263
+ * **attribute** {string} - The current name of the attribute
264
+ * **returns** {string} - The name as it should appear in the JSON
265
+ * camelize - (function) Allows users to supply their own custom camelization logic.
266
+ * **default**: RailsInflector.camelize
267
+ * parameters
268
+ * **attribute** {string} - The name as it appeared in the JSON
269
+ * **returns** {string} - The name as it should appear in the resource
270
+ * pluralize - (function) Allows users to supply their own custom pluralization logic.
271
+ * default: RailsInflector.pluralize
272
+ * parameters
273
+ * **attribute** {string} - The name as it appeared in the JSON
274
+ * **returns** {string} - The name as it should appear in the resource
275
+ * excludeByDefault {boolean} - Specifies whether or not JSON serialization should exclude all attributes from serialization by default.
276
+ * 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:
278
+ * string - Defines a prefix that is used to test for exclusion
279
+ * RegExp - A custom regular expression that is tested against the attribute name
280
+ * function - A custom function that accepts a string argument and returns a boolean with true indicating exclusion.
219
281
 
220
- Transforms and submits the instance using a PUT to the resource's URL.
282
+ #### Customization API
283
+ The customizer function passed to the railsSerializer has available to it the following methods for altering the serialization of an object. None of these methods support nested attribute names (e.g. <code>'books.publicationDate'</code>), in order to customize the serialization of the <code>books</code> objects you would need to specify a custom serializer for the <code>books</code> attribute.
221
284
 
222
- ####Parameters
285
+ * exclude (attributeName...) - Accepts a variable list of attribute names to exclude from JSON serialization. This has no impact on what is deserialized from the server.
223
286
 
224
- None
287
+ * only (attributeName...) - Accepts a variable list of attribute names that should be included in JSON serialization. This has no impact on what is deserialized from the server. Using this method will by default exclude all other attributes and only the ones explicitly included using <code>only</code> will be serialized.
225
288
 
289
+ * rename (javascriptName, jsonName) - Specifies a custom name mapping for an attribute. On serializing to JSON the <code>jsonName</code> will be used. On deserialization, if <code>jsonName</code> is seen then it will be renamed as javascriptName in the resulting resource. Right now it is still passed to underscore so you could do 'publicationDate' -> 'releaseDate' and it will still underscore as release_date. However, that may be changed to prevent underscore from breaking some custom name that it doesn't handle properly.
226
290
 
227
- ### remove / delete
228
- ***
291
+ * nestedAttribute (attributeName...) - This is a shortcut for rename that allows you to specify a variable number of attributes that should all be renamed to <code><name>_attributes</code> to work with the Rails nested_attributes feature. This does not perform any additional logic to accomodate specifying the <code>_destroy</code> property.
229
292
 
230
- Execute a DELETE to the resource's url.
293
+ * 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.
231
294
 
232
- ####Parameters
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.
233
296
 
234
- None
235
-
236
-
237
- ## Example
238
- For a complete working example (including the rails side), check out the [Employee Training Tracker](https://github.com/FineLinePrototyping/employee-training-tracker) application
239
- we open sourced based on an interface we created for use internally that uses this module as well as many others.
240
-
241
- ### Define Resource
242
- In order to create a Book resource, we would first define the factory within a module.
243
-
244
- angular.module('book.services', ['rails']);
245
- angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
246
- return railsResourceFactory({url: '/books', name: 'book'});
247
- }]);
248
-
249
- We would then inject that service into a controller:
250
-
251
- angular.module('book.controllers').controller('BookShelfCtrl', ['$scope', 'Book', function ($scope, Book) {
252
- // See following examples for using Book within your controller
253
- }]);
297
+ * 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>
254
298
 
255
- The examples below illustrate how you would then use the Book service to get, create, update, and delete data.
299
+ ### Serializer Methods
300
+ The serializers are defined using mostly instance prototype methods. For information on those methods please see the inline documentation. There are however a couple of class methods that
301
+ are also defined to expose underscore, camelize, and pluralize. Those functions are set to the value specified by the configuration options sent to the serializer.
256
302
 
257
- #### Extending
258
- You can add additional "class" or "instance" methods by modifying the resource returned from the factory call. For instance,
259
- if you wanted to add a "class" method named "findByTitle" to the Book resource you would modify the service setup as follows:
260
303
 
261
- angular.module('book.services', ['rails']);
262
- angular.module('book.services').factory('Book', ['railsResourceFactory', function (railsResourceFactory) {
263
- var resource = railsResourceFactory({url: '/books', name: 'book'});
264
- resource.findByTitle = function (title) {
265
- return resource.query({title: title});
266
- };
267
- return resource;
268
- }]);
269
-
270
- If you wanted to add an "instance" method to retrieve a related object:
271
-
272
- angular.module('book.services', ['rails']);
273
- angular.module('book.services').factory('Author', ['railsResourceFactory', function (railsResourceFactory) {
274
- return railsResourceFactory({url: '/authors', name: 'author'});
275
- }]);
276
- angular.module('book.services').factory('Book', ['railsResourceFactory', 'Author', function (railsResourceFactory, Author) {
277
- var resource = railsResourceFactory({url: '/books', name: 'book'});
278
- resource.prototype.getAuthor = function () {
279
- return Author.get(this.authorId);
280
- };
281
- }]);
282
-
283
- Or say you instead had a nested "references" service call that returned a list of referenced books for a given book instance. In that case you can add your own addition method that calls $http.get and then
284
- passes the resulting promise to the processResponse method which will perform the same transformations and handling that the get or query would use.
285
-
286
- angular.module('book.services', ['rails']);
287
- angular.module('book.services').factory('Book', ['railsResourceFactory', '$http', function (railsResourceFactory, $http) {
288
- var resource = railsResourceFactory({url: '/books', name: 'book'});
289
- resource.prototype.getReferences = function () {
290
- var self = this;
291
- return resource.processResponse($http.get(resource.resourceUrl(this.id) + '/references')).then(function (references) {
292
- self.references = references;
293
- return self.references;
294
- });
295
- };
296
- }]);
297
-
298
- ### Query Books
299
- To query for a list of books with the title "The Hobbit" you would use the query method:
300
-
301
- var books = Book.query({title: 'The Hobbit'});
302
-
303
- We now have a promise in the books variable which we could then use within a template if we just wanted to do an ng-repeat over the books. A lot of times though you'll probably want to show some indicator
304
- to your user that the search is executing so you'd want to use a then handler:
305
-
306
- $scope.searching = true;
307
- var books = Book.query({title: 'The Hobbit'});
308
- books.then(function(results) {
309
- $scope.searching = false;
310
- }, function (error) {
311
- $scope.searching = false;
312
- // display error
313
- });
304
+ ## Transformers / Interceptors
305
+ The transformers and interceptors can be specified using an array containing transformer/interceptor functions or strings
306
+ that can be resolved using Angular's DI. The transformers / interceptors concept was prior to the [serializers](#serializers) but
307
+ we kept the API available because there may be use cases that can be accomplished with these but not the serializers.
314
308
 
309
+ ### Transformers
310
+ Transformer functions are called to transform the data before we send it to $http for POST/PUT.
315
311
 
316
- ### Get Book
317
- var book = Book.get(1234);
312
+ A transformer function is called with two parameters:
313
+ * data - The data that is being sent to the server
314
+ * resource - The resource class that is calling the transformer
318
315
 
319
- Again, it's important to remember that book is a promise, if instead you wanted the book data you would use the "then" function:
316
+ A transformer function must return the data. This is to allow transformers to return entirely new objects in place of the current data (such as root wrapping).
320
317
 
321
- Book.get(1234).then(function (book) {
322
- // book contains the data returned from the service
323
- });
318
+ The resource also exposes a class method <code>beforeRequest(fn)</code> that accepts a function to execute and automatically wraps it as a transformer and appends it
319
+ 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
+ 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).
324
321
 
325
- ### Create Book
326
- var book = new Book({author: 'J. R. R. Tolkein', title: 'The Hobbit'});
327
- book.create().then(function (result) {
328
- // creation was successful
329
- });
322
+ ### Interceptors
323
+ Interceptor functions utilize [$q promises](http://docs.angularjs.org/api/ng.$q) to process the data returned from the server.
330
324
 
331
- ### Update Book
332
- Book.get(1234).then(function (book) {
333
- book.author = 'J. R. R. Tolkein';
334
- book.update();
335
- });
325
+ 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.
336
328
 
337
- Or, if you say the user typed in the book id into a scope variable and you wanted to update the book without having to first retrieve it:
329
+ 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
331
+ as an interceptor.
338
332
 
339
- var book = new Book({id: $scope.bookId, author: $scope.authorName, title: $scope.bookTitle});
340
- book.update();
341
333
 
342
334
  ## Tests
343
335
  The tests are written using [Jasmine](http://pivotal.github.com/jasmine/) and are run using [Karma](https://github.com/karma-runner/karma).