Skip to content

Commit

Permalink
Dependent qualifier now supports more values
Browse files Browse the repository at this point in the history
* :destroy
* :delete
* :nullify
* :restrict
* :restrict_with_exception (Rails 4+)
* :restrict_with_error (Rails 4+)
* true
* false
  • Loading branch information
barelyknown authored and mcmire committed Jan 20, 2015
1 parent 45f3960 commit 948757b
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 16 deletions.
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# HEAD

### Features

* Update `dependent` qualifier on association matchers to support `:destroy`,
`:delete`, `:nullify`, `:restrict`, `:restrict_with_exception`, and
`:restrict_with_error`. You can also pass `true` or `false` to assert that
the association has (or has not) been declared with *any* dependent option.
([#631])

[#631]: https://github.com/thoughtbot/shoulda-matchers/pull/631

# 2.8.0.rc1

### Deprecations
Expand Down
20 changes: 19 additions & 1 deletion lib/shoulda/matchers/active_record/association_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,24 @@ module ActiveRecord
# should belong_to(:world).dependent(:destroy)
# end
#
# To assert that *any* `:dependent` option was specified, use `true`:
#
# # RSpec
# describe Person do
# 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
# describe Person do
# it { should belong_to(:company).dependent(false) }
# end
#
# ##### counter_cache
#
# Use `counter_cache` to assert that the `:counter_cache` option was
Expand Down Expand Up @@ -993,7 +1011,7 @@ def macro_correct?
end

def macro_supports_primary_key?
macro == :belongs_to ||
macro == :belongs_to ||
([:has_many, :has_one].include?(macro) && !through?)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def description
def matches?(subject)
self.subject = ModelReflector.new(subject, name)

if option_verifier.correct_for_string?(:dependent, dependent)
if option_matches?
true
else
self.missing_option = "#{name} should have #{dependent} dependency"
self.missing_option = generate_missing_option
false
end
end
Expand All @@ -31,9 +31,30 @@ def matches?(subject)

attr_accessor :subject, :dependent, :name

private

def option_verifier
@option_verifier ||= OptionVerifier.new(subject)
end

def option_matches?
option_verifier.correct_for?(option_type, :dependent, dependent)
end

def option_type
case dependent
when true, false then :boolean
else :string
end
end

def generate_missing_option
[
"#{name} should have",
(dependent == true ? 'a' : dependent),
'dependency'
].join(' ')
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ def correct_for_relation_clause?(name, expected_value)
correct_for?(:relation_clause, name, expected_value)
end

def correct_for?(*args)
expected_value, name, type = args.reverse
if expected_value.nil?
true
else
expected_value = type_cast(
type,
expected_value_for(type, name, expected_value)
)
actual_value = type_cast(type, actual_value_for(name))
expected_value == actual_value
end
end

def actual_value_for(name)
if RELATION_OPTIONS.include?(name)
actual_value_for_relation_clause(name)
Expand All @@ -49,17 +63,6 @@ def actual_value_for(name)

attr_reader :reflector

def correct_for?(*args)
expected_value, name, type = args.reverse
if expected_value.nil?
true
else
expected_value = type_cast(type, expected_value_for(type, name, expected_value))
actual_value = type_cast(type, actual_value_for(name))
expected_value == actual_value
end
end

def type_cast(type, value)
case type
when :string, :relation_clause then value.to_s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,28 @@ def having_many_non_existent_class(model_name, assoc_name, options = {})
end

it 'accepts an association with a valid :dependent option' do
expect(having_one_detail(dependent: :destroy)).
to have_one(:detail).dependent(:destroy)
dependent_options.each do |option|
expect(having_one_detail(dependent: option)).
to have_one(:detail).dependent(option)
end
end

it 'accepts any dependent option if true' do
dependent_options.each do |option|
expect(having_one_detail(dependent: option)).
to have_one(:detail).dependent(true)
end
end

it 'rejects any dependent options if false' do
dependent_options.each do |option|
expect(having_one_detail(dependent: option)).
to_not have_one(:detail).dependent(false)
end
end

it 'accepts a nil dependent option if false' do
expect(having_one_detail).to have_one(:detail).dependent(false)
end

it 'rejects an association with a bad :dependent option' do
Expand Down Expand Up @@ -1117,4 +1137,13 @@ def define_association_with_order(model, macro, name, order, other_options={})
args << options
model.__send__(macro, name, *args)
end

def dependent_options
case Rails.version
when /\A3/
[:destroy, :delete, :nullify, :restrict]
when /\A4/
[:destroy, :delete, :nullify, :restrict_with_exception, :restrict_with_error]
end
end
end

0 comments on commit 948757b

Please sign in to comment.