Plugin autoregistration (inherited method call)

Hi,
i’m trying to write some kind of plugin system for my project … and
i really would like to make plugin developer’s life easier … so i decided
to carry most of the work in my classes… the problem is i’m unable to
get it to work properly :frowning:

My original idea was to register ‘transports’ this way:

— begin -----------------------------------------------------------------

class Transport
@@transports = {}
def Transport.add(addr, tgt)
@@transports[addr] = tgt
end
def Transport.registered_transports
ar =
@@transports.each_key { |x| ar << x }
ar
end
end

class TCPTransport < Transport
Transport.add(‘tcp’, self)
end

puts "Registered transports: " + Transport.registered_transports.join(', ')

— end -----------------------------------------------------------------

which works perfectly … but … you MUST call
“Transport.add(‘tcp’, self)”, which is (imho) pretty ugly.
I tried to rewrite it this way:

— 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 … :frowning:
Neither does “@@transports[sub.proto_id] = sub” – but I can’t figure out why.

Did I overlooked something (or is there any (better) way)?

Thanks in advance,
W.

PS: I’m still using Ruby 1.6 … but if there’s 1.7 way only …
i’ll switch :slight_smile:

···


Wejn <lists+rubytalk(at)box.cz>
(svamberk.net’s Linux section, fi.muni.cz student, linuxfan)

    Bored?  Want hours of entertainment?         <<<
      Just set the initdefault to 6!             <<<

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

···

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 … :frowning:

class TCPTransport < Transport
def_proto_id ‘tcp’
end

Thanks a lot! That’s exactly what i dreamt about :slight_smile:

W.

···


Wejn <lists+rubytalk(at)box.cz>
(svamberk.net’s Linux section, fi.muni.cz student, linuxfan)

    Bored?  Want hours of entertainment?         <<<
      Just set the initdefault to 6!             <<<

Hi why,

thanks for the code. Very nice idea.

There’s one error though: you don’t store the Transport’s subclass
in the hash, but the class of the subclass, which is Class. So
here’s the modified version:

class Transport
@@transports = {}
class << self
def def_proto_id( pid )
@@transports[ pid ] = self
end
def registered_transports
@@transports.keys
end
def create( pid )
@@transports[ pid ].new
end
end
end

class TCPTransport < Transport
def_proto_id 'tcp’
end

class UDPTransport < Transport
def_proto_id 'udp’
end

p( Transport.registered_transports.join(’, ') ) # => "udp, tcp"
p( Transport.create( ‘tcp’ ) ) # => #TCPTransport:0x2b89128
p( Transport.create( ‘udp’ ) ) # => #UDPTransport:0x2b89008

Regards,
Pit