Search code examples
rubyinitializationinstance-variablesself

Understanding the access of a variable assigned in initialize in Ruby


As a beginner, I've not quite got my head around self so I'm having trouble understanding how the self.blogs in initialize, and blogs then self.blogs on the next line after in the add_blog method, are all working together in the below code.

Why does blogs in the add_blog method access the same variable as self.blogs in initalize? And then why is self.blogs used afterwards to sort the blogs array?

Also, would it matter if I used @blogs in initialize, instead of self.blogs?

class User
  attr_accessor :username, :blogs

  def initialize(username)
    self.username = username
    self.blogs    = []
  end

  def add_blog(date, text)
    added_blog = Blog.new(date, self, text)
    blogs << added_blog
    self.blogs = blogs.sort_by { |blog| blog.date }.reverse
    added_blog
  end
end

Solution

  • For most method calls on self, self.method_name is equivalent to just method_name. That's not the case for methods whose name ends with an =, though.

    The first thing to note, then, is that self.blogs = etc doesn't call a method named blogs and then somehow 'assign etc to it'; that line calls the method blogs=, and passes etc to it as an argument.

    The reason you can't shorten that to just blogs = etc, like you can with other method calls, is because blogs = etc is indistinguishable from creating a new local variable named blogs.

    When, on the previous line, you see a bare blogs, that is also a method call, and could just as easily have been written self.blogs. Writing it with an implicit receiver is just shorter. Of course, blogs is also potentially ambiguous as the use of a local variable, but in this case the parser can tell it's not, since there's no local variable named blogs assigned previously in the method (and if there had been, a bare blogs would have the value of that local variable, and self.blogs would be necessary if you had meant the method call).

    As for using @blogs = instead of self.blogs =, in this case it would have the same effect, but there is a subtle difference: if you later redefine the blogs= method to have additional effects (say, writing a message to a log), the call to self.blogs = will pick up those changes, whereas the bare direct access will not. In the extreme case, if you redefine blogs= to store the value in a database rather than an instance variable, @blogs = won't even be similar anymore (though obviously that sort of major change in infrastructure will probably have knock-on effects internal to the class regardless).