eqq 0.0.2 → 0.0.3

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: fd093a7aa8c034327d463c56bfe120a8ecc48b52649ba7900491f05cc130cc38
4
- data.tar.gz: 13f7ca2f2f40d9be41abd4408f5486b892f345347eb07eeb3f4d936c47cc92ab
3
+ metadata.gz: 22307661e329771fea7efcd7c282d17eda0130d0edb7aec3088257b21b73e4ba
4
+ data.tar.gz: ebc3138ade59e38f9bc34a42a93654d3a6ab1564a48b47df13d65ed7227c7488
5
5
  SHA512:
6
- metadata.gz: 2e510f5313e0f890bf3c8277a413f937b0a26e2ba58e501591288556783bc5fffc944c23a1216ddf95308a111b06c6ac82557526d9f7de49ef9dbe7806f93bb6
7
- data.tar.gz: 719905cbd2f71a388e13ca0427b7db49115513ce11395a436d8ef9fc43397c9c77cc408135862147bbfc3bac3fb36615332752f56ab28e4136bc74b8db3f97dd
6
+ metadata.gz: 16ae2b50b3c81291028946ec6ddb8753c06bc7fc472f29ab00b3f69b9136540f26079b8c23f983d0bf1482f93543744f2fae557bb0ef71f6fdcc73f115df9455
7
+ data.tar.gz: eced8529614ce3b1b8a4cc4087d58943bdbaabd0637a86bb10bcafe472ccdece82bae71d390fde84e95176318961e4ef74207ab4debc6e117411608f2e074176
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.2'
15
+ gem 'eqq', '0.0.3'
18
16
  ```
19
17
 
20
18
  ### Overview
@@ -29,10 +27,94 @@ 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! :)
33
66
  ```
34
67
 
68
+ ### Explanation
69
+
70
+ All products can be called as `pattern === other`.
71
+
72
+ This signature will fit in most Ruby code.
73
+
74
+ * `case ~ when` syntax
75
+ * Enumerable#grep
76
+ * Enumerable#grep_v
77
+ * Enumerable#all?
78
+ * Enumerable#any?
79
+ * Enumerable#none?
80
+ * Enumerable#one?
81
+ * Enumerable#slice_after
82
+ * Enumerable#slice_before
83
+
84
+ They can take this interface as the `pattern`.
85
+
86
+ And you already saw. All of patterns can be mixed with other patterns as a parts.
87
+ Reuse as you wish!
88
+
89
+ Major builders as below
90
+
91
+ * OR(*patterns) - Product returns true when matched even one pattern
92
+ * AND(*patterns) - Product returns true when matched all patterns
93
+ * NOT(pattern) - Product returns true when not matched the pattern
94
+ * CAN(*method_names) - Product returns true when it has all of the methods (checked with `respond_to?`)
95
+ * RESCUE(exception_class/module, pattern) - Product returns true when the pattern raises the exception
96
+ * QUIET(*patterns) - Product returns true when all patterns did not raise any exception
97
+ * EQ(object) - Product returns true when matched with `#==`
98
+ * SAME(object) - Product returns true when matched with `#equal?`
99
+ * SEND(name, pattern) - Basically provided for Enumerable
100
+ * BOOLEAN() - Product returns true when matched to true or false
101
+ * ANYTHING() - Product returns true, always true
102
+
103
+ Minor builders as below, please see [API documents](https://kachick.github.io/eqq) for them.
104
+
105
+ * NAND
106
+ * NOR
107
+ * XOR
108
+
109
+ When you feel annoy to write `Eqq` in many place, please use `Eqq.define`.
110
+ In the block scope, all builder methods can be used without receiver specifying.
111
+
112
+ This gem provide [ruby/rbs](https://github.com/ruby/rbs) signature
113
+
35
114
  ## Links
36
115
 
37
116
  * [Repository](https://github.com/kachick/eqq)
38
- * [API documents](https://kachick.github.io/eqq)
117
+
118
+ ## NOTE
119
+
120
+ * [`eqq` is the implementation name of `#===` in CRuby](https://github.com/ruby/ruby/blob/2a685da1fcd928530509e99f5edb4117bc377994/range.c#L1859)
data/lib/eqq.rb CHANGED
@@ -4,19 +4,12 @@
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
 
10
9
  module Eqq
11
- extend Buildable
12
-
13
10
  class Error < StandardError; end
14
11
  class InvalidProductError < Error; end
15
12
 
16
- class DSLScope
17
- include Buildable
18
- end
19
-
20
13
  class << self
21
14
  def valid?(object)
22
15
  case object
@@ -31,6 +24,7 @@ module Eqq
31
24
  end
32
25
  end
33
26
 
27
+ # @return [#===]
34
28
  def define(&block)
35
29
  pattern = DSLScope.new.instance_exec(&block)
36
30
  raise InvalidProductError unless valid?(pattern)
@@ -39,3 +33,13 @@ module Eqq
39
33
  end
40
34
  end
41
35
  end
36
+
37
+ require_relative 'eqq/buildable'
38
+
39
+ module Eqq
40
+ extend Buildable
41
+
42
+ class DSLScope
43
+ include Buildable
44
+ end
45
+ end
data/lib/eqq/buildable.rb CHANGED
@@ -5,15 +5,59 @@ module Eqq
5
5
  module Buildable
6
6
  extend self
7
7
 
8
+ class << self
9
+ INSPECTION_FALLBACK = 'UninspectableObject'
10
+
11
+ # @api private
12
+ # @return [String]
13
+ def safe_inspect(object)
14
+ String.try_convert(object.inspect) || INSPECTION_FALLBACK
15
+ rescue Exception
16
+ # This implementation used `RSpec::Support::ObjectFormatter::UninspectableObjectInspector` as a reference, thank you!
17
+ # ref: https://github.com/kachick/times_kachick/issues/97
18
+ singleton_class = class << object; self; end
19
+ begin
20
+ klass = singleton_class.ancestors.detect { |ancestor| !ancestor.equal?(singleton_class) }
21
+ native_object_id = '%#016x' % (object.__id__ << 1)
22
+ "#<#{klass}:#{native_object_id}>"
23
+ rescue Exception
24
+ INSPECTION_FALLBACK
25
+ end
26
+ end
27
+
28
+ # @api private
29
+ # @return [void]
30
+ def set_inspect(name:, product:, arguments:)
31
+ inspect = "#{name}(#{arguments.map { |argument| safe_inspect(argument) }.join(', ')})".freeze
32
+ product.define_singleton_method(:inspect) do
33
+ inspect
34
+ end
35
+ end
36
+
37
+ # @api private
38
+ # @return [void]
39
+ def validate_patterns(*patterns)
40
+ invalids = patterns.reject { |pattern| Eqq.valid?(pattern) }
41
+ invalid_inspections = invalids.map { |invalid| safe_inspect(invalid) }.join(', ')
42
+ raise ArgumentError, "given `#{invalid_inspections}` are invalid as pattern objects" unless invalids.empty?
43
+ end
44
+ end
45
+
8
46
  # @param pattern1 [Proc, Method, #===]
9
47
  # @param pattern2 [Proc, Method, #===]
10
48
  # @param patterns [Array<Proc, Method, #===>]
11
49
  # @return [Proc]
12
- # this lambda return true if match all patterns
13
50
  def AND(pattern1, pattern2, *patterns)
14
- ->v {
15
- [pattern1, pattern2, *patterns].all? { |pattern| pattern === v }
51
+ patterns = [pattern1, pattern2, *patterns].freeze
52
+ Buildable.validate_patterns(*patterns)
53
+
54
+ product = ->v {
55
+ patterns.all? { |pattern| pattern === v }
16
56
  }
57
+
58
+ Buildable.set_inspect(name: 'AND', product: product, arguments: patterns)
59
+
60
+ product
17
61
  end
18
62
 
19
63
  # @param pattern1 [Proc, Method, #===]
@@ -28,11 +72,16 @@ module Eqq
28
72
  # @param pattern2 [Proc, Method, #===]
29
73
  # @param patterns [Array<Proc, Method, #===>]
30
74
  # @return [Proc]
31
- # this lambda return true if match a any pattern
32
75
  def OR(pattern1, pattern2, *patterns)
33
- ->v {
34
- [pattern1, pattern2, *patterns].any? { |pattern| pattern === v }
76
+ patterns = [pattern1, pattern2, *patterns].freeze
77
+ Buildable.validate_patterns(*patterns)
78
+
79
+ product = ->v {
80
+ patterns.any? { |pattern| pattern === v }
35
81
  }
82
+ Buildable.set_inspect(name: 'OR', product: product, arguments: patterns)
83
+
84
+ product
36
85
  end
37
86
 
38
87
  # @param pattern1 [Proc, Method, #===]
@@ -45,54 +94,60 @@ module Eqq
45
94
 
46
95
  # @param pattern1 [Proc, Method, #===]
47
96
  # @param pattern2 [Proc, Method, #===]
48
- # @param patterns [Array<Proc, Method, #===>]
49
97
  # @return [Proc]
50
- def XOR(pattern1, pattern2, *patterns)
51
- ->v {
52
- [pattern1, pattern2, *patterns].one? { |pattern| pattern === v }
98
+ def XOR(pattern1, pattern2)
99
+ patterns = [pattern1, pattern2].freeze
100
+ Buildable.validate_patterns(*patterns)
101
+
102
+ product = ->v {
103
+ patterns.one? { |pattern| pattern === v }
53
104
  }
54
- end
105
+ Buildable.set_inspect(name: 'XOR', product: product, arguments: patterns)
55
106
 
56
- # @param pattern1 [Proc, Method, #===]
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))
107
+ product
62
108
  end
63
109
 
64
110
  # @param pattern [Proc, Method, #===]
65
111
  # @return [Proc]
66
112
  def NOT(pattern)
67
- raise ArgumentError, 'wrong object for pattern' unless Eqq.valid?(pattern)
113
+ Buildable.validate_patterns(pattern)
114
+
115
+ product = ->v { !(pattern === v) }
68
116
 
69
- ->v { !(pattern === v) }
117
+ Buildable.set_inspect(name: 'NOT', product: product, arguments: [pattern])
118
+
119
+ product
70
120
  end
71
121
 
72
- # A pattern builder.
73
122
  # @param obj [#==]
74
123
  # @return [Proc]
75
124
  def EQ(obj)
76
- ->v { obj == v }
125
+ ->v { obj == v }.tap do |product|
126
+ Buildable.set_inspect(name: 'EQ', product: product, arguments: [obj])
127
+ end
77
128
  end
78
129
 
79
130
  # @param obj [#equal?]
80
131
  # @return [Proc]
81
132
  def SAME(obj)
82
- ->v { obj.equal?(v) }
133
+ ->v { obj.equal?(v) }.tap do |product|
134
+ Buildable.set_inspect(name: 'SAME', product: product, arguments: [obj])
135
+ end
83
136
  end
84
137
 
85
- # @param message1 [Symbol, String]
86
- # @param messages [Array<Symbol, String>]
138
+ # @param message1 [Symbol, String, #to_sym]
139
+ # @param messages [Array<Symbol, String, #to_sym>]
87
140
  # @return [Proc]
88
141
  def CAN(message1, *messages)
89
- messages = begin
90
- [message1, *messages].map(&:to_sym)
91
- rescue NoMethodError
92
- raise ArgumentError
93
- end
142
+ messages = (
143
+ begin
144
+ [message1, *messages].map(&:to_sym).freeze
145
+ rescue NoMethodError
146
+ raise ArgumentError
147
+ end
148
+ )
94
149
 
95
- ->v {
150
+ product = ->v {
96
151
  messages.all? { |message|
97
152
  begin
98
153
  v.respond_to?(message)
@@ -101,18 +156,20 @@ module Eqq
101
156
  end
102
157
  }
103
158
  }
159
+
160
+ Buildable.set_inspect(name: 'CAN', product: product, arguments: messages)
161
+
162
+ product
104
163
  end
105
164
 
106
165
  # @param pattern1 [Proc, Method, #===]
107
166
  # @param patterns [Array<Proc, Method, #===>]
108
167
  # @return [Proc]
109
168
  def QUIET(pattern1, *patterns)
110
- patterns = [pattern1, *patterns]
111
- unless patterns.all? { |pattern| Eqq.valid?(pattern) }
112
- raise ArgumentError, 'wrong object for pattern'
113
- end
169
+ patterns = [pattern1, *patterns].freeze
170
+ Buildable.validate_patterns(*patterns)
114
171
 
115
- ->v {
172
+ product = ->v {
116
173
  patterns.all? { |pattern|
117
174
  begin
118
175
  pattern === v
@@ -123,16 +180,20 @@ module Eqq
123
180
  end
124
181
  }
125
182
  }
183
+
184
+ Buildable.set_inspect(name: 'QUIET', product: product, arguments: patterns)
185
+
186
+ product
126
187
  end
127
188
 
128
189
  # @param mod [Module]
129
190
  # @param pattern [Proc, Method, #===]
130
191
  # @return [Proc]
131
192
  def RESCUE(mod, pattern)
132
- raise ArgumentError unless Eqq.valid?(pattern)
193
+ Buildable.validate_patterns(pattern)
133
194
  raise ArgumentError unless Module === mod
134
195
 
135
- ->v {
196
+ product = ->v {
136
197
  begin
137
198
  pattern === v
138
199
  false
@@ -142,23 +203,41 @@ module Eqq
142
203
  false
143
204
  end
144
205
  }
206
+
207
+ Buildable.set_inspect(name: 'RESCUE', product: product, arguments: [mod, pattern])
208
+
209
+ product
145
210
  end
146
211
 
147
- # @param name [Symbol, #to_sym]
212
+ # @param name [Symbol, String, #to_sym]
148
213
  # @param pattern [Proc, Method, #===]
149
214
  # @return [Proc]
150
215
  def SEND(name, pattern)
151
- raise InvalidProductError unless Eqq.valid?(pattern)
216
+ name = (
217
+ begin
218
+ name.to_sym
219
+ rescue NoMethodError
220
+ raise ArgumentError
221
+ end
222
+ )
223
+ Buildable.validate_patterns(pattern)
152
224
 
153
- ->v {
225
+ product = ->v {
154
226
  v.__send__(name, pattern)
155
227
  }
228
+
229
+ Buildable.set_inspect(name: 'SEND', product: product, arguments: [name, pattern])
230
+
231
+ product
156
232
  end
157
233
 
158
- # @return [BasicObject]
234
+ ANYTHING = ->_v { true }
235
+ Buildable.set_inspect(name: 'ANYTHING', product: ANYTHING, arguments: [])
236
+ private_constant :ANYTHING
237
+
238
+ # @return [ANYTHING]
159
239
  def ANYTHING
160
- # BasicObject.=== always passing
161
- BasicObject
240
+ ANYTHING
162
241
  end
163
242
 
164
243
  BOOLEAN = OR(SAME(true), SAME(false))
data/lib/eqq/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Eqq
5
- VERSION = '0.0.2'
5
+ VERSION = '0.0.3'
6
6
  end
data/sig/eqq.rbs CHANGED
@@ -7,26 +7,44 @@ module Eqq
7
7
  def to_sym: -> Symbol
8
8
  end
9
9
 
10
+ interface _Inspectable
11
+ def inspect: () -> String
12
+ end
13
+
10
14
  module Buildable
11
- # A pribate constant. Should not be used in your code.
12
- BOOLEAN: ^(untyped object) -> bool
15
+ type patternable_lambda = ^(untyped object) -> bool
16
+ type product = patternable_lambda & _Inspectable
17
+
18
+ # A private constant. Should not be used in your code.
19
+ ANYTHING: product
20
+
21
+ # A private constant. Should not be used in your code.
22
+ BOOLEAN: product
23
+
24
+ # A private API. Should not be used in your code.
25
+ def self.safe_inspect: (untyped object)-> String
26
+
27
+ # A private API. Should not be used in your code.
28
+ def self.set_inspect: (name: String, product: patternable_lambda, arguments: Array[untyped])-> void
29
+
30
+ # A private API. Should not be used in your code.
31
+ def self.validate_patterns: (*untyped) -> void
13
32
 
14
33
  extend Buildable
15
- def OR: (_Patternable, _Patternable, *_Patternable) -> ^(untyped object) -> bool
16
- def AND: (_Patternable, _Patternable, *_Patternable) -> ^(untyped object) -> bool
17
- def NAND: (_Patternable, _Patternable, *_Patternable) -> ^(untyped object) -> bool
18
- def NOR: (_Patternable, _Patternable, *_Patternable) -> ^(untyped object) -> bool
19
- def XOR: (_Patternable, _Patternable, *_Patternable) -> ^(untyped object) -> bool
20
- def XNOR: (_Patternable, _Patternable, *_Patternable) -> ^(untyped object) -> bool
21
- def NOT: (_Patternable) -> ^(untyped object) -> bool
22
- def EQ: (untyped object) -> ^(untyped object) -> bool
23
- def SAME: (untyped object) -> ^(untyped object) -> bool
24
- def CAN: (_ToSym, *_ToSym) -> ^(untyped object) -> bool
25
- def RESCUE: (Module, _Patternable) -> ^(untyped object) -> bool
26
- def QUIET: (_Patternable, *_Patternable) -> ^(untyped object) -> bool
27
- def SEND: (Symbol | String name, _Patternable) -> ^(untyped object) -> bool
28
- def ANYTHING: () -> BasicObject
29
- def BOOLEAN: () -> ^(untyped object) -> bool
34
+ def OR: (_Patternable, _Patternable, *_Patternable) -> product
35
+ def AND: (_Patternable, _Patternable, *_Patternable) -> product
36
+ def NAND: (_Patternable, _Patternable, *_Patternable) -> product
37
+ def NOR: (_Patternable, _Patternable, *_Patternable) -> product
38
+ def XOR: (_Patternable, _Patternable) -> product
39
+ def NOT: (_Patternable) -> product
40
+ def EQ: (untyped object) -> product
41
+ def SAME: (untyped object) -> product
42
+ def CAN: (_ToSym, *_ToSym) -> product
43
+ def RESCUE: (Module, _Patternable) -> product
44
+ def QUIET: (_Patternable, *_Patternable) -> product
45
+ def SEND: (Symbol | String name, _Patternable) -> product
46
+ def ANYTHING: () -> product
47
+ def BOOLEAN: () -> product
30
48
  end
31
49
 
32
50
  extend Buildable
@@ -37,13 +55,16 @@ module Eqq
37
55
  class InvalidProductError < Error
38
56
  end
39
57
 
40
- # A pribate API. Should not be used in your code.
58
+ # A private API. Should not be used in your code.
41
59
  class DSLScope
42
60
  include Buildable
43
61
  end
44
62
 
45
63
  VERSION: String
46
64
 
65
+ # A private constant. Should not be used in your code.
66
+ INSPECTION_FALLBACK: String
67
+
47
68
  def self.valid?: (untyped object) -> bool
48
69
  def self.define: { () -> _Patternable } -> _Patternable
49
70
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eqq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
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
11
+ date: 2021-06-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: " [4.2, 42, 42.0, 420].grep(Eqq.AND(Integer, 20..50)) #=> [42]\n"
14
14
  email: