diff --git a/lib/dry/validation/rule.rb b/lib/dry/validation/rule.rb index e83f5403..7c10e6d5 100644 --- a/lib/dry/validation/rule.rb +++ b/lib/dry/validation/rule.rb @@ -118,12 +118,18 @@ def parse_macros(*args) args.each_with_object([]) do |spec, macros| case spec when Hash - spec.each { |k, v| macros << [k, Array(v)] } + add_macro_from_hash(macros, spec) else macros << Array(spec) end end end + + def add_macro_from_hash(macros, spec) + spec.each do |k, v| + macros << [k, v.is_a?(Array) ? v : [v]] + end + end end end end diff --git a/spec/integration/macros/custom_macros_spec.rb b/spec/integration/macros/custom_macros_spec.rb index c5f1cc70..6b1f5ef5 100644 --- a/spec/integration/macros/custom_macros_spec.rb +++ b/spec/integration/macros/custom_macros_spec.rb @@ -79,4 +79,29 @@ class Test::BaseContract < Dry::Validation::Contract; end .to eql(numbers: ['must have at least 3 items']) end end + + context 'using a macro with a range option' do + before do + Test::BaseContract.register_macro(:in_range) do |macro:| + range = macro.args[0] + + all_included_in_range = value.all? { |elem| range.include?(elem) } + key.failure("every item must be included in #{range}") unless all_included_in_range + end + + contract_class.rule(:numbers).validate(in_range: 1..3) + end + + after do + Test::BaseContract.macros._container.delete('in_range') + end + + it 'succeeds with valid input' do + expect(contract.(numbers: [1, 2, 3])).to be_success + end + + it 'fails with invalid input' do + expect(contract.(numbers: [1, 2, 6])).to be_failure + end + end end