views:

264

answers:

3

I'm using Clearance for authentication in my Rails application. The Clearance::User mixin adds a couple of validations to my User model, but there's one of these that I would like to remove or override. What is the best way of doing this?

The validation in question is

validates_uniqueness_of :email, :case_sensitive => false

which in itself isn't bad, but I would need to add :scope => :account_id. The problem is that if I add this to my User model

validates_uniqueness_of :email, :scope => :account_id

I get both validations, and the one Clearance adds is more restrictive than mine, so mine has no effect. I need to make sure that only mine runs. How do I do this?

A: 

I ended up "solving" the problem with the following hack:

  1. look for an error on the :email attribute of type :taken
  2. check if the email is unique for this account (which is the validation I wanted to do)
  3. remove the error if the email is unique for this account.

Sounds reasonable until you read the code and discover how I remove an error. ActiveRecord::Errors has no methods to remove errors once added, so I have to grab hold of it's internals and do it myself. Super duper mega ugly.

This is the code:

def validate
  super
  remove_spurious_email_taken_error!(errors)
end

def remove_spurious_email_taken_error!(errors)
  errors.each_error do |attribute, error|
    if error.attribute == :email && error.type == :taken && email_unique_for_account?
      errors_hash = errors.instance_variable_get(:@errors)
      if Array == errors_hash[attribute] && errors_hash[attribute].size > 1
        errors_hash[attribute].delete_at(errors_hash[attribute].index(error))
      else
        errors_hash.delete(attribute)
      end
    end
  end
end

def email_unique_for_account?
  match = account.users.find_by_email(email)
  match.nil? or match == self
end

If anyone knows of a better way, I would be very grateful.

Theo
+1  A: 

pls see the explanation and answer here: http://neverlet.be/2009/2/18/active-record-validations-are-callbacks

Vitaly Fomichov
A: 

I recently had this problem and after google didn't give me the answers quick enough I found a neater yet still un-ideal solution to this problem. Now this won't necessarily work in your case as it seems your using pre-existing super classes but for me it was my own code so I just used an :if param with a type check in the super class.

def SuperClass
  validates_such_and_such_of :attr, :options => :whatever, :if => Proc.new{|obj| !(obj.is_a? SubClass)}
end

def SubClass < SuperClass
  validates_such_and_such_of :attr
end

In the case of multpile sub classes

def SuperClass
  validates_such_and_such_of :attr, :options => :whatever, :if => Proc.new{|obj| [SubClass1, SubClass2].select{|sub| obj.is_a? sub}.empty?}
end

def SubClass1 < SuperClass
  validates_such_and_such_of :attr
end

def SubClass2 < SuperClass
end
Dan
Unfortunately for me I can't change the superclass, it's in a gem.
Theo