Local and Instance Variables in Ruby with attr_accessor

August 28, 2016 Coding

In most older OOP languages, you have to write your own getters and setters for your object's variables. Ruby allows you to do this, of course, but it also allows you to create methods that are just the name of the variables:


        class ManualAccessors
          def time=(time)
            @time = time
          end
        
          def time
            @time
          end
        end
        

        m = ManualAccessors.new
        => #<ManualAccessors:0x007fea5bb11910>
        m.time = Time.now
        => 2016-08-29 13:06:45 -0500
        m.time
        => 2016-08-29 13:06:45 -0500
        

Ruby gives you a better way. attr_accessor creates a getter and a setter for you:


        class AttrAccessor
          attr_accessor :time
        end
        

        a = AttrAccessor.new
        => #<AttrAccessor:0x007fb7d42fa268>
        a.time = Time.now
        => 2016-08-29 13:08:10 -0500
        a.time
        => 2016-08-29 13:08:10 -0500
        

That's cool.

But there's a simple mistake I've made, easy to avoid now that I understand it, but maybe not so easy before. It occurs when your class gets more complex and you want to use your automatic setter inside the class's other methods.

Be careful with attr_accessor (and attr_writer) inside your class. Just because you have a fancy automatic setter doesn’t mean you can ignore the difference between local and instance variables:


        class InstanceVariablesVsLocalVariables
          attr_accessor :time
        
          def initialize
            self.time = Time.now
            print_time
          end
        
          def set_time_local
            time = Time.now
          end
        
          def set_time_instance
            self.time = Time.now
          end
        
          def print_time
            puts "The time is #{time}."
          end
        end
        

Using self is how you send a message to the instantiated object. And that's all that attr_accessor does: allow your class to respond to a message.


        i = InstanceVariablesVsLocalVariables.new
        The time is 2016-08-28 19:20:58 -0500.
        => #<InstanceVariablesVsLocalVariables:0x007fea5f521f10 @time=2016-08-28 19:20:58 -0500>
        i.set_time_instance
        => 2016-08-28 19:21:35 -0500
        i.print_time
        The time is 2016-08-28 19:21:35 -0500.
        i.set_time_local
        => 2016-08-28 19:21:55 -0500
        i.print_time
        The time is 2016-08-28 19:21:35 -0500.