dry-logic 1.0.3 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -32
  3. data/LICENSE +1 -1
  4. data/README.md +12 -14
  5. data/dry-logic.gemspec +27 -15
  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 +33 -8
  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 +10 -139
  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/bytesize_spec.rb +0 -48
  63. data/spec/unit/predicates/case_spec.rb +0 -35
  64. data/spec/unit/predicates/date_spec.rb +0 -33
  65. data/spec/unit/predicates/date_time_spec.rb +0 -33
  66. data/spec/unit/predicates/decimal_spec.rb +0 -34
  67. data/spec/unit/predicates/empty_spec.rb +0 -40
  68. data/spec/unit/predicates/eql_spec.rb +0 -23
  69. data/spec/unit/predicates/even_spec.rb +0 -33
  70. data/spec/unit/predicates/excluded_from_spec.rb +0 -37
  71. data/spec/unit/predicates/excludes_spec.rb +0 -58
  72. data/spec/unit/predicates/false_spec.rb +0 -37
  73. data/spec/unit/predicates/filled_spec.rb +0 -40
  74. data/spec/unit/predicates/float_spec.rb +0 -33
  75. data/spec/unit/predicates/format_spec.rb +0 -23
  76. data/spec/unit/predicates/gt_spec.rb +0 -42
  77. data/spec/unit/predicates/gteq_spec.rb +0 -42
  78. data/spec/unit/predicates/hash_spec.rb +0 -40
  79. data/spec/unit/predicates/included_in_spec.rb +0 -37
  80. data/spec/unit/predicates/includes_spec.rb +0 -24
  81. data/spec/unit/predicates/int_spec.rb +0 -36
  82. data/spec/unit/predicates/key_spec.rb +0 -31
  83. data/spec/unit/predicates/lt_spec.rb +0 -42
  84. data/spec/unit/predicates/lteq_spec.rb +0 -42
  85. data/spec/unit/predicates/max_bytesize_spec.rb +0 -39
  86. data/spec/unit/predicates/max_size_spec.rb +0 -51
  87. data/spec/unit/predicates/min_bytesize_spec.rb +0 -39
  88. data/spec/unit/predicates/min_size_spec.rb +0 -51
  89. data/spec/unit/predicates/none_spec.rb +0 -30
  90. data/spec/unit/predicates/not_eql_spec.rb +0 -23
  91. data/spec/unit/predicates/number_spec.rb +0 -39
  92. data/spec/unit/predicates/odd_spec.rb +0 -33
  93. data/spec/unit/predicates/respond_to_spec.rb +0 -31
  94. data/spec/unit/predicates/size_spec.rb +0 -57
  95. data/spec/unit/predicates/str_spec.rb +0 -34
  96. data/spec/unit/predicates/time_spec.rb +0 -33
  97. data/spec/unit/predicates/true_spec.rb +0 -37
  98. data/spec/unit/predicates/type_spec.rb +0 -37
  99. data/spec/unit/predicates/uuid_v4_spec.rb +0 -29
  100. data/spec/unit/predicates_spec.rb +0 -25
  101. data/spec/unit/rule/predicate_spec.rb +0 -55
  102. data/spec/unit/rule_compiler_spec.rb +0 -129
  103. data/spec/unit/rule_spec.rb +0 -213
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/core/constants'
4
- require 'dry/equalizer'
5
- require 'dry/logic/operators'
3
+ require "dry/core/constants"
4
+ require "dry/equalizer"
5
+ require "dry/logic/operators"
6
6
 
7
7
  module Dry
8
8
  module Logic
@@ -26,15 +26,15 @@ module Dry
26
26
  end
27
27
 
28
28
  def curry(*args)
29
- new(rules.map { |rule| rule.curry(*args) }, options)
29
+ new(rules.map { |rule| rule.curry(*args) }, **options)
30
30
  end
31
31
 
32
32
  def new(rules, **new_options)
33
- self.class.new(*rules, options.merge(new_options))
33
+ self.class.new(*rules, **options, **new_options)
34
34
  end
35
35
 
36
36
  def with(new_options)
37
- new(rules, options.merge(new_options))
37
+ new(rules, **options, **new_options)
38
38
  end
39
39
 
40
40
  def to_ast
@@ -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)
@@ -147,12 +147,12 @@ module Dry
147
147
  end
148
148
 
149
149
  def inclusion?(list, input)
150
- ::Kernel.warn 'inclusion is deprecated - use included_in instead.'
150
+ ::Kernel.warn "inclusion is deprecated - use included_in instead."
151
151
  included_in?(list, input)
152
152
  end
153
153
 
154
154
  def exclusion?(list, input)
155
- ::Kernel.warn 'exclusion is deprecated - use excluded_from instead.'
155
+ ::Kernel.warn "exclusion is deprecated - use excluded_from instead."
156
156
  excluded_from?(list, input)
157
157
  end
158
158
 
@@ -199,18 +199,43 @@ module Dry
199
199
  end
200
200
 
201
201
  def format?(regex, input)
202
- regex.match?(input)
202
+ !input.nil? && regex.match?(input)
203
203
  end
204
204
 
205
205
  def case?(pattern, input)
206
206
  pattern === input
207
207
  end
208
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
+
209
224
  def uuid_v4?(input)
210
- 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
211
226
  format?(uuid_v4_format, input)
212
227
  end
213
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
+
214
239
  def respond_to?(method, input)
215
240
  input.respond_to?(method)
216
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})