2013-09-06 3 views
0

J'ai deux champs, nom et adresse électronique, dont la combinaison doit être unique sans tenir compte de la casse. Voici le code pour la classe et les spécifications. Les 3 premiers tests passent, mais pas le 4ème.Vérification des valeurs dupliquées sur plusieurs champs avec correspondance insensible à la casse

class Person < ActiveRecord::Base 
    validates :name, presence: true 
    validates :email, presence: true 

    validates_uniqueness_of :email, :scope => :name, :case_sensitive => false 
    validates_uniqueness_of :name, :scope => :email, :case_sensitive => false 
end 

describe Person do 
    context "with duplicate name and email" do 
    before do 
     @person1 = create(:person) 
    end 
    it "for case-sensitive match of both" do 
     expect(build(:person, {name: @person1.name, email: @person1.email})).to_not be_valid 
    end 
    it "for case-insensitive match of name" do 
     expect(build(:person, {name: @person1.name.swapcase, email: @person1.email})).to_not be_valid 
    end 
    it "for case-insensitive match of email" do 
     expect(build(:person, {name: @person1.name, email: @person1.email.swapcase})).to_not be_valid 
    end 
    it "for case-insensitive match of both" do 
     expect(build(:person, {name: @person1.name.swapcase, email: @person1.email.swapcase})).to_not be_valid 
    end 
    end 
end 
+0

voir cette question http://stackoverflow.com/questions/2215237/rails- validates-uniqueness-of-across-multiple-columns-with-case-insensitivity? rq = 1 –

Répondre

1

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)') 
+0

Merci de prendre le temps de résoudre et de suggérer les alternatives. Bien que je n'ai pas encore essayé, je vais écrire un validateur personnalisé pour résoudre le problème. –

+0

Encore une chose, je me demandais juste si votre "sujet {@ person1}" avait raison. –

Questions connexes