Comment obtenir le nom de la méthode appelante?

161

y a-t-il un moyen dans Ruby de trouver le nom de la méthode appelante à l'intérieur d'une méthode?

Par exemple:

class Test
  def self.foo
    Fooz.bar
  end
end

class Fooz
  def self.bar
    # get Test.foo or foo
  end
end
jrichardlai
la source
1
duplication possible de Obtenir le nom de la méthode en cours d'exécution dans Ruby
Darshan Rivka Whittle
obtenir l'objet appelant: stackoverflow.com/questions/2703136/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Pas un doublon de "Obtenir le nom de la méthode actuellement exécutée dans Ruby." Cette question demande le nom de la méthode appelante, pas le nom de la méthode actuelle.
Wayne Conrad

Réponses:

210
puts caller[0]

ou peut-être...

puts caller[0][/`.*'/][1..-2]
DigitalRoss
la source
5
Oui, voir Kernel # caller, alias ruby-doc.org/core-1.8.7/classes/Kernel.html#M001073
DigitalRoss
10
Cela donne le nom de la méthode appelante, mais cela ne donne aucune indication à quel module ou classe appartient la méthode. Est-ce possible?
thomthom
@thomthom Oui, c'est possible, vous pouvez appeler self.class.name pour voir le nom de la classe
Thorin
Excellente utilisation de l'expression régulière comme index de chaîne! Peut également utilisercaller[0][/`(.*)'/,1]
aks
1
Cela ne semble pas fonctionner dans Rails 5.2.1. Dans le contrôleur Rails, cela revient "block in make_lambda". Je suppose que c'est pour Ruby uniquement.
dcangulo
164

Dans Ruby 2.0.0, vous pouvez utiliser:

caller_locations(1,1)[0].label

C'est beaucoup plus rapide que la solution Ruby 1.8+:

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

Sera inclus dans backportsquand j'aurai le temps (ou une pull request!).

Marc-André Lafortune
la source
Il convient de noter que cela n'est pas disponible dans Rubinius.
Max
1
si vous utilisez pry, vous devez ignorer le stacktrace de pry il semble ... il ne semble pas y avoir de solution par défaut pour cela.
dtc
6
Maintenant, il semble être caller_locations[0].labelsur Ruby 2.2.0 sinon vous avez toujours le send_actionrésultat
brcebn
1
Comment pouvons-nous obtenir uniquement la méthode de l'application et ignorer l'appel des frameworks?
Matrix du
31

Utilisez caller_locations(1,1)[0].label(pour rubis> = 2.0)

Edit : Ma réponse disait d'utiliser __method__mais j'avais tort, elle renvoie le nom de la méthode actuelle.

Dorian
la source
1
@OswaldoFerreira Merci, je l'ai trouvé sur SO dans une autre réponse quelque part
Dorian
5
Ceci est incorrect, il renvoie la méthode actuelle, pas la méthode qui a appelé la méthode actuelle ...
thrice801
1
fonctionne comme un charme. Semblent également être beaucoup plus rapides que les méthodes antérieures à 2.0.
Dr.Strangelove
21

j'utilise

caller[0][/`([^']*)'/, 1]
Héctor García
la source
4
Quel est l'avantage de cela par rapport à l'approche de DigitalRoss?
Andrew Grimm
2
Plus propre et plus précis. Plutôt que de faire la recherche, utilisez une méthode de tableau pour diviser les caractères indésirables en fonction de la position (ce qui pourrait être incorrect).
New Alexandria le
2
Pourquoi ne pas simplement utiliser l'appelant [0] [/ `(. *) '/, 1]? Je ne suis pas un gourou des expressions régulières, mais cela semble fonctionner.
collimarco
7
@collimarco Tant que la chaîne ne contient pas de chaîne 'au-delà de celle que vous recherchez (et je suppose que ce n'est pas le cas), le résultat sera le même, bien sûr. Cependant, [^']*cela fonctionnera mieux car le moteur d'expression régulière arrêtera d'essayer de faire correspondre cette partie à l'expression au moment où il atteint a '(votre version ira à la fin, puis reviendra parce qu'elle n'a pas trouvé de 'à la fin). La différence est assez négligeable dans ce cas bien sûr, mais c'est une bonne habitude à éviter .dans les regex si possible.
Thor84no
4

Que diriez-vous

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

Imo beaucoup plus propre.

trois fois801
la source
3

Au lieu de cela, vous pouvez l'écrire en tant que fonction de bibliothèque et faire un appel là où c'est nécessaire. Le code est le suivant:

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

Pour déclencher la méthode de module ci-dessus, vous devez appeler comme ceci: caller = CallChain.caller_method

référence de code de

amit karsale
la source
1
Un lien vers une solution potentielle est toujours le bienvenu, mais veuillez ajouter du contexte autour du lien afin que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là. Citez toujours la partie la plus pertinente d'un lien important, au cas où le site cible serait inaccessible ou serait définitivement hors ligne. Tenez compte du fait qu'être à peine plus qu'un lien vers un site externe est une raison possible pour savoir pourquoi et comment certaines réponses sont-elles supprimées? .
Xavi López
@ XaviLópez a mis à jour la réponse, veuillez rectifier si je fais mal ou
quelque chose me
1
Merci d'avoir amélioré votre réponse. Malheureusement, je n'ai pas assez de connaissances sur Ruby pour pouvoir commenter correctement cet article, mais la réponse semble correcte maintenant. J'ai supprimé mon vote défavorable. Bonne chance :-)
Xavi López
2

Afin de voir les informations sur l'appelant et l'appelé dans n'importe quel langage, que ce soit ruby, java ou python, vous voudrez toujours regarder la trace de la pile. Dans certains langages, tels que Rust et C ++, il existe des options intégrées au compilateur pour activer une sorte de mécanisme de profilage que vous pouvez afficher pendant l'exécution. Je crois qu'il en existe un pour Ruby appelé ruby-prof.

Et comme mentionné ci-dessus, vous pouvez consulter la pile d'exécution pour ruby. Cette pile d'exécution est un tableau contenant des objets d'emplacement de trace arrière.

Tout ce que vous devez savoir sur cette commande est essentiellement le suivant:

appelant (début = 1, longueur = nil) → tableau ou nil

user3769125
la source