matchi 4.1.0 → 4.2.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.
@@ -2,20 +2,102 @@
2
2
 
3
3
  module Matchi
4
4
  class Change
5
- # *Change by at least* matcher.
5
+ # Minimum delta matcher that verifies numeric changes meet or exceed a threshold.
6
+ #
7
+ # This matcher ensures that a numeric value changes by at least the specified amount
8
+ # after executing a block of code. It's particularly useful when testing operations
9
+ # where you want to ensure a minimum change occurs but larger changes are acceptable,
10
+ # such as performance improvements, resource allocation, or progressive counters.
11
+ #
12
+ # @example Testing collection growth
13
+ # items = []
14
+ # matcher = Matchi::Change::ByAtLeast.new(2) { items.size }
15
+ # matcher.match? { items.push(1, 2) } # => true # Changed by exactly 2
16
+ # matcher.match? { items.push(1, 2, 3) } # => true # Changed by more than 2
17
+ # matcher.match? { items.push(1) } # => false # Changed by less than 2
18
+ #
19
+ # @example Verifying performance improvements
20
+ # class Benchmark
21
+ # def initialize
22
+ # @score = 100
23
+ # end
24
+ #
25
+ # def optimize!
26
+ # @score += rand(20..30) # Improvement varies
27
+ # end
28
+ #
29
+ # def score
30
+ # @score
31
+ # end
32
+ # end
33
+ #
34
+ # benchmark = Benchmark.new
35
+ # matcher = Matchi::Change::ByAtLeast.new(20) { benchmark.score }
36
+ # matcher.match? { benchmark.optimize! } # => true # Any improvement >= 20 passes
37
+ #
38
+ # @example Resource allocation
39
+ # class Pool
40
+ # def initialize
41
+ # @capacity = 1000
42
+ # end
43
+ #
44
+ # def allocate(minimum, maximum)
45
+ # actual = rand(minimum..maximum)
46
+ # @capacity -= actual
47
+ # actual
48
+ # end
49
+ #
50
+ # def available
51
+ # @capacity
52
+ # end
53
+ # end
54
+ #
55
+ # pool = Pool.new
56
+ # matcher = Matchi::Change::ByAtLeast.new(50) { -pool.available }
57
+ # matcher.match? { pool.allocate(50, 100) } # => true # Allocates at least 50
58
+ #
59
+ # @example Price threshold monitoring
60
+ # class Stock
61
+ # attr_reader :price
62
+ #
63
+ # def initialize(price)
64
+ # @price = price
65
+ # end
66
+ #
67
+ # def fluctuate!
68
+ # @price += rand(-10.0..20.0)
69
+ # end
70
+ # end
71
+ #
72
+ # stock = Stock.new(100.0)
73
+ # matcher = Matchi::Change::ByAtLeast.new(10.0) { stock.price }
74
+ # matcher.match? { stock.fluctuate! } # => true if price rises by 10.0 or more
75
+ #
76
+ # @note This matcher verifies minimum changes only. For exact changes, use By,
77
+ # and for maximum changes, use ByAtMost.
78
+ #
79
+ # @see Matchi::Change::By For exact change validation
80
+ # @see Matchi::Change::ByAtMost For maximum change validation
81
+ # @see Matchi::Change::To For final value validation
6
82
  class ByAtLeast
7
- # Initialize the matcher with an object and a block.
83
+ # Initialize the matcher with a minimum expected change and a state block.
8
84
  #
9
- # @example
10
- # require "matchi/change/by_at_least"
85
+ # @api public
86
+ #
87
+ # @param expected [Numeric] The minimum amount by which the value should change
88
+ # @param state [Proc] Block that retrieves the current value
89
+ #
90
+ # @raise [ArgumentError] if expected is not a Numeric
91
+ # @raise [ArgumentError] if expected is negative
92
+ # @raise [ArgumentError] if no state block is provided
11
93
  #
12
- # object = []
94
+ # @return [ByAtLeast] a new instance of the matcher
13
95
  #
14
- # Matchi::Change::ByAtLeast.new(1) { object.length }
96
+ # @example With integer minimum
97
+ # ByAtLeast.new(5) { counter.value }
15
98
  #
16
- # @param expected [#object_id] An expected delta.
17
- # @param state [Proc] A block of code to execute to get the
18
- # state of the object.
99
+ # @example With floating point minimum
100
+ # ByAtLeast.new(0.5) { temperature.celsius }
19
101
  def initialize(expected, &state)
20
102
  raise ::ArgumentError, "expected must be a Numeric" unless expected.is_a?(::Numeric)
21
103
  raise ::ArgumentError, "a block must be provided" unless block_given?
@@ -25,21 +107,30 @@ module Matchi
25
107
  @state = state
26
108
  end
27
109
 
28
- # Boolean comparison on the expected change by comparing the value
29
- # before and after the code execution.
110
+ # Checks if the value changes by at least the expected amount.
30
111
  #
31
- # @example
32
- # require "matchi/change/by_at_least"
112
+ # This method compares the value before and after executing the provided block,
113
+ # ensuring that the difference is greater than or equal to the expected minimum.
114
+ # This is useful when you want to verify that a change meets a minimum threshold.
115
+ #
116
+ # @api public
33
117
  #
34
- # object = []
118
+ # @yield [] Block that should cause the state change
119
+ # @yieldreturn [Object] The result of the block (not used)
35
120
  #
36
- # matcher = Matchi::Change::ByAtLeast.new(1) { object.length }
37
- # matcher.match? { object << "foo" } # => true
121
+ # @return [Boolean] true if the value changed by at least the expected amount
38
122
  #
39
- # @yieldreturn [#object_id] The block of code to execute.
123
+ # @raise [ArgumentError] if no block is provided
40
124
  #
41
- # @return [Boolean] Comparison between the value before and after the
42
- # code execution.
125
+ # @example Basic usage
126
+ # counter = 0
127
+ # matcher = ByAtLeast.new(5) { counter }
128
+ # matcher.match? { counter += 7 } # => true # Changed by more than minimum
129
+ #
130
+ # @example Edge case - exact minimum
131
+ # items = []
132
+ # matcher = ByAtLeast.new(2) { items.size }
133
+ # matcher.match? { items.push(1, 2) } # => true # Changed by exactly minimum
43
134
  def match?
44
135
  raise ::ArgumentError, "a block must be provided" unless block_given?
45
136
 
@@ -50,9 +141,15 @@ module Matchi
50
141
  @expected <= (value_after - value_before)
51
142
  end
52
143
 
53
- # Returns a string representing the matcher.
144
+ # Returns a human-readable description of the matcher.
145
+ #
146
+ # @api public
54
147
  #
55
- # @return [String] a human-readable description of the matcher
148
+ # @return [String] A string describing what this matcher verifies
149
+ #
150
+ # @example
151
+ # ByAtLeast.new(5).to_s # => "change by at least 5"
152
+ # ByAtLeast.new(2.5).to_s # => "change by at least 2.5"
56
153
  def to_s
57
154
  "change by at least #{@expected.inspect}"
58
155
  end
@@ -2,44 +2,148 @@
2
2
 
3
3
  module Matchi
4
4
  class Change
5
- # *Change by at most* matcher.
5
+ # Maximum delta matcher that verifies numeric changes don't exceed a limit.
6
+ #
7
+ # This matcher ensures that a numeric value changes by no more than the specified amount
8
+ # after executing a block of code. It's particularly useful when testing operations
9
+ # where you want to enforce an upper bound on changes, such as rate limiting,
10
+ # resource consumption, or controlled increments.
11
+ #
12
+ # @example Testing controlled collection growth
13
+ # items = []
14
+ # matcher = Matchi::Change::ByAtMost.new(2) { items.size }
15
+ # matcher.match? { items.push(1) } # => true # Changed by less than limit
16
+ # matcher.match? { items.push(1, 2) } # => true # Changed by exactly limit
17
+ # matcher.match? { items.push(1, 2, 3) } # => false # Changed by more than limit
18
+ #
19
+ # @example Rate limiting
20
+ # class RateLimiter
21
+ # def initialize
22
+ # @requests = 0
23
+ # end
24
+ #
25
+ # def process_batch(items)
26
+ # items.each do |item|
27
+ # break if @requests >= 3 # Rate limit
28
+ # process_item(item)
29
+ # end
30
+ # end
31
+ #
32
+ # private
33
+ #
34
+ # def process_item(item)
35
+ # @requests += 1
36
+ # # Processing logic...
37
+ # end
38
+ #
39
+ # def requests
40
+ # @requests
41
+ # end
42
+ # end
43
+ #
44
+ # limiter = RateLimiter.new
45
+ # matcher = Matchi::Change::ByAtMost.new(3) { limiter.requests }
46
+ # matcher.match? { limiter.process_batch([1, 2, 3, 4, 5]) } # => true
47
+ #
48
+ # @example Resource consumption
49
+ # class ResourcePool
50
+ # attr_reader :used
51
+ #
52
+ # def initialize
53
+ # @used = 0
54
+ # end
55
+ #
56
+ # def allocate(requested)
57
+ # available = 5 - @used # Maximum pool size is 5
58
+ # granted = [requested, available].min
59
+ # @used += granted
60
+ # granted
61
+ # end
62
+ # end
63
+ #
64
+ # pool = ResourcePool.new
65
+ # matcher = Matchi::Change::ByAtMost.new(2) { pool.used }
66
+ # matcher.match? { pool.allocate(2) } # => true
67
+ # matcher.match? { pool.allocate(3) } # => false
68
+ #
69
+ # @example Score adjustments
70
+ # class GameScore
71
+ # attr_reader :value
72
+ #
73
+ # def initialize
74
+ # @value = 100
75
+ # end
76
+ #
77
+ # def apply_penalty(amount)
78
+ # max_penalty = 10
79
+ # actual_penalty = [amount, max_penalty].min
80
+ # @value -= actual_penalty
81
+ # end
82
+ # end
83
+ #
84
+ # score = GameScore.new
85
+ # matcher = Matchi::Change::ByAtMost.new(10) { -score.value }
86
+ # matcher.match? { score.apply_penalty(5) } # => true # Small penalty
87
+ # matcher.match? { score.apply_penalty(15) } # => true # Limited to max
88
+ #
89
+ # @note This matcher verifies maximum changes only. For exact changes, use By,
90
+ # and for minimum changes, use ByAtLeast.
91
+ #
92
+ # @see Matchi::Change::By For exact change validation
93
+ # @see Matchi::Change::ByAtLeast For minimum change validation
94
+ # @see Matchi::Change::To For final value validation
6
95
  class ByAtMost
7
- # Initialize the matcher with an object and a block.
96
+ # Initialize the matcher with a maximum allowed change and a state block.
8
97
  #
9
- # @example
10
- # require "matchi/change/by_at_most"
98
+ # @api public
99
+ #
100
+ # @param expected [Numeric] The maximum amount by which the value should change
101
+ # @param state [Proc] Block that retrieves the current value
102
+ #
103
+ # @raise [ArgumentError] if expected is not a Numeric
104
+ # @raise [ArgumentError] if expected is negative
105
+ # @raise [ArgumentError] if no state block is provided
11
106
  #
12
- # object = []
107
+ # @return [ByAtMost] a new instance of the matcher
13
108
  #
14
- # Matchi::Change::ByAtMost.new(1) { object.length }
109
+ # @example With integer maximum
110
+ # ByAtMost.new(5) { counter.value }
15
111
  #
16
- # @param expected [#object_id] An expected delta.
17
- # @param state [Proc] A block of code to execute to get the
18
- # state of the object.
112
+ # @example With floating point maximum
113
+ # ByAtMost.new(0.5) { temperature.celsius }
19
114
  def initialize(expected, &state)
20
115
  raise ::ArgumentError, "expected must be a Numeric" unless expected.is_a?(::Numeric)
21
116
  raise ::ArgumentError, "a block must be provided" unless block_given?
22
117
  raise ::ArgumentError, "expected must be non-negative" if expected.negative?
23
118
 
24
119
  @expected = expected
25
- @state = state
120
+ @state = state
26
121
  end
27
122
 
28
- # Boolean comparison on the expected change by comparing the value
29
- # before and after the code execution.
123
+ # Checks if the value changes by no more than the expected amount.
30
124
  #
31
- # @example
32
- # require "matchi/change/by_at_most"
125
+ # This method compares the value before and after executing the provided block,
126
+ # ensuring that the absolute difference is less than or equal to the expected maximum.
127
+ # This is useful for enforcing upper bounds on state changes.
128
+ #
129
+ # @api public
33
130
  #
34
- # object = []
131
+ # @yield [] Block that should cause the state change
132
+ # @yieldreturn [Object] The result of the block (not used)
35
133
  #
36
- # matcher = Matchi::Change::ByAtMost.new(1) { object.length }
37
- # matcher.match? { object << "foo" } # => true
134
+ # @return [Boolean] true if the value changed by at most the expected amount
38
135
  #
39
- # @yieldreturn [#object_id] The block of code to execute.
136
+ # @raise [ArgumentError] if no block is provided
40
137
  #
41
- # @return [Boolean] Comparison between the value before and after the
42
- # code execution.
138
+ # @example Basic usage with growth
139
+ # users = []
140
+ # matcher = ByAtMost.new(2) { users.size }
141
+ # matcher.match? { users.push('alice') } # => true
142
+ #
143
+ # @example With negative changes
144
+ # stock = 10
145
+ # matcher = ByAtMost.new(3) { stock }
146
+ # matcher.match? { stock -= 2 } # => true
43
147
  def match?
44
148
  raise ::ArgumentError, "a block must be provided" unless block_given?
45
149
 
@@ -50,9 +154,15 @@ module Matchi
50
154
  @expected >= (value_after - value_before)
51
155
  end
52
156
 
53
- # Returns a string representing the matcher.
157
+ # Returns a human-readable description of the matcher.
158
+ #
159
+ # @api public
54
160
  #
55
- # @return [String] a human-readable description of the matcher
161
+ # @return [String] A string describing what this matcher verifies
162
+ #
163
+ # @example
164
+ # ByAtMost.new(5).to_s # => "change by at most 5"
165
+ # ByAtMost.new(2.5).to_s # => "change by at most 2.5"
56
166
  def to_s
57
167
  "change by at most #{@expected.inspect}"
58
168
  end
@@ -3,44 +3,105 @@
3
3
  module Matchi
4
4
  class Change
5
5
  class From
6
- # *Change from to* matcher.
6
+ # Value transition matcher that verifies both initial and final states of an operation.
7
+ #
8
+ # This matcher ensures that a value not only changes to an expected final state but also
9
+ # starts from a specific initial state. This is particularly useful when testing state
10
+ # transitions where both the starting and ending conditions are important, such as in
11
+ # workflow systems, state machines, or data transformations.
12
+ #
13
+ # @example Basic string transformation
14
+ # text = "hello"
15
+ # matcher = Matchi::Change::From::To.new("hello", "HELLO") { text.to_s }
16
+ # matcher.match? { text.upcase! } # => true
17
+ # matcher.match? { text.reverse! } # => false # Wrong final state
18
+ #
19
+ # text = "other"
20
+ # matcher.match? { text.upcase! } # => false # Wrong initial state
21
+ #
22
+ # @example State machine transitions
23
+ # class Order
24
+ # attr_accessor :status
25
+ # def initialize(status)
26
+ # @status = status
27
+ # end
28
+ # end
29
+ #
30
+ # order = Order.new(:pending)
31
+ # matcher = Matchi::Change::From::To.new(:pending, :shipped) { order.status }
32
+ # matcher.match? { order.status = :shipped } # => true
33
+ #
34
+ # @example Complex object transformations
35
+ # class User
36
+ # attr_accessor :permissions
37
+ # def initialize
38
+ # @permissions = [:read]
39
+ # end
40
+ #
41
+ # def promote!
42
+ # @permissions += [:write, :delete]
43
+ # end
44
+ # end
45
+ #
46
+ # user = User.new
47
+ # matcher = Matchi::Change::From::To.new(
48
+ # [:read],
49
+ # [:read, :write, :delete]
50
+ # ) { user.permissions }
51
+ # matcher.match? { user.promote! } # => true
52
+ #
53
+ # @see Matchi::Change::To For checking only the final state
54
+ # @see Matchi::Change::By For checking numeric changes
7
55
  class To
8
- # Initialize the matcher with two objects and a block.
56
+ # Initialize the matcher with expected initial and final values.
9
57
  #
10
- # @example
11
- # require "matchi/change/from/to"
58
+ # @api public
59
+ #
60
+ # @param expected_init [#==] The expected initial value
61
+ # @param expected_new_value [#==] The expected final value
62
+ # @param state [Proc] Block that retrieves the current value
63
+ #
64
+ # @raise [ArgumentError] if no state block is provided
12
65
  #
13
- # object = "foo"
66
+ # @return [To] a new instance of the matcher
14
67
  #
15
- # Matchi::Change::From::To.new("foo", "FOO") { object.to_s }
68
+ # @example With simple value
69
+ # To.new("draft", "published") { document.status }
16
70
  #
17
- # @param expected_init [#object_id] An expected initial value.
18
- # @param expected_new_value [#object_id] An expected new value.
19
- # @param state [Proc] A block of code to execute to
20
- # get the state of the object.
71
+ # @example With complex state
72
+ # To.new([:user], [:user, :admin]) { account.roles }
21
73
  def initialize(expected_init, expected_new_value, &state)
22
74
  raise ::ArgumentError, "a block must be provided" unless block_given?
23
75
 
24
- @expected_init = expected_init
25
- @expected = expected_new_value
26
- @state = state
76
+ @expected_init = expected_init
77
+ @expected = expected_new_value
78
+ @state = state
27
79
  end
28
80
 
29
- # Boolean comparison on the expected change by comparing the value
30
- # before and after the code execution.
81
+ # Verifies both initial and final states during a transition.
31
82
  #
32
- # @example
33
- # require "matchi/change/from/to"
83
+ # This method first checks if the initial state matches the expected value,
84
+ # then executes the provided block and verifies the final state. The match
85
+ # fails if either the initial or final state doesn't match expectations.
86
+ #
87
+ # @api public
34
88
  #
35
- # object = "foo"
89
+ # @yield [] Block that should cause the state transition
90
+ # @yieldreturn [Object] The result of the block (not used)
36
91
  #
37
- # matcher = Matchi::Change::From::To.new("foo", "FOO") { object.to_s }
38
- # matcher.match? { object.upcase! } # => true
92
+ # @return [Boolean] true if both initial and final states match expectations
39
93
  #
40
- # @yieldreturn [#object_id] The block of code to execute.
94
+ # @raise [ArgumentError] if no block is provided
41
95
  #
42
- # @return [Boolean] Comparison between the value before and after the
43
- # code execution.
96
+ # @example Basic usage
97
+ # text = "hello"
98
+ # matcher = To.new("hello", "HELLO") { text }
99
+ # matcher.match? { text.upcase! } # => true
100
+ #
101
+ # @example Failed initial state
102
+ # text = "wrong"
103
+ # matcher = To.new("hello", "HELLO") { text }
104
+ # matcher.match? { text.upcase! } # => false
44
105
  def match?
45
106
  raise ::ArgumentError, "a block must be provided" unless block_given?
46
107
 
@@ -53,9 +114,15 @@ module Matchi
53
114
  @expected == value_after
54
115
  end
55
116
 
56
- # Returns a string representing the matcher.
117
+ # Returns a human-readable description of the matcher.
118
+ #
119
+ # @api public
57
120
  #
58
- # @return [String] a human-readable description of the matcher
121
+ # @return [String] A string describing what this matcher verifies
122
+ #
123
+ # @example
124
+ # To.new("draft", "published").to_s
125
+ # # => 'change from "draft" to "published"'
59
126
  def to_s
60
127
  "change from #{@expected_init.inspect} to #{@expected.inspect}"
61
128
  end
@@ -4,7 +4,32 @@ require_relative File.join("from", "to")
4
4
 
5
5
  module Matchi
6
6
  class Change
7
- # *Change from to* wrapper.
7
+ # Initial state wrapper for building a value transition matcher.
8
+ #
9
+ # This class acts as a wrapper that captures the expected initial state and
10
+ # provides methods to build a complete transition matcher. When combined with
11
+ # the 'to' method, it creates a matcher that verifies both the starting and
12
+ # ending values of a change operation. This is useful when you need to ensure
13
+ # not only the final state but also the initial state of a value.
14
+ #
15
+ # @example Basic string transformation
16
+ # text = "hello"
17
+ # Change.new(text, :to_s).from("hello").to("HELLO").match? { text.upcase! } # => true
18
+ #
19
+ # @example Object state transition
20
+ # class User
21
+ # attr_accessor :status
22
+ # def initialize
23
+ # @status = "pending"
24
+ # end
25
+ # end
26
+ #
27
+ # user = User.new
28
+ # Change.new(user, :status).from("pending").to("active").match? {
29
+ # user.status = "active"
30
+ # } # => true
31
+ #
32
+ # @see Matchi::Change::From::To For the complete transition matcher
8
33
  class From
9
34
  # Initialize the wrapper with an object and a block.
10
35
  #
@@ -27,6 +52,11 @@ module Matchi
27
52
 
28
53
  # Specifies the new value to expect.
29
54
  #
55
+ # Creates a complete transition matcher that verifies both the initial
56
+ # and final states of a value. The matcher will succeed only if the
57
+ # value starts at the expected initial state and changes to the specified
58
+ # new value after executing the test block.
59
+ #
30
60
  # @example
31
61
  # require "matchi/change/from"
32
62
  #
@@ -2,54 +2,103 @@
2
2
 
3
3
  module Matchi
4
4
  class Change
5
- # *Change to* matcher.
5
+ # Final state matcher that verifies if a method returns an expected value after a change.
6
+ #
7
+ # This matcher focuses on the final state of an object, verifying that a method call
8
+ # returns an expected value after executing a block of code. Unlike the full from/to
9
+ # matcher, it only cares about the end result, not the initial state.
10
+ #
11
+ # @example Basic string transformation
12
+ # text = "hello"
13
+ # matcher = Matchi::Change::To.new("HELLO") { text.to_s }
14
+ # matcher.match? { text.upcase! } # => true
15
+ # matcher.match? { text.reverse! } # => false
16
+ #
17
+ # @example Number calculations
18
+ # counter = 0
19
+ # matcher = Matchi::Change::To.new(5) { counter }
20
+ # matcher.match? { counter = 5 } # => true
21
+ # matcher.match? { counter += 1 } # => false
22
+ #
23
+ # @example With object attributes
24
+ # class User
25
+ # attr_accessor :status
26
+ # def initialize(status)
27
+ # @status = status
28
+ # end
29
+ # end
30
+ #
31
+ # user = User.new(:pending)
32
+ # matcher = Matchi::Change::To.new(:active) { user.status }
33
+ # matcher.match? { user.status = :active } # => true
34
+ #
35
+ # @see Matchi::Change::From::To For checking both initial and final states
36
+ # @see Matchi::Change::By For checking numeric changes
6
37
  class To
7
- # Initialize the matcher with an object and a block.
38
+ # Initialize the matcher with an expected new value and a state block.
8
39
  #
9
- # @example
10
- # require "matchi/change/to"
40
+ # @api public
41
+ #
42
+ # @param expected [#eql?] The expected final value
43
+ # @param state [Proc] Block that retrieves the value to check
44
+ #
45
+ # @raise [ArgumentError] if no state block is provided
11
46
  #
12
- # object = "foo"
47
+ # @return [To] a new instance of the matcher
13
48
  #
14
- # Matchi::Change::To.new("FOO") { object.to_s }
49
+ # @example With simple value
50
+ # To.new("test") { object.value }
15
51
  #
16
- # @param expected [#object_id] An expected new value.
17
- # @param state [Proc] A block of code to execute to get the
18
- # state of the object.
52
+ # @example With complex calculation
53
+ # To.new(100) { object.items.count }
19
54
  def initialize(expected, &state)
20
55
  raise ::ArgumentError, "a block must be provided" unless block_given?
21
56
 
22
57
  @expected = expected
23
- @state = state
58
+ @state = state
24
59
  end
25
60
 
26
- # Boolean comparison on the expected change by comparing the value
27
- # before and after the code execution.
61
+ # Checks if the state block returns the expected value after executing the provided block.
28
62
  #
29
- # @example
30
- # require "matchi/change/to"
63
+ # This method executes the provided block and then checks if the state block
64
+ # returns the expected value. It only cares about the final state, not any
65
+ # intermediate values or the initial state.
66
+ #
67
+ # @api public
31
68
  #
32
- # object = "foo"
69
+ # @yield [] Block that should cause the state change
70
+ # @yieldreturn [Object] The result of the block (not used)
33
71
  #
34
- # matcher = Matchi::Change::To.new("FOO") { object.to_s }
35
- # matcher.match? { object.upcase! } # => true
72
+ # @return [Boolean] true if the final state matches the expected value
36
73
  #
37
- # @yieldreturn [#object_id] The block of code to execute.
74
+ # @raise [ArgumentError] if no block is provided
38
75
  #
39
- # @return [Boolean] Comparison between the value before and after the
40
- # code execution.
76
+ # @example Basic usage
77
+ # text = "hello"
78
+ # matcher = To.new("HELLO") { text.to_s }
79
+ # matcher.match? { text.upcase! } # => true
80
+ #
81
+ # @example With method chaining
82
+ # array = [1, 2, 3]
83
+ # matcher = To.new(6) { array.sum }
84
+ # matcher.match? { array.map! { |x| x * 2 } } # => false
41
85
  def match?
42
86
  raise ::ArgumentError, "a block must be provided" unless block_given?
43
87
 
44
88
  yield
45
89
  value_after = @state.call
46
90
 
47
- @expected == value_after
91
+ @expected.eql?(value_after)
48
92
  end
49
93
 
50
- # Returns a string representing the matcher.
94
+ # Returns a human-readable description of the matcher.
95
+ #
96
+ # @api public
51
97
  #
52
- # @return [String] a human-readable description of the matcher
98
+ # @return [String] A string describing what this matcher verifies
99
+ #
100
+ # @example
101
+ # To.new("test").to_s # => 'change to "test"'
53
102
  def to_s
54
103
  "change to #{@expected.inspect}"
55
104
  end