2013-12-12 21:45:43 +00:00
|
|
|
require 'active_support/core_ext/module/delegation'
|
Refactor AssociationMatcher to use new OptionVerifier
When using an association matcher you may have qualifiers on that
matcher which let you make assertions on options passed to the
association method that you are testing. For instance, has_many has a
:dependent option and so in order to test this you say something like
it { should have_many(:people).dependent(:destroy) }
In order to test such an option we have to compare the option you passed
with what the actual value of that option is. This is usually obtained
by looking at the reflection object of the association in question,
although it can be obtained by other means too.
Anyway, the code that does this comparison isn't terrible, but there are
two problems with it. First, it involves typecasting both expected and
actual values. For instance, this:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent(:destroy) }
should be equivalent to:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent('destroy') }
should be equivalent to:
has_many :people, dependent: 'destroy'
it { should have_many(:people).dependent(:destroy) }
Second, we are a little screwed if the method of obtaining the actual
value of the option changes depending on which Rails version you're
using.
So, OptionVerifier attempts to address both of these issues. It's a
little crazy, but it works.
I also moved some methods from AssociationMatcher to ModelReflector
where they really belong.
2013-08-16 21:38:10 +00:00
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
module Shoulda
|
2010-12-15 22:34:19 +00:00
|
|
|
module Matchers
|
2014-01-23 18:07:36 +00:00
|
|
|
module ActiveRecord
|
|
|
|
# The `belong_to` matcher is used to ensure that a `belong_to` association
|
|
|
|
# exists on your model.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:organization) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:organization)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# Note that polymorphic associations are automatically detected and do not
|
|
|
|
# need any qualifiers:
|
|
|
|
#
|
|
|
|
# class Comment < ActiveRecord::Base
|
|
|
|
# belongs_to :commentable, polymorphic: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Comment, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:commentable) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class CommentTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:commentable)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# #### Qualifiers
|
|
|
|
#
|
|
|
|
# ##### conditions
|
|
|
|
#
|
|
|
|
# Use `conditions` if your association is defined with a scope that sets
|
|
|
|
# the `where` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :family, -> { where(everyone_is_perfect: false) }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should belong_to(:family).
|
|
|
|
# conditions(everyone_is_perfect: false)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:family).
|
|
|
|
# conditions(everyone_is_perfect: false)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### order
|
|
|
|
#
|
|
|
|
# Use `order` if your association is defined with a scope that sets the
|
|
|
|
# `order` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :previous_company, -> { order('hired_on desc') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:previous_company).order('hired_on desc') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:previous_company).order('hired_on desc')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### class_name
|
|
|
|
#
|
|
|
|
# Use `class_name` to test usage of the `:class_name` option. This
|
|
|
|
# asserts that the model you're referring to actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :ancient_city, class_name: 'City'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:ancient_city).class_name('City') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:ancient_city).class_name('City')
|
|
|
|
# end
|
|
|
|
#
|
2014-10-07 21:05:16 +00:00
|
|
|
# ##### with_primary_key
|
|
|
|
#
|
|
|
|
# Use `with_primary_key` to test usage of the `:primary_key` option.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :great_country, primary_key: 'country_id'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-10-07 21:05:16 +00:00
|
|
|
# it do
|
|
|
|
# should belong_to(:great_country).
|
|
|
|
# with_primary_key('country_id')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-10-07 21:05:16 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:great_country).
|
|
|
|
# with_primary_key('country_id')
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# ##### with_foreign_key
|
|
|
|
#
|
|
|
|
# Use `with_foreign_key` to test usage of the `:foreign_key` option.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :great_country, foreign_key: 'country_id'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should belong_to(:great_country).
|
|
|
|
# with_foreign_key('country_id')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:great_country).
|
|
|
|
# with_foreign_key('country_id')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### dependent
|
|
|
|
#
|
|
|
|
# Use `dependent` to assert that the `:dependent` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :world, dependent: :destroy
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:world).dependent(:destroy) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:world).dependent(:destroy)
|
|
|
|
# end
|
|
|
|
#
|
2014-12-28 13:04:49 +00:00
|
|
|
# To assert that *any* `:dependent` option was specified, use `true`:
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-12-28 13:04:49 +00:00
|
|
|
# it { should belong_to(:world).dependent(true) }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# To assert that *no* `:dependent` option was specified, use `false`:
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :company
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-12-28 13:04:49 +00:00
|
|
|
# it { should belong_to(:company).dependent(false) }
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# ##### counter_cache
|
|
|
|
#
|
|
|
|
# Use `counter_cache` to assert that the `:counter_cache` option was
|
|
|
|
# specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization, counter_cache: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:organization).counter_cache(true) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:organization).counter_cache(true)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### touch
|
|
|
|
#
|
|
|
|
# Use `touch` to assert that the `:touch` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization, touch: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:organization).touch(true) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:organization).touch(true)
|
|
|
|
# end
|
|
|
|
#
|
2016-09-27 18:57:50 +00:00
|
|
|
# ##### autosave
|
2014-01-23 18:07:36 +00:00
|
|
|
#
|
|
|
|
# Use `autosave` to assert that the `:autosave` option was specified.
|
|
|
|
#
|
|
|
|
# class Account < ActiveRecord::Base
|
|
|
|
# belongs_to :bank, autosave: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Account, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should belong_to(:bank).autosave(true) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class AccountTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:bank).autosave(true)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### inverse_of
|
|
|
|
#
|
|
|
|
# Use `inverse_of` to assert that the `:inverse_of` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization, inverse_of: :employees
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person
|
|
|
|
# it { should belong_to(:organization).inverse_of(:employees) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:organization).inverse_of(:employees)
|
|
|
|
# end
|
|
|
|
#
|
2017-10-02 05:19:03 +00:00
|
|
|
# ##### required
|
|
|
|
#
|
|
|
|
# Use `required` to assert that the association is not allowed to be nil.
|
|
|
|
# (Enabled by default in Rails 5+.)
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization, required: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person
|
|
|
|
# it { should belong_to(:organization).required }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Minitest (Shoulda)
|
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:organization).required
|
|
|
|
# end
|
|
|
|
#
|
2019-02-20 16:14:25 +00:00
|
|
|
# #### without_validating_presence
|
Add without_presence_validation q to belong_to
With the new Rails 5 behavior, `belong_to` will check to ensure that the
association has a presence validation on it. In some cases, however,
this is not desirable. For instance, say we have this setup:
class Employee < ApplicationRecord
# Assume belongs_to_required_by_default is true
belongs_to :manager
before_validation :add_manager
private
def add_manager
self.manager = Manager.create
end
end
In this case, even though the association is effectively defined with
`required: true`, the ensuing presence validation never fails, because
`manager` is always set to something before validations kick off. So
this test won't work:
it { should belong_to(:manager) }
To get around this, this commit allows us to say:
it { should belong_to(:manager).without_presence_validation }
which instructs the matcher not to test for any presence (or absence,
for that matter) of a presence validation, mimicking the pre-Rails 5
behavior.
2019-01-29 08:19:12 +00:00
|
|
|
#
|
2019-02-20 16:14:25 +00:00
|
|
|
# Use `without_validating_presence` with `belong_to` to prevent the
|
Add without_presence_validation q to belong_to
With the new Rails 5 behavior, `belong_to` will check to ensure that the
association has a presence validation on it. In some cases, however,
this is not desirable. For instance, say we have this setup:
class Employee < ApplicationRecord
# Assume belongs_to_required_by_default is true
belongs_to :manager
before_validation :add_manager
private
def add_manager
self.manager = Manager.create
end
end
In this case, even though the association is effectively defined with
`required: true`, the ensuing presence validation never fails, because
`manager` is always set to something before validations kick off. So
this test won't work:
it { should belong_to(:manager) }
To get around this, this commit allows us to say:
it { should belong_to(:manager).without_presence_validation }
which instructs the matcher not to test for any presence (or absence,
for that matter) of a presence validation, mimicking the pre-Rails 5
behavior.
2019-01-29 08:19:12 +00:00
|
|
|
# matcher from checking whether the association disallows nil (Rails 5+
|
|
|
|
# only). This can be helpful if you have a custom hook that always sets
|
|
|
|
# the association to a meaningful value:
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization
|
|
|
|
#
|
|
|
|
# before_validation :autoassign_organization
|
|
|
|
#
|
|
|
|
# private
|
|
|
|
#
|
|
|
|
# def autoassign_organization
|
|
|
|
# self.organization = Organization.create!
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person
|
2019-02-20 16:14:25 +00:00
|
|
|
# it { should belong_to(:organization).without_validating_presence }
|
Add without_presence_validation q to belong_to
With the new Rails 5 behavior, `belong_to` will check to ensure that the
association has a presence validation on it. In some cases, however,
this is not desirable. For instance, say we have this setup:
class Employee < ApplicationRecord
# Assume belongs_to_required_by_default is true
belongs_to :manager
before_validation :add_manager
private
def add_manager
self.manager = Manager.create
end
end
In this case, even though the association is effectively defined with
`required: true`, the ensuing presence validation never fails, because
`manager` is always set to something before validations kick off. So
this test won't work:
it { should belong_to(:manager) }
To get around this, this commit allows us to say:
it { should belong_to(:manager).without_presence_validation }
which instructs the matcher not to test for any presence (or absence,
for that matter) of a presence validation, mimicking the pre-Rails 5
behavior.
2019-01-29 08:19:12 +00:00
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Minitest (Shoulda)
|
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
2019-02-20 16:14:25 +00:00
|
|
|
# should belong_to(:organization).without_validating_presence
|
Add without_presence_validation q to belong_to
With the new Rails 5 behavior, `belong_to` will check to ensure that the
association has a presence validation on it. In some cases, however,
this is not desirable. For instance, say we have this setup:
class Employee < ApplicationRecord
# Assume belongs_to_required_by_default is true
belongs_to :manager
before_validation :add_manager
private
def add_manager
self.manager = Manager.create
end
end
In this case, even though the association is effectively defined with
`required: true`, the ensuing presence validation never fails, because
`manager` is always set to something before validations kick off. So
this test won't work:
it { should belong_to(:manager) }
To get around this, this commit allows us to say:
it { should belong_to(:manager).without_presence_validation }
which instructs the matcher not to test for any presence (or absence,
for that matter) of a presence validation, mimicking the pre-Rails 5
behavior.
2019-01-29 08:19:12 +00:00
|
|
|
# end
|
|
|
|
#
|
2017-10-02 05:19:03 +00:00
|
|
|
# ##### optional
|
|
|
|
#
|
|
|
|
# Use `optional` to assert that the association is allowed to be nil.
|
|
|
|
# (Rails 5+ only.)
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# belongs_to :organization, optional: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person
|
|
|
|
# it { should belong_to(:organization).optional }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Minitest (Shoulda)
|
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should belong_to(:organization).optional
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# @return [AssociationMatcher]
|
|
|
|
#
|
2010-12-15 22:34:19 +00:00
|
|
|
def belong_to(name)
|
|
|
|
AssociationMatcher.new(:belongs_to, name)
|
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# The `have_many` matcher is used to test that a `has_many` or `has_many
|
|
|
|
# :through` association exists on your model.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :friends
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:friends) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:friends)
|
|
|
|
# end
|
|
|
|
#
|
2015-06-17 22:15:52 +00:00
|
|
|
# Note that polymorphic associations are automatically detected and do not
|
|
|
|
# need any qualifiers:
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :pictures, as: :imageable
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2015-06-17 22:15:52 +00:00
|
|
|
# it { should have_many(:pictures) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2015-06-17 22:15:52 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:pictures)
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# #### Qualifiers
|
|
|
|
#
|
|
|
|
# ##### conditions
|
|
|
|
#
|
|
|
|
# Use `conditions` if your association is defined with a scope that sets
|
|
|
|
# the `where` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :coins, -> { where(quality: 'mint') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:coins).conditions(quality: 'mint') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:coins).conditions(quality: 'mint')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### order
|
|
|
|
#
|
|
|
|
# Use `order` if your association is defined with a scope that sets the
|
|
|
|
# `order` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :shirts, -> { order('color') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:shirts).order('color') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:shirts).order('color')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### class_name
|
|
|
|
#
|
|
|
|
# Use `class_name` to test usage of the `:class_name` option. This
|
|
|
|
# asserts that the model you're referring to actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :hopes, class_name: 'Dream'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:hopes).class_name('Dream') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:hopes).class_name('Dream')
|
|
|
|
# end
|
|
|
|
#
|
2014-10-07 21:05:16 +00:00
|
|
|
# ##### with_primary_key
|
|
|
|
#
|
|
|
|
# Use `with_primary_key` to test usage of the `:primary_key` option.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :worries, primary_key: 'worrier_id'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-11-04 21:06:41 +00:00
|
|
|
# it { should have_many(:worries).with_primary_key('worrier_id') }
|
2014-10-07 21:05:16 +00:00
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-10-07 21:05:16 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:worries).with_primary_key('worrier_id')
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# ##### with_foreign_key
|
|
|
|
#
|
|
|
|
# Use `with_foreign_key` to test usage of the `:foreign_key` option.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :worries, foreign_key: 'worrier_id'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:worries).with_foreign_key('worrier_id') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:worries).with_foreign_key('worrier_id')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### dependent
|
|
|
|
#
|
|
|
|
# Use `dependent` to assert that the `:dependent` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :secret_documents, dependent: :destroy
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:secret_documents).dependent(:destroy) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:secret_documents).dependent(:destroy)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### through
|
|
|
|
#
|
|
|
|
# Use `through` to test usage of the `:through` option. This asserts that
|
|
|
|
# the association you are going through actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :acquaintances, through: :friends
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:acquaintances).through(:friends) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:acquaintances).through(:friends)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### source
|
|
|
|
#
|
|
|
|
# Use `source` to test usage of the `:source` option on a `:through`
|
|
|
|
# association.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :job_offers, through: :friends, source: :opportunities
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should have_many(:job_offers).
|
|
|
|
# through(:friends).
|
|
|
|
# source(:opportunities)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:job_offers).
|
|
|
|
# through(:friends).
|
|
|
|
# source(:opportunities)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### validate
|
|
|
|
#
|
|
|
|
# Use `validate` to assert that the `:validate` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_many :ideas, validate: false
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:ideas).validate(false) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:ideas).validate(false)
|
|
|
|
# end
|
|
|
|
#
|
2016-09-27 18:57:50 +00:00
|
|
|
# ##### autosave
|
2014-01-23 18:07:36 +00:00
|
|
|
#
|
|
|
|
# Use `autosave` to assert that the `:autosave` option was specified.
|
|
|
|
#
|
|
|
|
# class Player < ActiveRecord::Base
|
|
|
|
# has_many :games, autosave: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Player, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_many(:games).autosave(true) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PlayerTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:games).autosave(true)
|
|
|
|
# end
|
|
|
|
#
|
2018-03-08 20:25:46 +00:00
|
|
|
# ##### index_errors
|
|
|
|
#
|
|
|
|
# Use `index_errors` to assert that the `:index_errors` option was
|
|
|
|
# specified.
|
|
|
|
#
|
|
|
|
# class Player < ActiveRecord::Base
|
|
|
|
# has_many :games, index_errors: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# RSpec.describe Player, type: :model do
|
|
|
|
# it { should have_many(:games).index_errors(true) }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Minitest (Shoulda)
|
|
|
|
# class PlayerTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:games).index_errors(true)
|
|
|
|
# end
|
|
|
|
#
|
2016-05-24 15:02:15 +00:00
|
|
|
# ##### inverse_of
|
|
|
|
#
|
|
|
|
# Use `inverse_of` to assert that the `:inverse_of` option was specified.
|
|
|
|
#
|
|
|
|
# class Organization < ActiveRecord::Base
|
|
|
|
# has_many :employees, inverse_of: :company
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Organization
|
|
|
|
# it { should have_many(:employees).inverse_of(:company) }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Minitest (Shoulda)
|
|
|
|
# class OrganizationTest < ActiveSupport::TestCase
|
|
|
|
# should have_many(:employees).inverse_of(:company)
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# @return [AssociationMatcher]
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
|
|
|
def have_many(name)
|
|
|
|
AssociationMatcher.new(:has_many, name)
|
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# The `have_one` matcher is used to test that a `has_one` or `has_one
|
|
|
|
# :through` association exists on your model.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :partner
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:partner) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:partner)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# #### Qualifiers
|
|
|
|
#
|
|
|
|
# ##### conditions
|
|
|
|
#
|
|
|
|
# Use `conditions` if your association is defined with a scope that sets
|
|
|
|
# the `where` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :pet, -> { where('weight < 80') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:pet).conditions('weight < 80') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:pet).conditions('weight < 80')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### order
|
|
|
|
#
|
|
|
|
# Use `order` if your association is defined with a scope that sets the
|
|
|
|
# `order` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :focus, -> { order('priority desc') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:focus).order('priority desc') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:focus).order('priority desc')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### class_name
|
|
|
|
#
|
|
|
|
# Use `class_name` to test usage of the `:class_name` option. This
|
|
|
|
# asserts that the model you're referring to actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :chance, class_name: 'Opportunity'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:chance).class_name('Opportunity') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:chance).class_name('Opportunity')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### dependent
|
|
|
|
#
|
|
|
|
# Use `dependent` to test that the `:dependent` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :contract, dependent: :nullify
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:contract).dependent(:nullify) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:contract).dependent(:nullify)
|
|
|
|
# end
|
|
|
|
#
|
2014-10-07 21:05:16 +00:00
|
|
|
# ##### with_primary_key
|
|
|
|
#
|
|
|
|
# Use `with_primary_key` to test usage of the `:primary_key` option.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :job, primary_key: 'worker_id'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-10-07 21:05:16 +00:00
|
|
|
# it { should have_one(:job).with_primary_key('worker_id') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-10-07 21:05:16 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:job).with_primary_key('worker_id')
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# ##### with_foreign_key
|
|
|
|
#
|
|
|
|
# Use `with_foreign_key` to test usage of the `:foreign_key` option.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :job, foreign_key: 'worker_id'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:job).with_foreign_key('worker_id') }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:job).with_foreign_key('worker_id')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### through
|
|
|
|
#
|
|
|
|
# Use `through` to test usage of the `:through` option. This asserts that
|
|
|
|
# the association you are going through actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :life, through: :partner
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:life).through(:partner) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:life).through(:partner)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### source
|
|
|
|
#
|
|
|
|
# Use `source` to test usage of the `:source` option on a `:through`
|
|
|
|
# association.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :car, through: :partner, source: :vehicle
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:car).through(:partner).source(:vehicle) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:car).through(:partner).source(:vehicle)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### validate
|
|
|
|
#
|
|
|
|
# Use `validate` to assert that the the `:validate` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :parking_card, validate: false
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:parking_card).validate(false) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:parking_card).validate(false)
|
|
|
|
# end
|
|
|
|
#
|
2016-09-27 18:57:50 +00:00
|
|
|
# ##### autosave
|
2014-01-23 18:07:36 +00:00
|
|
|
#
|
|
|
|
# Use `autosave` to assert that the `:autosave` option was specified.
|
|
|
|
#
|
|
|
|
# class Account < ActiveRecord::Base
|
|
|
|
# has_one :bank, autosave: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Account, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_one(:bank).autosave(true) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class AccountTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:bank).autosave(true)
|
|
|
|
# end
|
|
|
|
#
|
2017-10-02 05:19:03 +00:00
|
|
|
# ##### required
|
|
|
|
#
|
|
|
|
# Use `required` to assert that the association is not allowed to be nil.
|
|
|
|
# (Rails 5+ only.)
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_one :brain, required: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Person
|
|
|
|
# it { should have_one(:brain).required }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Minitest (Shoulda)
|
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_one(:brain).required
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# @return [AssociationMatcher]
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
|
|
|
def have_one(name)
|
|
|
|
AssociationMatcher.new(:has_one, name)
|
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# The `have_and_belong_to_many` matcher is used to test that a
|
|
|
|
# `has_and_belongs_to_many` association exists on your model and that the
|
|
|
|
# join table exists in the database.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :awards
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_and_belong_to_many(:awards) }
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:awards)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# #### Qualifiers
|
|
|
|
#
|
|
|
|
# ##### conditions
|
|
|
|
#
|
|
|
|
# Use `conditions` if your association is defined with a scope that sets
|
|
|
|
# the `where` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :issues, -> { where(difficulty: 'hard') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should have_and_belong_to_many(:issues).
|
|
|
|
# conditions(difficulty: 'hard')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:issues).
|
|
|
|
# conditions(difficulty: 'hard')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### order
|
|
|
|
#
|
|
|
|
# Use `order` if your association is defined with a scope that sets the
|
|
|
|
# `order` clause.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :projects, -> { order('time_spent') }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should have_and_belong_to_many(:projects).
|
|
|
|
# order('time_spent')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:projects).
|
|
|
|
# order('time_spent')
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# ##### class_name
|
|
|
|
#
|
|
|
|
# Use `class_name` to test usage of the `:class_name` option. This
|
|
|
|
# asserts that the model you're referring to actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :places_visited, class_name: 'City'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should have_and_belong_to_many(:places_visited).
|
|
|
|
# class_name('City')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:places_visited).
|
|
|
|
# class_name('City')
|
|
|
|
# end
|
|
|
|
#
|
2015-04-21 14:04:01 +00:00
|
|
|
# ##### join_table
|
|
|
|
#
|
|
|
|
# Use `join_table` to test usage of the `:join_table` option. This
|
|
|
|
# asserts that the table you're referring to actually exists.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :issues, join_table: 'people_tickets'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2015-04-21 14:04:01 +00:00
|
|
|
# it do
|
|
|
|
# should have_and_belong_to_many(:issues).
|
|
|
|
# join_table('people_tickets')
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2015-04-21 14:04:01 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:issues).
|
|
|
|
# join_table('people_tickets')
|
|
|
|
# end
|
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# ##### validate
|
|
|
|
#
|
|
|
|
# Use `validate` to test that the `:validate` option was specified.
|
|
|
|
#
|
|
|
|
# class Person < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :interviews, validate: false
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Person, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it do
|
|
|
|
# should have_and_belong_to_many(:interviews).
|
|
|
|
# validate(false)
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class PersonTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:interviews).
|
|
|
|
# validate(false)
|
|
|
|
# end
|
|
|
|
#
|
2016-09-27 18:57:50 +00:00
|
|
|
# ##### autosave
|
2014-01-23 18:07:36 +00:00
|
|
|
#
|
|
|
|
# Use `autosave` to assert that the `:autosave` option was specified.
|
|
|
|
#
|
|
|
|
# class Publisher < ActiveRecord::Base
|
|
|
|
# has_and_belongs_to_many :advertisers, autosave: true
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
2016-06-16 00:00:51 +00:00
|
|
|
# RSpec.describe Publisher, type: :model do
|
2014-01-23 18:07:36 +00:00
|
|
|
# it { should have_and_belong_to_many(:advertisers).autosave(true) }
|
|
|
|
# end
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
2015-09-30 19:15:23 +00:00
|
|
|
# # Minitest (Shoulda)
|
2014-01-23 18:07:36 +00:00
|
|
|
# class AccountTest < ActiveSupport::TestCase
|
|
|
|
# should have_and_belong_to_many(:advertisers).autosave(true)
|
|
|
|
# end
|
2012-04-11 16:45:52 +00:00
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# @return [AssociationMatcher]
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
|
|
|
def have_and_belong_to_many(name)
|
|
|
|
AssociationMatcher.new(:has_and_belongs_to_many, name)
|
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# @private
|
|
|
|
class AssociationMatcher
|
Refactor AssociationMatcher to use new OptionVerifier
When using an association matcher you may have qualifiers on that
matcher which let you make assertions on options passed to the
association method that you are testing. For instance, has_many has a
:dependent option and so in order to test this you say something like
it { should have_many(:people).dependent(:destroy) }
In order to test such an option we have to compare the option you passed
with what the actual value of that option is. This is usually obtained
by looking at the reflection object of the association in question,
although it can be obtained by other means too.
Anyway, the code that does this comparison isn't terrible, but there are
two problems with it. First, it involves typecasting both expected and
actual values. For instance, this:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent(:destroy) }
should be equivalent to:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent('destroy') }
should be equivalent to:
has_many :people, dependent: 'destroy'
it { should have_many(:people).dependent(:destroy) }
Second, we are a little screwed if the method of obtaining the actual
value of the option changes depending on which Rails version you're
using.
So, OptionVerifier attempts to address both of these issues. It's a
little crazy, but it works.
I also moved some methods from AssociationMatcher to ModelReflector
where they really belong.
2013-08-16 21:38:10 +00:00
|
|
|
delegate :reflection, :model_class, :associated_class, :through?,
|
2014-09-10 19:38:20 +00:00
|
|
|
:polymorphic?, to: :reflector
|
|
|
|
|
|
|
|
attr_reader :name, :options
|
Refactor AssociationMatcher to use new OptionVerifier
When using an association matcher you may have qualifiers on that
matcher which let you make assertions on options passed to the
association method that you are testing. For instance, has_many has a
:dependent option and so in order to test this you say something like
it { should have_many(:people).dependent(:destroy) }
In order to test such an option we have to compare the option you passed
with what the actual value of that option is. This is usually obtained
by looking at the reflection object of the association in question,
although it can be obtained by other means too.
Anyway, the code that does this comparison isn't terrible, but there are
two problems with it. First, it involves typecasting both expected and
actual values. For instance, this:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent(:destroy) }
should be equivalent to:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent('destroy') }
should be equivalent to:
has_many :people, dependent: 'destroy'
it { should have_many(:people).dependent(:destroy) }
Second, we are a little screwed if the method of obtaining the actual
value of the option changes depending on which Rails version you're
using.
So, OptionVerifier attempts to address both of these issues. It's a
little crazy, but it works.
I also moved some methods from AssociationMatcher to ModelReflector
where they really belong.
2013-08-16 21:38:10 +00:00
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def initialize(macro, name)
|
|
|
|
@macro = macro
|
2012-04-23 21:37:40 +00:00
|
|
|
@name = name
|
|
|
|
@options = {}
|
2013-06-07 14:14:27 +00:00
|
|
|
@submatchers = []
|
|
|
|
@missing = ''
|
2017-10-02 05:19:03 +00:00
|
|
|
|
Fix default behavior of belong_to under Rails 4.2
Rails 5 introduced a change where `belongs_to` would default to adding a
presence validation along with the association. However, it also
introduced a configuration option, `belongs_to_required_by_default`, to
emulate the old behavior prior to Rails 5. For Rails 4.2 projects as
well as Rails 5 which were migrated from 4, this setting is false, so
that existing apps do not break.
To mimic this, a change was made to the `belong_to` matcher to check for
the presence of the presence validator if
`belongs_to_required_by_default` is true and check for the absence of
the presence validator if it is false. However, this last bit of the
logic actually causes problems. Take this case, for example:
ActiveRecord::Base.belongs_to_required_by_default = false
class Post < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
end
RSpec.describe Post, type: :model do
it { is_expected.to belong_to(:user) }
end
In this example, the developer has chosen to place a presence validation
on the association manually. `belong_to` doesn't know this, however, and
will check to make sure that `user` can be nil, which of course it
can't. Therefore, this test will fail. In addition, the failure message
that `belong_to` generates is confusing:
Expected Post to have a belongs_to association called user (the
association should have been defined with `optional: true`, but was
not)
The reason why the test fails is that when
`belongs_to_required_by_default` is false, belong_to` will place an
implicit `optional` qualifier on itself. In other words, these two tests
are equivalent:
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:user).optional }
However, this is not only wrong, but the `belongs_to` macro in Rails 4.2
doesn't have an `optional` option (it has `required` instead), so the
failure message that `belong_to` generates is confusing.
This commit fixes this by modifying `belong_to` so that under Rails 4.2,
the matcher will have not have any qualifiers on it by default.
2018-09-13 00:54:29 +00:00
|
|
|
if macro == :belongs_to && RailsShim.active_record_gte_5?
|
|
|
|
required(belongs_to_required_by_default?)
|
2017-10-02 05:19:03 +00:00
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def through(through)
|
2017-10-02 05:19:03 +00:00
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::ThroughMatcher,
|
|
|
|
through,
|
|
|
|
name,
|
|
|
|
)
|
2010-12-15 22:34:19 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def dependent(dependent)
|
2017-10-02 05:19:03 +00:00
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::DependentMatcher,
|
|
|
|
dependent,
|
|
|
|
name,
|
|
|
|
)
|
2010-12-15 22:34:19 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2011-06-16 14:21:57 +00:00
|
|
|
def order(order)
|
2017-10-02 05:19:03 +00:00
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::OrderMatcher,
|
|
|
|
order,
|
|
|
|
name,
|
|
|
|
)
|
2011-06-16 14:21:57 +00:00
|
|
|
self
|
2011-09-12 10:25:05 +00:00
|
|
|
end
|
2011-10-16 12:06:24 +00:00
|
|
|
|
2013-06-19 23:52:46 +00:00
|
|
|
def counter_cache(counter_cache = true)
|
2017-10-02 05:19:03 +00:00
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::CounterCacheMatcher,
|
|
|
|
counter_cache,
|
|
|
|
name,
|
|
|
|
)
|
2013-06-19 23:52:46 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-03-19 03:13:28 +00:00
|
|
|
def inverse_of(inverse_of)
|
2017-10-02 05:19:03 +00:00
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::InverseOfMatcher,
|
|
|
|
inverse_of,
|
|
|
|
name,
|
|
|
|
)
|
2014-03-19 03:13:28 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2013-11-27 18:18:46 +00:00
|
|
|
def source(source)
|
2017-10-02 05:19:03 +00:00
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::SourceMatcher,
|
|
|
|
source,
|
|
|
|
name,
|
|
|
|
)
|
2013-11-27 18:18:46 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2011-09-23 11:19:17 +00:00
|
|
|
def conditions(conditions)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options[:conditions] = conditions
|
2011-09-23 11:19:17 +00:00
|
|
|
self
|
|
|
|
end
|
2011-06-16 14:21:57 +00:00
|
|
|
|
2014-01-22 20:40:38 +00:00
|
|
|
def autosave(autosave)
|
|
|
|
@options[:autosave] = autosave
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2018-03-08 20:25:46 +00:00
|
|
|
def index_errors(index_errors)
|
|
|
|
@options[:index_errors] = index_errors
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2011-10-16 12:06:24 +00:00
|
|
|
def class_name(class_name)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options[:class_name] = class_name
|
2011-10-16 12:06:24 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2012-08-24 05:48:17 +00:00
|
|
|
def with_foreign_key(foreign_key)
|
|
|
|
@options[:foreign_key] = foreign_key
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-10-07 21:05:16 +00:00
|
|
|
def with_primary_key(primary_key)
|
|
|
|
@options[:primary_key] = primary_key
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2017-10-02 05:19:03 +00:00
|
|
|
def required(required = true)
|
|
|
|
remove_submatcher(AssociationMatchers::OptionalMatcher)
|
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::RequiredMatcher,
|
|
|
|
name,
|
|
|
|
required,
|
|
|
|
)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2018-01-29 05:53:24 +00:00
|
|
|
def optional
|
2017-10-02 05:19:03 +00:00
|
|
|
remove_submatcher(AssociationMatchers::RequiredMatcher)
|
|
|
|
add_submatcher(
|
|
|
|
AssociationMatchers::OptionalMatcher,
|
|
|
|
name,
|
2018-01-29 05:53:24 +00:00
|
|
|
true,
|
2017-10-02 05:19:03 +00:00
|
|
|
)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2012-04-11 16:45:52 +00:00
|
|
|
def validate(validate = true)
|
2013-04-10 02:46:32 +00:00
|
|
|
@options[:validate] = validate
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def touch(touch = true)
|
|
|
|
@options[:touch] = touch
|
2012-04-11 16:45:52 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2014-07-23 23:26:15 +00:00
|
|
|
def join_table(join_table_name)
|
2014-09-10 19:38:20 +00:00
|
|
|
@options[:join_table_name] = join_table_name
|
2014-07-23 23:26:15 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
Add without_presence_validation q to belong_to
With the new Rails 5 behavior, `belong_to` will check to ensure that the
association has a presence validation on it. In some cases, however,
this is not desirable. For instance, say we have this setup:
class Employee < ApplicationRecord
# Assume belongs_to_required_by_default is true
belongs_to :manager
before_validation :add_manager
private
def add_manager
self.manager = Manager.create
end
end
In this case, even though the association is effectively defined with
`required: true`, the ensuing presence validation never fails, because
`manager` is always set to something before validations kick off. So
this test won't work:
it { should belong_to(:manager) }
To get around this, this commit allows us to say:
it { should belong_to(:manager).without_presence_validation }
which instructs the matcher not to test for any presence (or absence,
for that matter) of a presence validation, mimicking the pre-Rails 5
behavior.
2019-01-29 08:19:12 +00:00
|
|
|
def without_validating_presence
|
|
|
|
remove_submatcher(AssociationMatchers::RequiredMatcher)
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2013-06-14 15:21:50 +00:00
|
|
|
def description
|
2013-06-14 15:34:22 +00:00
|
|
|
description = "#{macro_description} #{name}"
|
|
|
|
description += " class_name => #{options[:class_name]}" if options.key?(:class_name)
|
|
|
|
[description, submatchers.map(&:description)].flatten.join(' ')
|
2013-06-14 15:21:50 +00:00
|
|
|
end
|
|
|
|
|
2013-12-24 11:24:27 +00:00
|
|
|
def failure_message
|
2013-06-14 15:34:22 +00:00
|
|
|
"Expected #{expectation} (#{missing_options})"
|
2013-06-14 15:21:50 +00:00
|
|
|
end
|
|
|
|
|
2013-12-24 11:24:27 +00:00
|
|
|
def failure_message_when_negated
|
2013-06-14 15:21:50 +00:00
|
|
|
"Did not expect #{expectation}"
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def matches?(subject)
|
|
|
|
@subject = subject
|
|
|
|
association_exists? &&
|
|
|
|
macro_correct? &&
|
2014-04-12 20:49:01 +00:00
|
|
|
(polymorphic? || class_exists?) &&
|
2010-12-15 22:34:19 +00:00
|
|
|
foreign_key_exists? &&
|
2014-10-07 21:05:16 +00:00
|
|
|
primary_key_exists? &&
|
2011-10-16 12:06:24 +00:00
|
|
|
class_name_correct? &&
|
2014-04-26 05:26:45 +00:00
|
|
|
join_table_correct? &&
|
2014-01-22 20:40:38 +00:00
|
|
|
autosave_correct? &&
|
2018-03-08 20:25:46 +00:00
|
|
|
index_errors_correct? &&
|
2011-09-23 11:19:17 +00:00
|
|
|
conditions_correct? &&
|
2013-04-10 02:46:32 +00:00
|
|
|
validate_correct? &&
|
2013-06-07 14:14:27 +00:00
|
|
|
touch_correct? &&
|
|
|
|
submatchers_match?
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2014-09-10 19:38:20 +00:00
|
|
|
def join_table_name
|
|
|
|
options[:join_table_name] || reflector.join_table_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def option_verifier
|
|
|
|
@option_verifier ||= AssociationMatchers::OptionVerifier.new(reflector)
|
|
|
|
end
|
|
|
|
|
2014-06-24 15:57:52 +00:00
|
|
|
protected
|
2013-06-14 15:21:50 +00:00
|
|
|
|
2014-09-10 19:38:20 +00:00
|
|
|
attr_reader :submatchers, :missing, :subject, :macro
|
2013-06-14 15:34:22 +00:00
|
|
|
|
Refactor AssociationMatcher to use new OptionVerifier
When using an association matcher you may have qualifiers on that
matcher which let you make assertions on options passed to the
association method that you are testing. For instance, has_many has a
:dependent option and so in order to test this you say something like
it { should have_many(:people).dependent(:destroy) }
In order to test such an option we have to compare the option you passed
with what the actual value of that option is. This is usually obtained
by looking at the reflection object of the association in question,
although it can be obtained by other means too.
Anyway, the code that does this comparison isn't terrible, but there are
two problems with it. First, it involves typecasting both expected and
actual values. For instance, this:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent(:destroy) }
should be equivalent to:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent('destroy') }
should be equivalent to:
has_many :people, dependent: 'destroy'
it { should have_many(:people).dependent(:destroy) }
Second, we are a little screwed if the method of obtaining the actual
value of the option changes depending on which Rails version you're
using.
So, OptionVerifier attempts to address both of these issues. It's a
little crazy, but it works.
I also moved some methods from AssociationMatcher to ModelReflector
where they really belong.
2013-08-16 21:38:10 +00:00
|
|
|
def reflector
|
|
|
|
@reflector ||= AssociationMatchers::ModelReflector.new(subject, name)
|
|
|
|
end
|
|
|
|
|
2017-10-02 05:19:03 +00:00
|
|
|
def add_submatcher(matcher_class, *args)
|
|
|
|
remove_submatcher(matcher_class)
|
|
|
|
submatchers << matcher_class.new(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def remove_submatcher(matcher_class)
|
|
|
|
submatchers.delete_if do |submatcher|
|
|
|
|
submatcher.is_a?(matcher_class)
|
|
|
|
end
|
2013-06-07 14:14:27 +00:00
|
|
|
end
|
|
|
|
|
2013-06-14 15:21:50 +00:00
|
|
|
def macro_description
|
2013-06-14 15:34:22 +00:00
|
|
|
case macro.to_s
|
2013-06-14 15:21:50 +00:00
|
|
|
when 'belongs_to'
|
|
|
|
'belong to'
|
|
|
|
when 'has_many'
|
|
|
|
'have many'
|
|
|
|
when 'has_one'
|
|
|
|
'have one'
|
|
|
|
when 'has_and_belongs_to_many'
|
|
|
|
'have and belong to many'
|
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2013-06-14 15:21:50 +00:00
|
|
|
def expectation
|
2013-06-14 15:34:22 +00:00
|
|
|
"#{model_class.name} to have a #{macro} association called #{name}"
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2013-06-14 15:34:22 +00:00
|
|
|
def missing_options
|
2014-09-21 22:21:52 +00:00
|
|
|
missing_options = [missing, missing_options_for_failing_submatchers]
|
2017-10-02 05:19:03 +00:00
|
|
|
missing_options.flatten.select(&:present?).join(', ')
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2013-06-14 15:21:50 +00:00
|
|
|
def failing_submatchers
|
2018-01-29 05:53:24 +00:00
|
|
|
@failing_submatchers ||= submatchers.select do |matcher|
|
|
|
|
!matcher.matches?(subject)
|
2013-06-14 15:21:50 +00:00
|
|
|
end
|
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
|
2014-09-21 22:21:52 +00:00
|
|
|
def missing_options_for_failing_submatchers
|
|
|
|
if defined?(@failing_submatchers)
|
|
|
|
@failing_submatchers.map(&:missing_option)
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def association_exists?
|
|
|
|
if reflection.nil?
|
2013-06-14 15:34:22 +00:00
|
|
|
@missing = "no association called #{name}"
|
2010-12-15 22:34:19 +00:00
|
|
|
false
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def macro_correct?
|
2013-06-14 15:34:22 +00:00
|
|
|
if reflection.macro == macro
|
2010-12-15 22:34:19 +00:00
|
|
|
true
|
2013-12-31 19:54:21 +00:00
|
|
|
elsif reflection.macro == :has_many
|
|
|
|
macro == :has_and_belongs_to_many &&
|
|
|
|
reflection.name == @name
|
2010-12-15 22:34:19 +00:00
|
|
|
else
|
|
|
|
@missing = "actual association type was #{reflection.macro}"
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-10-07 21:05:16 +00:00
|
|
|
def macro_supports_primary_key?
|
2014-12-28 13:04:49 +00:00
|
|
|
macro == :belongs_to ||
|
2014-10-07 21:05:16 +00:00
|
|
|
([:has_many, :has_one].include?(macro) && !through?)
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def foreign_key_exists?
|
|
|
|
!(belongs_foreign_key_missing? || has_foreign_key_missing?)
|
|
|
|
end
|
|
|
|
|
2014-10-07 21:05:16 +00:00
|
|
|
def primary_key_exists?
|
|
|
|
!macro_supports_primary_key? || primary_key_correct?(model_class)
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def belongs_foreign_key_missing?
|
2013-06-14 15:34:22 +00:00
|
|
|
macro == :belongs_to && !class_has_foreign_key?(model_class)
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def has_foreign_key_missing?
|
2013-06-14 15:34:22 +00:00
|
|
|
[:has_many, :has_one].include?(macro) &&
|
2010-12-15 22:34:19 +00:00
|
|
|
!through? &&
|
|
|
|
!class_has_foreign_key?(associated_class)
|
|
|
|
end
|
|
|
|
|
2011-10-16 12:06:24 +00:00
|
|
|
def class_name_correct?
|
2013-06-14 15:34:22 +00:00
|
|
|
if options.key?(:class_name)
|
2014-11-05 18:07:29 +00:00
|
|
|
if option_verifier.correct_for_constant?(:class_name, options[:class_name])
|
2012-04-23 21:37:40 +00:00
|
|
|
true
|
|
|
|
else
|
2013-06-14 15:34:22 +00:00
|
|
|
@missing = "#{name} should resolve to #{options[:class_name]} for class_name"
|
2012-04-23 21:37:40 +00:00
|
|
|
false
|
|
|
|
end
|
2011-10-16 12:06:24 +00:00
|
|
|
else
|
2012-04-23 21:37:40 +00:00
|
|
|
true
|
2011-10-16 12:06:24 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-26 05:26:45 +00:00
|
|
|
def join_table_correct?
|
|
|
|
if macro != :has_and_belongs_to_many || join_table_matcher.matches?(@subject)
|
|
|
|
true
|
|
|
|
else
|
|
|
|
@missing = join_table_matcher.failure_message
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def join_table_matcher
|
2014-07-20 19:45:44 +00:00
|
|
|
@join_table_matcher ||=
|
|
|
|
AssociationMatchers::JoinTableMatcher.new(self, reflector)
|
2014-04-26 05:26:45 +00:00
|
|
|
end
|
|
|
|
|
2014-01-10 17:25:14 +00:00
|
|
|
def class_exists?
|
|
|
|
associated_class
|
|
|
|
true
|
|
|
|
rescue NameError
|
|
|
|
@missing = "#{reflection.class_name} does not exist"
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2014-01-22 20:40:38 +00:00
|
|
|
def autosave_correct?
|
|
|
|
if options.key?(:autosave)
|
|
|
|
if option_verifier.correct_for_boolean?(:autosave, options[:autosave])
|
|
|
|
true
|
|
|
|
else
|
|
|
|
@missing = "#{name} should have autosave set to #{options[:autosave]}"
|
|
|
|
false
|
|
|
|
end
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-08 20:25:46 +00:00
|
|
|
def index_errors_correct?
|
|
|
|
return true unless options.key?(:index_errors)
|
|
|
|
|
|
|
|
if option_verifier.correct_for_boolean?(
|
|
|
|
:index_errors,
|
|
|
|
options[:index_errors]
|
|
|
|
)
|
|
|
|
true
|
|
|
|
else
|
|
|
|
@missing =
|
|
|
|
"#{name} should have index_errors set to " +
|
|
|
|
"#{options[:index_errors]}"
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-09-23 11:19:17 +00:00
|
|
|
def conditions_correct?
|
2013-06-14 15:34:22 +00:00
|
|
|
if options.key?(:conditions)
|
2013-05-14 02:42:12 +00:00
|
|
|
if option_verifier.correct_for_relation_clause?(:conditions, options[:conditions])
|
2012-04-23 21:37:40 +00:00
|
|
|
true
|
|
|
|
else
|
2013-06-14 15:34:22 +00:00
|
|
|
@missing = "#{name} should have the following conditions: #{options[:conditions]}"
|
2012-04-23 21:37:40 +00:00
|
|
|
false
|
|
|
|
end
|
2011-09-23 11:19:17 +00:00
|
|
|
else
|
2012-04-23 21:37:40 +00:00
|
|
|
true
|
2011-09-23 11:19:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-04-11 16:45:52 +00:00
|
|
|
def validate_correct?
|
Refactor AssociationMatcher to use new OptionVerifier
When using an association matcher you may have qualifiers on that
matcher which let you make assertions on options passed to the
association method that you are testing. For instance, has_many has a
:dependent option and so in order to test this you say something like
it { should have_many(:people).dependent(:destroy) }
In order to test such an option we have to compare the option you passed
with what the actual value of that option is. This is usually obtained
by looking at the reflection object of the association in question,
although it can be obtained by other means too.
Anyway, the code that does this comparison isn't terrible, but there are
two problems with it. First, it involves typecasting both expected and
actual values. For instance, this:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent(:destroy) }
should be equivalent to:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent('destroy') }
should be equivalent to:
has_many :people, dependent: 'destroy'
it { should have_many(:people).dependent(:destroy) }
Second, we are a little screwed if the method of obtaining the actual
value of the option changes depending on which Rails version you're
using.
So, OptionVerifier attempts to address both of these issues. It's a
little crazy, but it works.
I also moved some methods from AssociationMatcher to ModelReflector
where they really belong.
2013-08-16 21:38:10 +00:00
|
|
|
if option_verifier.correct_for_boolean?(:validate, options[:validate])
|
2013-04-10 02:46:32 +00:00
|
|
|
true
|
|
|
|
else
|
2014-01-17 20:20:44 +00:00
|
|
|
@missing = "#{name} should have validate: #{options[:validate]}"
|
2013-04-10 02:46:32 +00:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def touch_correct?
|
Refactor AssociationMatcher to use new OptionVerifier
When using an association matcher you may have qualifiers on that
matcher which let you make assertions on options passed to the
association method that you are testing. For instance, has_many has a
:dependent option and so in order to test this you say something like
it { should have_many(:people).dependent(:destroy) }
In order to test such an option we have to compare the option you passed
with what the actual value of that option is. This is usually obtained
by looking at the reflection object of the association in question,
although it can be obtained by other means too.
Anyway, the code that does this comparison isn't terrible, but there are
two problems with it. First, it involves typecasting both expected and
actual values. For instance, this:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent(:destroy) }
should be equivalent to:
has_many :people, dependent: :destroy
it { should have_many(:people).dependent('destroy') }
should be equivalent to:
has_many :people, dependent: 'destroy'
it { should have_many(:people).dependent(:destroy) }
Second, we are a little screwed if the method of obtaining the actual
value of the option changes depending on which Rails version you're
using.
So, OptionVerifier attempts to address both of these issues. It's a
little crazy, but it works.
I also moved some methods from AssociationMatcher to ModelReflector
where they really belong.
2013-08-16 21:38:10 +00:00
|
|
|
if option_verifier.correct_for_boolean?(:touch, options[:touch])
|
2012-04-11 16:45:52 +00:00
|
|
|
true
|
|
|
|
else
|
2014-01-17 20:20:44 +00:00
|
|
|
@missing = "#{name} should have touch: #{options[:touch]}"
|
2012-04-11 16:45:52 +00:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def class_has_foreign_key?(klass)
|
2013-06-14 15:34:22 +00:00
|
|
|
if options.key?(:foreign_key)
|
2013-05-14 02:42:12 +00:00
|
|
|
option_verifier.correct_for_string?(:foreign_key, options[:foreign_key])
|
2019-03-21 05:24:02 +00:00
|
|
|
elsif column_names_for(klass).include?(foreign_key)
|
|
|
|
true
|
2010-12-15 22:34:19 +00:00
|
|
|
else
|
2019-03-21 05:24:02 +00:00
|
|
|
@missing = "#{klass} does not have a #{foreign_key} foreign key."
|
|
|
|
false
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-10-07 21:05:16 +00:00
|
|
|
def primary_key_correct?(klass)
|
|
|
|
if options.key?(:primary_key)
|
|
|
|
if option_verifier.correct_for_string?(:primary_key, options[:primary_key])
|
|
|
|
true
|
|
|
|
else
|
|
|
|
@missing = "#{klass} does not have a #{options[:primary_key]} primary key"
|
|
|
|
false
|
|
|
|
end
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-12-15 22:34:19 +00:00
|
|
|
def foreign_key
|
2012-03-23 23:50:08 +00:00
|
|
|
if foreign_key_reflection
|
|
|
|
if foreign_key_reflection.respond_to?(:foreign_key)
|
|
|
|
foreign_key_reflection.foreign_key.to_s
|
|
|
|
else
|
|
|
|
foreign_key_reflection.primary_key_name.to_s
|
|
|
|
end
|
2012-03-11 18:49:27 +00:00
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2012-03-23 23:50:08 +00:00
|
|
|
def foreign_key_reflection
|
2013-06-14 15:34:22 +00:00
|
|
|
if [:has_one, :has_many].include?(macro) && reflection.options.include?(:inverse_of)
|
2012-03-23 23:50:08 +00:00
|
|
|
associated_class.reflect_on_association(reflection.options[:inverse_of])
|
|
|
|
else
|
|
|
|
reflection
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-14 15:21:50 +00:00
|
|
|
def submatchers_match?
|
|
|
|
failing_submatchers.empty?
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
2018-01-29 05:53:24 +00:00
|
|
|
|
2019-03-21 05:24:02 +00:00
|
|
|
def column_names_for(klass)
|
|
|
|
klass.column_names
|
|
|
|
rescue ::ActiveRecord::StatementInvalid
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
|
2018-01-29 05:53:24 +00:00
|
|
|
def belongs_to_required_by_default?
|
|
|
|
::ActiveRecord::Base.belongs_to_required_by_default
|
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|