Is there no way in Ruby to dynamically determine a local variable in the current context?

I am wondering if there is a method that will allow me to dynamically define a previously undefined variable in the current context. For example:

foo # => NameError: undefined method or local variable ... # Some method call which sets foo = 1 in the local context foo # => 1 

In other words, if foo is undefined, I look for any code that would allow me to define a local variable foo without using the variable foo (for example, if I had another bar variable whose value was :foo , and I had to rely on it, to set the value to foo ).

It seems that eval('foo = 1') or eval('foo = 1', binding) or, in Ruby 2.1, binding.local_variable_set(:foo, 1) equivalent:

 1.times do foo = 1 end 

In other words, they set foo in the context of the new local context, so the value is not available outside this context.

I want to make this possible?

Update: this question is not specific to any particular context of a local variable (module / class, method, proc, block, etc.). I would be interested to know definitively in any context where this is possible or cannot be done.

+9
ruby local-variables
source share
3 answers

It seems that Ruby magic would provide a way, but according to Matz, this was only possible in 1.8 via eval and only in certain contexts (i.e. irb ). Starting from 1.9, this behavior has been removed ("strictly forbidden"):

Matz himself weighs here: https://www.ruby-forum.com/topic/155673#685906

I read from somewhere that now Ruby cannot dynamically create a local variable. Is this true or just a mistake?

Local variables are created at compile time, so local variables that are defined in eval () cannot be accessed outside of Eval. In 1.8, irb and tryruby do line compilation, so local variables spill out from eval (), but in 1.9 this is strictly forbidden even by line compilation.

  matz. 

(An alternative alternative is here for those who want something like this, but not the exact technical situation they are asking about):

Use the hash:

 local_hash = {} my_vars.each_pair do |k,v| local_hash[k] = v end puts local_hash['foo'] #=> 'baz' 
+9
source share

In the context of creating the local variable itself, it is true that there are some difficulties to overcome, however, the dynamic task assignment still does not present a problem.

 >> my_lv = 0 => 0 >> instance_eval("#{'my_lv'} = 42") => 42 >> my_lv => 42 

So, just create from the collected input (from gets , chomped or stripped as needed, it will just naturally end as a string) and call to_sym on it and add a new character to local_variables and evade ...

 >> local_variables << :my_created_lv => [:my_lv, :__, :_, :_dir_, :_file_, :_ex_, :_pry_, :_out_, :_in_, :my_created_lv] >> 

Then you take the assembled string that you converted to a character and assigned in the code shown above and evaluate it to get the value.

 >> eval :my_lv.to_s >> 24 

As noted in another answer, I cannot easily reproduce this outside of Pry or IRB.

This has changed in future versions of Ruby, and Matz has removed and is working hard to make this no longer possible.

+2
source share

Will a class instance variable work for you?

 class Cat end Cat.instance_variable_set '@last_words', 'meow, meow, me...' Cat.instance_variable_get '@last_words' # => "meow, meow, me..." Cat.new.instance_variable_get '@last_words' # => nil 

If not, please clarify the context and how you will use the local variable.

0
source share

All Articles