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