Skip to content

Repository files navigation

✨ HyperActiveForm ✨

HyperActiveForm is a simple form object implementation for Rails.

Form objects are objects that encapsulate form logic and validations, they allow to extract the business logic out of the controller and models into specialized objects.

HyperActiveForm's form objects mimic the ActiveModel API, so they work out of the box with Rails' form helpers, and allow you to use the ActiveModel validations you already know.

This allows you to only keep strictly necessary validations in the model, and have business logic validations in the form object. This is especially useful when you want different validations to be applied depending on the context.

Installation

Add this line to your application's Gemfile:

gem'hyperactiveform'

And then execute:

$ bundle install

Run the install generator:

$ rails generate hyper_active_form:install

this will create an ApplicationForm class in your app/forms directory. You can use it as a base class for your form objects.

Generators

You can generate a form and its tests with the following command:

$ rails generate form FooBar

This will create the FooBarForm

Usage

Here is an example of an HyperActiveForm form object:

classProfileForm < ApplicationForm# proxy_for is used to delegate the model name to the class, and some methods to the object# this helps use `form_with` in views without having to specify the urlproxy_forUser,:@user# Define the form fields, using ActiveModel::Attributesattribute:first_nameattribute:last_nameattribute:birth_date,:date# Define the validations, using ActiveModel::Validationsvalidates:first_name,presence: truevalidates:last_name,presence: truevalidates:birth_date,presence: true# Pre-fill the form if neededdefsetup(user)@user=userself.first_name=user.first_nameself.last_name=user.last_nameself.birth_date=user.birth_dateend# Perform the form logicdefperform@user.update!(first_name: first_name,last_name: last_name,birth_date: birth_date)endend

The controller would look like this:

classUsersController < ApplicationControllerdefedit@form=ProfileForm.new(user: current_user)enddefupdate@form=ProfileForm.new(user: current_user)if@form.submit(params[:user])redirect_toroot_path,notice: "Profile updated"elserender:edit,status: :unprocessable_entityendend

And the view would look like this:

<%= form_with(model: @form) do |f| %><%= f.text_field :first_name %><%= f.text_field :last_name %><%= f.date_field :birth_date %><%= f.submit %><% end %>

Understanding proxy_for

HyperActiveForm mimics a model object, you can use proxy_for to tell it which class and object to delegate to.

When using form_for or form_with, Rails will choose the URL and method based on the object, according to the persisted state of the object and its model name.

The first argument of proxy_for is the class of the object, and the second argument is the name of the instance variable that holds the object.

classProfileForm < ApplicationFormproxy_forUser,:@user# Will delegate to @userend

If you pass an url and method yourself, you don't need to use proxy_for.

Understanding setup

setup is called just after the form is initialized, and is used to pre-fill the form with data from the object.

setup will receive the same arguments as the initializer, so you can use it to pass any data you need to the form.

classProfileForm < ApplicationFormdefsetup(user)@user=userself.first_name=user.first_nameself.last_name=user.last_nameself.birth_date=user.birth_dateendend

Understanding perform

When using submit or submit!, HyperActiveForm will first assign the form attributes to the object, then perform the validations, then call perform on the object if the form is valid.

The perform method is where you should do the actual form logic, like updating the object or creating a new one.

If the return value of perform is not truthy, HyperActiveForm will consider the form encountered an error and submit will return false, or submit! will raise a HyperActiveForm::FormDidNotSubmitError.

At any point during the form processing, you can raise HyperActiveForm::CancelForm to cancel the form submission, this is the same as returning false.

Understanding add_errors_from

HyperActiveForm provides a method to add errors from a model and apply them fo the form.

This is useful when the underlying model has validations that are not set up in the form object, and you want them to be applied to the form.

classUser < ApplicationRecordvalidates:first_name,presence: trueendclassProfileForm < ApplicationFormproxy_forUser,:@userattribute:first_namedefsetup(user)@user=userself.first_name=user.first_nameenddefperform@user.update!(first_name: first_name) || add_errors_from(@user)endend

Not all forms map to a single model

The power of HyperActiveForm is that you can use it to create forms that don't map to a single model.

Some forms can be used to create several models at once. Doing so without form objects can be tedious especially with nested attributes.

Some forms dont map to any model at all, like a simple contact form that only sends an email and saves nothing in the database, or a sign in form that would only validate the credentials and return the instance of the connected user.

One great example of such forms are search forms. You can use a form object to encapsulate the search logic :

classUserSearchForm < ApplicationFormattribute:nameattribute:emailattribute:min_age,:integerattr_reader:results# So the controller can access the resultsdefperform@results=User.allifname.present?@results=@results.where(name: name)endifemail.present?@results=@results.where(email: email)endifage.present?@results=@results.where("age >= ?",age)endtrueendend

And in the controller:

classUsersController < ApplicationControllerdefindex@form=UserSearchForm.new@form.submit!(params[:user])@users=@form.resultsendend

Callbacks

HyperActiveForm provides callbacks for assign_form_attributes and submit.

You can use these callbacks to run code before or after assigning the form attributes or before or after submitting the form.

classProfileForm < ApplicationForm# ...before_submit:do_something_before_submitbefore_assign_form_attributes:do_something_before_assign_form_attributesdefdo_something_before_submit# Do something before submitting the formenddefdo_something_before_assign_form_attributes# Do something before assigning the form attributesendend

About

Simple form objects for Rails

Resources

Stars

83 stars

Watchers

2 watching

Forks

Releases

Used by

Contributors

Languages