dry-logic 0.5.0 → 1.5.0

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 +222 -17
  3. data/LICENSE +1 -1
  4. data/README.md +11 -20
  5. data/dry-logic.gemspec +36 -20
  6. data/lib/dry/logic/appliable.rb +2 -0
  7. data/lib/dry/logic/builder.rb +88 -0
  8. data/lib/dry/logic/evaluator.rb +1 -1
  9. data/lib/dry/logic/operations/abstract.rb +4 -6
  10. data/lib/dry/logic/operations/and.rb +12 -3
  11. data/lib/dry/logic/operations/attr.rb +1 -1
  12. data/lib/dry/logic/operations/binary.rb +4 -3
  13. data/lib/dry/logic/operations/check.rb +3 -5
  14. data/lib/dry/logic/operations/each.rb +1 -2
  15. data/lib/dry/logic/operations/implication.rb +1 -2
  16. data/lib/dry/logic/operations/key.rb +3 -5
  17. data/lib/dry/logic/operations/negation.rb +1 -2
  18. data/lib/dry/logic/operations/or.rb +1 -2
  19. data/lib/dry/logic/operations/set.rb +3 -4
  20. data/lib/dry/logic/operations/unary.rb +1 -1
  21. data/lib/dry/logic/operations/xor.rb +1 -2
  22. data/lib/dry/logic/operators.rb +6 -4
  23. data/lib/dry/logic/predicates.rb +110 -28
  24. data/lib/dry/logic/result.rb +2 -4
  25. data/lib/dry/logic/rule/interface.rb +143 -0
  26. data/lib/dry/logic/rule/predicate.rb +23 -17
  27. data/lib/dry/logic/rule.rb +31 -31
  28. data/lib/dry/logic/rule_compiler.rb +2 -7
  29. data/lib/dry/logic/version.rb +3 -1
  30. data/lib/dry/logic.rb +21 -5
  31. data/lib/dry-logic.rb +3 -1
  32. metadata +27 -143
  33. data/.codeclimate.yml +0 -23
  34. data/.gitignore +0 -7
  35. data/.rspec +0 -3
  36. data/.rubocop.yml +0 -16
  37. data/.rubocop_todo.yml +0 -7
  38. data/.travis.yml +0 -30
  39. data/CONTRIBUTING.md +0 -29
  40. data/Gemfile +0 -17
  41. data/Rakefile +0 -12
  42. data/bin/console +0 -10
  43. data/examples/basic.rb +0 -14
  44. data/lib/dry/logic/operations.rb +0 -13
  45. data/spec/integration/result_spec.rb +0 -59
  46. data/spec/integration/rule_spec.rb +0 -53
  47. data/spec/shared/predicates.rb +0 -57
  48. data/spec/shared/rule.rb +0 -67
  49. data/spec/spec_helper.rb +0 -34
  50. data/spec/support/mutant.rb +0 -9
  51. data/spec/unit/operations/and_spec.rb +0 -64
  52. data/spec/unit/operations/attr_spec.rb +0 -27
  53. data/spec/unit/operations/check_spec.rb +0 -49
  54. data/spec/unit/operations/each_spec.rb +0 -47
  55. data/spec/unit/operations/implication_spec.rb +0 -30
  56. data/spec/unit/operations/key_spec.rb +0 -133
  57. data/spec/unit/operations/negation_spec.rb +0 -49
  58. data/spec/unit/operations/or_spec.rb +0 -73
  59. data/spec/unit/operations/set_spec.rb +0 -41
  60. data/spec/unit/operations/xor_spec.rb +0 -61
  61. data/spec/unit/predicates/array_spec.rb +0 -41
  62. data/spec/unit/predicates/attr_spec.rb +0 -29
  63. data/spec/unit/predicates/bool_spec.rb +0 -34
  64. data/spec/unit/predicates/case_spec.rb +0 -33
  65. data/spec/unit/predicates/date_spec.rb +0 -31
  66. data/spec/unit/predicates/date_time_spec.rb +0 -31
  67. data/spec/unit/predicates/decimal_spec.rb +0 -32
  68. data/spec/unit/predicates/empty_spec.rb +0 -38
  69. data/spec/unit/predicates/eql_spec.rb +0 -21
  70. data/spec/unit/predicates/even_spec.rb +0 -31
  71. data/spec/unit/predicates/excluded_from_spec.rb +0 -35
  72. data/spec/unit/predicates/excludes_spec.rb +0 -56
  73. data/spec/unit/predicates/false_spec.rb +0 -35
  74. data/spec/unit/predicates/filled_spec.rb +0 -38
  75. data/spec/unit/predicates/float_spec.rb +0 -31
  76. data/spec/unit/predicates/format_spec.rb +0 -21
  77. data/spec/unit/predicates/gt_spec.rb +0 -40
  78. data/spec/unit/predicates/gteq_spec.rb +0 -40
  79. data/spec/unit/predicates/included_in_spec.rb +0 -35
  80. data/spec/unit/predicates/includes_spec.rb +0 -24
  81. data/spec/unit/predicates/int_spec.rb +0 -34
  82. data/spec/unit/predicates/key_spec.rb +0 -29
  83. data/spec/unit/predicates/lt_spec.rb +0 -40
  84. data/spec/unit/predicates/lteq_spec.rb +0 -40
  85. data/spec/unit/predicates/max_size_spec.rb +0 -49
  86. data/spec/unit/predicates/min_size_spec.rb +0 -49
  87. data/spec/unit/predicates/none_spec.rb +0 -28
  88. data/spec/unit/predicates/not_eql_spec.rb +0 -21
  89. data/spec/unit/predicates/number_spec.rb +0 -37
  90. data/spec/unit/predicates/odd_spec.rb +0 -31
  91. data/spec/unit/predicates/size_spec.rb +0 -55
  92. data/spec/unit/predicates/str_spec.rb +0 -32
  93. data/spec/unit/predicates/time_spec.rb +0 -31
  94. data/spec/unit/predicates/true_spec.rb +0 -35
  95. data/spec/unit/predicates/type_spec.rb +0 -35
  96. data/spec/unit/predicates_spec.rb +0 -23
  97. data/spec/unit/rule/predicate_spec.rb +0 -53
  98. data/spec/unit/rule_compiler_spec.rb +0 -127
  99. data/spec/unit/rule_spec.rb +0 -141
@@ -1,6 +1,4 @@
1
- require 'dry/logic/operations/unary'
2
- require 'dry/logic/evaluator'
3
- require 'dry/logic/result'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Dry
6
4
  module Logic
@@ -10,13 +8,13 @@ module Dry
10
8
 
11
9
  attr_reader :path
12
10
 
13
- def self.new(rules, options)
11
+ def self.new(rules, **options)
14
12
  if options[:evaluator]
15
13
  super
16
14
  else
17
15
  name = options.fetch(:name)
18
16
  eval = options.fetch(:evaluator, evaluator(name))
19
- super(rules, options.merge(evaluator: eval, path: name))
17
+ super(rules, **options, evaluator: eval, path: name)
20
18
  end
21
19
  end
22
20
 
@@ -1,5 +1,4 @@
1
- require 'dry/logic/operations/unary'
2
- require 'dry/logic/result'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Dry
5
4
  module Logic
@@ -1,5 +1,4 @@
1
- require 'dry/logic/operations/binary'
2
- require 'dry/logic/result'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Dry
5
4
  module Logic
@@ -1,5 +1,4 @@
1
- require 'dry/logic/operations/abstract'
2
- require 'dry/logic/result'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Dry
5
4
  module Logic
@@ -14,7 +13,7 @@ module Dry
14
13
  success = results.all?(&:success?)
15
14
 
16
15
  Result.new(success, id) do
17
- [type, results.select(&:failure?).map { |failure| failure.to_ast }]
16
+ [type, results.select(&:failure?).map(&:to_ast)]
18
17
  end
19
18
  end
20
19
 
@@ -27,7 +26,7 @@ module Dry
27
26
  end
28
27
 
29
28
  def to_s
30
- "#{type}(#{rules.map(&:to_s).join(', ')})"
29
+ "#{type}(#{rules.map(&:to_s).join(", ")})"
31
30
  end
32
31
  end
33
32
  end
@@ -1,4 +1,4 @@
1
- require 'dry/logic/operations/abstract'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Dry
4
4
  module Logic
@@ -1,5 +1,4 @@
1
- require 'dry/logic/operations/binary'
2
- require 'dry/logic/result'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Dry
5
4
  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,17 +1,40 @@
1
- require 'bigdecimal'
2
- require 'bigdecimal/util'
3
- require 'date'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/constants"
4
+
5
+ require "bigdecimal"
6
+ require "bigdecimal/util"
7
+ require "date"
4
8
 
5
9
  module Dry
6
10
  module Logic
7
11
  module Predicates
12
+ include Dry::Core::Constants
13
+
14
+ # rubocop:disable Metrics/ModuleLength
8
15
  module Methods
16
+ def self.uuid_format(version)
17
+ ::Regexp.new(<<~FORMAT.chomp, ::Regexp::IGNORECASE)
18
+ \\A[0-9A-F]{8}-[0-9A-F]{4}-#{version}[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\\z
19
+ FORMAT
20
+ end
21
+
22
+ UUIDv1 = uuid_format(1)
23
+
24
+ UUIDv2 = uuid_format(2)
25
+
26
+ UUIDv3 = uuid_format(3)
27
+
28
+ UUIDv4 = uuid_format(4)
29
+
30
+ UUIDv5 = uuid_format(5)
31
+
9
32
  def [](name)
10
33
  method(name)
11
34
  end
12
35
 
13
36
  def type?(type, input)
14
- input.kind_of?(type)
37
+ input.is_a?(type)
15
38
  end
16
39
 
17
40
  def nil?(input)
@@ -57,11 +80,9 @@ module Dry
57
80
  end
58
81
 
59
82
  def number?(input)
60
- begin
61
- true if Float(input)
62
- rescue ArgumentError, TypeError
63
- false
64
- end
83
+ true if Float(input)
84
+ rescue ArgumentError, TypeError
85
+ false
65
86
  end
66
87
 
67
88
  def int?(input)
@@ -114,7 +135,7 @@ module Dry
114
135
 
115
136
  def size?(size, input)
116
137
  case size
117
- when Integer then size == input.size
138
+ when Integer then size.equal?(input.size)
118
139
  when Range, Array then size.include?(input.size)
119
140
  else
120
141
  raise ArgumentError, "+#{size}+ is not supported type for size? predicate."
@@ -129,13 +150,30 @@ module Dry
129
150
  input.size <= num
130
151
  end
131
152
 
153
+ def bytesize?(size, input)
154
+ case size
155
+ when Integer then size.equal?(input.bytesize)
156
+ when Range, Array then size.include?(input.bytesize)
157
+ else
158
+ raise ArgumentError, "+#{size}+ is not supported type for bytesize? predicate."
159
+ end
160
+ end
161
+
162
+ def min_bytesize?(num, input)
163
+ input.bytesize >= num
164
+ end
165
+
166
+ def max_bytesize?(num, input)
167
+ input.bytesize <= num
168
+ end
169
+
132
170
  def inclusion?(list, input)
133
- ::Kernel.warn 'inclusion is deprecated - use included_in instead.'
171
+ deprecated(:inclusion?, :included_in?)
134
172
  included_in?(list, input)
135
173
  end
136
174
 
137
175
  def exclusion?(list, input)
138
- ::Kernel.warn 'exclusion is deprecated - use excluded_from instead.'
176
+ deprecated(:exclusion?, :excluded_from?)
139
177
  excluded_from?(list, input)
140
178
  end
141
179
 
@@ -148,22 +186,23 @@ module Dry
148
186
  end
149
187
 
150
188
  def includes?(value, input)
151
- begin
152
- if input.respond_to?(:include?)
153
- input.include?(value)
154
- else
155
- false
156
- end
157
- rescue TypeError
189
+ if input.respond_to?(:include?)
190
+ input.include?(value)
191
+ else
158
192
  false
159
193
  end
194
+ rescue TypeError
195
+ false
160
196
  end
161
197
 
162
198
  def excludes?(value, input)
163
199
  !includes?(value, input)
164
200
  end
165
201
 
166
- def eql?(left, right)
202
+ # This overrides Object#eql? so we need to make it compatible
203
+ def eql?(left, right = Undefined)
204
+ return super(left) if right.equal?(Undefined)
205
+
167
206
  left.eql?(right)
168
207
  end
169
208
 
@@ -183,23 +222,65 @@ module Dry
183
222
  value.equal?(false)
184
223
  end
185
224
 
186
- if RUBY_VERSION < '2.4'
187
- def format?(regex, input)
188
- !regex.match(input).nil?
189
- end
190
- else
191
- def format?(regex, input)
192
- regex.match?(input)
193
- end
225
+ def format?(regex, input)
226
+ !input.nil? && regex.match?(input)
194
227
  end
195
228
 
196
229
  def case?(pattern, input)
230
+ # rubocop:disable Style/CaseEquality
197
231
  pattern === input
232
+ # rubocop:enable Style/CaseEquality
233
+ end
234
+
235
+ def uuid_v1?(input)
236
+ format?(UUIDv1, input)
237
+ end
238
+
239
+ def uuid_v2?(input)
240
+ format?(UUIDv2, input)
241
+ end
242
+
243
+ def uuid_v3?(input)
244
+ format?(UUIDv3, input)
245
+ end
246
+
247
+ def uuid_v4?(input)
248
+ format?(UUIDv4, input)
249
+ end
250
+
251
+ def uuid_v5?(input)
252
+ format?(UUIDv5, input)
253
+ end
254
+
255
+ def uri?(schemes, input)
256
+ uri_format = URI::DEFAULT_PARSER.make_regexp(schemes)
257
+ format?(uri_format, input)
258
+ end
259
+
260
+ def uri_rfc3986?(input)
261
+ format?(URI::RFC3986_Parser::RFC3986_URI, input)
262
+ end
263
+
264
+ # This overrides Object#respond_to? so we need to make it compatible
265
+ def respond_to?(method, input = Undefined)
266
+ return super if input.equal?(Undefined)
267
+
268
+ input.respond_to?(method)
198
269
  end
199
270
 
200
271
  def predicate(name, &block)
201
272
  define_singleton_method(name, &block)
202
273
  end
274
+
275
+ def deprecated(name, in_favor_of)
276
+ Core::Deprecations.warn(
277
+ "#{name} predicate is deprecated and will " \
278
+ "be removed in the next major version\n" \
279
+ "Please use #{in_favor_of} predicate instead",
280
+ tag: "dry-logic",
281
+ uplevel: 3
282
+ )
283
+ end
203
284
  end
204
285
 
205
286
  extend Methods
@@ -209,5 +290,6 @@ module Dry
209
290
  other.extend(Methods)
210
291
  end
211
292
  end
293
+ # rubocop:enable Metrics/ModuleLength
212
294
  end
213
295
  end
@@ -1,10 +1,8 @@
1
- require 'dry/core/constants'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Dry
4
4
  module Logic
5
5
  class Result
6
- include Core::Constants
7
-
8
6
  SUCCESS = Class.new {
9
7
  def success?
10
8
  true
@@ -67,7 +65,7 @@ module Dry
67
65
  if args.empty?
68
66
  name.to_s
69
67
  else
70
- "#{name}(#{args.map(&:last).map(&:inspect).join(', ')})"
68
+ "#{name}(#{args.map(&:last).map(&:inspect).join(", ")})"
71
69
  end
72
70
  end
73
71
 
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Logic
5
+ class Rule
6
+ class Interface < ::Module
7
+ SPLAT = ["*rest"].freeze
8
+
9
+ attr_reader :arity
10
+
11
+ attr_reader :curried
12
+
13
+ def initialize(arity, curried)
14
+ super()
15
+
16
+ @arity = arity
17
+ @curried = curried
18
+
19
+ if !variable_arity? && curried > arity
20
+ raise ArgumentError, "wrong number of arguments (#{curried} for #{arity})"
21
+ end
22
+
23
+ define_constructor if curried?
24
+
25
+ if constant?
26
+ define_constant_application
27
+ else
28
+ define_application
29
+ end
30
+ end
31
+
32
+ def constant?
33
+ arity.zero?
34
+ end
35
+
36
+ def variable_arity?
37
+ arity.negative?
38
+ end
39
+
40
+ def curried?
41
+ !curried.zero?
42
+ end
43
+
44
+ def unapplied
45
+ if variable_arity?
46
+ unapplied = arity.abs - 1 - curried
47
+
48
+ if unapplied.negative?
49
+ 0
50
+ else
51
+ unapplied
52
+ end
53
+ else
54
+ arity - curried
55
+ end
56
+ end
57
+
58
+ def name
59
+ if constant?
60
+ "Constant"
61
+ else
62
+ arity_str =
63
+ if variable_arity?
64
+ "Variable#{arity.abs - 1}Arity"
65
+ else
66
+ "#{arity}Arity"
67
+ end
68
+
69
+ curried_str =
70
+ if curried?
71
+ "#{curried}Curried"
72
+ else
73
+ EMPTY_STRING
74
+ end
75
+
76
+ "#{arity_str}#{curried_str}"
77
+ end
78
+ end
79
+
80
+ def define_constructor
81
+ assignment =
82
+ if curried.equal?(1)
83
+ "@arg0 = @args[0]"
84
+ else
85
+ "#{curried_args.join(", ")} = @args"
86
+ end
87
+
88
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
89
+ def initialize(*) # def initialize(*)
90
+ super # super
91
+ #
92
+ #{assignment} # @arg0 = @args[0]
93
+ end # end
94
+ RUBY
95
+ end
96
+
97
+ def define_constant_application
98
+ module_exec do
99
+ def call(*)
100
+ if @predicate[]
101
+ Result::SUCCESS
102
+ else
103
+ Result.new(false, id) { ast }
104
+ end
105
+ end
106
+
107
+ def [](*)
108
+ @predicate[]
109
+ end
110
+ end
111
+ end
112
+
113
+ def define_application
114
+ splat = variable_arity? ? SPLAT : EMPTY_ARRAY
115
+ parameters = (unapplied_args + splat).join(", ")
116
+ application = "@predicate[#{(curried_args + unapplied_args + splat).join(", ")}]"
117
+
118
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
119
+ def call(#{parameters}) # def call(input0, input1, *rest)
120
+ if #{application} # if @predicate[@arg0, @arg1, input0, input1, *rest]
121
+ Result::SUCCESS # ::Dry::Logic::Result::Success
122
+ else # else
123
+ Result.new(false, id) { ast(#{parameters}) } # ::Dry::Logic::Result.new(false, id) { ast(input0, input1, *rest) }
124
+ end # end
125
+ end # end
126
+ #
127
+ def [](#{parameters}) # def [](@arg0, @arg1, input0, input1, *rest)
128
+ #{application} # @predicate[@arg0, @arg1, input0, input1, *rest]
129
+ end # end
130
+ RUBY
131
+ end
132
+
133
+ def curried_args
134
+ @curried_args ||= ::Array.new(curried) { |i| "@arg#{i}" }
135
+ end
136
+
137
+ def unapplied_args
138
+ @unapplied_args ||= ::Array.new(unapplied) { |i| "input#{i}" }
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -1,28 +1,34 @@
1
- require 'dry/logic/rule'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Dry
4
4
  module Logic
5
- class Rule::Predicate < Rule
6
- def type
7
- :predicate
8
- end
5
+ class Rule
6
+ class Predicate < Rule
7
+ def self.specialize(arity, curried, base = Predicate)
8
+ super
9
+ end
9
10
 
10
- def name
11
- predicate.name
12
- end
11
+ def type
12
+ :predicate
13
+ end
13
14
 
14
- def to_s
15
- if args.size > 0
16
- "#{name}(#{args.map(&:inspect).join(', ')})"
17
- else
18
- "#{name}"
15
+ def name
16
+ predicate.name
17
+ end
18
+
19
+ def to_s
20
+ if args.empty?
21
+ name.to_s
22
+ else
23
+ "#{name}(#{args.map(&:inspect).join(", ")})"
24
+ end
19
25
  end
20
- end
21
26
 
22
- def ast(input = Undefined)
23
- [type, [name, args_with_names(input)]]
27
+ def ast(input = Undefined)
28
+ [type, [name, args_with_names(input)]]
29
+ end
30
+ alias_method :to_ast, :ast
24
31
  end
25
- alias_method :to_ast, :ast
26
32
  end
27
33
  end
28
34
  end
@@ -1,15 +1,14 @@
1
- require 'dry/core/constants'
2
- require 'dry/equalizer'
3
- require 'dry/logic/operations'
4
- require 'dry/logic/result'
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
5
4
 
6
5
  module Dry
7
6
  module Logic
8
7
  def self.Rule(*args, **options, &block)
9
8
  if args.any?
10
- Rule.new(*args, Rule::DEFAULT_OPTIONS.merge(options))
9
+ Rule.build(*args, **options)
11
10
  elsif block
12
- Rule.new(block, Rule::DEFAULT_OPTIONS.merge(options))
11
+ Rule.build(block, **options)
13
12
  end
14
13
  end
15
14
 
@@ -18,8 +17,6 @@ module Dry
18
17
  include Dry::Equalizer(:predicate, :options)
19
18
  include Operators
20
19
 
21
- DEFAULT_OPTIONS = { args: [].freeze }.freeze
22
-
23
20
  attr_reader :predicate
24
21
 
25
22
  attr_reader :options
@@ -28,10 +25,27 @@ module Dry
28
25
 
29
26
  attr_reader :arity
30
27
 
31
- def initialize(predicate, options = DEFAULT_OPTIONS)
28
+ def self.interfaces
29
+ @interfaces ||= ::Concurrent::Map.new
30
+ end
31
+
32
+ def self.specialize(arity, curried, base = Rule)
33
+ base.interfaces.fetch_or_store([arity, curried]) do
34
+ interface = Interface.new(arity, curried)
35
+ klass = Class.new(base) { include interface }
36
+ base.const_set("#{base.name.split("::").last}#{interface.name}", klass)
37
+ klass
38
+ end
39
+ end
40
+
41
+ def self.build(predicate, args: EMPTY_ARRAY, arity: predicate.arity, **options)
42
+ specialize(arity, args.size).new(predicate, {args: args, arity: arity, **options})
43
+ end
44
+
45
+ def initialize(predicate, options = EMPTY_HASH)
32
46
  @predicate = predicate
33
47
  @options = options
34
- @args = options[:args]
48
+ @args = options[:args] || EMPTY_ARRAY
35
49
  @arity = options[:arity] || predicate.arity
36
50
  end
37
51
 
@@ -43,41 +57,27 @@ module Dry
43
57
  options[:id]
44
58
  end
45
59
 
46
- def call(*input)
47
- Result.new(self[*input], id) { ast(*input) }
48
- end
49
-
50
- def [](*input)
51
- arity == 0 ? predicate.() : predicate[*args, *input]
52
- end
53
-
54
60
  def curry(*new_args)
55
- all_args = args + new_args
56
-
57
- if all_args.size > arity
58
- raise ArgumentError, "wrong number of arguments (#{all_args.size} for #{arity})"
59
- else
60
- with(args: all_args)
61
- end
61
+ with(args: args + new_args)
62
62
  end
63
63
 
64
64
  def bind(object)
65
- if UnboundMethod === predicate
66
- self.class.new(predicate.bind(object), options)
65
+ if predicate.respond_to?(:bind)
66
+ self.class.build(predicate.bind(object), **options)
67
67
  else
68
- self.class.new(
68
+ self.class.build(
69
69
  -> *args { object.instance_exec(*args, &predicate) },
70
- options.merge(arity: arity, parameters: parameters)
70
+ **options, arity: arity, parameters: parameters
71
71
  )
72
72
  end
73
73
  end
74
74
 
75
75
  def eval_args(object)
76
- with(args: args.map { |arg| UnboundMethod === arg ? arg.bind(object).() : arg })
76
+ with(args: args.map { |arg| arg.is_a?(UnboundMethod) ? arg.bind(object).() : arg })
77
77
  end
78
78
 
79
79
  def with(new_opts)
80
- self.class.new(predicate, options.merge(new_opts))
80
+ self.class.build(predicate, **options, **new_opts)
81
81
  end
82
82
 
83
83
  def parameters
@@ -1,13 +1,8 @@
1
- require 'dry/core/constants'
2
-
3
- require 'dry/logic/rule'
4
- require 'dry/logic/rule/predicate'
1
+ # frozen_string_literal: true
5
2
 
6
3
  module Dry
7
4
  module Logic
8
5
  class RuleCompiler
9
- include Core::Constants
10
-
11
6
  attr_reader :predicates
12
7
 
13
8
  def initialize(predicates)
@@ -52,7 +47,7 @@ module Dry
52
47
 
53
48
  def visit_predicate(node)
54
49
  name, params = node
55
- predicate = Rule::Predicate.new(predicates[name])
50
+ predicate = Rule::Predicate.build(predicates[name])
56
51
 
57
52
  if params.size > 1
58
53
  args = params.map(&:last).reject { |val| val == Undefined }
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Logic
3
- VERSION = '0.5.0'.freeze
5
+ VERSION = "1.5.0"
4
6
  end
5
7
  end