I would like to enhance existing class using instance_eval. There original definition contains validation, which require presence of certain fields, ie:
class Du
One solution is to extend validates :
#no need of instance_eval just open the class
class Dummy < ActiveRecord::Base
#validates :field, :presence => true
def self.validates(*attributes)
if attributes.first == :field #=> add condition on option if necessary
return # don't validate
else
super(*attributes) #let normal behavior take over
end
end
end
And no that's not monkey-patching but extending or decorating a behavior. Rails 3.1 is built on the idea of "multi- inheritance" with module inclusion, specifically to allow this kind agility.
One caveat is you must load the class with the redefined validates method before the gem containing the call to validates. To do so, require the file in config/application.rb after require "rails/all" as suggested in the railsguides. Something like that :
require File.expand_path('../boot', __FILE__)
require 'rails/all' # this where rails (including active_record) is loaded
require File.expand_path('../dummy' __FILE__) #or wherever you want it
#this is where the gems are loaded...
# the most important is that active_record is loaded before dummy but...
# not after the gem containing the call to validate :field
if defined?(Bundler)
Bundler.require *Rails.groups(:assets => %w(development test))
end
Hope it works now!
If you doesn't want to make any changes in Parent class then first clear all validations in child class and copy all required validation from parent class to child class
class Dummy < ActiveRecord::Base
validates :property, presence: true
validates :value, length: { maximum: 255 }
end
And override it in child class
Dummy.class_eval do
clear_validators!
validates :property, presence: true
end
Easest way to remove all validations:
clear_validators!
I think this is the most actual solution at this moment (I'm using rails 4.1.6):
# Common ninja
class Ninja < ActiveRecord::Base
validates :name, :martial_art, presence: true
end
# Wow! He has no martial skills
Ninja.class_eval do
_validators[:martial_art]
.find { |v| v.is_a? ActiveRecord::Validations::PresenceValidator }
.attributes
.delete(:martial_art)
end
This does not directly answer the question but here's an option you should consider in such a situation: instead of disabling validation, you could set the required fields in a before_validation
hook.
Since you don't need those required fields, set them with some dummy data that satisfies the validation and forget about them.
No ugly monkey patching.
I'd have to look more into the code and help, but I'm thining it might be possible to inspect the list of validators of the class, and then modify the entry for the validation you want to change to add in an :if => :some_function
conditional to it.
You'll need to do it only once for production (so it can be put inside an initializer, but for development you'll need to put it in the model, or somewhere else that will get loaded each time the corresponding model is (perhaps an observer?).
(I'll edit the answer with more information as I come to research it.)