A simple directory-tree printer in Ruby

The other day, I came across this interesting post by Tom Moertel. The blog post is called “A simple directory-tree printer in Haskell”. Haskell is interesting and this kind of programming exercise would really show how Haskell deals with real-world resources (the filesystem) as well as rendering the output in a nice human readable representation.

Here is some sample output:

|-- haskell.hs
|-- test.rb
|-- tree.rb
`-- tree.rb.html

His post was well written and I really liked how concise the resulting Haskell code was. I also liked the way he dealt with tree printing. I had never seen a tree printer algorithm like this before since I’m used to graphical versions which generally end up having a lot more code. For example, if I was doing this, I would have built a generic tree printer class and passed a lot more state around to each node (like the depth, sibling count, layout algorithms, etc.) Tom’s method is a great little pattern for console style tree output (like pstree) with only a few lines of code. For some past projects, such a simple routine would have come in handy.

So last night, I wondered what the Ruby version would look like. I tried to keep the example in the same functional style… just like his first example. The Ruby code stayed pretty concise.

Here is my Ruby version of Tom’s Haskell version:

 2 require 'pathname'

 4 $ArmMap = Hash.new("|   ")
 5 $ArmMap[""] = ""

 6 $ArmMap["`"] = "    "
 8 def visit(path, leader, tie, arm, node)

 9   print "#{leader}#{arm}#{tie}#{node}\n"

10   visitChildren(path + node, leader + $ArmMap[arm])

11 end
13 def visitChildren(path, leader)

14   return unless FileTest.directory? path
15   return unless FileTest.readable? path

16   files = path.children(false).sort    #false = return name, not full path

17   return if files.empty?
19   arms = Array.new(files.length - 1, "|") << "`"

20   pairs = files.zip(arms)
21   pairs.each { |e|  visit(path, leader, "-- ", e[1], e[0]) } 

22 end
24 ARGV << "." if ARGV.empty?

25 ARGV.map{ |path| visit Pathname.new("."), "","","",Pathname.new(path) }

This entry was posted in General. Bookmark the permalink.

Leave a Reply

Your email address will not be published.