2007-05-02

Ruby Tip - Cleaner Object Inspection

Here's a cute tip I picked up in Ruby a little while back. Ruby has some nice built-in inspection methods that let you discover some things about an object. The public_methods method will list all the public methods for a particular object:

irb(main):001:0> ''.methods
=> ["methods", "instance_eval", "%", "rindex", "map", "<<", "split", "any?", "du
p", "sort", "strip", "size", "instance_variables", "downcase", "min", "gsub!", "
count", "include?", "succ!", "instance_of?", "extend", "downcase!", "intern", "s
queeze!", "eql?", "*", "next", "find_all", "each", "rstrip!", "each_line", "+",
"id", "sub", "slice!", "hash", "singleton_methods", "tr", "replace", "inject", "
reverse", "taint", "sort_by", "lstrip", "frozen?", "instance_variable_get", "cap
italize", "max", "chop!", "kind_of?", "capitalize!", "scan", "select", "to_a", "
each_byte", "type", "casecmp", "gsub", "protected_methods", "empty?", "to_str",
"partition", "tr_s", "tr!", "match", "grep", "rstrip", "to_sym", "instance_varia
ble_set", "next!", "swapcase", "chomp!", "is_a?", "swapcase!", "ljust", "respond
_to?", "between?", "reject", "to_s", "upto", "hex", "sum", "class", "method", "r
everse!", "chop", "<=>", "insert", "<", "tainted?", "private_methods", "==", "de
lete", "dump", "===", "__id__", "member?", "tr_s!", "unpack", ">", "concat", "re
quire_gem", "nil?", "untaint", "succ", "find", "strip!", "each_with_index", ">="
, "to_i", "rjust", "<=", "send", "display", "index", "collect", "inspect", "slic
e", "oct", "all?", "gem", "clone", "length", "entries", "chomp", "=~", "public_m
ethods", "upcase", "sub!", "squeeze", "__send__", "upcase!", "crypt", "delete!",
"equal?", "freeze", "object_id", "detect", "require", "zip", "[]", "lstrip!", "
center", "[]=", "to_f"]

Above is an array of every method available for a String object. It's useful, but if you're trying to track down the name of a particular method, it's going to be hard to find. So the first step is to use sort on the resulting array:

irb(main):003:0> ''.public_methods.sort
(Picture the same output as above, but sorted ;-)

Better, but we still have a lot of inherited methods that aren't specific to Strings, like object_id, clone, and inspect (not to mention public_methods itself). How do we filter those out? With the reject method:

irb(main):014:0> ''.public_methods.reject {|m| Object.methods.index(m)}.sort
=> ["%", "*", "+", "<<", "[]", "[]=", "all?", "any?", "between?", "capitalize",
"capitalize!", "casecmp", "center", "chomp", "chomp!", "chop", "chop!", "collect
", "concat", "count", "crypt", "delete", "delete!", "detect", "downcase", "downc
ase!", "dump", "each", "each_byte", "each_line", "each_with_index", "empty?", "e
ntries", "find", "find_all", "grep", "gsub", "gsub!", "hex", "index", "inject",
"insert", "intern", "length", "ljust", "lstrip", "lstrip!", "map", "match", "max
", "member?", "min", "next", "next!", "oct", "partition", "reject", "replace", "
reverse", "reverse!", "rindex", "rjust", "rstrip", "rstrip!", "scan", "select",
"size", "slice", "slice!", "sort", "sort_by", "split", "squeeze", "squeeze!", "s
trip", "strip!", "sub", "sub!", "succ", "succ!", "sum", "swapcase", "swapcase!",
"to_f", "to_i", "to_str", "to_sym", "tr", "tr!", "tr_s", "tr_s!", "unpack", "up
case", "upcase!", "upto", "zip"]

The result are String-only public methods. Notice the list is much shorter (49 fewer methods, to be exact).

One more step: if you want to filter out any inherited methods (not just Object's), use superclass:

irb(main)> class A; def a; end; end;
irb(main)> class B < A; def b; end; end
irb(main> B.instance_methods.reject {|m| Object.methods.index(m)}
=> ["b", "a"]
irb(main)> B.instance_methods.reject {|m| B.superclass.instance_methods.index(m)}
=> ["b"]

In this contrived example, A is a class with one method (a), and B is a class inherited from A with its own method (b). If you filter out Object's methods from B's, it shows a and b. If you filter out A's methods from B's, it only shows b.

I should just point out that I used instance_methods, not methods or public_methods, since a and b are both instance methods, and would not show up in B.methods. If I made an instance of B (using B.new), then it would show up for that.

Whew! Hope this helped somebody...

--YY

5 comments:

Anonymous said...

this help meeee :)

Sam Livingston-Gray said...

Ran across this while searching for something else. I often use this at the irb prompt:

foo.methods.sort - Object.methods - Kernel.methods

Array subtraction accomplishes the same thing as your reject example, but is easier to type. (=

YodaYid said...

Great - thanks Sam!

Joshaven said...

String.instance_methods(false).sort will give you only the instance methods defined in the String class. Excluding Object & Kernel methods does not mean that your only including the String Objects methods.

See the Ruby API:
http://ruby-doc.org/core/classes/Module.html#M001683

YodaYid said...

Thanks Joshaven! That's much cleaner :-)