dry-logic 1.0.2 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +129 -26
  3. data/LICENSE +1 -1
  4. data/README.md +12 -14
  5. data/dry-logic.gemspec +27 -14
  6. data/lib/dry-logic.rb +1 -1
  7. data/lib/dry/logic.rb +2 -2
  8. data/lib/dry/logic/evaluator.rb +1 -1
  9. data/lib/dry/logic/operations.rb +11 -11
  10. data/lib/dry/logic/operations/abstract.rb +6 -6
  11. data/lib/dry/logic/operations/and.rb +4 -4
  12. data/lib/dry/logic/operations/attr.rb +1 -1
  13. data/lib/dry/logic/operations/binary.rb +1 -1
  14. data/lib/dry/logic/operations/check.rb +4 -4
  15. data/lib/dry/logic/operations/each.rb +2 -2
  16. data/lib/dry/logic/operations/implication.rb +2 -2
  17. data/lib/dry/logic/operations/key.rb +5 -5
  18. data/lib/dry/logic/operations/negation.rb +2 -2
  19. data/lib/dry/logic/operations/or.rb +2 -2
  20. data/lib/dry/logic/operations/set.rb +3 -3
  21. data/lib/dry/logic/operations/unary.rb +1 -1
  22. data/lib/dry/logic/operations/xor.rb +2 -2
  23. data/lib/dry/logic/operators.rb +4 -4
  24. data/lib/dry/logic/predicates.rb +60 -28
  25. data/lib/dry/logic/result.rb +2 -2
  26. data/lib/dry/logic/rule.rb +11 -11
  27. data/lib/dry/logic/rule/interface.rb +32 -37
  28. data/lib/dry/logic/rule/predicate.rb +3 -3
  29. data/lib/dry/logic/rule_compiler.rb +3 -3
  30. data/lib/dry/logic/version.rb +1 -1
  31. metadata +12 -133
  32. data/.codeclimate.yml +0 -15
  33. data/.gitignore +0 -9
  34. data/.rspec +0 -3
  35. data/.travis.yml +0 -31
  36. data/CONTRIBUTING.md +0 -29
  37. data/Gemfile +0 -15
  38. data/Rakefile +0 -14
  39. data/benchmarks/rule_application.rb +0 -30
  40. data/benchmarks/setup.rb +0 -13
  41. data/bin/console +0 -11
  42. data/examples/basic.rb +0 -16
  43. data/spec/integration/result_spec.rb +0 -61
  44. data/spec/integration/rule_spec.rb +0 -55
  45. data/spec/shared/predicates.rb +0 -59
  46. data/spec/shared/rule.rb +0 -74
  47. data/spec/spec_helper.rb +0 -36
  48. data/spec/support/mutant.rb +0 -11
  49. data/spec/unit/operations/and_spec.rb +0 -70
  50. data/spec/unit/operations/attr_spec.rb +0 -29
  51. data/spec/unit/operations/check_spec.rb +0 -51
  52. data/spec/unit/operations/each_spec.rb +0 -49
  53. data/spec/unit/operations/implication_spec.rb +0 -32
  54. data/spec/unit/operations/key_spec.rb +0 -135
  55. data/spec/unit/operations/negation_spec.rb +0 -51
  56. data/spec/unit/operations/or_spec.rb +0 -75
  57. data/spec/unit/operations/set_spec.rb +0 -43
  58. data/spec/unit/operations/xor_spec.rb +0 -63
  59. data/spec/unit/predicates/array_spec.rb +0 -43
  60. data/spec/unit/predicates/attr_spec.rb +0 -31
  61. data/spec/unit/predicates/bool_spec.rb +0 -36
  62. data/spec/unit/predicates/case_spec.rb +0 -35
  63. data/spec/unit/predicates/date_spec.rb +0 -33
  64. data/spec/unit/predicates/date_time_spec.rb +0 -33
  65. data/spec/unit/predicates/decimal_spec.rb +0 -34
  66. data/spec/unit/predicates/empty_spec.rb +0 -40
  67. data/spec/unit/predicates/eql_spec.rb +0 -23
  68. data/spec/unit/predicates/even_spec.rb +0 -33
  69. data/spec/unit/predicates/excluded_from_spec.rb +0 -37
  70. data/spec/unit/predicates/excludes_spec.rb +0 -58
  71. data/spec/unit/predicates/false_spec.rb +0 -37
  72. data/spec/unit/predicates/filled_spec.rb +0 -40
  73. data/spec/unit/predicates/float_spec.rb +0 -33
  74. data/spec/unit/predicates/format_spec.rb +0 -23
  75. data/spec/unit/predicates/gt_spec.rb +0 -42
  76. data/spec/unit/predicates/gteq_spec.rb +0 -42
  77. data/spec/unit/predicates/included_in_spec.rb +0 -37
  78. data/spec/unit/predicates/includes_spec.rb +0 -24
  79. data/spec/unit/predicates/int_spec.rb +0 -36
  80. data/spec/unit/predicates/key_spec.rb +0 -31
  81. data/spec/unit/predicates/lt_spec.rb +0 -42
  82. data/spec/unit/predicates/lteq_spec.rb +0 -42
  83. data/spec/unit/predicates/max_size_spec.rb +0 -51
  84. data/spec/unit/predicates/min_size_spec.rb +0 -51
  85. data/spec/unit/predicates/none_spec.rb +0 -30
  86. data/spec/unit/predicates/not_eql_spec.rb +0 -23
  87. data/spec/unit/predicates/number_spec.rb +0 -39
  88. data/spec/unit/predicates/odd_spec.rb +0 -33
  89. data/spec/unit/predicates/respond_to_spec.rb +0 -31
  90. data/spec/unit/predicates/size_spec.rb +0 -57
  91. data/spec/unit/predicates/str_spec.rb +0 -34
  92. data/spec/unit/predicates/time_spec.rb +0 -33
  93. data/spec/unit/predicates/true_spec.rb +0 -37
  94. data/spec/unit/predicates/type_spec.rb +0 -37
  95. data/spec/unit/predicates/uuid_v4_spec.rb +0 -29
  96. data/spec/unit/predicates_spec.rb +0 -25
  97. data/spec/unit/rule/predicate_spec.rb +0 -55
  98. data/spec/unit/rule_compiler_spec.rb +0 -129
  99. data/spec/unit/rule_spec.rb +0 -213
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/binary'
4
- require 'dry/logic/result'
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
- alias operator type
20
+ alias_method :operator, :type
21
21
 
22
22
  def call(input)
23
23
  left_result = left.(input)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/key'
3
+ require "dry/logic/operations/key"
4
4
 
5
5
  module Dry
6
6
  module Logic
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/abstract'
3
+ require "dry/logic/operations/abstract"
4
4
 
5
5
  module Dry
6
6
  module Logic
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/unary'
4
- require 'dry/logic/evaluator'
5
- require 'dry/logic/result'
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.merge(evaluator: evaluator))
20
+ super(rule, **options, evaluator: evaluator)
21
21
  end
22
22
  end
23
23
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/unary'
4
- require 'dry/logic/result'
3
+ require "dry/logic/operations/unary"
4
+ require "dry/logic/result"
5
5
 
6
6
  module Dry
7
7
  module Logic
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/binary'
4
- require 'dry/logic/result'
3
+ require "dry/logic/operations/binary"
4
+ require "dry/logic/result"
5
5
 
6
6
  module Dry
7
7
  module Logic
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/unary'
4
- require 'dry/logic/evaluator'
5
- require 'dry/logic/result'
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.merge(evaluator: eval, path: name))
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 'dry/logic/operations/unary'
4
- require 'dry/logic/result'
3
+ require "dry/logic/operations/unary"
4
+ require "dry/logic/result"
5
5
 
6
6
  module Dry
7
7
  module Logic
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/binary'
4
- require 'dry/logic/result'
3
+ require "dry/logic/operations/binary"
4
+ require "dry/logic/result"
5
5
 
6
6
  module Dry
7
7
  module Logic
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/abstract'
4
- require 'dry/logic/result'
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/abstract'
3
+ require "dry/logic/operations/abstract"
4
4
 
5
5
  module Dry
6
6
  module Logic
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operations/binary'
4
- require 'dry/logic/result'
3
+ require "dry/logic/operations/binary"
4
+ require "dry/logic/result"
5
5
 
6
6
  module Dry
7
7
  module Logic
@@ -6,22 +6,22 @@ module Dry
6
6
  def and(other)
7
7
  Operations::And.new(self, other)
8
8
  end
9
- alias & and
9
+ alias_method :&, :and
10
10
 
11
11
  def or(other)
12
12
  Operations::Or.new(self, other)
13
13
  end
14
- alias | or
14
+ alias_method :|, :or
15
15
 
16
16
  def xor(other)
17
17
  Operations::Xor.new(self, other)
18
18
  end
19
- alias ^ xor
19
+ alias_method :^, :xor
20
20
 
21
21
  def then(other)
22
22
  Operations::Implication.new(self, other)
23
23
  end
24
- alias > then
24
+ alias_method :>, :then
25
25
  end
26
26
  end
27
27
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bigdecimal'
4
- require 'bigdecimal/util'
5
- require 'date'
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.kind_of?(type)
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
- begin
63
- true if Float(input)
64
- rescue ArgumentError, TypeError
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 == input.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 'inclusion is deprecated - use included_in instead.'
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 'exclusion is deprecated - use excluded_from instead.'
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
- begin
154
- if input.respond_to?(:include?)
155
- input.include?(value)
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
- if RUBY_VERSION < '2.4'
189
- def format?(regex, input)
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 = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/core/constants'
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
 
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
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'
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('::').last}#{interface.name}", klass)
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, { args: args, arity: arity, **options })
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.merge(arity: arity, parameters: parameters)
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.merge(new_opts))
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 variable_arity?
22
- define_splat_application
23
- elsif constant?
23
+ if constant?
24
24
  define_constant_application
25
25
  else
26
- define_fixed_application
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.equal?(-1)
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
- 'Constant'
58
+ "Constant"
53
59
  else
54
- arity_str = variable_arity? ? 'VariableArity' : "#{arity}Arity"
55
- curried_str = curried? ? "#{curried}Curried" : EMPTY_STRING
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
- '@arg0 = @args[0]'
81
+ "@arg0 = @args[0]"
65
82
  else
66
- "#{curried_args.join(', ')} = @args"
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 define_splat_application
95
- application =
96
- if curried?
97
- "@predicate[#{curried_args.join(', ')}, *input]"
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})