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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99d5b67cbb65b32a588adc888741aa1b43d86dd26835404388de5444bee6c54c
4
- data.tar.gz: 6188812229591bbb66a7fe458eaaaf4cd5707d00cefcc8502dfc3e4ed41195a2
3
+ metadata.gz: 16b38ee69034c01d5f2961a5d4e14b6c150cad89a9368f7c0cf84071761e2999
4
+ data.tar.gz: f76f7a3286443d49f6e449af9ffefdb2f38b9d974aada733e1a611d75c627199
5
5
  SHA512:
6
- metadata.gz: 3f00907f22ba9615eeab7541ddba62e6507f6c3609268699b4c20fda2435c338e9307fec63a942a9c49ad105131c6d29f47354fdf8d7e5d8b28fc418f060e9f0
7
- data.tar.gz: 4aeea7b5f52278b733c58180753e95d6ab88a47fbc162242ecf63bcfc3bc44ce018976fa544ef48887724d247e6fcb37ccc6372c89fedb0661d008dcc46061b5
6
+ metadata.gz: ed8abd4dd002520dc4740fc80b91573098e3242319e74b807e6621482beb131d4a40e299584fc507bacbb806867a00691a3f4bdcebf611dd2c45eb1b90e9e76e
7
+ data.tar.gz: 3fd0b621816f91d0569fdebeb4a8c80c55890f83250ed87b0df2cd1b7683e02f390df159f372f6e87f62aab853b7d8042d438f1efc31f83fd3b169a12eab2b8e
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
+ * 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
- eql = Matchi::Matcher::Eql.new("foo")
53
- eql.matches? { "foo" } # => true
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
- equal = Matchi::Matcher::Equal.new(:foo)
60
- equal.matches? { :foo } # => true
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
- match = Matchi::Matcher::Match.new(/^foo$/)
67
- match.matches? { "foo" } # => true
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
- raise_exception = Matchi::Matcher::RaiseException.new(NameError)
74
- raise_exception.matches? { Boom } # => true
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
- be_an_instance_of = Matchi::Matcher::BeAnInstanceOf.new(:String)
102
- be_an_instance_of.matches? { "foo" } # => true
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
- change = Matchi::Matcher::Change.new(object, :length).by(1)
110
- change.matches? { object << 1 } # => true
100
+ matcher = Matchi::Change.new(object, :length).by(1)
101
+ matcher.matches? { object << 1 } # => true
111
102
 
112
103
  object = []
113
- change = Matchi::Matcher::Change.new(object, :length).by_at_least(1)
114
- change.matches? { object << 1 } # => true
104
+ matcher = Matchi::Change.new(object, :length).by_at_least(1)
105
+ matcher.matches? { object << 1 } # => true
115
106
 
116
107
  object = []
117
- change = Matchi::Matcher::Change.new(object, :length).by_at_most(1)
118
- change.matches? { object << 1 } # => true
108
+ matcher = Matchi::Change.new(object, :length).by_at_most(1)
109
+ matcher.matches? { object << 1 } # => true
119
110
 
120
111
  object = "foo"
121
- change = Matchi::Matcher::Change.new(object, :to_s).from("foo").to("FOO")
122
- change.matches? { object.upcase! } # => true
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
- change = Matchi::Matcher::Change.new(object, :to_s).to("FOO")
126
- change.matches? { object.upcase! } # => true
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
- satisfy = Matchi::Matcher::Satisfy.new { |value| value == 42 }
133
- satisfy.matches? { 42 } # => true
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 defined for expressing expectations.
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
- module Matcher
146
- class BeTheAnswer < ::Matchi::Matcher::Base
147
- def matches?
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
- be_the_answer = Matchi::Matcher::BeTheAnswer.new
155
- be_the_answer.matches? { 42 } # => true
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
- module Matcher
165
- class BePrime < ::Matchi::Matcher::Base
166
- def matches?
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
- be_prime = Matchi::Matcher::BePrime.new
174
- be_prime.matches? { 42 } # => false
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
- 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
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
- ```ruby
205
- require "matchi/helper"
170
+ def initialize(expected)
171
+ @expected = expected
172
+ end
206
173
 
207
- class MatcherCollection
208
- include ::Matchi::Helper
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
- ```ruby
215
- matcher = MatcherCollection.new
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://opensource.org/licenses/MIT).
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
- # 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,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