Как включить патч обезьяны для определенного метода?

Я использую драгоценный камень, по какой-то причине один из его методов должен быть исправлен, прежде чем он сможет использоваться некоторыми из моего кода.

Проблема здесь, как я могу включить этот патч только для некоторых из моего кода, скажем. для некоторого метода внутри класса мне нужно включить этот патч; некоторые я хочу отключить этот патч.

Как это сделать?

class FromGem
    def BlahBlah
       #patch here
    end
end

class A 
   def Test1
         #need patch
   end 

   def Test2
         # don't need patch
   end
end

Ответ 1

Это то, для чего нужны уточнения.

Скажем, у нас есть следующий сторонний код:

class FromGem
  def say_hello
    'Hello'
  end
end

FromGem.new.say_hello
# => 'Hello'

И мы хотим расширить его, чтобы вместо этого сказать "Hello World", мы сделали бы что-то вроде этого:

module ExtendFromGem
  def say_hello
    super + ' World'
  end
end

class FromGem
  prepend ExtendFromGem
end

FromGem.new.say_hello
# => 'Hello World'

Это просто стандартный способ расширения поведения, конечно, это все еще глобально. Если мы хотим ограничить сферу нашего обезьян-патча, нам нужно будет использовать уточнения:

module ExtendedFromGem
  module ExtendFromGem
    def say_hello
      super + ' World'
    end
  end

  refine FromGem do
    prepend ExtendFromGem
  end
end

FromGem.new.say_hello
# => 'Hello'
# We haven't activated our Refinement yet!

using ExtendedFromGem

FromGem.new.say_hello
# => 'Hello World'
# There it is!

Теперь мы хотим написать следующее:

class A 
  def test1
    using ExtendedFromGem
    FromGem.new.say_hello
  end

  def test2
    FromGem.new.say_hello
  end
end

A.new.test1
# => 'Hello World'

A.new.test2
# => 'Hello'

К сожалению, это не работает. Уточнения работают только в области script, и в этом случае уточнения активны только после вызова using или они работают в области модуля, и в этом случае они активны для весь модуль модуля, даже до вызова using, так что мы можем сделать, это (IMO, это чище):

class A 
  using ExtendedFromGem

  def test1
    FromGem.new.say_hello
  end
end

class A 
  def test2
    FromGem.new.say_hello
  end
end

A.new.test1
# => 'Hello World'

A.new.test2
# => 'Hello'

или это:

class A 
  def test2
    FromGem.new.say_hello
  end
end

using ExtendedFromGem

class A 
  def test1
    FromGem.new.say_hello
  end
end

A.new.test1
# => 'Hello World'

A.new.test2
# => 'Hello'

Et voilà: test1 видит уточнение, test2 не делает.

Ответ 2

Если вы хотите изменить поведение только в одном месте, вам не нужно ничего делать, просто напишите несколько строк кода, которые соответствуют вашим требованиям, используя или не используя метод gem

Если вам понадобится такое модифицированное поведение в нескольких местах, создайте метод, который реализует измененное поведение. В этом случае; вы собираетесь использовать шаблон дизайна адаптера;
Шаги:

  • Вы создаете AdaptorClass, который использует поведение класса Original, чтобы предоставить вам новое поведение, которое вы желаете.
  • Теперь вы используете поведение адаптера, а не оригинальный класс, чтобы выполнять свою работу там, где это необходимо.

Попробуйте не изменять исходный класс; следовать Принцип открытого закрытия


class A
  def Test1
    # need patch
    # you use GemAdapter rather than FromGem
  end

  def Test2
    # don't need patch
    # use FromGem class
  end

  def Test3
    # need patch
    # you use GemAdapter rather than FromGem
  end
end

class GemAdapter
  def new_behavior
    gem_instance = FromGem.new
    # implement your new behavior you desire
  end
end

введите описание изображения здесь

Ответ 3

Это также может быть решением

class << используется для изменения конкретного экземпляра; часть метапрограммирования

class A
  def print
    p 'Original method'
  end
end

class B
  def initialize
    @new_instance_of_a = A.new
  end
  def my_method
    modifiable_a = A.new

    # modifying the instance of class A i.e. modifiable_a 
    class << modifiable_a 
      def print
        p 'Monkey patch'
      end
    end

    modifiable_a .print
  end

  def normal_method
    @new_instance_of_a.print
  end
end

Однако изменение a local_instance не имеет смысла. Если этот модифицированный экземпляр можно использовать в некоторых местах, то использование этого метода достойно