Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Association matchers now support :source option
  • Loading branch information
Luciano Sousa authored and mcmire committed Dec 1, 2013
1 parent 5c86571 commit 803c06b
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 1 deletion.
5 changes: 4 additions & 1 deletion NEWS.md
Expand Up @@ -13,11 +13,14 @@
* The `ensure_inclusion_of` matcher now works with a decimal column.

* Fix association matchers on Rails 3 so they work when used in conjunction with
a submatcher such as #order and an association that has :include on it.
a submatcher such as #order and an association that has `:include` on it.

* Fix a bug where `validate_uniqueness_of` would fail if the attribute under
test had a limit of fewer than 16 characters.

* You can now test that your `has_many :through` or `has_one :through`
associations are defined with a `:source` option.

# v 2.4.0

* Fix a bug with the `validate_numericality_of` matcher that would not allow the
Expand Down
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -618,6 +618,7 @@ The `have_many` matcher tests your `has_many` and `has_many :through` associatio
class Person < ActiveRecord::Base
has_many :friends
has_many :acquaintances, through: :friends
has_many :job_offers, through: :friends, source: :opportunities
has_many :coins, -> { where(condition: 'mint') }
has_many :shirts, -> { order('color') }
has_many :hopes, class_name: 'Dream'
Expand All @@ -632,6 +633,7 @@ end
describe Person do
it { should have_many(:friends) }
it { should have_many(:acquaintances).through(:friends) }
it { should have_many(:job_offers).through(:friends).source(:opportunities) }
it { should have_many(:coins).conditions(condition: 'mint') }
it { should have_many(:shirts).order('color') }
it { should have_many(:hopes).class_name('Dream') }
Expand All @@ -646,6 +648,7 @@ end
class PersonTest < ActiveSupport::TestCase
should have_many(:friends)
should have_many(:acquaintances).through(:friends)
should have_many(:job_offers).through(:friends).source(:opportunities)
should have_many(:coins).conditions(condition: 'mint')
should have_many(:shirts).order('color')
should have_many(:hopes).class_name('Dream')
Expand All @@ -665,6 +668,7 @@ The `have_one` matcher tests your `has_one` and `has_one :through` associations.
class Person < ActiveRecord::Base
has_one :partner
has_one :life, through: :partner
has_one :car, through: :partner, source: :vehicle
has_one :pet, -> { where('weight < 80') }
has_one :focus, -> { order('priority desc') }
has_one :chance, class_name: 'Opportunity'
Expand All @@ -677,6 +681,7 @@ end
describe Person do
it { should have_one(:partner) }
it { should have_one(:life).through(:partner) }
it { should have_one(:car).through(:partner).source(:vehicle) }
it { should have_one(:pet).conditions('weight < 80') }
it { should have_one(:focus).order('priority desc') }
it { should have_one(:chance).class_name('Opportunity') }
Expand Down
1 change: 1 addition & 0 deletions lib/shoulda/matchers/active_record.rb
Expand Up @@ -3,6 +3,7 @@
require 'shoulda/matchers/active_record/association_matchers/order_matcher'
require 'shoulda/matchers/active_record/association_matchers/through_matcher'
require 'shoulda/matchers/active_record/association_matchers/dependent_matcher'
require 'shoulda/matchers/active_record/association_matchers/source_matcher'
require 'shoulda/matchers/active_record/association_matchers/model_reflector'
require 'shoulda/matchers/active_record/association_matchers/model_reflection'
require 'shoulda/matchers/active_record/association_matchers/option_verifier'
Expand Down
6 changes: 6 additions & 0 deletions lib/shoulda/matchers/active_record/association_matcher.rb
Expand Up @@ -109,6 +109,12 @@ def counter_cache(counter_cache = true)
self
end

def source(source)
source_matcher = AssociationMatchers::SourceMatcher.new(source, name)
add_submatcher(source_matcher)
self
end

def conditions(conditions)
@options[:conditions] = conditions
self
Expand Down
@@ -0,0 +1,40 @@
module Shoulda # :nodoc:
module Matchers
module ActiveRecord # :nodoc:
module AssociationMatchers
class SourceMatcher
attr_accessor :missing_option

def initialize(source, name)
@source = source
@name = name
@missing_option = ''
end

def description
"source => #{source}"
end

def matches?(subject)
self.subject = ModelReflector.new(subject, name)

if option_verifier.correct_for_string?(:source, source)
true
else
self.missing_option = "#{name} should have #{source} as source option"
false
end
end

private

attr_accessor :subject, :source, :name

def option_verifier
@option_verifier ||= OptionVerifier.new(subject)
end
end
end
end
end
end
13 changes: 13 additions & 0 deletions spec/shoulda/matchers/active_record/association_matcher_spec.rb
Expand Up @@ -282,6 +282,19 @@ def belonging_to_parent(options = {})
matcher.failure_message_for_should.should =~ /children should have destroy dependency/
end

it 'accepts an association with a valid :source option' do
having_many_children(:source => :user).
should have_many(:children).source(:user)
end

it 'rejects an association with a bad :source option' do
matcher = have_many(:children).source(:user)

having_many_children.should_not matcher

matcher.failure_message_for_should.should =~ /children should have user as source option/
end

it 'accepts an association with a valid :order option' do
having_many_children(:order => :id).
should have_many(:children).order(:id)
Expand Down

0 comments on commit 803c06b

Please sign in to comment.