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.
Files changed (97) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +214 -53
  3. data/LICENSE +1 -1
  4. data/README.md +14 -18
  5. data/dry-logic.gemspec +28 -15
  6. data/lib/dry-logic.rb +3 -1
  7. data/lib/dry/logic.rb +4 -2
  8. data/lib/dry/logic/appliable.rb +2 -0
  9. data/lib/dry/logic/evaluator.rb +3 -1
  10. data/lib/dry/logic/operations.rb +13 -11
  11. data/lib/dry/logic/operations/abstract.rb +8 -6
  12. data/lib/dry/logic/operations/and.rb +15 -3
  13. data/lib/dry/logic/operations/attr.rb +3 -1
  14. data/lib/dry/logic/operations/binary.rb +3 -1
  15. data/lib/dry/logic/operations/check.rb +6 -4
  16. data/lib/dry/logic/operations/each.rb +4 -2
  17. data/lib/dry/logic/operations/implication.rb +4 -2
  18. data/lib/dry/logic/operations/key.rb +11 -9
  19. data/lib/dry/logic/operations/negation.rb +9 -3
  20. data/lib/dry/logic/operations/or.rb +4 -2
  21. data/lib/dry/logic/operations/set.rb +5 -3
  22. data/lib/dry/logic/operations/unary.rb +3 -1
  23. data/lib/dry/logic/operations/xor.rb +4 -2
  24. data/lib/dry/logic/operators.rb +6 -4
  25. data/lib/dry/logic/predicates.rb +72 -28
  26. data/lib/dry/logic/result.rb +4 -2
  27. data/lib/dry/logic/rule.rb +35 -30
  28. data/lib/dry/logic/rule/interface.rb +141 -0
  29. data/lib/dry/logic/rule/predicate.rb +9 -3
  30. data/lib/dry/logic/rule_compiler.rb +7 -3
  31. data/lib/dry/logic/version.rb +3 -1
  32. metadata +21 -151
  33. data/.codeclimate.yml +0 -23
  34. data/.gitignore +0 -8
  35. data/.rspec +0 -3
  36. data/.rubocop.yml +0 -16
  37. data/.rubocop_todo.yml +0 -7
  38. data/.travis.yml +0 -31
  39. data/CONTRIBUTING.md +0 -29
  40. data/Gemfile +0 -18
  41. data/Rakefile +0 -12
  42. data/examples/basic.rb +0 -14
  43. data/spec/integration/result_spec.rb +0 -59
  44. data/spec/integration/rule_spec.rb +0 -53
  45. data/spec/shared/predicates.rb +0 -57
  46. data/spec/shared/rule.rb +0 -67
  47. data/spec/spec_helper.rb +0 -34
  48. data/spec/support/mutant.rb +0 -9
  49. data/spec/unit/operations/and_spec.rb +0 -64
  50. data/spec/unit/operations/attr_spec.rb +0 -27
  51. data/spec/unit/operations/check_spec.rb +0 -49
  52. data/spec/unit/operations/each_spec.rb +0 -47
  53. data/spec/unit/operations/implication_spec.rb +0 -30
  54. data/spec/unit/operations/key_spec.rb +0 -119
  55. data/spec/unit/operations/negation_spec.rb +0 -40
  56. data/spec/unit/operations/or_spec.rb +0 -73
  57. data/spec/unit/operations/set_spec.rb +0 -41
  58. data/spec/unit/operations/xor_spec.rb +0 -61
  59. data/spec/unit/predicates/array_spec.rb +0 -41
  60. data/spec/unit/predicates/attr_spec.rb +0 -29
  61. data/spec/unit/predicates/bool_spec.rb +0 -34
  62. data/spec/unit/predicates/case_spec.rb +0 -33
  63. data/spec/unit/predicates/date_spec.rb +0 -31
  64. data/spec/unit/predicates/date_time_spec.rb +0 -31
  65. data/spec/unit/predicates/decimal_spec.rb +0 -32
  66. data/spec/unit/predicates/empty_spec.rb +0 -38
  67. data/spec/unit/predicates/eql_spec.rb +0 -21
  68. data/spec/unit/predicates/even_spec.rb +0 -31
  69. data/spec/unit/predicates/excluded_from_spec.rb +0 -35
  70. data/spec/unit/predicates/excludes_spec.rb +0 -56
  71. data/spec/unit/predicates/false_spec.rb +0 -35
  72. data/spec/unit/predicates/filled_spec.rb +0 -38
  73. data/spec/unit/predicates/float_spec.rb +0 -31
  74. data/spec/unit/predicates/format_spec.rb +0 -21
  75. data/spec/unit/predicates/gt_spec.rb +0 -40
  76. data/spec/unit/predicates/gteq_spec.rb +0 -40
  77. data/spec/unit/predicates/included_in_spec.rb +0 -35
  78. data/spec/unit/predicates/includes_spec.rb +0 -24
  79. data/spec/unit/predicates/int_spec.rb +0 -34
  80. data/spec/unit/predicates/key_spec.rb +0 -29
  81. data/spec/unit/predicates/lt_spec.rb +0 -40
  82. data/spec/unit/predicates/lteq_spec.rb +0 -40
  83. data/spec/unit/predicates/max_size_spec.rb +0 -49
  84. data/spec/unit/predicates/min_size_spec.rb +0 -49
  85. data/spec/unit/predicates/none_spec.rb +0 -28
  86. data/spec/unit/predicates/not_eql_spec.rb +0 -21
  87. data/spec/unit/predicates/number_spec.rb +0 -37
  88. data/spec/unit/predicates/odd_spec.rb +0 -31
  89. data/spec/unit/predicates/size_spec.rb +0 -55
  90. data/spec/unit/predicates/str_spec.rb +0 -32
  91. data/spec/unit/predicates/time_spec.rb +0 -31
  92. data/spec/unit/predicates/true_spec.rb +0 -35
  93. data/spec/unit/predicates/type_spec.rb +0 -35
  94. data/spec/unit/predicates_spec.rb +0 -23
  95. data/spec/unit/rule/predicate_spec.rb +0 -53
  96. data/spec/unit/rule_compiler_spec.rb +0 -127
  97. data/spec/unit/rule_spec.rb +0 -141
data/lib/dry-logic.rb CHANGED
@@ -1 +1,3 @@
1
- require 'dry/logic'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/logic"
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 'dry/logic/rule/predicate'
9
- require 'dry/logic/operations'
10
+ require "dry/logic/rule/predicate"
11
+ require "dry/logic/operations"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Logic
3
5
  module Appliable
@@ -1,4 +1,6 @@
1
- require 'dry/equalizer'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
2
4
 
3
5
  module Dry
4
6
  module Logic
@@ -1,13 +1,15 @@
1
- require 'dry/logic/operations/and'
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 'dry/logic/operations/key'
8
- require 'dry/logic/operations/attr'
9
- require 'dry/logic/operations/each'
10
- require 'dry/logic/operations/set'
11
- require 'dry/logic/operations/check'
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 'dry/logic/operators'
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
- require 'dry/core/constants'
2
- require 'dry/equalizer'
3
- require 'dry/logic/operators'
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.merge(new_options))
33
+ self.class.new(*rules, **options, **new_options)
32
34
  end
33
35
 
34
36
  def with(new_options)
35
- new(rules, options.merge(new_options))
37
+ new(rules, **options, **new_options)
36
38
  end
37
39
 
38
40
  def to_ast
@@ -1,10 +1,19 @@
1
- require 'dry/logic/operations/binary'
2
- require 'dry/logic/result'
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) { [type, [left_result.to_ast, [:hint, right.ast(input)]]] }
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,4 +1,6 @@
1
- require 'dry/logic/operations/key'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/logic/operations/key"
2
4
 
3
5
  module Dry
4
6
  module Logic
@@ -1,4 +1,6 @@
1
- require 'dry/logic/operations/abstract'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/logic/operations/abstract"
2
4
 
3
5
  module Dry
4
6
  module Logic
@@ -1,6 +1,8 @@
1
- require 'dry/logic/operations/unary'
2
- require 'dry/logic/evaluator'
3
- require 'dry/logic/result'
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.merge(evaluator: evaluator))
20
+ super(rule, **options, evaluator: evaluator)
19
21
  end
20
22
  end
21
23
 
@@ -1,5 +1,7 @@
1
- require 'dry/logic/operations/unary'
2
- require 'dry/logic/result'
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
@@ -1,5 +1,7 @@
1
- require 'dry/logic/operations/binary'
2
- require 'dry/logic/result'
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
@@ -1,6 +1,8 @@
1
- require 'dry/logic/operations/unary'
2
- require 'dry/logic/evaluator'
3
- require 'dry/logic/result'
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.merge(evaluator: eval, path: name))
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 = nil)
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
- require 'dry/logic/operations/unary'
2
- require 'dry/logic/result'
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(!rule[input], id) { ast(input) }
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
- require 'dry/logic/operations/binary'
2
- require 'dry/logic/result'
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
@@ -1,5 +1,7 @@
1
- require 'dry/logic/operations/abstract'
2
- require 'dry/logic/result'
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
@@ -1,4 +1,6 @@
1
- require 'dry/logic/operations/abstract'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/logic/operations/abstract"
2
4
 
3
5
  module Dry
4
6
  module Logic
@@ -1,5 +1,7 @@
1
- require 'dry/logic/operations/binary'
2
- require 'dry/logic/result'
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
@@ -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
- alias & and
9
+ alias_method :&, :and
8
10
 
9
11
  def or(other)
10
12
  Operations::Or.new(self, other)
11
13
  end
12
- alias | or
14
+ alias_method :|, :or
13
15
 
14
16
  def xor(other)
15
17
  Operations::Xor.new(self, other)
16
18
  end
17
- alias ^ xor
19
+ alias_method :^, :xor
18
20
 
19
21
  def then(other)
20
22
  Operations::Implication.new(self, other)
21
23
  end
22
- alias > then
24
+ alias_method :>, :then
23
25
  end
24
26
  end
25
27
  end
@@ -1,6 +1,8 @@
1
- require 'bigdecimal'
2
- require 'bigdecimal/util'
3
- require 'date'
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.kind_of?(type)
16
+ input.is_a?(type)
15
17
  end
16
18
 
17
- def none?(input)
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
- begin
60
- true if Float(input)
61
- rescue ArgumentError, TypeError
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 == input.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 'inclusion is deprecated - use included_in instead.'
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 'exclusion is deprecated - use excluded_from instead.'
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
- begin
151
- if input.respond_to?(:include?)
152
- input.include?(value)
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
- if RUBY_VERSION < '2.4'
186
- def format?(regex, input)
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