Fork me on GitHub
Subscribe to RSS Feed

Elijah Miller

14 Oct 2013

Selfless Ruby

There are only a few occasions when you need to use self prefix in Ruby. Understanding these only takes a few minutes and can help you avoid some pretty common bugs and wasteful debugging attempts.

Avoiding local variable assignment

Calling a writer in the current scope, as in:

class User
  def verify
    self.verified = true
  end

  def verified=(value)
    @verified = value
  end
end

If you didn’t prefix self to that assignment, you would be making a new local variable and setting it to true. This variable has a lifetime of 1 statement, probably not what you were looking for and an easy bug to miss.

Side-stepping reserved words

Occasionally you need to call a method in the current scope that has the same name as a Ruby keyword. An instance wanting to introspect its its own class is a very common use.

class Song < ActiveRecord::Base
  def find_dupliactes
    self.class.find_by_name(name)
  end
end

In this case you are explicitly side-stepping the global behavior of the keyword. If you omit self, Ruby thinks you’re about to define a new class, and is going to give you a syntax error.

Defining class methods

class Store < ActiveRecord::Base
  def self.locate_by_zip_code(zip_code)
    # some fancy searching code
  end
end

The use of self in this example tells Ruby that we’re making methods on the Store class and not its instances.

That’s it!

Three simple cases where you need to use self. All other uses are just being overly explicit.

Gotcha: Tricky sub-objects

Sub-objects only need to use the self prefix when you want to assign the whole object at a time. For example when initializing or setting an entire Hash, Array, or some other object.

If you’re actually just manipulating the other object, you don’t need to call self, because you’re really calling two methods. In this case one is the reader, the other is the [] method on a Hash.

class Server
  attr_accessor :options

  def initialize
    # required, we want to call Server#options=
    self.options = {}
  end

  def toggle_warnings
    # not required, options[:warnings] is actually two method calls:
    #  * Server#options
    #  * Hash#[]
    options[:warnings] = !options[:warnings]
  end
end