Ruby Forensics
4/Oct 2010
This guest post is contributed by Elise Huard, who is based in Brussels, Belgium and is the owner of Jabberwocky, a solutions company mostly focused on Rails. She has worked with a few other technologies before falling in love with Rails and Ruby about 3 years ago and going freelance to work with Ruby full time. She contributes to open source projects as much as she can, and has given talks at a few Ruby and Rails conferences. She’s a jack of all trades, loves reading, tinkering, food, travel, learning, and people out of the ordinary.
Ruby Forensics
Say you want to use a library, but no or very little documentation is available, and you don’t feel like diving into the code right away.
Well, you picked the right language. Ruby is blessed with what is called introspection: if you ask a Ruby program/class/module politely, it will tell you almost anything about itself. This post will tell you some tricks I use on a daily basis.
module Layer
FILLINGS = [:chocolate, :meringue, :jam, :cream, :strawberry]
def fill(filling)
puts "fill with #{filling}"
end
end
class Cake
include Layer
attr_accessor :calories
def ice
@calories = @calories + 200
end
def eat
puts "nom"
end
def self.bake
return new
end
end
class CheeseCake < Cake; end
Say you have a class, but you’d like to know what method it defines.
Cake.public_instance_methods
Won’t be that useful, because Ruby objects (with some exceptions like
BasicObject
) have got a whole lot of methods out of the box. Rather,
use:
Cake.public_instance_methods - Object.public_instance_methods
=> [:calories, :calories=, :ice, :eat, :fill]
If you want one particular method, and you have an idea of which name it should have:
(Cake.public_instance_methods - Object.public_instance_methods).grep(/eat/)
Will show if your instinct was right or not.
The same can be done for class methods, of course:
(Cake.methods - Object.methods)
=> [:bake]
(public_class_methods
also works.)
Note: this won’t show you the dynamic methods, like find_by_X
for
ActiveRecord. The class doesn’t know it has these kind of methods in
itself. They’re executed on the fly when the program hits
method_missing
. You’d have to look at the classes’ method_missing
method to find out.
Then there’s the case where you’d like to know, exactly, where a method
was defined. Ruby gives us the method
method, which takes a symbol
as an argument.
Cake.new.method(:fill)
=> #<Method: Cake(Layer)#fill>
Which tells us it was defined in the Layer module, included in the Cake class.
Cake.new.method(:eat)
=> #<Method: Cake#eat>
Sometimes, you want to know which other classes your class was descended
from – including mixed in modules. The ancestors
method will show you:
CheeseCake.ancestors
=> [CheeseCake, Cake, Layer, Object, Kernel, BasicObject]
Should you want to know about any constants, there’s a method for that too:
Cake.constants
=> [:FILLINGS]
All these methods have a good chance of giving you the information you want. When used in irb, together with a little experimentation, they can really help you find the code you’re looking for.
I hope you found this article valuable. Feel free to ask questions and give feedback in the comments section of this post. Thanks!
Do read these awesome Guest Posts:
- An introduction to eventmachine, and how to avoid callback spaghetti
- The Testing Mindset
- An Introduction to Desktop Apps with Ruby
- The Ruby movement
- Almost everything is an object (and everything is almost an object!)
- So… you’re new to Ruby!
- Incorporating Web APIs to spark computer programming exercises
- 14 Ways To Have Fun Coding Ruby
- Writing modular web applications with Rack
- How to Learn Ruby (or any programming language)