Tree Crawling Problem
My opportunities to work on coding problems occur rarely in the office I work in. So when I was asked to figure out a menu generation problem, I was more than happy to accept. All of the content I'm working on will eventually be included on the DVD which will come included as supplemental material for a new edition of an oceanographic textbook titled Descriptive Physical Oceanography. What this means is all files included will be unchanging, static, little to no dynamic content. It is also desirable to have any materials included with the book be as self contained as possible due to the seafaring nature of oceanographic work, internet at sea may be unavailable or, when available, very expensive and slow.<br /><br />To allow the easy construction of static, self contained websites while utilizing templates and generators that use the state of the dynamic content at the time of "compile", we use a ruby gem</a> called webgen</a>. This gem allows easy creation of static websites using templates, markup languages, and a rather nifty programing language called ruby</a>.<br /><br />Webgen has a rather nice menu generation method which crawls a tree generated from the files and directories in a special folder of the webgen project. When the webgen command is run in the project directory, it crawls this folder looking for changes and updates the tree accordingly. As convenient as this was, it did not function in a way that would suit our needs based on what the very talented graphic designer had produced. The built in behavior was to create a nested unordered list</a>. What we needed it to do was only use the unordered list tag for the first level of the tree. Subsequent branches, if any, needed to be in the html div tag</a> and not be nested within the unordered list, as this both makes applying styling difficult, but also appears to be unsupported by the spec. To accomplish these changes, I needed to extend the functionality of webgen. Luckily, there was a framework in place to allow this and, thanks to the object oriented nature of ruby, I could easily inherent the functionally of built in menu generation class and simply override the methods I needed to. Just my luck, only one method need to be overridden. Bellow is the almost finished product.<br /> ### Loop through the tree and all the children to generate the menu ### ### Only the first level should be encapsulated in a
### Getting the Image path, this should act the same as the relocatable: tag ### out_div << "<img src=#{image_path}>" if child.node['thumb'] out_div << "<br />#{child.node['blerb']}" if child.node['blerb'] out_div << menu ### Closing the approprate tags to keep things tidy ### ### Add the output of the div enclosed items to the *END* of the output containing the ul ### Webgen::WebsiteAccess.website.config['contentprocessor.tags.map']['custommenu'] = 'Webgen::Tag::CustomMenu'
Code:<br /></p>
module Webgen
module Tag
include Webgen::Tag::Base
class CustomMenu < Webgen::Tag::Menu
def create_output_nested(context, tree, level = 1)
### output variable initialization ###
out = ""
out_div = ""
out = "
" if level == 1
out_div = "
tree.children.each do |child|
menu = child.children.length > 0 ? create_output_nested(context, child, level + 1) : ''
style, link = menu_item_details(context.dest_node, child.node, context.content_node.lang, level)
out << "
<li #{style}>#{link}" if level == 1 tag ###
if level > 1 then
out_div << "
out_div << "
image_path = ""
depth = context.node.level - 1
depth.times do
image_path << "../"
end
image_path << "images/"
image_path << child.node['thumb'] if child.node['thumb']
### Done ###
out_div << link
out_div << "</div>"
end
out << "</li>" if level == 1
end
out_div << "</div>" if level > 1
out << "</ul>"
out << out_div
out
end
end
end
end
</code>
</pre><br />You may be noticing that it uses a lot of if statements to check what level of the tree the loop is on. Granted this may not be the best practice and would probably have been better to separate all the level one stuff from the greater than level one stuff by putting them in separate methods. However, if you were able to spot it, you may have noticed that there is recursion</a> occurring in the method. Since it can be tricky to visualize the result of using recursive methods, I opted to just check to see what level of the tree the loop was on, and the construct the appropriate output accordingly. I even managed to throw in a connivence method by resolving the depth of the page in the website, and constructing an appropriate link to the images on the page. Bottom line, all the images work.<br /><br />In all this took about two days to sort out. Most of the first one spent learning the ins and outs of the API</a> and most of the second day moulding the output to what I wanted.<br /><br />Finished product due in 3 weeks.<br />-Andrew</p>