The Artima Developer Community
Sponsored Link

Thinking Upside Down
Ruby vs Python for Code Generation and Flippin' Templates
by Andy Dent
February 15, 2008
Summary
Ruby is the cool new kid of on the block and has some significant cred in code-generation circles. Python has whitespace sensitivities that make it a poorer fit for templating solutions. This sounds like a no-brainer decision but is the real argument about which language the market wants to see used?

Advertisement

With my return to running my own business as a full-time activity and freedom to indulge entrepeneurial instincts, one of the projects I'll be putting more time into is AppMaker. AppMaker is a long-standing Mac product for application generation to which I acquired the rights when the original author, Spec Bowers, retired. Progress has been stalled for much of the time of OS/X with Apple's closed attitude on Cocoa - a contributing factor to Spec's retirement.

Given the length of time that has elapsed since there was a new release of AppMaker and the fact that very few users seemed to customise templates significantly, I feel some freedom to vary the template approach. For a start, I'm planning to replace the somewhat-idiosyncratic OO language with Python or Ruby. There's no justification for maintaining a complex language, with an old code base, when the world has moved on to create outstanding scripting and text manipulation languages. The startup cost for any new AppMaker user wanting to customise their templates will be drastically reduced if they are based on a standard language.

I was very impressed with Jack Herrington's Code Generation in Action which includes a taxonomy of code generators, and Ruby examples. His book, and the growth of Ruby in the somewhat code-generationy Rails framework, have put me in the position of having to choose - would you use Ruby or Python for a code generator language?

The AppMaker generators best fit what he calls a Partial Class Generator where a definition file (the AppMaker Project) is combined with a set of templates (the selected language/framework) to generate the code. The previous market for the product included a strong educational segment as the code generated was deliberately made representative of best practice for a given framework.

Tagged vs Mode-Flipping Templates

Many people would be familiar with JSP templates as an example of the Partial Class Generator approach. However, the AppMaker templates have somewhat of a twist and I'm trying to decide if it is a style worth preserving. Here's a reminder of the traditional bounded template that Herrington shows or JSP conditional logic:

Herrington Example Template

public class <%= class_name %>Base {       
<% fields.each { |field| %>                              
  protected String _<%= field %>;<% } %>                 
  public <%= class_name %>Base()                         
  { <% fields.each { |field| %>                          
    _<%= field %> = new String();<% } %>                 
  }                                                      
<% fields.each { |field| %>                              
  public String get<%= field.capitalize %>() { return _<%= field %>; } 
  public void set<%= field.capitalize %>( String value ) { _<%= field %> = 
value; } 
<% } %> 
} 

JSP style Templates

 <% if ("someValue".equals(request.getParameter("param1"))) { %>
        Generate this template text if param1 equals someValue
    <% } else { %>
        Otherwise generate this template text
    <% } %>

AppMaker's template language

The AppMaker proprietary template language flips between a literal text state and a code state, using the percentage sign as the escape character to flip states. This results in templates that have less noise and probably would cause less confusion than using angle-bracketed tags like JSP. Of course, they are no longer viewable by a simple HTML browser which can ignore the JSP tags but the domains we're targetting for such generation are more likely to be generating traditional source code into multiple files such as shown (with elisions...) below.

 procedure Window.genWindow
   import   appName: string
   declare cmdList:  list of Command
   getViewCommands (/* cmdList */)
   declare dlgList:  list of Dialog
   getDialogList (/* cmdList, dlgList */)
   import   firstWindow:   bool  // param
   declare  windName:      string = window.name
...
   declare  hdrFile:    file
   filename = windName + ".h"
   hdrFile.create (name = filename/*, creator, fileType*/)
   hdrFile.open ()
   hdrFile.genText ()
%
// %filename%

#pragma once

#include "AMWindow.h"
%genHdrItemInclude ()%

class %dataName%;

class %WindName% : public AMWindow {
public:
         %WindName% (
               AMAEObject*    inSuperObject);
   virtual ~%WindName% ();

public:
   static void    Create      (AMDoc*        inDoc,
                         %dataName%*      inData);

public:
   virtual void   Open     (AMDoc*        inDoc);
   virtual void   Close ();
...
%
for each cmd in cmdList
   cmd.declareDoCommand ()
end for
%
protected:
   virtual void   ActivateData ();
   %for each WindowItem
      windowItem.genAllAuxWinDecl ()
   end for%

public:
   %dataName%*    mData;

%for each windowItem%
   %windowItem.genAllWindowField ()%
%end for%
};
%
   hdrFile.close ()

//-----
   declare  srcFile: file

   filename = windName + ".cp"
   srcFile.create (name = filename/*, creator, fileType*/)
   srcFile.open ()
   srcFile.genText ()
%
// %filename%

#include "%windname%.h"

#include <Types.h>
...
%
for each dlg in dlgList
   //declare   itsClassName:  string
   dlg.getClassName ()
%
#include "%itsClassName%.h"
%
end for
%
%genIncludeData ()%
%genSrcItemInclude ()%
%genCommandIncludes (/* cmdList */)%

%If lang = "MPW"%
   #pragma segment %windname%

%end if%

void  %WindName%::Create (
   AMDoc*         inDoc,
   %dataName%*    inData)
{
   %WindName%*    winObj = new %WindName% (inDoc);

   if (winObj != nil) {
      winObj->Open (inDoc);
      winObj->ConnectToData (inData);
      ShowWindow (winObj->mWindow);
   }
}

%WindName%::%WindName% (
   AMAEObject*    inSuperObject)
   : AMWindow (inSuperObject)
{
   mData = nil;
}

%WindName%::~%WindName% ()
{
   mData->RemoveResponder (this);
}


}

void  %WindName%::Close ()
{
   delete this;
}
...
%
   srcFile.close ()
end genWindow

Talk Back!

Have an opinion? Readers have already posted 16 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Andy Dent adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Andy is a free-lance developer in C++, REALbasic, Python, AJAX and other XML technologies. He works out of Perth, Western Australia for a local and international clients on cross-platform projects with a focus on usability for naive and infrequent users. Included in his range of interests are generative solutions, software usability and small-team software processes. He still bleeds six colors, even though Apple stopped, and uses migration projects from legacy Mac OS to justify the hardware collection.

This weblog entry is Copyright © 2008 Andy Dent. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use