AngularJS Rails Resource
A resource factory inspired by $resource from AngularJS and Misko's recommendation.
When starting out with AngularJS and Rails we initially were using $resource but there were three things we didn't like that this gem sets out to provide:
- $resource didn't return promises
- Rails prefers JSON be root wrapped
- Our JSON contained snake case keys coming from our database but we didn't want to mix snake case and camel case in our UI
Add this line to your application's Gemfile:
Since this is an AngularJS module it of course depends on that but more specifically the it depends on the following AngularJS services:
Creating a resource using this factory is similar to using $resource, you just call the factory with the config options and get back your resource.
The following options are available for the config object passed to the factory function.
- url - This is the base url of the service. See Resource URLs below for more information.
- name - This is the name used for root wrapping when dealing with singular instances.
- pluralName (optional) - If specified this name will be used for unwrapping query results, if not specified the singular name with an appended 's' will be used.
- httpConfig (optional) - Passed directly to $http.
- defaultParams (optional) - If the resource expects a default set of query params on every call you can specify them here.
- requestTransformers *(optional) - See Transformers / Interceptors
- responseInterceptors (optional) - See Transformers / Interceptors
NOTE: The names should be specified using camel case when using the key transformations because that happens before the root wrapping by default. For example, you should specify "publishingCompany" and "publishingCompanies" instead of "publishing_company" and "publishing_companies".
The resource url functionality is pretty basic right now. The base url is used for query and create and the resource url is assumed to be the base url followed by the id. Therefore, a base url of '/books' will result in a resource url of '/books/123'.
Transformers / Interceptors
The transformers and interceptors can be specified using an array containing transformer/interceptor functions or strings that can be resolved using Angular's DI.
The root wrapping and snake case to camel case conversions are implemented as transformers and interceptors. So if you override the default transformers and interceptors you will have to include those in the array as well (assuming you want that functionality).
That also means that if you don't want root wrapping and key conversions then you can just pass an emptry array for each and no processing will be done on the data.
Transformer functions are called to transform the data before we send it to $http for POST/PUT.
The transformer functions will be called with the following signature
The return value of the function must be the transformed data.
Interceptor functions utilize $q promises to process the data returned from the server.
The interceptor is called with the promise returned from $http and is expected to return a promise for chaining.
The promise passed to each interceptor contains a reference to the resource to expose the configured options of the resource.
Each interceptor promise is expected to return the response or a $q.reject. See Promises below for more information about the promise data.
$http documentation describes the promise data very well so I highly recommend reading that.
In addition to the fields listed in the $http documentation an additional field named originalData is added to the response object to keep track of what the field was originally pointing to. The originalData is not a deep copy, it just ensures that if response.data is reassigned that there's still a pointer to the original response.data object.
Resources created using this factory have the following methods available and each one (except the constructor) returns a Promise.
The constructor function is to create new instances of the resource.
- data (optional) - An object containing the data to be stored in the instance.
A "class" method that executes a GET request against the base url with query parameters set via the params option.
- params - An map of strings or objects that are passed to $http to be turned into query parameters
A "class" method that executes a GET request against the resource url.
- id - The id of the resource to retrieve
An "instance" method that executes a POST to the base url with the data defined in the instance.
An "instance" method that executes a PUT to the resource url with the data defined in the instance.
remove / delete
Both of these are "instance" methods that execute a DELETE to the resource url.
Creating a Book resource would look something like:
You can add additional "class" or "instance" methods by modifying the resource returned from the factory call. For instance, if you wanted to add a "class" method named "findByTitle" to the Book resource you would modify the service setup as follows:
If you wanted to add an "instance" method (I couldn't come up with an example for this) you would add a new function to the resource prototype object:
Running the tests should be as simple as following the instructions
- Fork it
- Create your feature branch (
git checkout -b my-new-feature)
- Commit your changes (
git commit -am 'Added some feature')
- Push to the branch (
git push origin my-new-feature)
- Create new Pull Request