Vishal
Vishal

Reputation: 7361

Conditional case_sensitive validation in model

I have one model validation like below

validates :value, presence: true, allow_blank: false, uniqueness: { scope: [:account_id, :provider] }

I want to add one more condition of case_sensitive inside uniqueness like below

validates :value, presence: true, allow_blank: false, uniqueness: { scope: [:account_id, :provider], case_sensitive: :is_email? }

def is_email?
  provider != email
end

In short, it should not validate case_sensitive when email provider is not email, But currently, it's not working it is expecting true or false only not any method or any conditions.

How can I achieve this in rails? I already wrote custom validation because it was not working.

UPDATE

if I add another validation like below

validates_uniqueness_of :value, case_sensitive: false, if: -> { provider == 'email' }

It's giving me same error for twice :value=>["has already been taken", "has already been taken"]

Upvotes: 1

Views: 224

Answers (1)

Sebastián Palma
Sebastián Palma

Reputation: 33420

In the specific case of case_sensitive, the value passed to the option will always be compared against its truthy value.

As you can see in the class UniquenessValidator, when the relation is built, it uses the options passed to check if the value of case_sensitive is truthy (not false nor nil), if so, it takes the elsif branch of the condition:

def build_relation(klass, attribute, value)
  ...
    if !options.key?(:case_sensitive) || bind.nil?
      klass.connection.default_uniqueness_comparison(attr, bind, klass)
    elsif options[:case_sensitive] # <--------------------------------- sadly, this returns true for :is_email?
      klass.connection.case_sensitive_comparison(attr, bind)
    else
      # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
      klass.connection.case_insensitive_comparison(attr, bind)
    end
  ...
end

As you're passing the method name is_email? to case_sensitive, which is in fact a symbol, the condition takes that branch.

tl;dr;. You must always use true or false with case_sensitive.

Upvotes: 2

Related Questions