Decouple your models from forms. 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.

Although reform can be used in any Ruby framework, it comes with Rails support, works with simple_form and other form gems, allows nesting forms to implement has_one and has_many relationships, can compose a form from multiple objects and gives you coercion.


Add this line to your Gemfile:

gem 'reform'

Defining Forms

You're working at a famous record label and your job is archiving all the songs, albums and artists. You start with a form to populate your songs table.

class SongForm < Reform::Form
  property :title
  property :length

  validates :title,  presence: true
  validates :length, numericality: true

To add fields to the form use the ::property method. Also, validations no longer go into the model but sit in the form.

Using Forms

In your controller you'd create a form instance and pass in the models you wanna work on.

class SongsController
  def new
    @form =

You can also setup the form for editing existing items.

class SongsController
  def edit
    @form =

Rendering Forms

Your @form is now ready to be rendered, either do it yourself or use something like Rails' #form_for, simple_form or formtastic.

= form_for @form do |f|

  = f.input :name
  = f.input :title

Validating Forms

After a form submission, you wanna validate the input.

class SongsController
  def create
    @form =

    #=> params: {song: {title: "Rio", length: "366"}}

    if @form.validate(params[:song])

Reform uses the validations you provided in the form - and nothing else.

Saving Forms

We provide a bullet-proof way to save your form data: by letting you do it!

  if @form.validate(params[:song]) do |data, nested|
      data.title  #=> "Rio"
      data.length #=> "366"

      nested      #=> {title: "Rio"}


While data gives you an object exposing the form property readers, nested is a hash reflecting the nesting structure of your form. Note how you can use arbitrary code to create/update models - in this example, we used Song::create.

To push the incoming data to the models directly, call #save without the block.  #=> populates song with incoming data
                #   by calling and

Think of as a sync operation where the submitted data is written to your models using public setters.

Note that this does not call save on your models per default: this only happens in an ActiveRecord environment or when Form::ActiveRecord is mixed in (learn more here).

Nesting Forms: 1-1 Relations

Songs have artists to compose them. Let's say your Song model would implement that as follows.

class Song < ActiveRecord::Base
  has_one :artist

The edit form should allow changing data for artist and song.

class SongForm < Reform::Form
  property :title
  property :length

  property :artist do
    property :name

    validates :name, presence: true

  #validates :title, ...

See how simple nesting forms is? By passing a block to ::property you can define another form nested into your main form.

has_one: Setup

This setup's only requirement is having a working Song#artist reader.

class SongsController
  def edit
    song = Song.find(1)
    song.artist #=> <0x999#Artist title="Duran Duran">

    @form =

has_one: Rendering

When rendering this form you could use the form's accessors manually.

= text_field :title,         @form.title
= text_field "artist[name]",

Or use something like #fields_for in a Rails environment.

= form_for @form |f|
  = f.text_field :title
  = f.text_field :length

  = f.fields_for :artist do |a|
    = a.text_field :name

has_one: Processing

The block form of #save would give you the following data. do |data, nested|
  data.title #=> "Hungry Like The Wolf" #=> "Duran Duran"

  nested #=> {title:  "Hungry Like The Wolf",
         #    artist: {name: "Duran Duran"}}

Supposed you use reform's automatic save without a block, the following assignments would be made.       = "Hungry Like The Wolf" = "Duran Duran"

Nesting Forms: 1-n Relations

Reform also gives you nested collections.

Let's have Albums with songs!

class Album < ActiveRecord::Base
  has_many :songs

The form might look like this.

class AlbumForm < Reform::Form
  property :title

  collection :songs do
    property :title

    validates :title, presence: true

This basically works like a nested property that iterates over a collection of songs.

has_many: Rendering

Reform will expose the collection using the #songs method.

= text_field :title,         @form.title
= text_field "songs[0][title]", @form.songs[0].title

However, #fields_for works just fine, again.

= form_for @form |f|
  = f.text_field :title

  = f.fields_for :songs do |s|
    = s.text_field :title

has_many: Processing

The block form of #save will expose the data structures already discussed. do |data, nested|
  data.title #=> "Rio"
  data.songs.first.title #=> "Hungry Like The Wolf"

  nested #=> {title: "Rio"
         #   songs: [{title: "Hungry Like The Wolf"},
         #          {title: "Last Chance On The Stairways"}]


Sometimes you might want to embrace two (or more) unrelated objects with a single form. While you could write a simple delegating composition yourself, reform comes with it built-in.

Say we were to edit a song and the label data the record was released from. Internally, this would imply working on the songs table and the labels table.

class SongWithLabelForm < Reform::Form
  include Composition

  property :title, on: :song
  property :city,  on: :label

  validates :title, :city, presence: true

Note that reform needs to know about the owner objects of properties. You can do so by using the on: option.

Composition: Setup

The constructor slightly differs.

@form =, label:

Composition: Rendering

After you configured your composition in the form, reform hides the fact that you're actually showing two different objects.

= form_for @form do |f|

  Song:     = f.input :title

  Label in: = f.input :city

Composition: Processing

When using `#save' without a block reform will use writer methods on the different objects to push validated data to the properties.

Here's how the block parameters look like. do |data, nested|
  data.title #=> "Rio"  #=> "London"

  nested #=> {
         #   song:  {title: "Rio"}
         #   label: {city: "London"}
         #   }


Often you want incoming form data to be converted to a type, like timestamps. Reform uses virtus for coercion, the DSL is seamlessly integrated into Reform with the :type option.

Be sure to add virtus to your Gemfile.

require 'reform/form/coercion'

class SongForm < Reform::Form
  include Coercion

  property :written_at, type: DateTime
end do |data, nested|
  data.written_at #=> <DateTime XXX>

Virtual Attributes

Virtual fields come in handy when there's no direct mapping to a model attribute or when you plan on displaying but not processing a value.

Empty Fields

Often, fields like password_confirmation shouldn't be retrieved from the model. Reform comes with the :empty option for that.

class PasswordForm < Reform::Form
  property :password
  property :password_confirmation, :empty => true

Here, the model won't be queried for a password_confirmation field when creating and rendering the form. Likewise, when saving the form, the input value is not written to the decorated model. It is only readable in validations and when saving the form.

form.validate("password" => "123", "password_confirmation" => "321")

form.password_confirmation #=> "321"

The nested hash in the block-#save provides the same value. do |f, nested|
  nested[:password_confirmation] #=> "321"

Read-Only Fields

Almost identical, the :virtual option makes fields read-only. Say you wanna show a value, but not process it after submission, this option is your friend.

class ProfileForm < Reform::Form
  property :country, :virtual => true

This time reform will query the model for the value by calling

You want to use this to display an initial value or to further process this field with JavaScript. However, after submission, the field is no longer considered: it won't be written to the model when saving.

It is still readable in the nested hash and through the form itself. do |f, nested|
  nested[:country] #=> "Australia" #=> "Australia"

Agnosticism: Mapping Data

Reform doesn't really know whether it's working with a PORO, an ActiveRecord instance or a Sequel row.

When rendering the form, reform calls readers on the decorated model to retrieve the field data (Song#title, Song#length).

When saving a submitted form, the same happens using writers. Reform simply calls Song#title=(value). No knowledge is required about the underlying database layer.

Nesting forms only requires readers for the nested properties as Album#songs.

Rails Integration

Check out @gogogarret's sample Rails app using Reform.

Rails and Reform work out-of-the-box. If you're using Rails but for some reason wanna use the pure reform, require reform/form, only.

ActiveRecord Compatibility

Reform provides the following ActiveRecord specific features. They're mixed in automatically in a Rails/AR setup.

  • Uniqueness validations. Use validates_uniqueness_of in your form.
  • Calling Form#save will explicitely call save on your model (added in 0.2.1) which will usually trigger a database insertion or update.

ActiveModel Compliance

Forms in Reform can easily be made ActiveModel-compliant.

Note that this step is not necessary in a Rails environment.

class SongForm < Reform::Form
  include Reform::Form::ActiveModel

If you're not happy with the model_name result, configure it manually.

class CoverSongForm < Reform::Form
  include Reform::Form::ActiveModel

  model :song

This is especially helpful when your framework tries to render cover_song_path although you wanna go with song_path.

FormBuilder Support

To make your forms work with all the form gems like simple_form or Rails form_for you need to include another module.

Again, this step is implicit in Rails and you don't need to do it manually.

class SongForm < Reform::Form
  include Reform::Form::ActiveModel
  include Reform::Form::ActiveModel::FormBuilderMethods


By explicitely defining the form layout using ::property there is no more need for protecting from unwanted input. strong_parameter or attr_accessible become obsolete. Reform will simply ignore undefined incoming parameters.


If you run into any trouble chat with us on


Nick Sutterer

Garrett Heinlen


Great thanks to Blake Education for giving us the freedom and time to develop this project while working on their project.