This commit is contained in:
Elliot Winkler 2014-12-13 17:50:20 -05:00
parent 14d2d73da9
commit 6ebcce6ce0
8 changed files with 208 additions and 30 deletions

View File

@ -244,6 +244,10 @@ module Shoulda
:context, :value, :matched_error, :after_setting_value_callback
def set_value(value)
if attribute_to_set.nil?
raise "No attribute has been set!"
end
instance.__send__("#{attribute_to_set}=", value)
after_setting_value_callback.call
end

View File

@ -288,38 +288,35 @@ module Shoulda
delegate :failure_message, :failure_message_when_negated,
:failure_message_for_should, :failure_message_for_should_not,
to: :submatchers
to: :matcher_collection
def initialize(attribute)
@attribute = attribute
@submatchers = MatcherCollection.new
@matcher_collection = MatcherCollection.new
@diff_to_compare = DEFAULT_DIFF_TO_COMPARE
add_disallow_value_matcher
submatchers.configure do |matcher|
matcher.for(attribute)
end
end
def only_integer
add_submatcher(Numericality::OnlyIntegerMatcher)
add_submatcher(Numericality::OnlyIntegerMatcher, attribute)
self
end
def allow_nil
add_submatcher(AllowValueMatcher, nil) do |matcher|
matcher.for(attribute)
matcher.with_message(:not_a_number)
end
self
end
def odd
add_submatcher(Numericality::OddNumberMatcher)
add_submatcher(Numericality::OddNumberMatcher, attribute)
self
end
def even
add_submatcher(Numericality::EvenNumberMatcher)
add_submatcher(Numericality::EvenNumberMatcher, attribute)
self
end
@ -349,13 +346,13 @@ module Shoulda
end
def with_message(message)
submatchers.configure { |matcher| matcher.with_message(message) }
matcher_collection.configure(:with_message, message)
self
end
def matches?(subject)
@subject = subject
submatchers.matches?(subject)
matcher_collection.matches?(subject)
end
def description
@ -364,22 +361,25 @@ module Shoulda
protected
attr_reader :attribute
attr_reader :attribute, :matcher_collection
private
def add_submatcher(klass, *args, &block)
submatchers.add(klass, *args, &block)
matcher_collection.add(klass, *args, &block)
end
def add_disallow_value_matcher
add_submatcher(DisallowValueMatcher, NON_NUMERIC_VALUE) do |matcher|
matcher.for(attribute)
matcher.with_message(:not_a_number)
end
end
def add_comparison_submatcher(value, operator)
add_submatcher(Numericality::ComparisonMatcher, self, value, operator)
add_submatcher(Numericality::ComparisonMatcher, self, value, operator) do |matcher|
matcher.for(attribute)
end
end
def diff_to_compare
@ -387,7 +387,7 @@ module Shoulda
end
def possible_diff_to_compares
[DEFAULT_DIFF_TO_COMPARE] + submatchers.invoke(:diff_to_compare)
[DEFAULT_DIFF_TO_COMPARE] + matcher_collection.invoke(:diff_to_compare)
end
def allowed_types
@ -399,7 +399,7 @@ module Shoulda
end
def submatcher_allowed_types
submatchers.invoke(:allowed_type)
matcher_collection.invoke(:allowed_type)
end
def comparison_descriptions
@ -411,7 +411,7 @@ module Shoulda
end
def submatcher_comparison_descriptions
submatchers.invoke(:comparison_description)
matcher_collection.invoke(:comparison_description)
end
end
end

View File

@ -1,6 +1,6 @@
module Shoulda
module Matchers
module MatcherCollection
class MatcherCollection
attr_reader :failure_message, :failure_message_when_negated
alias_method :failure_message_for_should,
:failure_message
@ -16,8 +16,18 @@ module Shoulda
matchers_to_create << [klass, args, block]
end
def configure(&block)
matcher_configurations << block
def configure(*args, &block)
puts 'configure'
# if block
# matcher_configurations << block
# else
# # configure do |matcher|
# # matcher.__send__(*args)
# # end
# matcher_configurations << proc do |matcher|
# matcher.__send__(*args)
# end
# end
end
def invoke(method_name)
@ -28,37 +38,52 @@ module Shoulda
def matches?(subject)
@subject = subject
@failure_message = first_failing_matcher.failure_message
if first_failing_matcher
@failure_message = first_failing_matcher.failure_message
false
else
true
end
end
def does_not_match?(subject)
@subject = subject
@failure_message_when_negated =
first_passing_matcher.failure_message_when_negated
if first_passing_matcher
@failure_message_when_negated =
first_passing_matcher.failure_message_when_negated
false
else
true
end
end
protected
attr_reader :matchers_to_create, :matcher_configurations, :subject
private
def matchers
@_matchers ||= matchers_to_create.map do |klass, args, block|
klass.new(*args).tap do |matcher|
matcher.call(block) if block
matcher_configurations.each do |block|
block.call(matcher)
end
# block.call(matcher) if block
# matcher_configurations.each do |block|
# block.call(matcher)
# end
end
end
end
def first_failing_matcher
matchers.detect do |matcher|
@_first_failing_matcher ||= matchers.detect do |matcher|
!matcher.matches?(subject)
end
end
def first_passing_matcher
matchers.detect do |matcher|
@_first_passing_matcher ||= matchers.detect do |matcher|
matcher.matches?(subject)
end
end

View File

@ -0,0 +1,48 @@
module UnitTests
module Matchers
def alias_instance_method(old_method_name)
AliasInstanceMethodMatcher.new(old_method_name)
end
class AliasInstanceMethodMatcher
def initialize(old_method_name)
@old_method_name = old_method_name
end
def to(new_method_name)
@new_method_name = new_method_name
self
end
def matches?(klass)
@klass = klass
instance_method(old_method_name) == instance_method(new_method_name)
end
def description
"should #{expectation}"
end
def failure_message
"Expected #{klass} to #{expectation}"
end
alias_method :failure_message_for_should, :failure_message
def failure_message_when_negated
"Expected #{klass} not to #{expectation}"
end
alias_method :failure_message_for_should_not,
:failure_message_when_negated
protected
attr_reader :old_method_name, :new_method_name, :klass
private
def expectation
"alias instance method ##{old_method_name} to ##{new_method_name}"
end
end
end
end

View File

@ -0,0 +1,71 @@
module UnitTests
module Matchers
def have_instance_method(method_name)
HaveInstanceMethodMatcher.new(method_name)
end
class HaveInstanceMethodMatcher
def initialize(method_name)
@method_name = method_name
end
def with_arity(arity)
@arity = arity
self
end
def matches?(klass)
@klass = klass
instance_method_defined? && instance_method_has_arity?
end
def description
"should #{expectation}"
end
def failure_message
"Expected #{klass} to #{expectation}"
end
alias_method :failure_message_for_should, :failure_message
def failure_message_when_negated
"Expected #{klass} not to #{expectation}"
end
alias_method :failure_message_for_should_not,
:failure_message_when_negated
protected
attr_reader :method_name, :arity, :klass
private
def arity_specified?
defined?(@arity)
end
def instance_method_defined?
method_name == :initialize ||
klass.instance_methods.include?(method_name)
end
def instance_method_has_arity?
!arity_specified? || instance_method.arity == arity
end
def instance_method
klass.instance_method(method_name)
end
def expectation
string = "have instance method ##{method_name}"
if arity_specified?
string << " with arity of #{arity}"
end
string
end
end
end
end

View File

@ -0,0 +1,18 @@
shared_examples_for 'a matcher' do
subject { described_class }
it { should have_instance_method(:description).with_arity(0) }
it { should have_instance_method(:matches?).with_arity(1) }
it { should have_instance_method(:failure_message).with_arity(0) }
it { should have_instance_method(:failure_message).with_arity(0) }
it do
should alias_instance_method(:failure_message).
to(:failure_message_for_should)
end
it do
should alias_instance_method(:failure_message_when_negated).
to(:failure_message_for_should_not)
end
end

View File

@ -0,0 +1,8 @@
shared_examples_for 'a validation matcher' do
subject { described_class }
it { should have_instance_method(:initialize).with_arity(1) }
it { should have_instance_method(:with_message).with_arity(1) }
# it { should have_instance_method(:on).with_arity(1) }
# it { should have_instance_method(:strict).with_arity(0) }
end

View File

@ -1,6 +1,10 @@
require 'unit_spec_helper'
describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher do
subject { described_class.new(:attribute) }
it_behaves_like 'a matcher'
it_behaves_like 'a validation matcher'
context 'with a model with a numericality validation' do
it 'accepts' do