[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Inherited & load

F. Senault

7/23/2007 9:55:00 PM

Hello.

I've written a mechanism to dynamically load plugins inheriting from a
base class, using the self.inherited method.

Now, in an irb session, I noticed that, even if I (re)load the classes,
the self.inherited is only triggered once. Short example :

> ruby -v
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-freebsd6]

> cat first.rb
#! /usr/local/bin/ruby
load "test.rb"
load "test.rb"

> cat test.rb
#! /usr/local/bin/ruby
class Test
def self.inherited(c)
puts "#{c} inherited Test !"
end
end
load "test2.rb"

> cat test2.rb
#! /usr/local/bin/ruby
puts "In #{__FILE__}."
class Test2 < Test ; end

> ruby first.rb
In ./test2.rb.
Test2 inherited Test !
In ./test2.rb.

Is there a trick somewhere that would allow me to trigger the inherited
method at each time the file containing the "sub-class" (e.g. Test2) is
loaded ?

(The real plugin class, if there is a need, is here :
http://www.lacave.net/~fred/projets... )

Fred
--
Arithmetic comparisons: greater-than and less-than considered to be
judgemental and divisive. In the interests of inclusiveness, only
equality-tests will be permitted.
(Tanuki in the SDM, on politically-correct coding)
14 Answers

Todd Benson

7/23/2007 11:25:00 PM

0

On 7/23/07, F. Senault <fred@lacave.net> wrote:
> Hello.
>
> I've written a mechanism to dynamically load plugins inheriting from a
> base class, using the self.inherited method.
>
> Now, in an irb session, I noticed that, even if I (re)load the classes,
> the self.inherited is only triggered once. Short example :
>
> > ruby -v
> ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-freebsd6]
>
> > cat first.rb
> #! /usr/local/bin/ruby
> load "test.rb"
> load "test.rb"
>
> > cat test.rb
> #! /usr/local/bin/ruby
> class Test
> def self.inherited(c)
> puts "#{c} inherited Test !"
> end
> end
> load "test2.rb"
>
> > cat test2.rb
> #! /usr/local/bin/ruby
> puts "In #{__FILE__}."
> class Test2 < Test ; end
>
> > ruby first.rb
> In ./test2.rb.
> Test2 inherited Test !
> In ./test2.rb.
>
> Is there a trick somewhere that would allow me to trigger the inherited
> method at each time the file containing the "sub-class" (e.g. Test2) is
> loaded ?
>
> (The real plugin class, if there is a need, is here :
> http://www.lacave.net/~fred/projets... )
>
> Fred

This is probably not much help, but you got me to learn a couple of
things while playing around.

First of all, I don't think you can inherit more than once without
removing the constant. Once you've declared class B < A then that's
it until removed. For example,

irb> class A; def self.inherited(s); puts "#{s}!"; end; end
=> nil
irb> class B < A; end
B!
=> nil
irb> class B < A; end
=> nil
irb> class C;end
=> nil
irb> class B < C; end
=>TypeError: superclass mismatch for class B
....blah blah

But, if you use a module,

$ cat m.rb
module M
class A
def self.inherited o
puts "A inherited by #{o}!"
end
end
def self.delete const
remove_const const.intern
end
end

$ cat test.rb
require "m"

puts "\nConstants: #{M.constants.inspect}"
print "First inherit: "
class M::B < M::A; end

puts "\nConstants: #{M.constants.inspect}"
print "Second try at inherit: "
class M::B < M::A; end

print "\n\nDelete B"
M::delete "B"
puts "\nConstants: #{M.constants.inspect}"
print "Try inherit now:"
class M::B < M::A; end
puts
$

Todd

Robert Dober

7/24/2007 8:03:00 AM

0

On 7/24/07, F. Senault <fred@lacave.net> wrote:
> Hello.
>
> I've written a mechanism to dynamically load plugins inheriting from a
> base class, using the self.inherited method.
>
> Now, in an irb session, I noticed that, even if I (re)load the classes,
> the self.inherited is only triggered once. Short example :
>
> > ruby -v
> ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-freebsd6]
>
> > cat first.rb
> #! /usr/local/bin/ruby
> load "test.rb"
> load "test.rb"
>
> > cat test.rb
> #! /usr/local/bin/ruby
> class Test
> def self.inherited(c)
> puts "#{c} inherited Test !"
> end
> end
> load "test2.rb"
>
> > cat test2.rb
> #! /usr/local/bin/ruby
> puts "In #{__FILE__}."
> class Test2 < Test ; end
>
> > ruby first.rb
> In ./test2.rb.
> Test2 inherited Test !
> In ./test2.rb.
>
> Is there a trick somewhere that would allow me to trigger the inherited
> method at each time the file containing the "sub-class" (e.g. Test2) is
> loaded ?
As Todd pointed out already, "inheritance" will happen only once,
might it be that you have chosen a suboptimal mechanism ;) for what
you want to achieve?
Maybe you could tell us a little bit more about that?

Cheers
Robert


--
I always knew that one day Smalltalk would replace Java.
I just didn't know it would be called Ruby
-- Kent Beck

Robert Dober

7/24/2007 8:38:00 AM

0

On 7/24/07, Todd Benson <caduceass@gmail.com> wrote:
> On 7/23/07, F. Senault <fred@lacave.net> wrote:
> > Hello.
> >
> > I've written a mechanism to dynamically load plugins inheriting from a
> > base class, using the self.inherited method.
> >
> > Now, in an irb session, I noticed that, even if I (re)load the classes,
> > the self.inherited is only triggered once. Short example :
> >
> > > ruby -v
> > ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-freebsd6]
> >
> > > cat first.rb
> > #! /usr/local/bin/ruby
> > load "test.rb"
> > load "test.rb"
> >
> > > cat test.rb
> > #! /usr/local/bin/ruby
> > class Test
> > def self.inherited(c)
> > puts "#{c} inherited Test !"
> > end
> > end
> > load "test2.rb"
> >
> > > cat test2.rb
> > #! /usr/local/bin/ruby
> > puts "In #{__FILE__}."
> > class Test2 < Test ; end
> >
> > > ruby first.rb
> > In ./test2.rb.
> > Test2 inherited Test !
> > In ./test2.rb.
> >
> > Is there a trick somewhere that would allow me to trigger the inherited
> > method at each time the file containing the "sub-class" (e.g. Test2) is
> > loaded ?
> >
> > (The real plugin class, if there is a need, is here :
> > http://www.lacave.net/~fred/projets... )
> >
> > Fred
>
> This is probably not much help, but you got me to learn a couple of
> things while playing around.
>
> First of all, I don't think you can inherit more than once without
> removing the constant. Once you've declared class B < A then that's
> it until removed. For example,
>
> irb> class A; def self.inherited(s); puts "#{s}!"; end; end
> => nil
> irb> class B < A; end
> B!
> => nil
> irb> class B < A; end
> => nil
> irb> class C;end
> => nil
> irb> class B < C; end
> =>TypeError: superclass mismatch for class B
> ....blah blah
>
> But, if you use a module,
>
> $ cat m.rb
> module M
> class A
> def self.inherited o
> puts "A inherited by #{o}!"
> end
> end
> def self.delete const
> remove_const const.intern
> end
> end
>
> $ cat test.rb
> require "m"
>
> puts "\nConstants: #{M.constants.inspect}"
> print "First inherit: "
> class M::B < M::A; end
>
> puts "\nConstants: #{M.constants.inspect}"
> print "Second try at inherit: "
> class M::B < M::A; end
>
> print "\n\nDelete B"
> M::delete "B"
> puts "\nConstants: #{M.constants.inspect}"
> print "Try inherit now:"
> class M::B < M::A; end
> puts
> $
>
> Todd
>
>
Hmm not sure how this fits in but what about include instead of
inherit, does this not what you want to achieve?
The more I am thinking about it I feel that this might indeed be what
is needed as Module#included is executed all the time the include
statement is called, hopefully Fred can use inclusion instead of
inheritance.

Cheers
Robert


--
I always knew that one day Smalltalk would replace Java.
I just didn't know it would be called Ruby
-- Kent Beck

F. Senault

7/24/2007 9:43:00 AM

0

Le 24 juillet 2007 à 10:02, Robert Dober a écrit :

> On 7/24/07, F. Senault <fred@lacave.net> wrote:
>> Is there a trick somewhere that would allow me to trigger the inherited
>> method at each time the file containing the "sub-class" (e.g. Test2) is
>> loaded ?
> As Todd pointed out already, "inheritance" will happen only once,
> might it be that you have chosen a suboptimal mechanism ;) for what
> you want to achieve?
> Maybe you could tell us a little bit more about that?

The full code is there :

http://www.lacave.net/~fred/projets...

It's working behind the scenes in this project :

http://talisker.lacave.net/ne...

Basically, I have a Plugin class, which implements a few methods,
including at the class level.

Then, I reuse this class in a few projects, by inheriting it in a base
class. For instance, I can write :

class Tester < Plugin
plugin_must_implement :register
plugin_must_implement :getdata
plugin_load_glob "./testers/[a-zA-Z0-9].rb"
# methods
end

Then, when I use Tester.load!, all the files in the specified path are
load'ed, and all the classes inheriting from Tester are registered as
plugins of that class, validated against the obligatory methods, and,
unless told otherwise, an instance of each is created and made available
and enumerable through an array. Then, I can use, for instance :

Tester.each do |t|
t.register
d = t.getdata
# process the tester data
end

In the concrete case of my newsserver, I collect data to make the graphs
from different sources. So, when I have new data, instead of modifying
the main body of the grapher program, I can add a new plugin in a small
rb file (averaging 25 lines) and it's "magically" taken into account...

I've already managed to reuse this class in at least four different
projects, and I kind of like the concept... :)

Now, in the event of a long running process, I would like to "hotplug"
the plugins, and, so, be able to simply reload everything. My problem
is that I don't see how I can find the classes contained in a file I
just loaded without playing with inherited / Object.constants.

My current working idea is this one :

#! /usr/local/bin/ruby

#module Toto
class Test
class <<self
attr_accessor :loaded_classes
def do_load
@loaded_classes ||= {}
[ "test2.rb", "test3.rb" ].each do |f|
o = Object.constants
load f
( Object.constants - o ).each do |c|
t = Object.const_get(c)
if t.is_a? Class then
if t.superclass == Test then
@loaded_classes[f] ||= []
@loaded_classes[f] << t
end
end
end
end
end
end
end

Test.do_load
p Test.loaded_classes
Test.do_load
p Test.loaded_classes

Needless to say, I still believe inherited was a bit simpler... :)

(And it doesn't cope with classes being suppressed.)

Fred
--
Au bout De la course Remonte jusqu'a A la source One trip One noise
Circuit Nuit bleue Spécialiste De l'enjeu One trip One noise
Longue attente avant de s'elancer One trip (Noir Désir,
Longue vie et tout a recracher One noise One Trip / One Noise)

F. Senault

7/24/2007 10:04:00 AM

0

Le 24 juillet 2007 à 01:25, Todd Benson a écrit :

> This is probably not much help, but you got me to learn a couple of
> things while playing around.

Good ! :)

> First of all, I don't think you can inherit more than once without
> removing the constant. Once you've declared class B < A then that's
> it until removed. For example,

Thats funny. I can find the constant in Object.constants, but there is
no delete_const in there. :|

Fred
--
Go not to Usenet for counsel, for they will say both 'No' and 'Yes' and
'Try another newsgroup'.

Robert Dober

7/24/2007 10:21:00 AM

0

On 7/24/07, F. Senault <fred@lacave.net> wrote:
> Le 24 juillet 2007 à 10:02, Robert Dober a écrit :
>
> > On 7/24/07, F. Senault <fred@lacave.net> wrote:
> >> Is there a trick somewhere that would allow me to trigger the inherited
> >> method at each time the file containing the "sub-class" (e.g. Test2) is
> >> loaded ?
> > As Todd pointed out already, "inheritance" will happen only once,
> > might it be that you have chosen a suboptimal mechanism ;) for what
> > you want to achieve?
> > Maybe you could tell us a little bit more about that?
>
> The full code is there :
>
> http://www.lacave.net/~fred/projets...
>
> It's working behind the scenes in this project :
>
> http://talisker.lacave.net/ne...
>
> Basically, I have a Plugin class, which implements a few methods,
> including at the class level.
>
> Then, I reuse this class in a few projects, by inheriting it in a base
> class. For instance, I can write :
>
> class Tester < Plugin
> plugin_must_implement :register
> plugin_must_implement :getdata
> plugin_load_glob "./testers/[a-zA-Z0-9].rb"
> # methods
> end
>
> Then, when I use Tester.load!, all the files in the specified path are
> load'ed, and all the classes inheriting from Tester are registered as
> plugins of that class, validated against the obligatory methods, and,
> unless told otherwise, an instance of each is created and made available
> and enumerable through an array.
I do not see any particular reason why not to use Module inclusion;
Instead of
class Tester < Plugin
class Tester
include Plugin ## or maybe extend, I have not looked into the details
end
Now you can be sure that
Plugin#included / Plugin#extended
is executed evry time you load a file.
Did I miss something?
Oh yes of course might be a pain to redesign :(.

Cheers
Robert
Then, I can use, for instance :
>
> Tester.each do |t|
> t.register
> d = t.getdata
> # process the tester data
> end
>
> In the concrete case of my newsserver, I collect data to make the graphs
> from different sources. So, when I have new data, instead of modifying
> the main body of the grapher program, I can add a new plugin in a small
> rb file (averaging 25 lines) and it's "magically" taken into account...
>
> I've already managed to reuse this class in at least four different
> projects, and I kind of like the concept... :)
>
> Now, in the event of a long running process, I would like to "hotplug"
> the plugins, and, so, be able to simply reload everything. My problem
> is that I don't see how I can find the classes contained in a file I
> just loaded without playing with inherited / Object.constants.
>
> My current working idea is this one :
>
> #! /usr/local/bin/ruby
>
> #module Toto
> class Test
> class <<self
> attr_accessor :loaded_classes
> def do_load
> @loaded_classes ||= {}
> [ "test2.rb", "test3.rb" ].each do |f|
> o = Object.constants
> load f
> ( Object.constants - o ).each do |c|
> t = Object.const_get(c)
> if t.is_a? Class then
> if t.superclass == Test then
> @loaded_classes[f] ||= []
> @loaded_classes[f] << t
> end
> end
> end
> end
> end
> end
> end
>
> Test.do_load
> p Test.loaded_classes
> Test.do_load
> p Test.loaded_classes
>
> Needless to say, I still believe inherited was a bit simpler... :)
>
> (And it doesn't cope with classes being suppressed.)
>
> Fred
> --
> Au bout De la course Remonte jusqu'a A la source One trip One noise
> Circuit Nuit bleue Spécialiste De l'enjeu One trip One noise
> Longue attente avant de s'elancer One trip (Noir Désir,
> Longue vie et tout a recracher One noise One Trip / One Noise)
>
>


--
I always knew that one day Smalltalk would replace Java.
I just didn't know it would be called Ruby
-- Kent Beck

F. Senault

7/24/2007 10:45:00 AM

0

Le 24 juillet 2007 à 12:20, Robert Dober a écrit :

> On 7/24/07, F. Senault <fred@lacave.net> wrote:

>> Then, when I use Tester.load!, all the files in the specified path are
>> load'ed, and all the classes inheriting from Tester are registered as
>> plugins of that class, validated against the obligatory methods, and,
>> unless told otherwise, an instance of each is created and made available
>> and enumerable through an array.
> I do not see any particular reason why not to use Module inclusion;
> Instead of
> class Tester < Plugin
> class Tester
> include Plugin ## or maybe extend, I have not looked into the details
> end
> Now you can be sure that
> Plugin#included / Plugin#extended
> is executed evry time you load a file.
> Did I miss something?
> Oh yes of course might be a pain to redesign :(.

Yup, basically, I didn't know I'd hit that problem with included, and it
*will* be a pain to redesign.

OTOH, I can't manage to use included neither :

#! /usr/local/bin/ruby

module Plugin
def self.included(m)
puts "#{m} includes me."
end
end

class TestA
include Plugin
end
class TestA1 < TestA ; end

Just gives "TestA includes me"... :|

Fred
--
I feel it move across my skin. I'm reaching up and reaching out, I'm
reaching for the random or what ever will bewilder me. And following
our will and wind we may just go where no one's been. We'll ride the
spiral to the end and may just go where no one's been. (Tool, Lateralus)

Robert Dober

7/24/2007 12:00:00 PM

0

On 7/24/07, F. Senault <fred@lacave.net> wrote:
> Le 24 juillet 2007 à 12:20, Robert Dober a écrit :
>
> > On 7/24/07, F. Senault <fred@lacave.net> wrote:
>
> >> Then, when I use Tester.load!, all the files in the specified path are
> >> load'ed, and all the classes inheriting from Tester are registered as
> >> plugins of that class, validated against the obligatory methods, and,
> >> unless told otherwise, an instance of each is created and made available
> >> and enumerable through an array.
> > I do not see any particular reason why not to use Module inclusion;
> > Instead of
> > class Tester < Plugin
> > class Tester
> > include Plugin ## or maybe extend, I have not looked into the details
> > end
> > Now you can be sure that
> > Plugin#included / Plugin#extended
> > is executed evry time you load a file.
> > Did I miss something?
> > Oh yes of course might be a pain to redesign :(.
>
> Yup, basically, I didn't know I'd hit that problem with included, and it
> *will* be a pain to redesign.
>
> OTOH, I can't manage to use included neither :
>
> #! /usr/local/bin/ruby
>
> module Plugin
> def self.included(m)
> puts "#{m} includes me."
> end
> end
>
> class TestA
> include Plugin
> end
> class TestA1 < TestA ; end
class TestA1
include Plugin

But that will not do the trick, you need meta information on class
level, I just have tried to adapt update.rb for usage with extend

class Test
extend Plugin

class Test1
extend Plugin

I will send the patch (err the new file the patch is longer ) off list.
Maybe you want to consider its deployment maybe not, was fun reading
your code and your ideas, thx 4 sharing.

Cheers
Robert

--
I always knew that one day Smalltalk would replace Java.
I just didn't know it would be called Ruby
-- Kent Beck

James Gray

7/24/2007 1:18:00 PM

0

On Jul 24, 2007, at 5:10 AM, F. Senault wrote:

> I can find the constant in Object.constants, but there is
> no delete_const in there. :|

$ ri -T remove_const
---------------------------------------------------- Module#remove_const
remove_const(sym) => obj
------------------------------------------------------------------------
Removes the definition of the given constant, returning that
constant's value. Predefined classes and singleton objects
(such as
_true_) cannot be removed.

Hope that helps.

James Edward Gray II

F. Senault

7/24/2007 2:38:00 PM

0

Le 24 juillet à 15:18, James Edward Gray II a écrit :

> On Jul 24, 2007, at 5:10 AM, F. Senault wrote:
>
>> I can find the constant in Object.constants, but there is
>> no delete_const in there. :|
>
> $ ri -T remove_const
> Hope that helps.

Alas, Object is not a module :

16:35 fred@balvenie:~/ruby/espions> irb
>> o = Object.constants
=> ["SocketError", ...]
>> class Test ; end
=> nil
>> Object.constants - o
=> ["Test"]
>> Object.remove_const(:Test)
NoMethodError: private method `remove_const' called for Object:Class
from (irb):4

Fred
--
A groan of tedium escapes me, startling the fearful. Is this a test ?
It has to be, otherwise I can't go on. Draining patience, drain
vitality. This paranoid, paralyzed vampire act's a little old. But I'm
still right here, giving blood, keeping faith (Tool, The Patient)