A very short and simple question: is there a function to "unextend" an
object ? Obviously it does not exist under that name, nor could I
find
anything like it. It would be useful though to do "traits":
dirk = Human.new
dirk.extend(actor)
dirk.work
if dirk.income < PITTANCE
dirk.unextend(actor)
dirk.extend(waiter)
dirk.work
end
Dirk van Deun
I don't believe there is an unextend method, however if you're
willing to live with some dark magic that I haven't extensively tested:
irb(main):001:0> require 'quasiextender'
=> true
irb(main):002:0> class A
irb(main):003:1> def each
irb(main):004:2> yield 1
irb(main):005:2> end
irb(main):006:1> include QuasiExtender
irb(main):007:1> end
=> A
irb(main):008:0> a = A.new
=> #<A:0x370718>
irb(main):009:0> a.quasi_extend(Enumerable)
=> [Enumerable]
irb(main):010:0> a.map { |x| x.to_s }
=> ["1"]
irb(main):011:0> a.quasi_retract(Enumerable)
=> []
irb(main):012:0> a.map { |x| x }
NoMethodError: undefined method `map' for #<A:0x370718
@__quasi_extensions=[]>
from ./quasiextender.rb:30:in `method_missing'
from (irb):12
And here's the evil code. Its actually not terribly long:
% cat quasiextender.rb
require 'delegate'
module QuasiExtender
def quasi_extend(mod)
@__quasi_extensions ||= []
@__quasi_extensions << mod
end
def quasi_retract(mod)
@__quasi_extensions ||= []
@__quasi_extensions.delete_if { |ext| ext == mod }
end
def method_missing(name, *args, &block)
@__quasi_extensions ||= []
meth = nil
found_mod = nil
@__quasi_extensions.each do |ext|
begin
meth = ext.instance_method(name.to_sym)
found_mod = ext
rescue NameError, NoMethodError
next
else
break
end
end
if meth.nil? # we didn't find it
super
else
sneaky_class = Class.new(SimpleDelegator) {
include(found_mod)
}
sneaky_obj = sneaky_class.new(self)
meth.bind(sneaky_obj).call(*args, &block)
end
end
end