Puzzle...cleaner way to redefine a method?

Here’s a puzzle. There must be a cleaner way to do this.

I would like to have users of this class be able to call “set_wrapper”
and define pre and post behavior on the “working” method. The only
simple way I’ve found is evaling a string the redefines “working”.
Searching threads on instance and class_eval tends to turn up an oil
slick of arguments and misinformation. Help is apprecaited!

class Thing
  def set_wrapper(string)
    eval(string)
  end

  def working
    yield
  end

  def main
    working do
      p 'test'
    end
  end
end

a = Thing.new

a.main

a.set_wrapper("def working; p 'pre'; yield; p 'post'; end")

a.main

Blackie wrote:

def set_wrapper(string)
end
end
end

a = Thing.new

a.main

a.set_wrapper(“def working; p ‘pre’; yield; p ‘post’; end”)

a.main

Try this (untested):

class Thing
def add_pre_call(&prc)
(@pre_procs ||= []) << prc
end
def add_post_call(&prc)
(@post_procs ||= []) << prc
end
def working
@pre_procs.each {|p| p.call} if @pre_procs
yield
@post_procs.each{|p| p.call} if @post_procs
end
end

a = Thing.new
a.main
a.add_pre_call { p ‘pre’ }
a.add_post_call { p ‘post’ }
a.main

I have to go to dinner now, but here’s something quick you should
look at:

Aquarium, for aspect oriented programming
http://aquarium.rubyforge.org/

Aspect Oriented Programming is definitely what your looking for :slight_smile:

--------------------------------------------|
If you’re not living on the edge,
then you’re just wasting space.

Hi,

maybe this is slightly cleaner in interface (in that you pass in pre and
post procs rather than a string) but you still need to use eval
somewhere
(in Ruby 1.8 anyway) to be able to use ‘def’ so you can pass on the
block.
Shows the principle anyway.

module Wrapper
def wrap(meth, pre_block, post_block)
m = Module.new do
define_method “pre_#{meth}”, &pre_block
define_method “post_#{meth}”, &post_block
eval %[
def #{meth}(*a, &b)
pre_#{meth}
rv = super(*a, &b)
post_#{meth}
rv
end
]
end
self.extend m
end
end

class Thing
include Wrapper

def working
yield
end

def main
working do
p ‘test’
end
end
end

a = Thing.new
a.main
a.wrap :working, proc { p ‘pre’ }, proc { p ‘post’ }
a.main
END
“test”
“pre”
“test”
“post”

If you don’t want to include Wrapper in the class, you could use
a.extend(Wrapper)
instead.

Regards,
Sean

Whoops - apologies for top posting