Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
An alternative to accepts_nested_attributes_for that doesn't tightly couple your view to your model
tag: v0.0.5

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.


Redtape provides an alternative to ActiveRecord::NestedAttributes#accepts_nested_attributes_for in the form of, well, a Form! The initial implementation was heavily inspired by "7 Ways to Decompose Fat Activerecord Models" by Bryan Helmkamp.

In a nutshell, accepts_nested_attributes_for tightly couples your View to your Model. This is highly undesirable as it makes both harder to maintain. Instead, the Form provides a Controller delegate that mediates between the two, acting like an ActiveModel from the View and Controller's perspective but acting a proxy to the Model layer.


Add this line to your application's Gemfile:

gem 'redtape'

And then execute:

$ bundle

Or install it yourself as:

$ gem install redtape


To use Redtape, create a subclass of Redtape::Form

Form class conventions

Call #validates_and_saves

This class method should be passed the underscored names of each "top level model" that this form will save. For instance, say you have a RegistrationForm that wants to manage a User--and that User class has one Account. You'd want your code to look like:

    class RegistrationForm < Redtape::Form
      validates_and_saves :user

Note that there is no mention of the Account class. We handle that elsewhere in the Form subclass.

Add accessors to your Form for each form field

The subclass also needs an accessor for each form field that you wish to capture. You can accomplish this with plain ol' #attr_accessor calls or, if you're feeling cute, you could use Virtus to provide more robust attribute definitions.

These accessors define the contract with the view. The fields are expected to be supplied (or optionally not) by the view and no more.

Implement a #populate method

ActionPack will populate the Form just like it would any other ActiveModel object. #populate then finds or builds your User and Account objects using the values set on the accessors by ActionPack.

So say we have a RegistrationForm with these fields:

    class RegistrationForm < Redtape::Form
      validates_and_saves :user

      attr_accessor :first_name, :last_name, :email

... then your #populate method may look something like this:

    def populate
      user = User.joins(:account).where(" = ?", email).first
      unless user
        account =
          :name => "#{first_name} #{last_name}",
          :email => email
        self.user = => account)

Using the Form subclass

In your #create or #update methods, you'll want somthing like the following:

    def update
      @form =[:registration_form]
        # happy path
        # sad path

In your view, you should be able to get by just using the Form instance where you would normally use a view.

In some special cases, e.g., you're using devise as we are, you may need something like this:

    <%= form_for(@form, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
    <%e end %>

That's it!

Redtape will use your model's/models' validations to determine if the form data is correct. That is, you validate and save the same way you would with any ActiveModel. If any of the models are invalid, errors are added to the Form for handling within the View/Controller.

What's left

We'd really like to add the following to make Redtape even easier for folks to plug n' play:

  • A Rails generator to add the app/forms and (test/spec)/forms directories
  • Handling of _id params to further automate updates via forms


    1. Fork it
    2. Create your feature branch (git checkout -b my-new-feature)
    3. Commit your changes (git commit -am 'Add some feature')
    4. Push to the branch (git push origin my-new-feature)
    5. Create new Pull Request

    Finally, we'd really like your feedback

Something went wrong with that request. Please try again.