2024-06-04:
Ruby: Wrapping All Methods In A Class

The Problem | The Script | How-To | links


The Problem

I wanted a way to monitor/wrap all the method calls in a number of classes I have written in a project. This is a pretty common problem, used for tracking, logging, profiling, etc..

Unfortunately I couldn't find a clean, automated solution that would magically wrap all instance and class methods in a Class so I could see when they were called.

So I wrote one in pure ruby, using the fantastic ability to 'prepend' a module, and initially inspired by "How I Built Timeasure" (see links below)

The solution is robust and is even stackable, if you would want to do such a thing, and it should not generally be "breakable" by the class module as long as it doesn't 'unprepend' the wrapper.


The Script

It's just a ruby file, which you can get here: wrapper.rb

It has some example test code at the end ('class Foo') so you can just run the script to see it work, obviously you'd want to delete that to include this in your project.


How-To

How to wrap your class

The basic way is to just add a call to Wrapper.wrap(self) at the very beginning of your class definition. You don't necessarily need to save the wrapper object, but you can if you use it in a way that you need to access it.
class Foo
	@@wrapper = Wrapper.wrap(self)

	....define the rest of Class Foo here....
end
If you don't need to ask the wrapper object from the wrapped class, then you can make this a little cleaner/simpler:
class Foo
	Wrapper.wrap(self)

	....define the rest of Class Foo here....
end
You could also do this so you can just put "watch_this_class" at the top of every class you want to watch
class Class
	def watch_this_class
		@@wrapper = Wrapper.wrap(self)
	end
end
Or you could inherit this object if you want to use inheritance
class WatchedObj
	def self.inherited(subclass)
		@@wrapper = Wrapper.wrap(subclass)
	end
end
If you don't have the ability to edit the class you want to wrap, you can also define this before-hand (or else do the ipso-facto wrapping mentioned at the top of Wrapper)
class Foo
	@@wrapper = Wrapper.wrap(self)
end


Links

Here are some other articles/options that inspired this
How I built Timeasure:
Required specifying every method to track
Other ways to wrap a method:
Just shows some methods, not a full solution. Uses 'alias' which has namespace issues
Ruby Monitor-Functions
Specify each method - but a great resource I wish I had seen prior to writing mine
Rails (which I generally avoid) also has Notifications


Bugs/Errata

Don't know of any currently.

If you find any, please feel free to contact me


Back to Solutions.

DaveSource.com - Dave's geek site GetDave.com - all the current Dave Pointers. MarginalHacks - I have an elegant script for that, but it's too small to fit in the margin.