こんにちは、id:daido1976 です。
Ruby 3.0 の「キーワード引数の分離」が原因で委譲用のメソッドが壊れた場合、特に Ruby 2.6 ~ 3.0 で互換性を保ちながら対応する場合の日本語記事が見当たらなかったので、書かせていただきます。
インスタンスメソッドが壊れた場合
以下のコードは Ruby 3.0 より前のバージョンでは動きますが、3.0 からは MyClass#other_method
の実行時に ArgumentError が起きるようになります。(2.7 の時も警告が出ます)
class MyClass def delegating_method(*args) other_method(*args) end def other_method(value:) p value end end MyClass.new.delegating_method(value: 'value')
こちらは公式の Separation of positional and keyword arguments in Ruby 3.0#a-compatible-delegation や ruby2_keywords gem や Module#ruby2_keywords を参考に以下のようなコードで対応できます。
class MyClass def delegating_method(*args) other_method(*args) end def other_method(value:) p value end # この 1 行を追加する ruby2_keywords :delegating_method if respond_to?(:ruby2_keywords, true) end MyClass.new.delegating_method(value: 'value')
ポイントは Ruby 2.7 や 3.0 でも旧来のスタイルで実行できるようにするための Module#ruby2_keywords
が Ruby 2.6 以前では定義されてないので、Object#respond_to? などでそのケアをしてあげる必要があることです。
クラスメソッドが壊れた場合
こちらがこの記事を書いた目的です。
サンプルコードに現実味を持たせるためにインスタンスメソッドの例とは少し変えています。クラスメソッドで引数を受けて、そのまま委譲して初期化するようなコードです。
このコードも Ruby 3.0 からは ArgumentError が発生します。
class MyClass def self.delegating_method(*args) new(*args) end def initialize(value:) p value end end MyClass.delegating_method(value: 'value')
こちらはインスタンスメソッドの場合と違い、以下のように class << self
で特異クラスをオープンしてから、Module#ruby2_keywords
の対応をしてあげる必要があります。
class MyClass def self.delegating_method(*args) new(*args) end def initialize(value:) p value end # この 3 行を追加する class << self ruby2_keywords :delegating_method if respond_to?(:ruby2_keywords, true) end end MyClass.delegating_method(value: 'value')
定義されたクラスメソッドの居場所が分からなくて、久しぶりに『メタプログラミングRuby』の 5 章を開きました。
Also see. https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#singleton_class
【おまけ】Ruby 2.6 ~ 3.0 で互換性を保たなくて良い場合
公式の Separation of positional and keyword arguments in Ruby 3.0#delegation-ruby-3 の通りにやれば OK です。