Nomas Prime
Nomas Prime

Reputation: 1344

Calling Singleton Class Methods Inside Open Singleton Class

I'm trying to understand why I can't call methods defined on the singleton class from within the open class but I can from the actual class.

Can someone explain why the first example fails and the second one doesn't?

class One
  class << self
    def one; end
    one
  end
end

NameError (undefined local variable or method 'one' for #<Class:One>)

class Two
  class << self
    def one; end
  end

  self.one
end

=> nil

Upvotes: 4

Views: 54

Answers (2)

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369438

Your example is more confusing than it needs to be. It doesn't require a singleton class at all:

class Foo
  def bar; end

  bar # NameError
end

Foo.new.bar

Here, we have a class Foo with an instance method bar. A singleton class is still just a class, so this is actually the exact same example as yours.

def without an explicit definee defines an instance method of the closest lexically enclosing class definition, in this case Foo. A message send without an explicit receiver like bar sends the message to self. Inside the class definition body, self is the class itself.

So, def bar defines an instance method in Foo, i.e. a method you can call on instances of Foo.

bar inside the class definition body sends a message to self, which is Foo. Since Foo is not an instance of itself, it does not have a method named bar, ergo, the method call fails.

This works exactly the same with a singleton class, since it is still just a class.

Upvotes: 2

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

You made the wrong assumption about where the method belongs to, in the first place. The call to the instance method one from inside class context should not succeed. In your first snippet, you try to call method one from the singleton class of the singleton class of One (because it’s called from singleton_class context).

Example with normal class / instance methods to clarify:

class One
  def self.one()
    puts :class
  end
  def one
    puts :instance
  end
  one()
end
#⇒ class

So, the expected behavior would be to raise NameError. Now the answer is simple: it raises NameError because this method does not exist.

Upvotes: 0

Related Questions