Внедрение новых вызовов методов в существующий метод внутри класса

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