I am trying to development an application that can present the same resource to different users and where the resource may have different validation behavior based on the user.
I have tried to use Ruby metaprogramming to solve this in an easy way but it looks like I am missing some key knowledge of the matter.
I can be examplified by a model such as
class Profile < ActiveRecord::Base
# validates_presence_of :string1
end
The model has a property 'string1' that sometimes are required and sometimes not. I would like to create sub-classes for each user (for reasons not apparent in this simplification) and have created a module that I would like to include:
module ExtendProfile
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def configure_validation(required)
if required
class_eval("ActiveRecord::Base.validates_presence_of :string1")
end
end
end
end
Its only purpose is to add a method that add the conditional validation based on the arguments given.
It does add the validation when called with an argument that is true but it does not do it cleanly. It appears that it does not separate the subclasses as I thought it would.
It can be illustrated by the following tests:
profile = Profile.new
profile.save; profile.errors
=> []
A profile can by default be saved without errors.
Object::const_set('FirstExtendedProfile'.intern, Class::new(Profile) { include ExtendProfile })
FirstExtendedProfile.configure_validation(true)
fep = FirstExtendedProfile.new; fep.save; fep.errors
=> {:string1=>["skal udfyldes", "skal udfyldes"]}
Creating a new subclass and calling configuring_validation
adds validation but for some reason it is called twice during validation ("skal udfyldes" - is Danish and means that it is required).
Object::const_set('SecondExtendedProfile'.intern, Class::new(Profile) { include ExtendProfile })
sep = SecondExtendedProfile.new; sep.save; sep.errors
=> {:string1=>["skal udfyldes"]}
Another descendant is created and even though configure_validation
isn't called it still validates the string1
property (but now only once).
Adding yet another descendant and calling configure_validation
adds the validation once more...
Why am I not able to add the validation to the specific Profile descendant?
I am using Ruby 1.9.2 and Rails 3.06. Please understand that I would like to understand how to make this dynamic class creation to work - I am aware of "standard" custom validation.