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.