Reputation: 113
Because there are a few different kinds of method parameters in Ruby (required, default, keyword, variable length...), sometimes determining how actual arguments will be bound to formal parameters can be tricky.
I'm wondering if there's a way to determine what this binding will be without actually calling the method. For example, for the following method A#foo
:
class A
def foo(a, *b, c)
...
end
end
I would like a method like determine_binding
that we can use as follows:
A.instance_method(:foo).determine_binding(1,2,3,4,5) ## returns { a: 1, b: [2,3,4], c: 5 }
That is, determine_binding
takes a list of arguments and determines the formal binding to foo
's parameters, without actually having to call foo
. Is there something like this (or similar) in Ruby?
Upvotes: 1
Views: 175
Reputation: 5363
So close. You're looking for Method#parameters
A.instance_method(:foo).parameters => [[:req, :a], [:rest, :b], [:req, :c]]
See the "Method" documentation for more information.
Upvotes: 2
Reputation: 121000
You might go with a tiny DSL for that purpose.
module Watcher
def self.prepended(base)
base.instance_methods(false).map do |m|
mthd = base.instance_method(m)
names = mthd.parameters.map(&:last).map(&:to_s)
values = names.join(", ")
params =
mthd.parameters.map do |type, name|
case type
when :req then name.to_s
when :rest then "*#{name}"
when :keyrest then "**#{name}"
when :block then "&#{name}"
end
end.join(", ")
base.class_eval """
def #{m}(#{params})
#{names}.zip([#{values}]).to_h
end
"""
end
end
end
class A; def zoo(a, *b, c); 42; end; end
A.new.zoo(1,2,3,4,5)
#⇒ 42
A.prepend Watcher
A.new.zoo(1,2,3,4,5)
#⇒ {"a"=>1, "b"=>[2, 3, 4], "c"=>5}
Upvotes: 3