thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_record/accept_nested_attributes_fo...

218 lines
5.8 KiB
Ruby

module Shoulda
module Matchers
module ActiveRecord
# The `accept_nested_attributes_for` matcher tests usage of the
# `accepts_nested_attributes_for` macro.
#
# class Car < ActiveRecord::Base
# accepts_nested_attributes_for :doors
# end
#
# # RSpec
# RSpec.describe Car, type: :model do
# it { should accept_nested_attributes_for(:doors) }
# end
#
# # Minitest (Shoulda) (using Shoulda)
# class CarTest < ActiveSupport::TestCase
# should accept_nested_attributes_for(:doors)
# end
#
# #### Qualifiers
#
# ##### allow_destroy
#
# Use `allow_destroy` to assert that the `:allow_destroy` option was
# specified.
#
# class Car < ActiveRecord::Base
# accepts_nested_attributes_for :mirrors, allow_destroy: true
# end
#
# # RSpec
# RSpec.describe Car, type: :model do
# it do
# should accept_nested_attributes_for(:mirrors).
# allow_destroy(true)
# end
# end
#
# # Minitest (Shoulda)
# class CarTest < ActiveSupport::TestCase
# should accept_nested_attributes_for(:mirrors).
# allow_destroy(true)
# end
#
# ##### limit
#
# Use `limit` to assert that the `:limit` option was specified.
#
# class Car < ActiveRecord::Base
# accepts_nested_attributes_for :windows, limit: 3
# end
#
# # RSpec
# RSpec.describe Car, type: :model do
# it do
# should accept_nested_attributes_for(:windows).
# limit(3)
# end
# end
#
# # Minitest (Shoulda)
# class CarTest < ActiveSupport::TestCase
# should accept_nested_attributes_for(:windows).
# limit(3)
# end
#
# ##### update_only
#
# Use `update_only` to assert that the `:update_only` option was
# specified.
#
# class Car < ActiveRecord::Base
# accepts_nested_attributes_for :engine, update_only: true
# end
#
# # RSpec
# RSpec.describe Car, type: :model do
# it do
# should accept_nested_attributes_for(:engine).
# update_only(true)
# end
# end
#
# # Minitest (Shoulda)
# class CarTest < ActiveSupport::TestCase
# should accept_nested_attributes_for(:engine).
# update_only(true)
# end
#
# @return [AcceptNestedAttributesForMatcher]
#
def accept_nested_attributes_for(name)
AcceptNestedAttributesForMatcher.new(name)
end
# @private
class AcceptNestedAttributesForMatcher
def initialize(name)
@name = name
@options = {}
end
def allow_destroy(allow_destroy)
@options[:allow_destroy] = allow_destroy
self
end
def limit(limit)
@options[:limit] = limit
self
end
def update_only(update_only)
@options[:update_only] = update_only
self
end
def matches?(subject)
@subject = subject
exists? &&
allow_destroy_correct? &&
limit_correct? &&
update_only_correct?
end
def failure_message
"Expected #{expectation} (#{@problem})"
end
def failure_message_when_negated
"Did not expect #{expectation}"
end
def description
description = "accepts_nested_attributes_for :#{@name}"
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
description
end
protected
def exists?
if config
true
else
@problem = 'is not declared'
false
end
end
def allow_destroy_correct?
failure_message = "#{should_or_should_not(@options[:allow_destroy])}"\
' allow destroy'
verify_option_is_correct(:allow_destroy, failure_message)
end
def limit_correct?
failure_message = "limit should be #{@options[:limit]},"\
" got #{config[:limit]}"
verify_option_is_correct(:limit, failure_message)
end
def update_only_correct?
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]
true
else
@problem = failure_message
false
end
else
true
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
def should_or_should_not(value)
if value
'should'
else
'should not'
end
end
end
end
end
end