ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Introducing TrimPath Junction

by Jack Herrington and Steve Yen
08/30/2007

The arrival of technologies like Google's Gears and Adobe's AIR marks a shift in how we view the relationship between the web browser and the web server. Now we can develop complete applications in JavaScript on the client, hold the data on the client, and then synchronize the data, and even the code, later with the server as needed.

To match this changing landscape, we need a fresh new look at the toolset we are using. Steven Yen has done that with his amazing TrimPath Junction framework. Junction is an all JavaScript framework that closely models the Ruby on Rails model-view-controller design pattern and implementation. And with the help of the Helma JavaScript web server, it runs the same code both on the client and on the server. Exactly the same code, in fact.

The framework not only handles the basics of rendering pages (using JavaScript templating), but it also handles data and code synchronization with the server, local client caching using Google Gears, model versioning, and much more. It's an amazingly complete solution for an entirely new model of web development.

Installation

To show it off, we are going to build a simple contact maintenance application. And to get started with the development, we first need to download the example code, which includes the TrimPath Junction framework, and install that somewhere locally.

Then, depending on the operating system, you can start the Helma web server using the start.bat batch file (on Windows), or the shell script on Linux or Macintosh OS X.

You may need to do some tweaking of the Helma configuration file to get it properly working on your system, depending on what web servers you may already have running. The Helma configuration file is called helma.conf and is located in the scripts directory.

Running the Contact Application

Once the Helma process is up and running, we can run the contact application. On my machine, this meant going to http://localhost:8080/engines/100/apps/contactApp. But your URL may vary depending on the settings in the helma.conf file. The above URL executes the contact application within the Helma server. So, each click I make in the contactApp sends a normal, full HTTP request to the Helma server, and the browser renders the returned HTML page as you'd expect.

Instead, if I wanted to run the contact application completely within my web browser, I would use the URL of http://localhost:8080/engines/100/apps/contactApp;start. That lets the application execute against my browser-local Gears RDBMS, or against a browser-local, memory-only database, if I don't have Gears installed. Since the contact application is all JavaScript, it runs just fine completely within the web browser.

In either case, when I use either URL, I would see the page as shown in Figure 1, if everything is installed properly.

The empty contact list page
Figure 1. The empty contact list page

This page shows the list of contacts in the database. It's empty now because there are no contacts in the database. But there is a handy link to get us to the page on which we can create.

If you are familiar with Rails, then this is the equivalent of the index page from the scaffolding. In fact, as we will soon see, it's the Junction version of the scaffolding that we will be working with.

The Schema

But let's backtrack for a second and look at the application. The contact application starts with a model. This model is a migration, as shown in Listing 1.

    { up: function() {
        createStandardTable('Contact', 
          column('first_name',     'varchar(100)'),
          column('last_name',      'varchar(100)'),
          column('address',        'varchar(300)'),
          column('phone',          'varchar(100)')
        );
      },
      down: function() {
        dropTable('Contact');
      }
    }

Just like Rails, there is a function for migrating up to this version, which creates a new table with the fields for each row. And there is a down function, which in this case just deletes the table.

Besides the variable types, which in this case are really standard, there is nothing vendor- or database-specific about the schema--which is great because this way Junction can take this schema and use it on the server side to hold it in a SQLite database, or on the client side in Google Gears' SQLite database.

The Home Controller

With the schema in hand, it's time to create the home controller. This is the landing page for the application. But in this case the home controller only has to forward us onto the index page of the contact controller. This home controller is shown in Listing 2.

    HomeController = function() {}

    HomeController.prototype.start = function(req, res) {
        res.redirectTo('contact', 'index');
    }

A controller, in Junction parlance, is just another object. It responds to many different messages--in this case, the start message. And the start message redirects the client to the index method on the contact controller.

The TrimPath Junction download comes with its own autogenerated documentation that you can use to aid you in developing applications for it.

The Contacts Controller

Amazingly, there is even less to the contacts controller than there was to the home controller. In fact, there is almost no code at all--just an invocation of the scaffold function, which sets up the basic create, read, update, and delete (CRUD) set of pages.

The contacts controller code is shown in Listing 3.

    ContactController = function() {}

    scaffold(ContactController, 'Contact');

Pretty crazy, huh? Well, there is a little more. Since this is just the controller, there also has to be a model and a view to go along with it.

The model is pretty simple, too. It's defined in the contact.js file shown in Listing 4.

    Contact = function() {}

    with (modelFor('Contact')) {
        validatesPresenceOf('first_name');
        validatesPresenceOf('last_name');
    }

In this case, Steven has ensured that the first and last names are present when the user adds or updates the contact record. Still going along the lines of Ruby on Rails, the validatesPresenceOf function names are identical (with the exception of the capitalization) to their Ruby counterparts.

Well, now we have our controller and our model--the next thing is the view. The view comes in several pieces. The first is the list of contacts, or the index page. That's defined in the index.est file.

    <h1>Contacts</h1>

    <%= linkToLocal('Create A New Contact', 'contact', 'newInstance') %>

    <ul>
      <% for (var i = 0; i < contacts.length; i++) { %>
        <li><%= linkToLocal(contacts[i].last_name + ', ' + contacts[i].first_name,
            'contact', 'show', contacts[i].id) %></li>
      <% } %>
    </ul>

While it certainly looks like an ERb template from Rails, it is in fact an EcmaScript Template (EST) that uses Erik Arvidsson's EcmaScript Template engine syntax. You can also use the alternative JavaScript Template (JST) syntax, if you prefer, for a syntax that looks more like Velocity or FreeMarker markup.

Looking back at the template, we can see at the top an invocation to linkToLocal, which sets up an anchor to the page newInstance action on the contacts controller, as well as a for loop, which loops through the contacts already in the database to display them.

Of course, since the database is empty we currently don't see them. So, let's fix that.

Adding a Contact

The first thing to do is to go back to the web browser and click on the "Create A New Contact" link to create a new contact. That takes us to the page shown in Figure 2.

The new instance page
Figure 2. The new instance page

This page is created from the newInstance.est template, which is used by the scaffolding in the contacts controller. The EST code for the page is shown in Listing 6.

    <h1>Create A New Contact</h1>
    <%= errorMessagesFor('contact') %>
    <%= startFormTagLocal('contact', 'create') %>
      <label>First Name:</label>
      <%= textField('contact', 'first_name') %>
        <br/>
      <label>Last Name:</label>
      <%= textField('contact', 'last_name') %>
        <br/>
      <label>Address:</label>
      <%= textArea('contact', 'address') %>
        <br/>
      <label>Phone:</label>
      <%= textField('contact', 'phone') %>
        <br/>
      <%= submitButtonLocal("ok", "OK") %>
      <%= linkToLocal('Cancel', 'contact', 'index') %>
    <%= endFormTag() %>

Again I think it's remarkable how closely TrimPath Junction models the Ruby on Rails framework, especially in a case like this. Just as with Rails, the code starts by putting in an error display section at the top of the page that displays any validation errors on the model. After that, we start a form tag and add fields for each of the items. Then at the bottom, there is a submit button and a link back to the main index page, as well as the end of the form tag.

It's all very easy-to-understand code, and that makes it very easy to maintain. Now, back to the web browser again, I'm going to add some valid, albeit fake, contact information and click on the OK button.

The Show Page

Adding the contact to the database brings up the show page, where we see the specified contact. This is shown in Figure 3.

The show page showing my fake contact
Figure 3. The show page showing my fake contact

This is just like the creation page, with the exception that we aren't creating a contact, we are just viewing it. The template code is shown in Listing 7.

    <h1>Contact <%= contact.id %></h1>
      <label>First Name:</label>
      <%= contact.first_name %>
        <br/>
      <label>Last Name:</label>
      <%= contact.last_name %>
        <br/>
      <label>Address:</label>
      <%= contact.address %>
        <br/>
      <label>Phone:</label>
      <%= contact.phone %>
        <br/>
      <%= linkToLocal('Edit', 'contact', 'edit', contact.id) %>
      or
      <%= linkToLocal('Remove', 'contact', 'destroy', contact.id, { confirm: true }) %>
    <br/><br/>
      Back to the <%= linkToLocal('Contacts List', 'contact', 'index') %>

The last step in my walkthrough is to go back to the index page to show that the item has been accurately added to the database. Let's have a look in Figure 4.

The index page with the new contact
Figure 4. The index page with the new contact

Indeed it has, and if I close the browser and re-open it to the same URL, I'll see the contact.

One thing that I did leave out was the basic template for all pages, which is also implemented in a very Rails-like way. It's called default.est and is shown in Listing 8.

    <html>
    <head>
      <title>Contact Manager App</title>
      <script type="text/javascript" src="/javascripts/trimpath/junctionUtil.js"></script>
    </head>
    <body>
      <%= contentForLayout %>
</body>
    </html>

Just as with Rails, the content for the page is specified within whatever bounding layout you want. All you have to do is ensure that the base TrimPath Junction library is included, as it is with the script tag in the head section of the page.

Conclusion

This little demonstration of TrimPath Junction just scratches the surface of its power. Just as with a Rails demo, using the scaffolding only shows a portion of its potential. For an in-depth view, you can check out the Next Action demo application, which is a very powerful to-do list maintainer. That application works well both online and offline through use of Google Gears.

Since it requires a specific web server solution, TrimPath Junction might not be appropriate for every web application or developer. But it does certainly show the potential of what can be done with JavaScript, both on the client and server side. And it is a fascinating glimpse into the potential offered by disruptive technologies like Google's Gears and Adobe's AIR platform.

Resources

Jack Herrington is an engineer, author and presenter who lives and works in the Bay Area. His mission is to expose his fellow engineers to new technologies. That covers a broad spectrum, from demonstrating programs that write other programs in the book Code Generation in Action. Providing techniques for building customer centered web sites in PHP Hacks. All the way writing a how-to on audio blogging called Podcasting Hacks.


Return to ONLamp.

Copyright © 2009 O'Reilly Media, Inc.