2

我正在尝试将引发异常的上下文传递给Exception子类,以便使用此上下文生成信息丰富的错误消息。我认识到我可以将个别数据传递给异常,但我想知道如何传递和使用整个上下文。

我知道我可以用Kernel#binding. Ruby 2.0 类文档Binding列出了一个方法:eval. 这允许您在 aString捕获的上下文中评估 a Binding。我想知道是否可以在绑定的上下文中评估一个块。我将按如下方式使用它:

class MyError < StandardError

  def initialize(str: nil, context: nil)
    @str = str; @context = context
    super(str)
  end

  def to_s
    @str ? @str : @context.**SOME_METHOD** { "Error: x == #{x}" }
  end

end 

x = 5
raise MyError.new(context: binding)

=> Error: x == #{5}

我都试过了instance_execinstance_eval对于SOME_METHOD,这两个都不起作用。有没有办法做到这一点?或者,有什么理由认为从整个上下文而不是从环境中生成单个数据片段是一个坏主意?

4

1 回答 1

3

用于eval(string, binding)运行具有给定变量绑定的 Ruby 代码。对于您的情况,您可以重写to_s方法如下:

def to_s
  @str ? @str : eval('"Error: x == #{x}"', @context)
end

请注意,'"some_string"'在将参数传递给eval.

如果你想用一些绑定来评估块,你必须将块分配给一个变量和eval块,如block.call. 现在您有两个绑定环境,一个用于变量block,另一个用于块中的变量。由于没有诸如join, combinefor之类的方法Binding,因此您似乎无法使用Kernel#evalor来实现此目的Binding#eval。此外,一个块在它定义的地方捕获它的绑定。当您调用 时eval('block.call', some_binding),它会忽略传入的绑定。

但是,您可以使用Object#instance_eval并通过 form 传递块instance_eval(&block),这将在类的实例方法的绑定中评估块。因此,您可以使用您捕获的上下文为块创建适当的绑定。

class BlockEnv
  def initialize(context, &block)
    # remove instance methods inherited from Object to minimize the impact
    # remove this if unnecessary
    BlockEnv.instance_methods
      .reject{|m|
        [:object_id, :call, :instance_eval, :method_missing].include?(m) ||
          m.to_s !~ /^[a-z]\w*$/i }
      .each do |m|
        eval("undef :#{m}")
      end
    @context = context
    @block = block
  end
  def call
    self.instance_eval(&@block)
  end
  def method_missing(name)
    eval(name.to_s, @context) rescue super
  end
end

class MyError < StandardError
  def initialize(str: nil, context: nil)
    @str = str; @context = context
    super(str)
  end

  def to_s
    @str ? @str : BlockEnv.new(@context){ "Error: x == #{x}" }.call
  end
end

x = 5
raise MyError.new(context: binding)
于 2013-06-18T01:25:35.057 回答