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

发布于 2024-12-29 06:48:08 字数 908 浏览 4 评论 0 原文

在 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

Is there a better way to do this using Ruby's Existing array of meta-programming arsenal?基本上,我不确定callcc是否会继续存在。

谢谢。

In Ruby, I'd like to catch the NoMethodError generated on an object in another object, then return some value to where the exception is raised and continue execution. Is there an existing way to do this?

The best I came up with is:

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

This prints

hello
world

Is there a better way to do this using Ruby's existing array of meta-programming arsenal? Basically, I am not sure if callcc will continue to exist.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半步萧音过轻尘 2025-01-05 06:48:08

这个简单的方法怎么样:

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

Will print

hello
cruel is undefined in both inner and outer classes
world

在这种情况下,如果内部类没有定义所需的方法,它会使用 对象#send。当Outer类没有定义委托方法时,您可以在method_missing内部捕获NoMethodError,以控制情况。


更新
还可以使用光纤来解决问题:

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

What about this simple approach:

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

Will print

hello
cruel is undefined in both inner and outer classes
world

In this case if inner class does not define required method it delegate it to the outer class using Object#send. You can catch NoMethodError inside method_missing in order to control situation when Outer class does not define delegated method.


UPDATE
You can also use fibers to solve the problem:

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
踏月而来 2025-01-05 06:48:08

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 中的剩余语句之后应该执行

希望这会有所帮助

Ruby has a keyword called throw that can be used to propagate errors upward. I can't really tell from your post which block you would want this in but it goes something like this:

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

After the throw statement and catch block execute the remaining statements in some_method should execute

Hope this helps

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文