matchi 2.2.1 → 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 +69 -74
- 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 -15
- 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/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
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,84 +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
|
-
|
|
85
|
+
matcher = Matchi::RaiseException.new(NameError)
|
|
86
|
+
matcher.matches? { Boom } # => true
|
|
75
87
|
```
|
|
76
88
|
|
|
77
|
-
**
|
|
89
|
+
**Type/class** matcher:
|
|
78
90
|
|
|
79
91
|
```ruby
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
matcher = Matchi::BeAnInstanceOf.new(:String)
|
|
93
|
+
matcher.matches? { "foo" } # => true
|
|
82
94
|
```
|
|
83
95
|
|
|
84
|
-
**
|
|
96
|
+
**Change** matcher:
|
|
85
97
|
|
|
86
98
|
```ruby
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
object = []
|
|
100
|
+
matcher = Matchi::Change.new(object, :length).by(1)
|
|
101
|
+
matcher.matches? { object << 1 } # => true
|
|
90
102
|
|
|
91
|
-
|
|
103
|
+
object = []
|
|
104
|
+
matcher = Matchi::Change.new(object, :length).by_at_least(1)
|
|
105
|
+
matcher.matches? { object << 1 } # => true
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
object = []
|
|
108
|
+
matcher = Matchi::Change.new(object, :length).by_at_most(1)
|
|
109
|
+
matcher.matches? { object << 1 } # => true
|
|
110
|
+
|
|
111
|
+
object = "foo"
|
|
112
|
+
matcher = Matchi::Change.new(object, :to_s).from("foo").to("FOO")
|
|
113
|
+
matcher.matches? { object.upcase! } # => true
|
|
114
|
+
|
|
115
|
+
object = "foo"
|
|
116
|
+
matcher = Matchi::Change.new(object, :to_s).to("FOO")
|
|
117
|
+
matcher.matches? { object.upcase! } # => true
|
|
96
118
|
```
|
|
97
119
|
|
|
98
|
-
**
|
|
120
|
+
**Satisfy** matcher:
|
|
99
121
|
|
|
100
122
|
```ruby
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
matcher = Matchi::Satisfy.new { |value| value == 42 }
|
|
124
|
+
matcher.matches? { 42 } # => true
|
|
103
125
|
```
|
|
104
126
|
|
|
105
127
|
### Custom matchers
|
|
106
128
|
|
|
107
|
-
Custom matchers can easily be
|
|
108
|
-
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.
|
|
109
130
|
|
|
110
131
|
A **Be the answer** matcher:
|
|
111
132
|
|
|
112
133
|
```ruby
|
|
113
134
|
module Matchi
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
42.equal?(yield)
|
|
118
|
-
end
|
|
135
|
+
class BeTheAnswer
|
|
136
|
+
def matches?
|
|
137
|
+
42.equal?(yield)
|
|
119
138
|
end
|
|
120
139
|
end
|
|
121
140
|
end
|
|
122
141
|
|
|
123
|
-
|
|
124
|
-
|
|
142
|
+
matcher = Matchi::BeTheAnswer.new
|
|
143
|
+
matcher.matches? { 42 } # => true
|
|
125
144
|
```
|
|
126
145
|
|
|
127
146
|
A **Be prime** matcher:
|
|
@@ -130,60 +149,36 @@ A **Be prime** matcher:
|
|
|
130
149
|
require "prime"
|
|
131
150
|
|
|
132
151
|
module Matchi
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Prime.prime?(yield)
|
|
137
|
-
end
|
|
152
|
+
class BePrime
|
|
153
|
+
def matches?
|
|
154
|
+
Prime.prime?(yield)
|
|
138
155
|
end
|
|
139
156
|
end
|
|
140
157
|
end
|
|
141
158
|
|
|
142
|
-
|
|
143
|
-
|
|
159
|
+
matcher = Matchi::BePrime.new
|
|
160
|
+
matcher.matches? { 42 } # => false
|
|
144
161
|
```
|
|
145
162
|
|
|
146
163
|
A **Start with** matcher:
|
|
147
164
|
|
|
148
165
|
```ruby
|
|
149
166
|
module Matchi
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def initialize(expected)
|
|
153
|
-
super()
|
|
154
|
-
@expected = expected
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def matches?
|
|
158
|
-
Regexp.new(/\A#{expected}/).match?(yield)
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
start_with = Matchi::Matcher::StartWith.new("foo")
|
|
165
|
-
start_with.matches? { "foobar" } # => true
|
|
166
|
-
```
|
|
167
|
+
class StartWith
|
|
168
|
+
attr_reader :expected
|
|
167
169
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
To do so, the `Helper` module can be included like this:
|
|
172
|
-
|
|
173
|
-
```ruby
|
|
174
|
-
require "matchi/helper"
|
|
170
|
+
def initialize(expected)
|
|
171
|
+
@expected = expected
|
|
172
|
+
end
|
|
175
173
|
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
def matches?
|
|
175
|
+
Regexp.new(/\A#{expected}/).match?(yield)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
178
|
end
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
The set of loaded matcher then becomes accessible via a dynamically generated instance method, like these:
|
|
182
179
|
|
|
183
|
-
|
|
184
|
-
matcher
|
|
185
|
-
matcher.equal(42).matches? { 44 } # => false
|
|
186
|
-
matcher.be_an_instance_of(:String).matches? { "안녕하세요" } # => true
|
|
180
|
+
matcher = Matchi::StartWith.new("foo")
|
|
181
|
+
matcher.matches? { "foobar" } # => true
|
|
187
182
|
```
|
|
188
183
|
|
|
189
184
|
## Contact
|
|
@@ -197,7 +192,7 @@ __Matchi__ follows [Semantic Versioning 2.0](https://semver.org/).
|
|
|
197
192
|
|
|
198
193
|
## License
|
|
199
194
|
|
|
200
|
-
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).
|
|
201
196
|
|
|
202
197
|
***
|
|
203
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
|