matchi 2.1.0 β†’ 2.3.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: c15a21ea2bcdc3ea9c43344bb1ff12216a384bc3d19a996c39a1138c28d18150
4
- data.tar.gz: d023d2472ced57399fd14aea2a35cd36b75f7a9ac3705efd48985ccc03a6b9df
3
+ metadata.gz: 96a52462a574281d4d485558a112fec5cef2e00f1fb90c13987407c18302887c
4
+ data.tar.gz: 00511b2878ebc9bbe2deb07f888944c9b2e2f2cebf9d8a78d1fe40e1a2b923c7
5
5
  SHA512:
6
- metadata.gz: be27946a8a1aa3d24b978603be5efc2ce29eea9a18455dba435276329388fd1448c8e0f770b649ca34ad0cfce711a4b34939d665254741406f4bfa7602342475
7
- data.tar.gz: dcdf2ece3a08c3dd266a9f01ebcb76013f3ab6e699b2bb8205656f7e3cd3ba76f786921c68516efc940404e72862a0bd5df9a021bb8c0c0b80720048e2738cc8
6
+ metadata.gz: d368cf94df8ebbfe50b7d5ffd521653853096cd7c36c1c23093916899d0f2524ccf3f3526495cc37e043af571b0df68652c406e7effb033c8d2e92c661dd2b4a
7
+ data.tar.gz: 5646d8c021cc964041af44412aefab04cc27ebbaa51ddf381b81b43577f233be8f4621735000f1ef7b63e759d80cf51e67c8c802ace3634e2174f72ee8b4ab9f
data/README.md CHANGED
@@ -1,12 +1,15 @@
1
1
  # Matchi
2
2
 
3
- [![Build Status](https://api.travis-ci.org/fixrb/matchi.svg?branch=main)][travis]
4
- [![Code Climate](https://codeclimate.com/github/fixrb/matchi/badges/gpa.svg)][codeclimate]
5
- [![Gem Version](https://badge.fury.io/rb/matchi.svg)][gem]
6
- [![Documentation](https://img.shields.io/:yard-docs-38c800.svg)][rubydoc]
3
+ [![Version](https://img.shields.io/github/v/tag/fixrb/matchi?label=Version&logo=github)](https://github.com/fixrb/matchi/releases)
4
+ [![Yard documentation](https://img.shields.io/badge/Yard-documentation-blue.svg?logo=github)](https://rubydoc.info/github/fixrb/matchi/main)
5
+ [![CI](https://github.com/fixrb/matchi/workflows/CI/badge.svg?branch=main)](https://github.com/fixrb/matchi/actions?query=workflow%3Aci+branch%3Amain)
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
+ [![License](https://img.shields.io/github/license/fixrb/matchi?label=License&logo=github)](https://github.com/fixrb/matchi/raw/main/LICENSE.md)
7
8
 
8
9
  > Collection of expectation matchers for Ruby 🀹
9
10
 
11
+ ![A rubyist juggling between colored balls representing expectation matchers](https://github.com/fixrb/matchi/raw/main/img/matchi.jpg)
12
+
10
13
  ## Installation
11
14
 
12
15
  Add this line to your application's Gemfile:
@@ -27,8 +30,20 @@ Or install it yourself as:
27
30
  gem install matchi
28
31
  ```
29
32
 
33
+ ## Overview
34
+
35
+ __Matchi__ provides a collection of damn simple expectation matchers.
36
+
30
37
  ## Usage
31
38
 
39
+ To make __Matchi__ available:
40
+
41
+ ```ruby
42
+ require "matchi"
43
+ ```
44
+
45
+ All examples here assume that this has been done.
46
+
32
47
  ### Built-in matchers
33
48
 
34
49
  **Equivalence** matcher:
@@ -83,13 +98,21 @@ be_nil.matches? { nil } # => true
83
98
  **Type/class** matcher:
84
99
 
85
100
  ```ruby
86
- be_an_instance_of = Matchi::Matcher::BeAnInstanceOf.new(String)
101
+ be_an_instance_of = Matchi::Matcher::BeAnInstanceOf.new(:String)
87
102
  be_an_instance_of.matches? { "foo" } # => true
88
103
  ```
89
104
 
105
+ **Satisfy** matcher:
106
+
107
+ ```ruby
108
+ satisfy = Matchi::Matcher::Satisfy.new { |value| value == 42 }
109
+ satisfy.matches? { 42 } # => true
110
+ ```
111
+
90
112
  ### Custom matchers
91
113
 
92
- Custom matchers can easily be defined for expressing expectations. They can be any Ruby class that responds to `matches?`, `to_s` and `to_h` instance methods.
114
+ Custom matchers can easily be defined for expressing expectations.
115
+ They can be any Ruby class that responds to `matches?` instance method with a block.
93
116
 
94
117
  A **Be the answer** matcher:
95
118
 
@@ -149,6 +172,27 @@ start_with = Matchi::Matcher::StartWith.new("foo")
149
172
  start_with.matches? { "foobar" } # => true
150
173
  ```
151
174
 
175
+ ### Helper methods
176
+
177
+ For convenience, it is possible to instantiate a matcher with a method rather than with its class.
178
+ To do so, the `Helper` module can be included like this:
179
+
180
+ ```ruby
181
+ require "matchi/helper"
182
+
183
+ class MatcherCollection
184
+ include ::Matchi::Helper
185
+ end
186
+ ```
187
+
188
+ The set of loaded matcher then becomes accessible via a dynamically generated instance method, like these:
189
+
190
+ ```ruby
191
+ matcher = MatcherCollection.new
192
+ matcher.equal(42).matches? { 44 } # => false
193
+ matcher.be_an_instance_of(:String).matches? { "μ•ˆλ…•ν•˜μ„Έμš”" } # => true
194
+ ```
195
+
152
196
  ## Contact
153
197
 
154
198
  * Home page: https://github.com/fixrb/matchi
@@ -170,8 +214,3 @@ The [gem](https://rubygems.org/gems/matchi) is available as open source under th
170
214
  src="https://github.com/fixrb/matchi/raw/main/img/sashite.png"
171
215
  alt="Sashite" /></a>
172
216
  </p>
173
-
174
- [gem]: https://rubygems.org/gems/matchi
175
- [travis]: https://travis-ci.org/fixrb/matchi
176
- [codeclimate]: https://codeclimate.com/github/fixrb/matchi
177
- [rubydoc]: https://rubydoc.info/gems/matchi/frames
data/lib/matchi.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Namespace for the Matchi library.
4
+ #
5
+ # @api public
4
6
  module Matchi
5
7
  end
6
8
 
data/lib/matchi/helper.rb CHANGED
@@ -3,23 +3,35 @@
3
3
  require_relative "matcher"
4
4
 
5
5
  module Matchi
6
- # Collection of helper methods.
6
+ # When included, this module defines a helper instance method per matcher.
7
+ #
8
+ # @example Define and use the dynamic helper instance method of a custom matcher
9
+ # require "matchi/matcher/base"
10
+ #
11
+ # module Matchi
12
+ # module Matcher
13
+ # class BeTheAnswer < ::Matchi::Matcher::Base
14
+ # def matches?
15
+ # 42.equal?(yield)
16
+ # end
17
+ # end
18
+ # end
19
+ # end
20
+ #
21
+ # require "matchi/helper"
22
+ #
23
+ # class MatcherBase
24
+ # include ::Matchi::Helper
25
+ # end
26
+ #
27
+ # matcher_base = MatcherBase.new
28
+ # matcher_base.be_the_answer.matches? { 42 } # => true
7
29
  module Helper
8
30
  ::Matchi::Matcher.constants.each do |matcher_const|
9
31
  next if matcher_const.equal?(:Base)
10
32
 
11
33
  matcher_klass = ::Matchi::Matcher.const_get(matcher_const)
12
34
 
13
- # Define a method for the given matcher.
14
- #
15
- # @example Given the `Matchi::Matchers::Equal::Matcher` matcher, its
16
- # method would be:
17
- #
18
- # def equal(expected)
19
- # Matchi::Matchers::Equal::Matcher.new(expected)
20
- # end
21
- #
22
- # @return [#matches?] The matcher.
23
35
  define_method(matcher_klass.to_sym) do |*args|
24
36
  matcher_klass.new(*args)
25
37
  end
@@ -4,9 +4,16 @@ module Matchi
4
4
  module Matcher
5
5
  # Abstract matcher class.
6
6
  class Base
7
+ # Returns a symbol identifying the matcher.
8
+ #
9
+ # @example The readable definition of a FooBar matcher class.
10
+ # matcher_class = Matchi::Matcher::FooBar
11
+ # matcher_class.to_sym # => "foo_bar"
12
+ #
7
13
  # @return [Symbol] A symbol identifying the matcher.
8
14
  def self.to_sym
9
- name.delete_prefix("Matchi::Matcher::")
15
+ name.split("::")
16
+ .fetch(-1)
10
17
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
11
18
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
12
19
  .downcase
@@ -18,6 +25,10 @@ module Matchi
18
25
 
19
26
  # A string containing a human-readable representation of the matcher.
20
27
  #
28
+ # @example The human-readable representation of a FooBar matcher instance.
29
+ # matcher = Matchi::Matcher::FooBar.new(42)
30
+ # matcher.inspect # => "Matchi::Matcher::FooBar(42)"
31
+ #
21
32
  # @return [String] The human-readable representation of the matcher.
22
33
  def inspect
23
34
  "#{self.class}(#{expected&.inspect})"
@@ -25,18 +36,25 @@ module Matchi
25
36
 
26
37
  # Abstract matcher class.
27
38
  #
28
- # @raise [NotImplementedError] Override me inside a matcher.
39
+ # @example Test the equivalence between two "foo" strings.
40
+ # eql = Matchi::Matcher::Eql.new("foo")
41
+ # eql.matches? { "foo" } # => true
42
+ #
43
+ # @yieldreturn [#object_id] The actual value to compare to the expected
44
+ # one.
45
+ #
46
+ # @raise [NotImplementedError] Override this method inside a matcher.
29
47
  def matches?
30
48
  raise ::NotImplementedError, "matcher MUST respond to this method."
31
49
  end
32
50
 
33
- # Returns a string representing the matcher.
51
+ # Returns a string representing the matcher instance.
34
52
  #
35
- # @example The readable definition of a FooBar matcher.
53
+ # @example The readable definition of a FooBar matcher instance.
36
54
  # matcher = Matchi::Matcher::FooBar.new(42)
37
55
  # matcher.to_s # => "foo_bar 42"
38
56
  #
39
- # @return [String] A string representing the matcher.
57
+ # @return [String] A string representing the matcher instance.
40
58
  def to_s
41
59
  [self.class.to_sym, expected&.inspect].compact.join(" ")
42
60
  end
@@ -8,13 +8,18 @@ module Matchi
8
8
  class BeAnInstanceOf < ::Matchi::Matcher::Base
9
9
  # Initialize the matcher with an object.
10
10
  #
11
- # @example A string matcher
12
- # Matchi::Matcher::BeAnInstanceOf.new(String)
11
+ # @example A duck matcher
12
+ # Matchi::Matcher::BeAnInstanceOf.new(:Duck)
13
13
  #
14
- # @param expected [Class] An expected class.
14
+ # @param expected [#to_s] The name of a module.
15
15
  def initialize(expected)
16
16
  super()
17
- @expected = expected
17
+ @expected = String(expected).to_sym
18
+ end
19
+
20
+ # (see Base#inspect)
21
+ def inspect
22
+ "#{self.class}(#{expected})"
18
23
  end
19
24
 
20
25
  # Boolean comparison between the class of the actual value and the
@@ -24,11 +29,22 @@ module Matchi
24
29
  # be_an_instance_of = Matchi::Matcher::BeInstanceOf.new(String)
25
30
  # be_an_instance_of.matches? { "foo" } # => true
26
31
  #
32
+ # be_an_instance_of = Matchi::Matcher::BeInstanceOf.new(:String)
33
+ # be_an_instance_of.matches? { "foo" } # => true
34
+ #
35
+ # be_an_instance_of = Matchi::Matcher::BeInstanceOf.new("String")
36
+ # be_an_instance_of.matches? { "foo" } # => true
37
+ #
27
38
  # @yieldreturn [#class] the actual value to compare to the expected one.
28
39
  #
29
40
  # @return [Boolean] Comparison between actual and expected values.
30
41
  def matches?(*, **)
31
- expected.equal?(yield.class)
42
+ self.class.const_get(expected).equal?(yield.class)
43
+ end
44
+
45
+ # (see Base#to_s)
46
+ def to_s
47
+ "#{self.class.to_sym} #{expected}"
32
48
  end
33
49
  end
34
50
  end
@@ -8,8 +8,8 @@ module Matchi
8
8
  class Eql < ::Matchi::Matcher::Base
9
9
  # Initialize the matcher with an object.
10
10
  #
11
- # @example The string 'foo' matcher.
12
- # Matchi::Matcher::Eql.new('foo')
11
+ # @example The string "foo" matcher.
12
+ # Matchi::Matcher::Eql.new("foo")
13
13
  #
14
14
  # @param expected [#eql?] An expected equivalent object.
15
15
  def initialize(expected)
@@ -19,9 +19,9 @@ module Matchi
19
19
 
20
20
  # Boolean comparison between the actual value and the expected value.
21
21
  #
22
- # @example Is it equivalent to 'foo'?
23
- # eql = Matchi::Matcher::Eql.new('foo')
24
- # eql.matches? { 'foo' } # => true
22
+ # @example Is it equivalent to "foo"?
23
+ # eql = Matchi::Matcher::Eql.new("foo")
24
+ # eql.matches? { "foo" } # => true
25
25
  #
26
26
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
27
  # one.
@@ -21,7 +21,7 @@ module Matchi
21
21
  #
22
22
  # @example Is it matching /^foo$/ regex?
23
23
  # match = Matchi::Matcher::Match.new(/^foo$/)
24
- # match.matches? { 'foo' } # => true
24
+ # match.matches? { "foo" } # => true
25
25
  #
26
26
  # @yieldreturn [#object_id] The actual value to compare to the expected
27
27
  # one.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Matchi
6
+ module Matcher
7
+ # *Satisfy* matcher.
8
+ class Satisfy < ::Matchi::Matcher::Base
9
+ # Initialize the matcher with a block.
10
+ #
11
+ # @example The number 42 matcher.
12
+ # Matchi::Matcher::Satisfy.new { |value| value == 42 }
13
+ #
14
+ # @param block [Proc] A block of code.
15
+ def initialize(&block)
16
+ super()
17
+ @expected = block
18
+ end
19
+
20
+ # (see Base#inspect)
21
+ def inspect
22
+ "#{self.class}(&block)"
23
+ end
24
+
25
+ # Boolean comparison between the actual value and the expected value.
26
+ #
27
+ # @example Is it equal to 42
28
+ # equal = Matchi::Matcher::Satisfy.new { |value| value == 42 }
29
+ # equal.matches? { 42 } # => 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.call(yield)
37
+ end
38
+
39
+ # (see Base#to_s)
40
+ def to_s
41
+ "#{self.class.to_sym} &block"
42
+ end
43
+ end
44
+ end
45
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-16 00:00:00.000000000 Z
11
+ date: 2021-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -142,6 +142,7 @@ files:
142
142
  - lib/matchi/matcher/equal.rb
143
143
  - lib/matchi/matcher/match.rb
144
144
  - lib/matchi/matcher/raise_exception.rb
145
+ - lib/matchi/matcher/satisfy.rb
145
146
  homepage: https://github.com/fixrb/matchi
146
147
  licenses:
147
148
  - MIT