Join the social network of Tech Nerds, increase skill rank, get work, manage projects...
 
  • Form objects with Reform gem (Rails refactoring)

    • 0
    • 0
    • 0
    • 0
    • 0
    • 0
    • 0
    • 0
    • 632
    Comment on it

    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)

Sign In
                           OR                           
                           OR                           
Register

Sign up using

                           OR                           
Forgot Password
Fill out the form below and instructions to reset your password will be emailed to you:
Reset Password
Fill out the form below and reset your password: