在如 rails 这样的开源库中,我们常常见到这样的一类写法:
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def xxx
# do something here
end
RUBY
令人困惑不已。
不过这里的知识点非常简单,只要掌握了 heredoc 与 eval 的用法,就很好理解这里的代码了。
heredoc
heredoc 是一种定义多行字符串的方法,同时保持原始缩进和格式。相当于 JavaScript 中的 ``。
heredoc 以 <<-
开头,跟上一个随意的单词,最后再以这个字符串结束:
multi_str = <<-FOO
1
2
3
FOO
puts multi_str
多行字符串会保留所有的缩进,并且在尾部会带上 \n
换行符。
对于换行符,可以使用 chomp
来去除:<<-FOO.chomp
。
如果你在缩进的情况下声明 heredoc,可能会变成这样,十分丑陋:
multi_str = <<-FOO
1
2
3
FOO
如果保持缩进:
multi_str = <<-FOO
1
2
3
FOO
最后的输出结果也会带上 1 所有缩进,这里可以使用 ~
代替 -
,或是使用 strip
。
multi_str = <<~FOO
1
2
3
FOO
multi_str = <<-FOO.strip
1
2
3
FOO
这两种方式都会以第一行为标准,在所有行的开头,删除空格。
eval
eval 在很多脚本语言中都存在,它可以让我们将字符串作为代码执行。
ruby 中存在多个 eval 方法,比如 eval
class_eval
module_eval
instance_eval
等。
他们分别可以实现不同的 eval 功能。
以 class_eval
为例:
class Foo
STR = <<-RUBY
def say
p 'I am Foo'
end
RUBY
Module.class_eval STR
end
Foo.say
我们可以轻松将字符串变成方法。
代码分析
现在我们终于可以看懂这段代码了,它其实就是使用了 heredoc 多行字符串,将字符串里变成了一个真正的 ruby 方法。
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def xxx
# do something here
end
RUBY
那么后面的 __FILE__, __LINE__ + 1
有什么用呢?
其实我们完全可以不传这两个参数。
但是加入我们字符串生成的方法报了错该怎么办呢?一般我们的方法报错都会有文件名、行号。
eval 使用字符串生成方法,自然没有这些东西,所以我们传入当前文件的文件名,和当前行的行号+1,来帮助 Ruby 生成更好的报错信息。
(完)