ruby2.0 - refinements - 鳥取ruby会 第11回

21
弘野/それぃゆ @hilohiro refinements Ruby 2.0 series

Upload: rie-hirono

Post on 22-Jul-2015

339 views

Category:

Technology


3 download

TRANSCRIPT

弘野/それぃゆ@hilohiro

refinements

Ruby 2.0 series

ご注意!

今回の内容はRuby 2.0.0 Preview 2で

確認した内容です。

正式リリースでは変更される可能性があります。

黒魔術をより安全に(?)

Rubyの特徴 - オープンクラス

● 定義されたクラスを実行中に改変できる– メソッドを追加したり変更したり削除したり

– システムが定義しているクラスでも可能

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で定義されたコードでのみ利用可能

参考資料

● http://qiita.com/items/77fd309178dcdd13b5cd● http://timelessrepo.com/refinements-in-ruby● http://www.infoq.com/jp/news/2012/11/ruby-20-

preview1