Wejn (lists+rubytalk / box.cz) wrote:
> # --- begin -----------------------------------------------------------------
> class Transport
>   @@transports = {}
>   def Transport.registered_transports
>     ar = []
>     @@transports.each_key { |x| ar << x }
>     ar
>   end
>   def Transport.inherited(sub)
>     @@transports[sub.new.proto_id] = sub
>   end
> end
> 
> class TCPTransport < Transport
>   def proto_id
>     'tcp'
>   end
> end
> 
> puts "Registered transports: " + Transport.registered_transports.join(', ')
> # ---  end  -----------------------------------------------------------------
> 
> but it just doesn't work ... :(

I believe Transport.inherited is called before the TCPTransport class definition
is complete.  At that point the 'proto_id' method doesn't exist yet.

See:

  class Transport
    def Transport.inherited( sub )
      p sub.new
    end
  end
  
  class TCPTransport < Transport
    def initialize
      @test = 1
    end
  end
  
  p TCPTransport.new

Prints:

  #<TCPTransport:0x806eaec>
  #<TCPTransport:0x806ea10 @test=1>
 
The 'sub.new' call inside Transport.inherited issues a TCPTransport object before
TCPTransport#initialize has actually been defined.  So you'll need to simply store
the class constant and call 'proto_id' later.

You have plenty of options for getting around this.  I've been working on some apps
which use similiar ideas for plugins.  Part of the decision lies in how you actually
will be loading the plugins.  If you have a list files which contain plugins, then you
could wrap them inside a TransportPlugin module when they are loaded.  Then,
loop through the TransportPlugin constants, searching for those which are valid 
plugin classes.

If you want to go the inheritance route, rather than using Class.inherited, you
should try giving your inheritance class a couple of toplevel singleton methods 
for use during class definitions.  Your examples modified:

  class Transport 
      @@transports = {}
      def Transport.add( addr, tgt )
          @@transports[ addr ] = tgt
      end
      def Transport.registered_transports
          @@transports.keys
      end
      class << self
          def def_proto_id( pid )
              Transport.add( pid, self.class )
          end
      end
  end
  
  class TCPTransport < Transport
    def_proto_id 'tcp'
  end
  
  puts "Registered transports: " + Transport.registered_transports.join(', ')

Good luck.

_why