-
-
Save thechrisoshow/2236521 to your computer and use it in GitHub Desktop.
class Banana < ActiveRecord::Base; end | |
banana = Banana.new | |
banana.valid? #=> true | |
banana.singleton_class.validates_presence_of :name | |
banana.valid? #=> true - why did the validation not work? | |
banana.class.validates_presence_of :name | |
banana.valid? #=> false - as we'd expect...but now... | |
new_banana = Banana.new | |
new_banana.valid? #=> false - because the previous call soiled the Banana class with it's validation | |
# So how does one apply validations to the eigenclass of an ActiveRecord object? | |
# Or am I misunderstanding what .singleton_class is? |
Woops! I thought I had posted a reply.
@mudge I should've mentioned, Rails 2.3.14, Ruby 1.8.7
Looks like you CAN do what I want in Rails 3 - sorry to send you off down a deprecated Rabbit Hole
Is there a trick to accomplishing this in Rails 3.2?
(rdb:1) @nmp.singleton_class._validators
{:child_new_medical_profiles=>[#<Mongoid::Validations::AssociatedValidator:0x0000010443f378 @attributes=[:child_new_medical_profiles], @options={}>], :conditions=>[#<Mongoid::Validations::AssociatedValidator:0x00000105965b58 @attributes=[:conditions], @options={}>]}
(rdb:1) @nmp.singleton_class._validate_callbacks.map &:filter
["_callback_before_139", "_callback_before_141", :validate_not_more_than_one_condition, :inches, :feet]
(rdb:1) @nmp.singleton_class.validates_presence_of :number
[Mongoid::Validations::PresenceValidator]
(rdb:1) @nmp.singleton_class._validators
{:child_new_medical_profiles=>[#<Mongoid::Validations::AssociatedValidator:0x0000010443f378 @attributes=[:child_new_medical_profiles], @options={}>], :conditions=>[#<Mongoid::Validations::AssociatedValidator:0x00000105965b58 @attributes=[:conditions], @options={}>], :number=>[#<Mongoid::Validations::PresenceValidator:0x00000102bdbd68 @attributes=[:number], @options={}>]}
(rdb:1) @nmp.singleton_class._validate_callbacks.map &:filter
["_callback_before_139", "_callback_before_141", :validate_not_more_than_one_condition, :inches, :feet, "_callback_before_359"]
(rdb:1)
After calling validates_presence_of
of the singleton_class I can see that it adds the validator and creates a new callback, "_callback_before_359". If I inspect that callback it is a huge long callback chain that does seem to include a callback with @raw_filter equal to my new PresenceValidator
.
However, the validator is not called:
(rdb:1) @nmp.number = nil
nil
(rdb:1) @nmp.valid?
true
Any help would be greatly appreciated...
I would like to do the exact same thing. Any news on this?
You may be better off just using #alias_method_chain
# Extending a User instance with this decorator will add a validation that the :old_password attribute is valid
class User
module PasswordProtection
def self.extended(user)
class << user
attr_writer :old_password
alias_method_chain :valid?, :password_protection
end
end
def valid_with_password_protection?
valid_without_password_protection?
validate_old_password
errors.empty?
end
private
def validate_old_password
unless self.valid_password?(@old_password)
errors.add :old_password, "is invalid"
end
end
end
end
Another solution would be using a Form Object and defining the validations depending on the context where they're used.
class class User
before_validation :add_instance_validations
private
def add_instance_validations
singleton_class.class_eval { validates :name, presence: true }
end
end
I actually just blogged bout this, if anyone is still in need of a solution
http://dvg.github.io/2015/03/05/apply-activemodel-validations-by-policy.html
wrote an article specially answering this topic: http://www.eq8.eu/blogs/22-different-ways-how-to-do-rails-validations :)
A method that is directly built into Rails: https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations/with.rb#L115-L123
class Person
include ActiveModel::Validations
validate :instance_validations, on: :create
def instance_validations
validates_with MyValidator, MyOtherValidator
end
end
A method that is directly built into Rails: https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations/with.rb#L115-L123
class Person include ActiveModel::Validations validate :instance_validations, on: :create def instance_validations validates_with MyValidator, MyOtherValidator end end
This solution is not corresponding with the initial subdmision. What the author need is to be able to "inject" as ad-hoc a validator without affecting to the Base class.
Ah, here's the key difference (as @h-lame's
teardown
hinted at):Both have the validator, but only the first has the callback to use it.