I am in the midst of learning Ruby and thought I was clever with the following piece of code:
[@start,@end].map!{ |time| time += operation == :add ? amount : -(amount) }
where @start, @end are two module level variables, operation can be one of :add or :sub, and amount is an float amount to adjust both @start and @end by.
Granted it only saves me a line of code, but why doesn’t this approach work, and how can I get something similar that does?
(My expected output is for @start/@end to be modified accordingly, however unit tests show that they stay at their original values.)
It’s important in Ruby to remember the distinction between variables and the objects they hold. Simply setting a variable will never change the object referenced by that variable. When you do
a += b, it’s just shorthand fora = a + b. So you’re assigning a new value to the variable a, not changing the object that used to be there or changing any other references to that object. So changing the variabletimedoesn’t change@start.In order to assign to an instance variable, you need to actually assign to that instance variable. Here’s a way to do what you were looking for:
You’ll notice that we’re not faffing around with that
:addand:subbusiness either — we can just pass the actual name of the message we want to send (I used + in this case, but it could be anything).If you had a big, dynamically generated list of ivars you wanted to set, it’s only a little bit more complicated. The only difference there is that need to get and set the ivars by name.