Project

General

Profile

Feature #14079

Validate argument list without calling method

Added by nate00 (Nate Sullivan) about 2 years ago. Updated about 2 years ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:83675]

Description

I would find it useful to check whether a list of arguments matches a method signature, but without calling the method.

I'd like to check the arguments list using a method called, for example, respond_to_arguments?. Here's an example:

class Foobar
  def self.baz(str)
  end
end

# Foobar.baz accepts 1 argument, not 0 or 2:
Foobar.respond_to_arguments?(:baz, "one", "two")  # => false
Foobar.respond_to_arguments?(:baz, "one")         # => true
Foobar.respond_to_arguments?(:baz)                # => false

# Indeed, we get an ArgumentError if we pass 0 or 2 arguments:
Foobar.baz("one", "two")   # raises ArgumentError
Foobar.baz("one")          # success!
Foobar.baz                 # raises ArgumentError

My use case is a background job processing system. It works like this: I call MyWorker.perform_async with some arguments; the arguments are serialized and put into a queue; and then a background worker takes those arguments from the queue, deserializes them and passes them to MyWorker.perform. If I passed invalid arguments, I don't know they were invalid until the background worker tries to call perform. But I'd like to know immediately when I call perform_async.

Perhaps a respond_to_arguments_missing? method would be required also.

Maybe respond_to_arguments? is a bad name. You could reasonably assume that it takes the same optional second parameter as respond_to? (i.e., include_all), but my proposal doesn't support an optional second parameter.

Thank you for your consideration!

History

Updated by Eregon (Benoit Daloze) about 2 years ago

Maybe this could be some method like check_arity or so on a Method/UnboundMethod instance.
There is already Method#arity but this is limited information.

Maybe it is possible to check manually with Method#parameters, could you try that?

Furthermore, on MRI, methods defined in C often have inaccurate arity (just -1) and no #parameters instead of the actual number of arguments they accept.
But perform is a user-defined method in your use-case so this should not be a problem.

Note that String#prepend accepts multiple arguments in recent releases.

Updated by shevegen (Robert A. Heiler) about 2 years ago

Hmm. Benoit, I may have misunderstood him but I think he was not
asking for argument checks per se, but instead being able to
know whether a method will respond with an ArgumentError or
possibly other errors beforehand (such as before calling a
particular method).

If that is the idea, and if I understood it, then I think this is
an interesting idea. But I may have misunderstood.

If I understood it correctly, then Nate sort of poses the question
to ruby like in this manner:

"Hello Ruby! I want to pass this data, such as the number 5, to
that particular method. Please tell me beforehand whether this
may lead to a problem such as ArgumentError."

So if this is the case, then this is a bit the reverse way of
the more known begin/rescue style:

begin
  do_stuff
rescue TheSpecificError; end

In one case, this will usually be defined within the method
body; whereas I assume that in Nate's example, he'd want to
be able to easily query this kind of behaviour beforehand
altogether. But perhaps Nate can explain whether this is
what he meant or not.

The names are a bit verbose though; .respond_to_arguments_missing?
for example.

Anyway, I personally think the idea is ok if I understood Nate
correctly. It's a bit like introspection, but doing so
beforehand.

Updated by nate00 (Nate Sullivan) about 2 years ago

Oops, I think my description was somewhat misleading. I don't expect respond_to_arguments? to check for lines in the method body that might raise an ArgumentError. I expect it to check only whether the provided list of arguments matches the method signature.

Maybe this example will clarify:

class Foobar
  def self.hello(str)
    raise ArgumentError unless str.is_a?(String)
  end
end

Foobar.respond_to_arguments?(:hello, "goodbye")  # => true
Foobar.respond_to_arguments?(:hello, 123)  # => true, because 123 matches the method signature, even though the method will raise an ArgumentError when you call it.
Foobar.respond_to_arguments?(:hello, "goodbye", "goodbye")  # => false, because the method has only one parameter

So it's right to think of respond_to_arguments? as a method very similar to arity and parameters. Thanks for pointing out parameters, Eregon, I'll try that out.

Updated by Hanmac (Hans Mackowiak) about 2 years ago

String#prepend might be a bad example because in your cases it doesn't raise an ArgumentError anymore

Updated by nate00 (Nate Sullivan) about 2 years ago

  • Description updated (diff)

I've removed the String#prepend example from the description. Thanks for catching that, Benoit and Hans.

I agree with Benoit that an instance method on Method/UnboundMethod would be better than my respond_to_arguments? method. His proposal would work for instance methods, even if we haven't yet made an instance.

Also available in: Atom PDF