A Code Challenge

Problem:

given a proc p and an object o with method x, such that the arity of x
and p are the same, dynamically append p into x.

statically you have this:

class O
def x(a)
puts "do something with #{a}"
end
end
o = O.new

then given:

p = Proc.new { |a| puts “do something else with #{a}” }

the goal is to dynamically modify instance o such that it were AS IF it
were an instantiation of:

class O
def x(a)
puts "do something with #{a}"
puts "do something else with #{a}"
end
end

~transami

Well…here is some Ruby-fu.

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

o.x(1)

p1 = Proc.new { |a| puts “do something wonderful with #{a}” }
o.extend_method(:x, p1)
o.x(2)

o.extend_method(:x) { |a| puts “do something sweet with #{a}” }
o.x(3)

-rich

From: Tom Sawyer [mailto:transami@transami.net]
Sent: Wednesday, July 24, 2002 11:56 PM
To: ruby-talk ML
Subject: A Code Challenge

Problem:

given a proc p and an object o with method x, such that the arity of
x
and p are the same, dynamically append p into x.

statically you have this:

class O
def x(a)
puts "do something with #{a}"
end
end
o = O.new

then given:

p = Proc.new { |a| puts “do something else with #{a}” }

the goal is to dynamically modify instance o such that it were AS IF
it

···

-----Original Message-----
were an instantiation of:

class O
def x(a)
puts "do something with #{a}"
puts "do something else with #{a}"
end
end

~transami

You mean like this?

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}” }

def o.x(a)
super
$p.call(a)
end

o.x(‘abc’)

···

On Wednesday 24 July 2002 08:56 pm, Tom Sawyer wrote:

Problem:

given a proc p and an object o with method x, such that the arity
of x and p are the same, dynamically append p into x.

statically you have this:

class O
def x(a)
puts "do something with #{a}"
end
end
o = O.new

then given:

p = Proc.new { |a| puts “do something else with #{a}” }

the goal is to dynamically modify instance o such that it were AS
IF it were an instantiation of:

class O
def x(a)
puts "do something with #{a}"
puts "do something else with #{a}"
end
end

~transami


Ned Konz
http://bike-nomad.com
GPG key ID: BEEA7EFE

not too elegant but a starting point:

class Object

meth is aSymbol, p is aProc :wink:

def append_proc_to_method( meth, &p )

# 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

o2 = O.new
o2.x(4)

_why

···

Tom Sawyer (transami@transami.net) wrote:

Problem:

given a proc p and an object o with method x, such that the arity of x
and p are the same, dynamically append p into x.

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

THANKS YOU ALL FOR PARTICIPATING! SOME COOL CODE!

paul. i think i’ll give you the prize $ cha-ching :wink: 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.

Paul


~transami

Hi,

···

At Thu, 25 Jul 2002 23:25:48 +0900, Paul Brannan wrote:

class Object
def append_to_method(method_name, p)
m = method(:x)
mod = Module.new
mod.send(:define_method, :x) do |*args|

These :x shouldn’t be method_name?


Nobu Nakada

module_eval is probably better. Using send to call private methods is
a little bit evil.

Paul

···

On Fri, Jul 26, 2002 at 01:33:53AM +0900, Tom Sawyer wrote:

i wonder how send vs. module_eval compare?

Yes, they should. My mistake for using cut-and-paste programming.

Paul

···

On Fri, Jul 26, 2002 at 03:48:40AM +0900, nobu.nokada@softhome.net wrote:

These :x shouldn’t be method_name?

module_eval is probably better. Using send to call private methods is
a little bit evil.

Sorry, but there is a small mistake in your message.

This is eval which is evil.

Guy Decoux

p.s.: sorry, I could not resist :slight_smile: