matchi 2.4.0 → 3.0.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.
- checksums.yaml +4 -4
- data/README.md +59 -95
- data/lib/matchi.rb +4 -2
- data/lib/matchi/be.rb +44 -0
- data/lib/matchi/be_an_instance_of.rb +44 -0
- data/lib/matchi/change.rb +109 -0
- data/lib/matchi/change/by.rb +58 -0
- data/lib/matchi/change/by_at_least.rb +58 -0
- data/lib/matchi/change/by_at_most.rb +58 -0
- data/lib/matchi/change/from.rb +44 -0
- data/lib/matchi/change/from/to.rb +64 -0
- data/lib/matchi/change/to.rb +57 -0
- data/lib/matchi/eq.rb +44 -0
- data/lib/matchi/match.rb +44 -0
- data/lib/matchi/raise_exception.rb +48 -0
- data/lib/matchi/satisfy.rb +44 -0
- metadata +17 -23
- data/lib/matchi/helper.rb +0 -40
- data/lib/matchi/matcher.rb +0 -11
- data/lib/matchi/matcher/base.rb +0 -63
- data/lib/matchi/matcher/be_an_instance_of.rb +0 -51
- data/lib/matchi/matcher/be_false.rb +0 -24
- data/lib/matchi/matcher/be_nil.rb +0 -24
- data/lib/matchi/matcher/be_true.rb +0 -24
- data/lib/matchi/matcher/change.rb +0 -78
- data/lib/matchi/matcher/change/by.rb +0 -57
- data/lib/matchi/matcher/change/by_at_least.rb +0 -57
- data/lib/matchi/matcher/change/by_at_most.rb +0 -57
- data/lib/matchi/matcher/change/from.rb +0 -35
- data/lib/matchi/matcher/change/from/to.rb +0 -68
- data/lib/matchi/matcher/change/to.rb +0 -56
- data/lib/matchi/matcher/eql.rb +0 -35
- data/lib/matchi/matcher/equal.rb +0 -35
- data/lib/matchi/matcher/match.rb +0 -35
- data/lib/matchi/matcher/raise_exception.rb +0 -39
- data/lib/matchi/matcher/satisfy.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16b38ee69034c01d5f2961a5d4e14b6c150cad89a9368f7c0cf84071761e2999
|
4
|
+
data.tar.gz: f76f7a3286443d49f6e449af9ffefdb2f38b9d974aada733e1a611d75c627199
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed8abd4dd002520dc4740fc80b91573098e3242319e74b807e6621482beb131d4a40e299584fc507bacbb806867a00691a3f4bdcebf611dd2c45eb1b90e9e76e
|
7
|
+
data.tar.gz: 3fd0b621816f91d0569fdebeb4a8c80c55890f83250ed87b0df2cd1b7683e02f390df159f372f6e87f62aab853b7d8042d438f1efc31f83fd3b169a12eab2b8e
|
data/README.md
CHANGED
@@ -6,9 +6,15 @@
|
|
6
6
|
[](https://github.com/fixrb/matchi/actions?query=workflow%3Arubocop+branch%3Amain)
|
7
7
|
[](https://github.com/fixrb/matchi/raw/main/LICENSE.md)
|
8
8
|
|
9
|
-
> Collection of expectation matchers for
|
9
|
+
> Collection of expectation matchers for Rubyists 🤹
|
10
10
|
|
11
|
-

|
12
|
+
|
13
|
+
## Project goals
|
14
|
+
|
15
|
+
* Provide a collection of useful generic matchers.
|
16
|
+
* Adding matchers should be as simple as possible.
|
17
|
+
* Being framework agnostic and easy to integrate.
|
12
18
|
|
13
19
|
## Installation
|
14
20
|
|
@@ -44,115 +50,97 @@ 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 just an object that responds to the `matches?` method with a block as argument, and returns a boolean. That's all it is.
|
56
|
+
|
57
|
+
But let's see some examples.
|
58
|
+
|
47
59
|
### Built-in matchers
|
48
60
|
|
49
61
|
**Equivalence** matcher:
|
50
62
|
|
51
63
|
```ruby
|
52
|
-
|
53
|
-
|
64
|
+
matcher = Matchi::Eq.new("foo")
|
65
|
+
matcher.matches? { "foo" } # => true
|
54
66
|
```
|
55
67
|
|
56
68
|
**Identity** matcher:
|
57
69
|
|
58
70
|
```ruby
|
59
|
-
|
60
|
-
|
71
|
+
matcher = Matchi::Be.new(:foo)
|
72
|
+
matcher.matches? { :foo } # => true
|
61
73
|
```
|
62
74
|
|
63
75
|
**Regular expressions** matcher:
|
64
76
|
|
65
77
|
```ruby
|
66
|
-
|
67
|
-
|
78
|
+
matcher = Matchi::Match.new(/^foo$/)
|
79
|
+
matcher.matches? { "foo" } # => true
|
68
80
|
```
|
69
81
|
|
70
82
|
**Expecting errors** matcher:
|
71
83
|
|
72
84
|
```ruby
|
73
|
-
|
74
|
-
|
75
|
-
```
|
76
|
-
|
77
|
-
**Truth** matcher:
|
78
|
-
|
79
|
-
```ruby
|
80
|
-
be_true = Matchi::Matcher::BeTrue.new
|
81
|
-
be_true.matches? { true } # => true
|
82
|
-
```
|
83
|
-
|
84
|
-
**Untruth** matcher:
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
be_false = Matchi::Matcher::BeFalse.new
|
88
|
-
be_false.matches? { false } # => true
|
89
|
-
```
|
90
|
-
|
91
|
-
**Nil** matcher:
|
92
|
-
|
93
|
-
```ruby
|
94
|
-
be_nil = Matchi::Matcher::BeNil.new
|
95
|
-
be_nil.matches? { nil } # => true
|
85
|
+
matcher = Matchi::RaiseException.new(NameError)
|
86
|
+
matcher.matches? { Boom } # => true
|
96
87
|
```
|
97
88
|
|
98
89
|
**Type/class** matcher:
|
99
90
|
|
100
91
|
```ruby
|
101
|
-
|
102
|
-
|
92
|
+
matcher = Matchi::BeAnInstanceOf.new(:String)
|
93
|
+
matcher.matches? { "foo" } # => true
|
103
94
|
```
|
104
95
|
|
105
96
|
**Change** matcher:
|
106
97
|
|
107
98
|
```ruby
|
108
99
|
object = []
|
109
|
-
|
110
|
-
|
100
|
+
matcher = Matchi::Change.new(object, :length).by(1)
|
101
|
+
matcher.matches? { object << 1 } # => true
|
111
102
|
|
112
103
|
object = []
|
113
|
-
|
114
|
-
|
104
|
+
matcher = Matchi::Change.new(object, :length).by_at_least(1)
|
105
|
+
matcher.matches? { object << 1 } # => true
|
115
106
|
|
116
107
|
object = []
|
117
|
-
|
118
|
-
|
108
|
+
matcher = Matchi::Change.new(object, :length).by_at_most(1)
|
109
|
+
matcher.matches? { object << 1 } # => true
|
119
110
|
|
120
111
|
object = "foo"
|
121
|
-
|
122
|
-
|
112
|
+
matcher = Matchi::Change.new(object, :to_s).from("foo").to("FOO")
|
113
|
+
matcher.matches? { object.upcase! } # => true
|
123
114
|
|
124
115
|
object = "foo"
|
125
|
-
|
126
|
-
|
116
|
+
matcher = Matchi::Change.new(object, :to_s).to("FOO")
|
117
|
+
matcher.matches? { object.upcase! } # => true
|
127
118
|
```
|
128
119
|
|
129
120
|
**Satisfy** matcher:
|
130
121
|
|
131
122
|
```ruby
|
132
|
-
|
133
|
-
|
123
|
+
matcher = Matchi::Satisfy.new { |value| value == 42 }
|
124
|
+
matcher.matches? { 42 } # => true
|
134
125
|
```
|
135
126
|
|
136
127
|
### Custom matchers
|
137
128
|
|
138
|
-
Custom matchers can easily be
|
139
|
-
They can be any Ruby class that responds to `matches?` instance method with a block.
|
129
|
+
Custom matchers can easily be added to express more specific expectations.
|
140
130
|
|
141
131
|
A **Be the answer** matcher:
|
142
132
|
|
143
133
|
```ruby
|
144
134
|
module Matchi
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
42.equal?(yield)
|
149
|
-
end
|
135
|
+
class BeTheAnswer
|
136
|
+
def matches?
|
137
|
+
42.equal?(yield)
|
150
138
|
end
|
151
139
|
end
|
152
140
|
end
|
153
141
|
|
154
|
-
|
155
|
-
|
142
|
+
matcher = Matchi::BeTheAnswer.new
|
143
|
+
matcher.matches? { 42 } # => true
|
156
144
|
```
|
157
145
|
|
158
146
|
A **Be prime** matcher:
|
@@ -161,60 +149,36 @@ A **Be prime** matcher:
|
|
161
149
|
require "prime"
|
162
150
|
|
163
151
|
module Matchi
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
Prime.prime?(yield)
|
168
|
-
end
|
152
|
+
class BePrime
|
153
|
+
def matches?
|
154
|
+
Prime.prime?(yield)
|
169
155
|
end
|
170
156
|
end
|
171
157
|
end
|
172
158
|
|
173
|
-
|
174
|
-
|
159
|
+
matcher = Matchi::BePrime.new
|
160
|
+
matcher.matches? { 42 } # => false
|
175
161
|
```
|
176
162
|
|
177
163
|
A **Start with** matcher:
|
178
164
|
|
179
165
|
```ruby
|
180
166
|
module Matchi
|
181
|
-
|
182
|
-
|
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
|
194
|
-
|
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:
|
167
|
+
class StartWith
|
168
|
+
attr_reader :expected
|
203
169
|
|
204
|
-
|
205
|
-
|
170
|
+
def initialize(expected)
|
171
|
+
@expected = expected
|
172
|
+
end
|
206
173
|
|
207
|
-
|
208
|
-
|
174
|
+
def matches?
|
175
|
+
Regexp.new(/\A#{expected}/).match?(yield)
|
176
|
+
end
|
177
|
+
end
|
209
178
|
end
|
210
|
-
```
|
211
|
-
|
212
|
-
The set of loaded matcher then becomes accessible via a dynamically generated instance method, like these:
|
213
179
|
|
214
|
-
|
215
|
-
matcher
|
216
|
-
matcher.equal(42).matches? { 44 } # => false
|
217
|
-
matcher.be_an_instance_of(:String).matches? { "안녕하세요" } # => true
|
180
|
+
matcher = Matchi::StartWith.new("foo")
|
181
|
+
matcher.matches? { "foobar" } # => true
|
218
182
|
```
|
219
183
|
|
220
184
|
## Contact
|
@@ -228,7 +192,7 @@ __Matchi__ follows [Semantic Versioning 2.0](https://semver.org/).
|
|
228
192
|
|
229
193
|
## License
|
230
194
|
|
231
|
-
The [gem](https://rubygems.org/gems/matchi) is available as open source under the terms of the [MIT License](https://
|
195
|
+
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
196
|
|
233
197
|
***
|
234
198
|
|
data/lib/matchi.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
3
|
+
# A collection of damn simple expectation matchers.
|
4
4
|
#
|
5
5
|
# @api public
|
6
6
|
module Matchi
|
7
7
|
end
|
8
8
|
|
9
|
-
|
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,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Matchi
|
4
|
+
# *Identity* matcher.
|
5
|
+
class Be
|
6
|
+
# Initialize the matcher with an object.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# require "matchi/be"
|
10
|
+
#
|
11
|
+
# Matchi::Be.new(:foo)
|
12
|
+
#
|
13
|
+
# @param expected [#equal?] The expected identical object.
|
14
|
+
def initialize(expected)
|
15
|
+
@expected = expected
|
16
|
+
end
|
17
|
+
|
18
|
+
# Boolean comparison between the actual value and the expected value.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# require "matchi/be"
|
22
|
+
#
|
23
|
+
# matcher = Matchi::Be.new(:foo)
|
24
|
+
# matcher.matches? { :foo } # => true
|
25
|
+
#
|
26
|
+
# @yieldreturn [#object_id] The actual value to compare to the expected
|
27
|
+
# one.
|
28
|
+
#
|
29
|
+
# @return [Boolean] Comparison between actual and expected values.
|
30
|
+
def matches?(*, **)
|
31
|
+
@expected.equal?(yield)
|
32
|
+
end
|
33
|
+
|
34
|
+
# A string containing a human-readable representation of the matcher.
|
35
|
+
def inspect
|
36
|
+
"#{self.class}(#{@expected.inspect})"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a string representing the matcher.
|
40
|
+
def to_s
|
41
|
+
"be #{@expected.inspect}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Matchi
|
4
|
+
# *Type/class* matcher.
|
5
|
+
class BeAnInstanceOf
|
6
|
+
# Initialize the matcher with (the name of) a class or module.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# require "matchi/be_an_instance_of"
|
10
|
+
#
|
11
|
+
# Matchi::BeAnInstanceOf.new(String)
|
12
|
+
#
|
13
|
+
# @param expected [#to_s] A (name of a) class or module.
|
14
|
+
def initialize(expected)
|
15
|
+
@expected = String(expected).to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
# Boolean comparison between the class of the actual value and the
|
19
|
+
# expected class.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# require "matchi/be_an_instance_of"
|
23
|
+
#
|
24
|
+
# matcher = Matchi::BeAnInstanceOf.new(String)
|
25
|
+
# matcher.matches? { "foo" } # => true
|
26
|
+
#
|
27
|
+
# @yieldreturn [#class] the actual value to compare to the expected one.
|
28
|
+
#
|
29
|
+
# @return [Boolean] Comparison between actual and expected values.
|
30
|
+
def matches?(*, **)
|
31
|
+
self.class.const_get(@expected).equal?(yield.class)
|
32
|
+
end
|
33
|
+
|
34
|
+
# A string containing a human-readable representation of the matcher.
|
35
|
+
def inspect
|
36
|
+
"#{self.class}(#{@expected})"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a string representing the matcher.
|
40
|
+
def to_s
|
41
|
+
"be an instance of #{@expected}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative File.join("change", "by_at_least")
|
4
|
+
require_relative File.join("change", "by_at_most")
|
5
|
+
require_relative File.join("change", "by")
|
6
|
+
require_relative File.join("change", "from")
|
7
|
+
require_relative File.join("change", "to")
|
8
|
+
|
9
|
+
module Matchi
|
10
|
+
# Wraps the target of a change matcher.
|
11
|
+
class Change
|
12
|
+
# Initialize a wrapper of the change matcher with an object and the name of
|
13
|
+
# one of its methods.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# require "matchi/change"
|
17
|
+
#
|
18
|
+
# Matchi::Change.new("foo", :to_s)
|
19
|
+
#
|
20
|
+
# @param object [#object_id] An object.
|
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
|
+
def initialize(object, method, *args, **kwargs, &block)
|
25
|
+
@state = -> { object.send(method, *args, **kwargs, &block) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Specifies a minimum delta of the expected change.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# require "matchi/change"
|
32
|
+
#
|
33
|
+
# object = []
|
34
|
+
#
|
35
|
+
# change = Matchi::Change.new(object, :length)
|
36
|
+
# change.by_at_least(1)
|
37
|
+
#
|
38
|
+
# @param expected [#object_id] The minimum delta of the expected change.
|
39
|
+
#
|
40
|
+
# @return [#matches?] A *change by at least* matcher.
|
41
|
+
def by_at_least(expected)
|
42
|
+
ByAtLeast.new(expected, &@state)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Specifies a maximum delta of the expected change.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# require "matchi/change"
|
49
|
+
#
|
50
|
+
# object = []
|
51
|
+
#
|
52
|
+
# change = Matchi::Change.new(object, :length)
|
53
|
+
# change.by_at_most(1)
|
54
|
+
#
|
55
|
+
# @param expected [#object_id] The maximum delta of the expected change.
|
56
|
+
#
|
57
|
+
# @return [#matches?] A *change by at most* matcher.
|
58
|
+
def by_at_most(expected)
|
59
|
+
ByAtMost.new(expected, &@state)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Specifies the delta of the expected change.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# require "matchi/change"
|
66
|
+
#
|
67
|
+
# object = []
|
68
|
+
#
|
69
|
+
# change = Matchi::Change.new(object, :length)
|
70
|
+
# change.by(1)
|
71
|
+
#
|
72
|
+
# @param expected [#object_id] The delta of the expected change.
|
73
|
+
#
|
74
|
+
# @return [#matches?] A *change by* matcher.
|
75
|
+
def by(expected)
|
76
|
+
By.new(expected, &@state)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Specifies the original value.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# require "matchi/change"
|
83
|
+
#
|
84
|
+
# change = Matchi::Change.new("foo", :to_s)
|
85
|
+
# change.from("foo")
|
86
|
+
#
|
87
|
+
# @param expected [#object_id] The original value.
|
88
|
+
#
|
89
|
+
# @return [#matches?] A *change from* wrapper.
|
90
|
+
def from(expected)
|
91
|
+
From.new(expected, &@state)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Specifies the new value to expect.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# require "matchi/change"
|
98
|
+
#
|
99
|
+
# change = Matchi::Change.new("foo", :to_s)
|
100
|
+
# change.to("FOO")
|
101
|
+
#
|
102
|
+
# @param expected [#object_id] The new value to expect.
|
103
|
+
#
|
104
|
+
# @return [#matches?] A *change to* matcher.
|
105
|
+
def to(expected)
|
106
|
+
To.new(expected, &@state)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|