The Artima Developer Community
Ruby Code & Style | Discuss | Print | Email | First Page | Previous | Next
Sponsored Link

Ruby Code & Style
Modular Architectures with Ruby
by Jack Herrington
October 10, 2005

Page 1 of 4  >>

Advertisement

Summary
Any reasonably complex end-user application is going to require some sort of customization and enhancement for effective deployment. This article shows one way to create a modular architecture as a way of leaving the door open for advanced users or consultants who want to extend the functionality without modifying the source.

End-user applications often require some customization and enhancement for effective deployment. A modular architecture is one where the user can create modules that conform to well-described APIs and plug them into the application to extend the functionality. It’s a way of leaving the door open for advanced users or consultants who want to extend the functionality without modifying the source.

One example of a popular modular application is the Apache web server[0]. Apache defines a set of processing steps in building a web page and allows programmers to write modules that may hook into one or more of these steps. Another example is the JavaDoc[1] comment processing system for Java. JavaDoc has a flexible Doclet back end. The basic Doclet produces HTML help files. But the interface has also been used in wide variety of applications including the popular XDoclet code generator.

Perhaps the best example of a modular API is Eclipse[2]. Eclipse is really just a modular framework that handles sets of interlocking modules that build IDEs, thick client applications, even portable device applications. If you want a reference work for how modular APIs are done, check out Eclipse.

I find that there are design smells that suggest when a modular architecture would be a good solution. Some of these are:

Builders
Any time the Builder design pattern is followed the builders could be implemented using modular architecture. The builder pattern has the code that builds some output using a builder object to do the actual construction work. It’s often used for building portable UIs where one builder can build text while another builds HTML, and so on.
Adapters
When systems interface with each other there is always a slot for a modular architecture. If modules are used then the adapter interface can be used to connect to one of many services as opposed to one particular service.
Math libraries and functions
Graphing and spreadsheet applications can extend their function libraries using a modular architecture.
Processing strategies
As with Apache, any time you have a complicated transaction it’s possible to use a modular architecture. This allows the user to define exactly how a transaction is processed. This can work for business logic at almost any level; for example, the validation of a customer record, or the sending out of notifications.
Graphic objects and filters
Applications like Photoshop and The GIMP extend their graphics system using modular APIs so people can create custom commands, graphic objects, and effects.

For the article I’m going to create a simple modular system for reading subscription sources, such as RSS, RDF, and Atom. One can then extend the system to handle new subscription formats in the field without having to change the main code.

Specin’ Out the API

Instead of starting with a complete example I’ll work through building a modular interface just like I did in practice. That starts with some simple test code and a small set of parsers. In fact, I don’t even break out the modules to start with. I start with everything in just two files just to make sure the API is right, then move to a modular architecture so that I’m not trying to solve multiple problems simultaneously.

Here is the test code. It creates a new RSS parser and then gets the types of feeds that it will handle. It also iterates through all of the available parsers and prints them out.

require "parse_mods.rb"

# Create new factory and instantiate a new parser
a = RSSParser.new
print "Building an RSS parser:\n"
p a.get_type()
print "\n"

# Iterate through all of the available types
print "Available parser types:\n"
Parser.parsers.each { |parser_class|
  p parser_class
}

Here is what it looks like when I run it:

% ruby test.rb
Building an RSS parser:
"RSS"

Available parser types:
RSSParser
RDFParser
%

And here is the code for the parsers.

class Parser
  @@parsers = []

  def get_type()
    return ""
  end
  def parse( xml )
    return nil
  end

  def Parser.add_parser( p )
    @@parsers.push( p )
  end
  def Parser.parsers()
    return @@parsers
  end
end

class RSSParser < Parser
  def get_type()
    return "RSS"
  end
  def parse( xml )
    # Parse the XML up and return some known format
    return nil
  end
end

Parser.add_parser( RSSParser )

class RDFParser < Parser
  def get_type()
    return "RDF"
  end
  def parse( xml )
    # Parse the XML up and return some known format
    return nil
  end
end

Parser.add_parser( RDFParser )

There are two parsers that descend from the base Parser class. One parser handles RSS and the other handles RDF. Actually, they don’t handle anything at the moment, but I’ll fix that by the end of the article.

The base Parser class acts as both an interface for all of the descendant parsers, as well as a repository for the list of all parsers. In addition each type of parsers adds itself to the list of all parsers.

In UML the system looks like Figure 1 so far.

Figure 1: The first pass at the parsers.
Figure 1. The first pass at the parsers

The test code is contained in test.rb, and the parsers in parser_mods.rb. The two parsers derive from the base class Parser.

Page 1 of 4  >>

Ruby Code & Style | Discuss | Print | Email | First Page | Previous | Next

Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us