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
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
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.