Ruby捕获NoMethodError并从发生异常的地方继续执行

时间:2012-01-28 16:26:43

标签: ruby metaprogramming

在Ruby中,我想捕获在另一个对象中的对象上生成的NoMethodError,然后将一些值返回到引发异常的位置并继续执行。有没有现成的方法呢?

我想出的最好的是:

class Exception
  attr_accessor :continuation
end

class Outer
  def hello
    puts "hello"
  end

  class Inner
    def world
      puts "world"
    end
    def method_missing(method, *args, &block)
      x = callcc do |cc|
        e = RuntimeError.exception(method)
        e.continuation = cc
        raise e
      end
      return x
    end
  end

  def inner(&block)
    inner = Inner.new
    begin
      inner.instance_eval(&block)
    rescue => e
      cc = e.continuation
      cc.call(hello())
    end
    inner
  end
end

o = Outer.new
o.inner do
  hello
  world
end

打印

hello
world

使用Ruby现有的元编程库数据库有没有更好的方法呢?基本上,我不确定callcc是否会继续存在。

感谢。

2 个答案:

答案 0 :(得分:5)

这个简单的方法怎么样:

class Outer
  def hello
    puts "hello"
  end

  class Inner
    def initialize outer
      @outer = outer
    end

    def world
      puts "world"
    end

    def method_missing(method, *args, &block)
      @outer.send(method, *args, &block)
    rescue NoMethodError # you can also add this
      puts "#{method} is undefined in both inner and outer classes"
    end
  end

  def inner(&block)
    inner = Inner.new self
    inner.instance_eval(&block)
    inner
  end
end

o = Outer.new
o.inner do
  hello
  cruel
  world
end

将打印

hello
cruel is undefined in both inner and outer classes
world

在这种情况下,如果内部类没有定义所需的方法,则使用Object#send将其委托给外部类。您可以在NoMethodError内捕获method_missing,以便在Outer类未定义委托方法时控制情况。


<强>更新 您也可以使用光纤来解决问题:

class Outer
    def hello
        puts "hello"
    end

    class Inner
        def world
            puts "world"
        end

        def method_missing(method, *args, &block)
            Fiber.yield [method, args, block] # pass method args to outer
        end
    end

    def inner(&block)
        inner = Inner.new
        f = Fiber.new { inner.instance_eval(&block) }
        result = nil # result for first fiber call does not matter, it will be ignored
        while (undef_method = f.resume result) # pass method execution result to inner
            result = self.send(undef_method[0], *undef_method[1], &undef_method[2])
        end
        inner
    end
end

答案 1 :(得分:2)

Ruby有一个名为throw的关键字,可用于向上传播错误。我无法从你的帖子中确切地告诉你你想要这个块,但它是这样的:

class Outer
  catch :NoMethodError do
  #process the exception thrown from inner
  end

  class Inner
    def some_method
      ##...some other processing
      throw :NoMethodError if #....
      #remaining statements
    end
  end
end

在throw语句和catch块执行后,some_method中的其余语句应该执行

希望这有帮助