There may be times when you want to decouple your form objects from the model, in simple words, insert another layer between your View and the Model, Reform is all about that.

Reform gives you a form object with validations and nested setup of models. It is completely framework-agnostic and doesn't care about your database.

Installing Reform

Add this line to your gemfile ;

gem 'reform'

Since Reform 2.2, you've to add reform-rails gem to your gemfile to automatically load ActiveModel/Rails files.

gem 'reform-rails'

Defining Forms

Let's say we want to decouple our Model Album, which has title attribute.
Forms are defined in a separate class, which map to the model.

class AlbumForm < Reform::Form
  property :title
  validates :title, presence: true
end

Fields are defined using property and their respective validations. The validations no longer go directly into the model.

Setup

To instantiate the Form object,

class AlbumsController
  def new
    @form = AlbumForm.new(Album.new)  #Note that we've decalred object of the AlbumForm class, which is mapping to our
                                                                           # model.
  end

Same goes for when editing an object,

def edit
 @form = AlbumForm.new(Album.find(1))
end

Rendering Forms

A simple form_for works for rendering the form object, as in rails. You can also use SimpleForm or FormTastic which are perfectly compatible with Reform.

= form_for @form do |f|
  = f.input :title

For nested forms, you can go with fields_for, similar to the rails way. Again remember to pass the Reform object and not the model object.

Prepopulation

There are times when you want to set your model up with some default values when it loads up fresh, prepopulate! method helps you with that.

Configuration:

It goes something like ;

class AlbumForm < Reform::Form
  property :artist, prepopulator: ->(options) { self.artist = Artist.new } do
  property :name
end

The options value can be a lambda or an instance method.

Invocation:

Prepopulation needs to be invoked manually.

form = AlbumForm.new(Album.new)
form.prepopulate!

The invoking needs to happen before the form in rendered, which is generally coded in the controller action.

Validations

After form submission, you can validate the submitted values.

class SongsController
   def create
     @form = SongForm.new(Song.new)   #=> params: {song: {title: "Rio", length: "366"}}
     if @form.validate(params[:song])
     #business logic

The validate method is called on the form object based on the validations you defined in the FormClass. The model remains untouched.

Syncing back

After the validations have passed, you can either call save and let reform do the rest or call sync which will write the properties back to the model.

Saving forms

The easiest approach is to call save on the form object and let reform do the rest. It goes as ;

if @form.validate(params[:song])
  @form.save  #=> populates album with incoming data
          #   by calling @form.album.title=.
else
  # handle validation errors.
end

The above code will sync the data into the model.

Saving forms manually

At times when you've your own protocols to follow, you can manually save the input values. When you call save with a block, it gives you a nested hash of input values which you can then save as per your terms.
The code goes as ;

  @form.save do |hash|
    hash      #=> {title: "Greatest Hits"}
    Album.create(hash)
  end

You can access the form's model by simply calling model on the form object, like ;

  @form.save do |hash|
    album = @form.model

Compositions

Reform allows you to map multiple objects into one form. It goes something like this ;

class AlbumForm < Reform::Form
   include Composition

   property :id,    on: :album
   property :title, on: :album
   property :songs, on: :cd
   property :cd_id, on: :cd, from: :id
end

When initialising a composition, you've to pass the hash which contains the composees

AlbumForm.new(album: album, cd: CD.find(1))

For further look into Reform, you can refer the following links,
Trailblazer, Reform GitHub, Reform Blog Post