diff --git a/CHANGELOG.md b/CHANGELOG.md index f63e69e..8d09d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## [0.12.3] - 2024-08-07 ### Changed - Renamed config option `:auto_wire_options` to `:auto_wire` at `:dry_validation` plugin +- Updated `Pathway::State#unwrap` to accept block with postional parameters - Updated `Pathway::State#unwrap` to raise an `ArgumentError` exception on invalid arguments ### Added - Provide alias `Pathway::State#use` to `Pathway::State#unwrap` diff --git a/lib/pathway.rb b/lib/pathway.rb index 4c4c508..8a8393d 100644 --- a/lib/pathway.rb +++ b/lib/pathway.rb @@ -85,16 +85,19 @@ def to_hash def use(&bl) raise ArgumentError, 'a block must be provided' if !block_given? + if bl.parameters.any? {|(type,_)| type == :keyrest || type == :rest } + raise ArgumentError, 'rest arguments are not supported' + end - params = bl.parameters + keys = bl.parameters.select {|(type,_)| type == :key || type == :keyreq }.map(&:last) + names = bl.parameters.select {|(type,_)| type == :req || type == :opt }.map(&:last) - if !params.all? { |(type,_)| [:block, :key, :keyreq, :keyrest].member?(type) } - raise ArgumentError, 'only keyword arguments are supported' - elsif params.any? {|(type,_)| type == :keyrest } - bl.call(**to_hash) - else - keys = params.select {|(type,_)| type == :key || type == :keyreq }.map(&:last) + if keys.any? && names.any? + raise ArgumentError, 'cannot mix positional and keyword arguments' + elsif keys.any? bl.call(**to_hash.slice(*keys)) + else + bl.call(*to_hash.values_at(*names)) end end diff --git a/spec/state_spec.rb b/spec/state_spec.rb index b6f3471..43b6961 100644 --- a/spec/state_spec.rb +++ b/spec/state_spec.rb @@ -34,43 +34,58 @@ class SimpleOp < Operation end context 'when a block is provided' do - it 'passes specified values by the keyword params', :aggregate_failures do + it 'passes specified values using only keyword params', :aggregate_failures do expect(state.unwrap {|val:| val }).to eq('RESULT') expect(state.unwrap {|foo:| foo }).to eq(99) expect(state.unwrap {|val:, bar:| [val, bar] }) .to eq(['RESULT', 131]) end - it 'passes all values if **kwargs is part of the params', :aggregate_failures do - expect(state.unwrap {|**kargs| kargs }) - .to eq(foo: 99, bar: 131, val: 'RESULT', input: 'some value') - expect(state.unwrap {|input:, **kargs| input }).to eq('some value') - expect(state.unwrap {|input:, **kargs| kargs }) - .to eq(foo: 99, bar: 131, val: 'RESULT') + it 'passes no arguments if no keyword or positional params are defined' do + expect(state.unwrap { 77 }).to eq(77) end - it 'passes no arguments if no keyword params are defined' do - expect(state.unwrap { 77 }).to eq(77) + it 'passes specified values using only positional params', :aggregate_failures do + expect(state.unwrap {|val| val }).to eq('RESULT') + expect(state.unwrap {|foo| foo }).to eq(99) + expect(state.unwrap {|val, bar| [val, bar] }) end - it 'fails if at least one positional param is defined', :aggregate_failures do + it 'fails if positional and keyword params are both defined', :aggregate_failures do expect { state.unwrap {|pos, input:| } } - .to raise_error('only keyword arguments are supported') - expect { state.unwrap {|input| } } - .to raise_error('only keyword arguments are supported') + .to raise_error('cannot mix positional and keyword arguments') + end + + it 'fails if using rest param', :aggregate_failures do + expect { state.unwrap {|*input| } } + .to raise_error('rest arguments are not supported') + expect { state.unwrap {|input, *args| args } } + .to raise_error('rest arguments are not supported') end - context 'and it takes a block argument' do - it 'fails if it has positional params' do - expect { state.unwrap {|input, &bl| } } - .to raise_error('only keyword arguments are supported') + it 'fails if using keyrest param', :aggregate_failures do + expect { state.unwrap {|**kargs| kargs } } + .to raise_error('rest arguments are not supported') + expect { state.unwrap {|input:, **kargs| kargs } } + .to raise_error('rest arguments are not supported') + end + + context 'that takes a block argument' do + it 'fails if it has positional and keyword params' do + expect { state.unwrap {|input, val:, &bl| } } + .to raise_error('cannot mix positional and keyword arguments') end - it 'does not fails if only keyword params', :aggregate_failures do + it 'does not fails if only has keyword params', :aggregate_failures do expect(state.unwrap {|val:, &bl| val }).to eq('RESULT') expect(state.unwrap {|val:, &_| val }).to eq('RESULT') expect(state.unwrap {|&_| 77 }).to eq(77) end + + it 'does not fails if only has positional params', :aggregate_failures do + expect(state.unwrap {|val, &bl| val }).to eq('RESULT') + expect(state.unwrap {|val, &_| val }).to eq('RESULT') + end end end