Skip to navigation

DIY field validation reflection

Published March 12, 2007

Scott Harvey asked on the Rails Australia list how to do client and server-side validation in one hit. Seeing as Rails has no public documented API for validation reflection, here’s an example of how you could roll your own required fields designation DSL:


require 'rubygems'
require 'active_record'

module ActiveRecord
  # DIY required field reflection, with auto +validates_presence_of+.
  # 
  # Adds a class method +required_fields+ that returns the array of required fields.
  module RequiredFields
    def self.included(mod)
      mod.extend ClassMethods
      mod.class_inheritable_array(:required_fields)
      mod.write_inheritable_array(:required_fields, [])
    end
    module ClassMethods
      # Adds the specified +fields+ to +required_fields+ and sets up the corresponding +validates_presence_of+ validations
      def requires_fields(*fields)
        validates_presence_of(fields)
        write_inheritable_array(:required_fields, fields)
      end
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecord::RequiredFields)

if $0 == __FILE__
  require 'test/unit'
  
  class RequiredFieldsTest < Test::Unit::TestCase
    class NoRequiredFields < ActiveRecord::Base
    end
    class OneRequiredFieldOneStatement < ActiveRecord::Base
      requires_fields :body
    end
    class MultipleRequiredOneStatement < ActiveRecord::Base
      requires_fields :body, :description
    end
    class MultipleRequiredMultipleStatements < ActiveRecord::Base
      requires_fields :body, :description
      requires_fields :created_at
    end    
    def test_required_fields
      assert_equal [], NoRequiredFields.required_fields
      assert_equal [:body], OneRequiredFieldOneStatement.required_fields
      assert_equal [:body, :description], MultipleRequiredOneStatement.required_fields
      assert_equal [:body, :description, :created_at], MultipleRequiredMultipleStatements.required_fields
    end
    # I'd test the actual field validation too, but that needs a (mocked, or otherwise) DB connection
  end
end

In your view you then just need to check if the field is required and mark it as so.

Archived comments

Comments were previously allowed on articles. Though no new comments are being accepted you can see the old comments below.

  1. Dan Kubb

    What about using alias_method_chain to write method that intercept calls to the validates_* methods, and populates instance variables like you do here? That way anyone who’s written code using the standard approach can reflect on them rather than using a new syntax.

  2. Tim Lucas

    Thanks Dan. Yeah you could, and people have already had a go, but the validations API doesn’t really provide much of what you’d need for creating forms with nice readable requirements. The above code is just a very simple example for learning purposes, I doubt it’d be that useful in a real-life app.

    I’m sure there’s a bunch of other situations where it’d be very useful to have full validation reflections, just like association reflections, I just haven’t had the need myself.

Previously: tumble me too

Next up: Announcing RailsCamp07

Thoughts

toolmantim

I’m Tim Lucas, a user experience developer currently in Sydney Australia.

I occasionally write, snap photos, present on various technical topics, tweet my going-ons, share teh codes and post tidbits to the scrapbook.

Most recently I published Simplifying ticket sales on sydneyoperahouse.com (February 16, 2010)

Work with me via Agency Rainford, or shoot an email to and say hello.

Powered by what isn’t being said