Graphing Moose Classes Automatically
A picture really is worth a thousand words. Or at least it's often a lot easier to understand than a thousand lines of code...
Tale of Woe
There was no escaping it. The latest version of the code that ran Santa's Workshop was much more complicated than it used to be, now that it needed to model so much more of what was going on in the ever-increasing North Pole operation.
The Elves had turned to Moose to increase their code re-use and it had been a great success. By using roles and parameterized roles they now had the power to easily bestow complex abilities on different classes with a carefully positioned
with statement. Suddenly a million abstract baseclasses were eliminated from their codebase, and complex code gymnastics were no longer necessary.
Even with this ability to consume roles - and have roles that further consumed roles - life was sometime harder than they'd like to admit when it came to debugging the code. They could, for example, see that the costing code was calling the
uses_default_paper method on the object, but they'd be darned if they could track down the sub that defined it....until I showed them how they could simply and automatically generate UML diagrams directly from their code.
Looking At Example Code
In the new Elf system there's a class for every type of gift, with configurable attributes that define who it's going to, size of the gift, etc. Here's the
Bike baseclass that
GiftBike extends is actually a lot more complicated than other gift classes in the codebase, because the elves use this same code in their internal transport management application.
Bike consumes is also consumed by the
Snowmobile, etc classes in other parts of the codebase. This gives the
Bike (and the
GiftBike subclass) the
GiftBike subclass represents a gift, it consumes the
Gift role, which, in addition to giving it the
associated_letter attribute and
put_on_sleigh method, gives it all the attributes and methods from the
DeliveryLocation role is straightforward:
As is the
GiftBike is also wrapped (not everything consuming the
Gift role is wrapped -- it's hard to wrap a new puppy, after all) and consumes the
Wrapped parameterized role. This parameterized role creates a new anonymous role when it is consumed, with methods and attributes that are dynamically created based on the parameters passed in when the role was consumed.
Wrapped class looks like this:
When this is consumed in the
There's an anonymous role created with a new
paper_type attribute and the new
uses_default_paper_type method in it.
Turning this into Pictures
The Meta::Grapher::Moose module is able to load in Moose classes and produce some pretty graphs from them.
For example, the default renderer uses GraphViz:
graph-meta.pl --package GiftBike --output=diagram.png
You can have GraphViz output in any of the formats it supports:
graph-meta.pl --package GiftBike --output=diagram.pdf
Either way, you get a simple diagram like this:
The thicke-bordered rectangles represent classes, while the thinner-bordered rectanges are roles. The various dashed-line-bordered rectangles represent the parameterized role and the anonymous role it creates.
To get more detail in our diagram, we need to switch renderers. The PlantUML project is a Java graphing library that can produce UML class diagrams. By passing the right options to
graph-meta.pl we can have it produce the PlantUML-compatible source code for the Moose classes and execute Java and the PlantUML code to produce a graph for us.
graph-meta.pl \ --package GiftBike --renderer=plantuml --plantuml=/opt/jar/plantuml.jar --output=diagram.png
This produces the much more detailed diagram we've already seen above.
We can even have PlantUML output the diagram in SVG if we want:
graph-meta.pl \ --package GiftBike --renderer=plantuml --plantuml=/opt/jar/plantuml.jar --output=diagram.svg