【问题标题】:How do I tack a string onto a variable and evaluated the entire thing as a variable in Ruby?如何将字符串跟踪到变量中并将整个事物评估为 Ruby 中的变量?
【发布时间】:2021-02-03 17:09:23
【问题描述】:

我有以下 Ruby 代码:

module BigTime
  FOO1_MONEY_PIT = 500
  FOO2_MONEY_PIT = 501
  
  class LoseMoney
    @@SiteName = 'FOO1'
    @site_num = @@SiteName_MONEY_PIT
    def other_unimportant_stuff
      whatever
    end
  end
end

所以,我在这里要做的是设置 SiteName,然后使用 SiteName 并将其与字符串 _MONEY_PIT 组合,这样我就可以访问 FOO1_MONEY_PIT 并将其内容(在本例中为 500)存储在 @site_num 中。上面的代码当然不行,但是一定有办法可以做到吗?

谢谢!!

【问题讨论】:

  • 可以做到,但它很笨拙,需要一些元编程。你最好使用哈希。

标签: ruby variables dynamic


【解决方案1】:

如果要动态获取常量的值,可以使用Module#const_get

module BigTime
  FOO1_MONEY_PIT = 500
  FOO2_MONEY_PIT = 501
  
  class LoseMoney
    @@SiteName = 'FOO1'
    @site_num = BigTime.const_get(:"#{@@SiteName}_MONEY_PIT")
  end
end

不要不要,在任何情况下,都使用Kernel#evalKernel#eval 在任何情况下都非常危险,即使攻击者有可能控制部分参数的最轻微

例如,如果用户可以选择站点名称,并将其站点命名为 require 'fileutils'; FileUtils.rm_rf('/'),那么 Ruby 将愉快地评估该代码,就像您告诉它的那样! p>

Kernel#eval 非常危险,您不应该养成在问题上直接扔eval 的习惯。这是一个非常专业的工具,只有在没有其他选择的情况下才应该使用(剧透警告:几乎总是有其他选择),并且只有在经过彻底的安全审查之后才能使用。

请注意,无论您是否使用eval,动态构造变量名本身已经是一种代码异味。它几乎总是指向某个地方的设计缺陷。一般来说,您几乎可以保证用数据结构替换多个变量。例如。在这种情况下是这样的:

module BigTime
  MONEY_PITS = {
    'FOO1' => 500,
    'FOO2' => 501,
  }.freeze
  
  class LoseMoney
    @@SiteName = 'FOO1'
    @site_num = MONEY_PITS[@@SiteName]
  end
end

【讨论】:

  • 在您的第二个示例中,@site_num 在类定义时间之外无法访问。 BigTime::LoseMoney.new.instance_variable_get :@site_num #=> nil
  • 是的,OP就是这样设计的。
  • 感谢您的回答,我已经按照您在上一个示例中提出的建议进行了尝试,但似乎不起作用。但是,让我提供更多信息。我实际上有一个我正在尝试访问的数组,这可能会使这复杂化。示例:`模块 BigTime SITE1 = 20 MONEY_PITS[SITE1][:site_num] = 500 SITE2 = 40 MONEY_PITS[SITE1][:site_num] = 501 类 LoseMoney @@SiteName = 'SITE1' @site_num = MONEY_PITS[@@SiteName][ :site_num] end end ` @site_num 应该设置为 500,但 @@SiteName 似乎没有被正确解释。
  • 抱歉,不知道怎么让cmets格式好...
【解决方案2】:

您可以将其重构为使用 Hash 进行名称查找,并使用 getter 方法来检索它以便于测试/验证。例如:

module BigTime
  MONEY_PITS       = { FOO1: 500, FOO2: 501 }
  MONEY_PIT_SUFFIX = '_MONEY_PIT'
  
  class LoseMoney
    @@site = :FOO1

    def initialize
      site_name
    end

    def site_name
      @site_name ||= '%d%s' % [MONEY_PITS[@@site], MONEY_PIT_SUFFIX]
    end
  end
end

BigTime::LoseMoney.new.site_name
#=> "500_MONEY_PIT"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 2019-07-09
    • 2010-10-07
    • 2010-09-28
    • 1970-01-01
    • 2014-08-31
    相关资源
    最近更新 更多