dry-logic 0.5.0 → 1.5.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 (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