Background
Recently I was trying to understand how the Arel (Relational Algebra for Ruby) internals work.
The project has strong OO design and uses some metaprogramming techniques. Main code base consists of ~ 80 files, with files having 30 LOCs on average.
I’m totally cool with that kind of modular/small classes design, but for new comer (like me) it could be a little daunting and I had to draw some diagrams on paper before finally getting it instead of getting lost in inheritance chains, compositional patterns and so on.
Solved In SmallTalk 20 Years Ago
Indeed as with many other problems it’s been solved in SmallTalk for many years. Having live classes reside in live virtual machine is one the SmallTalk features that makes rich reflection facilities possible and which is missing from Ruby.
So Can We Have A VM Please?
While there is no Ruby VM in a strict sense what’s that Rails development server
process we have running if not a VM with classes kept in sync with their «on the
disk»/plain-text representation? Well at least it’s pretty close, also the
fact that at least half of the metaprogramming stuff happens at load time
definitely helps for better reflections (think klass.included hook,
attr_reader, delegate methods)
What IDE?
Really I would love to have something like this built-in into my favorite IDE, but the problem is that I’m not even sure what’s mine favorite IDE is, and there is even less agreement on the subject in the community.
What Ruby Offers? Or Quick Recap Of Ruby Reflection Techniques
- Kernel#method and Kernel#instance_method let’s you determine where the method defined on an object (instance of the object) comes from (an old trick taught by Dave Thomas). Less known is that with 1.9 you can get the file and line number of method definition with Method#source_location
-
Module#ancestors
because of Ruby unified object model let’s you determine class ancestors,
modules included in class, and modules included by another module. This
also works seamlessly for singleton classes (i.e. when you have something
like
C.extend(M)moduleMbecomes an ancestor ofC.singleton_class) - Object#methods and Module#instance_methods when passed false as first parameter can be used to enumerate methods owned by class (as contrasted with returning owned and inherited methods by default)
-
Ripper Ruby parser
although not directly reflection related Ripper is part of a standard library
and is based on
parse.y(the grammar file which Ruby interpreter uses) which automatically makes it always up-to-date with any Ruby syntax changes.
Get Reflexive
With the set of aforementioned tools in hand I went and created sort of proof-of-concept good old web-app with virtually no UI and enjoyed the process a great deal. It browses classes and modules, and the corresponding source code files with some interactivity twists (and a little dose of pink color).
Visit the GitHub project page
to learn more about what it does and how to get
it for your applications.
You can also check out the
demo Rails 3 app with Reflexive installed
running on Heroku but be warned that Heroku has old Ruby 1.9.1 which has
some issues with the reflection capabilities Reflexive uses.
In Conclusion
It seems to me that live reflection is the really powerful way to get precise code
navigation/autocompletion/refactoring tools in Ruby IDEs. It also should be
possible to integrate Reflexive in RDoc or YARD to get more interactive source
code listings, but I scratched my own itch now and have to move on