Поиск…


замечания

Уточнения являются областью действия лексически, то есть они действуют с момента их активации (с 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 . Это может быть отложено до тех пор, пока не будет загружен весь код.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow