原文:Ruby class_eval, FILE and LINE arguments
这里我们将讨论一个class_eval调用的例子
def my_attr_reader(sym)
class_eval <<-READER, __FILE__ , __LINE__ + 1
def #{sym}
@#{sym}
end
READER
end
class_eval方法用来做什么呢?为什么向它传递文件名和行号?这些是我们接下来要回答的问题。
class_eval在接受者的上下文中对字符串或块进行计算,接受者可以为class或module。
在下面的例子中,我们使用class_eval为self(User类)定义实例方法:
module MyAttrReader
def my_attr_reader(sym)
class_eval <<-READER, __FILE__ , __LINE__ + 1
def #{sym}
@#{sym}
end
READER
end
end
class User
extend MyAttrReader
def initialize(name)
@name = name
end
my_attr_reader :name
end
u = User.new('dalibor')
p u.name # calls the method 'name' defined with class_eval
现在我们来解释上段代码中使用的伪变量
__FILE__ # the current source file name.当前文件名称
__LINE__ # the current line number in the source file.当前行号
我们可以将class_eval调用简化为以下形式:
def my_attr_reader(sym)
class_eval("def #{sym}; undefined_method; @#{sym}; end;", __FILE__ , __LINE__ )
end
现在语法很清晰了,我们查看文档中关于class_eval的说明发现,第二和第三个参数用来对错误消息进行设置,以便方便我们对错误进行定位。
来看看下面这两段代码产生的错误。注意我们在class_eval计算的字符串中添加了undefined_method,并且在第一段代码中移除了file和line这两个伪变量:
snippet1:
module MyAttrReader
def my_attr_reader(sym)
class_eval "def #{sym}; undefined_method; @#{sym}; end;"
end
end
class User
extend MyAttrReader
def initialize(name)
@name = name
end
my_attr_reader :name
end
u = User.new('dalibor')
p u.name
# (eval):1:in `name': undefined local variable or method `undefined_method' for # (NameError)
snippet2:
module MyAttrReader
def my_attr_reader(sym)
class_eval "def #{sym}; undefined_method; @#{sym}; end;", __FILE__ , __LINE__
end
end
class User
extend MyAttrReader
def initialize(name)
@name = name
end
my_attr_reader :name
end
u = User.new('dalibor')
p u.name
# t.rb:3:in `name': undefined local variable or method `undefined_method' for # (NameError)
报错信息见代码块底部。
可以注意到第二段代码的报错信息更加明确,阐明了错误发生在t.rb文件的第三行,但是第一段代码在错误发生时并没有给出类似的错误定位信息。