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

161

В Ruby есть способ найти имя вызывающего метода внутри метода?

Например:

class Test
  def self.foo
    Fooz.bar
  end
end

class Fooz
  def self.bar
    # get Test.foo or foo
  end
end
jrichardlai
источник
1
Возможный дубликат Получить имя исполняемого в настоящее время метода в Ruby
Даршан Ривка Уиттл
получить вызывающий объект: stackoverflow.com/questions/2703136/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Не дубликат «Получить имя выполняемого в данный момент метода в Ruby». Этот вопрос запрашивает имя вызывающего метода, а не имя текущего метода.
Уэйн Конрад

Ответы:

210
puts caller[0]

или возможно ...

puts caller[0][/`.*'/][1..-2]
DigitalRoss
источник
5
Да, см Kernel # вызывающему, иначе ruby-doc.org/core-1.8.7/classes/Kernel.html#M001073
DigitalRoss
10
Это дает имя вызывающего метода, но не указывает, к какому модулю или классу принадлежит метод. Это возможно?
августа
@thomthom Да, это возможно, вы можете позвонить self.class.name, чтобы увидеть имя класса
Торин
Отличное использование регулярных выражений в качестве строкового индекса! Можно также использоватьcaller[0][/`(.*)'/,1]
AKS
1
Похоже, это не работает в Rails 5.2.1. В Rails-контроллере это возвращается "block in make_lambda". Я думаю, это только для Ruby.
dcangulo
164

В Ruby 2.0.0 вы можете использовать:

caller_locations(1,1)[0].label

Это намного быстрее, чем решение Ruby 1.8+:

caller[0][/`([^']*)'/, 1]

Будут включены, backportsкогда я получу время (или запрос тянуть!).

Марк-Андре Лафортун
источник
Стоит отметить, что это не доступно в Rubinius.
Макс
1
если вы используете pry, вы должны игнорировать трассировку стека pry, как кажется ... для этого не существует решения по умолчанию.
DTC
6
Теперь это похоже caller_locations[0].labelна Ruby 2.2.0, иначе у вас всегда будет send_actionрезультат
brcebn
1
Как мы можем получить только вызов метода приложения и игнорировать вызов фреймворка?
Матрица
31

Использовать caller_locations(1,1)[0].label(для ruby> = 2.0)

Изменить : мой ответ говорил, чтобы использовать, __method__но я был не прав, он возвращает имя текущего метода.

Дориан
источник
1
@OswaldoFerreira Спасибо, нашел это на SO в другом ответе где-то
Дориан
5
Это неверно, он возвращает текущий метод, а не метод, который вызвал текущий метод ...
thrice801
1
работает как шарм Также, кажется, намного быстрее, чем до 2.0 методов.
Dr.Strangelove
21

я использую

caller[0][/`([^']*)'/, 1]
Гектор Гарсия
источник
4
В чем преимущество этого подхода перед DigitalRoss?
Эндрю Гримм
2
Чище и точнее. Вместо того, чтобы выполнять поиск, затем использовать метод массива для разделения нежелательных символов на основе позиции (что может быть неверно).
Новая Александрия
2
Почему бы просто не использовать caller [0] [/ `(. *) '/, 1]? Я не гуру в регулярных выражениях, но, похоже, это работает.
Коллимарко
7
@collimarco До тех пор, пока String не содержит ничего, 'кроме того, что вы ищете (и я полагаю, что не может), результат будет таким же, конечно. Тем не менее, он [^']*будет работать лучше, так как движок регулярных выражений перестанет пытаться сопоставить эту часть выражения с моментом, когда он достигнет '(ваша версия будет идти до конца, а затем возвращаться назад, потому что она не нашла 'в конце). Конечно, в этом случае разница довольно незначительна, но по возможности следует избегать .регулярных выражений.
Thor84no
4

Как насчет

caller[0].split("`").pop.gsub("'", "")

Гораздо чище имо.

thrice801
источник
3

Вместо этого вы можете написать его как библиотечную функцию и сделать вызов там, где это необходимо. Код выглядит следующим образом:

module CallChain
  def self.caller_method(depth=1)
    parse_caller(caller(depth+1).first).last
  end

  private

  # Copied from ActionMailer
  def self.parse_caller(at)
    if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
      file   = Regexp.last_match[1]
      line   = Regexp.last_match[2].to_i
      method = Regexp.last_match[3]
      [file, line, method]
    end
  end
end

Чтобы вызвать вышеупомянутый метод модуля, вам нужно вызвать так: caller = CallChain.caller_method

код ссылки от

Амит Карсале
источник
1
Всегда приветствуется ссылка на потенциальное решение, но, пожалуйста, добавьте контекст вокруг ссылки, чтобы ваши коллеги-пользователи имели представление о том, что это такое и почему оно есть. Всегда указывайте наиболее релевантную часть важной ссылки, если целевой сайт недоступен или постоянно недоступен. Примите во внимание, что наличие чуть больше, чем ссылка на внешний сайт является возможной причиной, почему и как удаляются некоторые ответы? ,
Хави Лопес
@ XaviLópez обновил ответ, пожалуйста, исправьте, если я делаю что-то не так или что-то ошибочно ... Спасибо за любезное предложение :)
amit karsale
1
Спасибо за улучшение вашего ответа. К сожалению, я не обладаю достаточными знаниями о Ruby, чтобы правильно комментировать этот пост, но теперь ответ выглядит хорошо. Я удалил свое понижение. Удачи :-)
Хави Лопес
2

Чтобы увидеть информацию о вызывающем и вызываемом абонентах на любом языке, будь то ruby, java или python, вы всегда должны смотреть на трассировку стека. В некоторых языках, таких как Rust и C ++, в компилятор встроены опции для включения какого-либо механизма профилирования, который вы можете просматривать во время выполнения. Я верю, что существует одна для Ruby, которая называется ruby-prof.

И, как упоминалось выше, вы можете заглянуть в стек выполнения для ruby. Этот стек выполнения представляет собой массив, содержащий объекты местоположения обратной трассировки.

По сути, все, что вам нужно знать об этой команде, заключается в следующем:

вызывающая сторона (начало = 1, длина = ноль) → массив или ноль

user3769125
источник