2009-07-29 5 views
4

Je comprends qu'il est possible de décorer des méthodes avec des crochets avant et après en rubis, mais est-il possible de le faire pour chaque ligne d'une méthode donnée? Par exemple, j'ai un test d'automatisation et je veux vérifier qu'après chaque étape il n'y a pas d'erreur affichée sur la page. L'erreur est montrée comme un div rouge et n'est pas visible par Ruby comme raise ou n'importe quoi de pareil, donc je dois le vérifier manuellement (il y a plusieurs autres cas d'utilisation aussi).Est-il possible d'exécuter du code après chaque ligne dans Ruby?

Je comprends qu'il pourrait être possible d'utiliser set_trace_func. Mais je pense que cela peut apporter plus de problèmes que d'avantages car cela fonctionne sur l'ensemble de l'arbre d'appel (et nécessite que je le filtre moi-même).

UPDATE (clarification):

Je suis intéressé à intercepter toutes les actions (ou appels) qui sont effectués au sein une méthode donnée. Cela signifie qu'un nombre indéterminé de classes est appelé, donc je ne peux pas intercepter un ensemble donné de classes/méthodes.

Répondre

2

Il est, et vous pouvez obtenir une description complète de la façon de le faire dans la section 8.9 de The Ruby Programming Language. L'exécution du code sur chaque appel de la méthode implique l'envoi de la méthode à une classe TracedObject qui a une implémentation pour method_missing. Chaque fois qu'il reçoit un message, il appelle method_missing et exécute le code que vous lui avez assigné. (Ceci est, bien sûr, pour le traçage général).

C'est la description générale de la procédure pour le faire, vous pouvez consulter le livre pour plus de détails.

+0

je crois comprendre que TracedObject (http://books.google.ru/books?id=jcUbTcr5XWwC&pg=PA286?) peut intercepter tous les appels à une méthode de classe donnée, mais que se passe-t-il si je veux intercepter tous les appels d'une méthode spécifique? La méthode en question peut appeler plus d'une classe, donc il me demande essentiellement d'envelopper toutes les classes appelées dans TracedObjects (ce qui semble un peu impossible en tant que solution générale)? –

+0

Si vous êtes intéressé par une seule méthode, il vous suffit de filtrer pour cela dans 'method_missing'; mais vous avez déclaré que vous n'aimez pas l'idée du filtrage. Vous avez raison que ce serait une solution inélégante à difficile pour une méthode qui messages sur plusieurs classes. Vous devriez peut-être modifier la question pour ajouter cette deuxième exigence (sur plusieurs classes). – Pinochle

+0

Je suis intéressé par les appels sortants d'une seule méthode, pas les appels entrants à elle. Ou je veux intercepter chaque ligne de la méthode originale, comme décrit dans la question originale. –

2

On dirait que vous voulez tracer sur chaque appel de méthode sur chaque objet, mais seulement pendant la durée de chaque appel à une méthode particulière. Dans ce cas, vous pourriez simplement redéfinir la méthode pour allumer et éteindre l'instrumentation. Tout d'abord, utiliser l'instrumentation universelle suggsted dans Pinochle's answer, redéfinir alors la méthode en question comme suit:

# original definition, in /lib/foo.rb: 
class Foo 
    def bar(baz) 
    do_stuff 
    end 
end 

# redefinition, in /test/add_instrumentation_to_foo.rb: 
Foo.class_eval do 
    original_bar = instance_method(:bar) 
    def bar(baz) 
    TracedObject.install! 
    original_bar.bind(self).call(baz) 
    TracedObject.uninstall! 
    end 
end 

Vous aurez besoin d'écrire les méthodes install! et uninstall, mais ils devraient être assez trivial: juste activer ou désactiver une classe variable et vérifiez-le dans la logique d'instrumentation.

+0

Si je lis correctement 8.11.3, il ne devrait pas y avoir de TracedObject global. ! disponible, car ce n'est pas un self.install !, il est disponible uniquement au niveau de l'instance. –

0

Qu'en est-il (essaie juste)

class User 

     def initialize(name) 
     @name = name 
     end 

     def say_hello 
     puts "hello #{@name}" 
     end 

     def say_hi(friend) 
     puts "hi #{@name} from #{friend}" 
     end 

     def say_bye(a, b = 'Anna') 
     puts "bye #{a} and #{b}" 
     end 

    end 

    User.class_eval do 
     User.instance_methods(false).each do |method| 
     original = instance_method(method) 
     define_method method do |*options| 
      parameters = original.parameters 
      if parameters.empty? 
      original.bind(self).call 
      else 
      original.bind(self).call(*options) 
      end 
      puts __method__ 
     end 
     end 
    end 

    user = User.new("John") 

    user.say_hello 
    user.say_hi("Bob") 
    user.say_bye("Bob") 
    user.say_bye("Bob", "Lisa") 

sorties:

hello John 
    say_hello 
    hi John from Bob 
    say_hi 
    bye Bob and Anna 
    say_bye 
    bye Bob and Lisa 
    say_bye 
Questions connexes