I have a class and I have a test. And everything is great until I add next test…
So, I had something like this:
class Formatter class << self TITLE = 'TEMPLATE TITLE' def format(records) output = TITLE # Initializing? records.each do |record| output << format_string_from(record) # Modifing `output` only? end output # Strange behaviour? end end end
Looks pretty harmless from the first glance. But then you see awkward behaviour: output gets concatenated with previous calls of the
format method.. why???
Let’s try some
2.7.0 :002 > string.object_id => 180 2.7.0 :003 > another_string = string 2.7.0 :004 > another_string => "I am a string. " 2.7.0 :005 > another_string.object_id => 180 2.7.0 :006 > another_string << "And now I am modifying the original string" => "I am a string. And now I am modifying the original string" 2.7.0 :007 > another_string.object_id => 180 2.7.0 :008 > string => "I am a string. And now I am modifying the original string" 2.7.0 :009 >
So as you can see
another_string = string looks like you are initializing
another_string, but you actually
got another reference to the same string, and any modification of the value of
another_string will be reflected in
string because they are references for the same string.
You can modify string with
In case if you are reassigning (creating new string) you will receive new
object_id and in that case old variable
string will be unchanged
2.7.0 :025 > string = "I am a string" 2.7.0 :026 > another_string = string 2.7.0 :027 > string.object_id => 240 2.7.0 :028 > another_string.object_id => 240 2.7.0 :029 > another_string = "New" 2.7.0 :030 > another_string.object_id => 260 2.7.0 :031 > string.object_id => 240 2.7.0 :032 > string => "I am a string"
So as you can see from the example above, after reassignment of
another_string it received new
object_id 260 and old
string stayed 240
Same applies to any object in Ruby, i.e. arrays, hashes.
And yeah, I should’ve used
TITLE = 'Text'.freeze for my constant 😅
Have a great weekend ahead!