To create a simple, light-weight, easy to override, and easy to understand boilerplate for standard controller actions.
- Rails 5
- Active Model Serializers 0.10.2
class ApplicationController < ActionController::API
include GenericActions
end
Now the controller has support for the basic index
, show
, create
,
update
, and destroy
actions. A find
action that will return a single
resource or 404 instead of a collection like the index
action is also
available.
Resources can be filtered and associations can be side-loaded in a single request. Simple header-based pagination is also included.
The param-based filtering and side-loading was inspired by the JSONAPI spec. The API for defining filters was inspired by JSONAPI::Resources.
Creating a filter is done by using the filter
method.
GET /customers?filter[first_name]=Joshua
class CustomersController < ApplicationController
filter :first_name
end
If your filter needs to do something besides check equality:
class CustomersController < ApplicationController
filter :first_name, -> (chain, value) do
chain.where('first_name like ?', "%#{value}%")
end
end
Filters are chained.
GET /customers?filter[first_name]=Joshua&filter[last_name]=Kappers
class CustomersController < ApplicationController
filter :last_name
filter :first_name
end
Thanks to Active Model Serializers, we can easily include association data in a single payload.
class Customer
has_many :accounts
has_many :addresses
end
class Account
belongs_to :customer
belongs_to :agency
end
class Address
belongs_to :customer
end
class Agency
has_many :accounts
end
GET /customers?include=accounts,addresses
{
...,
"accounts": [],
"addresses": []
}
GET /account/1?include=customer.addresses,agency
{
...,
"customer": {
"addresses": [],
},
"agency": {}
}
Nah, Active Model Serializers will handle it if you don't. It seems that for large payloads, defining a serializer speeds things up. I have not benchmarked this to be certain that is the case.
The actual method for applying the filter was lifted from Kaminari.
Default page size is currently 50
GET /customers?page=2&size=10
HEADERS
'X-Page' => 1,
'X-Page-Size' => 10,
'X-Page-Count' => 12,
'X-Total' => 120
Fine..
class ApplicationController
include GenericActions
def default_page_size
25
end
end
- I was tired of duplicating the same controller boilerplate.
- I used JSONAPI::Resources but...
- ...I had some problems with the JSONAPI spec.
First, there is no support for creating multiple entities in a single request. This was a huge problem for me and is a problem for many others. While there is a conversation and intent to include support in version 1.1, waiting is not an option. This is especially true considering the conversation is over a year old with seemingly no solution in sight.
I have tried several libraries and workarounds. My "favorite" workaround
was to use Rails acccepts_nested_attributes_for
along with JSONAPI::Resources.
While this wasn't too bad on the API, I found that library support for consuming the API is lacking.
I quickly realized I was sacrificing a lot of the productivity and duck-taping together libraries that weren't designed to work together, all for the sake of following a spec that doesn't add any value for my project or meet the needs of my project.
Rails' standard serialization has served me well and doesn't slow me down.
Yes! The side-loading of associations in a single request is very handy. The filtering features were also very nice.
I want to move off of the spec and that library, but I want to keep those features, so I replicated them here.