matchi 2.4.0 → 3.2.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: 99d5b67cbb65b32a588adc888741aa1b43d86dd26835404388de5444bee6c54c
4
- data.tar.gz: 6188812229591bbb66a7fe458eaaaf4cd5707d00cefcc8502dfc3e4ed41195a2
3
+ metadata.gz: aabf9e773ac6210d1170b3a0f3aa19f38845ea0215bb07280d97c35ef66add16
4
+ data.tar.gz: 2900c9b936d50bfbe50ba31c6418dcb1402c1fbef5cfbb9f7a1ce289ff6450c0
5
5
  SHA512:
6
- metadata.gz: 3f00907f22ba9615eeab7541ddba62e6507f6c3609268699b4c20fda2435c338e9307fec63a942a9c49ad105131c6d29f47354fdf8d7e5d8b28fc418f060e9f0
7
- data.tar.gz: 4aeea7b5f52278b733c58180753e95d6ab88a47fbc162242ecf63bcfc3bc44ce018976fa544ef48887724d247e6fcb37ccc6372c89fedb0661d008dcc46061b5
6
+ metadata.gz: e2cfb5b1c32a01b84c1e2eec84cfc3ad34df2a7a17c91ecb1e6012ef84d5b4ef5b11f24817de7dcea77e08006e72c578d0d88947470436f231efef8fed18f061
7
+ data.tar.gz: 235a73246185fec310960caf6b8c868cbc2daf7598f29c863def9b560ec66ba7a0f43ab303cd164b04c81b5ebbcbddae3224c5550002f9ab924f8202f58a289f
data/README.md CHANGED
@@ -6,9 +6,15 @@
6
6
  [![RuboCop](https://github.com/fixrb/matchi/workflows/RuboCop/badge.svg?branch=main)](https://github.com/fixrb/matchi/actions?query=workflow%3Arubocop+branch%3Amain)
7
7
  [![License](https://img.shields.io/github/license/fixrb/matchi?label=License&logo=github)](https://github.com/fixrb/matchi/raw/main/LICENSE.md)
8
8
 
9
- > Collection of expectation matchers for Ruby 🤹
9
+ > Collection of expectation matchers for Rubyists 🤹
10
10
 
11
- ![A rubyist juggling between colored balls representing expectation matchers](https://github.com/fixrb/matchi/raw/main/img/matchi.jpg)
11
+ ![A Rubyist juggling between Matchi letters](https://github.com/fixrb/matchi/raw/main/img/matchi.jpg)
12
+
13
+ ## Project goals
14
+
15
+ * Adding matchers should be as simple as possible.
16
+ * Being framework agnostic and easy to integrate.
17
+ * Avoid false positives/negatives due to malicious actual values.
12
18
 
13
19
  ## Installation
14
20
 
@@ -44,115 +50,136 @@ require "matchi"
44
50
 
45
51
  All examples here assume that this has been done.
46
52
 
53
+ ### Anatomy of a matcher
54
+
55
+ A __Matchi__ matcher is an object that must respond to the `matches?` method with a block as argument, and return a boolean.
56
+
57
+ To facilitate the integration of the matchers in other tools, __Matchi__ matchers may expose expected values via the `expected` method.
58
+
47
59
  ### Built-in matchers
48
60
 
61
+ Here is the collection of useful generic matchers.
62
+
49
63
  **Equivalence** matcher:
50
64
 
51
65
  ```ruby
52
- eql = Matchi::Matcher::Eql.new("foo")
53
- eql.matches? { "foo" } # => true
66
+ matcher = Matchi::Eq.new("foo")
67
+
68
+ matcher.expected # => "foo"
69
+ matcher.matches? { "foo" } # => true
54
70
  ```
55
71
 
56
72
  **Identity** matcher:
57
73
 
58
74
  ```ruby
59
- equal = Matchi::Matcher::Equal.new(:foo)
60
- equal.matches? { :foo } # => true
61
- ```
75
+ matcher = Matchi::Be.new(:foo)
62
76
 
63
- **Regular expressions** matcher:
64
-
65
- ```ruby
66
- match = Matchi::Matcher::Match.new(/^foo$/)
67
- match.matches? { "foo" } # => true
77
+ matcher.expected # => :foo
78
+ matcher.matches? { :foo } # => true
68
79
  ```
69
80
 
70
- **Expecting errors** matcher:
81
+ **Comparisons** matcher:
71
82
 
72
83
  ```ruby
73
- raise_exception = Matchi::Matcher::RaiseException.new(NameError)
74
- raise_exception.matches? { Boom } # => true
75
- ```
76
-
77
- **Truth** matcher:
84
+ matcher = Matchi::BeWithin.new(8).of(37)
78
85
 
79
- ```ruby
80
- be_true = Matchi::Matcher::BeTrue.new
81
- be_true.matches? { true } # => true
86
+ matcher.expected # => 37
87
+ matcher.matches? { 42 } # => true
82
88
  ```
83
89
 
84
- **Untruth** matcher:
90
+ **Regular expressions** matcher:
85
91
 
86
92
  ```ruby
87
- be_false = Matchi::Matcher::BeFalse.new
88
- be_false.matches? { false } # => true
93
+ matcher = Matchi::Match.new(/^foo$/)
94
+
95
+ matcher.expected # => /^foo$/
96
+ matcher.matches? { "foo" } # => true
89
97
  ```
90
98
 
91
- **Nil** matcher:
99
+ **Expecting errors** matcher:
92
100
 
93
101
  ```ruby
94
- be_nil = Matchi::Matcher::BeNil.new
95
- be_nil.matches? { nil } # => true
102
+ matcher = Matchi::RaiseException.new(:NameError)
103
+
104
+ matcher.expected # => "NameError"
105
+ matcher.matches? { Boom } # => true
96
106
  ```
97
107
 
98
108
  **Type/class** matcher:
99
109
 
100
110
  ```ruby
101
- be_an_instance_of = Matchi::Matcher::BeAnInstanceOf.new(:String)
102
- be_an_instance_of.matches? { "foo" } # => true
111
+ matcher = Matchi::BeAnInstanceOf.new(:String)
112
+
113
+ matcher.expected # => "String"
114
+ matcher.matches? { "foo" } # => true
103
115
  ```
104
116
 
105
117
  **Change** matcher:
106
118
 
107
119
  ```ruby
108
120
  object = []
109
- change = Matchi::Matcher::Change.new(object, :length).by(1)
110
- change.matches? { object << 1 } # => true
121
+ matcher = Matchi::Change.new(object, :length).by(1)
122
+
123
+ matcher.expected # => 1
124
+ matcher.matches? { object << 1 } # => true
111
125
 
112
126
  object = []
113
- change = Matchi::Matcher::Change.new(object, :length).by_at_least(1)
114
- change.matches? { object << 1 } # => true
127
+ matcher = Matchi::Change.new(object, :length).by_at_least(1)
128
+
129
+ matcher.expected # => 1
130
+ matcher.matches? { object << 1 } # => true
115
131
 
116
132
  object = []
117
- change = Matchi::Matcher::Change.new(object, :length).by_at_most(1)
118
- change.matches? { object << 1 } # => true
133
+ matcher = Matchi::Change.new(object, :length).by_at_most(1)
134
+
135
+ matcher.expected # => 1
136
+ matcher.matches? { object << 1 } # => true
119
137
 
120
138
  object = "foo"
121
- change = Matchi::Matcher::Change.new(object, :to_s).from("foo").to("FOO")
122
- change.matches? { object.upcase! } # => true
139
+ matcher = Matchi::Change.new(object, :to_s).from("foo").to("FOO")
140
+
141
+ matcher.expected # => "FOO"
142
+ matcher.matches? { object.upcase! } # => true
123
143
 
124
144
  object = "foo"
125
- change = Matchi::Matcher::Change.new(object, :to_s).to("FOO")
126
- change.matches? { object.upcase! } # => true
145
+ matcher = Matchi::Change.new(object, :to_s).to("FOO")
146
+
147
+ matcher.expected # => "FOO"
148
+ matcher.matches? { object.upcase! } # => true
127
149
  ```
128
150
 
129
151
  **Satisfy** matcher:
130
152
 
131
153
  ```ruby
132
- satisfy = Matchi::Matcher::Satisfy.new { |value| value == 42 }
133
- satisfy.matches? { 42 } # => true
154
+ matcher = Matchi::Satisfy.new { |value| value == 42 }
155
+
156
+ matcher.expected # => #<Proc:0x00007fbaafc65540>
157
+ matcher.matches? { 42 } # => true
134
158
  ```
135
159
 
136
160
  ### Custom matchers
137
161
 
138
- Custom matchers can easily be defined for expressing expectations.
139
- They can be any Ruby class that responds to `matches?` instance method with a block.
162
+ Custom matchers can easily be added to express more specific expectations.
140
163
 
141
164
  A **Be the answer** matcher:
142
165
 
143
166
  ```ruby
144
167
  module Matchi
145
- module Matcher
146
- class BeTheAnswer < ::Matchi::Matcher::Base
147
- def matches?
148
- 42.equal?(yield)
149
- end
168
+ class BeTheAnswer
169
+ def expected
170
+ 42
171
+ end
172
+
173
+ def matches?
174
+ expected.equal?(yield)
150
175
  end
151
176
  end
152
177
  end
153
178
 
154
- be_the_answer = Matchi::Matcher::BeTheAnswer.new
155
- be_the_answer.matches? { 42 } # => true
179
+ matcher = Matchi::BeTheAnswer.new
180
+
181
+ matcher.expected # => 42
182
+ matcher.matches? { 42 } # => true
156
183
  ```
157
184
 
158
185
  A **Be prime** matcher:
@@ -161,60 +188,39 @@ A **Be prime** matcher:
161
188
  require "prime"
162
189
 
163
190
  module Matchi
164
- module Matcher
165
- class BePrime < ::Matchi::Matcher::Base
166
- def matches?
167
- Prime.prime?(yield)
168
- end
191
+ class BePrime
192
+ def matches?
193
+ Prime.prime?(yield)
169
194
  end
170
195
  end
171
196
  end
172
197
 
173
- be_prime = Matchi::Matcher::BePrime.new
174
- be_prime.matches? { 42 } # => false
198
+ matcher = Matchi::BePrime.new
199
+
200
+ matcher.matches? { 42 } # => false
175
201
  ```
176
202
 
177
203
  A **Start with** matcher:
178
204
 
179
205
  ```ruby
180
206
  module Matchi
181
- module Matcher
182
- class StartWith < ::Matchi::Matcher::Base
183
- def initialize(expected)
184
- super()
185
- @expected = expected
186
- end
187
-
188
- def matches?
189
- Regexp.new(/\A#{expected}/).match?(yield)
190
- end
191
- end
192
- end
193
- end
207
+ class StartWith
208
+ attr_reader :expected
194
209
 
195
- start_with = Matchi::Matcher::StartWith.new("foo")
196
- start_with.matches? { "foobar" } # => true
197
- ```
198
-
199
- ### Helper methods
200
-
201
- For convenience, it is possible to instantiate a matcher with a method rather than with its class.
202
- To do so, the `Helper` module can be included like this:
203
-
204
- ```ruby
205
- require "matchi/helper"
210
+ def initialize(expected)
211
+ @expected = expected
212
+ end
206
213
 
207
- class MatcherCollection
208
- include ::Matchi::Helper
214
+ def matches?
215
+ Regexp.new(/\A#{expected}/).match?(yield)
216
+ end
217
+ end
209
218
  end
210
- ```
211
219
 
212
- The set of loaded matcher then becomes accessible via a dynamically generated instance method, like these:
220
+ matcher = Matchi::StartWith.new("foo")
213
221
 
214
- ```ruby
215
- matcher = MatcherCollection.new
216
- matcher.equal(42).matches? { 44 } # => false
217
- matcher.be_an_instance_of(:String).matches? { "안녕하세요" } # => true
222
+ matcher.expected # => "foo"
223
+ matcher.matches? { "foobar" } # => true
218
224
  ```
219
225
 
220
226
  ## Contact
@@ -228,7 +234,7 @@ __Matchi__ follows [Semantic Versioning 2.0](https://semver.org/).
228
234
 
229
235
  ## License
230
236
 
231
- The [gem](https://rubygems.org/gems/matchi) is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
237
+ The [gem](https://rubygems.org/gems/matchi) is available as open source under the terms of the [MIT License](https://github.com/fixrb/matchi/raw/main/LICENSE.md).
232
238
 
233
239
  ***
234
240
 
data/lib/matchi.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Namespace for the Matchi library.
3
+ # A collection of damn simple expectation matchers.
4
4
  #
5
5
  # @api public
6
6
  module Matchi
7
7
  end
8
8
 
9
- require_relative File.join("matchi", "matcher")
9
+ Dir[File.join(File.dirname(__FILE__), "matchi", "*.rb")].each do |fname|
10
+ require_relative fname
11
+ end
data/lib/matchi/be.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matchi
4
+ # *Identity* matcher.
5
+ class Be
6
+ # @return [#equal?] The expected identical object.
7
+ attr_reader :expected
8
+
9
+ # Initialize the matcher with an object.
10
+ #
11
+ # @example
12
+ # require "matchi/be"
13
+ #
14
+ # Matchi::Be.new(:foo)
15
+ #
16
+ # @param expected [#equal?] The expected identical object.
17
+ def initialize(expected)
18
+ @expected = expected
19
+ end
20
+
21
+ # Boolean comparison between the actual value and the expected value.
22
+ #
23
+ # @example
24
+ # require "matchi/be"
25
+ #
26
+ # matcher = Matchi::Be.new(:foo)
27
+ #
28
+ # matcher.expected # => :foo
29
+ # matcher.matches? { :foo } # => true
30
+ #
31
+ # @yieldreturn [#object_id] The actual value to compare to the expected
32
+ # one.
33
+ #
34
+ # @return [Boolean] Comparison between actual and expected values.
35
+ def matches?
36
+ expected.equal?(yield)
37
+ end
38
+
39
+ # A string containing a human-readable representation of the matcher.
40
+ def inspect
41
+ "#{self.class}(#{expected.inspect})"
42
+ end
43
+
44
+ # Returns a string representing the matcher.
45
+ def to_s
46
+ "be #{expected.inspect}"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Matchi
4
+ # *Type/class* matcher.
5
+ class BeAnInstanceOf
6
+ # @return [String] The expected class name.
7
+ attr_reader :expected
8
+
9
+ # Initialize the matcher with (the name of) a class or module.
10
+ #
11
+ # @example
12
+ # require "matchi/be_an_instance_of"
13
+ #
14
+ # Matchi::BeAnInstanceOf.new(String)
15
+ #
16
+ # @param expected [Class, #to_s] The expected class name.
17
+ def initialize(expected)
18
+ @expected = String(expected)
19
+ end
20
+
21
+ # Boolean comparison between the class of the actual value and the
22
+ # expected class.
23
+ #
24
+ # @example
25
+ # require "matchi/be_an_instance_of"
26
+ #
27
+ # matcher = Matchi::BeAnInstanceOf.new(String)
28
+ #
29
+ # matcher.expected # => "String"
30
+ # matcher.matches? { "foo" } # => true
31
+ #
32
+ # @yieldreturn [#class] the actual value to compare to the expected one.
33
+ #
34
+ # @return [Boolean] Comparison between actual and expected values.
35
+ def matches?
36
+ self.class.const_get(expected).equal?(yield.class)
37
+ end
38
+
39
+ # A string containing a human-readable representation of the matcher.
40
+ def inspect
41
+ "#{self.class}(#{expected})"
42
+ end
43
+
44
+ # Returns a string representing the matcher.
45
+ def to_s
46
+ "be an instance of #{expected}"
47
+ end
48
+ end
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