Recently I found myself needing to programmatically access arguments
passed to a method and after some searching found a somewhat convoluted
solution (added below).
Is there a built-in ruby variable via $<something>
, __something__
or
similar less-known approach that lets one access arguments passed to a
method?
# Known method arguments as of 2.4.1
# 1. mandatory argument
# 2. optional argument
# 3. splatted argument list
# 4. mandatory keyword argument
# 5. optional keyword argument
# 6. splatted keyword arguments
# 7. block
def frankenstein(mandatory, optional=nil, *argument_list,
mandatory_keyword:, optional_keyword: nil, **keyword_list)
naive_param_hash = method(__method__).parameters.inject({}){|mem, arr|
mem[arr[1].to_sym] = binding.local_variable_get(arr[1]) ; mem}
#=> {
# :mandatory=>"mandatory",
# :optional=>nil,
# :argument_list=>["argument_in_list_1", "argument_in_list_2"],
# :mandatory_keyword=>"mandatory_keyword",
# :optional_keyword=>nil,
# :keyword_list=>{:keyword_list_one=>"first",
:keyword_list_two=>"second"}
# }
descriptive_param_hash = method(__method__).parameters.inject({})do
|mem, arr|
arg_type = arr[0].to_sym
arg_name = arr[1].to_sym
arg_value = binding.local_variable_get(arg_name)
current_parameter_hash = {arg_name => arg_value}
mem[arg_type].respond_to?(:merge!) ?
mem[arg_type].merge!(current_parameter_hash) : (mem[arg_type] =
current_parameter_hash)
mem
end
#=> {
# :req=>{:mandatory=>"mandatory"},
# :opt=>{:optional=>nil},
# :rest=>{:argument_list=>["argument_in_list_1",
"argument_in_list_2"]},
# :keyreq=>{:mandatory_keyword=>"mandatory_keyword"},
# :key=>{:optional_keyword=>nil},
# :keyrest=> {
# :keyword_list=> {
# :keyword_list_one=>"first", :keyword_list_two=>"second"
# }
# }
# }
optimized_descriptive_param_hash =
method(__method__).parameters.inject({})do |mem, arr|
arg_type = arr[0].to_sym
arg_name = arr[1].to_sym
arg_value = binding.local_variable_get(arg_name)
current_parameter_hash = {arg_name => arg_value}
case arg_type
when :rest, :keyrest
# since these keys will definitely only appear once, no sense to
have a subhash, instead, use the method-definded key name
mem[arg_name] = arg_value
else
mem[arg_type].respond_to?(:merge!) ?
mem[arg_type].merge!(current_parameter_hash) : (mem[arg_type] =
current_parameter_hash)
end
mem
end
#=> {
# :req=>{:mandatory=>"mandatory"},
# :opt=>{:optional=>nil},
# :argument_list=>["argument_in_list_1", "argument_in_list_2"],
# :keyreq=>{:mandatory_keyword=>"mandatory_keyword"},
# :key=>{:optional_keyword=>nil},
# :keyword_list=>{
# :keyword_list_one=>"first", :keyword_list_two=>"second"
# }
# }
end
frankenstein("mandatory", nil, "argument_in_list_1",
"argument_in_list_2", mandatory_keyword: "mandatory_keyword",
keyword_list_one: "first", keyword_list_two: "second")