2

I want to programmatically insert a new variable into the local Ruby namespace. For example, I want to be able to write

label = 'some_name'
# some code equivalent to
#   some_name = 3
# but using only 'label' to get the name.
puts some_name  # returns 3

What do I put in the middle here to get this done?

6
  • When you say symbol, I guess you are not referring to symbols in Ruby, right (e.g. :a_symbol)? And what exactly is label for if it's not used in the later lines of your code? Commented Aug 11, 2011 at 2:57
  • @mikong: thanks, I've clarified the question. I want some_name to be available later on in the code. You can assume that label is just a mechanism for knowing what to call the variable. Commented Aug 11, 2011 at 3:03
  • At first I was thinking you should just use Ruby constants. But it seems you want to change the label arbitrarily, and your puts line can just be changed to the new name, correct? I'll write the solution in a bit, after you confirm. Commented Aug 11, 2011 at 3:17
  • 2
    You should really just use a Hash. Mucking around with eval makes your code uglier, slower less idiomatic and more error-prone. Commented Aug 11, 2011 at 3:58
  • @Chuck: ordinarily it would be horrendous practice, yes - however, in this particular case I want to support a certain DSL for my users that requires setup like this. It's certainly not being used as a general-purpose programming technique :) Commented Aug 11, 2011 at 16:27

3 Answers 3

3

I've answered another SO question similar to this. The short answer is this, if you specifically want to create a local variable with the name of it based on the value of another variable, then there is no way to do it. It you just want to make seem as though you've created a local but it is really ruby magic, then something like @mikong's answer is one way to go.

Note that if you relax your contraint and are happy to create an instance variable instead, then you can do it.

label = 'some_name'
self.instance_variable_set("#{label}", 3)
puts @some_name

You can even dynamically define an accessor and then you can get rid of the unsightly @, but once again you will simply have a method masquerading as a local rather than a real local variable.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 for the attr_reader dynamically defined. This lets me achieve my goals.
2

The following is not exactly code between the 2 lines that you mentioned above:

class Example
  attr_accessor :label

  def method_missing(name, *args, &block)
    return some_processing  if name == label.to_sym
  end

  def some_processing
    3 # of course, this can be something more complicated
  end

  def test
    @label = 'some_name'
    puts some_name
  end

end

Nonetheless it seems to work with what you need. The mechanism has changed from what you gave (label is now an attribute). Also, technically, it's not a variable but a method with a dynamic name that returns what you need.

Personally, I think your requirements seem a little bit dangerous in that the "variable" name changes. I would probably not use the code in my example. I guess depending on the project requirements, I'll think of a different approach.

Comments

2
label = 'some_name'
eval "#{label} = 3"
puts eval "#{label}"
puts local_variables

Note that you would presumably never have an opportunity to execute...

puts some_name

...because if you knew what local variables you were going to create there would be no need to name them with run-time code. And that's good, because the interpreter will not be able to puts some_name directly because it never parsed an assignment for some_name. But it is there and it is a local, as puts local_variables is able to show.

3 Comments

Interesting. All four lines of your code work for me in ruby-1.8.7-p334. However if I run the same program in ruby-1.9.2-p180 then puts local_variables outputs only "label" and puts eval "#{label} raises an exception: test.rb:3:in 'eval': undefined local variable or method 'some_name' for main:Object (NameError).
That's because in 1.9 local variables are only available within the context of the eval where they were defined. So you define a local within one eval and try to use it within another, you'll get ... well exactly what you got :).
Hmm. So it might be a good idea to either switch to creating instance variables rather than locals, or just use a more complex data structure in the first place so you don't need to dynamically create individual names. It's hard to see the benefit of creating Ruby-level names over a simple Hash key. (I try to avoid giving "don't do that in the first place" advice, since sometimes the motivation is academic curiosity. But in this case...)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.