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.

Permalink

 

0 comments

name (opt.)
email (req.)
comment
what color is the sky?

blog home