matchi 2.4.0 → 3.0.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: 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