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)
ziggurism
  • 2,264
  • 2
  • 16
  • 23

1 Answers1

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)
progfan
  • 2,454
  • 3
  • 22
  • 28
  • 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... – ziggurism Jun 28 '17 at 09:34