从Ruby中的字符串执行代码?

时间:2015-07-20 20:20:36

标签: ruby

我正在使用元数据方法来处理一些代码。假设以下是一个字符串:

if $bmi >= 18.5 && $bmi <= 24.9 then return 'healthy' elsif $bmi >= 25 && $bmi <= 29.9 then return 'overweight' elsif $bmi > 30 then return 'obese' end

$ bmi被正确替换,但我希望代码能够执行。

1 个答案:

答案 0 :(得分:6)

eval方法会将您的字符串计算为Ruby代码。的 BUT ...

eval是脆弱的

您的字符串包含return关键字,始终从方法调用返回。如果你在方法中eval该字符串,那么它之后的任何代码都将永远不会运行,因为该方法将返回。如果您eval在方法之外,则会出现LocalJumpError异常,因为return仅在方法内有效。我可以想象这会在你的代码中造成各种神秘的错误。

eval很危险

您说元数据来自您的身边,但是当您eval时,您仍然会在代码中打开一个巨大的失败点,因为它可以完全符合任何。对eval的一次调用可能会擦除整个磁盘,它可能会下载并运行漏洞利用程序,它甚至可能会将您的脚本重写为对人类产生仇恨的恶意AI。当你只想进行一些数值比较时,使用这种强大的方法确实没有意义。

eval令人困惑

实际上,使用eval可能很好。您的代码将起作用,世界将不会结束。但即便如此也很糟糕,因为它创造了完全无法读取的代码。任何阅读你的剧本的程序员(包括你自己,六个月后当你回来修复某些东西)都会达到eval并想到“呃......这究竟是做什么的?”在这种情况下,您应该始终在数据库中存储尽可能少的逻辑,并在代码中执行其余的计算。在这种情况下你会怎么做?我可以想到几个选择。

仅存储间隔

使用类似

的数据库架构
table BMIIntervals (
  MinValue    Double,
  Description String
)

您可以存储行

18.5, 'healthy'
25,   'overweight'
30,   'obese'

然后你可以在Ruby中轻松完成计算:

intervals = db.query("SELECT * FROM BMIIntervals ORDER BY MinValue DESC")
intervals.each { |row| return row[1] if $bmi >= row[0] }
return nil

仅存储计算名称

或者如果BMI间隔是固定的,并且元数据说明要做什么样的计算,你可以简单地在Ruby中定义计算:

module MetadataFuncs
  def self.bmi
    return 'obese' if $bmi > 30
    return 'overweight' if $bmi >= 25
    return 'healthy' if $bmi >= 18.5
  end
end

然后您的元数据字符串只是"bmi"而不是eval(metadata),您可以MetadataFuncs.send(metadata)。通过这种方式,您可以确切地知道元数据可能调用的代码,即使没有查看数据库中的数据也是如此。