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