Form objects are an informal pattern for separating validation logic from persistence logic and reducing the complexity of your models, making them easier to test and reason about. This guide shows you how to use form objects in your Rails application.
The form object
First step, add the formeze gem to your Gemfile (and run bundler to install):
gem 'formeze', '~> 3.0'
You can then define a form object by inheriting from
Formeze::Form and declaring the form fields. For example, here’s how you might define a form for adding products:
require 'formeze' class ProductForm < Formeze::Form field :title, scrub: [:strip] field :description, multiline: true, maxlength: 2000, scrub: [:strip] field :size, required: false, values: %w(Small Medium Large) end
The different field options are detailed in the formeze README.
This class can then be used to automatically parse, scrub, and validate form data submitted to your application, irrespective of where or how the data is persisted.
The view corresponding to the form object above might look something like this:
<%= form_tag action: :create do %> <input type="text" name="title" value="<%= @form.title %>"> <textarea name="description"><%= @form.description %></textarea> <select name="size"> <%= options_for_select %w(Small Medium Large), @form.size %> </select> <input type="submit" value="Add product"> <% end %>
The field values can be accessed as attributes on the form object. Either you can use regular HTML for the form inputs, the Rails form helpers, or a combination of both.
The controller corresponding to the form object and view above might look like this:
class ProductsController < ApplicationController def new @form = ProductForm.new end def create @form = ProductForm.new.parse(request.raw_post) if @form.valid? # save form data # @form.to_h returns a hash of field names and values # fields can also be accessed as attributes like @form.description else render :new, status: :unprocessable_entity end end end
If the form is valid you’ll typically pass the data to your model layer. If the form is invalid you’ll typically re-render the form so the user can make changes and re-submit.
Displaying validation errors
Re-rendering the form without any indication of why it won’t save is confusing for users, so in most cases you’ll want to display the validation errors to the user, for example:
<% if @form.errors? %> <div class="errors"> <ul> <% @form.errors.each do |error| %> <li><%= error %></li> <% end %> </ul> </div> <% end %>
This is similar to how you would display ActiveRecord validation errors in views, except that the validations and errors are defined on the form object, not your model object.
That’s all there is to it. Give it a try in your next application!