在Ruby中,class_eval <<-"end_eval", __FILE__, __LINE__的意思是什么?

49

我正在学习如何在模块中使用class_eval(我对class_eval有一定的了解),然后遇到了resource_controller中这个有用的类。在那里,他们有类似这样的东西:

class_eval <<-"end_eval", __FILE__, __LINE__

  def #{block_accessor}(*args, &block)
    unless args.empty? && block.nil?
      args.push block if block_given?
      @#{block_accessor} = [args].flatten
    end

    @#{block_accessor}
  end

end_eval

__FILE____LINE__在这个上下文中是什么意思?我知道__FILE__引用了当前文件,但这整个东西到底是干什么的呢?我不知道该如何搜索这个问题 :)

3个回答

59

__FILE____LINE__是一种动态常量,它们保存当前正在执行的文件和行。在此处传递它们可以使错误报告其位置更准确。

instance_eval <<-end_eval, __FILE__, __LINE__
  def foo
    a = 123
    b = :abc
    a.send b
  end
end_eval

foo

当你运行这个时

$ ruby foo.rb 
foo.rb:5:in `send': undefined method `abc' for 123:Fixnum (NoMethodError)
    from foo.rb:5:in `foo'
    from foo.rb:11

请注意,它显示的是文件和行号#5,尽管那只是eval中的文本。如果没有这些文件/行技巧,输出将如下所示:

$ ruby foo.rb 
(eval):5:in `send': undefined method `abc' for 123:Fixnum (NoMethodError)
    from (eval):5:in `foo'
    from foo.rb:11

堆栈跟踪仅显示(eval),这并不太有用。


1
可能需要提一下,使用define_method代替这种方法不需要这种技巧,而且也不会带来漏洞(参见https://dev59.com/JnA75IYBdhLWcg3w-OVA#3003509)。 - Marc-André Lafortune

4
<<heredoc的开始。这一行是一个多行字符串的开始。该字符串被evaled以创建函数。class_eval函数使用__FILE__和__LINE__添加调试信息。

1
但是但是但是...你不是来自无政府高尔夫的高尔夫巫师吗?你应该像熟悉自己手背一样熟悉Ruby!:-D(这是对你关于class_eval行为的声明含糊不清的评论。如果你的意图不是表现出不确定,我会收回我的评论。) - C. K. Young
哈哈...我从来没有用过那个函数,因为它太多字符了。 - Mark Byers
在您的情况下,它会在错误消息之前添加文件名和行号。请查看链接中的示例:http://ruby-doc.org/core/classes/Module.html#M001650 - Mark Byers

2

需要注意的是,在可能的情况下应避免对字符串进行eval操作。在您的特定情况下,可以将#class_eval替换为#class_exec,并且应该优先考虑使用:

class_exec do
  define_method block_accessor do |*args, &block|
    unless args.empty? && block.nil?
      args.push block if block_given?
      instance_variable_set "@#{block_accessor}", [args].flatten
    end
    instance_variable_get "@#{block_accessor}"
  end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接