Resourcerer
What resourcerer
proposes is that you go from this:
class PersonController < ApplicationController
def new
@person = Person.new
end
def create
@person = Person.new(person_params)
if @person.save
redirect_to(@person)
else
render :new
end
end
def edit
@person = Person.find(params[:id])
end
def update
@person = Person.find(params[:id])
if @person.update_attributes(person_params)
redirect_to(@person)
else
render :edit
end
end
private
def person_params
params.require(:person).permit(:name)
end
end
To something like this:
class PersonController < ApplicationController
resource :person
def create
if person.save
redirect_to(person)
else
render :new
end
end
def update
if person.save
redirect_to(person)
else
render :edit
end
end
private
def person_params
params.require(:person).permit(:name)
end
end
The idea is that you don't have to write boilerplate for standard CRUD actions, while at the same time improving your controllers readibility.
Usage
Let's see what Resourcerer is doing behind the curtains :smiley:.
This examples assume that you are using Rails 4 Strong Parameters.
Obtaining a resource:
resource :person
Query Explanation
id present? |
Query (get/delete) | Query (post/patch/put) |
true |
Person.find(params[:id]) |
Person.find(params[:id]).attributes = person_params |
false |
Person.new |
Person.new(person_params) |
Configuration
Let's take a look at some of the things you can do:
Specify the model name:
resource(:company, model: :enterprise)
Specify the parameter key to use to fetch the object:
resource(:enterprise, finder_param: :company_id)
Specify the model attribute to use to perform the search:
resource(:enterprise, find_by: :name)
Specify how to obtain the object attributes:
# Specify the strong parameters method's name when using the default `StrongParametersStrategy`
resource(:employee, attributes_method: :person_params)
# Specify the parameter key that holds the attributes when using the `EagerAttributesStrategy`
resource(:person, param_key: :employee)
DSL
Resourcer also features a nice DSL, which is helpful when you need more control over the resource lifecycle.
You can also access every configuration option available above:
resource(:employee) do
model :person
find_by :name
find {|name| company.find_employee(name) }
build { company.new_employee }
assign { params.require(:employee).permit(:name) }
end
# is the same as:
resource(:employee, model: :person, finder_attribute: :name, finder: ->(name){ company.find_employee(name) }, builder: ->{ company.new_employee }, attributes: ->{ params.require(:employee).permit(:name) })
The DSL is more convenient when you have an object oriented design and want to allow an object to handle its collections, or as a quick way to set the StrongParameters method.
Configuration options play well together, and the defaults try to make intelligent use of them. For example,
setting the finder_attribute
in the example above changes the finder_param
to person_name
instead of person_id
, and the value of that parameter is provided to the finder block.
Setting a distinct object for a single action
There are times when one action in a controller is different from the rest of the actions. A nice approach to circumvent this is to use the controller's setter methods. This example uses presenter_rails.
resource(:article)
def show_oldest
self.article = Article.find_oldest
end
present :article do
ArticlePresenter.new(article)
end
Custom strategies
For times when you need custom behavior for resource finding, you can
create your own strategy by extending Resourcerer::Strategy
:
class VerifiableStrategy < Resourcerer::Strategy
delegate :current_user, :to => :controller
def resource
instance = model.find(params[:id])
if current_user != instance.user
raise ActiveRecord::RecordNotFound
end
instance
end
end
You would then use your custom strategy in your controller:
resource(:post, strategy: VerifiableStrategy)
Using decorators or presenters
With draper
If you use decorators, you can go from something like this:
class PersonController < ApplicationController
def new
@person = Person.new.decorate
end
def create
@person = Person.new(person_params)
if @person.save
redirect_to(@person)
else
@person = @person.decorate
render :new
end
end
def edit
@person = Person.find(params[:id]).decorate
end
def update
@person = Person.find(params[:id])
if @person.update_attributes(person_params)
redirect_to(@person)
else
@person = @person.decorate
render :edit
end
end
private
def person_params
params.require(:person).permit(:name)
end
end
To something like this by adding presenter_rails to the mix:
class PersonController < ApplicationController
resource(:person)
present :person do
person.decorate
end
def create
if person.save
redirect_to(person)
else
render :new
end
end
def update
if person.save
redirect_to(person)
else
render :edit
end
end
private
def person_params
params.require(:person).permit(:name)
end
end
Comparison with decent_exposure.
Resourcerer is heavily inspired on decent exposure, it attempts to be more predictable by focusing on finding a resource and assigning attributes, and discarding completely the view exposure part.
Similarities
Both allow you to find or initialize a resource and assign attributes, removing the boilerplate from most CRUD actions.
Differences
Resourcerer does not expose an object to the view in any way, scope the query to a collection method if defined, nor deal with collections.
Caveats
When using StrongParametersStrategy
Since attributes are assigned on every POST, PUT, and PATCH request, sometimes when using Strong Parameters it's not desirable that the attributes method is called. For that reason, the presence of params[param_key]
is checked before assigning attributes.
Troubleshooting
- The attributes are not being assigned: Check that the resource name matches the param used in the attributes method, and set the
param_key
configuration if they are different. - Need an error to be thrown if the params are not present: Use the
EagerStrongParametersStrategy
, available in the sample strategies in this repository, you can set it using thestrategy
configuration option.
Special Thanks
Resourcerer was inspired by decent_exposure.