Categories
computer

Rails 2.0: Validations Without Extending ActiveRecord::Base

In more “enterprisey” web stacks (such as Java with Hibernate and Spring MVC), it’s straightforward to design a validatable form whose contents do not correspond directly — if at all — to a persistent OR/M class: such as may happen in an ecommerce site where you’re collecting payment information but can only store some of it in the database for legal reasons. Just create a MVC-level view object using your framework-specific validation mechanism, and translate to/from the relevant OR/M classes as necessary in the controller.

This scenario isn’t always straightforward to handle in Rails since the stack layers are smooshed together. In Rails 1.2.x, you could use (some) validations in your non-persistent PORO objects by extending ActiveRecord::Base and overriding the initializer to skip the databases-related stuff. Alas, this hack does not seem to work in Rails 2.0, but I have a solution which, in my brief testing, seems to work better than the aforementioned Rails 1.2 hack.

Here’s how I used ActiveRecord::Validations in one of my view-only classes in a Rails 2.0.2 application without needing a dummy table in the database or ActiveRecord::Base.


require 'active_record/validations'

class Card

# Define your arbitrary PORO attributes.
attr_accessor :number
attr_accessor :expiration_month
attr_accessor :expiration_year
attr_accessor :verification_value

# For the ActiveRecord::Errors object.
attr_accessor :errors

def initialize(opts = {})

# Create an Errors object, which is required by validations and to use some view methods.
@errors = ActiveRecord::Errors.new(self)

end

# Dummy stub to make validtions happy.
def save
end

# Dummy stub to make validtions happy.
def save!
end

# Dummy stub to make validtions happy.
def new_record?

false

end

# Dummy stub to make validtions happy.
def update_attribute
end

# Mix in that validation goodness!
include ActiveRecord::Validations

# Use validations.
validates_presence_of :number
validates_presence_of :expiration_month
validates_presence_of :expiration_year
validates_presence_of :verification_value

end