Ruby Forensics

by on October 4, 2010

Ruby Forensics

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.

Elise Huard 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:

Forensic Psychology Degree

Technorati Tags: , , , , ,

Posted by Elise Huard

Follow me on Twitter to communicate and stay connected

{ 11 comments… read them below or add one }

makuchaku October 4, 2010 at 10:43 am

Cake.new.method(:fill) was a new learning :)
Thanks!

Reply

maurizio de magnis October 4, 2010 at 12:55 pm

The infos above deserve a cheatsheet ;-)

Reply

Elise Huard October 4, 2010 at 1:20 pm

Heh, I guess this is a kind of cheat sheet :)
It’s pretty basic compared to some of the other guest posts, but I figured it might be helpful to beginning rubyists.

Reply

ChristiaanVDP October 4, 2010 at 1:26 pm

Hi Elise,

I’ve created a simple gem called ‘reflectorr’ (https://rubygems.org/gems/reflectorr) that can help with the question: ‘Which methods can I use here?’

For me this is an extra aid for understanding how things (like rails) can be used to the extreme.

C+++

Reply

Elise Huard October 4, 2010 at 3:05 pm

Hi Christiaan, thanks for that gem, I didn’t know about it !
I think it’s not a bad thing for people to know which ruby features they can use, though.

Reply

ChristiaanVDP October 4, 2010 at 4:39 pm

Elise,

you’re right, I’m certain this can be of help for many rubyists.

C+++

Reply

Jesse October 4, 2010 at 1:44 pm

Thanks for posting this! I already knew some of it but I never thought about subtracting Object.methods. Very clever!

Reply

Jarmo Pertman October 5, 2010 at 10:19 pm

There is another way of getting methods by providing a “false” to include_super parameter:

Cake.public_instance_methods(false)
=> ["ice", "calories", "eat", "calories="]

This will although also miss the method from Layer. So:
Cake.included_modules.map {|mod| mod.public_instance_methods(false) unless mod == Kernel}.compact
=> [["fill"]]

Anyway, there is room for improvement :)

Reply

Elise Huard October 6, 2010 at 12:37 pm

Nice, I didn’t know that ! thanks for sharing this, always happy to learn.

Reply

Aggelos Orfanakos October 6, 2010 at 12:26 am

Hello. I think you can avoid the hassle of subtracting `Object.public_instance_methods` and `Object.methods` by passing `false` to `public_instance_methods` and `methods` respectively: `Cake.public_instance_methods(false)` and `Cake.methods(false)` This should return only the methods defined in the receiver.

Thanks for the article, I learned a few things I didn’t know. :-)

Reply

Elise Huard October 6, 2010 at 12:38 pm

Thanks for sharing ! previous commenter also mentioned that.

Reply

Leave a Comment

{ 32 trackbacks }

Previous post:

Next post: