Reputation: 26066
In PHP I can dynamically access a variable by setting something like $variable_name = foo
and echo $$variable_name
which would then echo
the value of foo
. But I am unclear on how to do this in Ruby. In PHP I could do something like this; assign variable names in an array and iterate through them:
# Set string values.
$value_one = 'a one';
$value_two = 'and a two';
$value_three = 'and a three';
# Set array of variable names.
$process_items_array = array('value_one', 'value_two', 'value_three');
# Roll through the values.
foreach ($process_items_array as $value) {
$$value = do_something($$value) . '<br />';
}
# A simple function for example’s sake.
function do_something ($value = null) {
return strtoupper($value);
}
But in Ruby, what would be the equivalent? For example I have tried this and none of the items work as expected; note that is this psuedo-code as noted by the use of self.
references in a non-class structure:
# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';
# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']
# Roll through the values.
process_items_array.each { |value|
self.send("#{value}").to_sym = do_something value.try(self.send("#{value}").to_sym)
}
# A simple function for example’s sake.
def do_something value
value = value.try(:strip)
value = nil if value.blank?
value.upcase
end
Note my attempted use of instance_variable_get
, [send][2]
and to_sym
; I’m basically hoping something will work but nothing seems to work. What should I be doing instead?
Upvotes: 0
Views: 425
Reputation: 369458
You can use Kernel#binding
to get the current Binding
object and then Binding#local_variable_get
and Binding#local_variable_set
to get and set the local variables:
# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';
# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']
# Roll through the values.
process_items_array.each { |value|
binding.local_variable_set(value.to_sym, do_something(binding.local_variable_get(value).to_sym)
}
# A simple function for example’s sake.
def do_something value
value = value.try(:strip)
value = nil if value.blank?
value.upcase
end
# Show that it works:
p value_one, value_two, value_three
# "A ONE"
# "AND A TWO"
# "AND A THREE"
However, that code is a bit unidiomatic:
Symbol
s for representing the variable names, not String
sdo
/end
instead of {
/}
for a multiline side-effecting blockfoo_array
)You would also have to move the definition of do_something
up before it is used, otherwise you get a NoMethodError
.
And for easier reproducibility, I removed the dependency on the active_support
, so that people trying to test this don't have to install an extra gem which isn't even needed to reproduce the solution.
This would be a more idiomatic version of the code:
# Set string values.
value_one = 'a one'
value_two = 'and a two'
value_three = 'and a three'
# Set array of variable names.
items_to_process = %i[value_one value_two value_three]
# A simple method for example’s sake.
def do_something(value)
value.upcase
end
b = binding
# Roll through the values.
items_to_process.each do |var|
b.local_variable_set(var, do_something(b.local_variable_get(var)))
end
# Show that it works:
p value_one, value_two, value_three
# "A ONE"
# "AND A TWO"
# "AND A THREE"
Upvotes: -1
Reputation: 908
A more ruby way (so without the "dangerous" eval)
# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';
# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']
# Roll through the values.
process_items_array.each do |variable_name|
# Output value
puts binding.local_variable_get(variable_name.to_sym)
# Reassign variable
binding.local_variable_set(variable_name.to_sym, 'foo')
end
puts value_one, value_two, value_three
# foo
# foo
# foo
Upvotes: 1
Reputation: 945
You can do that with eval
:
# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';
# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']
# Roll through the values.
process_items_array.each do |variable_name|
# Output value
puts eval(variable_name)
# Reassign variable
new_value = 'foo'
eval("#{variable_name} = new_value")
end
As it was said in the comments, you might want to store your data within a Hash. Also, code where you need to dynamically create or read variables smells really bad.
Upvotes: 2