我有以下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中。当然,上面的代码不起作用,但我一定有办法做到这一点?
谢谢!!
如果您想动态获取常量的值,可以使用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#eval
。Kernel#eval
在任何情况下都是极其危险的,即使攻击者能够控制部分自变量的可能性很小。
例如,如果用户可以选择网站的名称,并将其网站命名为require 'fileutils'; FileUtils.rm_rf('/')
,那么Ruby将很高兴地评估该代码,就像你告诉它的那样!
Kernel#eval
是非常危险的,你不应该养成只在问题上抛出eval
的习惯。这是一个非常专业的工具,只有在没有其他选择的情况下(剧透提醒:几乎总是有另一个选择(,并且只有在彻底的安全审查之后才能使用。
请注意,无论是否使用eval
,动态构建变量名本身就已经是一种代码气味。它几乎总是指向某个地方的设计缺陷。一般来说,几乎可以保证用数据结构替换多个变量。例如,在这种情况下,类似于以下内容:
module BigTime
MONEY_PITS = {
'FOO1' => 500,
'FOO2' => 501,
}.freeze
class LoseMoney
@@SiteName = 'FOO1'
@site_num = MONEY_PITS[@@SiteName]
end
end
您可以将其重构为使用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"