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.