4

The code below outputs "I am Thing."

class Thing
   class << self
      def foo
         puts "I am #{self}."
      end
   end
end

Thing.foo

I thought "self" refers to an object of type Thing, but it refers to the class Thing. I have gathered from a previous question (linked in the comments) that this has something to do with the fact that Thing is an instance of Object. Is the block beginning with class << self actually executed in a context where "self" refers to Thing as an instance? What is going on here?

nickackerman42
  • 415
  • 4
  • 14

2 Answers2

5

Inside methods self refers to the instance, inside class/module definition, but outside of any method — to the class/module itself.

class Thing
  puts self.inspect
  def foo
    puts self.inspect
  end
end
puts "==="
Thing.new.foo

Try the code above and you’ll likely see what’s happening there: the class is being parsed in the first place, and the first puts is being executed. Then puts "===" prints the "===" string out. And, afterwards, the instance is being printed from inside the method.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
5

Let's find out.

class Thing
   puts "self=#{self}"
   class << self
     puts "self in singleton class=#{self}"
     def foo
       puts "I am #{self}."
     end
   end
end
  # self=Thing
  # self in singleton class=#<Class:Thing>

and

Thing.foo
  # I am Thing.

We see class << self is the same as class << Thing.

This is the same as

class Thing
end

class << Thing
   puts "self in singleton class=#{self}"
   def foo
     puts "I am #{self}."
   end
end
  # self in singleton class=#<Class:Thing>

Thing.foo
  # I am Thing.

class << self within the class or class << Thing outside the class does one thing and one thing only: it changes self to Thing's singleton class within the block that follows. That is the same as saying that it changes the scope to Thing's singleton class within the block.

This article by Yehuda Katz is required reading.

dstarh
  • 4,976
  • 5
  • 36
  • 68
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • 1
    It's worth noting that `self` referring to `Thing` inside the context of a `class Thing` expression (and outside the context of a method body) is a straightforward consequence of the design goals of the language: constructs like `attr_reader` and `include` are meant to feel like declarative keywords, but they are implemented as methods of `Class` (well, `Module`). To feel declarative, you want to be able to call them by bare name in the context of the class body. A method called by name with no explicit receiver is called on `self`, so, `self` _has_ to be the class. – philomory Nov 02 '16 at 19:56
  • 1
    "`class << self` within the class or `class << Thing` outside the class does one thing and one thing only: it changes `self` to `Thing`'s singleton class within the block that follows." – It also changes the *default definee* to `Thing`'s singleton class, which is usually *the* primary reason to use it in the first place. And it changes the constant definition context to `Thing`'s singleton class as well. So, it does *three* things, in fact, the same three things a "normal" class definition also changes. – Jörg W Mittag Nov 02 '16 at 22:24
  • @JörgWMittag, I'm not familiar with the expression "default definee". Is it defined somewhere for Ruby? Also, do things #2 and #3 follow from `self` being set to the singleton class? – Cary Swoveland Nov 02 '16 at 22:32
  • The *default definee* is where methods end up that are defined without an explicit receiver, i.e. normal instance methods defined like `def bar` instead of `def foo.bar`. The only article I know of where the concept is explained is [*Three implicit contexts in Ruby*](http://yugui.jp/articles/846) by yugui (Ruby core team member, committer, and Ruby 1.9 release manager). In it, she explicitly mentions that Yehuda Katz's article you linked to is wrong, BTW. These three contexts are independent. E.g. `instance_eval` changes `self` but doesn't change the default definee or the constant context. – Jörg W Mittag Nov 02 '16 at 22:37
  • Likewise, `def foo` changes `self` to the receiver object, but the default definee is still the innermost lexically enclosing module. – Jörg W Mittag Nov 02 '16 at 22:39
  • Thanks for the clarification, @Jörg. – Cary Swoveland Nov 02 '16 at 22:47