dry-matcher 0.7.0 → 0.9.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: a452c874d6095df3fd8a932dfc0a604fa2ef531dff6ca30ee2c850f1012ce13c
4
- data.tar.gz: 8a061065d80d1821fdb4e9b020050cd380ee69a73af6351bbc78bfeab5153911
3
+ metadata.gz: 2b0215bfb6c841e25bcd167ebf29b6dd05c642926c04bb253cabe5a3d430ee27
4
+ data.tar.gz: 6a69936b8ce9b3edfd22653c0e358f8c6d1149064a74782661b8a251e306ba86
5
5
  SHA512:
6
- metadata.gz: 8af536933244d441747638bcf7244f1918c4cb626717d48be1087704628f0c3b09266e6b55917fd18b776a464eaae4fc25e8382e22b661d3f408dc99c69c93bf
7
- data.tar.gz: 5ca949c319b53001db633bdf6493eb2f99ab27bea4171b3429523991f9c3063ef5edceb3a5ee068ec75f9af5269f0795d7d09d1b03437d57cbdfee2e6fd8e8b3
6
+ metadata.gz: a0641213eab8f65143040b0c6a61913415822e34fa985ff68a3efae84e4a21ffeb04675ad4ba5aaf7476cfd7d4bc89128f5faec7d705d36f8d262a0b131d2c76
7
+ data.tar.gz: 5b42966da62e0b5665ca89c20aaef62683a9e52e4fae91ae4eb852f9e2900f417800f5ce5ca82ccf3e5403d39ce54af7862942e7ee4fda12215eee297450c21f
data/CHANGELOG.md CHANGED
@@ -1,119 +1,222 @@
1
- # 0.7.0 / to-be-released
1
+ <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
- ## Changed
3
+ ## 0.9.0 2021-03-05
4
+
5
+
6
+ ### Changed
7
+
8
+ - Matcher evaluator is now a standard `Object` descendant (see #32) (@solnic)
9
+
10
+ [Compare v0.8.3...v0.9.0](https://github.com/dry-rb/dry-matcher/compare/v0.8.3...v0.9.0)
11
+
12
+ ## 0.8.3 2020-01-07
13
+
14
+
15
+ ### Fixed
16
+
17
+ - Delegation warnings about keyword arguments (flash-gordon)
18
+
19
+
20
+ [Compare v0.8.2...v0.8.3](https://github.com/dry-rb/dry-matcher/compare/v0.8.2...v0.8.3)
21
+
22
+ ## 0.8.2 2019-09-06
23
+
24
+
25
+ ### Fixed
26
+
27
+ - Minimal dry-core version set to 0.4.8 (flash-gordon)
28
+
29
+
30
+ [Compare v0.8.1...v0.8.2](https://github.com/dry-rb/dry-matcher/compare/v0.8.1...v0.8.2)
31
+
32
+ ## 0.8.1 2019-08-13
33
+
34
+
35
+ ### Added
36
+
37
+ - `Dry::Matcher#for` is a shortcut for `Dry::Matcher.for(..., with: matcher)` (flash-gordon)
38
+
39
+ ```ruby
40
+ require 'dry/matcher/result_matcher'
41
+
42
+ class CreateUser
43
+ include Dry::Matcher::ResultMatcher.for(:call)
44
+
45
+ def call(...)
46
+ # code returning an instance of Dry::Monads::Result
47
+ end
48
+ end
49
+ ```
50
+
51
+
52
+ [Compare v0.8.0...v0.8.1](https://github.com/dry-rb/dry-matcher/compare/v0.8.0...v0.8.1)
53
+
54
+ ## 0.8.0 2019-07-30
55
+
56
+
57
+ ### Added
58
+
59
+ - API for cases was changed to work with a single block instead of `match`/`resolve` combination (flash-gordon in [#23](https://github.com/dry-rb/dry-matcher/pull/23)):
60
+ ```ruby
61
+ Dry::Matcher::Case.new do |value, patterns|
62
+ if patterns.include?(value)
63
+ # value will be passed to the block
64
+ value
65
+ else
66
+ # Undefined stands for no match
67
+ Dry::Matcher::Undefined
68
+ end
69
+ end
70
+ ```
71
+ - `ResultMatcher` now uses patterns for matching and matches against the first element if an array is passed (flash-gordon in [#24](https://github.com/dry-rb/dry-matcher/pull/24) and [#22](https://github.com/dry-rb/dry-matcher/pull/22) and michaelherold in [#21](https://github.com/dry-rb/dry-matcher/pull/21))
72
+
73
+ ```ruby
74
+ value = Dry::Monads::Result::Failure.new([:invalid, :reasons])
75
+
76
+ Dry::Matcher::ResultMatcher.(value) do |m|
77
+ m.success do |v|
78
+ "Yay: #{v}"
79
+ end
80
+
81
+ m.failure(:not_found) do
82
+ "No such thing"
83
+ end
84
+
85
+ m.failure(:invalid) do |_code, errors|
86
+ "Cannot be done: #{errors.inspect}"
87
+ end
88
+ end #=> "Cannot be done: :reasons"
89
+ ```
90
+
91
+ ### Changed
92
+
93
+ - [BREAKING] Support for Ruby 2.3 was dropped as it's EOL
94
+
95
+ [Compare v0.7.0...v0.8.0](https://github.com/dry-rb/dry-matcher/compare/v0.7.0...v0.8.0)
96
+
97
+ ## 0.7.0 2018-01-11
98
+
99
+
100
+ ### Changed
4
101
 
5
102
  - `EitherMatcher` was renamed to `ResultMatcher` according to match the rename of `Either` to `Result` in dry-monads 0.4.0. The previous name is still there for backward compatibility, we'll deprecate and drop it in furure releases (flash-gordon in [#13](https://github.com/dry-rb/dry-matcher/pull/13))
6
103
 
7
104
  [Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-matcher/compare/v0.6.0...v0.7.0)
8
105
 
9
- # 0.6.0 / 2016-12-19
106
+ ## 0.6.0 2016-12-19
107
+
10
108
 
11
- ## Added
109
+ ### Added
12
110
 
13
111
  - API documentation for most methods (alsemyonov in [#8](https://github.com/dry-rb/dry-matcher/pull/8))
14
112
 
15
- ## Changed
113
+ ### Fixed
16
114
 
17
- - Matches must now be exhaustive - when matching against a value, at least one match block must be provided for each of a matcher's cases (timriley in [#7](https://github.com/dry-rb/dry-matcher/pull/7))
18
- - `Dry::Matcher::Case` objects can now be created without a `resolve:` proc. In this case, a default will be provided that passes the result value through (timriley in [#9](https://github.com/dry-rb/dry-matcher/pull/9))
115
+ - Fixed handling of calls to non-existent cases within a matcher block (timriley)
19
116
 
20
- ## Fixed
117
+ ### Changed
21
118
 
22
- - Fixed handling of calls to non-existent cases within a matcher block (timriley)
119
+ - Matches must now be exhaustive - when matching against a value, at least one match block must be provided for each of a matcher's cases (timriley in [#7](https://github.com/dry-rb/dry-matcher/pull/7))
120
+ - `Dry::Matcher::Case` objects can now be created without a `resolve:` proc. In this case, a default will be provided that passes the result value through (timriley in [#9](https://github.com/dry-rb/dry-matcher/pull/9))
23
121
 
24
122
  [Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-matcher/compare/v0.5.0...v0.6.0)
25
123
 
26
- # 0.5.0 / 2016-06-30
124
+ ## 0.5.0 2016-06-30
27
125
 
28
- ## Added
126
+
127
+ ### Added
29
128
 
30
129
  - Added support for building custom matchers, with an any number of match cases, each offering their own matching and resolving logic. This is now the primary API for dry-matcher. (timriley)
31
130
 
32
- ```ruby
33
- # Match `[:ok, some_value]` for success
34
- success_case = Dry::Matcher::Case.new(
35
- match: -> value { value.first == :ok },
36
- resolve: -> value { value.last }
37
- )
38
-
39
- # Match `[:err, some_error_code, some_value]` for failure
40
- failure_case = Dry::Matcher::Case.new(
41
- match: -> value, *pattern {
42
- value[0] == :err && (pattern.any? ? pattern.include?(value[1]) : true)
43
- },
44
- resolve: -> value { value.last }
45
- )
46
-
47
- # Build the matcher
48
- matcher = Dry::Matcher.new(success: success_case, failure: failure_case)
49
-
50
- # Then put it to use
51
- my_success = [:ok, "success!"]
52
-
53
- result = matcher.(my_success) do |m|
54
- m.success do |v|
55
- "Yay: #{v}"
56
- end
57
-
58
- m.failure :not_found do |v|
59
- "Oops, not found: #{v}"
60
- end
61
-
62
- m.failure do |v|
63
- "Boo: #{v}"
64
- end
131
+ ```ruby
132
+ # Match `[:ok, some_value]` for success
133
+ success_case = Dry::Matcher::Case.new(
134
+ match: -> value { value.first == :ok },
135
+ resolve: -> value { value.last }
136
+ )
137
+
138
+ # Match `[:err, some_error_code, some_value]` for failure
139
+ failure_case = Dry::Matcher::Case.new(
140
+ match: -> value, *pattern {
141
+ value[0] == :err && (pattern.any? ? pattern.include?(value[1]) : true)
142
+ },
143
+ resolve: -> value { value.last }
144
+ )
145
+
146
+ # Build the matcher
147
+ matcher = Dry::Matcher.new(success: success_case, failure: failure_case)
148
+
149
+ # Then put it to use
150
+ my_success = [:ok, "success!"]
151
+
152
+ result = matcher.(my_success) do |m|
153
+ m.success do |v|
154
+ "Yay: #{v}"
65
155
  end
66
156
 
67
- result # => "Yay: success!"
68
- ```
157
+ m.failure :not_found do |v|
158
+ "Oops, not found: #{v}"
159
+ end
69
160
 
70
- ## Changed
161
+ m.failure do |v|
162
+ "Boo: #{v}"
163
+ end
164
+ end
165
+
166
+ result # => "Yay: success!"
167
+ ```
168
+
169
+ ### Changed
71
170
 
72
171
  - Renamed to `dry-matcher`, since this is now a flexible, general purpose pattern matching API. All components are now available under the `Dry::Matcher` namespace. (timriley)
73
172
  - `Dry::Matcher.for` requires a matcher object to be passed when being included in a class:
74
173
 
75
- ```ruby
76
- MyMatcher = Dry::Matcher.new(...)
174
+ ```ruby
175
+ MyMatcher = Dry::Matcher.new(...)
77
176
 
78
- class MyOperation
79
- include Dry::Matcher.for(:call, with: MyMatcher)
177
+ class MyOperation
178
+ include Dry::Matcher.for(:call, with: MyMatcher)
80
179
 
81
- def call
82
- end
180
+ def call
83
181
  end
84
- ```
182
+ end
183
+ ```
85
184
  - The previous `Dry::ResultMatcher.match` behaviour (for matching `Either` monads) has been moved to `Dry::Matcher::EitherMatcher.call`
86
185
 
87
186
  [Compare v0.4.0...v0.5.0](https://github.com/dry-rb/dry-matcher/compare/v0.4.0...v0.5.0)
88
187
 
89
- # 0.4.0 / 2016-06-08
188
+ ## 0.4.0 2016-06-08
189
+
90
190
 
91
- ## Added
191
+ ### Added
92
192
 
93
- * Support convertible result objects responding to `#to_either` (ttdonovan)
193
+ - Support convertible result objects responding to `#to_either` (ttdonovan)
94
194
 
95
- ## Changed
195
+ ### Changed
96
196
 
97
- * Expect monads from [dry-monads](https://github.com/dry-rb/dry-monads) instead of [Kleisli](https://github.com/txus/kleisli) (ttdonovan)
197
+ - Expect monads from [dry-monads](https://github.com/dry-rb/dry-monads) instead of [Kleisli](https://github.com/txus/kleisli) (ttdonovan)
98
198
 
99
199
  [Compare v0.3.0...v0.4.0](https://github.com/dry-rb/dry-matcher/compare/v0.3.0...v0.4.0)
100
200
 
101
- # 0.3.0 / 2016-03-23
201
+ ## 0.3.0 2016-03-23
102
202
 
103
- ## Changed
104
203
 
105
- * Renamed to `dry-result_matcher`. Match results using `Dry::ResultMatcher.match` or extend your own classes with `Dry::ResultMatcher.for`.
204
+ ### Changed
205
+
206
+ - Renamed to `dry-result_matcher`. Match results using `Dry::ResultMatcher.match` or extend your own classes with `Dry::ResultMatcher.for`.
106
207
 
107
208
  [Compare v0.2.0...v0.3.0](https://github.com/dry-rb/dry-matcher/compare/v0.2.0...v0.3.0)
108
209
 
109
- # 0.2.0 / 2016-02-10
210
+ ## 0.2.0 2016-02-10
211
+
212
+
213
+ ### Added
110
214
 
111
- ## Added
215
+ - Added `EitherResultMatcher.for(*methods)` to return a module wrapping the specified methods (returning an `Either`) with the match block API. Example usage, in a class with a `#call` method: `include EitherResultMatcher.for(:call)`.
112
216
 
113
- * Added `EitherResultMatcher.for(*methods)` to return a module wrapping the specified methods (returning an `Either`) with the match block API. Example usage, in a class with a `#call` method: `include EitherResultMatcher.for(:call)`.
114
217
 
115
- [Compare v0.1.0...v0.22.0](https://github.com/dry-rb/dry-matcher/compare/v0.1.0...v0.2.0)
218
+ [Compare v0.1.0...v0.2.0](https://github.com/dry-rb/dry-matcher/compare/v0.1.0...v0.2.0)
116
219
 
117
- # 0.1.0 / 2015-12-03
220
+ ## 0.1.0 2015-12-03
118
221
 
119
222
  Initial release.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2021 dry-rb team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,23 +1,29 @@
1
- [gitter]: https://gitter.im/dry-rb/chat
2
1
  [gem]: https://rubygems.org/gems/dry-matcher
3
- [travis]: https://travis-ci.org/dry-rb/dry-matcher
4
- [code_climate]: https://codeclimate.com/github/dry-rb/dry-matcher
5
- [inch]: http://inch-ci.org/github/dry-rb/dry-matcher
2
+ [actions]: https://github.com/dry-rb/dry-matcher/actions
3
+ [codacy]: https://www.codacy.com/gh/dry-rb/dry-matcher
4
+ [chat]: https://dry-rb.zulipchat.com
5
+ [inchpages]: http://inch-ci.org/github/dry-rb/dry-matcher
6
6
 
7
- # dry-matcher [![Join the Gitter chat](https://badges.gitter.im/Join%20Chat.svg)][gitter]
7
+ # dry-matcher [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
8
8
 
9
- [![Gem Version](https://img.shields.io/gem/v/dry-matcher.svg)][gem]
10
- [![Build Status](https://img.shields.io/travis/dry-rb/dry-matcher.svg)][travis]
11
- [![Code Climate](https://img.shields.io/codeclimate/github/dry-rb/dry-matcher.svg)][code_climate]
12
- [![Test Coverage](https://img.shields.io/codeclimate/coverage/github/dry-rb/dry-matcher.svg)][code_climate]
13
- [![API Documentation Coverage](http://inch-ci.org/github/dry-rb/dry-matcher.svg)][inch]
14
-
15
- Flexible, expressive pattern matching for Ruby.
9
+ [![Gem Version](https://badge.fury.io/rb/dry-matcher.svg)][gem]
10
+ [![CI Status](https://github.com/dry-rb/dry-matcher/workflows/ci/badge.svg)][actions]
11
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f09a7d1745fd430d829a1f825357db88)][codacy]
12
+ [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/f09a7d1745fd430d829a1f825357db88)][codacy]
13
+ [![Inline docs](http://inch-ci.org/github/dry-rb/dry-matcher.svg?branch=master)][inchpages]
16
14
 
17
15
  ## Links
18
16
 
19
- * [Documentation](http://dry-rb.org/gems/dry-matcher)
17
+ * [User documentation](http://dry-rb.org/gems/dry-matcher)
18
+ * [API documentation](http://rubydoc.info/gems/dry-matcher)
19
+
20
+ ## Supported Ruby versions
21
+
22
+ This library officially supports the following Ruby versions:
23
+
24
+ * MRI >= `2.5`
25
+ * jruby >= `9.2`
20
26
 
21
27
  ## License
22
28
 
23
- Copyright © 2015-2016 [Icelab](http://icelab.com.au/). dry-matcher is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
29
+ See `LICENSE` file.
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ # this file is managed by dry-rb/devtools project
3
+
4
+ lib = File.expand_path('lib', __dir__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'dry/matcher/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'dry-matcher'
10
+ spec.authors = ["Tim Riley", "Nikita Shilnikov"]
11
+ spec.email = ["tim@icelab.com.au", "fg@flashgordon.ru"]
12
+ spec.license = 'MIT'
13
+ spec.version = Dry::Matcher::VERSION.dup
14
+
15
+ spec.summary = "Flexible, expressive pattern matching for Ruby"
16
+ spec.description = spec.summary
17
+ spec.homepage = 'https://dry-rb.org/gems/dry-matcher'
18
+ spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-matcher.gemspec", "lib/**/*"]
19
+ spec.bindir = 'bin'
20
+ spec.executables = []
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
+ spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-matcher/blob/master/CHANGELOG.md'
25
+ spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-matcher'
26
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-matcher/issues'
27
+
28
+ spec.required_ruby_version = ">= 2.5.0"
29
+
30
+ # to update dependencies edit project.yml
31
+ spec.add_runtime_dependency "dry-core", "~> 0.4", ">= 0.4.8"
32
+
33
+ spec.add_development_dependency "rake"
34
+ end
data/lib/dry-matcher.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "dry/matcher"
data/lib/dry/matcher.rb CHANGED
@@ -1,9 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/constants"
1
4
  require "dry/matcher/case"
2
5
  require "dry/matcher/evaluator"
3
6
 
4
7
  module Dry
5
8
  # @see http://dry-rb.org/gems/dry-matcher
6
9
  class Matcher
10
+ include Core::Constants
11
+
12
+ RUBY2_KEYWORDS = respond_to?(:ruby2_keywords, true)
13
+
7
14
  # Generates a module containing pattern matching for methods listed in
8
15
  # `match_methods` argument with behavior defined by `with` matcher
9
16
  #
@@ -40,6 +47,7 @@ module Dry
40
47
  result
41
48
  end
42
49
  end
50
+ ruby2_keywords(match_method) if RUBY2_KEYWORDS
43
51
  end
44
52
  end
45
53
 
@@ -70,7 +78,7 @@ module Dry
70
78
  # after matched pattern
71
79
  #
72
80
  # @example Usage with `dry-monads`
73
- # require 'dry-monads'
81
+ # require 'dry/monads/result'
74
82
  # require 'dry/matcher/result_matcher'
75
83
  #
76
84
  # value = Dry::Monads::Result::Failure.new('failure!')
@@ -82,5 +90,14 @@ module Dry
82
90
  def call(result, &block)
83
91
  Evaluator.new(result, cases).call(&block)
84
92
  end
93
+
94
+ # Shortcut for Dry::Matcher.for(..., with: matcher)
95
+ #
96
+ # @param [Array[Symbol]]
97
+ #
98
+ # @return [Module]
99
+ def for(*methods)
100
+ self.class.for(*methods, with: self)
101
+ end
85
102
  end
86
103
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Matcher
3
5
  # {Case} object contains logic for pattern matching and resolving result
@@ -7,29 +9,27 @@ module Dry
7
9
 
8
10
  # @param match [#call] callable used to test given pattern against value
9
11
  # @param resolve [#call] callable used to resolve value into a result
10
- def initialize(match:, resolve: DEFAULT_RESOLVE)
11
- @match = match
12
- @resolve = resolve
13
- end
14
-
15
- # Tests whether `value` (with optional `*pattern`) matches pattern using
16
- # callable given to {#initialize} as `match:` argument
17
- #
18
- # @param [Object] value
19
- # @param [<Object>] pattern optional pattern given after the `value` to
20
- # `match:` callable
21
- # @return [Boolean]
22
- def matches?(value, *pattern)
23
- @match.(value, *pattern)
12
+ def initialize(match: Undefined, resolve: DEFAULT_RESOLVE, &block)
13
+ if block
14
+ @match = block
15
+ else
16
+ @match = proc do |value, patterns|
17
+ if match.(value, *patterns)
18
+ resolve.(value)
19
+ else
20
+ Undefined
21
+ end
22
+ end
23
+ end
24
24
  end
25
25
 
26
- # Resolves result from `value` using callable given to {#initialize}
27
- # as `resolve:` argument
28
- #
29
- # @param [Object] value
30
- # @return [Object] result resolved from given `value`
31
- def resolve(value)
32
- @resolve.(value)
26
+ # @param [Object] value Value to match
27
+ # @param [Array<Object>] patterns Optional list of patterns to match against
28
+ # @yieldparam [Object] v Resolved value if match succeeds
29
+ # @return [Object,Dry::Core::Constants::Undefined] Either the yield result
30
+ # or Undefined if match wasn't successful
31
+ def call(value, patterns = EMPTY_ARRAY, &block)
32
+ Undefined.map(@match.(value, patterns), &block)
33
33
  end
34
34
  end
35
35
  end
@@ -1,7 +1,14 @@
1
- require "dry/matcher/result_matcher"
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/core/deprecations'
4
+ require 'dry/matcher/result_matcher'
2
5
 
3
6
  module Dry
4
7
  class Matcher
8
+ extend Dry::Core::Deprecations[:'dry-matcher']
9
+
5
10
  EitherMatcher = ResultMatcher
11
+
12
+ deprecate_constant(:EitherMatcher, message: 'Dry::Matcher::ResultMatcher')
6
13
  end
7
14
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Matcher
3
5
  NonExhaustiveMatchError = Class.new(StandardError)
4
6
 
5
7
  # {Evaluator} is used in {Dry::Matcher#call Dry::Matcher#call} block to handle different {Case}s
6
- class Evaluator < BasicObject
8
+ class Evaluator
7
9
  # @param [Object] result
8
10
  # @param [Hash{Symbol => Case}] cases
9
11
  def initialize(result, cases)
@@ -11,8 +13,6 @@ module Dry
11
13
  @result = result
12
14
 
13
15
  @unhandled_cases = @cases.keys.map(&:to_sym)
14
- @matched = false
15
- @output = nil
16
16
  end
17
17
 
18
18
  def call
@@ -20,14 +20,14 @@ module Dry
20
20
 
21
21
  ensure_exhaustive_match
22
22
 
23
- @output
23
+ @output if defined? @output
24
24
  end
25
25
 
26
26
  # Checks whether `cases` given to {#initialize} contains one called `name`
27
27
  # @param [String] name
28
28
  # @param [Boolean] include_private
29
29
  # @return [Boolean]
30
- def respond_to_missing?(name, include_private = false)
30
+ def respond_to_missing?(name, _include_private = false)
31
31
  @cases.key?(name)
32
32
  end
33
33
 
@@ -46,10 +46,15 @@ module Dry
46
46
  # @raise [NoMethodError] if there was no case called `name` given to
47
47
  # {#initialize} in `cases` hash
48
48
  def method_missing(name, *args, &block)
49
- return super unless @cases.key?(name)
49
+ kase = @cases.fetch(name) { return super }
50
50
 
51
51
  @unhandled_cases.delete name
52
- handle_case @cases[name], *args, &block
52
+
53
+ unless defined? @output
54
+ kase.(@result, args) do |result|
55
+ @output = yield(result)
56
+ end
57
+ end
53
58
  end
54
59
 
55
60
  private
@@ -59,17 +64,6 @@ module Dry
59
64
  ::Kernel.raise NonExhaustiveMatchError, "cases +#{@unhandled_cases.join(', ')}+ not handled"
60
65
  end
61
66
  end
62
-
63
- # @param [Case] kase
64
- # @param [Array] pattern
65
- def handle_case(kase, *pattern)
66
- return @output if @matched
67
-
68
- if kase.matches?(@result, *pattern)
69
- @matched = true
70
- @output = yield(kase.resolve(@result))
71
- end
72
- end
73
67
  end
74
68
  end
75
69
  end
@@ -1,7 +1,21 @@
1
- require "dry/matcher"
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/matcher'
2
4
 
3
5
  module Dry
4
6
  class Matcher
7
+ match = ::Proc.new do |value, patterns|
8
+ if patterns.empty?
9
+ value
10
+ elsif value.is_a?(::Array) && patterns.any? { |p| p === value[0] }
11
+ value
12
+ elsif patterns.any? { |p| p === value }
13
+ value
14
+ else
15
+ Undefined
16
+ end
17
+ end
18
+
5
19
  # Built-in {Matcher} ready to use with `Result` or `Try` monads from
6
20
  # [dry-monads](/gems/dry-monads) or any other compatible gems.
7
21
  #
@@ -16,7 +30,7 @@ module Dry
16
30
  # @return [Dry::Matcher]
17
31
  #
18
32
  # @example Usage with `dry-monads`
19
- # require 'dry-monads'
33
+ # require 'dry/monads/result'
20
34
  # require 'dry/matcher/result_matcher'
21
35
  #
22
36
  # value = Dry::Monads::Result::Success.new('success!')
@@ -48,27 +62,43 @@ module Dry
48
62
  # m.success { |v| "#{v.inspect} is truthy" }
49
63
  # m.failure { |v| "#{v.inspect} is falsey" }
50
64
  # end # => "nil is falsey"
65
+ #
66
+ # @example Usage with error codes
67
+ # value = Dry::Monads::Result::Failure.new([:invalid, :reasons])
68
+ #
69
+ # Dry::Matcher::ResultMatcher.(value) do |m|
70
+ # m.success do |v|
71
+ # "Yay: #{v}"
72
+ # end
73
+ #
74
+ # m.failure(:not_found) do
75
+ # "No such thing"
76
+ # end
77
+ #
78
+ # m.failure(:invalid) do |_code, errors|
79
+ # "Cannot be done: #{errors.inspect}"
80
+ # end
81
+ # end #=> "Cannot be done: :reasons"
82
+ #
51
83
  ResultMatcher = Dry::Matcher.new(
52
- success: Case.new(
53
- match: -> result, *pattern {
54
- result = result.to_result
55
- result.success?
56
- },
57
- resolve: -> result {
58
- result = result.to_result
59
- result.value!
60
- },
61
- ),
62
- failure: Case.new(
63
- match: -> result, *pattern {
64
- result = result.to_result
65
- result.failure?
66
- },
67
- resolve: -> result {
68
- result = result.to_result
69
- result.failure
70
- },
71
- )
84
+ success: Case.new { |result, patterns|
85
+ result = result.to_result
86
+
87
+ if result.success?
88
+ match.(result.value!, patterns)
89
+ else
90
+ Undefined
91
+ end
92
+ },
93
+ failure: Case.new { |result, patterns|
94
+ result = result.to_result
95
+
96
+ if result.failure?
97
+ match.(result.failure, patterns)
98
+ else
99
+ Undefined
100
+ end
101
+ }
72
102
  )
73
103
  end
74
104
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  class Matcher
3
- VERSION = "0.7.0".freeze
5
+ VERSION = '0.9.0'
4
6
  end
5
7
  end
metadata CHANGED
@@ -1,73 +1,38 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-matcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Riley
8
+ - Nikita Shilnikov
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2018-01-10 00:00:00.000000000 Z
12
+ date: 2021-03-05 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: bundler
15
+ name: dry-core
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: '1.10'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.10'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 10.4.2
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 10.4.2
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
20
+ version: '0.4'
21
+ - - ">="
46
22
  - !ruby/object:Gem::Version
47
- version: 3.3.0
48
- type: :development
23
+ version: 0.4.8
24
+ type: :runtime
49
25
  prerelease: false
50
26
  version_requirements: !ruby/object:Gem::Requirement
51
27
  requirements:
52
28
  - - "~>"
53
29
  - !ruby/object:Gem::Version
54
- version: 3.3.0
55
- - !ruby/object:Gem::Dependency
56
- name: simplecov
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 0.10.0
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
30
+ version: '0.4'
31
+ - - ">="
67
32
  - !ruby/object:Gem::Version
68
- version: 0.10.0
33
+ version: 0.4.8
69
34
  - !ruby/object:Gem::Dependency
70
- name: yard
35
+ name: rake
71
36
  requirement: !ruby/object:Gem::Requirement
72
37
  requirements:
73
38
  - - ">="
@@ -83,15 +48,15 @@ dependencies:
83
48
  description: Flexible, expressive pattern matching for Ruby
84
49
  email:
85
50
  - tim@icelab.com.au
51
+ - fg@flashgordon.ru
86
52
  executables: []
87
53
  extensions: []
88
54
  extra_rdoc_files: []
89
55
  files:
90
56
  - CHANGELOG.md
91
- - Gemfile
92
- - LICENSE.md
57
+ - LICENSE
93
58
  - README.md
94
- - Rakefile
59
+ - dry-matcher.gemspec
95
60
  - lib/dry-matcher.rb
96
61
  - lib/dry/matcher.rb
97
62
  - lib/dry/matcher/case.rb
@@ -99,16 +64,14 @@ files:
99
64
  - lib/dry/matcher/evaluator.rb
100
65
  - lib/dry/matcher/result_matcher.rb
101
66
  - lib/dry/matcher/version.rb
102
- - spec/examples.txt
103
- - spec/integration/class_enhancement_spec.rb
104
- - spec/integration/matcher_spec.rb
105
- - spec/integration/result_matcher_spec.rb
106
- - spec/spec_helper.rb
107
- - spec/unit/case_spec.rb
108
- homepage: http://dry-rb.org/gems/dry-matcher
67
+ homepage: https://dry-rb.org/gems/dry-matcher
109
68
  licenses:
110
69
  - MIT
111
- metadata: {}
70
+ metadata:
71
+ allowed_push_host: https://rubygems.org
72
+ changelog_uri: https://github.com/dry-rb/dry-matcher/blob/master/CHANGELOG.md
73
+ source_code_uri: https://github.com/dry-rb/dry-matcher
74
+ bug_tracker_uri: https://github.com/dry-rb/dry-matcher/issues
112
75
  post_install_message:
113
76
  rdoc_options: []
114
77
  require_paths:
@@ -117,15 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
80
  requirements:
118
81
  - - ">="
119
82
  - !ruby/object:Gem::Version
120
- version: 2.2.0
83
+ version: 2.5.0
121
84
  required_rubygems_version: !ruby/object:Gem::Requirement
122
85
  requirements:
123
86
  - - ">="
124
87
  - !ruby/object:Gem::Version
125
88
  version: '0'
126
89
  requirements: []
127
- rubyforge_project:
128
- rubygems_version: 2.7.3
90
+ rubygems_version: 3.1.4
129
91
  signing_key:
130
92
  specification_version: 4
131
93
  summary: Flexible, expressive pattern matching for Ruby
data/Gemfile DELETED
@@ -1,14 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
4
-
5
- group :test do
6
- gem "simplecov"
7
- gem "codeclimate-test-reporter"
8
- gem "dry-monads", '>= 0.4.0'
9
- end
10
-
11
- group :tools do
12
- gem "byebug", platform: :mri
13
- gem "pry"
14
- end
data/LICENSE.md DELETED
@@ -1,9 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright © 2015-2016 [Icelab](http://icelab.com.au/).
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
-
7
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
-
9
- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- require "rspec/core/rake_task"
4
- RSpec::Core::RakeTask.new
5
-
6
- task default: :spec
7
-
8
- require 'yard'
9
- require 'yard/rake/yardoc_task'
10
- YARD::Rake::YardocTask.new(:doc)
data/spec/examples.txt DELETED
@@ -1,18 +0,0 @@
1
- example_id | status | run_time |
2
- ----------------------------------------------------- | ------ | --------------- |
3
- ./spec/integration/class_enhancement_spec.rb[1:1:1:1] | passed | 0.00011 seconds |
4
- ./spec/integration/class_enhancement_spec.rb[1:1:2:1] | passed | 0.0001 seconds |
5
- ./spec/integration/class_enhancement_spec.rb[1:2:1:1] | passed | 0.00014 seconds |
6
- ./spec/integration/class_enhancement_spec.rb[1:2:2:1] | passed | 0.0001 seconds |
7
- ./spec/integration/matcher_spec.rb[1:1:1] | passed | 0.00017 seconds |
8
- ./spec/integration/matcher_spec.rb[1:1:2] | passed | 0.0001 seconds |
9
- ./spec/integration/matcher_spec.rb[1:1:3] | passed | 0.00122 seconds |
10
- ./spec/integration/matcher_spec.rb[1:1:4:1] | passed | 0.00009 seconds |
11
- ./spec/integration/matcher_spec.rb[1:1:4:2] | passed | 0.00147 seconds |
12
- ./spec/integration/result_matcher_spec.rb[1:1:1:1] | passed | 0.00012 seconds |
13
- ./spec/integration/result_matcher_spec.rb[1:1:2:1] | passed | 0.00008 seconds |
14
- ./spec/integration/result_matcher_spec.rb[1:1:3:1:1] | passed | 0.00015 seconds |
15
- ./spec/integration/result_matcher_spec.rb[1:1:3:2:1] | passed | 0.00017 seconds |
16
- ./spec/unit/case_spec.rb[1:1:1] | passed | 0.00087 seconds |
17
- ./spec/unit/case_spec.rb[1:2:1] | passed | 0.00008 seconds |
18
- ./spec/unit/case_spec.rb[1:2:2] | passed | 0.00056 seconds |
@@ -1,64 +0,0 @@
1
- require "dry-monads"
2
- require "dry/matcher/result_matcher"
3
-
4
- RSpec.describe "Class enhancement with Dry::Matcher.for" do
5
- let(:operation) {
6
- Class.new do
7
- include Dry::Matcher.for(:call, with: Dry::Matcher::ResultMatcher)
8
-
9
- def call(bool)
10
- bool ? Dry::Monads::Success("a success") : Dry::Monads::Failure("a failure")
11
- end
12
- end.new
13
- }
14
-
15
- describe "match blocks" do
16
- subject(:match) {
17
- operation.call(input) do |m|
18
- m.success do |v|
19
- "Matched success: #{v}"
20
- end
21
-
22
- m.failure do |v|
23
- "Matched failure: #{v}"
24
- end
25
- end
26
- }
27
-
28
- context "successful result" do
29
- let(:input) { true }
30
-
31
- it "matches on success" do
32
- expect(match).to eq "Matched success: a success"
33
- end
34
- end
35
-
36
- context "failed result" do
37
- let(:input) { false }
38
-
39
- it "matches on failure" do
40
- expect(match).to eq "Matched failure: a failure"
41
- end
42
- end
43
- end
44
-
45
- describe "without match blocks" do
46
- subject(:result) { operation.call(input) }
47
-
48
- context "successful result" do
49
- let(:input) { true }
50
-
51
- it "returns the result" do
52
- expect(result).to eq Dry::Monads::Success("a success")
53
- end
54
- end
55
-
56
- context "failed result" do
57
- let(:input) { false }
58
-
59
- it "returns the result" do
60
- expect(result).to eq Dry::Monads::Failure("a failure")
61
- end
62
- end
63
- end
64
- end
@@ -1,96 +0,0 @@
1
- require "dry-monads"
2
-
3
- RSpec.describe Dry::Matcher do
4
- context "with match cases provided" do
5
- let(:success_case) {
6
- Dry::Matcher::Case.new(
7
- match: -> result { result.success? },
8
- resolve: -> result { result.value! },
9
- )
10
- }
11
-
12
- let(:failure_case) {
13
- Dry::Matcher::Case.new(
14
- match: -> result { result.failure? },
15
- resolve: -> result { result.failure },
16
- )
17
- }
18
-
19
- let(:matcher) {
20
- Dry::Matcher.new(
21
- success: success_case,
22
- failure: failure_case,
23
- )
24
- }
25
-
26
- def call_match(input)
27
- matcher.(input) do |m|
28
- m.success do |v|
29
- "Success: #{v}"
30
- end
31
-
32
- m.failure do |v|
33
- "Failure: #{v}"
34
- end
35
- end
36
- end
37
-
38
- it "matches on success" do
39
- input = Dry::Monads::Success("Yes!")
40
- expect(call_match(input)).to eq "Success: Yes!"
41
- end
42
-
43
- it "matches on failure" do
44
- input = Dry::Monads::Failure("No!")
45
- expect(call_match(input)).to eq "Failure: No!"
46
- end
47
-
48
- it "requires an exhaustive match" do
49
- input = Dry::Monads::Success("Yes!")
50
-
51
- expect {
52
- matcher.(input) do |m|
53
- m.success { |v| "Success: #{v}" }
54
- end
55
- }.to raise_error Dry::Matcher::NonExhaustiveMatchError
56
- end
57
-
58
- context "with patterns" do
59
- let(:success_case) {
60
- Dry::Matcher::Case.new(
61
- match: -> result { result.first == :ok },
62
- resolve: -> result { result.last },
63
- )
64
- }
65
-
66
- let(:failure_case) {
67
- Dry::Matcher::Case.new(
68
- match: -> result, failure_type {
69
- result.length == 3 && result[0] == :failure && result[1] == failure_type
70
- },
71
- resolve: -> result { result.last },
72
- )
73
- }
74
-
75
- def call_match(input)
76
- matcher.(input) do |m|
77
- m.success
78
-
79
- m.failure :my_error do |v|
80
- "Pattern-matched failure: #{v}"
81
- end
82
- end
83
- end
84
-
85
- it "matches using the provided pattern" do
86
- input = [:failure, :my_error, "No!"]
87
- expect(call_match(input)).to eq "Pattern-matched failure: No!"
88
- end
89
-
90
- it "doesn't match if the pattern doesn't match" do
91
- input = [:failure, :non_matching_error, "No!"]
92
- expect(call_match(input)).to be_nil
93
- end
94
- end
95
- end
96
- end
@@ -1,56 +0,0 @@
1
- require "dry-monads"
2
- require "dry/matcher/result_matcher"
3
-
4
- RSpec.describe "Dry::Matcher::ResultMatcher" do
5
- describe "external matching" do
6
- subject(:match) {
7
- Dry::Matcher::ResultMatcher.(result) do |m|
8
- m.success do |v|
9
- "Matched success: #{v}"
10
- end
11
-
12
- m.failure do |v|
13
- "Matched failure: #{v}"
14
- end
15
- end
16
- }
17
-
18
- context "successful result" do
19
- let(:result) { Dry::Monads::Success("a success") }
20
-
21
- it "matches on success" do
22
- expect(match).to eq "Matched success: a success"
23
- end
24
- end
25
-
26
- context "failed result" do
27
- let(:result) { Dry::Monads::Failure("a failure") }
28
-
29
- it "matches on failure" do
30
- expect(match).to eq "Matched failure: a failure"
31
- end
32
- end
33
-
34
- context "result convertible to result" do
35
- context "converts to success" do
36
- let(:result) {
37
- Dry::Monads::Try.lift([StandardError], -> { 'a success' })
38
- }
39
-
40
- it "matches on success" do
41
- expect(match).to eq "Matched success: a success"
42
- end
43
- end
44
-
45
- context "converts to failure" do
46
- let(:result) {
47
- Dry::Monads::Try.lift([StandardError], -> { raise('a failure') })
48
- }
49
-
50
- it "matches on failure" do
51
- expect(match).to eq "Matched failure: a failure"
52
- end
53
- end
54
- end
55
- end
56
- end
data/spec/spec_helper.rb DELETED
@@ -1,57 +0,0 @@
1
- if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.3"
2
- require "simplecov"
3
- SimpleCov.start
4
- end
5
-
6
-
7
- begin
8
- require "byebug"
9
- rescue LoadError; end
10
-
11
- require "dry-matcher"
12
-
13
- Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each do |f| require f end
14
-
15
- RSpec.configure do |config|
16
- config.disable_monkey_patching!
17
-
18
- config.expect_with :rspec do |expectations|
19
- # This option will default to `true` in RSpec 4.
20
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
21
- end
22
-
23
- config.mock_with :rspec do |mocks|
24
- # This option will default to `true` in RSpec 4.
25
- mocks.verify_partial_doubles = true
26
- end
27
-
28
- # Allows RSpec to persist some state between runs in order to support
29
- # the `--only-failures` and `--next-failure` CLI options. We recommend
30
- # you configure your source control system to ignore this file.
31
- config.example_status_persistence_file_path = "spec/examples.txt"
32
-
33
- # This setting enables warnings. It's recommended, but in some cases may
34
- # be too noisy due to issues in dependencies.
35
- config.warnings = true
36
-
37
- # Many RSpec users commonly either run the entire suite or an individual
38
- # file, and it's useful to allow more verbose output when running an
39
- # individual spec file.
40
- if config.files_to_run.one?
41
- # Use the documentation formatter for detailed output, unless a formatter
42
- # has already been configured (e.g. via a command-line flag).
43
- config.default_formatter = "doc"
44
- end
45
-
46
- # Run specs in random order to surface order dependencies. If you find an
47
- # order dependency and want to debug it, you can fix the order by providing
48
- # the seed, which is printed after each run.
49
- # --seed 1234
50
- config.order = :random
51
-
52
- # Seed global randomization in this process using the `--seed` CLI option.
53
- # Setting this allows you to use `--seed` to deterministically reproduce
54
- # test failures related to randomization by passing the same `--seed` value
55
- # as the one that triggered the failure.
56
- Kernel.srand config.seed
57
- end
@@ -1,22 +0,0 @@
1
- RSpec.describe Dry::Matcher::Case do
2
- describe "#matches?" do
3
- it "calls the match proc with the value" do
4
- kase = described_class.new(match: -> value { value.even? })
5
- expect(kase.matches?(2)).to be true
6
- expect(kase.matches?(1)).to be false
7
- end
8
- end
9
-
10
- describe "#resolve" do
11
- it "calls the resolve proc with the value" do
12
- kase = described_class.new(match: -> * { true }, resolve: -> value { value.to_s })
13
-
14
- expect(kase.resolve(123)).to eq "123"
15
- end
16
-
17
- it "defaults to passing through the value" do
18
- kase = described_class.new(match: -> * { true })
19
- expect(kase.resolve(123)).to eq 123
20
- end
21
- end
22
- end