We have been discussing refactoring in our previous posts. Next to our refactoring pattern, we will discuss few gems that can be used to improve our coding style.
In my last post i explained form objects pattern. Form objects are nothing but a replacement of your model object for form. Controllers are meant to receive request and provide result to the views. We can extract the form create/update functionality from controller and abstract it in a form object class.
Benefits of form object pattern are:
1. It abstract the functionality in a class which is concerned about that particular functionality only as stated by SRP (single responsibility principle).
2. Extract validations out of models that may not be necessary.
3. Reduces complexity in saving associated records (multiple model form).
For this tutorial we will look in to Reform gem which helps to create form objects. It maintains validations for one or multiple models, where a model can be any kind of Ruby object. Add reform gem to gemfile and bundle install.
gem "reform"
Lets see an example:
class Author < ActiveRecord::Base
has_many :books
end
class Books < ActiveRecord::Base
belongs_to :author
end
We create a form object class that inherits from Reform::Form. We define attributes with property and validations in the class. Reform provides support for nested objects. As we have books association in our Author model. We can define this association in our form class with collection keyword.
class AuthorCreateForm < Reform::Form
property :name
validates :name, presence: true
collection :books do
property :name
property :description
property :publish_on
validates :name, presence: true
end
end
Now in our controller we can use this:
class AuthorsController
def new
@form = AuthorCreateForm.new(Author.new)
end
def create
@form = AuthorCreateForm.new(Author.new)
if @form.validate(params[:author])
@form.save
else
# handle validation errors.
end
end
end
Note: The #validate method first updates the values of the form. However, the model object does not save. It then runs validation. The values are saved only after #save or #sync operation are called on form object.
Passing this form object in our view:
= form_for @form do |f|
= f.input :name
= f.fields_for :books do |a|
= a.text_field :name
......
NOTE: The save method on the form object will call #save on the model and nested models.
Conclusion:
Isn't that nice, we have extracted the functionality from controller in separate class. Our controllers are now more clean and skinny. Also our controller no longer deals with create/update logic.
You may use simple ruby class or Reform gem for form objects. Form objects pattern is useful with complex nested model forms. Reform provides us neat ways to define fields and associations.
For more details and options on reform gem please visit https://github.com/apotonick/reform
0 Comment(s)