Module#refine ってなんぞ?
引数 klass で指定したクラスだけに対して、ブロックで指定した機能を提供で きるモジュールを定義します。定義した機能は Module#refine を使用せずに直 接 klass に対して変更を行う場合と異なり、限られた範囲のみ有効にできます。 そのため、既存の機能を局所的に修正したい場合などに用いる事ができます。
定義した機能は main.using を実行した場合のみ有効になります。
要は?
モンキーパッチを適用する範囲を限定できて便利
モンキーパッチ?
オリジナルのソースコードを変更することなく、実行時に動的言語(例えばSmalltalk, JavaScript, Objective-C, Ruby, Perl, Python, Groovy, など)のコードを拡張したり、変更したりする方法である
具体例
文字列を装飾する class
text_decorator.rb
class TextDecorator
def initialize(str)
@base_str = str
end
def decorate
gsub_space_to_atmark(surround_quote(@base_str))
end
private
def surround_quote(str)
"'#{str}'"
end
def gsub_space_to_atmark(str)
str.gsub(' ', "@")
end
end
puts TextDecorator.new("abc def").decorate #-> "abc@def"
surround_quoteとか、gsub_space_to_atmark は文字列を弄っているだけだから、String を拡張したら、こんなふうにかけるのに....
string.rb
class String
def surround_quote
"'#{self}'"
end
def gsub_space_to_atmark
self.gsub(' ', "@")
end
end
class TextDecorator
# String を拡張することで、メソッドチェインが実現
def decorate
@base_str.surround_quote.gsub_space_to_atmark
end
end
とは言え、TextDecorator でしか使わないメソッドなのに、 String 全体に影響を与えてしまうのはよろしくない
- 誰かが別の所で同じメソッドで、String を拡張するかも...?
- 他の所で思わぬ影響を与えるかも...?
そこで Refinements ですよ
mutable_string.rb
# module MutableString を using している所だけで、下記モンキーパッチが適用される
module MutableString
refine String do
def surround_quote
"'#{self}'"
end
def gsub_space_to_atmark
self.gsub(' ', "@")
end
end
end
class TextDecorator
def initialize(str)
@base_str = str
end
using MutableString
# String を拡張することで、メソッドチェインが実現
def decorate
@base_str.surround_quote.gsub_space_to_atmark
end
end
# undefined method `gsub_space_to_atmark' for "abc def":String (NoMethodError)
"abc def".gsub_space_to_atmark
これなら安心!!