Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove validation using instance_eval clause in Rails?

I would like to enhance existing class using instance_eval. There original definition contains validation, which require presence of certain fields, ie:

class Dummy < ActiveRecord::Base
  validates :field, :presence => true 
end

Now I want to change that to optional using instance_eval (or any other method, really):

Dummy.instance_eval do
  ...
end

What would be the proper syntax to remove the validation, so the field is optional. I would rather do this directly on the model layer, instead doing weird hacks in controllers or views. The use of instance_eval is not really required, but as far as I know, this is generally the best way to enhance classes in Rails.

Edit #1

In general - the original class is part of the gem and I don't want to fork it, nor tie to specific release. The general cause is not really important. Simply editing the original model has far worse consequences than monkey patching.

like image 880
mdrozdziel Avatar asked Sep 25 '11 13:09

mdrozdziel


4 Answers

I found a solution, not sure how solid it is, but it works well in my case. @aVenger was actually close with his answer. It's just that the _validators accessor contains only information used for reflection, but not the actual validator callbacks! They are contained in the _validate_callbacks accessor, not to be confused with _validations_callbacks.

Dummy.class_eval do   _validators.reject!{ |key, _| key == :field }    _validate_callbacks.reject! do |callback|     callback.raw_filter.attributes == [:field]   end end 

This will remove all validators for :field. If you want to be more precise, you can reject the specific validator for _validators which is the same as the raw_filter accessor of validate callbacks.

like image 181
Nicolas Buduroi Avatar answered Oct 02 '22 14:10

Nicolas Buduroi


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 
like image 21
18augst Avatar answered Oct 02 '22 15:10

18augst


Easest way to remove all validations:

clear_validators!
like image 39
tosh Avatar answered Oct 02 '22 14:10

tosh


As I was trying to do this to remove the phone validation from the spree Address model, below is the code I got to work. I added the type check for callback.raw_filter because I only wanted to remove the presence validator on the phone field. I also had to add it because it would fail when trying to run against one of the other validators specified in the Spree::Address model that did not have an 'attributes' key for callback.raw_filter, thus an exception was thrown.

Spree::Address.class_eval do

   # Remove the requirement on :phone being present.
  _validators.reject!{ |key, _| key == :phone }

  _validate_callbacks.each do |callback|
    callback.raw_filter.attributes.delete :phone if callback.raw_filter.is_a?(ActiveModel::Validations::PresenceValidator)
  end

end
like image 22
Blake Avatar answered Oct 02 '22 15:10

Blake