Normally, this would be a task I would try to keep in my data layer since PostgreSQL is so great at doing this with recursive queries. However, sometimes you just need to operate in Ruby and Ruby alone. Let’s say I want to model what I always call an adjacency list but what is more technically a a directed graph. We have a set of nodes where any given node can have a parent node:
1 / \ 2 3 / \ 4 5
We will define two classes: Tree and Node. A Node object is a very simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
We have accessors for the id which will be a unique identifier and will give us the ability to easily retrieve nodes from the tree. The name is just an arbitrary string. Parent is just another Node object. Children is an array of Node objects that all have the current node as a parent. The nodes above would could be modeled as:
1 2 3 4 5 6 7 |
|
Adding children like this is tedious and we also don’t have good way to traverse the tree. So let’s introduce a tree class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
This class gives us a single accessor for nodes which is just a hash of Node objects. We can retrieve
a node via a method or with hash-like syntax. The add_node
method will add a node to the tree and it will also
update the children on a parent node. Now we can do fun stuff like this:
1 2 3 4 5 6 7 |
|
Now lets add some functionality to traverse the tree to get ancestors as well as the path of a node. These methods are part of the Tree class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Ancestors will return an array of node objects ordered from the top of the graph down to the parent of the given node. The path method uses the ancestor array to generate a human readable path to the node. Given the same tree above:
1 2 |
|
The path method could be used, for example, to generate URL paths from an XML taxonomy tree or other data structure.