Ruby on Rails
HowToUseValidationsWithoutExtendingActiveRecord

So you want to use ActiveRecords Validations but don’t want to inherit from ActiveRecord::Base?

There are a few different approaches

  1. Validations for non-ActiveRecord Model Objects by Peter Donald.
  2. Validate your forms with a table-less model by Rick Olson/techno-weenie.
  3. the approach listed on this page.

Step 1 – create a ValidatingBase class in your models directory.


class ValidatingBase
  def save; end
  def save!; end 
  def update_attribute; end
  def new_record?; end
  def self.human_attribute_name(arg)
    arg.to_s
  end
  include ActiveRecord::Validations
  def [](key)
    instance_variable_get(key)
  end
  def initialize(attributes = nil)
    return nil if attributes.nil?

    attributes.each do |key, value|	
      method("#{key}=").call(value)
    end
  end
end

Step 2 – Live patch

Dispatcher.reset_application

Copy Dispatcher.reset_application from dispatcher.rb in the rails library in to public/dispatch.rb, and add ValidatingBase to the list of arguments passed to Dependencies.remove_subclasses_for. This will allow reloading pages in development mode to pick up changes to ValidatingBase subclasses. You should end up with something like this:

Note: with the latest version of Edge Rails, just add ‘include Reloadable’ to ValidatingBase and skip this step.

Note about previous note: “the latest version of Edge Rails” is a moving target; it is possibly quite different from what it was when this was first written up. At the very least such statements should be labelled with the current release number, so we can tell how stale this information has become.

Further note: The ‘latest version’ must be before 1.2. In the release candidate for 1.2, Reloadable is deprecated. It looks like you do not have to do anything to get the class reloadable as of 1.2.


require "dispatcher"

# Add this:
class Dispatcher
  class << self
    private
      def reset_application
        if Dependencies.load?
          Controllers.clear!
          Dependencies.clear
          Dependencies.remove_subclasses_for(
            ActiveRecord::Base, 
            ActiveRecord::Observer, 
            ActionController::Base, 
            ValidatingBase)
        end

        Breakpoint.deactivate_drb if defined?(BREAKPOINT_SERVER_PORT)
      end
  end
end

ADDITIONAL_LOAD_PATHS.reverse.each \
  { |dir| $:.unshift(dir) if File.directory?(dir) } \
  if defined?(Apache::RubyRun)
Dispatcher.dispatch

Step 3 – create a non-ActiveRecord::Base class (in your models directory) that extends ValidatingBase


class Something < ValidatingBase
  attr_accessor :foo
  validates_length_of :foo, :within => 5..8
end

Step 4 – in a controller somewhere…


s = Something.new
s.foo = "wibble"
s.valid?

Step 5 – have a cup of tea while you enjoy the validity of “wibble”

Note: Do not set model :something in your controller, as it will result in compounding error messages and they will not be reset. Another problem is that you will receive an error for a dynamic method called _XXXX_before_type_cast_ when trying to use _validates_numaricality_of_ which I have not investigated yet.

Note: Adding a method_missing to your validating base will eliminate the dynamic method error:


def method_missing( method_id, *args )
        method_name = method_id.to_s
        if md = /_before_type_cast$/.match(method_name)
          attribute_name, method_type = md.pre_match, md.to_s
          if self.respond_to?(attribute_name)
                self.instance_variable_get("@#{attribute_name}")
          else
            super
          end
        else
          super
        end
end

category: Howto