README.md
# restpack_serializer
[![Build Status](https://travis-ci.org/RestPack/restpack_serializer.png?branch=master)](https://travis-ci.org/RestPack/restpack_serializer) [![Code Climate](https://codeclimate.com/github/RestPack/restpack_serializer.png)](https://codeclimate.com/github/RestPack/restpack_serializer) [![Dependency Status](https://gemnasium.com/RestPack/restpack_serializer.png)](https://gemnasium.com/RestPack/restpack_serializer) [![Gem Version](https://badge.fury.io/rb/restpack_serializer.png)](http://badge.fury.io/rb/restpack_serializer) [![Coverage Status](https://coveralls.io/repos/RestPack/restpack_serializer/badge.png?branch=coveralls)](https://coveralls.io/r/RestPack/restpack_serializer?branch=coveralls)
**Model serialization, paging, side-loading and filtering**
restpack_serializer allows you to quickly provide a set of RESTful endpoints for your application. It is an implementation of the emerging [JSON API](http://jsonapi.org/) standard.
> [Live Demo of RestPack Serializer](http://restpack-serializer-sample.herokuapp.com/)
---
**NOTE: This gem needs maintainers**: https://github.com/RestPack/restpack_serializer/issues/128
---
* [An overview of RestPack](http://www.slideshare.net/gavinjoyce/taming-monolithic-monsters)
* [JSON API](http://jsonapi.org/)
## Getting Started
### For rails projects:
After adding the gem `restpack_serializer` to your Gemfile, add this code to `config/initializers/restpack_serializer.rb`:
```ruby
Dir[Rails.root.join('app/serializers/**/*.rb')].each do |path|
require path
end
```
## Serialization
Let's say we have an `Album` model:
```ruby
class Album < ActiveRecord::Base
attr_accessible :title, :year, :artist
belongs_to :artist
has_many :songs
end
```
restpack_serializer allows us to define a corresponding serializer:
```ruby
class AlbumSerializer
include RestPack::Serializer
attributes :id, :title, :year, :artist_id, :href
end
```
`AlbumSerializer.as_json(album)` produces:
```javascript
{
"id": "1",
"title": "Kid A",
"year": 2000,
"artist_id": 1,
"href": "/albums/1"
}
```
`as_json` accepts an optional `context` hash parameter which can be used by your Serializers to customize their output:
```ruby
class AlbumSerializer
include RestPack::Serializer
attributes :id, :title, :year, :artist_id, :extras
optional :score
can_include :artists, :songs
can_filter_by :year
def extras
if @context[:admin?]
{ markup_percent: 95 }
end
end
end
```
```ruby
AlbumSerializer.as_json(album, { admin?: true })
```
All `attributes` are serialized by default. If you'd like to skip an attribute, you can pass an option in the `@context` as follows:
```ruby
AlbumSerializer.as_json(album, { include_title?: false })
```
You can also define `optional` attributes which aren't included by default. To include:
```ruby
AlbumSerializer.as_json(album, { include_score?: true })
```
## Exposing an API
The `AlbumSerializer` provides `page` and `resource` methods which provide paged collection and singular resource GET endpoints.
```ruby
class AlbumsController < ApplicationController
def index
render json: AlbumSerializer.page(params)
end
def show
render json: AlbumSerializer.resource(params)
end
end
```
These endpoint will live at URLs such as `/albums` and `/albums/142857`:
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums/4.json
The `AlbumSerializer` also provides a `single` method which will return a serialized resource similar to `as_json` above.
`page`, `resource` and `single` methods take an optional scope argument allowing us to enforce arbitrary constraints:
```ruby
AlbumSerializer.page(params, Albums.where("year < 1950"))
```
In addition to `scope`, all three methods also accept an optional `context` hash:
```ruby
AlbumSerializer.page(params, Albums.where("year < 1950"), { admin?: true })
```
Other features:
* [Custom Attributes Hash](https://github.com/RestPack/restpack_serializer/blob/master/spec/serializable/serializer_spec.rb#L55)
## Paging
Collections are paged by default. `page` and `page_size` parameters are available:
* http://restpack-serializer-sample.herokuapp.com/api/v1/songs.json?page=2
* http://restpack-serializer-sample.herokuapp.com/api/v1/songs.json?page=2&page_size=3
Paging details are included in a `meta` attribute:
http://restpack-serializer-sample.herokuapp.com/api/v1/songs.json?page=2&page_size=3 yields:
```javascript
{
"songs": [
{
"id": "4",
"title": "How to Dissapear Completely",
"href": "/songs/4",
"links": {
"artist": "1",
"album": "1"
}
},
{
"id": "5",
"title": "Treefingers",
"href": "/songs/5",
"links": {
"artist": "1",
"album": "1"
}
},
{
"id": "6",
"title": "Optimistic",
"href": "/songs/6",
"links": {
"artist": "1",
"album": "1"
}
}
],
"meta": {
"songs": {
"page": 2,
"page_size": 3,
"count": 42,
"include": [],
"page_count": 14,
"previous_page": 1,
"next_page": 3,
"first_href": "/songs?page_size=3",
"previous_href": "/songs?page_size=3",
"next_href": "/songs?page=3&page_size=3",
"last_href": "/songs?page=14&page_size=3"
}
},
"links": {
"songs.artist": {
"href": "/artists/{songs.artist}",
"type": "artists"
},
"songs.album": {
"href": "/albums/{songs.album}",
"type": "albums"
}
}
}
```
URL Templates to related data are included in the `links` element. These can be used to construct URLs such as:
* /artists/1
* /albums/1
## Side-loading
Side-loading allows related resources to be optionally included in a single API response. Valid side-loads can be defined in Serializers by using ```can_include``` as follows:
```ruby
class AlbumSerializer
include RestPack::Serializer
attributes :id, :title, :year, :artist_id, :href
can_include :songs, :artists
end
```
In this example, we are allowing related `songs` and `artists` to be included in API responses. Side-loads can be specifed by using the `include` parameter:
#### No side-loads
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json
#### Side-load related Artists
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?include=artists
which yields:
```javascript
{
"albums": [
{
"id": "1",
"title": "Kid A",
"year": 2000,
"href": "/albums/1",
"links": {
"artist": "1"
}
},
{
"id": "2",
"title": "Amnesiac",
"year": 2001,
"href": "/albums/2",
"links": {
"artist": "1"
}
},
{
"id": "3",
"title": "Murder Ballads",
"year": 1996,
"href": "/albums/3",
"links": {
"artist": "2"
}
},
{
"id": "4",
"title": "Curtains",
"year": 2005,
"href": "/albums/4",
"links": {
"artist": "3"
}
}
],
"meta": {
"albums": {
"page": 1,
"page_size": 10,
"count": 4,
"include": [
"artists"
],
"page_count": 1,
"previous_page": null,
"next_page": null,
"first_href": '/albums',
"previous_href": null,
"next_href": null,
"last_href": '/albums'
}
},
"links": {
"albums.songs": {
"href": "/songs?album_id={albums.id}",
"type": "songs"
},
"albums.artist": {
"href": "/artists/{albums.artist}",
"type": "artists"
},
"artists.albums": {
"href": "/albums?artist_id={artists.id}",
"type": "albums"
},
"artists.songs": {
"href": "/songs?artist_id={artists.id}",
"type": "songs"
}
},
"linked": {
"artists": [
{
"id": "1",
"name": "Radiohead",
"website": "http://radiohead.com/",
"href": "/artists/1"
},
{
"id": "2",
"name": "Nick Cave & The Bad Seeds",
"website": "http://www.nickcave.com/",
"href": "/artists/2"
},
{
"id": "3",
"name": "John Frusciante",
"website": "http://johnfrusciante.com/",
"href": "/artists/3"
}
]
}
}
```
#### Side-load related Songs
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?include=songs
An album `:has_many` songs, so the side-loaded songs are paged. The `meta.songs` includes `previous_href` and `next_href` which point to the previous and next page of this side-loaded data. These URLs take the form:
* http://restpack-serializer-sample.herokuapp.com/api/v1/songs.json?album_ids=1,2,3,4&page=2
#### Side-load related Artists and Songs
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?include=artists,songs
## Filtering
Simple filtering based on primary and foreign keys is supported by default:
#### By primary key:
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?id=1
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?ids=1,2,4
#### By foreign key:
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?artist_id=1
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?artist_ids=2,3
#### Custom filters:
Custom filters can be defined with the `can_filter_by` option:
```ruby
class Account
include RestPack::Serializer
attributes :id, :application_id, :created_by, :name, :href
can_filter_by :application_id
end
```
Side-loading is available when filtering:
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?artist_ids=2,3&include=artists,songs
## Sorting
Sorting attributes can be defined with the `can_sort_by` option:
```ruby
class Account
include RestPack::Serializer
attributes :id, :application_id, :created_by, :name, :href
can_sort_by :id, :name
end
```
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?sort=id
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?sort=-name
* http://restpack-serializer-sample.herokuapp.com/api/v1/albums.json?sort=name,-id
## Running Tests
`bundle`
`rake spec`