From 43222e1c92c652838f9bbe318b1b2bedc31517bf Mon Sep 17 00:00:00 2001 From: Stef Schenkelaars Date: Fri, 2 Jul 2021 19:26:38 +0200 Subject: [PATCH] Ensure uniqueness validation matcher works with STI The uniqueness validation matcher generates a fake class when the scope name ends on _type. This fake class is a duplicate of the original class. In most cases this is not a problem, however when using single table inheritance (STI), this new fake class is not a subclass of the original class and therefore ActiveRecord will throw an error. See #1245 These problems are fixed by making the fake class inherrit from the original class. Therefore it closes #1245 and closes #854 --- .../active_record/uniqueness/model.rb | 2 +- .../active_record.rb | 2 +- .../validate_uniqueness_of_matcher_spec.rb | 28 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/shoulda/matchers/active_record/uniqueness/model.rb b/lib/shoulda/matchers/active_record/uniqueness/model.rb index a5ce8487f..441c10f4c 100644 --- a/lib/shoulda/matchers/active_record/uniqueness/model.rb +++ b/lib/shoulda/matchers/active_record/uniqueness/model.rb @@ -29,7 +29,7 @@ def next end def symlink_to(parent) - namespace.set(name, parent.dup) + namespace.set(name, Class.new(parent)) end def to_s diff --git a/spec/support/unit/model_creation_strategies/active_record.rb b/spec/support/unit/model_creation_strategies/active_record.rb index eb290d8b6..e92612137 100644 --- a/spec/support/unit/model_creation_strategies/active_record.rb +++ b/spec/support/unit/model_creation_strategies/active_record.rb @@ -74,7 +74,7 @@ def class_name end def table_name - class_name.tableize.gsub('/', '_') + options.fetch(:table_name, class_name.tableize.gsub('/', '_')) end def parent_class diff --git a/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb index 68886a128..9cc10dfbe 100644 --- a/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb @@ -1302,6 +1302,34 @@ def configure_validation_matcher(matcher) scoped_to(:favoriteable_type) end end + + context 'if the model *_type column refers to an STI class' do + it 'still works' do + animal_columns = { + type: { type: :string, options: { null: false } }, + } + animal_model = define_model 'Animal', animal_columns do + has_many :classifications, as: :classifiable + end + bird_model = define_model 'Bird', animal_columns, parent_class: animal_model, table_name: 'animals' + + classification_columns = { + classifiable_id: { type: :integer }, + classifiable_type: { type: :string }, + } + classification_model = define_model 'Classification', classification_columns do + belongs_to :classifiable, polymorphic: true + validates :classifiable_id, uniqueness: { scope: :classifiable_type } + end + + bird = bird_model.create! + classification = classification_model.new(classifiable: bird) + + expect(classification). + to validate_uniqueness_of(:classifiable_id). + scoped_to(:classifiable_type) + end + end end context 'when the model does not have the attribute being tested' do