6

I have variables

 <% mon_has_two_sets_of_working_hours = 0 %>
 <% tue_has_two_sets_of_working_hours = 0 %>
 <% wed_has_two_sets_of_working_hours = 0 %>

I want to change the values of these variables dynamically.

 <% days_array = ['mon', 'tue', 'wed'] %>

 <% days_array.each do |day| %>
   <% if condition? %>
    # here i want to set %>
     <% "#{day}__has_two_sets_of_working_hours" = 1 %>
  end
 end

The value is not getting assigned. Is there any way to assign value to variable dynamically?

3
  • 4
    Do, do, do use arrays (or hashes) for that. Commented Jan 8, 2011 at 12:10
  • 3
    The answer to 2530112 should help, it recommends instance_variable_set. Commented Jan 8, 2011 at 12:18
  • 1
    Dynamically creating a variable name on the fly is doable in some languages, including Ruby, but has been falling out of favor for years, and is considered a curiosity mostly. It leads to confusion which leads to maintenance problems, so sidestep the problem and use a hash. It can also lead to security problems if the variable names are coming from user-supplied input, or could cause weird bugs if a name collided with a previously created variable. Commented Jan 8, 2011 at 16:13

3 Answers 3

4

I don't think there is a way to do this. There is with instance or class variables, but with local variables there is very rarely a good need.

In your case you really should have the data in a hash. Also, logic like this really does not belong in erb. You want something like:

working_hour_sets = %w[mon tue wed thu fri sat sun].inject({}) do |hash, day|
  hash[day]=0;
  hash
end
# puts working_hour_sets #=> {"wed"=>0, "sun"=>0, "thu"=>0, "mon"=>0, "tue"=>0, "sat"=>0, "fri"=>0}

working_hour_sets.each do |day, value|
  working_hour_sets[day] = 1 if condition?
end
Sign up to request clarification or add additional context in comments.

2 Comments

It is doable using eval, but you are right saying that it should not be done that way. Using a hash instead is a better solution.
Or you could pass this block to inject: {|hash,day|hash.merge(day=>0)}
2

Now, I know this question is a bit old, but there is an easier way to do this and is using the standard Ruby send method. This is actually one of the methods that make Ruby so agile in the metaprogramming world.

This is actually a config setting I use in a Rails app:

# In a YAML    
twitter:
  consumer_key: 'CONSUMER-KEY'
  consumer_secret: 'CONSUMER-SECRET'
  oauth_token: 'OAUTH-KEY'
  oauth_token_secret: 'OAUTH-SECRET'

...

# And in your file.rb
config = YAML.load_file(Rails.root.join("config", "social_keys.yml"))[Rails.env]['twitter']

Twitter.configure do |twitter|
  config.each_key do |k|
    twitter.send("#{k}=", config[k])
  end
end

It's DRY and very easy to understand. :)

Comments

0

Yet another answer to this old question.

In my scenario, I wanted to count how many times a day showed up in an array of days (day_array). I didn't need to know if a day didn't show up in day_array, so I didn't initialize the days_count hash as gunn did in his answer.

Here's how I did it:

def count_days(day_array)
  days_count = {}
  day_array.each do |day|
    days_count[day].nil? ? days_count[day] = 1 : days_count[day] = days_count[day] + 1
  end
  puts days_count
end

If I copy and paste the above in irb, then:

> count_days(%w[SU MO])
{"SU"=>1, "MO"=>1}

> count_days(%w[SU SU MO])
{"SU"=>2, "MO"=>1}

Basically, consistent with prior answers. But, I thought an additional example couldn't hurt.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.