5

In Ruby, how can I interpolate a string to make a variable name?

I want to be able to set a variable like so:

"post_#{id}" = true

This returns a syntax error, funnily enough:

syntax error, unexpected '=', expecting keyword_end
t56k
  • 6,769
  • 9
  • 52
  • 115
  • Generally, the need to do this is a code smell, indicating bad choice of data structures. Instead of individual variables `post_1`, `post_2`, `post_N` it is better (in every imaginable way) to have, for example, a hash, `post = { 1 => true, 2 => true, ...}`. – Sergio Tulentsev May 22 '17 at 19:24
  • @Jörg, the OP did not ask how one can create a local variable dynamically. He/she may only want to know how to get or set an existing local variable dynamically. – Cary Swoveland May 23 '17 at 05:13

2 Answers2

6

I believe you can do something like:

  send("post_#{id}=", true)

That would require, of course, that you have appropriate setter/getter. Which, since you're doing this dynamically, you probably don't.

So, perhaps you could do:

  instance_variable_set("@post_#{id}",true)

To retrieve the variable:

  instance_variable_get("@post_#{id}")

BTW, if you get tired of typing instance_variable_set("@post_#{id}",true), just for fun you could do something like:

class Foo

  def dynamic_accessor(name) 
    class_eval do 
      define_method "#{name}" do
        instance_variable_get("@#{name}")
      end
      define_method "#{name}=" do |val|
        instance_variable_set("@#{name}",val)
      end
    end
  end

end

In which case you could:

2.3.1 :017 > id = 2
 => 2 
2.3.1 :018 > f = Foo.new
 => #<Foo:0x00000005436f20> 
2.3.1 :019 > f.dynamic_accessor("post_#{id}")
 => :post_2= 
2.3.1 :020 > f.send("post_#{id}=", true)
 => true 
2.3.1 :021 > f.send("post_#{id}")
 => true 
2.3.1 :022 > f.send("post_#{id}=", "bar")
 => "bar" 
2.3.1 :023 > f.send("post_#{id}")
 => "bar" 
jvillian
  • 19,953
  • 5
  • 31
  • 44
  • Yeah, `send` is out, `instance_variable_set` works nicely. It requires the `@` prefix though, but that's a pretty easy fix and totally makes sense given the name of the function. Thanks! – t56k May 22 '17 at 15:59
0

This concerns the getting and setting of local variables. Suppose

id = 1
s = "post_#{id}"
  #=> "post_1"

Since Ruby v1.8 it has not been possible to create local variables dynamically. Therefore, if a local variable post_1 does not exist, the only way you can create it is by using an assignment statement:

post_1 = false

If the local variable post_1 exists, you can retrieve its value dynamically with

b = binding
b.local_variable_get(s)
  #=> false

(or b.local_variable_get(s.to_sym)) and set its value dynamically with

b.local_variable_set(s, true)
  #=> true
post_1
  #=> true

(or b.local_variable_set(s.to_sym, true)).

See Binding#local_variable_get and Binding#local_variable_set.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100