From: "José Ignacio" Date: 2008-09-02T19:54:33+09:00 Subject: Re: Undo Object.extend I've modified my version by getting inspired by your code :) In this case there is no stack. You can remove any arbitrary behaviour/module, which can be an advantage or a risk. module Behaviourable def extend mod @ancestors ||= {} return if @ancestors[mod] mod_clone = mod.clone @ancestors[mod] = mod_clone super mod_clone end def remove mod mod_clone = @ancestors[mod] mod_clone.instance_methods.each {|m| mod_clone.module_eval { remove_method m } } @ancestors[mod] = nil end end class A include Behaviourable end module NormalBehaviour def talk; puts 'Hi'; end end module EnhancedBehaviour def talk; super; puts 'Goodbye'; end end a = A.new a.extend NormalBehaviour a.talk # Hi a.extend EnhancedBehaviour a.talk # Hi # Goodbye a.remove EnhancedBehaviour a.talk # Hi a.extend EnhancedBehaviour a.extend EnhancedBehaviour # Nothing happens a.talk # Hi # Goodbye Robert Dober wrote: > On Mon, Sep 1, 2008 at 10:34 PM, Jos� Ignacio > wrote: >> Thanks, that's a really nice piece of code that helps a lot :) >> >> Why use a @__bsp__ variable instead of using @__behavior_stack__.pop or >> @__behavior_stack__.last ? > Good question ;). Actually I am leaving empty modules on the stack > that can be reused, thus > after a push_behavior the stack might look like this [ x, module with > the methods from pushed behavior ] and the sp is 1 > after pop_behavior the situation is as follows stack : [x, empty > module] and the sp=0. > This avoids unnecessary recreation of modules ( and lets the GC rest, > but that was not the idea ;) Nice :) That's not the case in my code, where objects are created and deleted everytime you add or remove a behaviour. >> >> It seems that the trick to undo Object.extend is done by erasing all >> instance methods in the modules. Isn't this a bit strange? If Ruby >> allows including modules in an instance's ancestor chain, there should >> be a way to remove them... > I am not sure to understand this question, do you mean removing them > from the includee? > That would not work, sorry if I miss the obvious here maybe you might > elaborate. > Trouble is that I don't really know what happens in the background in Ruby when you use Object.extend. I suppose an internal module chain is kept in instances in a similar way as it happens with classes: A.ancestors # => [A, Behaviourable, Object, Kernel] a.extend NormalBehaviour # should make something like a.internal_chain = [NormalBehaviour.clone, A, Behaviourable, Object, Kernel] If we remove all methods from that NormalBehaviour.clone and re-extend the object with that module, the chain will grow with an empty module: a.remove NormalBehaviour # a.internal_chain = [NormalBehaviour.clone.empty!, A, Behaviourable, Object, Kernel] a.extend NormalBehaviour # a.internal_chain = [NormalBehaviour.clone, NormalBehaviour.clone.empty!, A, Behaviourable, Object, Kernel] But that's my way of viewing this... I don't really know what happens in the background :) Cheers Jose -- Posted via http://www.ruby-forum.com/.