#ruby-on-rails #ruby #sinatra
Вопрос:
в ruby мы можем внедрить новые методы в текущий экземпляр класса
Class A
def something
pp "something"
end
end
A.class_eval do
def another_thing
pp "another thing"
end
end
Но есть ли способ «внедрить» новые вызовы методов в существующий метод?
Пример:
что-то после переписывания он продолжает звонить
pp "something"
но теперь делаю еще один звонок, как:
pp 1 1
таким образом, в результате получается
def something
pp "something"
pp 1 2
end
Комментарии:
1. @SergioTulentsev, Но вы можете взять ссылку на исходный метод и вызвать оригинал в начале или в конце замены. Таким образом, вы можете как бы вводить код в начале или в конце метода, вроде того.
2. @muistooshort: да, вот на что намекает мой «метод украшения/упаковки».
3. Вы говорите «блокировать вызовы», но в поле зрения нет ни одного блока (за исключением
class_eval
, естественно, того, который нужно).4. @SergioTulentsev, конечно, ты можешь. Я делаю это все время. Предположим, что класс есть
A
, а метод-это символm
. ЗатемA.instance_method(m).source_location
предоставляет вам файл, содержащий исходный код для этого метода, и строку, с которой он начинается. Просто проанализируйте текст, вставьте нужный код,eval
полученную строку и Боб-ваш дядя . кстати, это один из пляжей Филадельфии с белым песком на фоне вашей фотографии?5. @SergioTulentsev Извините за это, я исправил, где я поставил «блок»
Ответ №1:
Можете ли вы волшебным образом ввести строку кода в произвольное расположение существующего метода? Нет. По крайней мере, не без некоторых безумных трюков с черной магией, которые я даже не осмеливаюсь попробовать.
Можете ли вы ввести поведение с помощью блока? Да (хотя я не уверен, действительно ли это то, о чем вы спрашиваете, поскольку ни один из ваших фрагментов кода не содержит ни одного блока). Для того, чтобы сделать это, исходный метод должен указать, куда yield
. Например:
A.class_eval do
def another_thing
pp "another thing"
yield if block_given?
end
end
a = A.new
# Invoke the "original" behaviour"
a.another_thing
# Inject your "new" behaviour at the end
a.another_thing { pp 1 2 }
Можете ли вы переопределить метод, все еще вызывая исходное поведение? Да, это возможно с помощью Module.prepend
:
module ThingOverrider
def another_thing
super
pp 1 2
end
end
A.prepend(ThingOverrider)
Использование super
дает вам возможность гибко выбирать, где вызывать исходную реализацию, однако это не позволяет произвольно вводить функциональность в середину исходного метода.
Комментарии:
1. Извините за это, я исправил, где я поставил «блок».
2. Это ответ на ваш вопрос? Я все еще не на 100% уверен, что вы подразумеваете под «внедрением новых вызовов в существующий метод», но этот метод, по крайней мере, можно было бы использовать для реализации приведенного вами примера.