Race condition in class inheritance

I need to identify classes from a string called kpath (class path)
reflecting the class inheritance. The string is made of the first
letter of each class. This is what I have now:
class Node
  @@classes = {'N' => self}
  def self.inherited(child)
    super
    @@classes[child.kpath] = child
  end

  def self.kpath
    self == Node ? ksel : (superclass.kpath + ksel)
  end

  def self.ksel
    self.to_s[0..0]
  end

  def self.class_from_kpath(kpath)
    @@classes[kpath]
  end
end

class Page < Node
end

class Document < Page
end

class Draft < Page
  def self.ksel
    'A'
  end
end

puts Node.class_from_kpath('N') # got 'Node', ok.
puts Node.class_from_kpath('NPD') # wanted 'Document', got 'Draft'
puts Node.class_from_kpath('NPA') # wanted 'Draft', got nil

I understand that Draft's ksel method is not known at the time
Node.inherited is called.

How can I have Node.inherited being called once the complete child
class is built ?

Thanks for your answers.
Gaspard

Gaspard Bucher wrote:

I need to identify classes from a string called kpath (class path)
reflecting the class inheritance. The string is made of the first
letter of each class. This is what I have now:

Defer building the hash until class_from_path is called.

One quick and dirty set of changes that does it:

class Node
  @@classes = {'N' => self}

Replace:

  def self.inherited(child)
    super
    @@classes[child.kpath] = child
  end

With:

   @@unhandled_children =
   def self.inherited(child)
     super
     @@unhandled_children << child
   end

  def self.kpath
    self == Node ? ksel : (superclass.kpath + ksel)
  end

  def self.ksel
    self.to_s[0..0]
  end

  def self.class_from_kpath(kpath)

Add:

     while child = @@unhandled_children.pop
       @@classes[child.kpath] = child
     end

···

    @@classes[kpath]
  end
end

class Page < Node
end

class Document < Page
end

class Draft < Page
  def self.ksel
    'A'
  end
end

puts Node.class_from_kpath('N') # got 'Node', ok.
puts Node.class_from_kpath('NPD') # wanted 'Document', got 'Draft'
puts Node.class_from_kpath('NPA') # wanted 'Draft', got nil

I understand that Draft's ksel method is not known at the time
Node.inherited is called.

How can I have Node.inherited being called once the complete child
class is built ?

Thanks for your answers.
Gaspard

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Thanks for the idea, that's a good workaround.

Gaspard

···

2007/9/14, Joel VanderWerf <vjoel@path.berkeley.edu>:

Gaspard Bucher wrote:
> I need to identify classes from a string called kpath (class path)
> reflecting the class inheritance. The string is made of the first
> letter of each class. This is what I have now:

Defer building the hash until class_from_path is called.

One quick and dirty set of changes that does it:

> class Node
> @@classes = {'N' => self}

Replace:

> def self.inherited(child)
> super
> @@classes[child.kpath] = child
> end

With:

   @@unhandled_children =
   def self.inherited(child)
     super
     @@unhandled_children << child
   end

>
> def self.kpath
> self == Node ? ksel : (superclass.kpath + ksel)
> end
>
> def self.ksel
> self.to_s[0..0]
> end
>
> def self.class_from_kpath(kpath)

Add:

     while child = @@unhandled_children.pop
       @@classes[child.kpath] = child
     end

> @@classes[kpath]
> end
> end
>
> class Page < Node
> end
>
> class Document < Page
> end
>
> class Draft < Page
> def self.ksel
> 'A'
> end
> end
>
> puts Node.class_from_kpath('N') # got 'Node', ok.
> puts Node.class_from_kpath('NPD') # wanted 'Document', got 'Draft'
> puts Node.class_from_kpath('NPA') # wanted 'Draft', got nil
>
> I understand that Draft's ksel method is not known at the time
> Node.inherited is called.
>
> How can I have Node.inherited being called once the complete child
> class is built ?
>
> Thanks for your answers.
> Gaspard

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407