J'ai essayé de comprendre le problème que vous rencontrez en utilisant les éléments suivants:
class Person < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
validates_uniqueness_of :name, :case_sensitive => false, :scope => [ :email ]
validates_uniqueness_of :email, :case_sensitive => false, :scope => [ :name ]
end
et
require 'spec_helper'
describe Person do
context 'with duplicate name and email' do
before do
@person1 = Person.new(name: '[email protected]', email: '[email protected]')
end
subject { @person1 }
it { should be_valid }
describe '(I) for case-sensitive match of both' do
before do
person = @person1.dup
person.save
end
it { should_not be_valid }
end
describe '(II) for case-insensitive match of name' do
before do
person = @person1.dup
person.name.swapcase!
person.save
end
it { should_not be_valid }
end
describe '(III) for case-insensitive match of email' do
before do
person = @person1.dup
person.email.swapcase!
person.save
end
it { should_not be_valid }
end
describe '(IV) for case-insensitive match of both' do
before do
person = @person1.dup
person.name.swapcase!
person.email.swapcase!
person.save
end
it { should_not be_valid }
end
end
end
Le comportement de case_sensitive est étrange en quelque sorte comme on le voit dans le journal de mon exemple case:
(0.4ms) SAVEPOINT active_record_1
Person Exists (1.2ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."name") = LOWER('[email protected]') AND "people"."email" = '[email protected]') LIMIT 1
Person Exists (0.5ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."email") = LOWER('[email protected]') AND "people"."name" = '[email protected]') LIMIT 1
SQL (3.7ms) INSERT INTO "people" ("created_at", "email", "name", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Sat, 07 Sep 2013 14:21:05 UTC +00:00], ["email", "[email protected]"], ["name", "[email protected]"], ["updated_at", Sat, 07 Sep 2013 14:21:05 UTC +00:00]]
(0.2ms) RELEASE SAVEPOINT active_record_1
Person Exists (0.3ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."name") = LOWER('[email protected]') AND "people"."email" = '[email protected]') LIMIT 1
Person Exists (0.3ms) SELECT 1 AS one FROM "people" WHERE (LOWER("people"."email") = LOWER('[email protected]') AND "people"."name" = '[email protected]') LIMIT 1
(0.2ms) ROLLBACK
Le problème semble être que les instructions "LOWER" ne sont utilisées que sur soit email ou nom mais pas sur les deux en même temps. Fondamentalement, je m'attendais à ce que votre code fonctionne très bien. Cependant, comme vu dans le db-log et également souligné dans une autre question (Rails 3. Validating email uniqueness and case sensitive fails), il pourrait ne pas être une bonne idée d'assurer un comportement insensible à la casse dans les contraintes lorsque la performance est un problème ;-) Plutôt utiliser un avant le filtre pour enregistrer l'email/nom en minuscules. Comme cela pourrait aussi ne pas être la meilleure idée (car vous voudrez peut-être ne pas perdre les informations de cas sur le nom), vous pouvez utiliser une autre minuscule-nom-col pour assurer la contrainte ou utiliser un filtre after_valition.
En utilisant le modèle suivant devrait verdir votre suite de tests:
class Person < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
before_validation :downcase_name_email
validates_uniqueness_of :name, :case_sensitive => false, :scope => [ :email ]
validates_uniqueness_of :email, :case_sensitive => false, :scope => [ :name ]
private
def downcase_name_email
self.email = self.email.downcase if self.email.present?
self.name = self.name.downcase if self.name.present?
end
end
Best, Ben.
PS: Je vous allez prendre l'approche minuscule, assurez-vous de migrer vos données db-:
Person.update_all('email = LOWER(email)')
Person.update_all('name = LOWER(name)')
voir cette question http://stackoverflow.com/questions/2215237/rails- validates-uniqueness-of-across-multiple-columns-with-case-insensitivity? rq = 1 –