1

In self.inherited method of base class, subclass is passed, calling subclass's name method instead calls base class method. Though same thing works If same method is called on same class elsewhere

class A
 def self.name 
  "a"
 end

 def self.inherited(subclass)
  puts B.hash
  puts B.name
 end
end

class B < A
 def self.name 
  "b"
 end
end

puts B.hash
puts B.name

output:

1428955046062147697
a
1428955046062147697
b
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • Does `inherited` kick in before or after the subclass has been created? – tadman Jun 05 '19 at 19:36
  • Fun fact: the built-in [`name`](http://ruby-doc.org/core-2.6.3/Module.html#method-i-name) method would return `"B"` in either case (because constant assignment happens before the callback when using the `class` keyword). – Stefan Jun 06 '19 at 08:38
  • Add the line `puts "cat"` right after `class B < A` and see when it's displayed. – Cary Swoveland Jun 06 '19 at 09:04

1 Answers1

3

No magic here.

When you declare B the things happen in the following order (roughly speaking):

  1. B (an instance of Class) is created (which inherits everything from A). At this moment it doesn't have anything specific.

  2. A.inherited hook is invoked.

  3. B class is opened and processed. Only at this point, it gets its own properties and methods (except the ones that could be created inside the hook).

So, when (2) happens the only name that is available for B is the one defined in A.

This is very easy to check using the following code:

class A
  def self.name 
    "a"
  end

  def self.inherited(subclass)
    puts "B own methods, point 1: #{subclass.methods(false).join(', ')}"
  end
end

class B < A
  puts "B own methods, point 2: #{self.methods(false).join(', ')}"

  def self.name 
    "b"
  end

  puts "B own methods, point 3: #{self.methods(false).join(', ')}"
end

# B own methods, point 1: 
# B own methods, point 2: 
# B own methods, point 3: name

Everything is clear now, right?

Konstantin Strukov
  • 2,899
  • 1
  • 10
  • 14
  • there seems to be a need for post_inherited method, otherwise if I have to process class by some user defined values it becomes cumbersome. – Anurag Uniyal Jun 05 '19 at 20:51
  • What do you want to do with imaginary `post_inherited` that cannot be done via class (re)opening and common Ruby idioms? – Konstantin Strukov Jun 05 '19 at 21:09
  • @AnuragUniyal, that would require a determination of when the construction of the inherited class is completed, which may be problematic. Moreover, considering that while the child is being constructed it could modify the parent or create a sibling. I'm not sure what the implications of such actions might be for a `post_inherited` method. I expect the main reason for `inherited` is build a list of instances. – Cary Swoveland Jun 05 '19 at 23:04
  • @KonstantinStrukov it can be done with inherited but not elegant, e.g. how do you make a hash of `{name => subclass}` – Anurag Uniyal Jun 06 '19 at 04:35
  • 1
    @AnuragUniyal you can build just a list of descendants using `inherited` and then build the hash on demand... Or you can use the same approach as Rais does for the `.descendants` method which doesn't use hooks at all (ObjectSpace is used instead) – Konstantin Strukov Jun 06 '19 at 06:50