Softwareaddict
Softwareaddict

Reputation: 87

How self works when using it in the initialize and other class methods?

I am Learning ruby and the concept of self is very confusing to me in this example.

class Alpha
    def initialize
        self.A
    end

    def self.A
        puts "A"
    end 

    def self.B 
        self.A
        puts "B"
    end
end

that when I tried to create a new instance from the class Alpha

Alpha.new

I got NoMethodError but when I called B method, it executed normally.

Alpha.B
# A
# B
#=>nil

Upvotes: 1

Views: 53

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110685

First define the class Alpha as follows.

class Alpha
  puts "self in Alpha = #{self}"

  def a
    puts "self in a = #{self}"
  end

  def Alpha.b
    puts "self in b = #{self}"
  end 

  def Alpha.c 
    puts "self in c = #{self}"
  end
end
  # self in Alpha = Alpha

We have one instance method, Alpha#a and two class methods, Alpha::b and Alpha::b. (Referencing instance and class methods that way is a Ruby convention, one you will see all the time in the docs.)

Alpha.methods(false)
  #=> [:c, :b] 
Alpha.instance_methods(false)
  #=> [:a] 

See Object::methods and Module#instance_methods, noting the implications of including the argument false.

Instance methods are invoked on instances of the class; class methods are invoked on the class.

alpha = Alpha.new
  #=> #<Alpha:0x000059c5ff614f08> 
alpha.a
  # self in a = #<Alpha:0x000059c5ff614f08>

Alpha.b
  # self in b = Alpha
Alpha.c
  # self in c = Alpha

What if we try to invoke an instance method on the class or a class method on an instance?

Alpha.a
  #=> NoMethodError (undefined method `a' for Alpha:Class)
alpha.b
  #=> NoMethodError (undefined method `b' for #<Alpha:0x000059c5ff614f08>)

We would normally write the class as follows.

class Alpha
  def a
    puts "self in a = #{self}"
  end

  def self.b
    puts "self in b = #{self}"
  end 

  def self.c 
    puts "self in c = #{self}"
  end
end

Within the class definition self is Alpha, which is why the two ways of writing the class are equivalent. The only reason for using self is that if we decide to later change the name of the class to Beta we won't have to change all the Alpha's to Beta in the class method definitions.

If we want to invoke the class method Alpha::b from the instance method Alpha#a we must change the latter as follows:

class Alpha
  def a
    puts "self in a = #{self}"
    self.class.b
  end
end

alpha.a
  # self in a = #<Alpha:0x000059c5ff614f08>
  # self in b = Alpha

Here self.class returns Alpha, and we know what Alpha.b returns.

Let's change Alpha#a once again and add a second instance method:

class Alpha
  def a
    puts "self in a = #{self}"
    d
  end

  def d
    puts "self in d = #{self}"
  end
end

alpha.a
  # self in a = #<Alpha:0x000059c5ff614f08>
  # self in d = #<Alpha:0x000059c5ff614f08>

I could have written:

  def a
    puts "self in a = #{self}"
    self.d
  end

but self. is not necessary and generally is not included. The reason is that when a method has no explicit receiver, as d in:

def a
  puts "self in a = #{self}"
  d
end

self becomes the default receiver. We know from the discussion above that within the instance method definition self equals the instance alpha.

Upvotes: 1

katafrakt
katafrakt

Reputation: 2488

initialize operates in the instance context, not class context. If you want to call class method, you need to self.class.A. Or you can try to go e bit more crazy and overwrite class new method:

class Alpha
  def self.new
    self.A
    super # this actually calls initialize after object is created
  end

  [...]
end

Upvotes: 1

Related Questions