题
我已经被使用 eval
功能的红宝石的许多一倍。但我听到有人说 eval
都是讨厌的。当被问及,为什么以及如何,我可能永远不会得到令人信服的理由,不要使用它。他们真讨厌?如果是的话,以什么方式?什么都是可能的"安全"选择eval?
解决方案
如果您是eval
荷兰国际集团由用户提交的,或可修改的字符串,这等于是使任意代码执行。试想一下,如果字符串包含一个操作系统调用rm -rf /
或相似。这就是说,在情况下,您知道的字符串进行适当的限制,或Ruby解释器进行适当沙盒,或理想两者<=>可以非常强大。
问题是类似于 SQL注入,如果你熟悉。这里的解决方案是类似的解决注入问题(参数化查询)。也就是说,如果该语句要<=>被称为是一个非常特殊的形式,而不是的所有的需要由用户,只有少数几个变量,数学提交的声明表达,或类似的,你可以在这些小碎片从用户,它们进行消毒必要时再评估安全模板声明与用户输入在适当的地方插入
其他提示
在红宝石有几个噱头可能更适合比 eval()
:
- 那里是
#send
它可以让你呼叫一个方法,其名称如串,并通过参数。 yield
可以让你通过一块代码一种方法,该方法将被执行的背景下接收方法。- 往往简单
Kernel.const_get("String")
足以获得类,其名称为string。
我认为我不能够解释他们正确地详细说明,所以我只是给你的暗示,如果你感兴趣你就会谷歌。
eval
不仅不安全(如已经指出的其他地方),它也是缓慢的。每次执行时,需要<=>编代码的AST被解析(和如JRuby的,变成字节码),新生活,这是一个字符串,繁重的操作,也是可能的缓存位置差(假设下该正在运行的程序不<=>很多,和解释的相应部分因此缓存冷,除了是大)。
为什么会出现在Ruby中所有<=>,你问? “因为我们可以”主要是 - 事实上,当<=>的发明(为LISP编程语言),它是多为显示!更重要的是,使用<=>是正确的事情,当你想“添加一个解释成你的解释”,元编程任务,比如写一个预处理器,调试器或一个模板引擎。这类应用的共同想法是按摩一些Ruby代码,并呼吁<=>它,它肯定比重塑和实施特定于域的玩具语言,一个陷阱也被称为的Greenspun's第十条。该警告是:当心成本,例如,对于一个模板引擎的,做你的<=>荷兰国际集团在启动时不运行时间;不<=>不受信任的代码,除非你知道如何“驯服”它,即根据的能力纪律。后者是一个的很多的真的很难的工作(例如参见怎么说是为Java 完成一>;我不知道的Ruby任何这种努力的不幸)
。有使调试困难。它使优化困难。但最重要的,它通常是一个迹象,表明有一个更好的办法做任何你正在尝试做的。
如果您告诉我们您正在尝试与eval
完成什么,你可能会得到有关您的具体方案更多一些相关的答案。
评估是一种非常强大的功能,应谨慎使用。除了安全问题,指出马特Ĵ,你还会发现,调试运行评估代码是非常困难的。在运行时评估代码块的问题将是困难的解释来表达 - 所以寻找将难以
这就是说,如果你是舒服这个问题,并且不关心安全问题,那么你不应该避免使用,使红宝石的吸引力,因为它的特色之一。
在某些情况下,一个有利的位置是eval
聪明和减少了代码所需的量。除了已经由马特Ĵ提到的安全问题,还需要问自己一个很简单的问题:
当这一切都说过和做过,可谁都看你的代码并理解你做了什么?
如果答案是否定的,那么你就已经获得了与<=>什么是抛弃的可维护性。这个问题不仅适用,如果你在一个团队中工作,但它也适用于你 - 你希望能够在你的代码个月回过头来,从现在开始,如果不是几年,知道你做了什么。
如果你逝去的东西,你从“外部”来eval
得到,你正在做的事情错了,这是非常讨厌的。它的非常的难以逃脱足够的代码,它是安全的,所以我认为这是非常不安全的。但是,如果你使用eval为避免重复或其他类似的东西,如下面的代码示例,这是确定使用它。
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
eval "def #{symbol}; @#{symbol}; end"
end
end
define_getters :foo, :bar, :baz
end
不过,至少在1.9.1,Ruby有真正强大的元编程方法,你可以做,而不是以下内容:
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
define_method(symbol) { instance_variable_get(symbol) }
end
end
define_getters :foo, :bar, :baz
end
在大多数情况下,要使用这些方法,并且不需要转义。
有关<=>的事实是,(至少在红宝石),这是相当缓慢的,因为翻译需要解析字符串,然后执行里面的当前绑定代码中的其他坏事。其他方法直接调用C函数,因此,你应该得到相当的速度提升。