matchi 3.3.2 → 4.1.0

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.
@@ -1,49 +1,119 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Matchi
4
- # *Type/class* matcher.
4
+ # *Type/class* matcher with enhanced class checking.
5
+ #
6
+ # This matcher aims to provide a more reliable way to check if an object is an exact
7
+ # instance of a specific class (not a subclass). While not foolproof, it uses a more
8
+ # robust method to get the actual class of an object that helps resist common
9
+ # attempts at type checking manipulation.
10
+ #
11
+ # @example Basic usage
12
+ # require "matchi/be_an_instance_of"
13
+ #
14
+ # matcher = Matchi::BeAnInstanceOf.new(String)
15
+ # matcher.match? { "foo" } # => true
16
+ # matcher.match? { :foo } # => false
17
+ #
18
+ # @example Enhanced class checking in practice
19
+ # # Consider a class that attempts to masquerade as String by overriding
20
+ # # common type checking methods:
21
+ # class MaliciousString
22
+ # def class
23
+ # ::String
24
+ # end
25
+ #
26
+ # def instance_of?(klass)
27
+ # self.class == klass
28
+ # end
29
+ #
30
+ # def is_a?(klass)
31
+ # "".is_a?(klass) # Delegates to a real String
32
+ # end
33
+ #
34
+ # def kind_of?(klass)
35
+ # is_a?(klass) # Maintains Ruby's kind_of? alias for is_a?
36
+ # end
37
+ # end
38
+ #
39
+ # obj = MaliciousString.new
40
+ # obj.class # => String
41
+ # obj.is_a?(String) # => true
42
+ # obj.kind_of?(String) # => true
43
+ # obj.instance_of?(String) # => true
44
+ #
45
+ # # Using our enhanced checking approach:
46
+ # matcher = Matchi::BeAnInstanceOf.new(String)
47
+ # matcher.match? { obj } # => false
5
48
  class BeAnInstanceOf
6
- # @return [String] The expected class name.
7
- attr_reader :expected
8
-
9
49
  # Initialize the matcher with (the name of) a class or module.
10
50
  #
11
51
  # @example
12
52
  # require "matchi/be_an_instance_of"
13
53
  #
14
54
  # Matchi::BeAnInstanceOf.new(String)
55
+ # Matchi::BeAnInstanceOf.new("String")
56
+ # Matchi::BeAnInstanceOf.new(:String)
15
57
  #
16
- # @param expected [Class, #to_s] The expected class name.
58
+ # @param expected [Class, #to_s] The expected class name
59
+ # @raise [ArgumentError] if the class name doesn't start with an uppercase letter
17
60
  def initialize(expected)
18
61
  @expected = String(expected)
62
+ return if /\A[A-Z]/.match?(@expected)
63
+
64
+ raise ::ArgumentError,
65
+ "expected must start with an uppercase letter (got: #{@expected})"
19
66
  end
20
67
 
21
- # Boolean comparison between the class of the actual value and the
22
- # expected class.
68
+ # Securely checks if the yielded object is an instance of the expected class.
23
69
  #
24
- # @example
25
- # require "matchi/be_an_instance_of"
70
+ # This method uses a specific Ruby reflection technique to get the true class of
71
+ # an object, bypassing potential method overrides:
26
72
  #
27
- # matcher = Matchi::BeAnInstanceOf.new(String)
73
+ # 1. ::Object.instance_method(:class) retrieves the original, unoverridden 'class'
74
+ # method from the Object class
75
+ # 2. .bind_call(obj) binds this original method to our object and calls it,
76
+ # ensuring we get the real class regardless of method overrides
28
77
  #
29
- # matcher.expected # => "String"
30
- # matcher.matches? { "foo" } # => true
78
+ # This approach is more reliable than obj.class because it uses Ruby's method
79
+ # binding mechanism to call the original implementation directly. While not
80
+ # completely foolproof, it provides better protection against type check spoofing
81
+ # than using regular method calls which can be overridden.
31
82
  #
32
- # @yieldreturn [#class] the actual value to compare to the expected one.
83
+ # @example Basic class check
84
+ # matcher = Matchi::BeAnInstanceOf.new(String)
85
+ # matcher.match? { "test" } # => true
86
+ # matcher.match? { StringIO.new } # => false
33
87
  #
34
- # @return [Boolean] Comparison between actual and expected values.
35
- def matches?
36
- self.class.const_get(expected).equal?(yield.class)
37
- end
88
+ # @see https://ruby-doc.org/core/Method.html#method-i-bind_call
89
+ # @see https://ruby-doc.org/core/UnboundMethod.html
90
+ #
91
+ # @yieldreturn [Object] the actual value to check
92
+ # @return [Boolean] true if the object's actual class is exactly the expected class
93
+ # @raise [ArgumentError] if no block is provided
94
+ def match?
95
+ raise ::ArgumentError, "a block must be provided" unless block_given?
38
96
 
39
- # A string containing a human-readable representation of the matcher.
40
- def inspect
41
- "#{self.class}(#{expected})"
97
+ actual_class = ::Object.instance_method(:class).bind_call(yield)
98
+ expected_class == actual_class
42
99
  end
43
100
 
44
101
  # Returns a string representing the matcher.
102
+ #
103
+ # @return [String] a human-readable description of the matcher
45
104
  def to_s
46
- "be an instance of #{expected}"
105
+ "be an instance of #{@expected}"
106
+ end
107
+
108
+ private
109
+
110
+ # Resolves the expected class name to an actual Class object.
111
+ # This method handles both string and symbol class names through constant resolution.
112
+ #
113
+ # @return [Class] the resolved class
114
+ # @raise [NameError] if the class doesn't exist
115
+ def expected_class
116
+ ::Object.const_get(@expected)
47
117
  end
48
118
  end
49
119
  end
@@ -4,9 +4,6 @@ module Matchi
4
4
  class BeWithin
5
5
  # *BeWithin of* matcher.
6
6
  class Of
7
- # @return [Numeric] An expected value.
8
- attr_reader :expected
9
-
10
7
  # Initialize the matcher with a delta and an expected value.
11
8
  #
12
9
  # @example
@@ -17,6 +14,10 @@ module Matchi
17
14
  # @param delta [Numeric] The accepted variation of the actual value.
18
15
  # @param expected [Numeric] The expected value.
19
16
  def initialize(delta, expected)
17
+ raise ::ArgumentError, "delta must be a Numeric" unless delta.is_a?(::Numeric)
18
+ raise ::ArgumentError, "expected must be a Numeric" unless expected.is_a?(::Numeric)
19
+ raise ::ArgumentError, "delta must be non-negative" if delta.negative?
20
+
20
21
  @delta = delta
21
22
  @expected = expected
22
23
  end
@@ -28,25 +29,22 @@ module Matchi
28
29
  # require "matchi/be_within/of"
29
30
  #
30
31
  # matcher = Matchi::BeWithin::Of.new(1, 41)
31
- #
32
- # matcher.expected # => 41
33
- # matcher.matches? { 42 } # => true
32
+ # matcher.match? { 42 } # => true
34
33
  #
35
34
  # @yieldreturn [Numeric] The block of code to execute.
36
35
  #
37
36
  # @return [Boolean] Comparison between the actual and the expected values.
38
- def matches?
39
- (expected - yield).abs <= @delta
40
- end
37
+ def match?
38
+ raise ::ArgumentError, "a block must be provided" unless block_given?
41
39
 
42
- # A string containing a human-readable representation of the matcher.
43
- def inspect
44
- "#{self.class}(#{@delta}, #{expected})"
40
+ (@expected - yield).abs <= @delta
45
41
  end
46
42
 
47
43
  # Returns a string representing the matcher.
44
+ #
45
+ # @return [String] a human-readable description of the matcher
48
46
  def to_s
49
- "be within #{@delta} of #{expected}"
47
+ "be within #{@delta} of #{@expected}"
50
48
  end
51
49
  end
52
50
  end
@@ -14,6 +14,9 @@ module Matchi
14
14
  #
15
15
  # @param delta [Numeric] A numeric value.
16
16
  def initialize(delta)
17
+ raise ::ArgumentError, "delta must be a Numeric" unless delta.is_a?(::Numeric)
18
+ raise ::ArgumentError, "delta must be non-negative" if delta.negative?
19
+
17
20
  @delta = delta
18
21
  end
19
22
 
@@ -27,7 +30,7 @@ module Matchi
27
30
  #
28
31
  # @param expected [Numeric] The expected value.
29
32
  #
30
- # @return [#matches?] A *be_within of* matcher.
33
+ # @return [#match?] A *be_within of* matcher.
31
34
  def of(expected)
32
35
  Of.new(@delta, expected)
33
36
  end
@@ -4,9 +4,6 @@ module Matchi
4
4
  class Change
5
5
  # *Change by* matcher.
6
6
  class By
7
- # @return [#object_id] An expected delta.
8
- attr_reader :expected
9
-
10
7
  # Initialize the matcher with an object and a block.
11
8
  #
12
9
  # @example
@@ -20,6 +17,9 @@ module Matchi
20
17
  # @param state [Proc] A block of code to execute to get the
21
18
  # state of the object.
22
19
  def initialize(expected, &state)
20
+ raise ::ArgumentError, "expected must be a Numeric" unless expected.is_a?(::Numeric)
21
+ raise ::ArgumentError, "a block must be provided" unless block_given?
22
+
23
23
  @expected = expected
24
24
  @state = state
25
25
  end
@@ -33,30 +33,27 @@ module Matchi
33
33
  # object = []
34
34
  #
35
35
  # matcher = Matchi::Change::By.new(1) { object.length }
36
- #
37
- # matcher.expected # => 1
38
- # matcher.matches? { object << "foo" } # => true
36
+ # matcher.match? { object << "foo" } # => true
39
37
  #
40
38
  # @yieldreturn [#object_id] The block of code to execute.
41
39
  #
42
40
  # @return [Boolean] Comparison between the value before and after the
43
41
  # code execution.
44
- def matches?
42
+ def match?
43
+ raise ::ArgumentError, "a block must be provided" unless block_given?
44
+
45
45
  value_before = @state.call
46
46
  yield
47
47
  value_after = @state.call
48
48
 
49
- expected == (value_after - value_before)
50
- end
51
-
52
- # A string containing a human-readable representation of the matcher.
53
- def inspect
54
- "#{self.class}(#{expected.inspect})"
49
+ @expected == (value_after - value_before)
55
50
  end
56
51
 
57
52
  # Returns a string representing the matcher.
53
+ #
54
+ # @return [String] a human-readable description of the matcher
58
55
  def to_s
59
- "change by #{expected.inspect}"
56
+ "change by #{@expected.inspect}"
60
57
  end
61
58
  end
62
59
  end
@@ -4,9 +4,6 @@ module Matchi
4
4
  class Change
5
5
  # *Change by at least* matcher.
6
6
  class ByAtLeast
7
- # @return [#object_id] An expected delta.
8
- attr_reader :expected
9
-
10
7
  # Initialize the matcher with an object and a block.
11
8
  #
12
9
  # @example
@@ -20,6 +17,10 @@ module Matchi
20
17
  # @param state [Proc] A block of code to execute to get the
21
18
  # state of the object.
22
19
  def initialize(expected, &state)
20
+ raise ::ArgumentError, "expected must be a Numeric" unless expected.is_a?(::Numeric)
21
+ raise ::ArgumentError, "a block must be provided" unless block_given?
22
+ raise ::ArgumentError, "expected must be non-negative" if expected.negative?
23
+
23
24
  @expected = expected
24
25
  @state = state
25
26
  end
@@ -33,30 +34,27 @@ module Matchi
33
34
  # object = []
34
35
  #
35
36
  # matcher = Matchi::Change::ByAtLeast.new(1) { object.length }
36
- #
37
- # matcher.expected # => 1
38
- # matcher.matches? { object << "foo" } # => true
37
+ # matcher.match? { object << "foo" } # => true
39
38
  #
40
39
  # @yieldreturn [#object_id] The block of code to execute.
41
40
  #
42
41
  # @return [Boolean] Comparison between the value before and after the
43
42
  # code execution.
44
- def matches?
43
+ def match?
44
+ raise ::ArgumentError, "a block must be provided" unless block_given?
45
+
45
46
  value_before = @state.call
46
47
  yield
47
48
  value_after = @state.call
48
49
 
49
- expected <= (value_after - value_before)
50
- end
51
-
52
- # A string containing a human-readable representation of the matcher.
53
- def inspect
54
- "#{self.class}(#{expected.inspect})"
50
+ @expected <= (value_after - value_before)
55
51
  end
56
52
 
57
53
  # Returns a string representing the matcher.
54
+ #
55
+ # @return [String] a human-readable description of the matcher
58
56
  def to_s
59
- "change by at least #{expected.inspect}"
57
+ "change by at least #{@expected.inspect}"
60
58
  end
61
59
  end
62
60
  end
@@ -4,9 +4,6 @@ module Matchi
4
4
  class Change
5
5
  # *Change by at most* matcher.
6
6
  class ByAtMost
7
- # @return [#object_id] An expected delta.
8
- attr_reader :expected
9
-
10
7
  # Initialize the matcher with an object and a block.
11
8
  #
12
9
  # @example
@@ -20,6 +17,10 @@ module Matchi
20
17
  # @param state [Proc] A block of code to execute to get the
21
18
  # state of the object.
22
19
  def initialize(expected, &state)
20
+ raise ::ArgumentError, "expected must be a Numeric" unless expected.is_a?(::Numeric)
21
+ raise ::ArgumentError, "a block must be provided" unless block_given?
22
+ raise ::ArgumentError, "expected must be non-negative" if expected.negative?
23
+
23
24
  @expected = expected
24
25
  @state = state
25
26
  end
@@ -33,30 +34,27 @@ module Matchi
33
34
  # object = []
34
35
  #
35
36
  # matcher = Matchi::Change::ByAtMost.new(1) { object.length }
36
- #
37
- # matcher.expected # => 1
38
- # matcher.matches? { object << "foo" } # => true
37
+ # matcher.match? { object << "foo" } # => true
39
38
  #
40
39
  # @yieldreturn [#object_id] The block of code to execute.
41
40
  #
42
41
  # @return [Boolean] Comparison between the value before and after the
43
42
  # code execution.
44
- def matches?
43
+ def match?
44
+ raise ::ArgumentError, "a block must be provided" unless block_given?
45
+
45
46
  value_before = @state.call
46
47
  yield
47
48
  value_after = @state.call
48
49
 
49
- expected >= (value_after - value_before)
50
- end
51
-
52
- # A string containing a human-readable representation of the matcher.
53
- def inspect
54
- "#{self.class}(#{expected.inspect})"
50
+ @expected >= (value_after - value_before)
55
51
  end
56
52
 
57
53
  # Returns a string representing the matcher.
54
+ #
55
+ # @return [String] a human-readable description of the matcher
58
56
  def to_s
59
- "change by at most #{expected.inspect}"
57
+ "change by at most #{@expected.inspect}"
60
58
  end
61
59
  end
62
60
  end
@@ -5,9 +5,6 @@ module Matchi
5
5
  class From
6
6
  # *Change from to* matcher.
7
7
  class To
8
- # @return [#object_id] An expected new value.
9
- attr_reader :expected
10
-
11
8
  # Initialize the matcher with two objects and a block.
12
9
  #
13
10
  # @example
@@ -22,6 +19,8 @@ module Matchi
22
19
  # @param state [Proc] A block of code to execute to
23
20
  # get the state of the object.
24
21
  def initialize(expected_init, expected_new_value, &state)
22
+ raise ::ArgumentError, "a block must be provided" unless block_given?
23
+
25
24
  @expected_init = expected_init
26
25
  @expected = expected_new_value
27
26
  @state = state
@@ -36,32 +35,29 @@ module Matchi
36
35
  # object = "foo"
37
36
  #
38
37
  # matcher = Matchi::Change::From::To.new("foo", "FOO") { object.to_s }
39
- #
40
- # matcher.expected # => "FOO"
41
- # matcher.matches? { object.upcase! } # => true
38
+ # matcher.match? { object.upcase! } # => true
42
39
  #
43
40
  # @yieldreturn [#object_id] The block of code to execute.
44
41
  #
45
42
  # @return [Boolean] Comparison between the value before and after the
46
43
  # code execution.
47
- def matches?
44
+ def match?
45
+ raise ::ArgumentError, "a block must be provided" unless block_given?
46
+
48
47
  value_before = @state.call
49
48
  return false unless @expected_init == value_before
50
49
 
51
50
  yield
52
51
  value_after = @state.call
53
52
 
54
- expected == value_after
55
- end
56
-
57
- # A string containing a human-readable representation of the matcher.
58
- def inspect
59
- "#{self.class}(#{@expected_init.inspect}, #{expected.inspect})"
53
+ @expected == value_after
60
54
  end
61
55
 
62
56
  # Returns a string representing the matcher.
57
+ #
58
+ # @return [String] a human-readable description of the matcher
63
59
  def to_s
64
- "change from #{@expected_init.inspect} to #{expected.inspect}"
60
+ "change from #{@expected_init.inspect} to #{@expected.inspect}"
65
61
  end
66
62
  end
67
63
  end
@@ -19,6 +19,8 @@ module Matchi
19
19
  # @param state [Proc] A block of code to execute to get the
20
20
  # state of the object.
21
21
  def initialize(expected, &state)
22
+ raise ::ArgumentError, "a block must be provided" unless block_given?
23
+
22
24
  @expected = expected
23
25
  @state = state
24
26
  end
@@ -35,7 +37,7 @@ module Matchi
35
37
  #
36
38
  # @param expected_new_value [#object_id] The new value to expect.
37
39
  #
38
- # @return [#matches?] A *change from to* matcher.
40
+ # @return [#match?] A *change from to* matcher.
39
41
  def to(expected_new_value)
40
42
  To.new(@expected, expected_new_value, &@state)
41
43
  end
@@ -4,9 +4,6 @@ module Matchi
4
4
  class Change
5
5
  # *Change to* matcher.
6
6
  class To
7
- # @return [#object_id] An expected new value.
8
- attr_reader :expected
9
-
10
7
  # Initialize the matcher with an object and a block.
11
8
  #
12
9
  # @example
@@ -20,6 +17,8 @@ module Matchi
20
17
  # @param state [Proc] A block of code to execute to get the
21
18
  # state of the object.
22
19
  def initialize(expected, &state)
20
+ raise ::ArgumentError, "a block must be provided" unless block_given?
21
+
23
22
  @expected = expected
24
23
  @state = state
25
24
  end
@@ -33,29 +32,26 @@ module Matchi
33
32
  # object = "foo"
34
33
  #
35
34
  # matcher = Matchi::Change::To.new("FOO") { object.to_s }
36
- #
37
- # matcher.expected # => "FOO"
38
- # matcher.matches? { object.upcase! } # => true
35
+ # matcher.match? { object.upcase! } # => true
39
36
  #
40
37
  # @yieldreturn [#object_id] The block of code to execute.
41
38
  #
42
39
  # @return [Boolean] Comparison between the value before and after the
43
40
  # code execution.
44
- def matches?
41
+ def match?
42
+ raise ::ArgumentError, "a block must be provided" unless block_given?
43
+
45
44
  yield
46
45
  value_after = @state.call
47
46
 
48
- expected == value_after
49
- end
50
-
51
- # A string containing a human-readable representation of the matcher.
52
- def inspect
53
- "#{self.class}(#{expected.inspect})"
47
+ @expected == value_after
54
48
  end
55
49
 
56
50
  # Returns a string representing the matcher.
51
+ #
52
+ # @return [String] a human-readable description of the matcher
57
53
  def to_s
58
- "change to #{expected.inspect}"
54
+ "change to #{@expected.inspect}"
59
55
  end
60
56
  end
61
57
  end
data/lib/matchi/change.rb CHANGED
@@ -19,9 +19,10 @@ module Matchi
19
19
  #
20
20
  # @param object [#object_id] An object.
21
21
  # @param method [Symbol] The name of a method.
22
- # @param args [Array] A list of arguments.
23
- # @param kwargs [Hash] A list of keyword arguments.
24
22
  def initialize(object, method, ...)
23
+ raise ::ArgumentError, "method must be a Symbol" unless method.is_a?(::Symbol)
24
+ raise ::ArgumentError, "object must respond to method" unless object.respond_to?(method)
25
+
25
26
  @state = -> { object.send(method, ...) }
26
27
  end
27
28
 
@@ -37,7 +38,7 @@ module Matchi
37
38
  #
38
39
  # @param minimum_delta [#object_id] The minimum delta of the expected change.
39
40
  #
40
- # @return [#matches?] A *change by at least* matcher.
41
+ # @return [#match?] A *change by at least* matcher.
41
42
  def by_at_least(minimum_delta)
42
43
  ByAtLeast.new(minimum_delta, &@state)
43
44
  end
@@ -54,7 +55,7 @@ module Matchi
54
55
  #
55
56
  # @param maximum_delta [#object_id] The maximum delta of the expected change.
56
57
  #
57
- # @return [#matches?] A *change by at most* matcher.
58
+ # @return [#match?] A *change by at most* matcher.
58
59
  def by_at_most(maximum_delta)
59
60
  ByAtMost.new(maximum_delta, &@state)
60
61
  end
@@ -71,7 +72,7 @@ module Matchi
71
72
  #
72
73
  # @param delta [#object_id] The delta of the expected change.
73
74
  #
74
- # @return [#matches?] A *change by* matcher.
75
+ # @return [#match?] A *change by* matcher.
75
76
  def by(delta)
76
77
  By.new(delta, &@state)
77
78
  end
@@ -86,7 +87,7 @@ module Matchi
86
87
  #
87
88
  # @param old_value [#object_id] The original value.
88
89
  #
89
- # @return [#matches?] A *change from* wrapper.
90
+ # @return [#match?] A *change from* wrapper.
90
91
  def from(old_value)
91
92
  From.new(old_value, &@state)
92
93
  end
@@ -101,7 +102,7 @@ module Matchi
101
102
  #
102
103
  # @param new_value [#object_id] The new value to expect.
103
104
  #
104
- # @return [#matches?] A *change to* matcher.
105
+ # @return [#match?] A *change to* matcher.
105
106
  def to(new_value)
106
107
  To.new(new_value, &@state)
107
108
  end
data/lib/matchi/eq.rb CHANGED
@@ -3,9 +3,6 @@
3
3
  module Matchi
4
4
  # *Equivalence* matcher.
5
5
  class Eq
6
- # @return [#eql?] An expected equivalent object.
7
- attr_reader :expected
8
-
9
6
  # Initialize the matcher with an object.
10
7
  #
11
8
  # @example
@@ -24,26 +21,23 @@ module Matchi
24
21
  # require "matchi/eq"
25
22
  #
26
23
  # matcher = Matchi::Eq.new("foo")
27
- #
28
- # matcher.expected # => "foo"
29
- # matcher.matches? { "foo" } # => true
24
+ # matcher.match? { "foo" } # => true
30
25
  #
31
26
  # @yieldreturn [#object_id] The actual value to compare to the expected
32
27
  # one.
33
28
  #
34
29
  # @return [Boolean] Comparison between actual and expected values.
35
- def matches?
36
- expected.eql?(yield)
37
- end
30
+ def match?
31
+ raise ::ArgumentError, "a block must be provided" unless block_given?
38
32
 
39
- # A string containing a human-readable representation of the matcher.
40
- def inspect
41
- "#{self.class}(#{expected.inspect})"
33
+ @expected.eql?(yield)
42
34
  end
43
35
 
44
36
  # Returns a string representing the matcher.
37
+ #
38
+ # @return [String] a human-readable description of the matcher
45
39
  def to_s
46
- "eq #{expected.inspect}"
40
+ "eq #{@expected.inspect}"
47
41
  end
48
42
  end
49
43
  end
data/lib/matchi/match.rb CHANGED
@@ -3,9 +3,6 @@
3
3
  module Matchi
4
4
  # *Regular expressions* matcher.
5
5
  class Match
6
- # @return [#match] A regular expression.
7
- attr_reader :expected
8
-
9
6
  # Initialize the matcher with an instance of Regexp.
10
7
  #
11
8
  # @example
@@ -15,6 +12,8 @@ module Matchi
15
12
  #
16
13
  # @param expected [#match] A regular expression.
17
14
  def initialize(expected)
15
+ raise ::ArgumentError, "expected must respond to match?" unless expected.respond_to?(:match?)
16
+
18
17
  @expected = expected
19
18
  end
20
19
 
@@ -24,26 +23,23 @@ module Matchi
24
23
  # require "matchi/match"
25
24
  #
26
25
  # matcher = Matchi::Match.new(/^foo$/)
27
- #
28
- # matcher.expected # => /^foo$/
29
- # matcher.matches? { "foo" } # => true
26
+ # matcher.match? { "foo" } # => true
30
27
  #
31
28
  # @yieldreturn [#object_id] The actual value to compare to the expected
32
29
  # one.
33
30
  #
34
31
  # @return [Boolean] Comparison between actual and expected values.
35
- def matches?
36
- expected.match?(yield)
37
- end
32
+ def match?
33
+ raise ::ArgumentError, "a block must be provided" unless block_given?
38
34
 
39
- # A string containing a human-readable representation of the matcher.
40
- def inspect
41
- "#{self.class}(#{expected.inspect})"
35
+ @expected.match?(yield)
42
36
  end
43
37
 
44
38
  # Returns a string representing the matcher.
39
+ #
40
+ # @return [String] a human-readable description of the matcher
45
41
  def to_s
46
- "match #{expected.inspect}"
42
+ "match #{@expected.inspect}"
47
43
  end
48
44
  end
49
45
  end