重构 eval(some_variable).is_a?(Proc) 以不使用 eval

Refactor eval(some_variable).is_a?(Proc) to not use eval

我有一些旧代码,如下所示:

some_variable = "-> (params) { Company.search_by_params(params) }"
if eval(some_variable).is_a?(Proc)
...

Rubocop 抱怨 eval 的使用。关于如何删除 eval 的用法的任何想法?

我不是很了解 Proc,所以如果有任何相关指导,我们将不胜感激。

简单。不要将变量对象定义为字符串,而是定义为 lambda Proc

my_lamda = -> (params) { Company.search_by_params(params) }
if my_lambda.is_a?(Proc)
  #do stuff
end

但是,当您可以定义 Proc 而不是时,为什么要实例化一个包含看似普通 lambda which is a Proc 的字符串对象呢?

我要回答问题"If I want to run code at a later time, What is the difference between using a proc and a eval'd string?"(我认为这是你的问题和困惑的一部分):

eval 所做的是获取一个字符串并将其解析为代码,然后 运行 对其进行处理。该字符串可以来自任何地方,包括用户输入。但是 eval 非常不安全且有问题,尤其是 与原始用户输入一起使用时。

problems with eval通常是:

  • 几乎总有更好的方法
  • 非常危险和不安全
  • 调试困难

使用 eval 可以完全控制 ruby 进程,如果您对 ruby 进程授予高权限,甚至有可能获得机器的 root 权限。因此,一般建议仅在您绝对没有其他选择时才使用 'eval',尤其是在用户输入的情况下。

Procs/lambdas/blocks 还可以让您保存代码供以后使用(并解决了 eval 的大部分问题,它们是 "better way")但不是将任意代码存储为字符串以供以后读取,它们已经是代码,已经解析并准备就绪。在某种程度上,它们是您可以稍后传递的方法。制作一个 proc/lambda 给你一个带有 #call 方法的对象。然后,当您以后想要 运行 proc/block/lambda 时,您可以调用 call([arguments...])。你不能用 procs 做的是让用户编写任意代码(通常这很好)。您必须在 ruby 加载的文件中编写 proc 的代码(大部分时间)。 Eval 确实解决了这个问题,但如果您真的希望它成为可能,您真的应该重新考虑一下。

您的代码示例奇怪地结合了这两种方法:它将字符串计算为 lambda。所以这里发生的事情是 eval 是立即 运行 字符串中的代码并返回(最后)结果,在本例中恰好是 lambda/proc。 (请注意,每次您 运行 eval 时都会发生这种情况,这将导致 proc 的多个副本,具有不同的身份,但具有相同的行为)。由于字符串中的代码恰好构成了一个 lambda,因此返回的值是一个 Proc,稍后可以是 #call。所以当eval为运行时,解析代码,并创建一个新的lambda,lambda中的代码存储为稍后的运行。如果字符串中的代码没有创建 lambda,则当使用字符串调用 eval 时,所有代码将立即 运行。

这种行为可能是需要的,但可能有更好的方法来做到这一点,这绝对是一把脚枪:如果你不这样做,这段代码至少有六种微妙的方式可以做一些意想不到的事情真的不小心。