dry-logic 0.4.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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