Navigation

ADS specializes in using Ruby on Rails to build advanced, scalable, database-backed web sites for organizations of all sizes. Find out more at our website.

Atlantic Dominion Solutions

Have you ever wanted to create a form with the nice ActiveRecord Validations, but not use a database or ActiveRecord model? Like if you wanted to just have the result of the form emailed. An example might be a contact form or a special inquiry form that requires a name, an email, and perhaps a phone number.

Well, you can, and it”s quite easy too. Especially with the help of a little gem called Validatable.

To install:

sudo gem install validatable

There are at least 2 ways to include this in your Rails project, I’m going to cheat and use the shortcut method of including it in the environment.rb file to save on time and typing (and your patience):

Add to your environment.rb file:

require "validatable"

Next, lets create a new class file called contact.rb. You can put this in lib, models, or some other place, it”s up to you. I put it in my models directory for this example.

The contents of contact.rb will look like the following:

class Contact
  include Validatable
 
  attr_accessor :name, :email, :phone, :message
 
  validates_presence_of :name, :message => "Name is required."
  validates_format_of :email, :with => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
    :message => "Email is not valid."
  validates_format_of :phone, :with => /^[0-9]{3,3}-[0-9]{3,3}-[0-9]{4,4}$/,
    :message => "Phone number is not valid (xxx-xxx-xxxx)."
 
  def phone=(phone)
    @phone = phone.gsub(/[^0-9]/, "").gsub(/^([0-9]{0,3})([0-9]{0,3})([0-9]{0,})$/) do |match|
      tmp = ""
      tmp += $1 if $1.size > 0
      tmp += "-" + $2 if $2.size > 0
      tmp += "-" + $3 if $3.size > 0
      tmp
    end
  end  
 
  def deliver
    if valid?
      Notification.deliver_contact(self)
    else
      false
    end
  end
end

Oh my, doesn”t that look familiar? It looks just like an ActiveRecord model, except, gasp! It doesn”t inherit from ActiveRecord::Base. It”s just a plain Ruby class. Spiffy!

I”m assuming everything else looks familiar to you, yes? Swell, moving on then.

You will also notice I have a Notification mailer in there.

Note: I”m assuming you also know how to set up a mailer and the respective views. If you do not, comment below and I will add those pieces to this post if you desire.

Try it out, open up your console and play around.

>> c = Contact.new
=> #<Contact:0x218e974>
>> c.name = "Chris"
=> "Chris"
>> c.valid?
=> false
>> c.errors.full_messages
=> ["Email Email is not valid.", "Phone Phone number is not valid (xxx-xxx-xxxx)."]
>> c.email = "chris@techcfl.com"
=> "chris@techcfl.com"
>> c.phone = "1234567890"
=> "1234567890"
>> c.valid?
=> true
>> c.name
=> "Chris"
>> c.email
=> "chris@techcfl.com"
>> c.phone
=> "123-456-7890"
>> c.message = "Stillwater? Wtf is that?"
=> "Stillwater? Wtf is that?"

Cool! Now let”s put this into Rails context and see how to use it for handling validations in our controller and view.

In the controller where you want to handle your contact form, you might have something like the following:

def contact
  @contact = Contact.new
  if request.post?
    @contact.name = params[:name]
    @contact.email = params[:email]
    @contact.phone = params[:phone]
    @contact.ticker = params[:ticker]
    if @contact.valid?
      @contact.deliver
      flash[:notice] = "Thank you for you interest."
      redirect_to root_url
    end
  end
end

Here is an example view with custom error handling:

<% form_tag contact_path do %>
  <table>
    <tr>
      <td>
        <label for="name">Name:</label>
      </td>
      <td>
        <%= text_field_tag "name", @contact.name %>
        <%= error_messages_for_attribute(@contact, :name) %>
      </td>
    </tr>
    <tr>
      <td>
        <label for="email">Email:</label>
      </td>
      <td>
        <%= text_field_tag "email", @contact.email %>
        <%= error_messages_for_attribute(@contact, :email) %>
      </td>
    </tr>
    <tr>
      <td>
        <label for="phone">Phone:</label>
      </td>
      <td>
        <%= text_field_tag "phone", @contact.phone %>
        <%= error_messages_for_attribute(@contact, :phone) %>
      </td>
    </tr>
    <tr>
      <td>
        <label for="message">Message:</label>
      </td>
      <td>
        <%= text_area_tag "message", @contact.message %>
        <%= error_messages_for_attribute(@contact, :message) %>
      </td>
    </tr>
    <tr>
      <td colspan="2">
        <%= submit_tag "Submit" %>
      </td>
    </tr>
  </table>
<% end %>

You will notice a custom error_messages_for_attribute helper method I created, here is the code I used for that:

def error_messages_for_attribute(object, attribute)
  if object.errors.on(attribute)
    html = '<small class="errors"><ul>'
    object.errors.on(attribute).each do |message|
      html += "<li>" + message + "</li>"
    end
    html += "</ul></small>"
  end
end

I hope you found this useful.

Share this post

Other posts you may enjoy

You can leave a response, or trackback from your own site.

Print This Post Print This Post

8 Responses to “Custom Form Validations Without ActiveRecord”

On April 4th, 2008 at 6:18 am A Fresh Cup » Blog Archive » Double Shot #180 said:

[...] Custom Forms Validation Without ActiveRecord - Using the validatable plugin. [...]

On April 7th, 2008 at 8:30 am Rails Podcast Brasil - Episódio 12 said:

[...] Custom Form Validations Without ActiveRecord [...]

On April 14th, 2008 at 5:48 am Thando Vuzane - Net Age said:

Hey, Thank you for the above. I was cracking my skull over it. You have relieved me from a lot of mind strain.

On April 16th, 2008 at 1:26 pm Jordan said:

This was perfect. Thanks.

On April 24th, 2008 at 2:11 pm Billy said:

While this looks like a promising gem, it lacks a lot of the more powerful ActiveRecord class methods, such as validate ! You cannot specify custom validations with this gem. You are constrained to the 6 or 8 validates_x methods it provides. Hopefully this will be fixed in the future.

On May 28th, 2008 at 10:27 am WN said:

Question from a RoR novice, Is it possible to have this as a plugin ?

On June 19th, 2008 at 2:41 pm Chad said:

Thanks for your contribution!

However, you should also look at a plugin called activerecord_base_without_table.

It allows you to define models that do not save into the database. I found this solution worked better for me because you end up with objects that are a little more compatible with the rest of the rails world. For example, I wanted to use the ‘human_attribute_override’ plugin for prettier error messages. This also solves Billy’s problem above where he wanted to write custom validations. And it’s a plugin rather than a gem, so it should make WN happy.

The plugin is remarkably short and sweet (only 26 lines long). However, sadly the author has not upgraded it to be compatible with rails 2. If you’re using rails 2, you need to edit one line: The call to readmethods on line 21 must be changed to generated_methods.

Good luck, and I hope this helps!

Here is the URL for the plugin:

http://agilewebdevelopment.com/plugins/activerecord_base_without_table

On July 31st, 2008 at 7:02 am Walter Lockhart said:

Re: Note: I”m assuming you also know how to set up a mailer and the respective views. If you do not, comment below and I will add those pieces to this post if you desire.

Hi Chris,

I really enjoyed your post.

Would it be possible please to show how I would integrate these validations with a mailer and the respective views. I am developing a special inquiry form that requires a number of fields to be validated before the result of the form is emailed.

Thanks in advance.

Kind Regards

Walter

Leave a Reply