2012-04-11 18:33:21 +00:00
|
|
|
module Shoulda
|
|
|
|
module Matchers
|
|
|
|
module ActiveRecord
|
|
|
|
# Ensures that the model can accept nested attributes for the specified
|
|
|
|
# association.
|
|
|
|
#
|
|
|
|
# Options:
|
|
|
|
# * <tt>allow_destroy</tt> - Whether or not to allow destroy
|
|
|
|
# * <tt>limit</tt> - Max number of nested attributes
|
|
|
|
# * <tt>update_only</tt> - Only allow updates
|
|
|
|
#
|
|
|
|
# Example:
|
|
|
|
# it { should accept_nested_attributes_for(:friends) }
|
|
|
|
# it { should accept_nested_attributes_for(:friends).
|
|
|
|
# allow_destroy(true).
|
|
|
|
# limit(4) }
|
|
|
|
# it { should accept_nested_attributes_for(:friends).
|
|
|
|
# update_only(true) }
|
|
|
|
#
|
|
|
|
def accept_nested_attributes_for(name)
|
|
|
|
AcceptNestedAttributesForMatcher.new(name)
|
|
|
|
end
|
|
|
|
|
|
|
|
class AcceptNestedAttributesForMatcher
|
|
|
|
def initialize(name)
|
|
|
|
@name = name
|
2012-04-23 21:37:40 +00:00
|
|
|
@options = {}
|
2012-04-11 18:33:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def allow_destroy(allow_destroy)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options[:allow_destroy] = allow_destroy
|
2012-04-11 18:33:21 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def limit(limit)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options[:limit] = limit
|
2012-04-11 18:33:21 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def update_only(update_only)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options[:update_only] = update_only
|
2012-04-11 18:33:21 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def matches?(subject)
|
|
|
|
@subject = subject
|
|
|
|
exists? &&
|
|
|
|
allow_destroy_correct? &&
|
|
|
|
limit_correct? &&
|
|
|
|
update_only_correct?
|
|
|
|
end
|
|
|
|
|
2013-01-08 17:29:01 +00:00
|
|
|
def failure_message_for_should
|
2012-04-11 18:33:21 +00:00
|
|
|
"Expected #{expectation} (#{@problem})"
|
|
|
|
end
|
|
|
|
|
2013-01-08 17:29:01 +00:00
|
|
|
def failure_message_for_should_not
|
2012-04-11 18:33:21 +00:00
|
|
|
"Did not expect #{expectation}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def description
|
|
|
|
description = "accepts_nested_attributes_for :#{@name}"
|
2012-05-11 15:37:32 +00:00
|
|
|
if @options.key?(:allow_destroy)
|
|
|
|
description += " allow_destroy => #{@options[:allow_destroy]}"
|
|
|
|
end
|
|
|
|
if @options.key?(:limit)
|
|
|
|
description += " limit => #{@options[:limit]}"
|
|
|
|
end
|
|
|
|
if @options.key?(:update_only)
|
|
|
|
description += " update_only => #{@options[:update_only]}"
|
|
|
|
end
|
2012-04-11 18:33:21 +00:00
|
|
|
description
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def exists?
|
|
|
|
if config
|
|
|
|
true
|
|
|
|
else
|
2012-12-20 05:04:27 +00:00
|
|
|
@problem = 'is not declared'
|
2012-04-11 18:33:21 +00:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def allow_destroy_correct?
|
2012-05-11 15:37:45 +00:00
|
|
|
failure_message = "#{should_or_should_not(@options[:allow_destroy])} allow destroy"
|
|
|
|
verify_option_is_correct(:allow_destroy, failure_message)
|
2012-04-11 18:33:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def limit_correct?
|
2012-05-11 15:37:45 +00:00
|
|
|
failure_message = "limit should be #{@options[:limit]}, got #{config[:limit]}"
|
|
|
|
verify_option_is_correct(:limit, failure_message)
|
2012-04-11 18:33:21 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def update_only_correct?
|
2012-05-11 15:37:45 +00:00
|
|
|
failure_message = "#{should_or_should_not(@options[:update_only])} be update only"
|
|
|
|
verify_option_is_correct(:update_only, failure_message)
|
|
|
|
end
|
|
|
|
|
|
|
|
def verify_option_is_correct(option, failure_message)
|
|
|
|
if @options.key?(option)
|
|
|
|
if @options[option] == config[option]
|
2012-04-23 21:37:40 +00:00
|
|
|
true
|
|
|
|
else
|
2012-05-11 15:37:45 +00:00
|
|
|
@problem = failure_message
|
2012-04-23 21:37:40 +00:00
|
|
|
false
|
|
|
|
end
|
2012-04-11 18:33:21 +00:00
|
|
|
else
|
2012-04-23 21:37:40 +00:00
|
|
|
true
|
2012-04-11 18:33:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def config
|
|
|
|
model_config[@name]
|
|
|
|
end
|
|
|
|
|
|
|
|
def model_config
|
|
|
|
model_class.nested_attributes_options
|
|
|
|
end
|
|
|
|
|
|
|
|
def model_class
|
|
|
|
@subject.class
|
|
|
|
end
|
|
|
|
|
|
|
|
def expectation
|
|
|
|
"#{model_class.name} to accept nested attributes for #{@name}"
|
|
|
|
end
|
2012-05-11 15:37:45 +00:00
|
|
|
|
|
|
|
def should_or_should_not(value)
|
|
|
|
if value
|
2012-12-20 05:04:27 +00:00
|
|
|
'should'
|
2012-05-11 15:37:45 +00:00
|
|
|
else
|
2012-12-20 05:04:27 +00:00
|
|
|
'should not'
|
2012-05-11 15:37:45 +00:00
|
|
|
end
|
|
|
|
end
|
2012-04-11 18:33:21 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|