Ruby Language
精密化
サーチ…
備考
絞り込みは範囲が語彙的であり、制御が移行するまで有効になってから(キーワードusing
)有効であることを意味します。通常、制御はモジュール、クラス、またはファイルの最後で変更されます。
限られた範囲で猿のパッチを当てる
モンキーパッチの主な問題は、グローバルスコープを汚染することです。あなたのコードの作業は、お互いのつま先で足を踏み入れることなく、あなたが使用しているすべてのモジュールの慈悲にあります。これに対するRubyの解決策は洗練されたものであり、基本的には限られた範囲にあるサルのパッチです。
module Patches
refine Fixnum do
def plus_one
self + 1
end
def plus(num)
self + num
end
def concat_one
self.to_s + '1'
end
end
end
class RefinementTest
# has access to our patches
using Patches
def initialize
puts 1.plus_one
puts 3.concat_one
end
end
# Main scope doesn't have changes
1.plus_one
# => undefined method `plus_one' for 1:Fixnum (NoMethodError)
RefinementTest.new
# => 2
# => '31'
デュアルパーパスモジュール(リファインメントまたはグローバルパッチ)
洗練された方法でパッチを適用するのは良い習慣ですが、開発やテストなどでグローバルに読み込むのが良いこともあります。
たとえば、コンソールを起動し、ライブラリを必要とし、パッチ適用されたメソッドをグローバルスコープで使用したいとします。クラス/モジュール定義でusing
する必要があるため、これを洗練されたものではできません。しかし、それは二重目的のような方法でコードを書くことが可能です:
module Patch
def patched?; true; end
refine String do
include Patch
end
end
# globally
String.include Patch
"".patched? # => true
# refinement
class LoadPatch
using Patch
"".patched? # => true
end
動的リファインメント
精緻化には特別な制限があります。
refine
はモジュールスコープでのみ使用できますが、 send :refine
を使用してプログラムできます。
using
はより制限されています。クラス/モジュール定義でのみ呼び出すことができます。それでも、モジュールを指す変数を受け入れることができ、ループ内で呼び出すことができます。
これらの概念を示す例:
module Patch
def patched?; true; end
end
Patch.send(:refine, String) { include Patch }
patch_classes = [Patch]
class Patched
patch_classes.each { |klass| using klass }
"".patched? # => true
end
using
は静的なので、詳細ファイルが最初にロードされないと、ロード順で発行されます。これに対処する方法は、パッチされたクラス/モジュール定義をprocにラップすることです。例えば:
module Patch
refine String do
def patched; true; end
end
end
class Foo
end
# This is a proc since methods can't contain class definitions
create_patched_class = Proc.new do
Foo.class_exec do
class Bar
using Patch
def self.patched?; ''.patched == true; end
end
end
end
create_patched_class.call
Foo::Bar.patched? # => true
procを呼び出すと、パッチされたクラスFoo::Bar
が作成されます。これは、すべてのコードがロードされるまで遅延させることができます。