Login

Leveraging Ruby

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

  • Add "Category" view to the mobile web site.

    My current website allows me to assign categories to an article. For example, I assigned this article two categories, "Polyglot" and "Ruby". My current (full size) website is built with the PHP content management system (CMS), Drupal. Drupal dynamically renders content. One of the dynamic views Drupal renders is, a list of articles organized by category.

    My mobile web site is simply a set of static web pages. There are a couple of dynamic features, like the "popup contact boc". Those dynamic features generate document fragments, not full web page documents (like Drupal).

    In addition, what makes sense on a full sized web site, doesn't translate exactly when you move to mobile. On my full size, Drupal website, a category view consists of a list of articles. For each article, the website displays the article, title, and teaser/summary. A teaser/summary is usually the first few paragraphs of an article suffixed with a link to "read more". The "read more" links allows the reader to view the whole article.

    For the mobile website, I want to tweak the user experience (UX). Rather than a list of article "teaser/summaries", I want the mobile website to just display a list of article titles. Each article title is link to a display of the full article. The idea is, I want the reader to see as many article titles as possible when they view a "category". Since we have so much real estate on a mobile device, we limit our category display to article titles. Limiting the display to just title links, allows the reader to peruse the article list with the least amount of scrolling.

    For right now (June 2013), I am just interested in two categories, "JavaScript" and "Polyglot". That means when we get to the implementation details, I can hard-code the two categories. Later in the project (later meaning is future releases), we can make the categories configurable and dynamic.

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=http://public-action.org/category/technology/javascript 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="http://public-action.org/category/technology/javascript 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 - I'm an experienced developer. My main interest is in new technology. Please use our contact box here if you are interested in hiring me. Please no recruiters :)

Comments

Nice write up

Thanks for the article. Nice to see the same task implemented in different languages.