Как включить патч обезьяны для определенного метода?
Я использую драгоценный камень, по какой-то причине один из его методов должен быть исправлен, прежде чем он сможет использоваться некоторыми из моего кода.
Проблема здесь, как я могу включить этот патч только для некоторых из моего кода, скажем. для некоторого метода внутри класса мне нужно включить этот патч; некоторые я хочу отключить этот патч.
Как это сделать?
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
не имеет смысла. Если этот модифицированный экземпляр можно использовать в некоторых местах, то использование этого метода достойно