Is it possible to run code after each line in Ruby?

前端 未结 3 813
攒了一身酷
攒了一身酷 2020-12-19 16:56

I understand that it is possible to decorate methods with before and after hooks in ruby, but is it possible to do it for each line of a given method?

For example, I

相关标签:
3条回答
  • 2020-12-19 17:05

    It sounds like you want tracing on every method call on every object, but only during the span of every call to one particular method. In that case, you could just redefine the method to turn on- and off instrumentation. First, use the universal instrumentation suggsted in Pinochle's answer, then redefine the method in question as follows:

    # 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
    

    You'd need to write the install! and uninstall methods, but they should be pretty trivial: just set or unset a class variable and check for it in the instrumentation logic.

    0 讨论(0)
  • 2020-12-19 17:10

    It is, and you can get a full description of how to do it in section 8.9 of The Ruby Programming Language. Running the code on each invocation of the method involves sending the method to a TracedObject class that has an implementation for method_missing. Whenever it receives a message, it invokes method_missing and executes whatever code you have assigned to it. (This is, of course, for general tracing).

    That's the general description of the procedure for doing it, you can consult the book for details.

    0 讨论(0)
  • 2020-12-19 17:11

    What about (just tries)

        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")
    

    outputs:

        hello John
        say_hello
        hi John from Bob
        say_hi
        bye Bob and Anna
        say_bye
        bye Bob and Lisa
        say_bye
    
    0 讨论(0)
提交回复
热议问题