Validation Without a Database Table
Written by steve on 02-11-2007 at 11:06 PM
Contact forms are a way of life on the Web and so we as developers implement them time and again. One nice touch is to do some validation of the data entered in the form to get higher quality data and lower the spam. Rails offers so much magic in how it joins models to views through controllers—it’s a shame to see that all go away when you implement your contact form. The good news is you can have your contact form and validations too!
I’ve tried several variations on how to perform ActiveRecord validations on non-database backed models, and keep coming back to the one from technoweenie. Here’s how it works. You define a model thusly:
class Contact < ActiveRecord::Base
def self.columns() @columns ||= []; end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
column :full_name, :string
column :email, :string
column :comments, :string
column :address, :string
column :city, :string
column :state, :string
column :zip_postal, :string
column :phone, :string
validates_presence_of :full_name
validates_confirmation_of :email
validates_format_of :email,
:with => /^[_a-z0-9-]+(.[_a-z0-9-]+)*@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$/i,
:message => 'does not appear to be a valid email'
validates_presence_of :address
validates_presence_of :city
validates_length_of :state, :is => 2
validates_format_of :phone,
:with => /\d(?{3})?[- ]?\d{3}-?\d{4}/,
:message => 'should be ten digit number in the form: 999-999-9999',
:on => :create
validates_length_of :zip_postal, :in => 5..10,
:message => 'Zip/postal code must be a valid US or Canadian zip/postal code'
validates_format_of :zip_postal,
:with => /(\d{5}(-\d{4})?)|([ABCEGHJKLMNPRSTVXY]\d[A-Z] \d[A-Z]\d)/,
:message => 'Zip/postal code matches neither US nor Canadian formats'
protected
def validate
errors.add(:state, 'unrecognized state name. use two-letter abbreviation.') unless State.valid?(self[:state])
end
end
I left all the validations in there in case you want to use simple email and postal-code regex validation.
This model basically creates the columns from your specification on the fly. The key is in the first four lines of code which overrides a couple of ActiveRecord methods. Namely column and columns. You use this model exactly as you would a model that represents a database table.
Just to convince you I really use this, here’s my controller code:
class ContactController < ApplicationController
layout 'two-col'
def index
@contact = Contact.new
end
def thank_you
@contact = Contact.new(params[:contact])
if !@contact.valid?
render(:action => 'index') and return
else
email = ContactMailer.create_sent(@contact)
ContactMailer.deliver(email)
email = ContactMailer.create_confirm(@contact)
ContactMailer.deliver(email)
end
end
end
Now you can create all the forms you like the same way regardless of whether the results of form submission are saved to a database.
0 comments