ruby2.0 - refinements - 鳥取ruby会 第11回
TRANSCRIPT
refinements
● クラスの変更の影響範囲を限定する– モンキーパッチはアプリケーション全体に影響する
● 既存のクラスの改変を行う– メソッドの追加、上書き
– モジュールのインクルード
● モジュール/クラス内でrefineする
– Module#refine(clazz) として定義されている
● refinementsを利用したいところでusingを呼び出す
書き方
module MyScope refine String do def double_string self + self end endend
class ScopeUser using MyScope
def run_refined 'sample string'.double_string endend
p ScopeUser.new.run_refined # => "sample stringsample string"p 'outer string'.double_stringNoMethodError: undefined method `double_string' for "outer string":String from (irb):3 from /usr/local/bin/irb:12:in `<main>'
Module#refine(clazz, &definitions)
● refinementsの内容を定義する
● refinements対象のクラスをパラメータで指定する
– 参考資料にはモジュールはrefine出来ないと書かれていたけど成功した…バグ?
● 1つのモジュールに複数のrefineを書くことが出来る
– 全てのrefineがまとめて利用される
Module#using(mod)
● モジュールで定義されたrefinementsの内容を利用可能にする
● モジュールのincludeは行われない
– モジュールをネームスペースとして利用するだけ
● メソッド内でもusingを呼び出せる
– そのメソッドだけで有効
● グローバルにもusingを呼び出せる
– 普通にモンキーパッチ? それともソースファイルスコープ?
Module#used(mod)
● usingでrefinementsが利用されたときに呼び出される
– Module#included(mod)のusing版
– mod.send(:include, self)でincludeを強制できる
refinementsの優先順位
refineのブロックで定義されたメソッドは…
● includeしたモジュールで定義されたものよりも優先される
● クラスで定義されたものよりも優先される– オープンクラスで改変したものよりも優先される
● 派生クラスでオーバーライドしたものよりは優先度が低い– デモします
refinementsの優先順位(その2)
refineのブロックでincludeしたモジュールのメソッドは…
● includeしたモジュールで定義されたものよりも優先される
● クラスで定義されたものよりも優先される● 派生クラスでオーバーライドしたものよりは優先度
が低い
● refineのブロックでdefされたメソッドよりは優先度が低い
Ruby 2.0 rcのrefinements
● Ruby 2.0 Preview 2までと違ってrcではrequire 'refinement.so'が必要
– using が定義されていない
– refinements.soじゃないようです…
● のですが今のところ refinement.so のビルドが上手くいっていません…– はまりました
refinementsと特異メソッド
● refinementsのブロックで特異メソッドを定義しようとしても上手くいかない
– def self.foo で定義したときは呼び出せない特異メソッドになる
– def Refined.foo で定義したときは、refinementsとは無関係に特異メソッドが書き換わる(通常のオープンクラスで定義した場合と同様)
● refine Refined.singleton_classでクラスを指定する
refinementsとundef
● refineしたクラスがもつメソッドをundefしても、refineで定義された同名のメソッドはundefされない
● refine内でundefすると無限ループに…
usingと継承
● 親クラスでusingを呼び出したrefinementsは派生クラスでも利用可能
● 子クラスでusingを呼び出しても親クラスのメソッドには効果を現さない– ただし、子クラスでオーバーライドしたメソッドを親クラ
スが呼び出した場合は別
refineされるクラスのメソッド
● refinementsはSelectorNamespace● refineされるクラスのメソッドは、通常refineで書き換えられたメソッドを呼び出すこと
が出来ない
class A def foo bar end def bar 'A#bar' endendmodule RefineA refine A do def bar 'RefineA#bar' end endenddef test using RefineA A.new.fooendp test # => 'A#bar'
モジュールでのusing
● モジュールAでモジュールRefinerをusingしても、モジュールAをincludeしたクラスBではRefinerで定義されたrefinementsは利用できない
– モジュールAで定義されたコードでのみ利用可能