Skip to content

Commit

Permalink
Ensure uniqueness validation matcher works with STI (#1610)
Browse files Browse the repository at this point in the history
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

Co-authored-by: Stef Schenkelaars <[email protected]>
  • Loading branch information
matsales28 and StefSchenkelaars authored Feb 9, 2024
1 parent cbff141 commit 5781741
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/shoulda/matchers/active_record/uniqueness/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5781741

Please sign in to comment.