我正在尝试将引发异常的上下文传递给Exception
子类,以便使用此上下文生成信息性错误消息。我知道我可以将各个数据传递给Exception,但我想知道如何传递和使用整个上下文。
我知道我可以使用Kernel#binding
捕获上下文。类Binding
的Ruby 2.0文档列出了一个方法:eval
。这使您可以评估String
捕获的上下文中的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}
对于 SOME_METHOD ,我已经尝试了instance_exec
和instance_eval
,两者都不起作用。有没有办法做到这一点?或者,有一些原因是从整个上下文生成错误消息是一个坏主意,而不是来自环境的单个数据片段?
答案 0 :(得分: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
没有combine
,Binding
这样的方法,因此您似乎无法通过Kernel#eval
或Binding#eval
实现此目标。此外,一个块捕获它定义的绑定。当您调用eval('block.call', some_binding)
时,它会忽略传入的绑定。
但是,您可以使用Object#instance_eval
并使用instance_eval(&block)
形式传递块,这将在Class的实例方法的绑定中评估块。因此,您可以使用捕获的上下文为块创建正确的绑定。
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)