ruby 2.0.0の主な機能のうちのひとつ「Refinements」の挙動について。
Refinementsとは
既存のクラスの挙動を変更したり、拡張するときの影響範囲を名前空間で限定する機能です。 これまでも既存のクラスやメソッドに対する、動的な挙動の変更や追加は可能でしたが、以下のような問題もありました。
- プログラム全体に影響を与えてしまう
- 変更が見えにくい
モンキーパッチをモジュールとして分割してincludeするなどして、 明示的にするなどの対処法はとられてきましたが、 refinmentsは書き換えをより厳密で局所的なものにとどめるものになっています。
例としてStringクラスに局所的にメソッドを追加するコードを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
HogeExtensionモジュールでは、Stringクラスに対するto_calmelcaseメソッドの追加をrefineキーワードで行なっています。 この実装は、HogeExtensionモジュールをusingキーワードでとりこんだRefineTestクラスでは適用されるので、利用できます。 しかし、それ以外の部分でStringクラスのインスタンスに対しては、利用できないことがわかります。
スコープ
refinementsは厳密なレキシカルスコープ(内部で定義した変数は外から見えないが、その逆は可能というスコープ)で動きます。 つまり、usingしたスコープと違うスコープに対しては、適用されません。 また、外側よりは内部の、前のよりは後の方が優先度が高く適用されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
モジュールには適用できない
モジュールのメソッドにはrefinementsを適用できません。
1 2 3 4 5 |
|
SelectorNamespcaceである
これはちょっとややこしいので、例を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
26行目のintroduceに注目。 intstanceのintroduceが呼ぶのは元のAクラスのsay_hogeメソッドですので、’fuga’ではなく’hoge’の方が画面に出力されます。 これは、refinementsがレキシカルスコープで動くことの証拠です。 Aのintroduceメソッドは、それとは違うスコープで適用されたrefinementsの影響を受けません。 このような挙動をSelectorNamespcace と呼ぶそうです。