P
Paul Mucur
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:
=> 2
When optional arguments are introduced however:
=> -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:
=> -2
=> -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.tryname)
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_sdb)
# 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.tryto_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
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:
=> 2
When optional arguments are introduced however:
=> -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:
=> -2
=> -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.tryname)
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_sdb)
# 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.tryto_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