Properly extending test::unit to add logging

Hi,

I’d like to open up test::unit to add default logging to setup and
teardown. In java I did this with a base test class that every other
test extended, but that was never very nice as you had to remember to
call super in subclasses. The other option was adding aspects and all
that complexity. So I thought I could do it nicer in ruby with the
following:

alias_method :old_setup, :setup
def setup
logger.debug “**** Beginning setup for: #{name} ****”
old_setup
end

Is this the proper way to extend builtin methods, and still preserve
the old behavior? I’m thinking the timing that the above code gets
called is important. Right now I’m getting one test error when I
extend setup as above, as opposed to leaving it out, so I must be dong
something wrong.

Btw - this w/i a Rails app (the above code is w/i test_helper), but I
figured this is more ruby then rails specific.

thanks,
Rob

On Thu, 8 Jun 2006, Rob S. wrote:

def setup
logger.debug “**** Beginning setup for: #{name} ****”
old_setup
end

whenever you wrap methods like this always, always, always
do this

alias_method “old_method”, “method”

def method *a, &b
# …
old_method *a, &b
# …
end

you need to collect any args and block and pass them through.

regards.

-a

On 6/7/06, [email protected] [email protected] wrote:

you need to collect any args and block and pass them through.

regards.

Thanks for the tip. Besides that, are there any other gotchas related
to redefining methods? For instance, I’m redefining my_foo inside
module x, but y and z call my_foo before module x ever needs to get
loaded. Does that mean that my_foo will be the old version for y and
z, then the new version for any callers after module x gets loaded?

  • rob

On Thu, Jun 08, 2006 at 08:15:21AM +0900, Rob S. wrote:

end

you need to collect any args and block and pass them through.

regards.

Thanks for the tip. Besides that, are there any other gotchas related
to redefining methods?

This one is at least as conspicuous as the above one, but just in
case…

Try to come up with a more imaginative name than old_method for the old
definition, or you’ll get SystemStackErrors when somebody also overrides
it
using the same name…

If the method doesn’t take a block (or you’re running 1.9), you can also
use
the following idiom:

old_meth = instance_method(:method)
define_method(:method) do |*a| # &b too on 1.9

old_meth.bind(self).call(*a) # ditto

end

which is safer name-clash-wise, but note that this is slower than the
alias_method mechanism.

For instance, I’m redefining my_foo inside
module x, but y and z call my_foo before module x ever needs to get
loaded. Does that mean that my_foo will be the old version for y and
z, then the new version for any callers after module x gets loaded?

Yes (if the later calls would see the new definition at all under the
standard
method resolution rules).