2

I'm working on an upgrade from Ruby 2.7 to 3.0 and faced an issue with the keyword arguments change.

Previously we used define_method in our code for some purposes. However, with the keyword arguments change, it can no longer handle an array of arguments properly anymore.

class Foo
  def test(a: 1, b: 2)
    puts a
    puts b
  end

  old_method = instance_method(:test)

  define_method(:test) do |*args, &block|
    old_method.bind(self).call(*args, &block)
  end
end

Foo.new.test(a: 1)

This will raise wrong number of arguments (given 1, expected 0) (ArgumentError). And it previously worked in Ruby 2.7. Is there anything we can do to get the *args to work again?

Marlin Pierce
  • 9,931
  • 4
  • 30
  • 52
wwang
  • 59
  • 3

1 Answers1

5

Try

  define_method(:test) do |*args, **kwords, &block|
    old_method.bind(self).call(*args, **kwords, &block)
  end

Ruby 3.0 is changing the handling of method arguments which give a deprecation warning in 2.7. There is some unexpected behavior including a keywords in a *args array. This had to be done using a hash and there needs to be more of a distinction between a hash argument and keywords. So in ruby 3.x you need to capture an arbitrary collection of keywords in a double splat because they cannot be included in the splat argument.

Mostly my understanding is shaky but I read this as an explicit example in the explanation of the changed to the arguments in the release notes for ruby 3.0.

Marlin Pierce
  • 9,931
  • 4
  • 30
  • 52
  • [Official Explanation](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/) Scroll down to "Handling argument delegation" – engineersmnky Apr 28 '21 at 19:12
  • @MarlinPierce: I see why your solution is correct. The OP claims that his buggy solution had worked before Ruby 2.7. Do you have an explanation for this? – user1934428 Apr 29 '21 at 08:07
  • @user1934428 Good point. I'm glad I waited to see if it worked first though. My understanding is a little shaky but I think I recall this as an explicit example in the documentation for the change. – Marlin Pierce Apr 29 '21 at 13:50