eqq 0.0.1 → 0.0.6

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: 4bca6982f43fb57004acdca8facb594d0d178999bdcb323a41663138d9f00325
4
- data.tar.gz: 31f2e909e814925d47cf64824ac1267597fc2b85d3a4704ed9612704f6b41071
3
+ metadata.gz: d0645f6178f68b0a105efa1bcf389bd3c0b1f056cd94a6c6684b0d18bc9284f8
4
+ data.tar.gz: a93f3a7df8098fbb1e7bdb61b5ad36c29a67e4471e563c63bb562b7896cfa60d
5
5
  SHA512:
6
- metadata.gz: e30c7e8c661ffb9764e9c604503454e3bd29de40075f6e8296540d0b3a41a482c1221178d155e2c090b1b075ea4ec4d6f63306d5eaeba3b69e505e981abd0712
7
- data.tar.gz: '0885388e12a098f0c26415b252f0bc552c39e526df9ffbed2beffcc046ffe778eee99a71120e23e6ea80c61c1aee67b5769208196fee9d422646a6d90e4e92d4'
6
+ metadata.gz: a5d02720dfa28fc5aefed2907008e14e76dc00fd087eb83eb6c89bbf8c976b0258410d554592fc1368938bfcda8e96efc99ae2a811f285dfba33b210bf795ba5
7
+ data.tar.gz: e8077c6bd4ab3c5e363abeaa5154dca3bf8154140a73f75d238883cefe92855fb8da843ba46037c12ffb9bc710b9449b1d121a6e2119dc774520bd237f71d564
data/MIT-LICENSE.txt CHANGED
@@ -7,7 +7,7 @@ of this software and associated documentation files (the "Software"), to deal
7
7
  in the Software without restriction, including without limitation the rights
8
8
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
10
+ furnished to do so, subject to the following patterns:
11
11
 
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
data/README.md CHANGED
@@ -3,18 +3,16 @@
3
3
  ![Build Status](https://github.com/kachick/eqq/actions/workflows/test_behaviors.yml/badge.svg?branch=main)
4
4
  [![Gem Version](https://badge.fury.io/rb/eqq.png)](http://badge.fury.io/rb/eqq)
5
5
 
6
- Pattern objects builder.
7
-
8
- `eqq` means `#===`
6
+ Pattern objects builder
9
7
 
10
8
  ## Usage
11
9
 
12
- Require Ruby 2.5 or later
10
+ Require Ruby 2.6 or later
13
11
 
14
12
  Add below code into your Gemfile
15
13
 
16
14
  ```ruby
17
- gem 'eqq', '>= 0.0.1', '< 0.1.0'
15
+ gem 'eqq', '>= 0.0.6', '< 0.1.0'
18
16
  ```
19
17
 
20
18
  ### Overview
@@ -29,10 +27,129 @@ require 'eqq'
29
27
  pattern = Eqq.define do
30
28
  OR(AND(Float, 20..50), Integer)
31
29
  end
30
+
31
+ p pattern #=> "OR(AND(Float, 20..50), Integer)"
32
32
  [4.2, 42, 42.0, 420].grep(pattern) #=> [42, 42.0, 420]
33
+
34
+ inverted = Eqq.NOT(pattern)
35
+ p inverted #=> "NOT(OR(AND(Float, 20..50), Integer))"
36
+ [4.2, 42, 42.0, 420].grep(inverted) #=> [4.2]
37
+
38
+ Eqq.SEND(:all?, pattern) === [4.2, 42, 42.0, 420] #=> false
39
+ Eqq.SEND(:any?, pattern) === [4.2, 42, 42.0, 420] #=> true
40
+
41
+ ret_in_case = (
42
+ case 42
43
+ when pattern
44
+ 'Should be matched here! :)'
45
+ when inverted
46
+ 'Should not be matched here! :<'
47
+ else
48
+ 'Should not be matched here too! :<'
49
+ end
50
+ )
51
+
52
+ p ret_in_case #=> Should be matched here! :)
53
+
54
+ ret_in_case = (
55
+ case 4.2
56
+ when pattern
57
+ 'Should not be matched here! :<'
58
+ when inverted
59
+ 'Should be matched here! :)'
60
+ else
61
+ 'Should not be matched here too! :<'
62
+ end
63
+ )
64
+
65
+ p ret_in_case #=> Should be matched here! :)
66
+
67
+ class MyClass
68
+ include Eqq::Buildable
69
+
70
+ def example
71
+ [4.2, 42, 42.0, 420].grep(OR(AND(Float, 20..50), Integer))
72
+ end
73
+ end
74
+ MyClass.new.example #=> [42, 42.0, 420]
33
75
  ```
34
76
 
77
+ ### Explanation
78
+
79
+ All products can be called as `pattern === other`.
80
+
81
+ This signature will fit in most Ruby code.
82
+
83
+ * `case ~ when` syntax
84
+ * Enumerable#grep
85
+ * Enumerable#grep_v
86
+ * Enumerable#all?
87
+ * Enumerable#any?
88
+ * Enumerable#none?
89
+ * Enumerable#one?
90
+ * Enumerable#slice_after
91
+ * Enumerable#slice_before
92
+
93
+ They can take this interface as the `pattern`.
94
+
95
+ And you already saw. All of patterns can be mixed with other patterns as a parts.
96
+ Reuse as you wish!
97
+
98
+ ### Builders
99
+
100
+ * OR(*patterns) - Product returns `true` when matched even one pattern
101
+ * AND(*patterns) - Product returns `true` when matched all patterns
102
+ * NOT(pattern) - Product returns `true` when not matched the pattern
103
+ * CAN(*method_names) - Product returns `true` when it has all of the methods (checked with `respond_to?`)
104
+ * RESCUE(exception_class/module, pattern) - Product returns `true` when the pattern raises the exception
105
+ * QUIET(*patterns) - Product returns `true` when all patterns did not raise any exception
106
+ * EQ(object) - Product returns `true` when matched with `#==`
107
+ * SAME(object) - Product returns `true` when matched with `#equal?`
108
+ * SEND(name, pattern) - Basically provided for Enumerable
109
+ * BOOLEAN() - Product returns `true` when matched to `true` or `false`
110
+ * ANYTHING() - Product returns `true`, always `true`
111
+ * NEVER() - Product returns `false`, always `false`
112
+ * XOR(pattern1, pattern2) - Product returns `true` when matched one of the pattern, when matched both returns `false`
113
+ * NAND(*patterns) - Product is an inverted `AND`
114
+ * NOR(*patterns) - Product is an inverted `OR`
115
+
116
+ ### Best fit for RSpec's `satisfy` matcher too
117
+
118
+ All builders actually generate a `Proc (lambda)` instance.
119
+ The signature will fit for RSpec's built-in [`satisfy` matcher](https://relishapp.com/rspec/rspec-expectations/v/3-10/docs/built-in-matchers/satisfy-matcher) too.
120
+
121
+ ```ruby
122
+ RSpec.describe RSpec::Matchers::BuiltIn::Satisfy do
123
+ let(:product) { Eqq.AND(Integer, 24..42) }
124
+
125
+ it 'perfectly works' do
126
+ expect(23).not_to satisfy(&product)
127
+ expect(24).to satisfy(&product)
128
+ expect(24.0).not_to satisfy(&product)
129
+ expect(42).to satisfy(&product)
130
+ expect(42.0).not_to satisfy(&product)
131
+ expect(43).not_to satisfy(&product)
132
+ end
133
+ end
134
+ ```
135
+
136
+ ### Use builders without receiver specifying
137
+
138
+ When you felt annoy to write `Eqq` in many place, some ways exist.
139
+
140
+ * `Eqq.define(&block)` - In the block scope, all builder methods can be used without receiver
141
+ * `extend Eqq::Buildable` - In the class/module, all builders can be used as class methods
142
+ * `include Eqq::Buildable` - In the class/module, all builders can be used as instance methods
143
+
144
+ ### Signature
145
+
146
+ * This gem provides [ruby/rbs](https://github.com/ruby/rbs) signature file
147
+
35
148
  ## Links
36
149
 
37
150
  * [Repository](https://github.com/kachick/eqq)
38
151
  * [API documents](https://kachick.github.io/eqq)
152
+
153
+ ## NOTE
154
+
155
+ * [`eqq` is the implementation name of `#===` in CRuby](https://github.com/ruby/ruby/blob/2a685da1fcd928530509e99f5edb4117bc377994/range.c#L1859)
data/lib/eqq.rb CHANGED
@@ -2,238 +2,60 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Copyright (c) 2011 Kenichi Kamiya
5
- # Forked from https://github.com/kachick/eqq at 2021
5
+ # Forked from https://github.com/kachick/validation at 2021
6
6
 
7
- module Eqq
8
- def self.conditionable?(object)
9
- case object
10
- when Proc, Method
11
- object.arity == 1
12
- else
13
- begin
14
- object.respond_to?(:===)
15
- rescue NoMethodError
16
- false
17
- end
18
- end
19
- end
20
-
21
- def self.define(&block)
22
- module_exec(&block)
23
- end
24
-
25
- module_function
26
-
27
- # A condition builder.
28
- # @param condition1 [Proc, Method, #===]
29
- # @param condition2 [Proc, Method, #===]
30
- # @param conditions [Array<Proc, Method, #===>]
31
- # @return [Proc]
32
- # this lambda return true if match all conditions
33
- def AND(condition1, condition2, *conditions)
34
- ->v {
35
- [condition1, condition2, *conditions].all? { |condition| condition === v }
36
- }
37
- end
38
-
39
- # A condition builder.
40
- # @param condition1 [Proc, Method, #===]
41
- # @param condition2 [Proc, Method, #===]
42
- # @param conditions [Array<Proc, Method, #===>]
43
- # @return [Proc]
44
- def NAND(condition1, condition2, *conditions)
45
- NOT(AND(condition1, condition2, *conditions))
46
- end
47
-
48
- # A condition builder.
49
- # @param condition1 [Proc, Method, #===]
50
- # @param condition2 [Proc, Method, #===]
51
- # @param conditions [Array<Proc, Method, #===>]
52
- # @return [Proc]
53
- # this lambda return true if match a any condition
54
- def OR(condition1, condition2, *conditions)
55
- ->v {
56
- [condition1, condition2, *conditions].any? { |condition| condition === v }
57
- }
58
- end
59
-
60
- # A condition builder.
61
- # @param condition1 [Proc, Method, #===]
62
- # @param condition2 [Proc, Method, #===]
63
- # @param conditions [Array<Proc, Method, #===>]
64
- # @return [Proc]
65
- def NOR(condition1, condition2, *conditions)
66
- NOT(OR(condition1, condition2, *conditions))
67
- end
68
-
69
- # A condition builder.
70
- # @param condition1 [Proc, Method, #===]
71
- # @param condition2 [Proc, Method, #===]
72
- # @param conditions [Array<Proc, Method, #===>]
73
- # @return [Proc]
74
- def XOR(condition1, condition2, *conditions)
75
- ->v {
76
- [condition1, condition2, *conditions].one? { |condition| condition === v }
77
- }
78
- end
79
-
80
- # A condition builder.
81
- # @param condition1 [Proc, Method, #===]
82
- # @param condition2 [Proc, Method, #===]
83
- # @param conditions [Array<Proc, Method, #===>]
84
- # @return [Proc]
85
- def XNOR(condition1, condition2, *conditions)
86
- NOT(XOR(condition1, condition2, *conditions))
87
- end
88
-
89
- # A condition builder.
90
- # @param condition [Proc, Method, #===]
91
- # @return [Proc] A condition invert the original condition.
92
- def NOT(condition)
93
- unless Eqq.conditionable?(condition)
94
- raise TypeError, 'wrong object for condition'
95
- end
96
-
97
- ->v { !(condition === v) }
98
- end
99
-
100
- # A condition builder.
101
- # @param obj [#==]
102
- # @return [Proc]
103
- # this lambda return true if a argument match under #== method
104
- def EQ(obj)
105
- ->v { obj == v }
106
- end
107
-
108
- # A condition builder.
109
- # @param obj [#equal?]
110
- # @return [Proc]
111
- # this lambda return true if a argument match under #equal? method
112
- def SAME(obj)
113
- ->v { obj.equal?(v) }
114
- end
115
-
116
- # A condition builder.
117
- # @param message1 [Symbol, String]
118
- # @param messages [Array<Symbol, String>]
119
- # @return [Proc]
120
- # this lambda return true if a argument respond to all messages
121
- def CAN(message1, *messages)
122
- messages = [message1, *messages].map(&:to_sym)
7
+ require_relative 'eqq/version'
123
8
 
124
- ->v {
125
- messages.all? { |message| v.respond_to?(message) }
126
- }
127
- end
9
+ # Pattern objects builder
10
+ module Eqq
11
+ # Base error of this library
12
+ class Error < StandardError; end
128
13
 
129
- # A condition builder.
130
- # @param condition1 [Proc, Method, #===]
131
- # @param conditions [Array<Proc, Method, #===>]
132
- # @return [Proc]
133
- # this lambda return true
134
- # if face no exception when a argument checking under all conditions
135
- def QUIET(condition1, *conditions)
136
- conditions = [condition1, *conditions]
137
- unless conditions.all? { |c| Eqq.conditionable?(c) }
138
- raise TypeError, 'wrong object for condition'
139
- end
14
+ # Raised when found some products are invalid as a pattern object
15
+ class InvalidProductError < Error; end
140
16
 
141
- ->v {
142
- conditions.all? { |condition|
17
+ class << self
18
+ def pattern?(object)
19
+ case object
20
+ when Proc, Method
21
+ object.arity == 1
22
+ else
143
23
  begin
144
- condition === v
145
- rescue Exception
24
+ object.respond_to?(:===)
25
+ rescue NoMethodError
146
26
  false
147
- else
148
- true
149
27
  end
150
- }
151
- }
152
- end
153
-
154
- # A condition builder.
155
- # @param exception [Exception]
156
- # @param exceptions [Array<Exception>]
157
- # @return [Proc]
158
- # this lambda return true
159
- # if catch any kindly exceptions when a argument checking in a block parameter
160
- def RESCUE(exception, *exceptions, &condition)
161
- exceptions = [exception, *exceptions]
162
- raise ArgumentError unless Eqq.conditionable?(condition)
163
- raise ArgumentError unless exceptions.all?(Exception)
164
-
165
- ->v {
166
- begin
167
- condition.call(v)
168
- false
169
- rescue *exceptions
170
- true
171
- rescue Exception
172
- false
173
28
  end
174
- }
175
- end
176
-
177
- # A condition builder.
178
- # @param exception [Exception]
179
- # @return [Proc]
180
- # this lambda return true
181
- # if catch a specific exception when a argument checking in a block parameter
182
- def CATCH(exception, &condition)
183
- raise ArgumentError unless Eqq.conditionable?(condition)
184
- raise ArgumentError unless exceptions.all?(Exception)
29
+ end
185
30
 
186
- ->v {
187
- begin
188
- condition.call(v)
189
- rescue Exception => err
190
- err.instance_of?(exception)
191
- else
192
- false
193
- end
194
- }
195
- end
31
+ # @deprecated Use {pattern?} instead. This will be dropped since `0.1.0`
32
+ def valid?(object)
33
+ pattern?(object)
34
+ end
196
35
 
197
- # A condition builder.
198
- # @param condition1 [Proc, Method, #===]
199
- # @param condition2 [Proc, Method, #===]
200
- # @param conditions [Array<Proc, Method, #===>]
201
- # @return [Proc]
202
- # this lambda return true
203
- # if all included objects match all conditions
204
- def ALL(condition1, condition2, *conditions)
205
- condition = Eqq.AND(condition1, condition2, *conditions)
36
+ # @api private
37
+ def satisfy?(object)
38
+ (Proc === object) && object.lambda? && (object.arity == 1)
39
+ end
206
40
 
207
- ->list {
208
- enum = (
209
- case
210
- when list.respond_to?(:each_value)
211
- list.each_value
212
- when list.respond_to?(:all?)
213
- list
214
- when list.respond_to?(:each)
215
- list.each
216
- else
217
- return false
218
- end
219
- )
41
+ # @return [#===]
42
+ # @raise [InvalidProductError] if the return value is invalid as a pattern object
43
+ def define(&block)
44
+ pattern = DSLScope.new.instance_exec(&block)
45
+ raise InvalidProductError unless satisfy?(pattern)
220
46
 
221
- enum.all?(condition)
222
- }
47
+ pattern
48
+ end
223
49
  end
50
+ end
224
51
 
225
- def ANYTHING
226
- # BasicObject.=== always passing
227
- BasicObject
228
- end
52
+ require_relative 'eqq/buildable'
229
53
 
230
- BOOLEAN = OR(SAME(true), SAME(false))
54
+ module Eqq
55
+ extend Buildable
231
56
 
232
- # A getter for a useful condition.
233
- # @return [BOOLEAN] "true or false"
234
- def BOOLEAN
235
- BOOLEAN
57
+ class DSLScope
58
+ include Buildable
236
59
  end
60
+ private_constant :DSLScope
237
61
  end
238
-
239
- require_relative 'eqq/version'
@@ -0,0 +1,292 @@
1
+ # coding: us-ascii
2
+ # frozen_string_literal: true
3
+
4
+ module Eqq
5
+ # Actually having definitions for the pattern builders
6
+ module Buildable
7
+ class << self
8
+ # When the inspection is failed some unexpected reasons, it will fallback to this value
9
+ # This value is not fixed as a spec, might be changed in future
10
+ INSPECTION_FALLBACK = 'UninspectableObject'
11
+
12
+ # @api private
13
+ # @return [String]
14
+ def safe_inspect_for(object)
15
+ String.try_convert(object.inspect) || INSPECTION_FALLBACK
16
+ rescue Exception
17
+ # This implementation used `RSpec::Support::ObjectFormatter::UninspectableObjectInspector` as a reference, thank you!
18
+ # ref: https://github.com/kachick/times_kachick/issues/97
19
+ singleton_class = class << object; self; end
20
+ begin
21
+ klass = singleton_class.ancestors.detect { |ancestor| !ancestor.equal?(singleton_class) }
22
+ native_object_id = '%#016x' % (object.__id__ << 1)
23
+ "#<#{klass}:#{native_object_id}>"
24
+ rescue Exception
25
+ INSPECTION_FALLBACK
26
+ end
27
+ end
28
+
29
+ # @api private
30
+ # @return [void]
31
+ def define_inspect_on(product, name:, arguments:)
32
+ inspect = "#{name}(#{arguments.map { |argument| safe_inspect_for(argument) }.join(', ')})".freeze
33
+ product.define_singleton_method(:inspect) do
34
+ inspect
35
+ end
36
+ end
37
+
38
+ # @api private
39
+ # @return [void]
40
+ def validate_patterns(*patterns)
41
+ invalids = patterns.reject { |pattern| Eqq.pattern?(pattern) }
42
+ invalid_inspections = invalids.map { |invalid| safe_inspect_for(invalid) }.join(', ')
43
+ raise ArgumentError, "given `#{invalid_inspections}` are invalid as pattern objects" unless invalids.empty?
44
+ end
45
+ end
46
+
47
+ # Product returns `true` when matched all patterns
48
+ #
49
+ # @param pattern1 [Proc, Method, #===]
50
+ # @param pattern2 [Proc, Method, #===]
51
+ # @param patterns [Array<Proc, Method, #===>]
52
+ # @return [Proc]
53
+ def AND(pattern1, pattern2, *patterns)
54
+ patterns = [pattern1, pattern2, *patterns].freeze
55
+ Buildable.validate_patterns(*patterns)
56
+
57
+ product = ->v {
58
+ patterns.all? { |pattern| pattern === v }
59
+ }
60
+
61
+ Buildable.define_inspect_on(product, name: 'AND', arguments: patterns)
62
+
63
+ product
64
+ end
65
+
66
+ # Product is an inverted {#AND}
67
+ #
68
+ # @param pattern1 [Proc, Method, #===]
69
+ # @param pattern2 [Proc, Method, #===]
70
+ # @param patterns [Array<Proc, Method, #===>]
71
+ # @return [Proc]
72
+ def NAND(pattern1, pattern2, *patterns)
73
+ NOT(AND(pattern1, pattern2, *patterns))
74
+ end
75
+
76
+ # Product returns `true` when matched even one pattern
77
+ #
78
+ # @param pattern1 [Proc, Method, #===]
79
+ # @param pattern2 [Proc, Method, #===]
80
+ # @param patterns [Array<Proc, Method, #===>]
81
+ # @return [Proc]
82
+ def OR(pattern1, pattern2, *patterns)
83
+ patterns = [pattern1, pattern2, *patterns].freeze
84
+ Buildable.validate_patterns(*patterns)
85
+
86
+ product = ->v {
87
+ patterns.any? { |pattern| pattern === v }
88
+ }
89
+ Buildable.define_inspect_on(product, name: 'OR', arguments: patterns)
90
+
91
+ product
92
+ end
93
+
94
+ # Product is an inverted {#OR}
95
+ #
96
+ # @param pattern1 [Proc, Method, #===]
97
+ # @param pattern2 [Proc, Method, #===]
98
+ # @param patterns [Array<Proc, Method, #===>]
99
+ # @return [Proc]
100
+ def NOR(pattern1, pattern2, *patterns)
101
+ NOT(OR(pattern1, pattern2, *patterns))
102
+ end
103
+
104
+ # Product returns `true` when matched one of the pattern, when matched both returns `false`
105
+ #
106
+ # @param pattern1 [Proc, Method, #===]
107
+ # @param pattern2 [Proc, Method, #===]
108
+ # @return [Proc]
109
+ def XOR(pattern1, pattern2)
110
+ patterns = [pattern1, pattern2].freeze
111
+ Buildable.validate_patterns(*patterns)
112
+
113
+ product = ->v {
114
+ patterns.one? { |pattern| pattern === v }
115
+ }
116
+ Buildable.define_inspect_on(product, name: 'XOR', arguments: patterns)
117
+
118
+ product
119
+ end
120
+
121
+ # Product returns `true` when not matched the pattern
122
+ #
123
+ # @param pattern [Proc, Method, #===]
124
+ # @return [Proc]
125
+ def NOT(pattern)
126
+ Buildable.validate_patterns(pattern)
127
+
128
+ product = ->v { !(pattern === v) }
129
+
130
+ Buildable.define_inspect_on(product, name: 'NOT', arguments: [pattern])
131
+
132
+ product
133
+ end
134
+
135
+ # Product returns `true` when matched with `#==`
136
+ #
137
+ # @param obj [#==]
138
+ # @return [Proc]
139
+ def EQ(obj)
140
+ product = ->v { obj == v }
141
+ Buildable.define_inspect_on(product, name: 'EQ', arguments: [obj])
142
+ product
143
+ end
144
+
145
+ # Product returns `true` when matched with `#equal?`
146
+ #
147
+ # @param obj [#equal?]
148
+ # @return [Proc]
149
+ def SAME(obj)
150
+ product = ->v { obj.equal?(v) }
151
+ Buildable.define_inspect_on(product, name: 'SAME', arguments: [obj])
152
+ product
153
+ end
154
+
155
+ # Product returns `true` when it has all of the methods (checked with `respond_to?`)
156
+ #
157
+ # @param message1 [Symbol, String, #to_sym]
158
+ # @param messages [Array<Symbol, String, #to_sym>]
159
+ # @return [Proc]
160
+ def CAN(message1, *messages)
161
+ messages = (
162
+ begin
163
+ [message1, *messages].map(&:to_sym).freeze
164
+ rescue NoMethodError
165
+ raise ArgumentError
166
+ end
167
+ )
168
+
169
+ product = ->v {
170
+ messages.all? { |message|
171
+ begin
172
+ v.respond_to?(message)
173
+ rescue NoMethodError
174
+ false
175
+ end
176
+ }
177
+ }
178
+
179
+ Buildable.define_inspect_on(product, name: 'CAN', arguments: messages)
180
+
181
+ product
182
+ end
183
+
184
+ # Product returns `true` when all patterns did not raise any exception
185
+ #
186
+ # @param pattern1 [Proc, Method, #===]
187
+ # @param patterns [Array<Proc, Method, #===>]
188
+ # @return [Proc]
189
+ def QUIET(pattern1, *patterns)
190
+ patterns = [pattern1, *patterns].freeze
191
+ Buildable.validate_patterns(*patterns)
192
+
193
+ product = ->v {
194
+ patterns.all? { |pattern|
195
+ begin
196
+ pattern === v
197
+ rescue Exception
198
+ false
199
+ else
200
+ true
201
+ end
202
+ }
203
+ }
204
+
205
+ Buildable.define_inspect_on(product, name: 'QUIET', arguments: patterns)
206
+
207
+ product
208
+ end
209
+
210
+ # Product returns `true` when the pattern raises the exception
211
+ #
212
+ # @param mod [Module]
213
+ # @param pattern [Proc, Method, #===]
214
+ # @return [Proc]
215
+ def RESCUE(mod, pattern)
216
+ Buildable.validate_patterns(pattern)
217
+ raise ArgumentError unless Module === mod
218
+
219
+ product = ->v {
220
+ begin
221
+ pattern === v
222
+ false
223
+ rescue mod
224
+ true
225
+ rescue Exception
226
+ false
227
+ end
228
+ }
229
+
230
+ Buildable.define_inspect_on(product, name: 'RESCUE', arguments: [mod, pattern])
231
+
232
+ product
233
+ end
234
+
235
+ # Basically provided for Enumerable
236
+ #
237
+ # @param name [Symbol, String, #to_sym]
238
+ # @param pattern [Proc, Method, #===]
239
+ # @return [Proc]
240
+ def SEND(name, pattern)
241
+ name = (
242
+ begin
243
+ name.to_sym
244
+ rescue NoMethodError
245
+ raise ArgumentError
246
+ end
247
+ )
248
+ Buildable.validate_patterns(pattern)
249
+
250
+ product = ->v {
251
+ v.__send__(name, pattern)
252
+ }
253
+
254
+ Buildable.define_inspect_on(product, name: 'SEND', arguments: [name, pattern])
255
+
256
+ product
257
+ end
258
+
259
+ ANYTHING = ->_v { true }
260
+ define_inspect_on(ANYTHING, name: 'ANYTHING', arguments: [])
261
+ private_constant :ANYTHING
262
+
263
+ # Product returns `true`, always `true`
264
+ #
265
+ # @return [Proc]
266
+ def ANYTHING
267
+ ANYTHING
268
+ end
269
+
270
+ NEVER = ->_v { false }
271
+ define_inspect_on(NEVER, name: 'NEVER', arguments: [])
272
+ private_constant :NEVER
273
+
274
+ # Product returns `false`, always `false`
275
+ #
276
+ # @return [Proc]
277
+ def NEVER
278
+ NEVER
279
+ end
280
+
281
+ BOOLEAN = ->v { true.equal?(v) || false.equal?(v) }
282
+ define_inspect_on(BOOLEAN, name: 'BOOLEAN', arguments: [])
283
+ private_constant :BOOLEAN
284
+
285
+ # Product returns `true` when matched to `true` or `false`
286
+ #
287
+ # @return [Proc]
288
+ def BOOLEAN
289
+ BOOLEAN
290
+ end
291
+ end
292
+ end
data/lib/eqq/version.rb CHANGED
@@ -2,5 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Eqq
5
- VERSION = '0.0.1'
5
+ # This will be same as latest published gem version
6
+ VERSION = '0.0.6'
6
7
  end
data/sig/eqq.rbs ADDED
@@ -0,0 +1,112 @@
1
+ # Pattern objects builder
2
+ module Eqq
3
+ interface _Patternable
4
+ def ===: (untyped object) -> bool
5
+ end
6
+
7
+ interface _ToSym
8
+ def to_sym: -> Symbol
9
+ end
10
+
11
+ interface _Inspectable
12
+ def inspect: () -> String
13
+ end
14
+
15
+ module Buildable
16
+ type patternable_lambda = ^(untyped object) -> bool
17
+ type product = patternable_lambda & _Inspectable
18
+
19
+ # A private constant. Should not be used in your code.
20
+ ANYTHING: product
21
+
22
+ # A private constant. Should not be used in your code.
23
+ NEVER: product
24
+
25
+ # A private constant. Should not be used in your code.
26
+ BOOLEAN: product
27
+
28
+ # A private API. Should not be used in your code.
29
+ def self.safe_inspect_for: (untyped object)-> String
30
+
31
+ # A private API. Should not be used in your code.
32
+ def self.define_inspect_on: (patternable_lambda product, name: String, arguments: Array[untyped])-> void
33
+
34
+ # A private API. Should not be used in your code.
35
+ def self.validate_patterns: (*untyped) -> void
36
+
37
+ # Product returns `true` when matched even one pattern
38
+ def OR: (_Patternable, _Patternable, *_Patternable) -> product
39
+
40
+ # Product returns `true` when matched all patterns
41
+ def AND: (_Patternable, _Patternable, *_Patternable) -> product
42
+
43
+ # Product is an inverted `AND`
44
+ def NAND: (_Patternable, _Patternable, *_Patternable) -> product
45
+
46
+ # Product is an inverted `OR`
47
+ def NOR: (_Patternable, _Patternable, *_Patternable) -> product
48
+
49
+ # Product returns `true` when matched one of the pattern, when matched both returns `false`
50
+ def XOR: (_Patternable, _Patternable) -> product
51
+
52
+ # Product returns `true` when not matched the pattern
53
+ def NOT: (_Patternable) -> product
54
+
55
+ # Product returns `true` when matched with `#==`
56
+ def EQ: (untyped object) -> product
57
+
58
+ # Product returns `true` when matched with `#equal?`
59
+ def SAME: (untyped object) -> product
60
+
61
+ # Product returns `true` when it has all of the methods (checked with `respond_to?`)
62
+ def CAN: (_ToSym, *_ToSym) -> product
63
+
64
+ # Product returns `true` when the pattern raises the exception
65
+ def RESCUE: (Module, _Patternable) -> product
66
+
67
+ # Product returns `true` when all patterns did not raise any exception
68
+ def QUIET: (_Patternable, *_Patternable) -> product
69
+
70
+ # Basically provided for Enumerable
71
+ def SEND: (Symbol | String name, _Patternable) -> product
72
+
73
+ # Product returns `true`, always `true`
74
+ def ANYTHING: () -> product
75
+
76
+ # Product returns `false`, always `false`
77
+ def NEVER: () -> product
78
+
79
+ # Product returns `true` when matched to `true` or `false`
80
+ def BOOLEAN: () -> product
81
+ end
82
+
83
+ extend Buildable
84
+
85
+ # Base error of this library
86
+ class Error < StandardError
87
+ end
88
+
89
+ # Raised when found some products are invalid as a pattern object
90
+ class InvalidProductError < Error
91
+ end
92
+
93
+ # A private API. Should not be used in your code.
94
+ class DSLScope
95
+ include Buildable
96
+ end
97
+
98
+ VERSION: String
99
+
100
+ # Returns `true` when given object has patternable signature
101
+ def self.pattern?: (untyped object) -> bool
102
+
103
+ # Alias of `pattern?`. But deperecated. Do not use in your code anymore.
104
+ def self.valid?: (untyped object) -> bool
105
+
106
+ # Returns `true` when given object has correct signature as a product of builders
107
+ # Basically this is a private API. Should not be used in your code.
108
+ def self.satisfy?: (untyped object) -> bool
109
+
110
+ # In the block scope, all builder methods can be used without receiver
111
+ def self.define: { () -> _Patternable } -> _Patternable
112
+ end
metadata CHANGED
@@ -1,230 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eqq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenichi Kamiya
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-01 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: test-unit
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 3.4.1
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '4.0'
23
- type: :development
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 3.4.1
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '4.0'
33
- - !ruby/object:Gem::Dependency
34
- name: irb
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 1.3.5
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: '2.0'
43
- type: :development
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 1.3.5
50
- - - "<"
51
- - !ruby/object:Gem::Version
52
- version: '2.0'
53
- - !ruby/object:Gem::Dependency
54
- name: irb-power_assert
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - '='
58
- - !ruby/object:Gem::Version
59
- version: 0.0.2
60
- type: :development
61
- prerelease: false
62
- version_requirements: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - '='
65
- - !ruby/object:Gem::Version
66
- version: 0.0.2
67
- - !ruby/object:Gem::Dependency
68
- name: warning
69
- requirement: !ruby/object:Gem::Requirement
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: 1.2.0
74
- - - "<"
75
- - !ruby/object:Gem::Version
76
- version: '2.0'
77
- type: :development
78
- prerelease: false
79
- version_requirements: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- version: 1.2.0
84
- - - "<"
85
- - !ruby/object:Gem::Version
86
- version: '2.0'
87
- - !ruby/object:Gem::Dependency
88
- name: rake
89
- requirement: !ruby/object:Gem::Requirement
90
- requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- version: 13.0.3
94
- - - "<"
95
- - !ruby/object:Gem::Version
96
- version: '20.0'
97
- type: :development
98
- prerelease: false
99
- version_requirements: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: 13.0.3
104
- - - "<"
105
- - !ruby/object:Gem::Version
106
- version: '20.0'
107
- - !ruby/object:Gem::Dependency
108
- name: yard
109
- requirement: !ruby/object:Gem::Requirement
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: 0.9.26
114
- - - "<"
115
- - !ruby/object:Gem::Version
116
- version: '2'
117
- type: :development
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - ">="
122
- - !ruby/object:Gem::Version
123
- version: 0.9.26
124
- - - "<"
125
- - !ruby/object:Gem::Version
126
- version: '2'
127
- - !ruby/object:Gem::Dependency
128
- name: rubocop
129
- requirement: !ruby/object:Gem::Requirement
130
- requirements:
131
- - - ">="
132
- - !ruby/object:Gem::Version
133
- version: 1.15.0
134
- - - "<"
135
- - !ruby/object:Gem::Version
136
- version: 1.16.0
137
- type: :development
138
- prerelease: false
139
- version_requirements: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - ">="
142
- - !ruby/object:Gem::Version
143
- version: 1.15.0
144
- - - "<"
145
- - !ruby/object:Gem::Version
146
- version: 1.16.0
147
- - !ruby/object:Gem::Dependency
148
- name: rubocop-rake
149
- requirement: !ruby/object:Gem::Requirement
150
- requirements:
151
- - - ">="
152
- - !ruby/object:Gem::Version
153
- version: 0.5.1
154
- - - "<"
155
- - !ruby/object:Gem::Version
156
- version: 0.6.0
157
- type: :development
158
- prerelease: false
159
- version_requirements: !ruby/object:Gem::Requirement
160
- requirements:
161
- - - ">="
162
- - !ruby/object:Gem::Version
163
- version: 0.5.1
164
- - - "<"
165
- - !ruby/object:Gem::Version
166
- version: 0.6.0
167
- - !ruby/object:Gem::Dependency
168
- name: rubocop-performance
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - ">="
172
- - !ruby/object:Gem::Version
173
- version: 1.11.3
174
- - - "<"
175
- - !ruby/object:Gem::Version
176
- version: 1.12.0
177
- type: :development
178
- prerelease: false
179
- version_requirements: !ruby/object:Gem::Requirement
180
- requirements:
181
- - - ">="
182
- - !ruby/object:Gem::Version
183
- version: 1.11.3
184
- - - "<"
185
- - !ruby/object:Gem::Version
186
- version: 1.12.0
187
- - !ruby/object:Gem::Dependency
188
- name: rubocop-rubycw
189
- requirement: !ruby/object:Gem::Requirement
190
- requirements:
191
- - - ">="
192
- - !ruby/object:Gem::Version
193
- version: 0.1.6
194
- - - "<"
195
- - !ruby/object:Gem::Version
196
- version: 0.2.0
197
- type: :development
198
- prerelease: false
199
- version_requirements: !ruby/object:Gem::Requirement
200
- requirements:
201
- - - ">="
202
- - !ruby/object:Gem::Version
203
- version: 0.1.6
204
- - - "<"
205
- - !ruby/object:Gem::Version
206
- version: 0.2.0
207
- - !ruby/object:Gem::Dependency
208
- name: rubocop-md
209
- requirement: !ruby/object:Gem::Requirement
210
- requirements:
211
- - - ">="
212
- - !ruby/object:Gem::Version
213
- version: 1.0.1
214
- - - "<"
215
- - !ruby/object:Gem::Version
216
- version: 2.0.0
217
- type: :development
218
- prerelease: false
219
- version_requirements: !ruby/object:Gem::Requirement
220
- requirements:
221
- - - ">="
222
- - !ruby/object:Gem::Version
223
- version: 1.0.1
224
- - - "<"
225
- - !ruby/object:Gem::Version
226
- version: 2.0.0
227
- description: " [4.2, 42, 42.0, 420].grep(Eqq.AND(Integer, 20..50)) #=> [42]\n"
11
+ date: 2021-06-06 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |2
14
+ Pattern objects builder.
15
+
16
+ All products can be used as `pattern === something`.
17
+
18
+ All products can be mixed with other products as a parts.
19
+
20
+ Reuse as you wish!
228
21
  email:
229
22
  - kachick1+ruby@gmail.com
230
23
  executables: []
@@ -234,7 +27,9 @@ files:
234
27
  - MIT-LICENSE.txt
235
28
  - README.md
236
29
  - lib/eqq.rb
30
+ - lib/eqq/buildable.rb
237
31
  - lib/eqq/version.rb
32
+ - sig/eqq.rbs
238
33
  homepage: https://github.com/kachick/eqq
239
34
  licenses:
240
35
  - MIT
@@ -251,7 +46,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
251
46
  requirements:
252
47
  - - ">="
253
48
  - !ruby/object:Gem::Version
254
- version: 2.5.0
49
+ version: 2.6.0
255
50
  required_rubygems_version: !ruby/object:Gem::Requirement
256
51
  requirements:
257
52
  - - ">="
@@ -261,5 +56,5 @@ requirements: []
261
56
  rubygems_version: 3.2.15
262
57
  signing_key:
263
58
  specification_version: 4
264
- summary: Pattern objects builder. `eqq` means `#===`
59
+ summary: Pattern objects builder
265
60
  test_files: []