When to use 'self' in Ruby
Asked Answered
D

2

10

This method:

  def format_stations_and_date
    from_station.titelize! if from_station.respond_to?(:titleize!)
    to_station.titleize! if to_station.respond_to?(:titleize!)
    if date.respond_to?(:to_date)
      date = date.to_date
    end
  end

Fails with this error when date is nil:

NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.to_date):
  app/models/schedule.rb:87:in `format_stations_and_date'
  app/controllers/schedules_controller.rb:15:in `show'

However, if I change date = date.to_date to self.date = self.date.to_date, the method works correctly.

What's going on? In general, when do I have to write self?

Edit: It's not related to the question, but please note that there is no "titleize!" method.

Determined answered 9/8, 2009 at 19:31 Comment(1)
possible duplicate of Why do ruby setters need "self." qualification within the class?Putrefy
D
46

Whenever you want to invoke a setter method on self, you have to write self.foo = bar. If you just write foo = bar, the ruby parser recognizes that as a variable assignment and thinks of foo as a local variable from now on. For the parser to realize, that you want to invoke a setter method, and not assign a local variable, you have to write obj.foo = bar, so if the object is self, self.foo = bar

Daliladalis answered 9/8, 2009 at 19:35 Comment(0)
D
7

You disambiguiate between the instance method name and a local variable using self (it is allowed to have both with the same name in the same scope). In other words, there will be a method name resolution only if there is no local or block variable of the same name in scope. Behold:

class Foo
  attr_accessor :boo
  def do_boo
    boo = 123
    puts "Locvar: #{boo} Method: #{self.boo}"
  end
end
Foo.new.do_boo

Here's why: imagine you have a module which implements a method. This method assigns something to it's internal local variable "foo" which is used for some computation. If you skip the "self" part, the method will make a "foo=" method call on the object whose class includes the module, which was not the intention of the author and can be downright disastrous.

class Foo
  def bar=(new_value_of_bar)
    set_off_nukes(new_value_of_bar / 3)
  end
end

module InnocentModule # written by a different author elsewhere
  def do_useful_stuff
    ...
    bar = Math.sin(something) # we're dead
  end
end
Foo.send(:include, InnocentModule)

Another crucial part where you have to use self is when invoking the Object#class method, because simply saying "class" means a class keyword for Ruby.

Dexamethasone answered 10/8, 2009 at 17:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.