mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Add support for hbtm :join_table option
Rails supports declaring a `has_and_belongs_to_many` relationship with a custom `:join_table` option and some developers want to assert that the correct value is being used. Add `AssocationMatcher#join_table` to allow developers to test the following: 1) That the :join_table option is being used for the relationship 2) That the *correct value* is being used 3) That the custom join table exists in the database
This commit is contained in:
parent
f80b85fa86
commit
af85ac1f0e
4 changed files with 114 additions and 39 deletions
3
NEWS.md
3
NEWS.md
|
@ -72,6 +72,8 @@
|
|||
|
||||
* Add `on` qualifier to `validate_numericality_of`. (h/t [#356], [#358])
|
||||
|
||||
* Add `join_table` qualifier to `have_and_belong_to_many`. ([#556])
|
||||
|
||||
[#402]: https://github.com/thoughtbot/shoulda-matchers/pull/402
|
||||
[#587]: https://github.com/thoughtbot/shoulda-matchers/pull/587
|
||||
[#662]: https://github.com/thoughtbot/shoulda-matchers/pull/662
|
||||
|
@ -86,6 +88,7 @@
|
|||
[#693]: https://github.com/thoughtbot/shoulda-matchers/pull/693
|
||||
[#356]: https://github.com/thoughtbot/shoulda-matchers/pull/356
|
||||
[#358]: https://github.com/thoughtbot/shoulda-matchers/pull/358
|
||||
[#556]: https://github.com/thoughtbot/shoulda-matchers/pull/556
|
||||
|
||||
# 2.8.0
|
||||
|
||||
|
|
|
@ -823,7 +823,9 @@ module Shoulda
|
|||
# @private
|
||||
class AssociationMatcher
|
||||
delegate :reflection, :model_class, :associated_class, :through?,
|
||||
:join_table_name, :polymorphic?, to: :reflector
|
||||
:polymorphic?, to: :reflector
|
||||
|
||||
attr_reader :name, :options
|
||||
|
||||
def initialize(macro, name)
|
||||
@macro = macro
|
||||
|
@ -906,6 +908,7 @@ module Shoulda
|
|||
end
|
||||
|
||||
def join_table(join_table_name)
|
||||
@options[:join_table_name] = join_table_name
|
||||
self
|
||||
end
|
||||
|
||||
|
@ -941,18 +944,22 @@ module Shoulda
|
|||
submatchers_match?
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :submatchers, :missing, :subject, :macro, :name, :options
|
||||
|
||||
def reflector
|
||||
@reflector ||= AssociationMatchers::ModelReflector.new(subject, name)
|
||||
def join_table_name
|
||||
options[:join_table_name] || reflector.join_table_name
|
||||
end
|
||||
|
||||
def option_verifier
|
||||
@option_verifier ||= AssociationMatchers::OptionVerifier.new(reflector)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :submatchers, :missing, :subject, :macro
|
||||
|
||||
def reflector
|
||||
@reflector ||= AssociationMatchers::ModelReflector.new(subject, name)
|
||||
end
|
||||
|
||||
def add_submatcher(matcher)
|
||||
@submatchers << matcher
|
||||
end
|
||||
|
|
|
@ -8,8 +8,8 @@ module Shoulda
|
|||
|
||||
alias :missing_option :failure_message
|
||||
|
||||
delegate :model_class, :join_table_name, :associated_class,
|
||||
to: :association_matcher
|
||||
delegate :model_class, :join_table_name, :associated_class, :options,
|
||||
:name, :option_verifier, to: :association_matcher
|
||||
|
||||
delegate :connection, to: :model_class
|
||||
|
||||
|
@ -19,10 +19,24 @@ module Shoulda
|
|||
end
|
||||
|
||||
def matches?(subject)
|
||||
join_table_exists? &&
|
||||
join_table_option_correct? &&
|
||||
join_table_exists? &&
|
||||
join_table_has_correct_columns?
|
||||
end
|
||||
|
||||
def join_table_option_correct?
|
||||
if options.key?(:join_table_name)
|
||||
if option_verifier.correct_for_string?(:join_table, options[:join_table_name])
|
||||
true
|
||||
else
|
||||
@failure_message = "#{name} should use '#{options[:join_table_name]}' for :join_table option"
|
||||
false
|
||||
end
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def join_table_exists?
|
||||
if connection.tables.include?(join_table_name)
|
||||
true
|
||||
|
|
|
@ -886,46 +886,97 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher, type: :model do
|
|||
end.to fail_with_message_including('missing columns: person_id, relative_id')
|
||||
end
|
||||
|
||||
it "rejects an association with a bad :join_table option" do
|
||||
define_model :relative
|
||||
join_table_name = 'people_and_their_families'
|
||||
context 'when the association is declared with a :join_table option' do
|
||||
it 'accepts when testing with the same :join_table option' do
|
||||
join_table_name = 'people_and_their_families'
|
||||
|
||||
define_model :person do
|
||||
has_and_belongs_to_many(
|
||||
:relatives, join_table: join_table_name
|
||||
)
|
||||
define_model :relative
|
||||
|
||||
define_model :person do
|
||||
has_and_belongs_to_many(:relatives, join_table: join_table_name)
|
||||
end
|
||||
|
||||
create_table(join_table_name, id: false) do |t|
|
||||
t.references :person
|
||||
t.references :relative
|
||||
end
|
||||
|
||||
expect(Person.new).
|
||||
to have_and_belong_to_many(:relatives).
|
||||
join_table(join_table_name)
|
||||
end
|
||||
|
||||
create_table("people_relatives", id: false) do |t|
|
||||
t.references :person
|
||||
t.references :relative
|
||||
it 'accepts even when not explicitly testing with a :join_table option' do
|
||||
join_table_name = 'people_and_their_families'
|
||||
|
||||
define_model :relative
|
||||
|
||||
define_model :person do
|
||||
has_and_belongs_to_many(:relatives,
|
||||
join_table: join_table_name
|
||||
)
|
||||
end
|
||||
|
||||
create_table(join_table_name, id: false) do |t|
|
||||
t.references :person
|
||||
t.references :relative
|
||||
end
|
||||
|
||||
expect(Person.new).to have_and_belong_to_many(:relatives)
|
||||
end
|
||||
|
||||
expect do
|
||||
expect(Person.new).to(
|
||||
have_and_belong_to_many(:relatives).join_table(join_table_name)
|
||||
it 'rejects when testing with a different :join_table option' do
|
||||
join_table_name = 'people_and_their_families'
|
||||
|
||||
define_model :relative
|
||||
|
||||
define_model :person do
|
||||
has_and_belongs_to_many(
|
||||
:relatives,
|
||||
join_table: join_table_name
|
||||
)
|
||||
end
|
||||
|
||||
create_table(join_table_name, id: false) do |t|
|
||||
t.references :person
|
||||
t.references :relative
|
||||
end
|
||||
|
||||
assertion = lambda do
|
||||
expect(Person.new).
|
||||
to have_and_belong_to_many(:relatives).
|
||||
join_table('family_tree')
|
||||
end
|
||||
|
||||
expect(&assertion).to fail_with_message_including(
|
||||
"relatives should use 'family_tree' for :join_table option"
|
||||
)
|
||||
end.to fail_with_message_including("#{join_table_name} doesn't exist")
|
||||
end
|
||||
end
|
||||
|
||||
it "accepts an association with a valid :join_table option" do
|
||||
define_model :relative
|
||||
join_table_name = 'people_and_their_families'
|
||||
context 'when the association is not declared with a :join_table option' do
|
||||
it 'rejects when testing with a :join_table option' do
|
||||
define_model :relative
|
||||
|
||||
define_model :person do
|
||||
has_and_belongs_to_many(
|
||||
:relatives, join_table: join_table_name
|
||||
define_model :person do
|
||||
has_and_belongs_to_many(:relatives)
|
||||
end
|
||||
|
||||
create_table('people_relatives', id: false) do |t|
|
||||
t.references :person
|
||||
t.references :relative
|
||||
end
|
||||
|
||||
assertion = lambda do
|
||||
expect(Person.new).
|
||||
to have_and_belong_to_many(:relatives).
|
||||
join_table('family_tree')
|
||||
end
|
||||
|
||||
expect(&assertion).to fail_with_message_including(
|
||||
"relatives should use 'family_tree' for :join_table option"
|
||||
)
|
||||
end
|
||||
|
||||
create_table(join_table_name, id: false) do |t|
|
||||
t.references :person
|
||||
t.references :relative
|
||||
end
|
||||
|
||||
expect(Person.new).to(
|
||||
have_and_belong_to_many(:relatives).join_table(join_table_name)
|
||||
)
|
||||
end
|
||||
|
||||
context 'using a custom foreign key' do
|
||||
|
|
Loading…
Reference in a new issue