Enhancements to Railroad
A picture is worth a thousand words, and is certainly easier to talk about than reading a bunch of code. At least for me. On a recent Rails project two dozen models and sixty associations were needed to drive the application. A medium sized effort. However, it's tough to talk users through the complexity when the need arises, even when you're just dealing with a small chunk. After drawing circles and arrows on whiteboards too many times, I decided to mechanize. I'd used Railroad (a great rubygem written by Javier Smaldone) a few times before to document smaller projects. It loads models and controllers from a rails project and renders everything in `dot` format that can be processed by `graphviz`, an open source graph rendering framework. I railroaded the app and quickly had diagrams in hand. Happiness all around. But there were a few things I noticed as I talked around the pictures. I needed colors. And labels. And fewer circles and arrows. I decided to take a dive into the railroad code and add some features. What I eventually ended up with was a considerable set of changes. ### Changes to Allow Subgraphing Often I didn't want to see the whole graph at once. What I wanted was the ability to include only the models I listed, or exclude a set of models from the whole. After implementing this, I decided I also wanted the ability to focus on a set of nodes, including them and any other directly-connected nodes. * I added support to only include model classes in a diagram by name specifying `-I class1[,classN]` on the command line. * I added support to exclude model classes from a diagram by name using `-E class1[,classN]`. * I added the capability to focus on a set of model classes. By specifying `-F class1[,classN]` on the command line, only these nodes, the nodes they connect to, and the associations between them are displayed. However, this is also subject to any additional model class exclusions (`-E`) or inclusions (`-I`) that are specified. By using `-F`, `-E` and `-I` in combination, a logical subset of the model can be displayed fairly easily. ### Model Node Content Display Changes I found that I sometimes needed more or less information in a displayed node. Brief mode was already present (for just displaying the class name in a node) but I wanted a little more content control. * I changed the display of magic fields in model nodes to be off by default. Before, you'd hide the magic fields by specifying `--hide-magic` on the command line. I added a `--show-magic` option to turn them on instead. * I changed the display of association fields in the node to be off by default, shown only by specifying `--show-assoc-fields` on the command line, and label them with the name of the associated model. * I added a `-B` option to display all nodes as brief except focused nodes (those specified with `-F`). Using `-B` with `-F` turned out to be a great way to present information. I could see what was being focused upon in context, without extraneous detail. ### Changes to Association Display I found I needed to be able to show different associations differently. Conventional Rails associations were fine, left black and unlabeled. But polymorphic associations, many-to-many through relationships, and unconventionally named associations needed different labels and colors. By default, I decided to always display these and provide methods to hide them. * Unconventional associations and their labels are blue; navy blue if only one side of the relationship is unconventional. The labels are hidden by specifying `--hide-uaslab` on the command line. * Polymorphic associations and labels are red. The labels are hidden with `--hide-paslab`. * Through associations and labels are dark green. The labels are hidden with `--hide-taslab`. * For convenience, all association labels may be hidden with `--hide-aslab`. ### Multiple Diagrams at Once Once the rest was done and I'd used it all for a while, I decided that automatically generating a focused diagram for each model would save me a lot of time. * I added support to create a focused diagram for each model class in its own dot file using `-O FILE`. The output file name for each model is created as `FILE.dot`, and `FILE` may include directory separators. A simple rake task completed the automation: {% highlight ruby %} @railroad_command = Config::CONFIG["target_vendor"] == 'pc' ? 'railroad.bat' : 'railroad' task :graphs do FileUtils.mkdir_p 'graphs' `#{@railroad_command} -M -b -o graphs/_overview.dot` `#{@railroad_command} -M -B -O graphs/` FileUtils.cd 'graphs' do FileList['*.dot'].each do |f| `dot -Tpng #{f} -o #{f.gsub(/dot$/,"png")}` end end end {% endhighlight %} Finally, I refactored the completed code, DRYing out the iterative changes I'd made. What I now have is a nice, simple way to produce diagrams of the database and associations between tables for discussion and documentation. I submitted the patch to Javier and hopefully it will be integrated into the railroad trunk fairly soon. You can grab [railroad-0.5.0](https://rubygems.org/gems/railroad/versions/0.5.0){:target="rubygems"} and the [patch]({{ page.relative_prefix }}/code/railroad.patch){:target="railroad.patch"} and play with it if you want by downloading it from rubyforge.