Search code examples
rubykeyword-argument

Is there a nice way to "repackage" keyword args in Ruby?


I have several methods that take many (keyword) arguments that end up passing the same set of arguments along to another method.

The following is normal.

def foo(a:, b:, c:, d:, e:)
  bar('var', a:a, b:b, c:c, d:d, e:e)
end

# takes the same arguments as #foo + one more
def bar(var, a:, b:, c:, d:, e:)
  ...
end

This is just kind of tedious and annoying. I'm wondering if there is anything in the Ruby core that would make it easy to do the following...

def foo(a:, b:, c:, d:, e:)
  bar('var', <something that automagically collects all of the keyword args>)
end

I know that you can parse over method(__method__).parameters, do some gymnastics, and package everything up into a hash that could be double-splatted and passed to bar. I'm just wondering if there is already something in the core that does that in a nice, neat way?

If there is something that applies in a more general way, i.e. not only to keyword args, then that's certainly interesting too.


Solution

  • Yes, **args will gather arbitrary keyword arguments as a Hash. Use ** again to flatten the Hash into keyword arguments for bar, Ruby 3 will no longer do this for you.

    def foo(**bar_args)
      # The ** is necessary in Ruby 3.
      bar('var', **bar_args)
    end
    
    def bar(var, a:, b:, c:, d:, e:)
      puts "#{var} #{a} #{b} #{c} #{d} #{e}"
    end
    

    This is appropriate if foo never uses those arguments, it just passes them along to bar. If foo were to use some arguments, those should be defined in foo.

    def foo(a:, **bar_args)
      puts "#{a} is for a"
      bar('var', a: a, **bar_args)
    end
    
    def bar(var, a:, b:, c:, d:, e:)
      puts "#{var} #{a} #{b} #{c} #{d} #{e}"
    end