eqq 0.0.2 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +123 -5
- data/lib/eqq.rb +37 -10
- data/lib/eqq/buildable.rb +177 -48
- data/lib/eqq/version.rb +2 -1
- data/sig/eqq.rbs +92 -20
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: becfb749a93e73ea402265899e57d3deea45bf0eb457a29a66eafa03965b1572
|
4
|
+
data.tar.gz: 3f6578bd2f0b3334369834ff1f344b4b033318172586a9324496027a057507a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d76a45da1f1e181d77b6f9cbfc6f80d88af6abed0782b1da92f6562934bf551c2f55e5691be7108d0acf0521562a042bb5cba6f54f9846af78b7ba4031d35fb
|
7
|
+
data.tar.gz: 2ec4137407dfaf6b4189255532e59669798ac9ed57be8e6ecdb07c31525d132b9246da9d9fd6b98975e31b712e002e2693f523acf285d172df1b13c7c46617d5
|
data/README.md
CHANGED
@@ -3,9 +3,7 @@
|
|
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
|
|
@@ -14,7 +12,7 @@ Require Ruby 2.6 or later
|
|
14
12
|
Add below code into your Gemfile
|
15
13
|
|
16
14
|
```ruby
|
17
|
-
gem 'eqq', '0.0.
|
15
|
+
gem 'eqq', '>= 0.0.7', '< 0.1.0'
|
18
16
|
```
|
19
17
|
|
20
18
|
### Overview
|
@@ -26,13 +24,133 @@ require 'eqq'
|
|
26
24
|
[42, nil, true, false, '', 0].grep(Eqq.BOOLEAN) #=> [true, false]
|
27
25
|
[42, [], {}, 'string', Object.new, nil].grep(Eqq.CAN(:to_h)) #=> [[], {}, nil]
|
28
26
|
|
29
|
-
pattern = Eqq.
|
27
|
+
pattern = Eqq.build 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
|
+
* NIL() - Product returns `true` when matched to `nil` (Not consider `nil?`)
|
111
|
+
* ANYTHING() - Product returns `true`, always `true`
|
112
|
+
* NEVER() - Product returns `false`, always `false`
|
113
|
+
* XOR(pattern1, pattern2) - Product returns `true` when matched one of the pattern, when matched both returns `false`
|
114
|
+
* NAND(*patterns) - Product is an inverted `AND`
|
115
|
+
* NOR(*patterns) - Product is an inverted `OR`
|
116
|
+
|
117
|
+
### Best fit for RSpec's `satisfy` matcher too
|
118
|
+
|
119
|
+
All builders actually generate a `Proc (lambda)` instance.
|
120
|
+
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.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
RSpec.describe RSpec::Matchers::BuiltIn::Satisfy do
|
124
|
+
let(:product) { Eqq.AND(Integer, 24..42) }
|
125
|
+
|
126
|
+
it 'perfectly works' do
|
127
|
+
expect(23).not_to satisfy(&product)
|
128
|
+
expect(24).to satisfy(&product)
|
129
|
+
expect(24.0).not_to satisfy(&product)
|
130
|
+
expect(42).to satisfy(&product)
|
131
|
+
expect(42.0).not_to satisfy(&product)
|
132
|
+
expect(43).not_to satisfy(&product)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
### Use builders without receiver specifying
|
138
|
+
|
139
|
+
When you felt annoy to write `Eqq` in many place, some ways exist.
|
140
|
+
|
141
|
+
* `Eqq.build(&block)` - In the block scope, all builder methods can be used without receiver
|
142
|
+
* `extend Eqq::Buildable` - In the class/module, all builders can be used as class methods
|
143
|
+
* `include Eqq::Buildable` - In the class/module, all builders can be used as instance methods
|
144
|
+
|
145
|
+
### Signature
|
146
|
+
|
147
|
+
* This gem provides [ruby/rbs](https://github.com/ruby/rbs) signature file
|
148
|
+
|
35
149
|
## Links
|
36
150
|
|
37
151
|
* [Repository](https://github.com/kachick/eqq)
|
38
152
|
* [API documents](https://kachick.github.io/eqq)
|
153
|
+
|
154
|
+
## NOTE
|
155
|
+
|
156
|
+
* [`eqq` is the implementation name of `#===` in CRuby](https://github.com/ruby/ruby/blob/2a685da1fcd928530509e99f5edb4117bc377994/range.c#L1859)
|
data/lib/eqq.rb
CHANGED
@@ -4,21 +4,18 @@
|
|
4
4
|
# Copyright (c) 2011 Kenichi Kamiya
|
5
5
|
# Forked from https://github.com/kachick/validation at 2021
|
6
6
|
|
7
|
-
require_relative 'eqq/buildable'
|
8
7
|
require_relative 'eqq/version'
|
9
8
|
|
9
|
+
# Pattern objects builder
|
10
10
|
module Eqq
|
11
|
-
|
12
|
-
|
11
|
+
# Base error of this library
|
13
12
|
class Error < StandardError; end
|
14
|
-
class InvalidProductError < Error; end
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
14
|
+
# Raised when found some products are invalid as a pattern object
|
15
|
+
class InvalidProductError < Error; end
|
19
16
|
|
20
17
|
class << self
|
21
|
-
def
|
18
|
+
def pattern?(object)
|
22
19
|
case object
|
23
20
|
when Proc, Method
|
24
21
|
object.arity == 1
|
@@ -31,11 +28,41 @@ module Eqq
|
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
34
|
-
|
31
|
+
# @deprecated Use {pattern?} instead. This will be dropped since `0.1.0`
|
32
|
+
def valid?(object)
|
33
|
+
pattern?(object)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def satisfy?(object)
|
38
|
+
(Proc === object) && object.lambda? && (object.arity == 1) && object.respond_to?(:inspect)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Proc]
|
42
|
+
# @raise [InvalidProductError] if the return value is not looks to be built with builders
|
43
|
+
def build(&block)
|
44
|
+
raise ArgumentError, 'might be mis used the `Eqq.build` in your code' unless block
|
45
|
+
|
35
46
|
pattern = DSLScope.new.instance_exec(&block)
|
36
|
-
raise InvalidProductError unless
|
47
|
+
raise InvalidProductError, 'might be mis used the `Eqq.build` in your code' unless satisfy?(pattern)
|
37
48
|
|
38
49
|
pattern
|
39
50
|
end
|
51
|
+
|
52
|
+
# @deprecated Use {build} instead. This will be dropped since `0.1.0`
|
53
|
+
def define(&block)
|
54
|
+
build(&block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
require_relative 'eqq/buildable'
|
60
|
+
|
61
|
+
module Eqq
|
62
|
+
extend Buildable
|
63
|
+
|
64
|
+
class DSLScope
|
65
|
+
include Buildable
|
40
66
|
end
|
67
|
+
private_constant(:DSLScope)
|
41
68
|
end
|
data/lib/eqq/buildable.rb
CHANGED
@@ -2,20 +2,69 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module Eqq
|
5
|
+
# Actually having definitions for the pattern builders
|
5
6
|
module Buildable
|
6
|
-
|
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'
|
7
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
|
+
#
|
8
49
|
# @param pattern1 [Proc, Method, #===]
|
9
50
|
# @param pattern2 [Proc, Method, #===]
|
10
51
|
# @param patterns [Array<Proc, Method, #===>]
|
11
52
|
# @return [Proc]
|
12
|
-
# this lambda return true if match all patterns
|
13
53
|
def AND(pattern1, pattern2, *patterns)
|
14
|
-
|
15
|
-
|
54
|
+
patterns = [pattern1, pattern2, *patterns].freeze
|
55
|
+
Buildable.validate_patterns(*patterns)
|
56
|
+
|
57
|
+
product = ->v {
|
58
|
+
patterns.all? { |pattern| pattern === v }
|
16
59
|
}
|
60
|
+
|
61
|
+
Buildable.define_inspect_on(product, name: 'AND', arguments: patterns)
|
62
|
+
|
63
|
+
product
|
17
64
|
end
|
18
65
|
|
66
|
+
# Product is an inverted {#AND}
|
67
|
+
#
|
19
68
|
# @param pattern1 [Proc, Method, #===]
|
20
69
|
# @param pattern2 [Proc, Method, #===]
|
21
70
|
# @param patterns [Array<Proc, Method, #===>]
|
@@ -24,17 +73,26 @@ module Eqq
|
|
24
73
|
NOT(AND(pattern1, pattern2, *patterns))
|
25
74
|
end
|
26
75
|
|
76
|
+
# Product returns `true` when matched even one pattern
|
77
|
+
#
|
27
78
|
# @param pattern1 [Proc, Method, #===]
|
28
79
|
# @param pattern2 [Proc, Method, #===]
|
29
80
|
# @param patterns [Array<Proc, Method, #===>]
|
30
81
|
# @return [Proc]
|
31
|
-
# this lambda return true if match a any pattern
|
32
82
|
def OR(pattern1, pattern2, *patterns)
|
33
|
-
|
34
|
-
|
83
|
+
patterns = [pattern1, pattern2, *patterns].freeze
|
84
|
+
Buildable.validate_patterns(*patterns)
|
85
|
+
|
86
|
+
product = ->v {
|
87
|
+
patterns.any? { |pattern| pattern === v }
|
35
88
|
}
|
89
|
+
Buildable.define_inspect_on(product, name: 'OR', arguments: patterns)
|
90
|
+
|
91
|
+
product
|
36
92
|
end
|
37
93
|
|
94
|
+
# Product is an inverted {#OR}
|
95
|
+
#
|
38
96
|
# @param pattern1 [Proc, Method, #===]
|
39
97
|
# @param pattern2 [Proc, Method, #===]
|
40
98
|
# @param patterns [Array<Proc, Method, #===>]
|
@@ -43,56 +101,72 @@ module Eqq
|
|
43
101
|
NOT(OR(pattern1, pattern2, *patterns))
|
44
102
|
end
|
45
103
|
|
104
|
+
# Product returns `true` when matched one of the pattern, when matched both returns `false`
|
105
|
+
#
|
46
106
|
# @param pattern1 [Proc, Method, #===]
|
47
107
|
# @param pattern2 [Proc, Method, #===]
|
48
|
-
# @param patterns [Array<Proc, Method, #===>]
|
49
108
|
# @return [Proc]
|
50
|
-
def XOR(pattern1, pattern2
|
51
|
-
|
52
|
-
|
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 }
|
53
115
|
}
|
54
|
-
|
116
|
+
Buildable.define_inspect_on(product, name: 'XOR', arguments: patterns)
|
55
117
|
|
56
|
-
|
57
|
-
# @param pattern2 [Proc, Method, #===]
|
58
|
-
# @param patterns [Array<Proc, Method, #===>]
|
59
|
-
# @return [Proc]
|
60
|
-
def XNOR(pattern1, pattern2, *patterns)
|
61
|
-
NOT(XOR(pattern1, pattern2, *patterns))
|
118
|
+
product
|
62
119
|
end
|
63
120
|
|
121
|
+
# Product returns `true` when not matched the pattern
|
122
|
+
#
|
64
123
|
# @param pattern [Proc, Method, #===]
|
65
124
|
# @return [Proc]
|
66
125
|
def NOT(pattern)
|
67
|
-
|
126
|
+
Buildable.validate_patterns(pattern)
|
68
127
|
|
69
|
-
->v { !(pattern === v) }
|
128
|
+
product = ->v { !(pattern === v) }
|
129
|
+
|
130
|
+
Buildable.define_inspect_on(product, name: 'NOT', arguments: [pattern])
|
131
|
+
|
132
|
+
product
|
70
133
|
end
|
71
134
|
|
72
|
-
#
|
135
|
+
# Product returns `true` when matched with `#==`
|
136
|
+
#
|
73
137
|
# @param obj [#==]
|
74
138
|
# @return [Proc]
|
75
139
|
def EQ(obj)
|
76
|
-
->v { obj == v }
|
140
|
+
product = ->v { obj == v }
|
141
|
+
Buildable.define_inspect_on(product, name: 'EQ', arguments: [obj])
|
142
|
+
product
|
77
143
|
end
|
78
144
|
|
145
|
+
# Product returns `true` when matched with `#equal?`
|
146
|
+
#
|
79
147
|
# @param obj [#equal?]
|
80
148
|
# @return [Proc]
|
81
149
|
def SAME(obj)
|
82
|
-
->v { obj.equal?(v) }
|
150
|
+
product = ->v { obj.equal?(v) }
|
151
|
+
Buildable.define_inspect_on(product, name: 'SAME', arguments: [obj])
|
152
|
+
product
|
83
153
|
end
|
84
154
|
|
85
|
-
#
|
86
|
-
#
|
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>]
|
87
159
|
# @return [Proc]
|
88
160
|
def CAN(message1, *messages)
|
89
|
-
messages =
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
161
|
+
messages = (
|
162
|
+
begin
|
163
|
+
[message1, *messages].map(&:to_sym).freeze
|
164
|
+
rescue NoMethodError
|
165
|
+
raise ArgumentError
|
166
|
+
end
|
167
|
+
)
|
94
168
|
|
95
|
-
->v {
|
169
|
+
product = ->v {
|
96
170
|
messages.all? { |message|
|
97
171
|
begin
|
98
172
|
v.respond_to?(message)
|
@@ -101,18 +175,22 @@ module Eqq
|
|
101
175
|
end
|
102
176
|
}
|
103
177
|
}
|
178
|
+
|
179
|
+
Buildable.define_inspect_on(product, name: 'CAN', arguments: messages)
|
180
|
+
|
181
|
+
product
|
104
182
|
end
|
105
183
|
|
184
|
+
# Product returns `true` when all patterns did not raise any exception
|
185
|
+
#
|
106
186
|
# @param pattern1 [Proc, Method, #===]
|
107
187
|
# @param patterns [Array<Proc, Method, #===>]
|
108
188
|
# @return [Proc]
|
109
189
|
def QUIET(pattern1, *patterns)
|
110
|
-
patterns = [pattern1, *patterns]
|
111
|
-
|
112
|
-
raise ArgumentError, 'wrong object for pattern'
|
113
|
-
end
|
190
|
+
patterns = [pattern1, *patterns].freeze
|
191
|
+
Buildable.validate_patterns(*patterns)
|
114
192
|
|
115
|
-
->v {
|
193
|
+
product = ->v {
|
116
194
|
patterns.all? { |pattern|
|
117
195
|
begin
|
118
196
|
pattern === v
|
@@ -123,16 +201,22 @@ module Eqq
|
|
123
201
|
end
|
124
202
|
}
|
125
203
|
}
|
204
|
+
|
205
|
+
Buildable.define_inspect_on(product, name: 'QUIET', arguments: patterns)
|
206
|
+
|
207
|
+
product
|
126
208
|
end
|
127
209
|
|
210
|
+
# Product returns `true` when the pattern raises the exception
|
211
|
+
#
|
128
212
|
# @param mod [Module]
|
129
213
|
# @param pattern [Proc, Method, #===]
|
130
214
|
# @return [Proc]
|
131
215
|
def RESCUE(mod, pattern)
|
132
|
-
|
216
|
+
Buildable.validate_patterns(pattern)
|
133
217
|
raise ArgumentError unless Module === mod
|
134
218
|
|
135
|
-
->v {
|
219
|
+
product = ->v {
|
136
220
|
begin
|
137
221
|
pattern === v
|
138
222
|
false
|
@@ -142,31 +226,76 @@ module Eqq
|
|
142
226
|
false
|
143
227
|
end
|
144
228
|
}
|
229
|
+
|
230
|
+
Buildable.define_inspect_on(product, name: 'RESCUE', arguments: [mod, pattern])
|
231
|
+
|
232
|
+
product
|
145
233
|
end
|
146
234
|
|
147
|
-
#
|
235
|
+
# Basically provided for Enumerable
|
236
|
+
#
|
237
|
+
# @param name [Symbol, String, #to_sym]
|
148
238
|
# @param pattern [Proc, Method, #===]
|
149
239
|
# @return [Proc]
|
150
240
|
def SEND(name, pattern)
|
151
|
-
|
241
|
+
name = (
|
242
|
+
begin
|
243
|
+
name.to_sym
|
244
|
+
rescue NoMethodError
|
245
|
+
raise ArgumentError
|
246
|
+
end
|
247
|
+
)
|
248
|
+
Buildable.validate_patterns(pattern)
|
152
249
|
|
153
|
-
->v {
|
250
|
+
product = ->v {
|
154
251
|
v.__send__(name, pattern)
|
155
252
|
}
|
253
|
+
|
254
|
+
Buildable.define_inspect_on(product, name: 'SEND', arguments: [name, pattern])
|
255
|
+
|
256
|
+
product
|
156
257
|
end
|
157
258
|
|
158
|
-
|
259
|
+
EQQ_BUILTIN_ANYTHING = ->_v { true }
|
260
|
+
define_inspect_on(EQQ_BUILTIN_ANYTHING, name: 'ANYTHING', arguments: [])
|
261
|
+
|
262
|
+
# Product returns `true`, always `true`
|
263
|
+
#
|
264
|
+
# @return [Proc]
|
159
265
|
def ANYTHING
|
160
|
-
|
161
|
-
|
266
|
+
EQQ_BUILTIN_ANYTHING
|
267
|
+
end
|
268
|
+
|
269
|
+
EQQ_BUILTIN_NEVER = ->_v { false }
|
270
|
+
define_inspect_on(EQQ_BUILTIN_NEVER, name: 'NEVER', arguments: [])
|
271
|
+
|
272
|
+
# Product returns `false`, always `false`
|
273
|
+
#
|
274
|
+
# @return [Proc]
|
275
|
+
def NEVER
|
276
|
+
EQQ_BUILTIN_NEVER
|
162
277
|
end
|
163
278
|
|
164
|
-
|
165
|
-
|
279
|
+
EQQ_BUILTIN_BOOLEAN = ->v { true.equal?(v) || false.equal?(v) }
|
280
|
+
define_inspect_on(EQQ_BUILTIN_BOOLEAN, name: 'BOOLEAN', arguments: [])
|
166
281
|
|
167
|
-
#
|
282
|
+
# Product returns `true` when matched to `true` or `false`
|
283
|
+
#
|
284
|
+
# @return [Proc]
|
168
285
|
def BOOLEAN
|
169
|
-
|
286
|
+
EQQ_BUILTIN_BOOLEAN
|
287
|
+
end
|
288
|
+
|
289
|
+
EQQ_BUILTIN_NIL = ->v { nil.equal?(v) }
|
290
|
+
define_inspect_on(EQQ_BUILTIN_NIL, name: 'NIL', arguments: [])
|
291
|
+
|
292
|
+
# Product returns `true` when matched to `nil` (Not consider `nil?`)
|
293
|
+
#
|
294
|
+
# @return [Proc]
|
295
|
+
def NIL
|
296
|
+
EQQ_BUILTIN_NIL
|
170
297
|
end
|
298
|
+
|
299
|
+
private_constant(:EQQ_BUILTIN_ANYTHING, :EQQ_BUILTIN_NEVER, :EQQ_BUILTIN_BOOLEAN, :EQQ_BUILTIN_NIL)
|
171
300
|
end
|
172
301
|
end
|
data/lib/eqq/version.rb
CHANGED
data/sig/eqq.rbs
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# Pattern objects builder
|
1
2
|
module Eqq
|
2
3
|
interface _Patternable
|
3
4
|
def ===: (untyped object) -> bool
|
@@ -7,43 +8,114 @@ module Eqq
|
|
7
8
|
def to_sym: -> Symbol
|
8
9
|
end
|
9
10
|
|
11
|
+
interface _Inspectable
|
12
|
+
def inspect: () -> String
|
13
|
+
end
|
14
|
+
|
10
15
|
module Buildable
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def
|
28
|
-
|
29
|
-
|
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
|
+
EQQ_BUILTIN_ANYTHING: product
|
21
|
+
|
22
|
+
# A private constant. Should not be used in your code.
|
23
|
+
EQQ_BUILTIN_NEVER: product
|
24
|
+
|
25
|
+
# A private constant. Should not be used in your code.
|
26
|
+
EQQ_BUILTIN_BOOLEAN: product
|
27
|
+
|
28
|
+
# A private constant. Should not be used in your code.
|
29
|
+
EQQ_BUILTIN_NIL: product
|
30
|
+
|
31
|
+
# A private API. Should not be used in your code.
|
32
|
+
def self.safe_inspect_for: (untyped object)-> String
|
33
|
+
|
34
|
+
# A private API. Should not be used in your code.
|
35
|
+
def self.define_inspect_on: (patternable_lambda product, name: String, arguments: Array[untyped])-> void
|
36
|
+
|
37
|
+
# A private API. Should not be used in your code.
|
38
|
+
def self.validate_patterns: (*untyped) -> void
|
39
|
+
|
40
|
+
# Product returns `true` when matched even one pattern
|
41
|
+
def OR: (_Patternable, _Patternable, *_Patternable) -> product
|
42
|
+
|
43
|
+
# Product returns `true` when matched all patterns
|
44
|
+
def AND: (_Patternable, _Patternable, *_Patternable) -> product
|
45
|
+
|
46
|
+
# Product is an inverted `AND`
|
47
|
+
def NAND: (_Patternable, _Patternable, *_Patternable) -> product
|
48
|
+
|
49
|
+
# Product is an inverted `OR`
|
50
|
+
def NOR: (_Patternable, _Patternable, *_Patternable) -> product
|
51
|
+
|
52
|
+
# Product returns `true` when matched one of the pattern, when matched both returns `false`
|
53
|
+
def XOR: (_Patternable, _Patternable) -> product
|
54
|
+
|
55
|
+
# Product returns `true` when not matched the pattern
|
56
|
+
def NOT: (_Patternable) -> product
|
57
|
+
|
58
|
+
# Product returns `true` when matched with `#==`
|
59
|
+
def EQ: (untyped object) -> product
|
60
|
+
|
61
|
+
# Product returns `true` when matched with `#equal?`
|
62
|
+
def SAME: (untyped object) -> product
|
63
|
+
|
64
|
+
# Product returns `true` when it has all of the methods (checked with `respond_to?`)
|
65
|
+
def CAN: (_ToSym, *_ToSym) -> product
|
66
|
+
|
67
|
+
# Product returns `true` when the pattern raises the exception
|
68
|
+
def RESCUE: (Module, _Patternable) -> product
|
69
|
+
|
70
|
+
# Product returns `true` when all patterns did not raise any exception
|
71
|
+
def QUIET: (_Patternable, *_Patternable) -> product
|
72
|
+
|
73
|
+
# Basically provided for Enumerable
|
74
|
+
def SEND: (Symbol | String name, _Patternable) -> product
|
75
|
+
|
76
|
+
# Product returns `true`, always `true`
|
77
|
+
def ANYTHING: () -> product
|
78
|
+
|
79
|
+
# Product returns `false`, always `false`
|
80
|
+
def NEVER: () -> product
|
81
|
+
|
82
|
+
# Product returns `true` when matched to `true` or `false`
|
83
|
+
def BOOLEAN: () -> product
|
84
|
+
|
85
|
+
# Product returns `true` when matched to `nil` (Not consider `nil?`)
|
86
|
+
def NIL: () -> product
|
30
87
|
end
|
31
88
|
|
32
89
|
extend Buildable
|
33
90
|
|
91
|
+
# Base error of this library
|
34
92
|
class Error < StandardError
|
35
93
|
end
|
36
94
|
|
95
|
+
# Raised when found some products are invalid as a pattern object
|
37
96
|
class InvalidProductError < Error
|
38
97
|
end
|
39
98
|
|
40
|
-
# A
|
99
|
+
# A private API. Should not be used in your code.
|
41
100
|
class DSLScope
|
42
101
|
include Buildable
|
43
102
|
end
|
44
103
|
|
45
104
|
VERSION: String
|
46
105
|
|
106
|
+
# Returns `true` when given object has patternable signature
|
107
|
+
def self.pattern?: (untyped object) -> bool
|
108
|
+
|
109
|
+
# Alias of `pattern?`. But deperecated. Do not use in your code anymore.
|
47
110
|
def self.valid?: (untyped object) -> bool
|
111
|
+
|
112
|
+
# Returns `true` when given object has correct signature as a product of builders
|
113
|
+
# Basically this is a private API. Should not be used in your code.
|
114
|
+
def self.satisfy?: (untyped object) -> bool
|
115
|
+
|
116
|
+
# In the block scope, all builder methods can be used without receiver
|
117
|
+
def self.build: { () -> _Patternable } -> _Patternable
|
118
|
+
|
119
|
+
# Alias of `build`. But deperecated. Do not use in your code anymore.
|
48
120
|
def self.define: { () -> _Patternable } -> _Patternable
|
49
121
|
end
|
metadata
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eqq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
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-
|
11
|
+
date: 2021-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
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!
|
14
21
|
email:
|
15
22
|
- kachick1+ruby@gmail.com
|
16
23
|
executables: []
|
@@ -49,5 +56,5 @@ requirements: []
|
|
49
56
|
rubygems_version: 3.2.15
|
50
57
|
signing_key:
|
51
58
|
specification_version: 4
|
52
|
-
summary: Pattern objects builder
|
59
|
+
summary: Pattern objects builder
|
53
60
|
test_files: []
|