Ruby 元编程 - __FILE__ 和 __LINE__ 的作用

前言

Ruby 元编程中,经常可以看到各种 eval 和 heredoc 的结合。其中很多用 heredoc 的位置都会出现 __FILE__ 和 __LINE__ 这两个变量。本文介绍一下它们的作用,以及为什么要这样做。

正文

先看一个用 class_eval 动态生成实例方法的例子:

class A
  def self.my_attr_reader(*args)
    args.each do |method|
      class_eval <<-EOF, __FILE__, __LINE__
        def #{method}
          raise "#{method}"
        end
      EOF
    end
  end
end

这是模仿 Ruby 的 attr_reader 的代码,当然实现简化过了,关键是,它会抛出异常。我们待会再解释为什么这样做。现在先大概解释下代码。

class_eval 是 Ruby 中 eval 的一种,它的上下文是调用它的对象,也就是类 A 。它通常用来为类生成实例方法,用法就像上面代码中的那样。你可以传把一段 Ruby 代码写在一个字符串中传过去,class_eval 就会动态解释 Ruby 代码。这和 Javascript 中的 eval 十分类似。

heredoc 就是被 EOF 包起来的那部分。它实际上是一个多行的字符串,EOF 只是一个标识,代表 heredoc 的开始和结束,你可以随意换成其他玩意。用 heredoc 的好处之一是可以不用操心单引号和双引号的问题,还有就是把代码写在多行看得比较清楚。喜欢深挖的可以看看 这个链接

第4行 EOF 后面的 __FILE__ 和 __LINE__ 是两个特殊变量,它们保存了当前文件的名字和当前代码的行数。其实 heredoc 后面不加这两个变量也可以。比如这样也是合法的:

class_eval <<-EOF
EOF

那加了它们有什么好处呢?

主要还是在测试方面。我们可以用如下代码测试一下:

A.class_eval do
  my_attr_reader :name
end

a = A.new
a.name      # 会抛出异常,异常信息为 name

把这段代码和上面第一段放在一起,保存成 Ruby 文件(我保存成 test.rb),再用解释器执行,会看到如下的错误:

# test.rb:5:in `raise': exception object expected (TypeError)
# 	  from test.rb:5:in `name'
# 	  from test.rb:18

可以看到,异常信息会提示你错误出在 test.rb 第5行的 name 方法内。即使 name 方法是用元编程手段动态产生的。

现在我们把 EOF 后面的 __FILE__ 和 __LINE__ 去掉。再运行下代码,会看到如下的结果:

# (eval):2:in `raise': exception object expected (TypeError)
#  	  from (eval):2:in `name'
#	  from test.rb:18

可以看到,异常信息最后是从 eval 中抛出来的,最多只能看到18行调用 name 的那一行出错了,但看不到 name 方法在哪定义的。这样对 debug 几乎没有帮助,尤其是当程序复杂的时候。比如 Rails 就使用了大量元编程技巧。

所以,__FILE__ 和 __LINE__ 在这里就是为了方便 debug 的。它们并不会对元编程动态创建的方法有任何影响。

posted @ 2011-06-11 00:36  darkbaby123  阅读(2734)  评论(1编辑  收藏  举报