dry-logic 1.0.2 → 1.0.8
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 +4 -4
- data/CHANGELOG.md +129 -26
- data/LICENSE +1 -1
- data/README.md +12 -14
- data/dry-logic.gemspec +27 -14
- data/lib/dry-logic.rb +1 -1
- data/lib/dry/logic.rb +2 -2
- data/lib/dry/logic/evaluator.rb +1 -1
- data/lib/dry/logic/operations.rb +11 -11
- data/lib/dry/logic/operations/abstract.rb +6 -6
- data/lib/dry/logic/operations/and.rb +4 -4
- data/lib/dry/logic/operations/attr.rb +1 -1
- data/lib/dry/logic/operations/binary.rb +1 -1
- data/lib/dry/logic/operations/check.rb +4 -4
- data/lib/dry/logic/operations/each.rb +2 -2
- data/lib/dry/logic/operations/implication.rb +2 -2
- data/lib/dry/logic/operations/key.rb +5 -5
- data/lib/dry/logic/operations/negation.rb +2 -2
- data/lib/dry/logic/operations/or.rb +2 -2
- data/lib/dry/logic/operations/set.rb +3 -3
- data/lib/dry/logic/operations/unary.rb +1 -1
- data/lib/dry/logic/operations/xor.rb +2 -2
- data/lib/dry/logic/operators.rb +4 -4
- data/lib/dry/logic/predicates.rb +60 -28
- data/lib/dry/logic/result.rb +2 -2
- data/lib/dry/logic/rule.rb +11 -11
- data/lib/dry/logic/rule/interface.rb +32 -37
- data/lib/dry/logic/rule/predicate.rb +3 -3
- data/lib/dry/logic/rule_compiler.rb +3 -3
- data/lib/dry/logic/version.rb +1 -1
- metadata +12 -133
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -9
- data/.rspec +0 -3
- data/.travis.yml +0 -31
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -15
- data/Rakefile +0 -14
- data/benchmarks/rule_application.rb +0 -30
- data/benchmarks/setup.rb +0 -13
- data/bin/console +0 -11
- data/examples/basic.rb +0 -16
- data/spec/integration/result_spec.rb +0 -61
- data/spec/integration/rule_spec.rb +0 -55
- data/spec/shared/predicates.rb +0 -59
- data/spec/shared/rule.rb +0 -74
- data/spec/spec_helper.rb +0 -36
- data/spec/support/mutant.rb +0 -11
- data/spec/unit/operations/and_spec.rb +0 -70
- data/spec/unit/operations/attr_spec.rb +0 -29
- data/spec/unit/operations/check_spec.rb +0 -51
- data/spec/unit/operations/each_spec.rb +0 -49
- data/spec/unit/operations/implication_spec.rb +0 -32
- data/spec/unit/operations/key_spec.rb +0 -135
- data/spec/unit/operations/negation_spec.rb +0 -51
- data/spec/unit/operations/or_spec.rb +0 -75
- data/spec/unit/operations/set_spec.rb +0 -43
- data/spec/unit/operations/xor_spec.rb +0 -63
- data/spec/unit/predicates/array_spec.rb +0 -43
- data/spec/unit/predicates/attr_spec.rb +0 -31
- data/spec/unit/predicates/bool_spec.rb +0 -36
- data/spec/unit/predicates/case_spec.rb +0 -35
- data/spec/unit/predicates/date_spec.rb +0 -33
- data/spec/unit/predicates/date_time_spec.rb +0 -33
- data/spec/unit/predicates/decimal_spec.rb +0 -34
- data/spec/unit/predicates/empty_spec.rb +0 -40
- data/spec/unit/predicates/eql_spec.rb +0 -23
- data/spec/unit/predicates/even_spec.rb +0 -33
- data/spec/unit/predicates/excluded_from_spec.rb +0 -37
- data/spec/unit/predicates/excludes_spec.rb +0 -58
- data/spec/unit/predicates/false_spec.rb +0 -37
- data/spec/unit/predicates/filled_spec.rb +0 -40
- data/spec/unit/predicates/float_spec.rb +0 -33
- data/spec/unit/predicates/format_spec.rb +0 -23
- data/spec/unit/predicates/gt_spec.rb +0 -42
- data/spec/unit/predicates/gteq_spec.rb +0 -42
- data/spec/unit/predicates/included_in_spec.rb +0 -37
- data/spec/unit/predicates/includes_spec.rb +0 -24
- data/spec/unit/predicates/int_spec.rb +0 -36
- data/spec/unit/predicates/key_spec.rb +0 -31
- data/spec/unit/predicates/lt_spec.rb +0 -42
- data/spec/unit/predicates/lteq_spec.rb +0 -42
- data/spec/unit/predicates/max_size_spec.rb +0 -51
- data/spec/unit/predicates/min_size_spec.rb +0 -51
- data/spec/unit/predicates/none_spec.rb +0 -30
- data/spec/unit/predicates/not_eql_spec.rb +0 -23
- data/spec/unit/predicates/number_spec.rb +0 -39
- data/spec/unit/predicates/odd_spec.rb +0 -33
- data/spec/unit/predicates/respond_to_spec.rb +0 -31
- data/spec/unit/predicates/size_spec.rb +0 -57
- data/spec/unit/predicates/str_spec.rb +0 -34
- data/spec/unit/predicates/time_spec.rb +0 -33
- data/spec/unit/predicates/true_spec.rb +0 -37
- data/spec/unit/predicates/type_spec.rb +0 -37
- data/spec/unit/predicates/uuid_v4_spec.rb +0 -29
- data/spec/unit/predicates_spec.rb +0 -25
- data/spec/unit/rule/predicate_spec.rb +0 -55
- data/spec/unit/rule_compiler_spec.rb +0 -129
- data/spec/unit/rule_spec.rb +0 -213
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/logic/operations/binary"
|
4
|
+
require "dry/logic/result"
|
5
5
|
|
6
6
|
module Dry
|
7
7
|
module Logic
|
@@ -9,7 +9,7 @@ module Dry
|
|
9
9
|
class And < Binary
|
10
10
|
attr_reader :hints
|
11
11
|
|
12
|
-
def initialize(
|
12
|
+
def initialize(*, **)
|
13
13
|
super
|
14
14
|
@hints = options.fetch(:hints, true)
|
15
15
|
end
|
@@ -17,7 +17,7 @@ module Dry
|
|
17
17
|
def type
|
18
18
|
:and
|
19
19
|
end
|
20
|
-
|
20
|
+
alias_method :operator, :type
|
21
21
|
|
22
22
|
def call(input)
|
23
23
|
left_result = left.(input)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "dry/logic/operations/unary"
|
4
|
+
require "dry/logic/evaluator"
|
5
|
+
require "dry/logic/result"
|
6
6
|
|
7
7
|
module Dry
|
8
8
|
module Logic
|
@@ -17,7 +17,7 @@ module Dry
|
|
17
17
|
keys = options.fetch(:keys)
|
18
18
|
evaluator = Evaluator::Set.new(keys)
|
19
19
|
|
20
|
-
super(rule, options
|
20
|
+
super(rule, **options, evaluator: evaluator)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "dry/logic/operations/unary"
|
4
|
+
require "dry/logic/evaluator"
|
5
|
+
require "dry/logic/result"
|
6
6
|
|
7
7
|
module Dry
|
8
8
|
module Logic
|
@@ -12,13 +12,13 @@ module Dry
|
|
12
12
|
|
13
13
|
attr_reader :path
|
14
14
|
|
15
|
-
def self.new(rules, options)
|
15
|
+
def self.new(rules, **options)
|
16
16
|
if options[:evaluator]
|
17
17
|
super
|
18
18
|
else
|
19
19
|
name = options.fetch(:name)
|
20
20
|
eval = options.fetch(:evaluator, evaluator(name))
|
21
|
-
super(rules, options
|
21
|
+
super(rules, **options, evaluator: eval, path: name)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/logic/operations/abstract"
|
4
|
+
require "dry/logic/result"
|
5
5
|
|
6
6
|
module Dry
|
7
7
|
module Logic
|
@@ -29,7 +29,7 @@ module Dry
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def to_s
|
32
|
-
"#{type}(#{rules.map(&:to_s).join(
|
32
|
+
"#{type}(#{rules.map(&:to_s).join(", ")})"
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
data/lib/dry/logic/operators.rb
CHANGED
@@ -6,22 +6,22 @@ module Dry
|
|
6
6
|
def and(other)
|
7
7
|
Operations::And.new(self, other)
|
8
8
|
end
|
9
|
-
|
9
|
+
alias_method :&, :and
|
10
10
|
|
11
11
|
def or(other)
|
12
12
|
Operations::Or.new(self, other)
|
13
13
|
end
|
14
|
-
|
14
|
+
alias_method :|, :or
|
15
15
|
|
16
16
|
def xor(other)
|
17
17
|
Operations::Xor.new(self, other)
|
18
18
|
end
|
19
|
-
|
19
|
+
alias_method :^, :xor
|
20
20
|
|
21
21
|
def then(other)
|
22
22
|
Operations::Implication.new(self, other)
|
23
23
|
end
|
24
|
-
|
24
|
+
alias_method :>, :then
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/dry/logic/predicates.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "bigdecimal"
|
4
|
+
require "bigdecimal/util"
|
5
|
+
require "date"
|
6
6
|
|
7
7
|
module Dry
|
8
8
|
module Logic
|
@@ -13,7 +13,7 @@ module Dry
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def type?(type, input)
|
16
|
-
input.
|
16
|
+
input.is_a?(type)
|
17
17
|
end
|
18
18
|
|
19
19
|
def nil?(input)
|
@@ -59,11 +59,9 @@ module Dry
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def number?(input)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
false
|
66
|
-
end
|
62
|
+
true if Float(input)
|
63
|
+
rescue ArgumentError, TypeError
|
64
|
+
false
|
67
65
|
end
|
68
66
|
|
69
67
|
def int?(input)
|
@@ -116,7 +114,7 @@ module Dry
|
|
116
114
|
|
117
115
|
def size?(size, input)
|
118
116
|
case size
|
119
|
-
when Integer then size
|
117
|
+
when Integer then size.equal?(input.size)
|
120
118
|
when Range, Array then size.include?(input.size)
|
121
119
|
else
|
122
120
|
raise ArgumentError, "+#{size}+ is not supported type for size? predicate."
|
@@ -131,13 +129,30 @@ module Dry
|
|
131
129
|
input.size <= num
|
132
130
|
end
|
133
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
|
+
|
134
149
|
def inclusion?(list, input)
|
135
|
-
::Kernel.warn
|
150
|
+
::Kernel.warn "inclusion is deprecated - use included_in instead."
|
136
151
|
included_in?(list, input)
|
137
152
|
end
|
138
153
|
|
139
154
|
def exclusion?(list, input)
|
140
|
-
::Kernel.warn
|
155
|
+
::Kernel.warn "exclusion is deprecated - use excluded_from instead."
|
141
156
|
excluded_from?(list, input)
|
142
157
|
end
|
143
158
|
|
@@ -150,15 +165,13 @@ module Dry
|
|
150
165
|
end
|
151
166
|
|
152
167
|
def includes?(value, input)
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
else
|
157
|
-
false
|
158
|
-
end
|
159
|
-
rescue TypeError
|
168
|
+
if input.respond_to?(:include?)
|
169
|
+
input.include?(value)
|
170
|
+
else
|
160
171
|
false
|
161
172
|
end
|
173
|
+
rescue TypeError
|
174
|
+
false
|
162
175
|
end
|
163
176
|
|
164
177
|
def excludes?(value, input)
|
@@ -185,25 +198,44 @@ module Dry
|
|
185
198
|
value.equal?(false)
|
186
199
|
end
|
187
200
|
|
188
|
-
|
189
|
-
|
190
|
-
!regex.match(input).nil?
|
191
|
-
end
|
192
|
-
else
|
193
|
-
def format?(regex, input)
|
194
|
-
regex.match?(input)
|
195
|
-
end
|
201
|
+
def format?(regex, input)
|
202
|
+
!input.nil? && regex.match?(input)
|
196
203
|
end
|
197
204
|
|
198
205
|
def case?(pattern, input)
|
199
206
|
pattern === input
|
200
207
|
end
|
201
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
|
+
|
202
224
|
def uuid_v4?(input)
|
203
|
-
uuid_v4_format =
|
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
|
204
226
|
format?(uuid_v4_format, input)
|
205
227
|
end
|
206
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
|
+
|
207
239
|
def respond_to?(method, input)
|
208
240
|
input.respond_to?(method)
|
209
241
|
end
|
data/lib/dry/logic/result.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/core/constants"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Logic
|
@@ -69,7 +69,7 @@ module Dry
|
|
69
69
|
if args.empty?
|
70
70
|
name.to_s
|
71
71
|
else
|
72
|
-
"#{name}(#{args.map(&:last).map(&:inspect).join(
|
72
|
+
"#{name}(#{args.map(&:last).map(&:inspect).join(", ")})"
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
data/lib/dry/logic/rule.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
3
|
+
require "concurrent/map"
|
4
|
+
require "dry/core/constants"
|
5
|
+
require "dry/equalizer"
|
6
|
+
require "dry/logic/operations"
|
7
|
+
require "dry/logic/result"
|
8
|
+
require "dry/logic/rule/interface"
|
9
9
|
|
10
10
|
module Dry
|
11
11
|
module Logic
|
@@ -38,13 +38,13 @@ module Dry
|
|
38
38
|
base.interfaces.fetch_or_store([arity, curried]) do
|
39
39
|
interface = Interface.new(arity, curried)
|
40
40
|
klass = Class.new(base) { include interface }
|
41
|
-
base.const_set("#{base.name.split(
|
41
|
+
base.const_set("#{base.name.split("::").last}#{interface.name}", klass)
|
42
42
|
klass
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
def self.build(predicate, args: EMPTY_ARRAY, arity: predicate.arity, **options)
|
47
|
-
specialize(arity, args.size).new(predicate, {
|
47
|
+
specialize(arity, args.size).new(predicate, {args: args, arity: arity, **options})
|
48
48
|
end
|
49
49
|
|
50
50
|
def initialize(predicate, options = EMPTY_HASH)
|
@@ -68,11 +68,11 @@ module Dry
|
|
68
68
|
|
69
69
|
def bind(object)
|
70
70
|
if predicate.respond_to?(:bind)
|
71
|
-
self.class.build(predicate.bind(object), options)
|
71
|
+
self.class.build(predicate.bind(object), **options)
|
72
72
|
else
|
73
73
|
self.class.build(
|
74
74
|
-> *args { object.instance_exec(*args, &predicate) },
|
75
|
-
options
|
75
|
+
**options, arity: arity, parameters: parameters
|
76
76
|
)
|
77
77
|
end
|
78
78
|
end
|
@@ -82,7 +82,7 @@ module Dry
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def with(new_opts)
|
85
|
-
self.class.build(predicate, options
|
85
|
+
self.class.build(predicate, **options, **new_opts)
|
86
86
|
end
|
87
87
|
|
88
88
|
def parameters
|
@@ -4,6 +4,8 @@ module Dry
|
|
4
4
|
module Logic
|
5
5
|
class Rule
|
6
6
|
class Interface < ::Module
|
7
|
+
SPLAT = ["*rest"].freeze
|
8
|
+
|
7
9
|
attr_reader :arity
|
8
10
|
|
9
11
|
attr_reader :curried
|
@@ -18,12 +20,10 @@ module Dry
|
|
18
20
|
|
19
21
|
define_constructor if curried?
|
20
22
|
|
21
|
-
if
|
22
|
-
define_splat_application
|
23
|
-
elsif constant?
|
23
|
+
if constant?
|
24
24
|
define_constant_application
|
25
25
|
else
|
26
|
-
|
26
|
+
define_application
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -32,7 +32,7 @@ module Dry
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def variable_arity?
|
35
|
-
arity.
|
35
|
+
arity.negative?
|
36
36
|
end
|
37
37
|
|
38
38
|
def curried?
|
@@ -41,7 +41,13 @@ module Dry
|
|
41
41
|
|
42
42
|
def unapplied
|
43
43
|
if variable_arity?
|
44
|
-
-1
|
44
|
+
unapplied = arity.abs - 1 - curried
|
45
|
+
|
46
|
+
if unapplied.negative?
|
47
|
+
0
|
48
|
+
else
|
49
|
+
unapplied
|
50
|
+
end
|
45
51
|
else
|
46
52
|
arity - curried
|
47
53
|
end
|
@@ -49,10 +55,21 @@ module Dry
|
|
49
55
|
|
50
56
|
def name
|
51
57
|
if constant?
|
52
|
-
|
58
|
+
"Constant"
|
53
59
|
else
|
54
|
-
arity_str =
|
55
|
-
|
60
|
+
arity_str =
|
61
|
+
if variable_arity?
|
62
|
+
"Variable#{arity.abs - 1}Arity"
|
63
|
+
else
|
64
|
+
"#{arity}Arity"
|
65
|
+
end
|
66
|
+
|
67
|
+
curried_str =
|
68
|
+
if curried?
|
69
|
+
"#{curried}Curried"
|
70
|
+
else
|
71
|
+
EMPTY_STRING
|
72
|
+
end
|
56
73
|
|
57
74
|
"#{arity_str}#{curried_str}"
|
58
75
|
end
|
@@ -61,9 +78,9 @@ module Dry
|
|
61
78
|
def define_constructor
|
62
79
|
assignment =
|
63
80
|
if curried.equal?(1)
|
64
|
-
|
81
|
+
"@arg0 = @args[0]"
|
65
82
|
else
|
66
|
-
"#{curried_args.join(
|
83
|
+
"#{curried_args.join(", ")} = @args"
|
67
84
|
end
|
68
85
|
|
69
86
|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
@@ -91,32 +108,10 @@ module Dry
|
|
91
108
|
end
|
92
109
|
end
|
93
110
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
else
|
99
|
-
'@predicate[*input]'
|
100
|
-
end
|
101
|
-
|
102
|
-
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
103
|
-
def call(*input)
|
104
|
-
if #{application}
|
105
|
-
Result::SUCCESS
|
106
|
-
else
|
107
|
-
Result.new(false, id) { ast(*input) }
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def [](*input)
|
112
|
-
#{application}
|
113
|
-
end
|
114
|
-
RUBY
|
115
|
-
end
|
116
|
-
|
117
|
-
def define_fixed_application
|
118
|
-
parameters = unapplied_args.join(', ')
|
119
|
-
application = "@predicate[#{ (curried_args + unapplied_args).join(', ') }]"
|
111
|
+
def define_application
|
112
|
+
splat = variable_arity? ? SPLAT : EMPTY_ARRAY
|
113
|
+
parameters = (unapplied_args + splat).join(", ")
|
114
|
+
application = "@predicate[#{(curried_args + unapplied_args + splat).join(", ")}]"
|
120
115
|
|
121
116
|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
122
117
|
def call(#{parameters})
|