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.
-
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.
-
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.