🧙‍♀️

Rubyで、今いるファイルでだけヤンチャする機能

2020/10/04に公開

Refinementsのカジュアルな使い方の紹介です。

Refinementsと聞いて、「なんだかよくわからない難しい機能」と身構えてしまう人も多いかと思います。

ですが、Refinementsはもっとカジュアルに使っても良いんじゃないかと思うので、その使い方の紹介です。

他のファイルへは一切影響を与えないヘルパーメソッド

using Module.new {
  refine Object do
    # ここに便利メソッドを書く
    def benri(arg)
      puts arg
    end
  end
}

# 後は使うだけ
benri('hi')
benri('hi')

例えばrake taskを書くときに、「ちょっと処理をまとめておきたいだけなんだけど、他の部分に影響を与えたらやだな……。」という時に使えるテクニックです。
usingは「ここ以降でRefinementsが有効ですよ」という魔法で、モジュールに対して使います。
refineはRefinementsとして拡張するclassの指定です。Objectに対して拡張することで、benri('hi')と書けば動くメソッドが作れます。

この書き方を覚えておけば、今書いてるファイルの中で好き放題メソッドを定義できます。ヤンチャし放題。しかも他に影響を与えません。

他のファイルへは一切影響を与えない、Rubyの既存のclass拡張

例えば「割り算がしたいんだけど、分母が0だったら答えも0にしたい。」場合。

kekka1 = bunbo1.zero? ? 0.0 : bunshi1.fdiv(bunbo1)
kekka2 = bunbo2.zero? ? 0.0 : bunshi2.fdiv(bunbo2)
kekka3 = bunbo3.zero? ? 0.0 : bunshi3.fdiv(bunbo3)
...

とすればいいですが、左から読むといきなり例外的な処理がドンときて迫力があります。分母の変数も2回出てくるので間違って書いてないか不安です。

本当は「割り算をしている」箇所なので、ここは割り算を主役すなわち左側に書いて、例外的な処理は脇に据えるのはどうでしょうか。

using Module.new {
  refine Float do
    def finite_or_zero
      finite? ? self : 0.0
    end
  end
}

kekka1 = bunshi1.fdiv(bunbo1).finite_or_zero
kekka2 = bunshi2.fdiv(bunbo2).finite_or_zero
kekka3 = bunshi3.fdiv(bunbo3).finite_or_zero
...

書いているファイル以外では絶対に影響を与えないので、安心してこんなヤンチャもできますね。

まとめ

せっかくRefinementsという「ヤンチャできるけど影響を小さくできる便利機能」があるので、使ってみてはいかがでしょうか。
他にも「あちこちに分散した処理を縦にまとめる」とか「ライブラリーの公開APIを制限する」など様々な使い方ができます。

余談

2020/10/4のKaigi on RailsにてRefinements話の中で、紹介があったようです。(私は子供と遊んでて観てませんでした)

発表が @a_matsuda さんだと知って「あの話か」とピンときました。

多分、ライブラリーを作る時に公開APIにはしたくないけど処理をまとめるメソッドが作れて便利みたいな話をしたんじゃないかなあ……。

Discussion