ActiveModel::Model [Rails 4 Countdown to 2013]

Posted on

This post is part of a series of 31 Rails 4 articles being released each day in December 2012.

The form helpers provided by Rails make working with Active Model resources a pleasure. For example, some benefits of using form_for are:

  • The path of where the form should be submitted to is set automatically based on the resource
  • Fields are bound
  • Controllers receive form data nicely structured in a params hash

What happens in the scenario where you have a form to submit, but no matching Active Record instance? In these cases, you would either build the form using form_tag and various form helpers, or create a model that isn't backed by a database table.

Introducing ActiveModel::Model

ActiveModel::Model is a module mixin that allows Ruby objects to work with Action Pack. What this means is you can now include ActiveModel::Model in a plain old Ruby class, and use instances of that class in view helpers such as form_for. Classes that include ActiveModel::Model also get several Active Model features out of the box, such as:

  • Model name introspections
  • Conversions
  • Translations
  • Validations

Contact Form Example

An example of a scenario that could benefit from Active Model features, such as validations, is creating a contact form. To get started, create a new class named Contact:

class Contact
  include ActiveModel::Model

  attr_accessor :name, :email, :message

  validates :name, presence: true
  validates :email, presence: true
  validates :message, presence: true, length: { maximum: 300 }
end

The Contact model has three attributes, name, email, and message. All are required, and a message can be no longer than 300 characters.

With our basic model in place, we can now create a form using form_for:

<h1>Contact Us</h1>

<%= form_for @contact do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>

  <%= f.label :email %>
  <%= f.email_field :email %>

  <%= f.label :message %>
  <%= f.text_area :message %>

  <%= f.submit 'Submit' %>
<% end %>

Next, create a resourceful route contacts, that only points to :new, and :create.

# config/routes.rb
resources :contacts, only: [:new, :create]

Finally, we will need a controller to handle the creation of a Contact instance for our new action, and sending an email for a valid contact in the create action.

class ContactsController < ApplicationController
  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(params[:contact])
    if @contact.valid?
      ContactMailer.new_contact(@contact).deliver
      redirect_to root_path
    else
      render :new
    end
  end
end

Upgrade Path

To take advantage of something like ActiveModel::Model today, you can use the gem active_attr. Like ActiveModel::Model, it provides a module mixin which allows your objects to act as Active Model resources.

Here is the same Contact class, using ActiveAttr instead of ActiveModel::Model:

class Contact
  include ActiveAttr::Model

  attr_accessor :name, :email, :message

  validates :name, presence: true
  validates :email, presence: true
  validates :message, presence: true, length: { maximum: 300 }
end

Once Rails 4 is released, you can easily just replace all your ActiveAttr::Model inclusions with ActiveModel::Model for a seamless upgrade.

Further Reading

002

This post is by Kevin Faustino. Kevin is the Chief Craftsman of Remarkable Labs and also the founder of the Toronto Ruby Brigade.


Comments

comments powered by Disqus