It’s a bit evil with eval…but it works with both a proc as a parameter
or the block
class Object
def extend_method(meth, proc=nil, &block)
proc = block unless proc
meth = meth.to_s if meth.kind_of? Symbol
self.instance_eval {
@procs = {} unless @procs
@procs[meth]=proc
}
return if respond_to? “#{meth}”
instance_eval <<-EOS
class << self
alias_method :#{meth}, :#{meth}
def #{meth}(*args)
send(:#{meth}, *args)
@procs[‘#{meth}’].call(*args)
end
end
EOS
end
end
class O
def x(a)
puts “do something with #{a}”
end
end
o = O.new
# check arity
if method( meth ).arity.abs != p.arity.abs
raise ArgumentError, "Arity doesn't match #{type}##{meth.id2name} and #{p.inspect}"
end
# stores procs in this instance var
@__proc_add__ ||= {}
unless @__proc_add__.has_key?( meth )
@__proc_add__[meth] = [p]
# eval is a code smell but oh oh well
type.module_eval <<-"end;"
alias_method :__#{meth.to_i}__, :#{meth.id2name}
private :__#{meth.to_i}__
def #{meth.id2name}( *args )
__#{meth.to_i}__( *args )
if @__proc_add__.is_a? Hash
if @__proc_add__.has_key?( :#{meth.id2name} )
@__proc_add__[:#{meth.id2name}].each { |p|
p.call( *args )
}
end
end
end
end;
else
@__proc_add__[meth] << p
end
end
end
Simple test
class O
def x(a)
puts “do something with #{a}”
end
end
Add two procs to this instance’s x meth
o = O.new
p = Proc.new { |a| puts “do something else with #{a}” }
o.append_proc_to_method( :x, &p )
o.append_proc_to_method( :x ) { |a| puts “do another something else with #{a}” }
o.x(2)
Make sure this instance’s x is still just running the original
I will first answer your question with a question:
*** WHY!?! ***
But here’s a solution, just for fun (based off the same idea as
[ruby-talk:29122]):
class Object
def append_to_method(method_name, p)
m = method(:x)
mod = Module.new
mod.send(:define_method, :x) do |*args|
m.call(*args)
p.call(*args)
end
extend(mod)
end
end
class O
def x(a)
puts "do something with #{a}"
end
end
o = O.new
p = Proc.new { |a| puts “do something else with #{a}” }
o.append_to_method(:x, p)
o.x(“foo”)
The only disadvantage I can think of is that if x takes a block, you can
now no longer pass it a block. I figure this probably isn’t a problem
anyway since you are appending a proc to it, which cannot take a block.
paul. i think i’ll give you the prize $ cha-ching your solution is
the overall best i think. rich’s and the lucky stiff’s were definite
feats and took some extra things into consideration which was nice:
arity error checking for instance. and ned’s was amazingly short and
sweet --good for toplevel execution. but yours is small and versitle and
is much like what i finally worked out myself, only i kept it external
from the object:
m = o.method(:x) # get the object method
mod = Module.new # make a module with redefined method
mod.module_eval {
define_method(:x) { |*args|
m.call(*args)
p.call(*args)
}
}
o.extend mod # extend object
i wonder how send vs. module_eval compare?
thanks everyone. hope it was a fun and educational.
~transami
···
On Thu, 2002-07-25 at 08:25, Paul Brannan wrote:
I will first answer your question with a question:
*** WHY!?! ***
But here’s a solution, just for fun (based off the same idea as
[ruby-talk:29122]):
class Object
def append_to_method(method_name, p)
m = method(:x)
mod = Module.new
mod.send(:define_method, :x) do |*args|
m.call(*args)
p.call(*args)
end
extend(mod)
end
end
class O
def x(a)
puts “do something with #{a}”
end
end
o = O.new
p = Proc.new { |a| puts “do something else with #{a}” }
o.append_to_method(:x, p)
o.x(“foo”)
The only disadvantage I can think of is that if x takes a block, you can
now no longer pass it a block. I figure this probably isn’t a problem
anyway since you are appending a proc to it, which cannot take a block.