lib/rspec/rails/matchers/be_a_new.rb
module RSpec
module Rails
module Matchers
# @api private
#
# Matcher class for `be_a_new`. Should not be instantiated directly.
#
# @see RSpec::Rails::Matchers#be_a_new
class BeANew < RSpec::Rails::Matchers::BaseMatcher
# @private
def initialize(expected)
@expected = expected
end
# @private
def matches?(actual)
@actual = actual
actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
end
# @api public
# @see RSpec::Rails::Matchers#be_a_new
def with(expected_attributes)
attributes.merge!(expected_attributes)
self
end
# @private
def failure_message
[].tap do |message|
unless actual.is_a?(expected) && actual.new_record?
message << "expected #{actual.inspect} to be a new #{expected.inspect}"
end
unless attributes_match?(actual)
describe_unmatched_attributes = surface_descriptions_in(unmatched_attributes)
if unmatched_attributes.size > 1
message << "attributes #{describe_unmatched_attributes.inspect} were not set on #{actual.inspect}"
else
message << "attribute #{describe_unmatched_attributes.inspect} was not set on #{actual.inspect}"
end
end
end.join(' and ')
end
private
def attributes
@attributes ||= {}
end
def attributes_match?(actual)
attributes.stringify_keys.all? do |key, value|
values_match?(value, actual.attributes[key])
end
end
def unmatched_attributes
attributes.stringify_keys.reject do |key, value|
values_match?(value, actual.attributes[key])
end
end
end
# @api public
# Passes if actual is an instance of `model_class` and returns `true` for
# `new_record?`. Typically used to specify instance variables assigned to
# views by controller actions
#
# Use the `with` method to specify the specific attributes to match on the
# new record.
#
# @example
# get :new
# assigns(:thing).should be_a_new(Thing)
#
# post :create, :thing => { :name => "Illegal Value" }
# assigns(:thing).should be_a_new(Thing).with(:name => nil)
def be_a_new(model_class)
BeANew.new(model_class)
end
end
end
end