dry-logic 0.4.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +214 -53
- data/LICENSE +1 -1
- data/README.md +14 -18
- data/dry-logic.gemspec +28 -15
- data/lib/dry-logic.rb +3 -1
- data/lib/dry/logic.rb +4 -2
- data/lib/dry/logic/appliable.rb +2 -0
- data/lib/dry/logic/evaluator.rb +3 -1
- data/lib/dry/logic/operations.rb +13 -11
- data/lib/dry/logic/operations/abstract.rb +8 -6
- data/lib/dry/logic/operations/and.rb +15 -3
- data/lib/dry/logic/operations/attr.rb +3 -1
- data/lib/dry/logic/operations/binary.rb +3 -1
- data/lib/dry/logic/operations/check.rb +6 -4
- data/lib/dry/logic/operations/each.rb +4 -2
- data/lib/dry/logic/operations/implication.rb +4 -2
- data/lib/dry/logic/operations/key.rb +11 -9
- data/lib/dry/logic/operations/negation.rb +9 -3
- data/lib/dry/logic/operations/or.rb +4 -2
- data/lib/dry/logic/operations/set.rb +5 -3
- data/lib/dry/logic/operations/unary.rb +3 -1
- data/lib/dry/logic/operations/xor.rb +4 -2
- data/lib/dry/logic/operators.rb +6 -4
- data/lib/dry/logic/predicates.rb +72 -28
- data/lib/dry/logic/result.rb +4 -2
- data/lib/dry/logic/rule.rb +35 -30
- data/lib/dry/logic/rule/interface.rb +141 -0
- data/lib/dry/logic/rule/predicate.rb +9 -3
- data/lib/dry/logic/rule_compiler.rb +7 -3
- data/lib/dry/logic/version.rb +3 -1
- metadata +21 -151
- data/.codeclimate.yml +0 -23
- data/.gitignore +0 -8
- data/.rspec +0 -3
- data/.rubocop.yml +0 -16
- data/.rubocop_todo.yml +0 -7
- data/.travis.yml +0 -31
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -18
- data/Rakefile +0 -12
- data/examples/basic.rb +0 -14
- data/spec/integration/result_spec.rb +0 -59
- data/spec/integration/rule_spec.rb +0 -53
- data/spec/shared/predicates.rb +0 -57
- data/spec/shared/rule.rb +0 -67
- data/spec/spec_helper.rb +0 -34
- data/spec/support/mutant.rb +0 -9
- data/spec/unit/operations/and_spec.rb +0 -64
- data/spec/unit/operations/attr_spec.rb +0 -27
- data/spec/unit/operations/check_spec.rb +0 -49
- data/spec/unit/operations/each_spec.rb +0 -47
- data/spec/unit/operations/implication_spec.rb +0 -30
- data/spec/unit/operations/key_spec.rb +0 -119
- data/spec/unit/operations/negation_spec.rb +0 -40
- data/spec/unit/operations/or_spec.rb +0 -73
- data/spec/unit/operations/set_spec.rb +0 -41
- data/spec/unit/operations/xor_spec.rb +0 -61
- data/spec/unit/predicates/array_spec.rb +0 -41
- data/spec/unit/predicates/attr_spec.rb +0 -29
- data/spec/unit/predicates/bool_spec.rb +0 -34
- data/spec/unit/predicates/case_spec.rb +0 -33
- data/spec/unit/predicates/date_spec.rb +0 -31
- data/spec/unit/predicates/date_time_spec.rb +0 -31
- data/spec/unit/predicates/decimal_spec.rb +0 -32
- data/spec/unit/predicates/empty_spec.rb +0 -38
- data/spec/unit/predicates/eql_spec.rb +0 -21
- data/spec/unit/predicates/even_spec.rb +0 -31
- data/spec/unit/predicates/excluded_from_spec.rb +0 -35
- data/spec/unit/predicates/excludes_spec.rb +0 -56
- data/spec/unit/predicates/false_spec.rb +0 -35
- data/spec/unit/predicates/filled_spec.rb +0 -38
- data/spec/unit/predicates/float_spec.rb +0 -31
- data/spec/unit/predicates/format_spec.rb +0 -21
- data/spec/unit/predicates/gt_spec.rb +0 -40
- data/spec/unit/predicates/gteq_spec.rb +0 -40
- data/spec/unit/predicates/included_in_spec.rb +0 -35
- data/spec/unit/predicates/includes_spec.rb +0 -24
- data/spec/unit/predicates/int_spec.rb +0 -34
- data/spec/unit/predicates/key_spec.rb +0 -29
- data/spec/unit/predicates/lt_spec.rb +0 -40
- data/spec/unit/predicates/lteq_spec.rb +0 -40
- data/spec/unit/predicates/max_size_spec.rb +0 -49
- data/spec/unit/predicates/min_size_spec.rb +0 -49
- data/spec/unit/predicates/none_spec.rb +0 -28
- data/spec/unit/predicates/not_eql_spec.rb +0 -21
- data/spec/unit/predicates/number_spec.rb +0 -37
- data/spec/unit/predicates/odd_spec.rb +0 -31
- data/spec/unit/predicates/size_spec.rb +0 -55
- data/spec/unit/predicates/str_spec.rb +0 -32
- data/spec/unit/predicates/time_spec.rb +0 -31
- data/spec/unit/predicates/true_spec.rb +0 -35
- data/spec/unit/predicates/type_spec.rb +0 -35
- data/spec/unit/predicates_spec.rb +0 -23
- data/spec/unit/rule/predicate_spec.rb +0 -53
- data/spec/unit/rule_compiler_spec.rb +0 -127
- data/spec/unit/rule_spec.rb +0 -141
data/lib/dry-logic.rb
CHANGED
data/lib/dry/logic.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# A collection of micro-libraries, each intended to encapsulate
|
2
4
|
# a common task in Ruby
|
3
5
|
module Dry
|
@@ -5,5 +7,5 @@ module Dry
|
|
5
7
|
end
|
6
8
|
end
|
7
9
|
|
8
|
-
require
|
9
|
-
require
|
10
|
+
require "dry/logic/rule/predicate"
|
11
|
+
require "dry/logic/operations"
|
data/lib/dry/logic/appliable.rb
CHANGED
data/lib/dry/logic/evaluator.rb
CHANGED
data/lib/dry/logic/operations.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
|
2
|
-
require 'dry/logic/operations/or'
|
3
|
-
require 'dry/logic/operations/xor'
|
4
|
-
require 'dry/logic/operations/implication'
|
5
|
-
require 'dry/logic/operations/negation'
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
3
|
+
require "dry/logic/operations/and"
|
4
|
+
require "dry/logic/operations/or"
|
5
|
+
require "dry/logic/operations/xor"
|
6
|
+
require "dry/logic/operations/implication"
|
7
|
+
require "dry/logic/operations/negation"
|
12
8
|
|
13
|
-
require
|
9
|
+
require "dry/logic/operations/key"
|
10
|
+
require "dry/logic/operations/attr"
|
11
|
+
require "dry/logic/operations/each"
|
12
|
+
require "dry/logic/operations/set"
|
13
|
+
require "dry/logic/operations/check"
|
14
|
+
|
15
|
+
require "dry/logic/operators"
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/constants"
|
4
|
+
require "dry/core/equalizer"
|
5
|
+
require "dry/logic/operators"
|
4
6
|
|
5
7
|
module Dry
|
6
8
|
module Logic
|
@@ -24,15 +26,15 @@ module Dry
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def curry(*args)
|
27
|
-
new(rules.map { |rule| rule.curry(*args) }, options)
|
29
|
+
new(rules.map { |rule| rule.curry(*args) }, **options)
|
28
30
|
end
|
29
31
|
|
30
32
|
def new(rules, **new_options)
|
31
|
-
self.class.new(*rules, options
|
33
|
+
self.class.new(*rules, **options, **new_options)
|
32
34
|
end
|
33
35
|
|
34
36
|
def with(new_options)
|
35
|
-
new(rules, options
|
37
|
+
new(rules, **options, **new_options)
|
36
38
|
end
|
37
39
|
|
38
40
|
def to_ast
|
@@ -1,10 +1,19 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/operations/binary"
|
4
|
+
require "dry/logic/result"
|
3
5
|
|
4
6
|
module Dry
|
5
7
|
module Logic
|
6
8
|
module Operations
|
7
9
|
class And < Binary
|
10
|
+
attr_reader :hints
|
11
|
+
|
12
|
+
def initialize(*, **)
|
13
|
+
super
|
14
|
+
@hints = options.fetch(:hints, true)
|
15
|
+
end
|
16
|
+
|
8
17
|
def type
|
9
18
|
:and
|
10
19
|
end
|
@@ -22,7 +31,10 @@ module Dry
|
|
22
31
|
Result.new(false, id) { right_result.ast(input) }
|
23
32
|
end
|
24
33
|
else
|
25
|
-
Result.new(false, id)
|
34
|
+
Result.new(false, id) do
|
35
|
+
left_ast = left_result.to_ast
|
36
|
+
hints ? [type, [left_ast, [:hint, right.ast(input)]]] : left_ast
|
37
|
+
end
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/operations/unary"
|
4
|
+
require "dry/logic/evaluator"
|
5
|
+
require "dry/logic/result"
|
4
6
|
|
5
7
|
module Dry
|
6
8
|
module Logic
|
@@ -15,7 +17,7 @@ module Dry
|
|
15
17
|
keys = options.fetch(:keys)
|
16
18
|
evaluator = Evaluator::Set.new(keys)
|
17
19
|
|
18
|
-
super(rule, options
|
20
|
+
super(rule, **options, evaluator: evaluator)
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/operations/unary"
|
4
|
+
require "dry/logic/evaluator"
|
5
|
+
require "dry/logic/result"
|
4
6
|
|
5
7
|
module Dry
|
6
8
|
module Logic
|
@@ -10,13 +12,13 @@ module Dry
|
|
10
12
|
|
11
13
|
attr_reader :path
|
12
14
|
|
13
|
-
def self.new(rules, options)
|
15
|
+
def self.new(rules, **options)
|
14
16
|
if options[:evaluator]
|
15
17
|
super
|
16
18
|
else
|
17
19
|
name = options.fetch(:name)
|
18
20
|
eval = options.fetch(:evaluator, evaluator(name))
|
19
|
-
super(rules, options
|
21
|
+
super(rules, **options, evaluator: eval, path: name)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -49,11 +51,11 @@ module Dry
|
|
49
51
|
rule[evaluator[hash]]
|
50
52
|
end
|
51
53
|
|
52
|
-
def ast(input =
|
53
|
-
if input
|
54
|
-
[type, [path, rule.ast(evaluator[input])]]
|
55
|
-
else
|
54
|
+
def ast(input = Undefined)
|
55
|
+
if input.equal?(Undefined) || !input.is_a?(Hash)
|
56
56
|
[type, [path, rule.ast]]
|
57
|
+
else
|
58
|
+
[type, [path, rule.ast(evaluator[input])]]
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/operations/unary"
|
4
|
+
require "dry/logic/result"
|
3
5
|
|
4
6
|
module Dry
|
5
7
|
module Logic
|
@@ -10,7 +12,11 @@ module Dry
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def call(input)
|
13
|
-
Result.new(
|
15
|
+
Result.new(rule.(input).failure?, id) { ast(input) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](input)
|
19
|
+
!rule[input]
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/operations/abstract"
|
4
|
+
require "dry/logic/result"
|
3
5
|
|
4
6
|
module Dry
|
5
7
|
module Logic
|
@@ -27,7 +29,7 @@ module Dry
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def to_s
|
30
|
-
"#{type}(#{rules.map(&:to_s).join(
|
32
|
+
"#{type}(#{rules.map(&:to_s).join(", ")})"
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
data/lib/dry/logic/operators.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Logic
|
3
5
|
module Operators
|
4
6
|
def and(other)
|
5
7
|
Operations::And.new(self, other)
|
6
8
|
end
|
7
|
-
|
9
|
+
alias_method :&, :and
|
8
10
|
|
9
11
|
def or(other)
|
10
12
|
Operations::Or.new(self, other)
|
11
13
|
end
|
12
|
-
|
14
|
+
alias_method :|, :or
|
13
15
|
|
14
16
|
def xor(other)
|
15
17
|
Operations::Xor.new(self, other)
|
16
18
|
end
|
17
|
-
|
19
|
+
alias_method :^, :xor
|
18
20
|
|
19
21
|
def then(other)
|
20
22
|
Operations::Implication.new(self, other)
|
21
23
|
end
|
22
|
-
|
24
|
+
alias_method :>, :then
|
23
25
|
end
|
24
26
|
end
|
25
27
|
end
|
data/lib/dry/logic/predicates.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
require "bigdecimal/util"
|
5
|
+
require "date"
|
4
6
|
|
5
7
|
module Dry
|
6
8
|
module Logic
|
@@ -11,12 +13,13 @@ module Dry
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def type?(type, input)
|
14
|
-
input.
|
16
|
+
input.is_a?(type)
|
15
17
|
end
|
16
18
|
|
17
|
-
def
|
19
|
+
def nil?(input)
|
18
20
|
input.nil?
|
19
21
|
end
|
22
|
+
alias_method :none?, :nil?
|
20
23
|
|
21
24
|
def key?(name, input)
|
22
25
|
input.key?(name)
|
@@ -56,11 +59,9 @@ module Dry
|
|
56
59
|
end
|
57
60
|
|
58
61
|
def number?(input)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
false
|
63
|
-
end
|
62
|
+
true if Float(input)
|
63
|
+
rescue ArgumentError, TypeError
|
64
|
+
false
|
64
65
|
end
|
65
66
|
|
66
67
|
def int?(input)
|
@@ -113,7 +114,7 @@ module Dry
|
|
113
114
|
|
114
115
|
def size?(size, input)
|
115
116
|
case size
|
116
|
-
when Integer then size
|
117
|
+
when Integer then size.equal?(input.size)
|
117
118
|
when Range, Array then size.include?(input.size)
|
118
119
|
else
|
119
120
|
raise ArgumentError, "+#{size}+ is not supported type for size? predicate."
|
@@ -128,13 +129,30 @@ module Dry
|
|
128
129
|
input.size <= num
|
129
130
|
end
|
130
131
|
|
132
|
+
def bytesize?(size, input)
|
133
|
+
case size
|
134
|
+
when Integer then size.equal?(input.bytesize)
|
135
|
+
when Range, Array then size.include?(input.bytesize)
|
136
|
+
else
|
137
|
+
raise ArgumentError, "+#{size}+ is not supported type for bytesize? predicate."
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def min_bytesize?(num, input)
|
142
|
+
input.bytesize >= num
|
143
|
+
end
|
144
|
+
|
145
|
+
def max_bytesize?(num, input)
|
146
|
+
input.bytesize <= num
|
147
|
+
end
|
148
|
+
|
131
149
|
def inclusion?(list, input)
|
132
|
-
::Kernel.warn
|
150
|
+
::Kernel.warn "inclusion is deprecated - use included_in instead."
|
133
151
|
included_in?(list, input)
|
134
152
|
end
|
135
153
|
|
136
154
|
def exclusion?(list, input)
|
137
|
-
::Kernel.warn
|
155
|
+
::Kernel.warn "exclusion is deprecated - use excluded_from instead."
|
138
156
|
excluded_from?(list, input)
|
139
157
|
end
|
140
158
|
|
@@ -147,15 +165,13 @@ module Dry
|
|
147
165
|
end
|
148
166
|
|
149
167
|
def includes?(value, input)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
else
|
154
|
-
false
|
155
|
-
end
|
156
|
-
rescue TypeError
|
168
|
+
if input.respond_to?(:include?)
|
169
|
+
input.include?(value)
|
170
|
+
else
|
157
171
|
false
|
158
172
|
end
|
173
|
+
rescue TypeError
|
174
|
+
false
|
159
175
|
end
|
160
176
|
|
161
177
|
def excludes?(value, input)
|
@@ -182,20 +198,48 @@ module Dry
|
|
182
198
|
value.equal?(false)
|
183
199
|
end
|
184
200
|
|
185
|
-
|
186
|
-
|
187
|
-
!regex.match(input).nil?
|
188
|
-
end
|
189
|
-
else
|
190
|
-
def format?(regex, input)
|
191
|
-
regex.match?(input)
|
192
|
-
end
|
201
|
+
def format?(regex, input)
|
202
|
+
!input.nil? && regex.match?(input)
|
193
203
|
end
|
194
204
|
|
195
205
|
def case?(pattern, input)
|
196
206
|
pattern === input
|
197
207
|
end
|
198
208
|
|
209
|
+
def uuid_v1?(input)
|
210
|
+
uuid_v1_format = /\A[0-9A-F]{8}-[0-9A-F]{4}-1[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
211
|
+
format?(uuid_v1_format, input)
|
212
|
+
end
|
213
|
+
|
214
|
+
def uuid_v2?(input)
|
215
|
+
uuid_v2_format = /\A[0-9A-F]{8}-[0-9A-F]{4}-2[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
216
|
+
format?(uuid_v2_format, input)
|
217
|
+
end
|
218
|
+
|
219
|
+
def uuid_v3?(input)
|
220
|
+
uuid_v3_format = /\A[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
221
|
+
format?(uuid_v3_format, input)
|
222
|
+
end
|
223
|
+
|
224
|
+
def uuid_v4?(input)
|
225
|
+
uuid_v4_format = /\A[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
226
|
+
format?(uuid_v4_format, input)
|
227
|
+
end
|
228
|
+
|
229
|
+
def uuid_v5?(input)
|
230
|
+
uuid_v5_format = /\A[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
231
|
+
format?(uuid_v5_format, input)
|
232
|
+
end
|
233
|
+
|
234
|
+
def uri?(schemes, input)
|
235
|
+
uri_format = URI::DEFAULT_PARSER.make_regexp(schemes)
|
236
|
+
format?(uri_format, input)
|
237
|
+
end
|
238
|
+
|
239
|
+
def respond_to?(method, input)
|
240
|
+
input.respond_to?(method)
|
241
|
+
end
|
242
|
+
|
199
243
|
def predicate(name, &block)
|
200
244
|
define_singleton_method(name, &block)
|
201
245
|
end
|