Ruby 2.6 に投げたパッチが取り込まれた話

先日 Ruby 2.6 がリリースされましたが、それにわたしが投げたパッチが4つ含まれています。
と、いうことで具体的にどんなパッチを投げてどんな機能が取り込まれたのかを簡単に書いてみようかと

Refinements で定義した #to_proc が &hoge 時に呼ばれないのを緩和する提案

表題そのままなんですが、次のように Refinements で to_proc を定義した場合、&hogeto_proc が呼ばれなかったのを呼ばれるようにしたパッチです。

using Module.new {
    # Refinements で to_proc を定義
    refine String do
        def to_proc
            proc { |it| it.send self }
        end
    end
}

p "upcase".to_proc.call "homu"
# => HOMU

# "upcase".to_proc が呼ばれるようになった
p %w(homu mami mado).map &"upcase"
# => ["HOMU", "MAMI", "MADO"]

このパッチを投げたのは Ruby 2.5 のリリース直前で、ドサクサに紛れて取り込まれないかなーという気持ちで投げました。
まあ流石に直前だと無理だったので Ruby 2.5 には入りませんでしたが、パッチ自体は特に問題なく取り込まれて Ruby 2.6 で正式に使えるようになりました。
ここから怒涛の Refinements パッチが始まります。

Proposal: Enable refinements to #public_send

先程の to_proc のパッチが取り込まれてからだいぶ時間が経って、11月取り込まれたパッチになります。
前回からだいぶ間が空いているんですが、この間にもいくつかパッチを投げており…。
matz に『日本語よりも英語で上げたほうがいいよー』とツッコまれたのでこのあたりからがんばって英語でパッチを投げるようにしました。
パッチも diff ファイルを直接添付していたんですが、CI を回したかったので github 経由で pull request を投げるようにしています。
パッチ自体は以下のように『public_send でも Refinements を有効にする』と言うような内容です。

using Module.new {
    # Refinements でメソッドを定義
    refine String do
        def twice
            self + self
        end
    end
}

# public_send 経由でもメソッドが呼ばれるようになった
p "homu".public_send(:twice)
# => "hogehoge"

まあ #send も Refinements が有効になっているから #public_send も有効になってもいいよねーという気持ちで提案しました。
提案したものの(Refinements 的な意味で)議論されて取り込まれるまでに時間がかかるかなー、と心配していたんですが割とあっさり入ったのでちょっと面食らいました。
感触としては Ruby の方針としてはこのあたりはそこまで厳密に Refinements を制限する、という感じではなさそうな感じなんですかね。

Proposal: Enable refinements to #respond_to?

これも先程の #public_send と同じで『#public_send で Refinements を有効にする』というパッチになります。

using Module.new {
    # Refinements でメソッドを定義
    refine String do
        def twice
            self + self
        end
    end
}

p "homu".respond_to?(:twice)
# => true

こちらも #public_send と同じタイミングで取り込まれました。

Ruby で定義したメソッドに&:hogeを渡しても refinements が有効にならない

これは今回投げた中では一番大変なパッチでした。
このパッチを投げた経緯はこちらを参照してもらうと詳しく書いてあります。

パッチ自体は9月ぐらいに書いてすぐに投げたんですが、その後特に進展がなく、先月頭ぐらいにバグ認定されて、今月の頭に取り込まれたんですが、テストで落ちてたみたいで一旦 revert されて…という経緯があります。
Ruby 2.6 に絶対に入れたかったのでテストで落ちていた箇所を直してもう一度パッチを投げたんですが、そしたら別の場所でテストが失敗してしまったらしくて…。
けど、原因を調べてみたら実際はこのパッチとは全然関係ない箇所で、しょうがないのでこっちで原因を調べてみたら実は Ruby のバグを踏んでいてそれを報告して…。

というのがここ1〜2週間のハイライトになります。
まさか trunk で Ruby のバグを踏んでいるとは思わなくて実はかなり悩んでいました。
まあ結果的にこの不具合が修正されたので Ruby 2.6 ではいい感じに Refinements が使えるようになると思います。

using Module.new {
    # Refinements でメソッドを定義
    refine String do
        def twice
            self + self
        end
    end
}

def meth &block
    block.call "homu"
end

# Ruby で定義したメソッドのブロック引数でも有効になった
p meth &:twice

これが今まで動かなかったんですよねー。
何が嬉しいかって ActiveRecord のリレーションで定義されている map のブロック引数とかでも Refinements が有効になったのが大きいかなーと思います。
ってか、それで使えなくて困っていたのでパッチ書いて投げました。

所感

と、言う感じで Ruby 2.6 で取り込まれたパッチをいくつか紹介しました。
改めて見ると全部 Refinements のパッチですね、これ。
Refinements はもっと使い勝手よくしたいんですよねえ。

ちなみに今回取り込まれたパッチは4つでしたが、実際にはこの倍近くのパッチを投げています。
本当は #method#method_missing で Refinements が有効になるパッチも投げていたんですが、残念ながら Ruby 2.6 には間に合いませんでした。
このあたりが Ruby 2.7 で取り込まれるとだいぶ Refinements が使いやすくなるかなーと思います。
Ruby 2.7 で入るといいですねえ。
そろそろ Ruby のパッチを書くのにも慣れてきたので、何か思いついたらまたパッチを投げていきたいですねー。
パッチを書くよりも取り込まれるまでが大変ですが…。