Permalink
Browse files

validates_uniqueness_of behaves well with abstract superclasses. Refe…

…rences #3833, closes #9886.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8000 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 6a35ed1 commit 28729a4829c68409717ecf82a6b407672ff5fa7d @jeremy jeremy committed Oct 23, 2007
View
@@ -29,7 +29,8 @@
* MySQL: fix change_column on not-null columns that don't accept dfeault values of ''. #6663 [Jonathan Viney, Tarmo Tänav]
-* validates_uniqueness_of behaves well with single-table inheritance. #3833 [Gabriel Gironda, rramdas, François Beausoleil, Josh Peek, Tarmo Tänav]
+* validates_uniqueness_of behaves well with abstract superclasses and
+single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Beausoleil, Josh Peek, Tarmo Tänav, pat]
* Raise ProtectedAttributeAssignmentError in development and test environments when mass-assigning to an attr_protected attribute. #9802 [Henrik N]
@@ -633,7 +633,21 @@ def validates_uniqueness_of(*attr_names)
condition_params << record.send(:id)
end
- if find(:first, :conditions => [condition_sql, *condition_params])
+ # The check for an existing value should be run from a class that
+ # isn't abstract. This means working down from the current class
+ # (self), to the first non-abstract class. Since classes don't know
+ # their subclasses, we have to build the hierarchy between self and
+ # the record's class.
+ class_hierarchy = [record.class]
+ while class_hierarchy.first != self
+ class_hierarchy.insert(0, class_hierarchy.first.superclass)
+ end
+
+ # Now we can work our way down the tree to the first non-abstract
+ # class (which has a database table to query from).
+ finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
+
+ if finder_class.find(:first, :conditions => [condition_sql, *condition_params])
record.errors.add(attr_name, configuration[:message])
end
end
@@ -289,4 +289,10 @@ def create_table(*args, &block)
t.column :message, :string, :null=>false
t.column :developer_id, :integer, :null=>false
end
+
+ create_table :inept_wizards, :force => true do |t|
+ t.column :name, :string, :null => false
+ t.column :city, :string, :null => false
+ t.column :type, :string
+ end
end
@@ -33,6 +33,22 @@ class Topic < ActiveRecord::Base
has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
end
+class Wizard < ActiveRecord::Base
+ self.abstract_class = true
+
+ validates_uniqueness_of :name
+end
+
+class IneptWizard < Wizard
+ validates_uniqueness_of :city
+end
+
+class Conjurer < IneptWizard
+end
+
+class Thaumaturgist < IneptWizard
+end
+
class ValidationsTest < Test::Unit::TestCase
fixtures :topics, :developers
@@ -402,6 +418,35 @@ def test_validate_case_insensitive_uniqueness
assert t2.save, "should save with nil"
end
+ def test_validate_straight_inheritance_uniqueness
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
+ assert w1.valid?, "Saving w1"
+
+ # Should use validation from base class (which is abstract)
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
+ assert !w2.valid?, "w2 shouldn't be valid"
+ assert w2.errors.on(:name), "Should have errors for name"
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
+
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
+ assert !w3.valid?, "w3 shouldn't be valid"
+ assert w3.errors.on(:name), "Should have errors for name"
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
+
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
+ assert w4.valid?, "Saving w4"
+
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
+ assert !w5.valid?, "w5 shouldn't be valid"
+ assert w5.errors.on(:name), "Should have errors for name"
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
+
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
+ assert !w6.valid?, "w6 shouldn't be valid"
+ assert w6.errors.on(:city), "Should have errors for city"
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
+ end
+
def test_validate_format
Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")

0 comments on commit 28729a4

Please sign in to comment.