Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

DEV Community

Wilbur Suero
Wilbur Suero

Posted on

Understanding Form Objects in Ruby on Rails

In Ruby on Rails applications, handling complex forms can become messy when too much logic is stuffed into models or controllers. This is where Form Objects come in—a design pattern that helps separate form-related logic from Active Record models, making our applications more maintainable and testable.

The Problem with Traditional Forms

By default, Rails encourages using Active Record models directly in forms. However, this approach has some downsides:

  • Fat Models: Business logic and validation bloat the model, making it harder to maintain.
  • Fat Controllers: Controllers become responsible for handling complex form submissions.
  • Multiple Models in One Form: Standard Rails forms work well with single models, but handling multiple related models can be cumbersome.

To address these issues, we use Form Objects.

What is a Form Object?

A Form Object is a Plain Old Ruby Object (PORO) designed to handle form submissions. It encapsulates form-specific validations and persistence logic while keeping models and controllers clean.

When to Use a Form Object

  • When a form involves multiple models.
  • When you want to keep your controllers thin and models focused.
  • When form validation differs from the database schema.
  • When reusing form logic across multiple places in your application.

Implementing a Form Object in Rails

Let's walk through an example where we create a UserRegistrationForm that handles user sign-ups along with profile information.

Step 1: Create the Form Object

Create a new file in app/forms/user_registration_form.rb:

class UserRegistrationForm
  include ActiveModel::Model

  attr_accessor :name, :email, :password, :password_confirmation, :bio

  validates :name, :email, :password, :password_confirmation, presence: true
  validates :password, confirmation: true
  validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }

  def initialize(attributes = {})
    super(attributes)
  end

  def save
    return false unless valid?

    ActiveRecord::Base.transaction do
      user = User.create!(name: name, email: email, password: password)
      Profile.create!(user: user, bio: bio)
    end
    true
  rescue ActiveRecord::RecordInvalid
    false
  end
end
Enter fullscreen mode Exit fullscreen mode

Step 2: Use the Form Object in the Controller

Modify the UsersController to use the UserRegistrationForm:

class UsersController < ApplicationController
  def new
    @form = UserRegistrationForm.new
  end

  def create
    @form = UserRegistrationForm.new(user_params)

    if @form.save
      redirect_to root_path, notice: 'User registered successfully!'
    else
      render :new
    end
  end

  private

  def user_params
    params.require(:user_registration_form).permit(:name, :email, :password, :password_confirmation, :bio)
  end
end
Enter fullscreen mode Exit fullscreen mode

Step 3: Update the View

Modify the new.html.erb view:

<%= form_with model: @form, url: users_path do |form| %>
  <div>
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div>
    <%= form.label :email %>
    <%= form.email_field :email %>
  </div>

  <div>
    <%= form.label :password %>
    <%= form.password_field :password %>
  </div>

  <div>
    <%= form.label :password_confirmation %>
    <%= form.password_field :password_confirmation %>
  </div>

  <div>
    <%= form.label :bio %>
    <%= form.text_area :bio %>
  </div>

  <div>
    <%= form.submit 'Register' %>
  </div>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Benefits of Using Form Objects

  • Separation of Concerns: Keeps models and controllers focused on their primary responsibilities.
  • Improved Testability: You can test form logic independently.
  • Easier Maintenance: Avoids bloated models with unnecessary validations.

Form Objects in Ruby on Rails provide a clean way to manage complex form submissions while keeping code organized. By encapsulating form logic in a dedicated class, we improve maintainability, readability, and re-usability in our applications.

If you're dealing with bloated models and controllers due to complex forms, consider adopting Form Objects as a structured solution!

Top comments (0)