I have rules with repeated logic in them that I would like to extract to prevent duplication. Here, I’m using values[:age] < 21
as an example of the repeated logic:
# frozen_string_literal: true
require 'dry-validation'
class MyContract < Dry::Validation::Contract
params do
required(:age).value(:integer)
optional(:rent_car).value(:bool)
optional(:rent_boat).value(:bool)
end
rule(:rent_car, :age) do
key.failure("Not old enough to rent a car") if values[:age] < 21
end
rule(:rent_boat, :age) do
key.failure("Not old enough to rent a boat") if values[:age] < 21
end
end
validation = MyContract.new.call(age: 10, rent_car: true, rent_boat: true)
p validation.errors[:rent_car]
p validation.errors[:rent_boat]
My naive attempt to define a method fails:
class MyContract < Dry::Validation::Contract
params do
required(:age).value(:integer)
optional(:rent_car).value(:bool)
optional(:rent_boat).value(:bool)
end
rule(:rent_car, :age) do
key.failure("Not old enough to rent a car") unless old_enough?
end
rule(:rent_boat, :age) do
key.failure("Not old enough to rent a boat") unless old_enough?
end
def old_enough?
values[:age] >= 21
end
end
Traceback (most recent call last):
12: from repro.rb:25:in `<main>'
11: from /.../gems/dry-validation-1.2.1/lib/dry/validation/contract.rb:94:in `call'
10: from /.../gems/dry-validation-1.2.1/lib/dry/validation/result.rb:25:in `new'
9: from /.../gems/dry-validation-1.2.1/lib/dry/validation/contract.rb:95:in `block in call'
8: from /.../gems/dry-validation-1.2.1/lib/dry/validation/contract.rb:95:in `each'
7: from /.../gems/dry-validation-1.2.1/lib/dry/validation/contract.rb:98:in `block (2 levels) in call'
6: from /.../gems/dry-validation-1.2.1/lib/dry/validation/rule.rb:38:in `call'
5: from /.../gems/dry-validation-1.2.1/lib/dry/validation/rule.rb:38:in `new'
4: from /.../gems/dry-validation-1.2.1/lib/dry/validation/evaluator.rb:73:in `initialize'
3: from /.../gems/dry-validation-1.2.1/lib/dry/validation/evaluator.rb:73:in `instance_exec'
2: from repro.rb:13:in `block in <class:MyContract>'
1: from /.../gems/dry-validation-1.2.1/lib/dry/validation/evaluator.rb:190:in `method_missing'
repro.rb:21:in `old_enough?': undefined local variable or method `values' for #<MyContract:0x00007fa3799928d8> (NameError)
What is the appropriate pattern to avoid repeating logic like this?