简体   繁体   English

是否可以在Ruby中的每一行之后运行代码?

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

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? 我知道可以在ruby中使用挂钩之前和之后修饰方法,但是是否可以为给定方法的每一行执行此操作?

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). 该错误显示为红色的div和不可见的红宝石作为raise之类的东西,所以我必须手动检查它(还有其他一些使用情况以及)。

I understand that it might be possible using set_trace_func . 我知道使用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. 这意味着调用了未指定数量的类,因此我不能只截取任何给定的类/方法集。

It is, and you can get a full description of how to do it in section 8.9 of The Ruby Programming Language . 它是,您可以在Ruby编程语言的第8.9节中获得如何执行此操作的完整描述。 Running the code on each invocation of the method involves sending the method to a TracedObject class that has an implementation for method_missing . 在每次调用方法时运行代码涉及将方法发送到具有method_missing实现的TracedObject类。 Whenever it receives a message, it invokes method_missing and executes whatever code you have assigned to it. 每当它收到一条消息时,它就会调用method_missing并执行你分配给它的任何代码。 (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. 这是对此过程的一般描述,您可以查阅本书了解详细信息。

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: 首先,使用Pinochle答案提出的通用仪器,然后重新定义所讨论的方法如下:

# 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! 你需要编写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. uninstall方法,但它们应该是非常简单的:只需设置或取消设置类变量并在检测逻辑中检查它。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM