<% @lan_??? = Lan.find(lan_role.lan_id) %>
  • <% @lan_??? = Lan.find(lan_role.lan_id) %>
  • <% @lan_??? = Lan.find(lan_role.lan_id) %>
  • How do I sub in data in a variable name, Ruby?

    91 Views Asked by At

    So I've got

    LanRole.where("lan_id = ?", requestable.id).each do |lan_role| %>
        <% @lan_??? = Lan.find(lan_role.lan_id) %>
        <li><a data-toggle="tab" href="#lan_info_<%= lan.id %>"><%= lan.name %> INFO</a></li>
    

    Where ??? should be lan_role.id so that i can put it in the tab later

    <div class="tab-content">
      <div id="lan_info_<%= lan.id %>" class="tab-pane fade">
        <h4><%= @lan_???.name %> INFO</h4>
        etc
      </div>
    

    But I don't know how to make a variable variable name, sub it in there.

    If it was a string, I could do

    #{lan_role.id}
    

    but it's not a string.

    ... I misread the error, it's on <div id="lan_info_<%= lan.id %>" class="tab-pane fade">

    ActionView::Template::Error (undefined local variable or method `lan' for #<#<Class:0x007f7afb9e98a0>:0x007f7afb28aeb0>):
    
    3

    There are 3 best solutions below

    0
    Alex On

    For "variable" variables:

    {one: 1, two: 2, three: 3}.each do |name, id|
      instance_variable_set("@lan_#{name}", [id, name])
      p @lan_one, @lan_two, @lan_three
    end
    
    [1, :one]
    nil
    nil
    [1, :one]
    [2, :two]
    nil
    [1, :one]
    [2, :two]
    [3, :three]
    

    But since you're making tabs, just have two loops, one for links and one for bodies:

    collection = LanRole.where("lan_id = ?", requestable.id)
    
    # make tabs
    collection.each do |role|
      role.name
    end
    
    # make tab contents
    # collection is now loaded it won't hit the database again, at least it shouldn't
    collection.each do |role|
      role.description
    end
    
    0
    Allacassar On

    you can do it bu using instance_variable_set and instance_variable_get like this:

    <% LanRole.where("lan_id = ?", requestable.id).each do |lan_role| %>
        <% instance_variable_set(:"@lan_#{lan_role.lan_id}", Lan.find(lan_role.lan_id)) %>
    <% end %>
    

    Then when you try to get it out use:

    <h4><%= instance_variable_get(:"@lan_#{lan_role.lan_id}").name %> INFO</h4>
    
    0
    spickermann On

    You can use instance_variable_set and instance_variable_get to set and get instance variables with dynamic names, but the is cumbersome. And unless you are doing meta-programming (and especially in your example) I would call using those methods a code-smell.

    Instead, the go-to data structure to store data by a key is using a Hash. In your example, utilizing a Hash to store data by its id could look like this:

    <% @lans_by_id = {} %>
    <% LanRole.where("lan_id = ?", requestable.id).each do |lan_role| %>
      <% @lans_by_id[lan_role.lan_id] = Lan.find(lan_role.lan_id) %>
      # ...
    

    And you can read the data similarly later in your code:

    <h4><%= @lans_by_id[lan_role.lan_id].name %> INFO</h4>
    

    But: You tagged your question with Ruby on Rails and instances of LanRole have a lan_id attribute already. Therefore, I suggest that you take advantage of Ruby on Rails and configure proper Active Record Associations between your models.

    When you configure the associations between LanRole and Lan like this:

    # in app/models/lan_role.rb
    class LanRole < ApplicationRecord
      belongs_to :lan
      # ... 
    
    #in app/models/lan.rb
    class LanRole < ApplicationRecord
      has_many :lan_roles # or just `has_one :lan_role`?
    

    When you have those associations set up in your models, then you can get the lan for a given lan_role by calling lan_role.lan instead of Lan.find(lan_role.lan_id). And to avoid loading one lan in each block of the each iteration, I recommend eager loading all associated lanswith includes at once.

    Putting is all together: Loading and iterating

    LanRole.where(lan_id: requestable.id).includes(:lan).each do |lan_role| %>
    

    And then later using the associated lan like this:

    <h4><%= lan_role.lan.name %> INFO</h4>