redinger-rr 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +221 -0
- data/README.rdoc +343 -0
- data/Rakefile +88 -0
- data/VERSION.yml +4 -0
- data/lib/rr.rb +88 -0
- data/lib/rr/adapters/rr_methods.rb +122 -0
- data/lib/rr/adapters/rspec.rb +59 -0
- data/lib/rr/adapters/test_unit.rb +29 -0
- data/lib/rr/double.rb +152 -0
- data/lib/rr/double_definitions/child_double_definition_creator.rb +27 -0
- data/lib/rr/double_definitions/double_definition.rb +348 -0
- data/lib/rr/double_definitions/double_definition_creator.rb +167 -0
- data/lib/rr/double_definitions/double_definition_creator_proxy.rb +37 -0
- data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +15 -0
- data/lib/rr/double_definitions/strategies/implementation/proxy.rb +62 -0
- data/lib/rr/double_definitions/strategies/implementation/reimplementation.rb +14 -0
- data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
- data/lib/rr/double_definitions/strategies/scope/instance.rb +15 -0
- data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +50 -0
- data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +15 -0
- data/lib/rr/double_definitions/strategies/strategy.rb +70 -0
- data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +34 -0
- data/lib/rr/double_definitions/strategies/verification/mock.rb +44 -0
- data/lib/rr/double_definitions/strategies/verification/stub.rb +45 -0
- data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +15 -0
- data/lib/rr/double_injection.rb +180 -0
- data/lib/rr/double_matches.rb +51 -0
- data/lib/rr/errors/argument_equality_error.rb +6 -0
- data/lib/rr/errors/double_definition_error.rb +6 -0
- data/lib/rr/errors/double_not_found_error.rb +6 -0
- data/lib/rr/errors/double_order_error.rb +6 -0
- data/lib/rr/errors/rr_error.rb +20 -0
- data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
- data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
- data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
- data/lib/rr/errors/times_called_error.rb +6 -0
- data/lib/rr/expectations/any_argument_expectation.rb +21 -0
- data/lib/rr/expectations/argument_equality_expectation.rb +41 -0
- data/lib/rr/expectations/times_called_expectation.rb +57 -0
- data/lib/rr/hash_with_object_id_key.rb +44 -0
- data/lib/rr/method_dispatches/base_method_dispatch.rb +108 -0
- data/lib/rr/method_dispatches/method_dispatch.rb +61 -0
- data/lib/rr/method_dispatches/method_missing_dispatch.rb +49 -0
- data/lib/rr/proc_from_block.rb +7 -0
- data/lib/rr/recorded_calls.rb +103 -0
- data/lib/rr/space.rb +123 -0
- data/lib/rr/spy_verification.rb +48 -0
- data/lib/rr/spy_verification_proxy.rb +18 -0
- data/lib/rr/times_called_matchers/any_times_matcher.rb +18 -0
- data/lib/rr/times_called_matchers/at_least_matcher.rb +15 -0
- data/lib/rr/times_called_matchers/at_most_matcher.rb +23 -0
- data/lib/rr/times_called_matchers/integer_matcher.rb +19 -0
- data/lib/rr/times_called_matchers/non_terminal.rb +27 -0
- data/lib/rr/times_called_matchers/proc_matcher.rb +11 -0
- data/lib/rr/times_called_matchers/range_matcher.rb +21 -0
- data/lib/rr/times_called_matchers/terminal.rb +20 -0
- data/lib/rr/times_called_matchers/times_called_matcher.rb +44 -0
- data/lib/rr/wildcard_matchers.rb +158 -0
- data/lib/rr/wildcard_matchers/anything.rb +18 -0
- data/lib/rr/wildcard_matchers/boolean.rb +23 -0
- data/lib/rr/wildcard_matchers/duck_type.rb +32 -0
- data/lib/rr/wildcard_matchers/hash_including.rb +29 -0
- data/lib/rr/wildcard_matchers/is_a.rb +25 -0
- data/lib/rr/wildcard_matchers/numeric.rb +13 -0
- data/lib/rr/wildcard_matchers/range.rb +7 -0
- data/lib/rr/wildcard_matchers/regexp.rb +7 -0
- data/lib/rr/wildcard_matchers/satisfy.rb +26 -0
- data/spec/core_spec_suite.rb +19 -0
- data/spec/environment_fixture_setup.rb +7 -0
- data/spec/high_level_spec.rb +398 -0
- data/spec/proc_from_block_spec.rb +14 -0
- data/spec/rr/adapters/rr_methods_argument_matcher_spec.rb +67 -0
- data/spec/rr/adapters/rr_methods_creator_spec.rb +149 -0
- data/spec/rr/adapters/rr_methods_space_spec.rb +115 -0
- data/spec/rr/adapters/rr_methods_spec_helper.rb +11 -0
- data/spec/rr/adapters/rr_methods_times_matcher_spec.rb +17 -0
- data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +112 -0
- data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +155 -0
- data/spec/rr/double_definitions/double_definition_creator_spec.rb +502 -0
- data/spec/rr/double_definitions/double_definition_spec.rb +1165 -0
- data/spec/rr/double_injection/double_injection_spec.rb +339 -0
- data/spec/rr/double_injection/double_injection_verify_spec.rb +29 -0
- data/spec/rr/double_spec.rb +352 -0
- data/spec/rr/errors/rr_error_spec.rb +67 -0
- data/spec/rr/expectations/any_argument_expectation_spec.rb +47 -0
- data/spec/rr/expectations/anything_argument_equality_expectation_spec.rb +14 -0
- data/spec/rr/expectations/argument_equality_expectation_spec.rb +135 -0
- data/spec/rr/expectations/boolean_argument_equality_expectation_spec.rb +34 -0
- data/spec/rr/expectations/hash_including_argument_equality_expectation_spec.rb +82 -0
- data/spec/rr/expectations/hash_including_spec.rb +17 -0
- data/spec/rr/expectations/satisfy_argument_equality_expectation_spec.rb +59 -0
- data/spec/rr/expectations/satisfy_spec.rb +14 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +46 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +69 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +71 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +23 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +104 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +81 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +83 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +38 -0
- data/spec/rr/rspec/invocation_matcher_spec.rb +279 -0
- data/spec/rr/rspec/rspec_adapter_spec.rb +66 -0
- data/spec/rr/rspec/rspec_backtrace_tweaking_spec.rb +31 -0
- data/spec/rr/rspec/rspec_backtrace_tweaking_spec_fixture.rb +11 -0
- data/spec/rr/rspec/rspec_usage_spec.rb +86 -0
- data/spec/rr/space/hash_with_object_id_key_spec.rb +88 -0
- data/spec/rr/space/space_spec.rb +550 -0
- data/spec/rr/test_unit/test_helper.rb +7 -0
- data/spec/rr/test_unit/test_unit_backtrace_test.rb +36 -0
- data/spec/rr/test_unit/test_unit_integration_test.rb +57 -0
- data/spec/rr/times_called_matchers/any_times_matcher_spec.rb +47 -0
- data/spec/rr/times_called_matchers/at_least_matcher_spec.rb +55 -0
- data/spec/rr/times_called_matchers/at_most_matcher_spec.rb +70 -0
- data/spec/rr/times_called_matchers/integer_matcher_spec.rb +70 -0
- data/spec/rr/times_called_matchers/proc_matcher_spec.rb +55 -0
- data/spec/rr/times_called_matchers/range_matcher_spec.rb +76 -0
- data/spec/rr/times_called_matchers/times_called_matcher_spec.rb +118 -0
- data/spec/rr/wildcard_matchers/anything_spec.rb +24 -0
- data/spec/rr/wildcard_matchers/boolean_spec.rb +36 -0
- data/spec/rr/wildcard_matchers/duck_type_spec.rb +52 -0
- data/spec/rr/wildcard_matchers/is_a_spec.rb +32 -0
- data/spec/rr/wildcard_matchers/numeric_spec.rb +32 -0
- data/spec/rr/wildcard_matchers/range_spec.rb +35 -0
- data/spec/rr/wildcard_matchers/regexp_spec.rb +43 -0
- data/spec/rr_spec.rb +28 -0
- data/spec/rspec_spec_suite.rb +17 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/spec_suite.rb +31 -0
- data/spec/spy_verification_spec.rb +129 -0
- data/spec/test_unit_spec_suite.rb +21 -0
- metadata +193 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
module RR
|
2
|
+
class SpyVerification
|
3
|
+
def initialize(subject, method_name, args)
|
4
|
+
@subject = subject
|
5
|
+
@method_name = method_name.to_sym
|
6
|
+
set_argument_expectation_for_args(args)
|
7
|
+
@ordered = false
|
8
|
+
once
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :argument_expectation, :method_name, :times_matcher
|
12
|
+
attr_accessor :subject
|
13
|
+
|
14
|
+
include RR::DoubleDefinitions::DoubleDefinition::TimesDefinitionConstructionMethods
|
15
|
+
include RR::DoubleDefinitions::DoubleDefinition::ArgumentDefinitionConstructionMethods
|
16
|
+
|
17
|
+
def ordered
|
18
|
+
@ordered = true
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def ordered?
|
23
|
+
@ordered
|
24
|
+
end
|
25
|
+
|
26
|
+
def call
|
27
|
+
(error = RR.recorded_calls.match_error(self)) && raise(error)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_proc
|
31
|
+
lambda do
|
32
|
+
call
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
attr_writer :times_matcher
|
38
|
+
|
39
|
+
def set_argument_expectation_for_args(args)
|
40
|
+
# with_no_args and with actually set @argument_expectation
|
41
|
+
args.empty? ? with_no_args : with(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def install_method_callback(return_value_block)
|
45
|
+
# Do nothing. This is to support DefinitionConstructionMethods
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RR
|
2
|
+
class SpyVerificationProxy
|
3
|
+
instance_methods.each do |m|
|
4
|
+
unless m =~ /^_/ || m.to_s == 'object_id' || m.to_s == "instance_eval" || m.to_s == "instance_exec" || m.to_s == 'respond_to?'
|
5
|
+
alias_method "__blank_slated_#{m}", m
|
6
|
+
undef_method m
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(subject)
|
11
|
+
@subject = subject
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
SpyVerification.new(@subject, method_name, args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
3
|
+
class AnyTimesMatcher < TimesCalledMatcher
|
4
|
+
include NonTerminal
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(times_called)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def expected_times_message
|
14
|
+
"any number of times"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
3
|
+
class AtLeastMatcher < TimesCalledMatcher
|
4
|
+
include NonTerminal
|
5
|
+
|
6
|
+
def matches?(times_called)
|
7
|
+
times_called >= @times
|
8
|
+
end
|
9
|
+
|
10
|
+
def expected_times_message
|
11
|
+
"at least #{@times.inspect} times"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
3
|
+
class AtMostMatcher < TimesCalledMatcher
|
4
|
+
include Terminal
|
5
|
+
|
6
|
+
def possible_match?(times_called)
|
7
|
+
times_called <= @times
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(times_called)
|
11
|
+
times_called <= @times
|
12
|
+
end
|
13
|
+
|
14
|
+
def attempt?(times_called)
|
15
|
+
times_called < @times
|
16
|
+
end
|
17
|
+
|
18
|
+
def expected_times_message
|
19
|
+
"at most #{@times.inspect} times"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
3
|
+
class IntegerMatcher < TimesCalledMatcher
|
4
|
+
include Terminal
|
5
|
+
|
6
|
+
def possible_match?(times_called)
|
7
|
+
times_called <= @times
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(times_called)
|
11
|
+
times_called == @times
|
12
|
+
end
|
13
|
+
|
14
|
+
def attempt?(times_called)
|
15
|
+
times_called < @times
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers
|
3
|
+
# Including this module marks the TimesCalledMatcher as NonTerminal.
|
4
|
+
# Being NonTerminal means the Double will not "terminate" even when
|
5
|
+
# called infinite times.
|
6
|
+
#
|
7
|
+
# The Double that uses a NonTerminal TimesCalledMatcher will
|
8
|
+
# continue using the Double when passed the matching arguments.
|
9
|
+
# This is done by the attempt? always returning true.
|
10
|
+
#
|
11
|
+
# This is in opposition to Terminal TimesCalledMatchers, where
|
12
|
+
# attempt? will eventually return false.
|
13
|
+
module NonTerminal #:nodoc:
|
14
|
+
def terminal?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def possible_match?(times_called)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def attempt?(times_called)
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers
|
3
|
+
class RangeMatcher < TimesCalledMatcher #:nodoc:
|
4
|
+
include Terminal
|
5
|
+
|
6
|
+
def possible_match?(times_called)
|
7
|
+
return true if times_called < @times.begin
|
8
|
+
return true if @times.include?(times_called)
|
9
|
+
return false
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(times_called)
|
13
|
+
@times.include?(times_called)
|
14
|
+
end
|
15
|
+
|
16
|
+
def attempt?(times_called)
|
17
|
+
possible_match?(times_called)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers
|
3
|
+
# Including this module marks the TimesCalledMatcher as Terminal.
|
4
|
+
# Being Terminal the Double will "terminate" when times called is
|
5
|
+
# finite.
|
6
|
+
#
|
7
|
+
# The Double that uses a Terminal TimesCalledMatcher will
|
8
|
+
# eventually be passed over to the next Double when passed
|
9
|
+
# the matching arguments enough times. This is done by the attempt?
|
10
|
+
# method returning false when executed a finite number of times.
|
11
|
+
#
|
12
|
+
# This is in opposition to NonTerminal TimesCalledMatchers, where
|
13
|
+
# attempt? will always return true.
|
14
|
+
module Terminal #:nodoc:
|
15
|
+
def terminal?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RR
|
2
|
+
module TimesCalledMatchers
|
3
|
+
class TimesCalledMatcher #:nodoc:
|
4
|
+
class << self
|
5
|
+
def create(value)
|
6
|
+
return value if value.is_a?(TimesCalledMatcher)
|
7
|
+
return IntegerMatcher.new(value) if value.is_a?(Integer)
|
8
|
+
return RangeMatcher.new(value) if value.is_a?(Range )
|
9
|
+
return ProcMatcher.new(value) if value.is_a?(Proc)
|
10
|
+
raise ArgumentError, "There is no TimesCalledMatcher for #{value.inspect}."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :times
|
15
|
+
|
16
|
+
def initialize(times)
|
17
|
+
@times = times
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(times_called)
|
21
|
+
end
|
22
|
+
|
23
|
+
def attempt?(times_called)
|
24
|
+
end
|
25
|
+
|
26
|
+
def error_message(times_called)
|
27
|
+
"Called #{times_called.inspect} #{pluralized_time(times_called)}.\nExpected #{expected_times_message}."
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
self.class == other.class && self.times == other.times
|
32
|
+
end
|
33
|
+
|
34
|
+
def expected_times_message
|
35
|
+
"#{@times.inspect} times"
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
def pluralized_time(times_called)
|
40
|
+
(times_called == 1) ? "time" : "times"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= Writing your own custom wildcard matchers.
|
4
|
+
Writing new wildcard matchers is not too difficult. If you've ever written
|
5
|
+
a custom expectation in RSpec, the implementation is very similar.
|
6
|
+
|
7
|
+
As an example, let's say that you want a matcher that will match any number
|
8
|
+
divisible by a certain integer. In use, it might look like this:
|
9
|
+
|
10
|
+
# Will pass if BananaGrabber#bunch_bananas is called with an integer
|
11
|
+
# divisible by 5.
|
12
|
+
|
13
|
+
mock(BananaGrabber).bunch_bananas(divisible_by(5))
|
14
|
+
|
15
|
+
To implement this, we need a class RR::WildcardMatchers::DivisibleBy with
|
16
|
+
these instance methods:
|
17
|
+
|
18
|
+
* ==(other)
|
19
|
+
* eql?(other) (usually aliased to #==)
|
20
|
+
* inspect
|
21
|
+
* wildcard_match?(other)
|
22
|
+
|
23
|
+
and optionally, a sensible initialize method. Let's look at each of these.
|
24
|
+
|
25
|
+
=== .initialize
|
26
|
+
|
27
|
+
Most custom wildcard matchers will want to define initialize to store
|
28
|
+
some information about just what should be matched. DivisibleBy#initialize
|
29
|
+
might look like this:
|
30
|
+
|
31
|
+
class RR::WildcardMatchers::DivisibleBy
|
32
|
+
def initialize(divisor)
|
33
|
+
@expected_divisor = divisor
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
=== #==(other)
|
38
|
+
DivisibleBy#==(other) should return true if other is a wildcard matcher that
|
39
|
+
matches the same things as self, so a natural way to write DivisibleBy#== is:
|
40
|
+
|
41
|
+
|
42
|
+
class RR::WildcardMatchers::DivisibleBy
|
43
|
+
def ==(other)
|
44
|
+
# Ensure that other is actually a DivisibleBy
|
45
|
+
return false unless other.is_a?(self.class)
|
46
|
+
|
47
|
+
# Does other expect to match the same divisor we do?
|
48
|
+
self.expected_divisor = other.expected_divisor
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Note that this implementation of #== assumes that we've also declared
|
53
|
+
attr_reader :expected_divisor
|
54
|
+
|
55
|
+
=== #inspect
|
56
|
+
|
57
|
+
Technically we don't have to declare DivisibleBy#inspect, since inspect is
|
58
|
+
defined for every object already. But putting a helpful message in inspect
|
59
|
+
will make test failures much clearer, and it only takes about two seconds to
|
60
|
+
write it, so let's be nice and do so:
|
61
|
+
|
62
|
+
class RR::WildcardMatchers::DivisibleBy
|
63
|
+
def inspect
|
64
|
+
"integer divisible by #{expected.divisor}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Now if we run the example from above:
|
69
|
+
|
70
|
+
mock(BananaGrabber).bunch_bananas(divisible_by(5))
|
71
|
+
|
72
|
+
and it fails, we get a helpful message saying
|
73
|
+
|
74
|
+
bunch_bananas(integer divisible by 5)
|
75
|
+
Called 0 times.
|
76
|
+
Expected 1 times.
|
77
|
+
|
78
|
+
=== #wildcard_matches?(other)
|
79
|
+
|
80
|
+
wildcard_matches? is the method that actually checks the argument against the
|
81
|
+
expectation. It should return true if other is considered to match,
|
82
|
+
false otherwise. In the case of DivisibleBy, wildcard_matches? reads:
|
83
|
+
|
84
|
+
class RR::WildcardMatchers::DivisibleBy
|
85
|
+
def wildcard_matches?(other)
|
86
|
+
# If other isn't a number, how can it be divisible by anything?
|
87
|
+
return false unless other.is_a?(Numeric)
|
88
|
+
|
89
|
+
# If other is in fact divisible by expected_divisor, then
|
90
|
+
# other modulo expected_divisor should be 0.
|
91
|
+
|
92
|
+
other % expected_divisor == 0
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
=== A finishing touch: wrapping it neatly
|
97
|
+
|
98
|
+
We could stop here if we were willing to resign ourselves to using
|
99
|
+
DivisibleBy this way:
|
100
|
+
|
101
|
+
mock(BananaGrabber).bunch_bananas(DivisibleBy.new(5))
|
102
|
+
|
103
|
+
But that's less expressive than the original:
|
104
|
+
|
105
|
+
mock(BananaGrabber).bunch_bananas(divisible_by(5))
|
106
|
+
|
107
|
+
To be able to use the convenient divisible_by matcher rather than the uglier
|
108
|
+
DivisibleBy.new version, re-open the module RR::Adapters::RRMethods and
|
109
|
+
define divisible_by there as a simple wrapper around DivisibleBy.new:
|
110
|
+
|
111
|
+
module RR::Adapters::RRMethods
|
112
|
+
def divisible_by(expected_divisor)
|
113
|
+
RR::WildcardMatchers::DivisibleBy.new(expected_divisor)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
== Recap
|
118
|
+
|
119
|
+
Here's all the code for DivisibleBy in one place for easy reference:
|
120
|
+
|
121
|
+
class RR::WildcardMatchers::DivisibleBy
|
122
|
+
def initialize(divisor)
|
123
|
+
@expected_divisor = divisor
|
124
|
+
end
|
125
|
+
|
126
|
+
def ==(other)
|
127
|
+
# Ensure that other is actually a DivisibleBy
|
128
|
+
return false unless other.is_a?(self.class)
|
129
|
+
|
130
|
+
# Does other expect to match the same divisor we do?
|
131
|
+
self.expected_divisor = other.expected_divisor
|
132
|
+
end
|
133
|
+
|
134
|
+
def inspect
|
135
|
+
"integer divisible by #{expected.divisor}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def wildcard_matches?(other)
|
139
|
+
# If other isn't a number, how can it be divisible by anything?
|
140
|
+
return false unless other.is_a?(Numeric)
|
141
|
+
|
142
|
+
# If other is in fact divisible by expected_divisor, then
|
143
|
+
# other modulo expected_divisor should be 0.
|
144
|
+
|
145
|
+
other % expected_divisor == 0
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
module RR::Adapters::RRMethods
|
150
|
+
def divisible_by(expected_divisor)
|
151
|
+
RR::WildcardMatchers::DivisibleBy.new(expected_divisor)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
=end
|
156
|
+
|
157
|
+
module RR::WildcardMatchers
|
158
|
+
end
|