Ruby Language
Уточнения
Поиск…
замечания
Уточнения являются областью действия лексически, то есть они действуют с момента их активации (с using
ключевого слова 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'
Модули двойного назначения (уточнения или глобальные патчи)
Это хорошая практика для охвата патчей с использованием Refinements, но иногда приятно загружать их по всему миру (например, в процессе разработки или тестирования).
Скажем, например, вы хотите запустить консоль, потребовать свою библиотеку, а затем использовать исправленные методы в глобальной области. Вы не могли бы сделать это с уточнениями, потому что 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
. Это может быть отложено до тех пор, пока не будет загружен весь код.