matchi 3.0.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16b38ee69034c01d5f2961a5d4e14b6c150cad89a9368f7c0cf84071761e2999
4
- data.tar.gz: f76f7a3286443d49f6e449af9ffefdb2f38b9d974aada733e1a611d75c627199
3
+ metadata.gz: 5596c7ba2d1153eadaa1ba438b009c0cc17954a360b4192bb6419c674d864822
4
+ data.tar.gz: 0c717e7642961b8707433584c58a1e2369f810c11114caa7530c890563df78a3
5
5
  SHA512:
6
- metadata.gz: ed8abd4dd002520dc4740fc80b91573098e3242319e74b807e6621482beb131d4a40e299584fc507bacbb806867a00691a3f4bdcebf611dd2c45eb1b90e9e76e
7
- data.tar.gz: 3fd0b621816f91d0569fdebeb4a8c80c55890f83250ed87b0df2cd1b7683e02f390df159f372f6e87f62aab853b7d8042d438f1efc31f83fd3b169a12eab2b8e
6
+ metadata.gz: 8e89b77b4f7fd25fc53ea570b865b3479be5a3291bc437f55b3ea126111fdd120afd907439ec87c565bbb28b4fe6e7be34a932a218e55933bb3d4bb890b0edcf
7
+ data.tar.gz: f19a1f799d1b8f9c1f40dc747c00863ae1cd9bf1ed545aabdea774e4c658a242a74d2bd6f03709fe124786f327fd7401b104aa9e567496265b966937c61841e3
data/README.md CHANGED
@@ -12,9 +12,9 @@
12
12
 
13
13
  ## Project goals
14
14
 
15
- * Provide a collection of useful generic matchers.
16
15
  * Adding matchers should be as simple as possible.
17
16
  * Being framework agnostic and easy to integrate.
17
+ * Avoid false positives/negatives due to malicious actual values.
18
18
 
19
19
  ## Installation
20
20
 
@@ -52,16 +52,20 @@ All examples here assume that this has been done.
52
52
 
53
53
  ### Anatomy of a matcher
54
54
 
55
- A __Matchi__ matcher is just an object that responds to the `matches?` method with a block as argument, and returns a boolean. That's all it is.
55
+ A __Matchi__ matcher is an object that must respond to the `matches?` method with a block as argument, and return a boolean.
56
56
 
57
- But let's see some examples.
57
+ To facilitate the integration of the matchers in other tools, __Matchi__ matchers may expose expected values via the `expected` method.
58
58
 
59
59
  ### Built-in matchers
60
60
 
61
+ Here is the collection of useful generic matchers.
62
+
61
63
  **Equivalence** matcher:
62
64
 
63
65
  ```ruby
64
66
  matcher = Matchi::Eq.new("foo")
67
+
68
+ matcher.expected # => "foo"
65
69
  matcher.matches? { "foo" } # => true
66
70
  ```
67
71
 
@@ -69,20 +73,35 @@ matcher.matches? { "foo" } # => true
69
73
 
70
74
  ```ruby
71
75
  matcher = Matchi::Be.new(:foo)
76
+
77
+ matcher.expected # => :foo
72
78
  matcher.matches? { :foo } # => true
73
79
  ```
74
80
 
81
+ **Comparisons** matcher:
82
+
83
+ ```ruby
84
+ matcher = Matchi::BeWithin.new(8).of(37)
85
+
86
+ matcher.expected # => 37
87
+ matcher.matches? { 42 } # => true
88
+ ```
89
+
75
90
  **Regular expressions** matcher:
76
91
 
77
92
  ```ruby
78
93
  matcher = Matchi::Match.new(/^foo$/)
94
+
95
+ matcher.expected # => /^foo$/
79
96
  matcher.matches? { "foo" } # => true
80
97
  ```
81
98
 
82
99
  **Expecting errors** matcher:
83
100
 
84
101
  ```ruby
85
- matcher = Matchi::RaiseException.new(NameError)
102
+ matcher = Matchi::RaiseException.new(:NameError)
103
+
104
+ matcher.expected # => "NameError"
86
105
  matcher.matches? { Boom } # => true
87
106
  ```
88
107
 
@@ -90,30 +109,56 @@ matcher.matches? { Boom } # => true
90
109
 
91
110
  ```ruby
92
111
  matcher = Matchi::BeAnInstanceOf.new(:String)
112
+
113
+ matcher.expected # => "String"
93
114
  matcher.matches? { "foo" } # => true
94
115
  ```
95
116
 
117
+ **Predicate** matcher:
118
+
119
+ ```ruby
120
+ matcher = Matchi::Predicate.new(:be_empty)
121
+
122
+ matcher.expected # => [:empty?, [], {}, nil]
123
+ matcher.matches? { [] } # => true
124
+
125
+ matcher = Matchi::Predicate.new(:have_key, :foo)
126
+
127
+ matcher.expected # => [:has_key?, [:foo], {}, nil]
128
+ matcher.matches? { { foo: 42 } } # => true
129
+ ```
130
+
96
131
  **Change** matcher:
97
132
 
98
133
  ```ruby
99
134
  object = []
100
135
  matcher = Matchi::Change.new(object, :length).by(1)
136
+
137
+ matcher.expected # => 1
101
138
  matcher.matches? { object << 1 } # => true
102
139
 
103
140
  object = []
104
141
  matcher = Matchi::Change.new(object, :length).by_at_least(1)
142
+
143
+ matcher.expected # => 1
105
144
  matcher.matches? { object << 1 } # => true
106
145
 
107
146
  object = []
108
147
  matcher = Matchi::Change.new(object, :length).by_at_most(1)
148
+
149
+ matcher.expected # => 1
109
150
  matcher.matches? { object << 1 } # => true
110
151
 
111
152
  object = "foo"
112
153
  matcher = Matchi::Change.new(object, :to_s).from("foo").to("FOO")
154
+
155
+ matcher.expected # => "FOO"
113
156
  matcher.matches? { object.upcase! } # => true
114
157
 
115
158
  object = "foo"
116
159
  matcher = Matchi::Change.new(object, :to_s).to("FOO")
160
+
161
+ matcher.expected # => "FOO"
117
162
  matcher.matches? { object.upcase! } # => true
118
163
  ```
119
164
 
@@ -121,6 +166,8 @@ matcher.matches? { object.upcase! } # => true
121
166
 
122
167
  ```ruby
123
168
  matcher = Matchi::Satisfy.new { |value| value == 42 }
169
+
170
+ matcher.expected # => #<Proc:0x00007fbaafc65540>
124
171
  matcher.matches? { 42 } # => true
125
172
  ```
126
173
 
@@ -133,13 +180,19 @@ A **Be the answer** matcher:
133
180
  ```ruby
134
181
  module Matchi
135
182
  class BeTheAnswer
183
+ def expected
184
+ 42
185
+ end
186
+
136
187
  def matches?
137
- 42.equal?(yield)
188
+ expected.equal?(yield)
138
189
  end
139
190
  end
140
191
  end
141
192
 
142
193
  matcher = Matchi::BeTheAnswer.new
194
+
195
+ matcher.expected # => 42
143
196
  matcher.matches? { 42 } # => true
144
197
  ```
145
198
 
@@ -157,6 +210,7 @@ module Matchi
157
210
  end
158
211
 
159
212
  matcher = Matchi::BePrime.new
213
+
160
214
  matcher.matches? { 42 } # => false
161
215
  ```
162
216
 
@@ -178,6 +232,8 @@ module Matchi
178
232
  end
179
233
 
180
234
  matcher = Matchi::StartWith.new("foo")
235
+
236
+ matcher.expected # => "foo"
181
237
  matcher.matches? { "foobar" } # => true
182
238
  ```
183
239
 
data/lib/matchi/be.rb CHANGED
@@ -3,6 +3,9 @@
3
3
  module Matchi
4
4
  # *Identity* matcher.
5
5
  class Be
6
+ # @return [#equal?] The expected identical object.
7
+ attr_reader :expected
8
+
6
9
  # Initialize the matcher with an object.
7
10
  #
8
11
  # @example
@@ -21,24 +24,26 @@ module Matchi
21
24
  # require "matchi/be"
22
25
  #
23
26
  # matcher = Matchi::Be.new(:foo)
27
+ #
28
+ # matcher.expected # => :foo
24
29
  # matcher.matches? { :foo } # => true
25
30
  #
26
31
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
32
  # one.
28
33
  #
29
34
  # @return [Boolean] Comparison between actual and expected values.
30
- def matches?(*, **)
31
- @expected.equal?(yield)
35
+ def matches?
36
+ expected.equal?(yield)
32
37
  end
33
38
 
34
39
  # A string containing a human-readable representation of the matcher.
35
40
  def inspect
36
- "#{self.class}(#{@expected.inspect})"
41
+ "#{self.class}(#{expected.inspect})"
37
42
  end
38
43
 
39
44
  # Returns a string representing the matcher.
40
45
  def to_s
41
- "be #{@expected.inspect}"
46
+ "be #{expected.inspect}"
42
47
  end
43
48
  end
44
49
  end
@@ -3,6 +3,9 @@
3
3
  module Matchi
4
4
  # *Type/class* matcher.
5
5
  class BeAnInstanceOf
6
+ # @return [String] The expected class name.
7
+ attr_reader :expected
8
+
6
9
  # Initialize the matcher with (the name of) a class or module.
7
10
  #
8
11
  # @example
@@ -10,9 +13,9 @@ module Matchi
10
13
  #
11
14
  # Matchi::BeAnInstanceOf.new(String)
12
15
  #
13
- # @param expected [#to_s] A (name of a) class or module.
16
+ # @param expected [Class, #to_s] The expected class name.
14
17
  def initialize(expected)
15
- @expected = String(expected).to_sym
18
+ @expected = String(expected)
16
19
  end
17
20
 
18
21
  # Boolean comparison between the class of the actual value and the
@@ -22,23 +25,25 @@ module Matchi
22
25
  # require "matchi/be_an_instance_of"
23
26
  #
24
27
  # matcher = Matchi::BeAnInstanceOf.new(String)
28
+ #
29
+ # matcher.expected # => "String"
25
30
  # matcher.matches? { "foo" } # => true
26
31
  #
27
32
  # @yieldreturn [#class] the actual value to compare to the expected one.
28
33
  #
29
34
  # @return [Boolean] Comparison between actual and expected values.
30
- def matches?(*, **)
31
- self.class.const_get(@expected).equal?(yield.class)
35
+ def matches?
36
+ self.class.const_get(expected).equal?(yield.class)
32
37
  end
33
38
 
34
39
  # A string containing a human-readable representation of the matcher.
35
40
  def inspect
36
- "#{self.class}(#{@expected})"
41
+ "#{self.class}(#{expected})"
37
42
  end
38
43
 
39
44
  # Returns a string representing the matcher.
40
45
  def to_s
41
- "be an instance of #{@expected}"
46
+ "be an instance of #{expected}"
42
47
  end
43
48
  end
44
49
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative File.join("be_within", "of")
4
+
5
+ module Matchi
6
+ # Wraps the target of a be_within matcher.
7
+ class BeWithin
8
+ # Initialize a wrapper of the be_within matcher with a numeric value.
9
+ #
10
+ # @example
11
+ # require "matchi/be_within"
12
+ #
13
+ # Matchi::BeWithin.new(1)
14
+ #
15
+ # @param delta [Numeric] A numeric value.
16
+ def initialize(delta)
17
+ @delta = delta
18
+ end
19
+
20
+ # Specifies an expected numeric value.
21
+ #
22
+ # @example
23
+ # require "matchi/be_within"
24
+ #
25
+ # be_within_wrapper = Matchi::BeWithin.new(1)
26
+ # be_within_wrapper.of(41)
27
+ #
28
+ # @param expected [Numeric] The expected value.
29
+ #
30
+ # @return [#matches?] A *be_within of* matcher.
31
+ def of(expected)
32
+ Of.new(@delta, expected)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matchi
4
+ class BeWithin
5
+ # *BeWithin of* matcher.
6
+ class Of
7
+ # @return [Numeric] An expected value.
8
+ attr_reader :expected
9
+
10
+ # Initialize the matcher with a delta and an expected value.
11
+ #
12
+ # @example
13
+ # require "matchi/be_within/of"
14
+ #
15
+ # Matchi::BeWithin::Of.new(1, 41)
16
+ #
17
+ # @param delta [Numeric] The accepted variation of the actual value.
18
+ # @param expected [Numeric] The expected value.
19
+ def initialize(delta, expected)
20
+ @delta = delta
21
+ @expected = expected
22
+ end
23
+
24
+ # Boolean comparison on the expected be_within by comparing the actual
25
+ # value and the expected value.
26
+ #
27
+ # @example
28
+ # require "matchi/be_within/of"
29
+ #
30
+ # matcher = Matchi::BeWithin::Of.new(1, 41)
31
+ #
32
+ # matcher.expected # => 41
33
+ # matcher.matches? { 42 } # => true
34
+ #
35
+ # @yieldreturn [Numeric] The block of code to execute.
36
+ #
37
+ # @return [Boolean] Comparison between the actual and the expected values.
38
+ def matches?
39
+ (expected - yield).abs <= @delta
40
+ end
41
+
42
+ # A string containing a human-readable representation of the matcher.
43
+ def inspect
44
+ "#{self.class}(#{@delta}, #{expected})"
45
+ end
46
+
47
+ # Returns a string representing the matcher.
48
+ def to_s
49
+ "be within #{@delta} of #{expected}"
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/matchi/change.rb CHANGED
@@ -32,14 +32,14 @@ module Matchi
32
32
  #
33
33
  # object = []
34
34
  #
35
- # change = Matchi::Change.new(object, :length)
36
- # change.by_at_least(1)
35
+ # change_wrapper = Matchi::Change.new(object, :length)
36
+ # change_wrapper.by_at_least(1)
37
37
  #
38
- # @param expected [#object_id] The minimum delta of the expected change.
38
+ # @param minimum_delta [#object_id] The minimum delta of the expected change.
39
39
  #
40
40
  # @return [#matches?] A *change by at least* matcher.
41
- def by_at_least(expected)
42
- ByAtLeast.new(expected, &@state)
41
+ def by_at_least(minimum_delta)
42
+ ByAtLeast.new(minimum_delta, &@state)
43
43
  end
44
44
 
45
45
  # Specifies a maximum delta of the expected change.
@@ -49,14 +49,14 @@ module Matchi
49
49
  #
50
50
  # object = []
51
51
  #
52
- # change = Matchi::Change.new(object, :length)
53
- # change.by_at_most(1)
52
+ # change_wrapper = Matchi::Change.new(object, :length)
53
+ # change_wrapper.by_at_most(1)
54
54
  #
55
- # @param expected [#object_id] The maximum delta of the expected change.
55
+ # @param maximum_delta [#object_id] The maximum delta of the expected change.
56
56
  #
57
57
  # @return [#matches?] A *change by at most* matcher.
58
- def by_at_most(expected)
59
- ByAtMost.new(expected, &@state)
58
+ def by_at_most(maximum_delta)
59
+ ByAtMost.new(maximum_delta, &@state)
60
60
  end
61
61
 
62
62
  # Specifies the delta of the expected change.
@@ -66,14 +66,14 @@ module Matchi
66
66
  #
67
67
  # object = []
68
68
  #
69
- # change = Matchi::Change.new(object, :length)
70
- # change.by(1)
69
+ # change_wrapper = Matchi::Change.new(object, :length)
70
+ # change_wrapper.by(1)
71
71
  #
72
- # @param expected [#object_id] The delta of the expected change.
72
+ # @param delta [#object_id] The delta of the expected change.
73
73
  #
74
74
  # @return [#matches?] A *change by* matcher.
75
- def by(expected)
76
- By.new(expected, &@state)
75
+ def by(delta)
76
+ By.new(delta, &@state)
77
77
  end
78
78
 
79
79
  # Specifies the original value.
@@ -81,14 +81,14 @@ module Matchi
81
81
  # @example
82
82
  # require "matchi/change"
83
83
  #
84
- # change = Matchi::Change.new("foo", :to_s)
85
- # change.from("foo")
84
+ # change_wrapper = Matchi::Change.new("foo", :to_s)
85
+ # change_wrapper.from("foo")
86
86
  #
87
- # @param expected [#object_id] The original value.
87
+ # @param old_value [#object_id] The original value.
88
88
  #
89
89
  # @return [#matches?] A *change from* wrapper.
90
- def from(expected)
91
- From.new(expected, &@state)
90
+ def from(old_value)
91
+ From.new(old_value, &@state)
92
92
  end
93
93
 
94
94
  # Specifies the new value to expect.
@@ -96,14 +96,14 @@ module Matchi
96
96
  # @example
97
97
  # require "matchi/change"
98
98
  #
99
- # change = Matchi::Change.new("foo", :to_s)
100
- # change.to("FOO")
99
+ # change_wrapper = Matchi::Change.new("foo", :to_s)
100
+ # change_wrapper.to("FOO")
101
101
  #
102
- # @param expected [#object_id] The new value to expect.
102
+ # @param new_value [#object_id] The new value to expect.
103
103
  #
104
104
  # @return [#matches?] A *change to* matcher.
105
- def to(expected)
106
- To.new(expected, &@state)
105
+ def to(new_value)
106
+ To.new(new_value, &@state)
107
107
  end
108
108
  end
109
109
  end
@@ -4,6 +4,9 @@ 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
+
7
10
  # Initialize the matcher with an object and a block.
8
11
  #
9
12
  # @example
@@ -30,28 +33,30 @@ module Matchi
30
33
  # object = []
31
34
  #
32
35
  # matcher = Matchi::Change::By.new(1) { object.length }
36
+ #
37
+ # matcher.expected # => 1
33
38
  # matcher.matches? { object << "foo" } # => true
34
39
  #
35
40
  # @yieldreturn [#object_id] The block of code to execute.
36
41
  #
37
42
  # @return [Boolean] Comparison between the value before and after the
38
43
  # code execution.
39
- def matches?(*, **)
44
+ def matches?
40
45
  value_before = @state.call
41
46
  yield
42
47
  value_after = @state.call
43
48
 
44
- @expected == (value_after - value_before)
49
+ expected == (value_after - value_before)
45
50
  end
46
51
 
47
52
  # A string containing a human-readable representation of the matcher.
48
53
  def inspect
49
- "#{self.class}(#{@expected.inspect})"
54
+ "#{self.class}(#{expected.inspect})"
50
55
  end
51
56
 
52
57
  # Returns a string representing the matcher.
53
58
  def to_s
54
- "change by #{@expected.inspect}"
59
+ "change by #{expected.inspect}"
55
60
  end
56
61
  end
57
62
  end
@@ -4,6 +4,9 @@ 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
+
7
10
  # Initialize the matcher with an object and a block.
8
11
  #
9
12
  # @example
@@ -30,28 +33,30 @@ module Matchi
30
33
  # object = []
31
34
  #
32
35
  # matcher = Matchi::Change::ByAtLeast.new(1) { object.length }
36
+ #
37
+ # matcher.expected # => 1
33
38
  # matcher.matches? { object << "foo" } # => true
34
39
  #
35
40
  # @yieldreturn [#object_id] The block of code to execute.
36
41
  #
37
42
  # @return [Boolean] Comparison between the value before and after the
38
43
  # code execution.
39
- def matches?(*, **)
44
+ def matches?
40
45
  value_before = @state.call
41
46
  yield
42
47
  value_after = @state.call
43
48
 
44
- @expected <= (value_after - value_before)
49
+ expected <= (value_after - value_before)
45
50
  end
46
51
 
47
52
  # A string containing a human-readable representation of the matcher.
48
53
  def inspect
49
- "#{self.class}(#{@expected.inspect})"
54
+ "#{self.class}(#{expected.inspect})"
50
55
  end
51
56
 
52
57
  # Returns a string representing the matcher.
53
58
  def to_s
54
- "change by at least #{@expected.inspect}"
59
+ "change by at least #{expected.inspect}"
55
60
  end
56
61
  end
57
62
  end
@@ -4,6 +4,9 @@ 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
+
7
10
  # Initialize the matcher with an object and a block.
8
11
  #
9
12
  # @example
@@ -30,28 +33,30 @@ module Matchi
30
33
  # object = []
31
34
  #
32
35
  # matcher = Matchi::Change::ByAtMost.new(1) { object.length }
36
+ #
37
+ # matcher.expected # => 1
33
38
  # matcher.matches? { object << "foo" } # => true
34
39
  #
35
40
  # @yieldreturn [#object_id] The block of code to execute.
36
41
  #
37
42
  # @return [Boolean] Comparison between the value before and after the
38
43
  # code execution.
39
- def matches?(*, **)
44
+ def matches?
40
45
  value_before = @state.call
41
46
  yield
42
47
  value_after = @state.call
43
48
 
44
- @expected >= (value_after - value_before)
49
+ expected >= (value_after - value_before)
45
50
  end
46
51
 
47
52
  # A string containing a human-readable representation of the matcher.
48
53
  def inspect
49
- "#{self.class}(#{@expected.inspect})"
54
+ "#{self.class}(#{expected.inspect})"
50
55
  end
51
56
 
52
57
  # Returns a string representing the matcher.
53
58
  def to_s
54
- "change by at most #{@expected.inspect}"
59
+ "change by at most #{expected.inspect}"
55
60
  end
56
61
  end
57
62
  end
@@ -30,8 +30,8 @@ module Matchi
30
30
  #
31
31
  # object = "foo"
32
32
  #
33
- # change = Matchi::Change::From.new("foo") { object.to_s }
34
- # change.to("FOO")
33
+ # change_from_wrapper = Matchi::Change::From.new("foo") { object.to_s }
34
+ # change_from_wrapper.to("FOO")
35
35
  #
36
36
  # @param expected_new_value [#object_id] The new value to expect.
37
37
  #
@@ -5,6 +5,9 @@ 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
+
8
11
  # Initialize the matcher with two objects and a block.
9
12
  #
10
13
  # @example
@@ -33,30 +36,32 @@ module Matchi
33
36
  # object = "foo"
34
37
  #
35
38
  # matcher = Matchi::Change::From::To.new("foo", "FOO") { object.to_s }
39
+ #
40
+ # matcher.expected # => "FOO"
36
41
  # matcher.matches? { object.upcase! } # => true
37
42
  #
38
43
  # @yieldreturn [#object_id] The block of code to execute.
39
44
  #
40
45
  # @return [Boolean] Comparison between the value before and after the
41
46
  # code execution.
42
- def matches?(*, **)
47
+ def matches?
43
48
  value_before = @state.call
44
49
  return false unless @expected_init == value_before
45
50
 
46
51
  yield
47
52
  value_after = @state.call
48
53
 
49
- @expected == value_after
54
+ expected == value_after
50
55
  end
51
56
 
52
57
  # A string containing a human-readable representation of the matcher.
53
58
  def inspect
54
- "#{self.class}(#{@expected_init.inspect}, #{@expected.inspect})"
59
+ "#{self.class}(#{@expected_init.inspect}, #{expected.inspect})"
55
60
  end
56
61
 
57
62
  # Returns a string representing the matcher.
58
63
  def to_s
59
- "change from #{@expected_init.inspect} to #{@expected.inspect}"
64
+ "change from #{@expected_init.inspect} to #{expected.inspect}"
60
65
  end
61
66
  end
62
67
  end
@@ -4,6 +4,9 @@ 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
+
7
10
  # Initialize the matcher with an object and a block.
8
11
  #
9
12
  # @example
@@ -13,7 +16,7 @@ module Matchi
13
16
  #
14
17
  # Matchi::Change::To.new("FOO") { object.to_s }
15
18
  #
16
- # @param expected [#object_id] An expected result value.
19
+ # @param expected [#object_id] An expected new value.
17
20
  # @param state [Proc] A block of code to execute to get the
18
21
  # state of the object.
19
22
  def initialize(expected, &state)
@@ -30,27 +33,29 @@ module Matchi
30
33
  # object = "foo"
31
34
  #
32
35
  # matcher = Matchi::Change::To.new("FOO") { object.to_s }
36
+ #
37
+ # matcher.expected # => "FOO"
33
38
  # matcher.matches? { object.upcase! } # => true
34
39
  #
35
40
  # @yieldreturn [#object_id] The block of code to execute.
36
41
  #
37
42
  # @return [Boolean] Comparison between the value before and after the
38
43
  # code execution.
39
- def matches?(*, **)
44
+ def matches?
40
45
  yield
41
46
  value_after = @state.call
42
47
 
43
- @expected == value_after
48
+ expected == value_after
44
49
  end
45
50
 
46
51
  # A string containing a human-readable representation of the matcher.
47
52
  def inspect
48
- "#{self.class}(#{@expected.inspect})"
53
+ "#{self.class}(#{expected.inspect})"
49
54
  end
50
55
 
51
56
  # Returns a string representing the matcher.
52
57
  def to_s
53
- "change to #{@expected.inspect}"
58
+ "change to #{expected.inspect}"
54
59
  end
55
60
  end
56
61
  end
data/lib/matchi/eq.rb CHANGED
@@ -3,6 +3,9 @@
3
3
  module Matchi
4
4
  # *Equivalence* matcher.
5
5
  class Eq
6
+ # @return [#eql?] An expected equivalent object.
7
+ attr_reader :expected
8
+
6
9
  # Initialize the matcher with an object.
7
10
  #
8
11
  # @example
@@ -21,24 +24,26 @@ module Matchi
21
24
  # require "matchi/eq"
22
25
  #
23
26
  # matcher = Matchi::Eq.new("foo")
27
+ #
28
+ # matcher.expected # => "foo"
24
29
  # matcher.matches? { "foo" } # => true
25
30
  #
26
31
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
32
  # one.
28
33
  #
29
34
  # @return [Boolean] Comparison between actual and expected values.
30
- def matches?(*, **)
31
- @expected.eql?(yield)
35
+ def matches?
36
+ expected.eql?(yield)
32
37
  end
33
38
 
34
39
  # A string containing a human-readable representation of the matcher.
35
40
  def inspect
36
- "#{self.class}(#{@expected.inspect})"
41
+ "#{self.class}(#{expected.inspect})"
37
42
  end
38
43
 
39
44
  # Returns a string representing the matcher.
40
45
  def to_s
41
- "eq #{@expected.inspect}"
46
+ "eq #{expected.inspect}"
42
47
  end
43
48
  end
44
49
  end
data/lib/matchi/match.rb CHANGED
@@ -3,6 +3,9 @@
3
3
  module Matchi
4
4
  # *Regular expressions* matcher.
5
5
  class Match
6
+ # @return [#match] A regular expression.
7
+ attr_reader :expected
8
+
6
9
  # Initialize the matcher with an instance of Regexp.
7
10
  #
8
11
  # @example
@@ -21,24 +24,26 @@ module Matchi
21
24
  # require "matchi/match"
22
25
  #
23
26
  # matcher = Matchi::Match.new(/^foo$/)
27
+ #
28
+ # matcher.expected # => /^foo$/
24
29
  # matcher.matches? { "foo" } # => true
25
30
  #
26
31
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
32
  # one.
28
33
  #
29
34
  # @return [Boolean] Comparison between actual and expected values.
30
- def matches?(*, **)
31
- @expected.match?(yield)
35
+ def matches?
36
+ expected.match?(yield)
32
37
  end
33
38
 
34
39
  # A string containing a human-readable representation of the matcher.
35
40
  def inspect
36
- "#{self.class}(#{@expected.inspect})"
41
+ "#{self.class}(#{expected.inspect})"
37
42
  end
38
43
 
39
44
  # Returns a string representing the matcher.
40
45
  def to_s
41
- "match #{@expected.inspect}"
46
+ "match #{expected.inspect}"
42
47
  end
43
48
  end
44
49
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matchi
4
+ # *Predicate* matcher.
5
+ class Predicate
6
+ # Initialize the matcher with a name and arguments.
7
+ #
8
+ # @example
9
+ # require "matchi/predicate"
10
+ #
11
+ # Matchi::Predicate.new(:be_empty)
12
+ #
13
+ # @param name [#to_s] A matcher name.
14
+ # @param args [Array] A list of parameters.
15
+ # @param kwargs [Hash] A list of keyword parameters.
16
+ # @param block [Proc] A block of code.
17
+ def initialize(name, *args, **kwargs, &block)
18
+ @name = String(name)
19
+
20
+ raise ::ArgumentError unless valid_name?
21
+
22
+ @args = args
23
+ @kwargs = kwargs
24
+ @block = block
25
+ end
26
+
27
+ # @return [Array] The method name with any arguments to send to the subject.
28
+ def expected
29
+ [method_name, @args, @kwargs, @block]
30
+ end
31
+
32
+ # Boolean comparison between the actual value and the expected value.
33
+ #
34
+ # @example
35
+ # require "matchi/predicate"
36
+ #
37
+ # matcher = Matchi::Predicate.new(:be_empty)
38
+ #
39
+ # matcher.expected # => [:empty?, [], {}, nil]
40
+ # matcher.matches? { [] } # => true
41
+ #
42
+ # @example
43
+ # require "matchi/predicate"
44
+ #
45
+ # matcher = Matchi::Predicate.new(:have_key, :foo)
46
+ #
47
+ # matcher.expected # => [:has_key?, [:foo], {}, nil]
48
+ # matcher.matches? { { foo: 42 } } # => true
49
+ #
50
+ # @yieldreturn [#object_id] The actual value to receive the method request.
51
+ #
52
+ # @return [Boolean] A boolean returned by the actual value being tested.
53
+ def matches?
54
+ value = yield.send(method_name, *@args, **@kwargs, &@block)
55
+ return value if [false, true].include?(value)
56
+
57
+ raise ::TypeError, "Boolean expected, but #{value.class} instance returned."
58
+ end
59
+
60
+ # A string containing a human-readable representation of the matcher.
61
+ def inspect
62
+ "#{self.class}(#{@name}, *#{@args.inspect}, **#{@kwargs.inspect}, &#{@block.inspect})"
63
+ end
64
+
65
+ # Returns a string representing the matcher.
66
+ def to_s
67
+ (
68
+ "#{@name.tr('_', ' ')} " + [
69
+ @args.map(&:inspect).join(", "),
70
+ @kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", "),
71
+ (@block.nil? ? "" : "&block")
72
+ ].reject { |i| i.eql?("") }.join(", ")
73
+ ).strip
74
+ end
75
+
76
+ private
77
+
78
+ # The name of the method to send to the object.
79
+ def method_name
80
+ if @name.start_with?("be_")
81
+ :"#{@name.gsub("be_", "")}?"
82
+ else
83
+ :"#{@name.gsub("have_", "has_")}?"
84
+ end
85
+ end
86
+
87
+ # Verify the matcher name structure.
88
+ def valid_name?
89
+ return false if @name.end_with?("?", "!")
90
+
91
+ @name.start_with?("be_", "have_")
92
+ end
93
+ end
94
+ end
@@ -3,6 +3,9 @@
3
3
  module Matchi
4
4
  # *Expecting errors* matcher.
5
5
  class RaiseException
6
+ # @return [String] The expected exception name.
7
+ attr_reader :expected
8
+
6
9
  # Initialize the matcher with a descendant of class Exception.
7
10
  #
8
11
  # @example
@@ -10,9 +13,9 @@ module Matchi
10
13
  #
11
14
  # Matchi::RaiseException.new(NameError)
12
15
  #
13
- # @param expected [Exception] The class of the expected exception.
16
+ # @param expected [Exception, #to_s] The expected exception name.
14
17
  def initialize(expected)
15
- @expected = expected
18
+ @expected = String(expected)
16
19
  end
17
20
 
18
21
  # Boolean comparison between the actual value and the expected value.
@@ -21,15 +24,17 @@ module Matchi
21
24
  # require "matchi/raise_exception"
22
25
  #
23
26
  # matcher = Matchi::RaiseException.new(NameError)
27
+ #
28
+ # matcher.expected # => "NameError"
24
29
  # matcher.matches? { Boom } # => true
25
30
  #
26
31
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
32
  # one.
28
33
  #
29
34
  # @return [Boolean] Comparison between actual and expected values.
30
- def matches?(*, **)
35
+ def matches?
31
36
  yield
32
- rescue @expected => _e
37
+ rescue self.class.const_get(expected) => _e
33
38
  true
34
39
  else
35
40
  false
@@ -37,12 +42,12 @@ module Matchi
37
42
 
38
43
  # A string containing a human-readable representation of the matcher.
39
44
  def inspect
40
- "#{self.class}(#{@expected.inspect})"
45
+ "#{self.class}(#{expected})"
41
46
  end
42
47
 
43
48
  # Returns a string representing the matcher.
44
49
  def to_s
45
- "raise exception #{@expected.inspect}"
50
+ "raise exception #{expected}"
46
51
  end
47
52
  end
48
53
  end
@@ -3,6 +3,9 @@
3
3
  module Matchi
4
4
  # *Satisfy* matcher.
5
5
  class Satisfy
6
+ # @return [Proc] A block of code.
7
+ attr_reader :expected
8
+
6
9
  # Initialize the matcher with a block.
7
10
  #
8
11
  # @example
@@ -21,14 +24,16 @@ module Matchi
21
24
  # require "matchi/satisfy"
22
25
  #
23
26
  # matcher = Matchi::Satisfy.new { |value| value == 42 }
27
+ #
28
+ # matcher.expected # => #<Proc:0x00007fbaafc65540>
24
29
  # matcher.matches? { 42 } # => true
25
30
  #
26
31
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
32
  # one.
28
33
  #
29
34
  # @return [Boolean] Comparison between actual and expected values.
30
- def matches?(*, **)
31
- @expected.call(yield)
35
+ def matches?
36
+ expected.call(yield)
32
37
  end
33
38
 
34
39
  # A string containing a human-readable representation of the matcher.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-22 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -133,6 +133,8 @@ files:
133
133
  - lib/matchi.rb
134
134
  - lib/matchi/be.rb
135
135
  - lib/matchi/be_an_instance_of.rb
136
+ - lib/matchi/be_within.rb
137
+ - lib/matchi/be_within/of.rb
136
138
  - lib/matchi/change.rb
137
139
  - lib/matchi/change/by.rb
138
140
  - lib/matchi/change/by_at_least.rb
@@ -142,6 +144,7 @@ files:
142
144
  - lib/matchi/change/to.rb
143
145
  - lib/matchi/eq.rb
144
146
  - lib/matchi/match.rb
147
+ - lib/matchi/predicate.rb
145
148
  - lib/matchi/raise_exception.rb
146
149
  - lib/matchi/satisfy.rb
147
150
  homepage: https://github.com/fixrb/matchi