diff --git a/lib/shoulda/matchers/active_record/uniqueness/model.rb b/lib/shoulda/matchers/active_record/uniqueness/model.rb index 441c10f4c..983dc66e6 100644 --- a/lib/shoulda/matchers/active_record/uniqueness/model.rb +++ b/lib/shoulda/matchers/active_record/uniqueness/model.rb @@ -29,7 +29,19 @@ def next end def symlink_to(parent) - namespace.set(name, Class.new(parent)) + table_name = parent.table_name + + new_class = Class.new(parent) do + define_singleton_method :table_name do + table_name + end + + define_singleton_method :base_class do + self + end + end + + namespace.set(name, new_class) end def to_s 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 9cc10dfbe..767111f1a 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 @@ -1274,6 +1274,36 @@ def configure_validation_matcher(matcher) scoped_to(:favoriteable_type) end + context 'when polymorphic model is used on a STI model' do + it 'still works' do + columns = { + attachable_id: { type: :integer, options: { null: false } }, + attachable_type: { type: :string, options: { null: false } }, + title: { type: :string, options: { null: false } }, + } + + asset_model = define_model 'Asset', columns do + belongs_to :attachable, polymorphic: true + + validates_uniqueness_of :title, scope: [:attachable_id, :attachable_type] + + def attachable_type=(class_name) + super(class_name.constantize.base_class.to_s) + end + end + + post_model = define_model 'Post', {} do + has_many :assets, as: :attachable, dependent: :destroy + end + + guest_post_model = define_model 'GuestPost', {}, parent_class: post_model, table_name: 'posts' + + asset = asset_model.create!(attachable: guest_post_model.create!, title: 'foo') + + expect(asset).to validate_uniqueness_of(:title).scoped_to(:attachable_id, :attachable_type) + end + end + context 'if the model the *_type column refers to is namespaced, and shares the last part of its name with an existing model' do it 'still works' do define_class 'User'