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


A Holiday Gantry Web Application

by Phil Crow
06/14/2007

Nearly 10 years ago, a small Internet and cable service provider in Kansas began serving applications via the Web. Almost two years ago, the third incarnation of our Perl-based web framework was released as an open source project called Gantry (under the same license as Perl). While Gantry is easy to use, we felt the need for even more laziness. Thus, we also developed Bigtop to generate code, SQL, configuration files, and other things for Gantry applications.

Bigtop is not only a code generator, it is a regenerator. As you'll see in the short story below, even after you have a running application, you can still use Bigtop to regenerate parts of the application. There is no risk of losing custom code you've written.

With Bigtop and Gantry, the core of a simple app can be ready in a few minutes. The start of a complex app can be generated in a short time as well. Then, the inevitable changes requested by users and developers can be incorporated into the application by regeneration. Only custom code needs to be updated manually.

To show the power of our tools, I've written a short story. Though it is set around Christmas time, I hope you'll find its meaning is timeless.

A Story

'Twas the Friday before Christmas, and all through the office, not a creature was stirring, except for my boss. At the door to the bat cave he made a request, "My wife needs an app to keep up with grandkids. Think about that while I order our lunch. After lunch, when you're done, you can leave for vacation."

From the corner of my ear I heard the boss order pizza from a place close at hand. Visions of pizza slices danced not in my head, but rather of the app, which I built in a flash.

"It should be here in half an hour," the boss said as he sat.

"You'll have to dine alone. I'm done."

"Now, that can't be."

Despite my protests, the boss wouldn't believe. When I showed him the app, he accused me of thievery. So, I replied, "I'll start again." And this is what he saw.

I typed at my keyboard, fingers in a whirl:

    bigtop --new GrandKids 'child->family
    child(name,birth_day:date)
    family(parent,address,city,state,zip)'

This created a new two table application by the name of "GrandKids." The first table called child, having a foreign key to the second called family. Children keeping track of name and birth date. Families storing addresses for the boss to send toys.

When Bigtop finished, it told me on my screen:

 I have generated your "GrandKids" application.  To run the application:
     cd GrandKids
     sqlite app.db < docs/schema.sqlite
     ./app.server [ port ]
 The app.server runs on port 8080 by default.
 Once the app.server starts, it will print a list of the URLs it can serve.
 Point your browser to one of those and enjoy.
 If you prefer to run the app with Postgres or MySQL type one of these:
     bigtop --pg_help
     bigtop --mysql_help

So, I followed instructions to the GrandKids directory and built my db with SQLite3:

    sqlite3 app.db < docs/schema.sqlite

Then started the app with a bit of a flare:

    ./app.server

Which gave me this list of where I could go:

    Available urls:
       http://localhost:8080/
       http://localhost:8080/child
       http://localhost:8080/family

I chose family.

The book

Clicking Add, I put in some data to save. Then chose Child from navigation links and added one kid.

The book

Having named one child, entered the date of her birth, and picked her parents from the pull down; I saved triumphantly and looked to the boss.

"Why," was all he could spout.

I rose from my chair to retrieve my long coat, but was stopped short by the boss fearing lunch all alone. "What about gifts?"

"Gifts?" I asked unsure.

"Yes, Mrs. Boss has long feared double gifting a child, after the trauma of giving one girl a 'My Little Pony Castle' two years in a row."

With coat on my lap I sat down once again, stopped app.server and typed:

    bigtop --add docs/grandkids.bigtop 'gift(name,occassion,year:int4)->child'

In the new gift table I asked for a gift 'name,' its 'occasion' and 'year,' the later an 'int4.'

To amend my db, I asked to see:

    cat docs/schema.sqlite
    
CREATE TABLE gift ( 
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name varchar,
  description varchar,
  created datetime,
  modified datetime,        
  child INTEGER,
  occasion varchar,
  year INTEGER
);

Which I fed in by click of my wonderful mouse:

    sqlite3 app.db
    sqlite> CREATE TABLE gift (
       ...> ...
    sqlite> .q

Then restarted the server to show off my change:

    ./app.server

At that smiling moment our pizza appeared. So, I joined in the feast as we talked of grandkids. With the slices all gone and the box in the trash, the boss said, "It still needs reports, which can wait 'til next year. Head on home, with great thanks from me and my wife."

I fled with no need to twice be excused. The boss heard faintly as I flew from his sight, "Merry Christmas to all and too all a good afternoon."

Summary

Bigtop is a language for describing web applications. While you can follow the approach of the story above to add tables, you can do much more by editing the Bigtop file. You could do that with a text editor. That is not always easy enough, so we wrote tentmaker.

Tentmaker is a Bigtop source file editor delivered via a web browser like Firefox or Safari. With it you can safely edit the Bigtop file, without having to worry about typing the syntax correctly. Now, let me explain what Bigtop built for my little story above.

The code generated during the story is a complete CRUD only application for managing the family, child, and gift tables. The base URLs listed by app.server show lists of all the items in the tables. From those pages, you can add more items, as shown in the story. Each row listed also has edit and delete links.

If you want variations on basic CRUD, you can get them using tentmaker. This would include things like: removing the delete links, adjusting which columns appear in the main listings, controlling which fields appear on input forms, how those field are validated, etc.

For more sophisticated changes, you might have to write some code. The next section gives a quick tour of the generated files and shows where to put modifications.

Touring generated files

Gantry and Bigtop may be installed from CPAN. Once you install them (and their dependencies, of course) you can follow the example from the short story. If you do that, these are the files and subdirectories you will see in your build directory:

    Build.PL      MANIFEST.SKIP app.db        html
    Changes       README        app.server    lib
    MANIFEST      app.cgi       docs          t

Some of these are familiar to anyone who has built a distribution for CPAN: Changes, MANIFEST, MANIFEST.SKIP, and README. Build.PL is the Module::Build replacement for Makefile.PL. The t directory has the tests. The generated tests check compilation of each module in the application, POD and POD coverage, and perform a hit on the default page of each controller. We'll see the controllers shortly.

The html directory has a templates subdirectory which contains a Template Toolkit wrapper called genwrapper.tt. It has basic styling, and is meant for development use. The rest of the templates you need for a CRUD application come with Gantry. We use them, as they ship, in our production environment, albeit with different stylesheets.

Both app.cgi and app.server are Perl scripts. In the story, the narrator used app.server for his development server. It is based on HTTP::Server::Simple, so it is persistent and fast, but not meant for production use. The app.cgi script could be used in a CGI environment, so long as you place the SQLite database carefully (where your web server can write to it and to its directory). Alternatively, you could change to another database, like PostgreSQL or MySQL, by changing the connection string in app.cgi. As we're about to see, Bigtop has already made the SQL statements for those two databases. Switching to mod_perl is not difficult, but is beyond our scope here.

In the docs directory there are four files. One of those is the Bigtop file, which describes the whole application in the Bigtop language. In the story, the narrator worked on this file exclusively through the Bigtop script. That is not usually enough. Normally, you need to edit it with tentmaker or your favorite text editor.

The other three files in the docs directory hold the SQL commands, which can be fed directly to the command line tools for MySQL, PostgreSQL, and SQLite. All of these are kept up to date by Bigtop as the model changes.

That brings us to the lib directory, where the code lives. Gantry generally operates in MVC fashion (though it doesn't force that fashion on you). So the code modules are of two types: models and controllers. Views are Template Toolkit templates, not code modules.

In addition to dividing models and controllers, Bigtop introduces another dichotomy: generated and stub. Stub modules are made once, either when the app is initially built or when you regenerate after adding to the Bigtop description. Generated modules are overwritten each time you regenerate the application, by running bigtop. This distinction gives you a safe place to put custom code, without having to worry about whether it will be overwritten. There is no way to get Bigtop to overwrite a stub, short of deleting or renaming it.

Now that you know what to expect, here is a listing of the two files and one directory in the lib directory:

    GENGrandKids.pm    GrandKids       GrandKids.pm

Files which include GEN in their name or path are generated and will be regenerated (there are ways to turn off generation, but they usually make life more difficult in the end). So the stub for the base module of our app is GrandKids.pm, while the generated module is GENGrandKids.pm. The rest of the modules are in the GrandKids subdirectory, as usual.

Let's descend into the GrandKids directory to see the more interesting code modules. In it there are again files and directories:

    Child.pm    Family.pm   GEN         GENModel.pm Model       Model.pm

The modules here are stubs. There is not much useful code in them. Their main purpose is to provide a place for custom code. As a fundamental principle, stubs inherit from GEN modules whenever possible. This allows you to override GEN methods at will, simply by coding a replacement in the stub. You can use SUPER to call the GEN version, then modify its effects as needed. There is even a utility module to help you modify forms made by GEN modules.

The interesting code is in the GEN modules. Which are in the GEN directory. Listing its contents we see:

    Child.pm   Family.pm

Even though the code in these generated modules is more interesting, and does the real work for CRUD, editing them is a mistake. Bigtop controls everything in them, so editing the Bigtop file and regenerating is the right choice.

The Model directory is less interesting, but not less useful. It contains stub and GEN modules for each table in the data model. These all descend from DBIx::Class.

That's what you get from a few minutes work with Bigtop at the command line.

References

Gantry and Bigtop have a home on the web: http://usegantry.org. Go there to join in the fun of our mailing list, watch screen casts of example sessions like the one in the short story above, check out the latest sources, and much more.

There is now a Gantry book available from lulu.com: http://www.lulu.com/philcrow. The code from the book is available from usegantry.org, which also has another link to the book on lulu.com.

Phil Crow


Return to ONLamp.com.

Copyright © 2009 O'Reilly Media, Inc.