Join table check for habtm w/ custom foreign keys should still work

If the habtm association in question specifies a custom foreign_key or
association_foreign_key, when the habtm matcher checks that the join
table has the correct columns, it should use the specified column names
instead of the defaults.
This commit is contained in:
Elliot Winkler 2014-07-20 13:45:44 -06:00
parent 0695c8647b
commit 48f45e814a
5 changed files with 72 additions and 8 deletions

View File

@ -957,7 +957,8 @@ module Shoulda
end
def join_table_matcher
@join_table_matcher ||= AssociationMatchers::JoinTableMatcher.new(self)
@join_table_matcher ||=
AssociationMatchers::JoinTableMatcher.new(self, reflector)
end
def class_exists?

View File

@ -4,7 +4,8 @@ module Shoulda
module AssociationMatchers
# @private
class JoinTableMatcher
attr_reader :association_matcher, :failure_message
attr_reader :failure_message
alias :missing_option :failure_message
delegate :model_class, :join_table, :associated_class,
@ -12,8 +13,9 @@ module Shoulda
delegate :connection, to: :model_class
def initialize(association_matcher)
def initialize(association_matcher, reflector)
@association_matcher = association_matcher
@reflector = reflector
end
def matches?(subject)
@ -39,8 +41,14 @@ module Shoulda
end
end
protected
attr_reader :association_matcher, :reflector
private
delegate :foreign_key, :association_foreign_key, to: :reflector
def missing_columns
@missing_columns ||= expected_join_table_columns.select do |key|
!actual_join_table_columns.include?(key)
@ -48,10 +56,7 @@ module Shoulda
end
def expected_join_table_columns
[
"#{model_class.name.underscore}_id",
"#{associated_class.name.underscore}_id"
]
[foreign_key, association_foreign_key]
end
def actual_join_table_columns

View File

@ -45,6 +45,25 @@ module Shoulda
end
end
def foreign_key
if has_and_belongs_to_many_reflection
has_and_belongs_to_many_reflection.foreign_key
elsif reflection.respond_to?(:primary_key_name)
reflection.primary_key_name
else
reflection.foreign_key
end
end
def association_foreign_key
if has_and_belongs_to_many_reflection
join_model = has_and_belongs_to_many_reflection.options[:class]
join_model.right_reflection.foreign_key
else
reflection.association_foreign_key
end
end
protected
attr_reader :reflection, :subject

View File

@ -5,7 +5,8 @@ module Shoulda
# @private
class ModelReflector
delegate :associated_class, :through?, :join_table,
:association_relation, :polymorphic?, to: :reflection
:association_relation, :polymorphic?, :foreign_key,
:association_foreign_key, to: :reflection
def initialize(subject, name)
@subject = subject

View File

@ -732,6 +732,44 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
end.to fail_with_message_including('missing columns: person_id, relative_id')
end
context 'using a custom foreign key' do
it 'rejects an association with a join table with incorrect columns' do
define_model :relative
define_model :person do
has_and_belongs_to_many :relatives,
foreign_key: :custom_foreign_key_id
end
define_model :people_relative,
id: false,
custom_foreign_key_id: :integer,
some_crazy_id: :integer
expect do
expect(Person.new).to have_and_belong_to_many(:relatives)
end.to fail_with_message_including('missing columns: custom_foreign_key_id, relative_id')
end
end
context 'using a custom association foreign key' do
it 'rejects an association with a join table with incorrect columns' do
define_model :relative
define_model :person do
has_and_belongs_to_many :relatives,
association_foreign_key: :custom_association_foreign_key_id
end
define_model :people_relative,
id: false,
custom_association_foreign_key_id: :integer,
some_crazy_id: :integer
expect do
expect(Person.new).to have_and_belong_to_many(:relatives)
end.to fail_with_message_including('missing columns: person_id, custom_association_foreign_key_id')
end
end
it 'rejects an association of the wrong type' do
define_model :relative, person_id: :integer
define_model :person do