How to dynamically create a local variable?
You cannot dynamically create local variables in Ruby 1.9+ (you could in Ruby 1.8 via eval
):
eval 'foo = "bar"'
foo # NameError: undefined local variable or method `foo' for main:Object
They can be used within the eval-ed code itself, though:
eval 'foo = "bar"; foo + "baz"'
#=> "barbaz"
Ruby 2.1 added local_variable_set
, but that cannot create new local variables either:
binding.local_variable_set :foo, 'bar'
foo # NameError: undefined local variable or method `foo' for main:Object
This behavior cannot be changed without modifying Ruby itself. The alternative is to instead consider storing your data within another data structure, e.g. a Hash, instead of many local variables:
hash = {}
hash[:my_var] = :foo
Note that both eval
and local_variable_set
do allow reassigning an existing local variable:
foo = nil
eval 'foo = "bar"'
foo #=> "bar"
binding.local_variable_set :foo, 'baz'
foo #=> "baz"
How to dynamically create local and global variables?
You cannot define a local variable with a full stop (.
) character in ruby. That is not valid syntax.
(eval):2: unexpected fraction part after numeric literal
string_1.0 = "1.0"
Additionally, you cannot dynamically define local variables. There are various workarounds to sort-of achieve this, however, fundamentally I think you are asking an XY problem.
For example, have you considered using an OpenStruct
, or passing this hash as locales
when rendering a template, or instead dynamically setting instance variables?
Dynamically set local variables in Ruby
The problem here is that the block inside each_pair has a different scope. Any local variables assigned therein will only be accessible therein. For instance, this:
args = {}
args[:a] = 1
args[:b] = 2
args.each_pair do |k,v|
key = k.to_s
eval('key = v')
eval('puts key')
end
puts a
Produces this:
1
2
undefined local variable or method `a' for main:Object (NameError)
In order to get around this, you could create a local hash, assign keys to this hash, and access them there, like so:
args = {}
args[:a] = 1
args[:b] = 2
localHash = {}
args.each_pair do |k,v|
key = k.to_s
localHash[key] = v
end
puts localHash['a']
puts localHash['b']
Of course, in this example, it's merely copying the original hash with strings for keys. I'm assuming that the actual use-case, though, is more complex.
How do I dynamically create a local variable in Ruby?
You have to use the correct binding. In IRB for example this would work:
irb(main):001:0> eval "t=2", IRB.conf[:MAIN_CONTEXT].workspace.binding
=> 2
irb(main):002:0> local_variables
=> [:t, :_]
irb(main):003:0> eval "t"
=> 2
irb(main):004:0> t
=> 2
In Ruby, is there no way to dynamically define a local variable in the current context?
It seems that Ruby's 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
). As of 1.9, this behavior was taken out ("strictly forbidden"):
Matz himself weighs in here: https://www.ruby-forum.com/topic/155673#685906
I read from somewhere that now Ruby can't dynamically create local variable. Is it true or just a bug?
The local variables are created in compile time, so that local
variables that are defined in eval() cannot be accessed outside of
eval. In 1.8, irb and tryruby does line by line compilation so that
local variables are spilled from eval(), but in 1.9, it's strictly
prohibited even under line-by-line compilation.matz.
(Non-sequitur alternative here, for anyone who wants something like this but not the exact technical situation that the questioner has):
Use a hash:
local_hash = {}
my_vars.each_pair do |k,v|
local_hash[k] = v
end
puts local_hash['foo']
#=> 'baz'
How to create dynamic global and local variables?
Take this answer with a grain of salt because I'm no expert on Ruby's eval
, but this worked for me:
require 'json'
hash = '{"$a":5,"b":10}'
hash1 = JSON.parse(hash)
bind = binding
hash1.each do |k,v|
# Dunno what this is for and things work fine without it.
# singleton_class.send(:attr_accessor,k)
# You don't want to call `send` here, just call `eval` directly.
eval("#{k}=#{v}", bind)
end
puts $a
# => 5
puts b
# => 10
Note that I'm passing in a binding from the parent scope to eval
since you'll want to access local variables such as b
in the parent scope after you exit the loop.
Lastly, v
needed to be part of the string passed to eval
, not the second parameter passed to eval
, which, if anything, needs to be a binding. If you pass an integer it will raise an exception.
However, I should warn you that global variables should be used with restraint, and that using eval
can be dangerous, especially if you get this JSON from someone else.
Dynamic variable from each method
As other commenters suggested, you may need a hash instead of a dynamic variable:
collection = ["s", "g", "l"]
collection.each do |key|
devan[key] = ['h1', 'h2', 'h3']
devan[key].each do |s1|
puts "from #{key} we got #{s1}"
end
end
With a hash, you get "for free" an arsenal of easy to use methods, which would otherwise be more cumbersome with dynamic variables. For example:
# Add an element to the array for the 's' key:
devan['s'].push 'h4'
# Add an element to the array for each key:
devan.each_key { |k| devan[k].push 'h5' }
# Print array length for each key:
devan.each { |k,v| puts v.length }
Dynamically create Ruby variables with string interpolation
You cannot dynamically set local variables
in this manner because what you are actually trying to do is set a String
. Your code is interpreted as follows
"member1" = Fabricate(:user)
Which will raise a SyntaxError
for unexpected =
because you cannot set a String
to anything.
You can however perform this operation with instance_variables
like so:
4.times do |n|
instance_variable_set("@member#{n}", Fabricate(:user))
end
Then access them with @member1
,@member2
, etc.
To answer your second question is no send
and eval
have no particular use in this case
creating and passing dynamic local variables to partials
In the controller, along with the array of placeholder values, I did
@i = 0
The forms_for partial was being rendered 4 times because I created 4 answers.
4.times { @question.answers.build}
Therefore, after each rendering of the partial, I incremented the @i instance variable and passed the incremented value into the @placeholder array @placeholder[@i]
and inside the partial used the appropriate element of the array
<%= f.fields_for :kanswers do |builder| %>
<%= render :partial => 'kanswer_fields', :locals => { :f => builder, :k => @placeholder[@i]} %>
<% @i += 1 %>
<% end %>
Related Topics
Warning: Constant ::Fixnum Is Deprecated When Generating New Model
What Does the "$" Character Mean in Ruby
Guard with Rspec on Rails 4 Giving a Lot of Warnings
How to Alter the Timezone of a Datetime in Ruby
How to Run Ruby and Git Commands in One Place on Windows
How to Iterate Activerecord Attributes, Including Attr_Accessor Methods
Rails Forms for Has_Many Through Association with Additional Attributes
Rails 3. How to Display Two Decimal Places in Edit Form
Ruby on Rails: How to Explicitly Define Plural Names and Singular Names in Rails
Strange Inability to Require Config/Boot After Upgrading to Ruby 1.9.2
How to Save an Object to a File
How to Debug in Rubymine 4.5 Using Ruby 1.9.3