Reputation: 1
I have the following Ruby code:
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
So, what I'm trying to do here is set the SiteName and then use SiteName and combine it with the string _MONEY_PIT so I can access FOO1_MONEY_PIT and store its contents (500 in this case) in @site_num. Of course, the above code doesn't work, but there must be a way I can do this?
Thanks!!
Upvotes: 0
Views: 46
Reputation: 84443
You can refactor this as to use a Hash for your name lookups, and a getter method to retrieve it for easy testing/validation. For example:
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"
Upvotes: 0
Reputation: 369594
If you want to dynamically get the value of a constant, you can use 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
Do not, under any circumstance, use Kernel#eval
for this. Kernel#eval
is extremely dangerous in any context where there is even the slightest possibility that an attacker may be able to control parts of the argument.
For example, if a user can choose the name of the site, and they name their site require 'fileutils'; FileUtils.rm_rf('/')
, then Ruby will happily evaluate that code, just like you told it to!
Kernel#eval
is very dangerous and you should not get into the habit of just throwing an eval
at a problem. It is a very specialized tool that should only be employed when there is no other option (spoiler alert: there almost always is another option), and only after a thorough security review.
Please note that dynamically constructing variable names is already a code smell by itself, regardless of whether you use eval
or not. It pretty much always points to a design flaw somewhere. In general, you can almost guaranteed replace the multiple variables with a data structure. E.g. in this case something like this:
module BigTime
MONEY_PITS = {
'FOO1' => 500,
'FOO2' => 501,
}.freeze
class LoseMoney
@@SiteName = 'FOO1'
@site_num = MONEY_PITS[@@SiteName]
end
end
Upvotes: 1