Wicked Jester
Wicked Jester

Reputation: 1

How do I tack a string onto a variable and evaluated the entire thing as a variable in Ruby?

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

Answers (2)

Todd A. Jacobs
Todd A. Jacobs

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

Jörg W Mittag
Jörg W Mittag

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

Related Questions