はじめに
Refinementsで遊んでみた記事です。具体的な内容としてはトップレベルでRefinementsが使えないかなと試してみたとき備忘録です。
ブログからの転載です
Refinementsとは
Rubyでは、後から自由にクラスにメソッドを追加することができる。
class Integer
def hoge
puts :hoge
end
end
42.hoge
# => :hoge
これはこれで便利なんだけど、グローバルに変更されるし、継承とかでも意図しない動きをする可能性があるのであまりいいとは言えない。
そこでRefinementsの出番。
module Refine
refine Integer do
def hoge
p :hoge
end
end
end
# 42.hoge
# => Error!
using Refine
42.hoge
# => :hoge
Refinementsではusing
した以降で再定義したものを使える。
またusing
した箇所のファイルスコープに限定されるので影響範囲が小さくて済む。
やりたかったこと
以下のようにトップレベルでrefine
を使って特定のクラスにメソッドを生やしたかった(毎回、using
とか書くのが面倒だったので……)
refine Integer do
def hoge
puts :hoge
end
end
42.hoge
ただ、このコードはrefine
というメソッドが定義されていないのでエラーになる。
やってみた
で、出来そうか試してみたところ以下のコードで動作した。
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
refine Integer do
public
def hoge
p :hoge
end
end
42.hoge
# => :hoge
やっていること
Refinementsをよく使われる人は以下のようなコードで無名モジュールを使っているんじゃないかと思う。
using Module.new {
refine Integer do
def hoge
p :hoge
end
end
}
42.hoge
# => :hoge
このコードを元にKernel
モジュールにrefine
メソッドを作成。
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
refine
メソッドは第一引数に再定義したいクラスなどを受け取り、メソッドの再定義などはブロック引数で渡します。
これにより以下のようにhoge
メソッドを追加できます。
refine Integer do
public
def hoge
puts :hoge
end
end
42.hoge
# => :hoge
ただし……
ただ、このコードはusing
していないのに再定義した内容が使用できているんですよね……。
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
# ここで usingを使っていないが作成したhogeメソッドが使えている……
refine Integer do
public
def hoge
p :hoge
end
end
42.hoge
# => :hoge
これはRefinementsの仕様通りの動きなのかがはっきりとしていないのでなぜ動いているのやら……。
それと今回のコードではpublic
を追加しないとメソッドが呼び出されなかった。
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
refine Integer do
def hoge
p :hoge
end
end
42.hoge
# => private method `hoge' called for 42:Integer (NoMethodError)
エラーからprivate
メソッドとして定義されているらしいことがわかったのでpublic
を付けてみたら動いたという……。
この辺も仕様通りの動きなんだろうか……?
おわりに
とりあえず、やりたいことはできた。
これが仕様通りの動きなのかは判断がつかないので、Refinementsの実装とか読んでみようかと思う。