RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


10 Minutes to Your First Ruby Application : Page 4

There's no better way to experience the elegance and power of Ruby than to fire up your code editor and start writing Ruby code. Create a small, useful Ruby application, and along the way, you'll learn what makes the language tick.

Writing a Handler Class
Now that you have a simple framework for routing file names to Ruby code, create a handler class for HTML files. The class needs to implement a run method that accepts at least one argument for the target file name, and an optional array of additional parameters. The class name must be Html in a file named html.rb, and placed in a handlers subdirectory:

class Html

  DEFAULT_BROWSER = 'firefox'

  def run file, args
    if args.empty?
      system( "#{DEFAULT_BROWSER} #{file}" ) 
      dispatch_on_parameters file, args

  def dispatch_on_parameters file, args
    cmd = args.shift
    send( "do_#{cmd}", file, args )

  def do_opera file, args=nil
    system( "opera #{file}  #{args}" )

  def do_konq file, args=nil
    system( "konqueror #{file}  #{args}" )

The code defines a constant for a default browser. In the absence of any extra arguments, then, you can have the target file launched in Firefox. (Note that you may have to change this so that it defines an executable command. On my Ubuntu machine I can run firefox with no explicit path and have a browser come up. On Windows, for example, the full path to the executable may be needed.)

If there are additional arguments, run calls out to dispatch_on_parameters, which extracts the first item from the args array and uses it to dynamically construct a message string. The send method is built in to all Ruby objects. It allows you to explicitly send a message to an object. When used by itself (as you are doing here), the receiver object is assumed to be the current object. So the code is sending a message to itself.

You prepend do_ to the actual argument value as a safeguard against method name collision. (For example, if the first argument were exit, you probably would not want to invoke Ruby's exit method. You'd call do_exit, which would then decide what the correct behavior should be).

This handler code has some fairly trivial examples of possible parameter handling. As is, you can launch a target HTML file in either some default browser or specify a particular browser:

$ ./go index.html opera
$ ./go index.html konq

A Little Overtime for Coolness
You've received an educational and practical example, but can you push things a little further? Of course you can. Mind you, this will take you past the 10-minute mark, but it should be worth it.

The standard Ruby distribution includes a wealth of libraries for all sorts of tasks. One of the most interesting is REXML, an XML parser written in pure Ruby. Developer Sean Russell wrote REXML to allow the manipulation of XML using a Ruby-style API rather than the usual W3C DOM API. Before too long, Sean's work became part of the Ruby standard library.

For the sake of simplicity, your HTML files in this example must use XHTML because REXML handles only XML. (There are very good Ruby tools for processing near-arbitrary HTML, one being Hpricot. However, they require installing additional libraries, the explanation of which is beyond the scope of this article.) Trusting that you are working with well-formed XHTML source, you can have your HTML handler do some file analysis. Add this code to the end of your Html class and you'll be able to run some simple reports on your XHTML:

  def do_report( file, args=nil )
    require 'rexml/document'
      dom = REXML::Document.new( IO.read( file ) )
      if args.empty?
        puts basic_xhtml_report( dom )
        puts report_on( dom, args.first )
    rescue Exception
      warn "There was a problem reading '#{file}':\n#{$!}" 

  def report_on dom, element
    els =   dom.root.elements.to_a( "//#{element}" )
    "The document has #{els.size} '#{element}' elements" 

  def basic_xhtml_report( dom ) 
    report = []
    css = dom.root.elements.to_a( '//link[@rel="stylesheet"]' )
    unless css.empty?
      report << "The file references #{css.size} stylesheets" 
      css.each do |el|
        file_name = el.attributes['href']
        file_name.gsub!( /^\//, '')
        unless File.exist?(file_name)
          report << "*** Cannot find stylesheet file '#{file_name}'" 

    js = dom.root.elements.to_a( '//script' )
    unless js.empty?
      report << "The file references #{js.size} JavaScript files" 
      js.each do |el|
        file_name = el.attributes['src']
        file_name.gsub!( /^\//, '')
        unless File.exist?(file_name)
          report << "*** Cannot find JavaScript file '#{file_name}'" 

    report.join( "\n" )

There's a lot going on here, but key method is do_report. The code creates a REXML Document object and assigns it to dom. If there are no extra arguments, you get back a basic report. Otherwise, the code does some cursory examination of a particular element.

The report_on method takes a document argument and an element name, and uses REXML's XPath features to find out how often that element is used. Although it's rudimentary, it certainly can serve as a demonstration and starting point for you to keep hacking.

The basic_xhtml_report method is similar, but focuses on a particular set of elements. It uses REXML to find all the CSS and JavaScript references, and then uses the File class to check that the referenced files exist. Again, not deep, but adding additional logic makes for a nice project.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date