From: Jean-denis Vauguet on
Hi.

I tried to design a simple plugin system using :extend called upon
instance (http://ruby-doc.org/core/classes/Object.html#M000335 usual
documented behavior), so as to inject some methods redefinitions while
keeping the ability to call super in order to fall back on the default
behavior if needed.

It looks like this: http://gist.github.com/339025
It works as expected, as one can see by reading the commented output at
the end of the file: the inheritance chain is altered, with the plugin
"subclassing" the original class of the plugin receiver.

Yet, I tried to introduce some modularity in the system, so I went for:
http://gist.github.com/339028
It fails but I don't understand why. The inheritance chain is not
altered.
I tried to use :include instead of :extend, which gives merely the same
behavior (overwriting instance methods), but this time the plugin module
is added as the parent of the original class within the inheritance
chain, so it is useless for the purpose ;)

I'll be glad if someone could give me a hint on this :)
Thank you!
--
Posted via http://www.ruby-forum.com/.

From: Space Ship Traveller on
I'm not sure if this helps, but I did something like this before:

http://www.oriontransfer.co.nz/blog/2009-10/loading-anonymous-ruby-classes/index

Kind regards,
Samuel

On 21/03/2010, at 3:46 PM, Jean-denis Vauguet wrote:

> Hi.
>
> I tried to design a simple plugin system using :extend called upon
> instance (http://ruby-doc.org/core/classes/Object.html#M000335 usual
> documented behavior), so as to inject some methods redefinitions while
> keeping the ability to call super in order to fall back on the default
> behavior if needed.
>
> It looks like this: http://gist.github.com/339025
> It works as expected, as one can see by reading the commented output at
> the end of the file: the inheritance chain is altered, with the plugin
> "subclassing" the original class of the plugin receiver.
>
> Yet, I tried to introduce some modularity in the system, so I went for:
> http://gist.github.com/339028
> It fails but I don't understand why. The inheritance chain is not
> altered.
> I tried to use :include instead of :extend, which gives merely the same
> behavior (overwriting instance methods), but this time the plugin module
> is added as the parent of the original class within the inheritance
> chain, so it is useless for the purpose ;)
>
> I'll be glad if someone could give me a hint on this :)
> Thank you!
> --
> Posted via http://www.ruby-forum.com/.
>


From: Josh Cheek on
[Note: parts of this message were removed to make it a legal post.]

On Sat, Mar 20, 2010 at 9:46 PM, Jean-denis Vauguet <jd(a)vauguet.fr> wrote:

> Hi.
>
> I tried to design a simple plugin system using :extend called upon
> instance (http://ruby-doc.org/core/classes/Object.html#M000335 usual
> documented behavior), so as to inject some methods redefinitions while
> keeping the ability to call super in order to fall back on the default
> behavior if needed.
>
> It looks like this: http://gist.github.com/339025
> It works as expected, as one can see by reading the commented output at
> the end of the file: the inheritance chain is altered, with the plugin
> "subclassing" the original class of the plugin receiver.
>
> Yet, I tried to introduce some modularity in the system, so I went for:
> http://gist.github.com/339028
> It fails but I don't understand why. The inheritance chain is not
> altered.
> I tried to use :include instead of :extend, which gives merely the same
> behavior (overwriting instance methods), but this time the plugin module
> is added as the parent of the original class within the inheritance
> chain, so it is useless for the purpose ;)
>
> I'll be glad if someone could give me a hint on this :)
> Thank you!
> --
> Posted via http://www.ruby-forum.com/.
>
>
Hi, I got it to do what I think you are looking for by having plugins pass
the object to extend. http://gist.github.com/339101
To test it, I had the redefined object say things in reverse. I also added
another module to show that you can activate different behaviours in
different objects.

Then I played around with it a little bit more, trying to make it more
modular, and behave similar to ActiveRecord's scopes
http://gist.github.com/339114

I wanted to try to make the plugin agnostic to the method it was activating
so that you could, for example, make a plugin that could then be applied to
any method of any object. But I just can never seem to get define_method to
work right -.- every time I try to do that, I seem to struggle a lot with
it, and usually end up using eval with a string, because dynamically adding
a method is so difficult.

Oh well, I'm kind of happy with it.

Guess I should pick up PragProg's Metaprogramming Ruby book... or try out
Lisp >:D

From: Jean-denis Vauguet on
Thank you Josh. Actually I've already tested what you wrote and that's
just fine as long as what you're redefining belongs to the Base::Server
instance (that's the purpose of :extend).

I posted a "mockup" of what I'd like in a single file:
http://gist.github.com/339129

Basically, it's the "same" thing, but the purpose here is to redefine
instance methods of *other classes* than the Base::Server which has the
"include Plugins"; plus, to do so not for a particular instance of these
classes, but for any of their instances.

Problem is: in this situation, one cannot access a particular instance
of the class to be altered within a given plugin. In my example, this
means the Backward plugin should alter any instance of the Base::Speaker
class once loaded. So you've got to work at the class level somehow
(Base::Speaker). Using :extend at this class level seems useless to me
here (it makes plugins redefinitions available as class methods for
Base::Speaker, not instance's); and using :include does not override the
class' instance methods, for the plugin module is added *before* the
class in it's inheritance chain (say: [Base::Speaker,
Base::Plugins::Backward::SpeakerRedef, Object, Kernel] once the Backward
plugin is loaded).

A workaround should be to undef (or alias) the original instance method
when the plugin's module is :included, so that a call to the "original"
method force the object to go finding the method in the plugin's module,
but it feels clumsy to me. Maybe that's the only way to achieve this?
After all, that was the point of alias_method_chain, wasn't it?

Enlight me :)
--
Posted via http://www.ruby-forum.com/.

From: Jean-denis Vauguet on
Another idea I had is the following:
- any loaded lugin registers as a "callback" for the classes it wants to
alter (instances of the classes, actually!);
- through the Plugins module, which is mix-ined within the Base module
using :extend, have any class nested within the base module to be able
(forced?), everytime they're initialized, to have their instances
:extend the plugins which have registered as callback for the class.

Not really sure about it, but...
Theoretically it would allow for per-instance :extend, thus overriding
behavior of any instance without the need for aliasing. Same behavior as
my standalone and Josh codes, but same flexibility as my previous gist
attempted to achieve.

I'll give it a try when I get the time to, unless I'm told this is BS ;)
--
Posted via http://www.ruby-forum.com/.