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

家住魔仙堡 提交于 2019-12-18 06:49:32

问题


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 have an automation test and I want to verify that after each step there no error shown on the page. The error is shown as a red div and is not visible to Ruby as raise or anything like that, so I have to check for it manually (there are several other use cases as well).

I understand that it might be possible using set_trace_func. But I think this may bring more problems than benefits since it works over entire call tree (and requires me to filter it myself).

UPDATE (clarification):

I am interested in intercepting all actions (or calls) that are performed within a given method. This means unspecified number of classes is called, so I can't just intercept any given set of classes/methods.


回答1:


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.




回答2:


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.




回答3:


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


来源:https://stackoverflow.com/questions/1199179/is-it-possible-to-run-code-after-each-line-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!