0

How do I create a method dynamically with a fixed list of arguments determined at runtime by an array of symbols, or a fixed hash of named parameters? Yes, I could use a spot operator to accept any arguments, but I want to raise the same ArgumentError as in the static case whenever the arguments don't match the runtime array (or hash).

Statically, I write

 def foo(bar,baz)
   [bar,baz]
 end

 foo(1,2) #=>[1,2]
 foo(1,2,3) #=> ArgumentError: wrong number of arguments (given 3, expected 2)

Now I want to create this method at runtime. According to define_method: How to dynamically create methods with arguments we can just

 define_method(:foo) do |bar,baz|
   [bar,baz]
 end

 foo(1,2) #=> [1,2]
 foo(1,2,3) #=> ArgumentError: wrong number of arguments (given 3, expected 2)

But that only works if I know the argument list when writing the method. What if only have the argument list at runtime? At How do you pass arguments to define_method? it is suggested we can use the splat operator to accept any array of arguments:

 define_method(:foo) do |*args|
   args
 end

 foo(1,2) #=> [1,2]
 foo(1,2,3) #=> [1,2,3]

But can we fix the allowed arguments to conform to a list of arguments given at runtime, duplicate the static code, so that the following happens?

 arr = [:bar,:baz]

 define_method(:foo) do |*args=arr| #or some other magic here
   args
 end

 foo(1,2) #=> [1,2]
 foo(1,2,3) #=> ArgumentError: wrong number of arguments (given 3, expected 2)
2

1 Answer 1

0

One way I can think of is to explicitly check args.count and raise ArgumentError if the number of arguments does not match the expectation you set:

class Foo 
  def self.create_method(method_name, number_of_arguments)
    define_method(:"#{method_name}") do |*args|
      if args.count > number_of_arguments
        raise ArgumentError, "wrong number of arguments (given #{args.count}, expected #{number_of_arguments})"
      end 

      puts "Inside #{method_name}, recevied arguments: #{args.join(", ")}"  
    end 
  end 
end

Output:

Foo.create_method("yolo", 2)
Foo.new.yolo("arg1", "arg2") // Inside yolo, recevied arguments: arg1, arg2
Foo.new.yolo("arg1", "arg2", "arg3") //  wrong number of arguments (given 3, expected 2) (ArgumentError)
Sign up to request clarification or add additional context in comments.

1 Comment

And if I want to include ruby 2 keyword arguments? Yes then I could include a double-splat and check all hash keys. But I'm reimplementing a lot of complex code. Would be nice if Ruby allowed me to access the same parameter signature checks that def _method_ uses...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.