This article is part of series where we build and enhance the "mobile site generation" project. Each installment in this articles series looks at different computer language.

Project Summary
The "mobile site generation" project is a command line utility. The command line utility generates a section of my website which can be viewed on a mobile web browser. Please refer to previous installments of this series for more details.

Why Specifications Matter
This is the third version of my "mobile site generation" project. The first version was written in Java, the second version in Scala, and now, the third version in Ruby.The processes, data structures, data flow and basic logic have remained constant through all 3 versions. These constants are typically recorded in the "system specification". The system specification describes "what" the project does and "how" the project does it. The system specification can be expressed in many different forms (E.G. a formal document, diagrams, even test plans). The point is, the system specification has some value and it exists independent of the computer language used to implement the system.

Ruby
Ruby is the computer language name. "Ruby on Rails" is a application framework. A good portion of "Ruby on Rails" is written with Ruby (the computer language). However, "Ruby" and "Ruby on Rails" are not the same. In this article series, we are looking at computer languages (not application frameworks). Thus, this article is about "Ruby" the computer language (not "Ruby on Rails" the application framework).

Ruby's Elegance
Ruby's syntax and program statements are concise (short). Like Scala, we can use a like break to delimit statements. Ruby delimits functions with a simple "def" and "end". Again, like Scala your Ruby functions (methods) don't require an explicit "return" instruction. The function return the last statement result.

I used a SAX parser and SAX event handler to parse the website's RSS XML document. I used a Ruby Gem (I.E. library) LibXml. As an example of how elegant and concise Ruby is, here is my SAX event handler implementation:

class DocNodeListHandler
    def initialize
        @start = false
        @docNodeList = DocNodeList.new
        @docNode = DocNode.new
        @sb = ""
    end 

    include XML::SaxParser::Callbacks

    def on_start_element(element, attributes)
        @sb = ""
        if element == "language" then
            @start = true
        end
        if element == "item" && @start == true then
            @docNode = DocNode.new
        end
    end

    def on_end_element_ns (name, prefix, uri)
        case name
            when "title" then @docNode.title = @sb
            when "item"  then 
                    @docNodeList.add(@docNode)
                    @docNode = DocNode.new
            when "pubDate" then @docNode.pubDate = @sb
            when "link" then @docNode.link = splitLink(@sb)
            when "description" then @docNode.body = @sb
            when "category" then @docNode.addCategory(@sb)
        end

    end

    def on_characters (chars)
        @sb << chars
    end

    def getList
        @docNodeList
    end
    def splitLink(link)
        elems = link.split("/")
        num_elems = (elems.length - 1)
        elems[num_elems]
    end
end

A quick explanation of the above code listing. Ruby class instance properties (I.E. variables local to a class instance) as designated with "@" (E.G. @start). A Ruby class instance constructor is the "initialize" method (I.E. initialized is called with CLASSNAME.new). The "on_end_element_ns" method demonstrates Ruby's "case" construct (I.E. analogous to Java's switch/case construct). The statement " @sb << chars" concatenates the string buffer ( << is a Ruby concatenation operator).

Note! Again, the SAX parsing routine looks very similar to the Java and Scala implementations.

Project Functional Enhancements

Implementation Summary

The RSS feed is an xml document. The rss xml document includes the "categories" for each article. Thus, all we need to do is add a property to our existing model "DocNode" that stores a list of categories.

As mentioned above, we are retaining our SAX parser strategy with our Ruby implementation. Thus, all we need to do is add a couple of statements to our Sax event listener that responds to the recognition of a "category" tag in the RSS XML document.

We already have a routine that creates a "table of contents"/"index.html". The "table of contents" is simply a list all article titles published on the mobile website. Each article is a link that allow the reader to view the full article on their mobile device. Our existing "table of contents" routine takes three parameters, 1) a list of articles, represented as DocNodes, 2) the file name we are creating, 3) a "header" displayed at the top of the "table of contents" document (E.G. "Public Action Articles"). So, all we need to do is substitute the parameters. Instead of a list of all articles, a list of articles that contain a specific category (E.G. JavaScript or Polyglot). In other words, all we have to do is taking our existing "table of contents" routine and call it 2 more times. Once to produce a JavaScript Articles - table of contents. Once to produce a Polyglot Articles - table of contents.

Implementation Details

We already have a class that modifies each article's body. Thus, all we need to do is add a couple new steps to the existing "body modification" method. Specifically, search for instances of the following link href=js-index.html and replace the href value with our new mobile JavaScript category table of contents page, href=http://public-action.org/mob/js-index.html

Same thing for "Polyglot". Search for href="js-index.html and replace it with href="http://public-action.org/mob/polyglot-index.html.

Like Scala, Ruby has built in support for interspersing Ruby statements and markup (E.G. XML, HTML). I used the Embedded Ruby (ERB) to generate the individual web page articles. If you used JavaScript templates, Apache Velocity, Microsoft Active Server Pages (ASP), Java Server Pages (JSP), then ERB is very familiar. ERB allowed to embed conditional statements within the HTML markup. You can view the full source listing on our Git Hub repository.

In addition, Ruby allowed me to intersperse Ruby statements with string blocks. Here is my listing which generates our mobile site map.

class SiteMap 

    def initialize(baseAddr)
        @baseAddr = baseAddr
    end

    def writeOutputFile(fileName, docList) 
      @output =
        "
          
            #{@baseAddr}
            daily
            1.0
            #{convertDate(docList[0].pubDate)}
          "

         docList.each { |doc| @output << 
            "
                #{fullLink(doc.link)}
                #{convertDate(doc.pubDate)}
            "
          }
        @output << ""

        File.open(fileName,"w+") do |f| f.write(@output) end
    end

    def fullLink(link)
        @baseAddr + "/"+ link + ".html"
    end

    def convertDate(date)
        elems = date.split(" ")
        elems[3] + "-" + convertMonth(elems[2]) + "-" + elems[1]
    end

    def convertMonth(monthStr) 
        case monthStr
            when "Jan" then "01"
            when "Feb" then "02"
            when "Mar" then "03"
            when "Apr" then "04"
            when "May" then "05"
            when "Jun" then "06"
            when "Jul" then "07"
            when "Aug" then "08"
            when "Sep" then "09"
            when "Oct" then "10"
            when "Nov" then "11"
            when "Dec" then "12"
        end
    end
end

Note the line

#{fullLink(doc.link)}

. "#{fullLink(doc.link)}" is method call embedded in the string block.

Generating Documentation.
Ruby provides a documentation generator, just like Java and Scala. Ruby's documentation generator is called Rdoc. You add comment annotations to your Ruby source which instruct Rdoc to customize the internal documentation.

Ruby Packaging.
The Ruby community provides are rich set of packaging tools (E.G. gem, rake). For this project, I considered packaging up the source listings. We are using the default Ruby implementation (I.E. not JRuby which is compiled). The default Ruby implementation is interpreted. Thus, for our small program, I decided to forgo packaging.

Running the Program
Our Ruby program is simple command line. We just change in to the directory where the Ruby source files are located and issue the following command:

$ ruby ruby MainProcess.rb -in RSS-DATA-FILE -out OUTPUTDIR

. RSS-DATA-FILE is the path and file name of the RSS XML document. OUTPUTDIR is a directory the program creates, relative to the current directory (thus a simple value like "mob").

Full source code available on Git Hub repository.
I added a new repository on Git Hub for my Ruby version of the "mobile site generator. You can peruse (and use) all of the source code located here.

I recommend use of Ruby.
As advertised Ruby is both powerful and easy to learn. The Ruby language documentation is well written and provides essential sample usages (always a big plus for me). Thus, I recommend Ruby.

About the Author:
Lorin M Klugman is an experienced developer focused on new technology.
Open for work (contract or hire).
Drop Lorin a note. Click here for address.
Please no recruiters :)
- Home