Saturday, January 10, 2009

A Gem Is Born: Announcing Gemviz

Final (potentially) update: Now appearing on github:

Update to first update: I realized that I did not specify a gem dependency on ruby-graphviz in my hoe-generated Rakefile. This is in place as of version 0.1.2.

To quote Groundskeeper Willie in The Simpsons - Treehouse of Horror V: "Och, I'm bad at this!"

We now return you to the first update.

Update: Peter Cooper correctly points out in his comment that Gemviz has a hard dependency on Graphviz, which isn't installed by default on Macs. I tend to take it for granted because it's one of the first things on my checklist to install when breaking in a new Mac. I use The Mac OS X Edition of Graphviz, as referenced on the Graphviz home page.

We now return you to the regularly scheduled original post.

Original Post: I can't really call myself a hotshot Ruby programmer until I release a RubyGem, now can I? Well, I still won't call myself a hotshot Ruby programmer yet...but, as of today -- and the release of versions 0.1.0 and 0.1.1 (for documentation purposes) of my new gem gemviz to -- at least I'm one step closer!

Check it out, yo:
[sudo] gem install gemviz
Now you can do e.g.,
gemviz rails
(assuming you have Rails installed as a gem which, if you don't by now: huh?), which produces this graph on my system:

In fairness, I should disclose that Gemviz is only about 30 lines of code, and makes heavy use of other people's work to do its magic. Here's how it works:

* it scrapes the output of the system command gem dep gem_name --pipe.
* for each dependency thus listed, it adds that dependency to the list of dependent gems.
* it does the same recursively for each dependent gem until it finds that the current gem has no dependencies, or that some (recursively) dependent gem is not installed on the system.
* it builds a Graphviz graph from the graph so built.
* it runs the awesome tred utility on the resulting Graphviz graph, which translates the original messy graph into one that properly represents all of the transitive dependencies.

If you're not sure what that last bit is about, don't feel bad: I didn't either at first. So here's the picture that's worth a thousand words (give or take):

The difference is subtle, but telling. The second graph shows that the rails gem has dependencies on activesupport and actionpack. Which is true, as far as it goes, and is reflected in the output of the gem dep command thusly:
gem dep rails
Gem rails-2.2.2
rake (>= 0.8.3, runtime)
activesupport (= 2.2.2, runtime)
activerecord (= 2.2.2, runtime)
actionpack (= 2.2.2, runtime)
actionmailer (= 2.2.2, runtime)
activeresource (= 2.2.2, runtime)

But it turns out that gem dep doesn't tell the whole story. rails does depend on those other gems, but only through its dependencies on activerecord, actionmailer, and activeresource, as shown in the first graph. That's what the tred utility (part of the Graphviz package) does: reduce the second graph to its minimal equivalent representation in the first graph.

Here's another more fun example, for merb, prior to transitive reduction:

And post tred:

This implementation owes much to the existing DepGraph, especially its use of Graphviz and tred. I basically just extracted the simple use case of graphing a gem at a time, and neglected to implement a bunch of DepGraph's options. For instance, the output of gemviz is always a single .png file per requested gem, named after the gem and placed in the current directory.

Keep in mind that this is a very simple initial release. Please let me know if you find it useful, and especially what improvements you can think of.