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
8 replies on “Rails 2.0: Validations Without Extending ActiveRecord::Base”
What about just marking the class as abstract?
class Car < ActiveRecord::Base
self.abstract = true
end
@Hardbap, I guess it’s because you can’t instantiate abstract classes.
And what about “normal” rails methods like .to_xml and such?
I had a lot of problems with this on Rails 2.2.2, in the end after trying to make this hack work-it was a lot easier to go with the validator gem (http://validatable.rubyforge.org/). It works almost identically to the built in model validation. After validation, errors are loaded into the errors attribute and can be handled in the view. No auto helpers built in, but this blog got me started : http://rorblog.techcfl.com/2008/04/02/custom-form-validations-without-activerecord/
Hopefully this saves someone else some time.
Ben
Thanks for the note, Ben. This was written with 1.2 in mind, and I don’t think I’ve tested this approach post ~2.0. I’ll definitely have to check out that gem in the future!
The validatable gem worked like butta for me.
[…] I want to create a Rails (2.1 and 2.2) model with ActiveRecord validations, but without a database table. What is the most widely used approach? I’ve found some plugins that claim to offer this functionality, but many of them don’t appear to be widely used or maintained. What does the community recommend I do? Right now I am leaning toward coming up with my own solution based on this blog post. […]
[…] I want to create a Rails (2.1 and 2.2) model with ActiveRecord validations, but without a database table. What is the most widely used approach? I’ve found some plugins that claim to offer this functionality, but many of them don’t appear to be widely used or maintained. What does the community recommend I do? Right now I am leaning toward coming up with my own solution based on this blog post. […]