Eleven Qualities of The Perfect Line Notation for the Web 2

Posted by Rich Apodaca Wed, 14 Mar 2007 10:18:00 GMT

If you had to design the perfect line notation for the Web, what would it look like? This is hardly an academic exercise given the central role played by line notations in information systems. For a variety of reasons, existing line notations may not be the right match for the Web. This article explores this question and outlines the main qualities needed by a Web-friendly line notation.

A Few Lines About Line Notations

A line notation is any system that converts a molecular structure into a single line of text. Chemists have been using line notations for over 140 years - long before the advent of computers. Because of their versatility, line notations are frequently used in situations they were not designed for. When this happens, limitations become apparent, resulting in renewed efforts to build a better system.

As noted previously, the invention of new line notations is a field whose popularity ebbs and flows over time. Currently, the three most important line notations are:

  • IUPAC Nomenclature
  • Simplified Molecular Input Line Entry System (SMILES)
  • IUPAC International Chemical Identifier (InChI)

Each of these systems has its own unique characteristics. IUPAC nomenclature is the oldest and most widely-used line notation. It appears in numerous contexts, including Web pages, peer-reviewed journals, reports, patents, MSDS sheets, catalogs, and reagent bottles. By comparison, SMILES is a distant second in popularity. It's main role has been to facilitate machine entry of structural information by humans, like this. InChI is the newest of the bunch. It serves both as a line notation and as a unique identifier requiring no central authority.

The Perfect Line Notation for the Web

The emergence of the Web as a standard information delivery platform has refocused the attention of many developers on the line notation problem. With this idea in mind, here are some guesses about the qualities of the ideal Web-friendly line notation.

  1. Readily Encodable and Decodable by Humans. There's something unnerving about a line notation that can't easily be deciphered by humans. Is this really the right string? Did I copy it completely? This problem surfaces with every line notation, but some fare better than others. IUPAC nomenclature, for example, is one of the first things taught in many beginning organic chemistry classes. It's complicated, but still understandable by non-experts.

  2. Readily Encodable and Decodable by Machines. It may be relatively simple for humans to read and write IUPAC nomenclature, but not so for machines. Software that reads and writes SMILES, on the other hand, is by comparison easy to write. This explains the abundance of software packages that handle SMILES and the scarcity of those that handle IUPAC nomenclature.

  3. Uses URI-Safe Characters Only. A URI uniquely identifies every document on the Internet. Why can't a line notation be used in combination with a URI to uniquely identify every molecule? One reason is that every line notation currently in use contains characters unsafe for use in URIs. Any line notation designed for use on the Web needs to avoid these characters in its syntax. Update: InChI doesn't use unsafe characters, but it does use the reserved characters "=", "?", and "/". These characters may therefore need to be escaped, depending on the context.

  4. Encodes All Molecules. Buried within every line notation is an opinion on what chemistry is really about. To operate on the Web, these opinions need to be as closely aligned as possible with those of chemists themselves. Several Depth-First articles have discussed the limitations of existing line notations as molecular languages.

  5. Compact. Nobody wants to look at or manipulate a line of text that's longer than it needs to be. Of course, the more expressive a line notation is, the more verbose it will be. In other words, qualities 4 and 5 will always be in conflict.

  6. Canonicalizable. A line notation supports canonicalization when it specifies rules that can be guaranteed to always generate the same line notation for a given molecule. This feature enables many labor-saving assumptions. For example, a canonical representation makes a great identifier in a database, reducing the cost of storing and retrieving structural information.

  7. Explicit Hydrogen Atom Encoding. SMILES makes few requirements regarding hydrogen atom encoding. As a result, each software implementation is left to its own devices. The resulting confusion is the price paid for the convenience (Quality 1) of a compact notation (Quality 5).

  8. Hierarchical Structure. One of InChI's innovations was the introduction of a hierarchical encoding system. This system, also referred to as InChI "layers", enables a molecule to be viewed at several levels of resolution: as a molecular formula; as a network of atoms; as a network of atoms containing hydrogen atoms; as an atomic network with stereochemistry; and so on. I'm unaware of any reports in which this feature has been exploited in a practical way, although they aren't difficult to imagine.

  9. Flat Structure. By grouping structural features into layers (Quality 8), InChI introduces a lot of complexity that is absent in SMILES and even IUPAC nomenclature. This complexity, in part, makes it difficult for both humans and machines to properly encode InChIs (Qualities 1 and 2). Given this complexity, and the fact that the utility of hierarchical encoding has yet to be conclusively demonstrated, it may be better to avoid it.

  10. Open Source Software Implementation. No encoding standard in today's world stands a chance of gaining acceptance without an open source reference implementation. InChI broke new ground in this area and should serve as a model for any system that follows.

  11. Unencumbered by Patents. The success of molfile and SMILES as de facto standards derives partly from the decision made by their authors to refrain from patenting their languages. As a result, developers are motivated build their own implementations, rather than invent yet another language.

Conclusions

A robust and modern line notation system is a key technology for chemically enabling the Web. Existing line notations, although useful in many contexts, were not designed with this particular role in mind. The time has come to consider whether a new line notation system, designed specifically with the Web and modern chemistry in mind, might offer a better solution.

Photo credit: Wenwen - Flickr

An Object-Oriented Framework for Molecular Representation: Getting Started with Octet

Posted by Rich Apodaca Tue, 30 Jan 2007 14:45:00 GMT

If applications are hard to design and toolkits are harder, then frameworks are hardest of all. A framework designer gambles that one architecture will work for all applications in the domain. Any substantive change to the framework's design would reduce its benefits considerably, since the framework's main contribution to an application is the architecture it defines. Therefore it's imperative to design the framework to be as flexible and extensible as possible.

-Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides- Design Patterns

One of the most important considerations when building an application is the choice of framework. As the quote from the Gang of Four implies, there's much more to frameworks than just a collection of re-usable code. At their best, frameworks provide a foundation for thinking about a problem domain and a language for communicating with other developers about it. In this article, I'll introduce Octet, an object-oriented framework for molecular representation.

The Molecular Representation Problem

Isn't molecular representation a solved problem? After all, don't SMILES, Molfile, InChI, and CML adequately represent any molecule the average software developer is likely to see?

As previously discussed, molecular representation technologies have stagnated while the molecules chemists themselves now routinely make and use have continued to become more and more "exotic." Developers are now faced with the thorny problem that a variety of common structural motifs in chemistry can't be adequately represented with industry-standard cheminformatics tools.

This point is so important, I'll repeat it: cheminformatics has fallen behind chemistry in the kinds of molecules it can work with. Quick fixes only allow the problem to fester; what's needed is a comprehensive solution. This is Octet's problem domain.

Every framework is bounded by a specific problem domain. Although the size of the domain can vary, a framework provides a comprehensive solution within it. For complex and poorly standardized domains (such as molecular representation), a good framework can greatly accelerate application development.

A good frameworks stays within its problem domain. One of the most important reasons is to prevent featuritis, the root of much software evil. Keeping a framework focused on its core mission makes it much more likely that it can remain documented, tested, extensible, and efficient.

By intention, a variety of features fall outside Octet's problem domain and so will never be directly supported. For example, rendering 2-D structure diagrams is a common problem in cheminformatics that has nothing to do with solving the molecular representation problem. Similarly, reading and writing SMILES strings and Molfiles are supported by many toolkits, but not by Octet directly. After all, it's the inherent limitations of these languages that Octet is trying to overcome.

Higher-level functionality such as legacy language support and 2-D rendering, although not part of Octet itself, can be developed with Octet as a foundation. For example, two Octet add-on frameworks specifically address these problems. They are called Rosetta and Structure, respectively.

About This Series

This article is the first in a series discussing Octet. Future articles will describe in detail Octet's design, implementation, and use. Although Octet has come a long way, it's far from finished. My motivation for writing these articles is to hear what you have to say about Octet, so please feel free to contact me.

Although Octet is written in Java, code examples discussed here will be written in Ruby. I've taken the same approach in discussing the Chemistry Development Kit (CDK) and Structure-CDK. Ruby's brevity and comfortable syntax make it ideal for both writing and discussing code.

Ruby Java Bridge (RJB) is the magic technology that makes this possible. Previous articles have discussed the installation and use of RJB on Windows and Linux.

A Simple Test

Assuming you've installed Ruby, RubyGems and Ruby Java Bridge, you can perform a simple demonstration of Octet in Ruby:

require 'rubygems'
require_gem 'rjb'
require 'rjb'

BasicMoleculeBuilder = Rjb::import('net.sf.octet.builder.BasicMoleculeBuilder')
RepresentationKit = Rjb::import('net.sf.octet.util.RepresentationKit')
MoleculeKit = Rjb::import('net.sf.octet.util.MoleculeKit')
System = Rjb::import('java.lang.System')

builder = BasicMoleculeBuilder.new

RepresentationKit.buildHexane(builder)

molecule = builder.releaseMolecule

MoleculeKit.printMolecule(molecule, System.out)
The above code generates an Octet representation for n-hexane, and prints the representation to the console. To run this example, save the above code to a file called test.rb in your working directory. Then add octet-0.8.2.jar, which can be found in the Octet-0.8.2 source distribution, to the same directory. The test can then be run with the following sequence of commands:
$ export CLASSPATH=./octet-0.8.2.jar
$ ruby test.rb
**Molecule Properties**

Atom Count: 6, Bonding System Count: 5

Atoms:
atom: C[0] (2nu 0e, 0or, 0.0fc, 1bs, 1n, 4.0val, 3ih )
atom: C[1] (2nu 0e, 0or, 0.0fc, 2bs, 2n, 4.0val, 2ih )
atom: C[2] (2nu 0e, 0or, 0.0fc, 2bs, 2n, 4.0val, 2ih )
atom: C[3] (2nu 0e, 0or, 0.0fc, 2bs, 2n, 4.0val, 2ih )
atom: C[4] (2nu 0e, 0or, 0.0fc, 2bs, 2n, 4.0val, 2ih )
atom: C[5] (2nu 0e, 0or, 0.0fc, 1bs, 1n, 4.0val, 3ih )

No non-natural isotopic distributions specified.

No Orbitals specified.

Bonding Systems:
bonding system:  ( 2be, 0abe, 2a, 1ap ) [ (0, 1) ]
bonding system:  ( 2be, 0abe, 2a, 1ap ) [ (1, 2) ]
bonding system:  ( 2be, 0abe, 2a, 1ap ) [ (2, 3) ]
bonding system:  ( 2be, 0abe, 2a, 1ap ) [ (3, 4) ]
bonding system:  ( 2be, 0abe, 2a, 1ap ) [ (4, 5) ]

Atom Pairs:
atom pair: (0, 1) (1.0 bo)
atom pair: (1, 2) (1.0 bo)
atom pair: (2, 3) (1.0 bo)
atom pair: (3, 4) (1.0 bo)
atom pair: (4, 5) (1.0 bo)

No Atomic Configurations specified.
No Conformation specified.

As you can see, Octet shares the same concepts and vocabulary as FlexMol. We'll drill down into the meaning of the output in later articles. The important thing to remember is that we can print out a report like the one above for any Molecule, no matter how complex.

Conclusions

Octet is an object-oriented framework designed to solve the molecular representation problem and serve as a solid foundation for a variety of cheminformatics applications. Of course, there's much more to Octet than the simple example shown here. Future articles will describe in greater detail the design and use of Octet through illustrative examples.

Making the Case: Similarity by Compression

Posted by Rich Apodaca Wed, 13 Dec 2006 14:49:00 GMT

...The structures were converted to SMILES format and canonicalized using a program written with the open-source Java cheminformatics library JOELib2. ... To conclude, we have demonstrated that SMILES strings and compression programs are a simple, yet powerful method for similarity searching, competitive with state-of-the-art-techniques. The Ruby scripts used to carry out the experiments described in this paper are available for download from http://comp.chem.nottingham.ac.uk/download/zippity/.

James Melville, Jenna Riley, and Johathan Hirst, J. Chem Inf. Model.

Yet another appearance of Open Source software in the literature comes by way of a paper from Melville, Riley, and Hirst. This work takes advantage of the alphabet-like nature of SMILES strings and widely-available compression algorithms to perform molecular similarity analyses. Not only does this work use the Open Source JOELib library but the authors have made the Ruby scripts that perform the similarity analysis freely available under the same terms as Ruby (Ruby's license or the GPL).

The times they are a-changein'.

The Problem with Ferrocene

Posted by Rich Apodaca Tue, 12 Dec 2006 14:53:00 GMT


Four different Compound Identifiers. Three different canonical SMILES. Three different InChIs. This is how Ferrocene is represented in PubChem. Even more strangely, none of bonding arrangements accurately reflect the ways most chemists would think about it.

It's not a Good Thing to list the same compound under four different entries in the same molecular database. In the best case it's inconvenient. In the worst case it can cause information that does exist to act as if it does not. I'm guessing, but I would suspect that behind the scenes at PubChem one or more chemical informatics tools are being pushed beyond their area of expertise.

SMILES, InChI, Molfile, and CML are molecular languages that were designed primarily with organic compounds in mind. In this world, bonds occur in neat two-atom units with an even (integer) number of shared electrons. This system falls apart in the world of organometallic chemistry, where multi-atom bonding is commonplace. The same problem also crops up when describing de-localized organic ions and radicals. Multi-atom bonding even rears its head in something as prosaic as the aromaticity of naphthalene and benzene. (To be fair, InChI has less of a problem here than most other molecular languages because of its focus on atoms rather than bonds.)

Is the problem serious enough to do something about it? Forty years ago, metallocenes were a novelty - today they're ubiquitous. They're key components of new materials, catalysts, and perhaps eventually even drugs. They're abundant in every major chemical supplier's catalog. Every respectable journal runs at least one article per issue in which metallocenes play an important role. It seems unlikely that the problem with Ferrocene and its multi-atom bonding cousins can continue to be swept under the rug much longer.

Maybe the problem lies with the deficiencies of the molecular languages currently in use. After all, it seems unlikely that any system can ever become the "universal" language of chemical informatics. On the other hand, the problem may instead arise from these languages, and their limitations, figuring too prominently in the design of the underlying software.

Anatomy of a Cheminformatics Web Application: Beautifying Depict

Posted by Rich Apodaca Mon, 27 Nov 2006 15:05:00 GMT

A recent article outlined the steps for building a Rails Web application that renders SMILES strings as 2-D molecular images. Although this application, Depict, performed its stated purpose, it was neither much to look at nor as easy to use as it could be. In this tutorial, we'll give Depict a face-lift and make it more user-friendly.

The Problem

As it now stands, Depict accepts a SMILES string as input, and then renders a new Web page containing a 2-D molecular image. We'd like to make it easier to enter multiple SMILES strings by combining data entry and image display into the same Web page. We'd also like to make the application as a whole look better by using Cascading Style Sheets and other UI enhancements.

Download and Prerequisites

For this tutorial, you'll need Ruby CDK (RCDK). A recent article described the small amount of system configuration required for RCDK on Linux. Another article showed how to install RCDK on Windows.

In addition, you'll need to install Ruby on Rails - something that can be done through RubyGems.

The Rails application that this tutorial starts with can be downloaded from this link. If you'd rather work with the version of Depict produced by applying the changes outlined in this tutorial, the full source code can be downloaded from this link.

Step 1: Consolidate Actions

Our first version of Depict defined three SmilesController actions: input; depict; and image_for. Because we want to show the molecular image on the same page on which SMILES input happens, we'll consolidate input and depict into a single action.

To do this, we'll edit depict/app/controllers/smiles_controller.rb by removing the input method and rewriting the depict and image_for methods:

require_gem 'rcdk'
require 'rcdk/util'

jrequire 'java.io.ByteArrayOutputStream'
jrequire 'net.sf.structure.cdk.util.ImageKit'
jrequire 'javax.imageio.ImageIO'

class SmilesController < ApplicationController

  # Consolidated depict method.
  def depict
    if params[:smiles]
      @smiles = params[:smiles][:value]
    else
      @smiles = ''
    end
  end

  def image_for
    smiles = params[:smiles]

    # Just return if we can't get a SMILES string.
    if !smiles
      return
    end

    mol = RCDK::Util::Lang.read_smiles smiles
    mol = RCDK::Util::XY::coordinate_molecule mol
    out=Java::Io::ByteArrayOutputStream.new
    image=Net::Sf::Structure::Cdk::Util::ImageKit.createRenderedImage(mol, 300, 300)

    Javax::Imageio::ImageIO.write(image, "png", out)

    send_data(out.toByteArray, :type => "image/png", :disposition => "inline", :filename => "molecule.png")
  end
end

We need to check whether the SMILES in image_for is nil because when the application is first lanched, no SMILES string is defined. By checking for this condition and exiting if found, our application can gracefully start up and respond to a blank input field.

We no longer need a View for the input action, the functionality of which we'll be moving into the View for our new depict action. Delete depict/app/views/smiles/input.rhtml, and edit depict/app/view/smiles/depict.rhtml so that is looks like the following:

<html>
  <head>
    <title>Depict</title>
  </head>
  <body>
    <h1>Depict a SMILES String</h1>
    <img src="<%= url_for :action => "image_for", :smiles => @smiles %>"></img><br />

    <%= form_tag :action=>'depict' %>
      <label>SMILES: </label>
      <%= text_field('smiles', 'value', :value => @smiles) %><br />
    <%= end_form_tag %>
  </body>
</html>

This new template is simply a combination of the two previous templates. Pointing your browser to http://localhost:3000/smiles/depict and entering a valid SMILES string should give a screen similar to the one below:

Step 2: Add a Helper for Serving Images

If a blank or invalid SMILES is entered, we'd like to give feedback by loading an image that reflects this condition. The user is expecting to see an image anyway, so we may as well put our error message there. To do so, we need to first re-think our image_for action.

Currently, image_for tries to generate an image from any string of characters. When it fails, no image is produced, giving rise to the familar "broken image" icon below:

We could add some conditional logic in our view that would detect an invalid or empty SMILES string. However, for several reasons such co-mingling of application code and HTML is generally considered a Bad Thing. Fortunately, Rails offers just what we need: Helpers. A helper is code contained in a module that is automatically included in a view.

Each Rails Controller comes complete with an associated Helper. Our SmilesHelper was already created and wired together for us when we created the SmilesController. All we need to do is to add our own Helper methods.

We're going to add a method called image_for_smiles that will return a URL to an image based on a SMILES string. It needs to handle three possible types of string input:

  • Blank SMILES: Returns a static URL to an image on our server indicating no SMILES string has been entered. We'll discuss where to put this image in Step 5.

  • Invalid SMILES: Returns a static URL to an image on our server indicating an invalid SMILES. We'll add this in Step 5.

  • Valid SMILES: Returns a dynamic URL that will generate a 2-D molecular image on the fly from binary data generated in the same manner as our current image_for action.

Let's add the functionality we need to our SmilesHelper, which is contained in the file depict/app/helpers/smiles_helper.rb:

# Load the RCDK library
require_gem 'rcdk'
require 'rcdk/util'

# New jrequire calls.
jrequire 'java.io.ByteArrayOutputStream'
jrequire 'net.sf.structure.cdk.util.ImageKit'
jrequire 'javax.imageio.ImageIO'

module SmilesHelper
  def image_for_smiles(param)
    smiles = param[:smiles]

    if smiles.eql? ''
      return '/images/blank.png'
    end

    render(smiles)
  end

  def render(smiles)
    begin    
      mol = RCDK::Util::Lang.read_smiles smiles
      mol = RCDK::Util::XY::coordinate_molecule mol
      image=Net::Sf::Structure::Cdk::Util::ImageKit.createRenderedImage(mol, 400, 400)
    rescue
      return '/images/invalid.png'
    end

    out = Java::Io::ByteArrayOutputStream.new

    Javax::Imageio::ImageIO.write(image, "png", out)

    flash[:bytes] = out.toByteArray
    flash[:smiles] = smiles

    url_for :action => 'image_for', :id => smiles
  end
end

Here, we introduce another Rails element - the flash. The flash provides temporary storage for data that needs to be passed from one Action to another. In the render method, we're storing the byte array created by Ruby CDK in the flash so that it can be sent into Depict's image window as dynamically-generated content.

If successful, the render method returns a URL of the form:

http://localhost:3000/smiles/image_for/SMILES

where SMILES is the escaped form of the user-specified SMILES string. If two images are served with exactly the same URL, some browsers (e.g., Konqueror) will assume they represent the same image and will re-use the image in their cache. So, we append the SMILES string to the URL as a way to get these browsers to refresh Depict's image area.

Step 3: Invoke the New image_for_smiles Method

We've added a new image_for_smiles method as a Helper, but Depict isn't yet using it. Let's change that by modifying the way that our image URL is generated in depict/app/views/smiles/depict.rhtml:

<html>
  <head>
    <title>Depict</title>
  </head>
  <body>
    <h1>Depict a SMILES String</h1>
    <img src="<%= image_for_smiles :smiles => @smiles %>"></img><br />

    <%= form_tag :action=>'depict' %>
      <label>SMILES: </label>
      <%= text_field('smiles', 'value', :value => @smiles) %><br />
    <%= end_form_tag %>
  </body>
</html>

Step 4: Simplify SmilesController

We're now no longer using SmilesController (depict/app/controllers/smiles_controller.rb) to perform the bulk of the work related to 2-D image generation. Let's update our Controller to reflect these changes:

# No libraries need to be loaded now.

class SmilesController < ApplicationController
  # Consolidated depict method.
  def depict
    if params[:smiles]
      @smiles = params[:smiles][:value]
    else
      @smiles = ''
    end
  end

  # Consolidated image_for method.
  def image_for
    if flash[:bytes]
      send_data(flash[:bytes], :type => "image/png", :disposition => "inline", :filename => "#{flash[:smiles]}.png")
    end
  end
end

Notice how much simpler the image_for method now is. The byte array saved in Rails' flash (introduced in Step 2) is simply sent out as a PNG image to any receiver requesting it.

Our application, when provided with a valid SMILES string, now looks like the image below.

Step 5: Add Static Images

We'd like to have Depict render an appropriate image in those cases where a molecular image can not be rendered. In fact, Depict is already configured to do so - all we need to do is add the images themselves.

Where do we put these images? Rails creates several directories when an application template is produced. One of these is called public. This directory in turn contains an images subdirectory. Currently, depict/public/images only contains the Rails logo. It is this directory into which static images are designed to go. Let's add these two images to depict/public/images: blank.png and invalid.png. You could, of course, create your own custom 400x400 pixel images for this purpose.

Deleting any SMILES input from Depict now should generate the screen shown below.

Not exactly subtle, but it gets the message across. A similar screen results by entering an invalid SMILES string, such as "hello".

Step 6: Create and Use a Cascading Style Sheet

We'd like to have fine-grained control over the appearance of our application through a single file - a job ideally suited for Cascading Style Sheets (CSS). Where do CSS files live in a Rails application? Along with the images directory described above, Rails also creates a public/stylesheets directory when an application template is generated. This is where custom style sheets can be placed. Create a CSS file called default.css in this directory containing the following definitions:

h1 {
    text-align: center;
    font-size: 30pt;
    background: #993333;
    color: white;
}

.image {
    margin-left: auto;
    margin-right: auto;
    width: 400px;
}

.smiles {
    margin-left: auto;
    margin-right: auto;
    width: 400px;
}

.smiles input {
    width: 100%;
    font-size: 18pt;
    text-align: center;
    border: solid #993333;
    border-width: 2px 2px 2px 2px;
}

.smiles label {
    background: #993333;
    color: white;
    padding: 4px;
    font: sans-serif;
    font-weight: bold;
}

.about {
    text-align: center;
    font-size: 16pt;
}

a:link,  a:visited { color: #930; }
a:hover, a:active {color: #FFFFFF; background: #993333;}

Next, we need to tell Rails where to find the above CSS. Open up depict/app/views/smiles/depict.rhtml and add the following eRuby line inside the head tags:

<%= stylesheet_link_tag "default", :media => "all" %>

That's all there is to it. Reloading Depict should give a screen similar to the one below.

Step 7: Clean Up the View

You may have noticed that the style sheet added in the previous step defines some features we're not currently using. Let's update Depict's View (depict/app/views/depict.rhtml) to reflect the changes to our CSS:

<html>
  <head>
    <title>Depict</title>
    <%= stylesheet_link_tag "default", :media => "all" %>
  </head>
  <body>
    <h1>Depict a SMILES String</h1>
    <div class="image">
      <img src="<%= image_for_smiles :smiles => @smiles %>"></img>
    </div>
    <br /><br />
      <div class="smiles">
      <%= form_tag :action=>'depict' %>
        <label>SMILES: </label>
        <%= text_field('smiles', 'value', :align=>'center', :value => @smiles) %><br />
      <%= end_form_tag %>
      </div>
  </body>

  <div class="about">
    <a href="http://depth-first.com/articles/2006/11/27/anatomy-of-a-cheminformatics-web-application-beautifying-depict">About this Application</a>
  </div>
</html>

The changes here consist of grouping related HTML elements together into div blocks and adding a link to the article you're reading at the bottom of the article. The interaction of the above code and the style sheet we created in Step 6 produces a screen, such as the one below, when a valid SMILES string is entered.

Summary

Even if you haven't followed along through this tutorial, it should be apparent that Rails is a powerful tool for the agile development of Web applications. Although we haven't used any sophisticated techniques, we now have a working Depict server with a simple, logical Web interface that does something useful.

But we're not quite done with Depict yet. Currently, you need to hit the return key to get a 2-D rendering. Wouldn't it be better if the application automatically updated the image as a SMILES string is typed? If you're thinking "Ajax", you're right on target.

Older posts: 1 2 3 4 5 6