Skip to content

Commit

Permalink
Handle keyword arguments in a Ruby 3.0 world.
Browse files Browse the repository at this point in the history
Now that keyword arguments are no longer implicitly translated into hashes, we need to explicitly handle them as a separate argument type.

This commit may not cover all cases (either from a code or spec perspective), but hopefully it’s at least a useful start.
  • Loading branch information
pat committed Mar 10, 2021
1 parent df19988 commit 05934f7
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 8 deletions.
20 changes: 14 additions & 6 deletions lib/dry/transformer/function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ class Function
# @api private
attr_reader :args

# Additional keyword arguments that will be passed to the wrapped proc
#
# @return [Hash]
#
# @api private
attr_reader :kwargs

# @!attribute [r] name
#
# @return [<type] The name of the function
Expand All @@ -36,6 +43,7 @@ class Function
def initialize(fn, options = {})
@fn = fn
@args = options.fetch(:args, [])
@kwargs = options.fetch(:kwargs, {})
@name = options.fetch(:name, fn)
end

Expand All @@ -46,8 +54,8 @@ def initialize(fn, options = {})
# @alias []
#
# @api public
def call(*value)
fn.call(*value, *args)
def call(*value, **additional_kwargs)
fn.call(*value, *args, **(kwargs).merge(additional_kwargs))
end
alias_method :[], :call

Expand All @@ -71,15 +79,15 @@ def compose(other)
# @return [Function]
#
# @api private
def with(*args)
self.class.new(fn, name: name, args: args)
def with(*args, **additional_kwargs)
self.class.new(fn, name: name, args: args, kwargs: kwargs.merge(additional_kwargs))
end

# @api public
def ==(other)
return false unless other.instance_of?(self.class)

[fn, name, args] == [other.fn, other.name, other.args]
[fn, name, args, kwargs] == [other.fn, other.name, other.args, other.kwargs]
end
alias_method :eql?, :==

Expand All @@ -99,7 +107,7 @@ def to_ast
#
def to_proc
if !args.empty?
proc { |*value| fn.call(*value, *args) }
proc { |*value| fn.call(*value, *args, **kwargs) }
else
fn.to_proc
end
Expand Down
4 changes: 2 additions & 2 deletions lib/dry/transformer/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ module Registry
#
# @alias :t
#
def [](fn, *args)
def [](fn, *args, **kwargs)
fetched = fetch(fn)

return Function.new(fetched, args: args, name: fn) unless already_wrapped?(fetched)
return Function.new(fetched, args: args, kwargs: kwargs, name: fn) unless already_wrapped?(fetched)

args.empty? ? fetched : fetched.with(*args)
end
Expand Down
14 changes: 14 additions & 0 deletions spec/unit/registry_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
def self.prefix(value, prefix)
"#{prefix}_#{value}"
end

def self.trim(value, limit:)
value[0..(limit - 1)]
end
end
end

Expand All @@ -32,6 +36,16 @@ def self.prefix(value, prefix)
expect(transproc["qux"]).to eql "baz_qux"
end
end

context "with keyword arguments" do
it "passes keywords through" do
expect(foo[:trim]["string", limit: 3]).to eql("str")
end

it "works with a transproc approach" do
expect(foo[:trim, limit: 2]["string"]).to eql("st")
end
end
end

describe ".t" do
Expand Down

0 comments on commit 05934f7

Please sign in to comment.