Search code examples
ruby

understanding ruby class attributes, using accessor macros and self


So I create a class in ruby:

class User
  def initialize
  end
end

Now say I want to create a attribute that is a hash with a getter/setter, I'm confused as to the options I have doing this.

If I do this:

class User
  attr_accessor :some_hash
end

But I don't want this hash to be nil ever, always an empty hash.

I'm confused when I should do:

def some_hash
  self.some_hash ||= {}
end

and do:

def some_hash
  @some_hash ||= {}
end

What is the difference?

If I don't use the attr_accessor, I have to create both the reader and writer (or getter/setter):

def some_hash=()
end

def some_hash
end

I'm hoping someone can clear up the options I would have for creating a some_hash attribute which is a hash, and that never returns a nil if it is empty.

i.e. using attr_accessor, manually creating the methods, and finally when to use @some_hash and self.some_hash


Solution

  • attr_accessor :some_hash defines reader and writer methods for the given attribute. It is equivalent to this:

    class User
      def some_hash
        @some_hash
      end
    
      def some_hash=(a_hash)
        @some_hash = a_hash
      end
    end
    

    @some_hash refers to an instance variable of the object, while some_hash and some_hash= are methods. The former returns the value of the variable, the latter sets it.

    The idiom self.some_hash ||= {} is equivalent to self.some_hash || self.some_hash = {}.

    Boolean operators in Ruby short circuit, which means the second expression (self.some_hash = {}) will not be executed at all if the first expression (self.some_hash) returns a true value.

    The method:

    def some_hash
      self.some_hash ||= {}
    end
    

    Is actually recursive, since it expands to some_hash || self.some_hash = {}. It will keep calling itself until you get a stack overflow. Use the second form:

    def some_hash
      @some_hash ||= {}
    end
    

    Since it sets the instance variable directly, you will not have problems with recursion, nor will you have to call a writer method. some_hash can never return nil, because if @some_hash is nil, it will be assigned an empty hash before the method returns.

    By the way, this is also called lazy initialization, because the variable is initialized when it is first accessed, not when the User instance is created.