Method Introspection with Noah Gibbs
Video transcript & code
In Ruby, you can call .methods() on an object to see what other methods you can call on it. For instance:
Remember that everything in Ruby is an object, so the number 7 is too. That means you can call .methods() on it.
That output is kind of hard to read. It's in no particular order. So I like to sort them:
Sort does the right thing, so that looks better.
This list also doesn't tell you, "what interesting methods does this have?" There's a fun old trick for that:
7.methods.sort - Object.methods
You can try to do this with any parent class -- not just Object -- but the obvious approach doesn't work quite right:
7.methods.sort - Fixnum.methods
Seven is a Fixnum, so you'd expect that to be empty. But .methods() gets called on the Fixnum object itself -- the class Fixnum, not a specific instance of Fixnum. We want the instance methods that an instance of Fixnum would have.
7.methods.sort - Fixnum.instance_methods
That's better. And we can do it with any parent class we want.
7.methods.sort - Integer.instance_methods
Now that's interesting. Apparently Ruby doesn't define things like multiplication or to_f for the Integer class. You wouldn't directly make an instance of class Integer, but that's still a little quirk that we might care about some day -- for instance, if you inherit one of your own objects from class Integer.
So what else can we do with this?
You can ask, "what was that method name again?"
Here's one I use often. I forget the names of some of the Array and Hash methods, and this trick can find them for me:
.methods.sort - Object.methods
Remember that you have to use an actual array object -- an instance -- for the first argument. You won't get a useful answer if you say this:
Array.methods.sort - Object.methods
because that's checking the Array class object. Not what you want!
Ruby allows you to define a class across many files, so sometimes it's hard to know what methods there are, or where they're defined. It's nice to be able to get the list of methods for an object. But how can we find out more about a specific method? And especially, how can we find out who defined it?
Imagine that somebody has monkeypatched a class. You know some libraries that do this.
I'll add a very simple file that adds a sum method to your arrays and everything else enumerable. You can find it in the files for this episode. I'll require it here as an example of a mysterious third-party library that monkeypatches standard Ruby classes. If you're following along, put this into the same directory you're working in.
# enumerable_monkeypatch.rb module Enumerable def sum inject(&:+) # => 6, 1, 2.0, nil end end
Now, let's make sure it works.
require "./enumerable_monkeypatch" (1..99).sum
Now if we look for this method on Array, we'll find it:
But where did it come from? Ruby can tell us a lot about a method. So first let's grab that method:
What can we do with a method?
.method(:sum).methods.sort - Object.methods
Hm. "Owner" looks promising. Let's try that.
That told us that it's on Enumerable. But maybe we can do even better.
Oh, hey! There's our source file and line number. And now you can find out where any method was defined. Like the chopsticks say, now you can debug anything!
Oh -- one final bit of trivia before we're done. What if we try it on a function that isn't defined in Ruby? "Plus" on Fixnums is a C method, not a method in a Ruby source file.
Ruby says, "not telling." And now I'm done telling for this episode. I'm sure you'll enjoy the next one soon!