Any Ruby Library to Inspect What Are the Arguments That a Certain Methods Take

Any ruby library to inspect what are the arguments that a certain methods take?

I don't know of any third-party libraries or even RubyGems that can do this reliably. It just isn't possible to do this kind of inspection given the limited reflection facilities Ruby provides.

You will have to make do with what is available in the core library, which is basically Method#parameters:

def foo(a, b=nil, *c, d, &e); end
method(:foo).parameters
# => [[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]]

How do i get the names or arguments inside the method definition?

You can use the values directly.

def speak(name, age, address)
puts "Hello #{name}!"
end

To access the names you could use local_variables but I don't really recommend it.

def speak(name, age, address)
p local_variables # => [:name, :age, :address]
end

But most likely you'll want to use a hash:

def speak(hash)
# use the keys/values of hash
end

Now you can use

speak({:name => "foo", :age => 42, :address => "123"})
# or
speak(:name => "foo", :age => 42, :address => "123")

Is there a way to access method arguments in Ruby?

In Ruby 1.9.2 and later you can use the parameters method on a method to get the list of parameters for that method. This will return a list of pairs indicating the name of the parameter and whether it is required.

e.g.

If you do

def foo(x, y)
end

then

method(:foo).parameters # => [[:req, :x], [:req, :y]]

You can use the special variable __method__ to get the name of the current method. So within a method the names of its parameters can be obtained via

args = method(__method__).parameters.map { |arg| arg[1].to_s }

You could then display the name and value of each parameter with

logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')

Note: since this answer was originally written, in current versions of Ruby eval can no longer be called with a symbol. To address this, an explicit to_s has been added when building the list of parameter names i.e. parameters.map { |arg| arg[1].to_s }

ArgumentError: wrong number of arguments' on Ruby inspect (same code line, only for some values)

The _argument error says: given 0, expected 1 for inspect. This means that the inspect being invoked here, expects one argument. But inspect is always supposed to be invoked without arguments.

From this I conclude that the method inspect has been redefined for context.class.

UPDATE:

Now, based on the comment of the OP to my answer, I suggest the following modification the failing program:

First, we verify that the exception really comes from that invocation of inspect that we suspect; though it seems to be obvious, the devil nevers sleeps and we want to go sure. If it does, we have a closer look at the properties of the offending object:

We replace p context by

begin
# Doing an explicit `inspect` to be in control of what is going on
ic = context.inspect
puts ic
rescue ArgumentError => e
puts "Exception!"
puts e
puts "context is a #{context.class}"
puts "inspect expects: #{context.class.method(:inspect).parameters}"
end

If it is really the case, as the OP claims, that this code displays Exception!, but still shows an empty parameter array, I would report this exact example to the Ruby bug tracker.

Passing arguments to Ruby methods

Examine the AST with Ripper

In Ruby, parentheses are largely optional except when needed to avoid ambiguity, such as when passing arguments to methods that take a block. Under the hood, mainline Ruby has a lot of moving parts that tokenize and parse the code you write. One of the most useful is the Ripper module (documented here) which enables you to see the abstract syntax tree that Ruby produces from your code.

Here are two versions of your code as Ruby sees them. You can see from the S-expressions that the failing version is different from the non-failing version. In irb:

Ripper.sexp %q{x = fruits.include?(item) ? 'You picked a fruit' : 'You did not pick a fru
it'}
#=>
[:program,
[[:assign,
[:var_field, [:@ident, "x", [1, 0]]],
[:ifop,
[:method_add_arg,
[:call,
[:vcall, [:@ident, "fruits", [1, 4]]],
[:@period, ".", [1, 10]],
[:@ident, "include?", [1, 11]]],
[:arg_paren, [:args_add_block, [[:vcall, [:@ident, "item", [1, 20]]]], false]]],
[:string_literal, [:string_content, [:@tstring_content, "You picked a fruit", [1, 29]]]],
[:string_literal, [:string_content, [:@tstring_content, "You did not pick a fruit", [1, 52]]]]]]]]
Ripper.sexp %q{x = fruits.include? item ? 'You picked a fruit' : 'You did not pick a frui
t'}
#=>
[:program,
[[:assign,
[:var_field, [:@ident, "x", [1, 0]]],
[:command_call,
[:vcall, [:@ident, "fruits", [1, 4]]],
[:@period, ".", [1, 10]],
[:@ident, "include?", [1, 11]],
[:args_add_block,
[[:ifop,
[:vcall, [:@ident, "item", [1, 20]]],
[:string_literal, [:string_content, [:@tstring_content, "You picked a fruit", [1, 28]]]],
[:string_literal, [:string_content, [:@tstring_content, "You did not pick a fruit", [1, 51]]]]]],
false]]]]]

Since Ruby treats almost everything as an expression that returns a value, the order of operations can affect how the parser forms expressions. The ternary operator must ultimately evaluate as three expressions, and if you use what the parser considers ambiguous syntax it will cause problems.

See Also

  • parse.y

How to get argument names using reflection

I suggest you take a look at Merb's action-args library.

require 'rubygems'
require 'merb'

include GetArgs

def foo(bar, zed=42)
end

method(:foo).get_args # => [[[:bar], [:zed, 42]], [:zed]]

If you don't want to depend on Merb, you can choose and pick the best parts from the source code in github.



Related Topics



Leave a reply



Submit