Introspecting Method Arguments with Method.arity

Discussion in 'Ruby' started by Paul Mucur, Mar 28, 2008.

  1. Paul Mucur

    Paul Mucur Guest

    Is there a way to return the number of required arguments, the number
    of optional arguments and whether or not a splat is present for a
    method?

    The closest I've managed to find is Method.arity which will return a
    number which indicates the number of required arguments and the
    *presence* of optional arguments only (but in a non-obvious way):

    With only required arguments, it behaves as expected:

    >> def foo(a, b); end

    => nil
    >> method:)foo).arity

    => 2

    When optional arguments are introduced however:

    >> def foo(a, b=2); end

    => nil
    >> method:)foo).arity

    => -2

    Documentation reveals that when a method has optional arguments, the
    arity is -n - 1 where n is the number of required arguments (which
    seems a little arbitrary, is there a historical reason for this?).

    However, adding new optional arguments and splats makes no difference
    to the arity:

    >> def foo(a, b=2, *c); end

    => nil
    >> method:)foo).arity

    => -2

    >> def foo(a, b=2, c=3, d=4, e=5); end

    => nil
    >> method:)foo).arity

    => -2

    As for the use of such functionality: I was expanding on Chris
    Wanstrath's excellent try method (http://ozmm.org/posts/try.html)
    which is used like Io's ? operator. For example, when chaining calls
    that might return nil, instead of doing something like:

    @order.owner && @order.owner.name

    You can do:

    @order.owner.try:)name)

    I already added the ability to pass in arguments and a block (a very
    trivial change shown at the bottom of this email) but also wanted to
    extend Chris's implementation to not only use respond_to? but to also
    check that the number of arguments is correct for a method (this
    because of things like Rails overloading DateTime's to_s method to
    accept an optional argument which would raise an ArgumentError if
    attempted on nil).

    e.g.
    @order.placed_at.to_s:)db)
    # If placed_at is nil, it still responds to to_s but we get an
    ArgumentError

    As it stands I can check if there are the correct number of arguments
    if there are no optional ones but once there are it is possible to
    specify *too many* arguments and raise an exception.

    e.g.
    @order.placed_at.try:)to_s, :db, :something_else) # ArgumentError

    You might ask why I don't simply rescue the exceptions but that would
    also rescue exceptions further down the call stack making them
    silently fail when it is only the specific method given that is to
    fail silently.

    Of course, I can work around this by avoiding such overloaded methods
    (in the case of Rails' DateTime, I can use to_formatted_s which to_s
    is an alias to) but I think it remains a valid question.

    To date the most relevant thing I have found is Mauricio Fernandez's
    "Using introspection to get method arguments and other information" http://eigenclass.org/hiki/method arguments via introspection
    but he seems to take a brute-force approach to determining arguments
    by repeatedly trying a diminishing number of arguments on a method
    (though I could be reading it wrong).

    Thanks in advance,

    -- Paul

    class Object

    # An enhanced version of Chris Wanstrath's try method.
    #
    # Due to the overloading of DateTime's to_s by Rails, this version of
    # try will also silently fail if the method exists but has not been
    # supplied the right number of arguments.
    def try(method_name, *args, &block)
    if respond_to?(method_name)
    method = method(method_name)

    sufficient_arguments = if (arity = method.arity) < 0

    # There are variable arguments present.
    #
    # The best we can do here to see if we have at least
    # the required number of arguments. However, it is possible to
    # give *too many* arguments and have this return true. There
    isn't
    # too much we can do about this though as I don't know how to
    # detect if a method has a splat (which allows for infinite
    # arguments). Though even if I could, I can't return the
    number of
    # variable arguments.
    args.length >= (arity.abs - 1)
    else

    # There are only required arguments.
    args.length == arity
    end

    if sufficient_arguments
    method.call(*args, &block)
    end
    end
    end
    end
    Paul Mucur, Mar 28, 2008
    #1
    1. Advertising

  2. Paul Mucur

    Robert Dober Guest

    AFAIK there is only the brute force approach, but it will loop forever
    if there is a splat (note that Mauricio assumes a maximum of 20
    parameters), I am afraid there is no solution in Ruby1.8 and I do not
    know if this changes in Ruby1.9?

    Robert

    --
    http://ruby-smalltalk.blogspot.com/

    ---
    Whereof one cannot speak, thereof one must be silent.
    Ludwig Wittgenstein
    Robert Dober, Mar 28, 2008
    #2
    1. Advertising

  3. Paul Mucur

    Marc Heiler Guest

    Old topic but ... has there anything changed in ruby 1.9.x ?

    It seems to work fine for small and non-complicated methods.
    --
    Posted via http://www.ruby-forum.com/.
    Marc Heiler, Dec 14, 2009
    #3
  4. Ruby 1.9.2 has Method#parameters:

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

    Ruby 1.9.1 gains this ability by installing the "methopara" gem.

    Regards,
    Florian
    Florian Gilcher, Dec 14, 2009
    #4
  5. Paul Mucur

    Roger Pack Guest


    > Documentation reveals that when a method has optional arguments, the
    > arity is -n - 1 where n is the number of required arguments (which
    > seems a little arbitrary, is there a historical reason for this?).


    I was reading something about this the other day in README.EXT -- its
    description of rb_define_method I think.

    Basically "-1 and -2 are magic numbers" denoting the type of argument
    your C method expects (an array or not). Something like that.

    > Is there a way to return the number of required arguments, the number
    > of optional arguments and whether or not a splat is present for a
    > method?


    arguments (or rdp-arguments gem) gems might be useful for you.


    [this snippet'll need to be in a file for 1.9]
    require 'rubygems'
    require 'arguments'
    class A
    def go a, b = 3, *args
    end
    end
    puts Arguments.names A, :go, false
    --
    Posted via http://www.ruby-forum.com/.
    Roger Pack, Dec 15, 2009
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Gonçalo Rodrigues

    introspecting builtin functions/methods

    Gonçalo Rodrigues, Dec 16, 2003, in forum: Python
    Replies:
    3
    Views:
    314
    Martin Franklin
    Dec 16, 2003
  2. Mark Harrison

    guide to introspecting python?

    Mark Harrison, Aug 4, 2004, in forum: Python
    Replies:
    4
    Views:
    280
    Chris Irish
    Aug 4, 2004
  3. Evan Driscoll

    Introspecting optparse/argparse objects

    Evan Driscoll, Jan 11, 2012, in forum: Python
    Replies:
    2
    Views:
    228
    Evan Driscoll
    Jan 12, 2012
  4. James Mead
    Replies:
    0
    Views:
    141
    James Mead
    Jan 15, 2008
  5. Alex Chaffee
    Replies:
    6
    Views:
    125
    Phlip
    Sep 7, 2010
Loading...

Share This Page