From: Jean-denis Vauguet on 20 Mar 2010 22:46 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 20 Mar 2010 23:38 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 21 Mar 2010 01:23 [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 21 Mar 2010 02:39 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 21 Mar 2010 03:13
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/. |