Building a custom GUI to configure a template

Templating System

As I mentioned in my outline of a modern web application the server just has to serve up a source to the browser and respond to JSON web service calls.

To get a convenient work flow for the Translator I use a ‘template’ module. The main advantage of this module is that it uses some techniques to allow the Javascript, CSS and HTML template files it serves up to be edited on the fly in the Translator without requiring a milestone to be saved and the channel restarted.

How it works is that the template module effectively operates in three modes:

  1. Development mode.
    • When iguana.isTest() == true i.e., running in test mode
    • and when iguana.isTest() == false, i.e., running in the channel
  2. Production mode.

In development mode in the editor, when ever one changes a template, the change is immediately written as a file to disc. If the channel itself is running then when it serves up a request for each file it pulls the file off disc. In this manner the templates can be changed on the fly without requiring saving the milestone or restarting the channel. This is very convenient for tweaking Javascript and CSS code.

In production mode the templates are all stored in memory and served up quickly when required with no reference made to disc. As an experiment I also got the Translator to compile Javascript using Google’s Closure javascript compiler using it’s RESTful API.

This is how the interface works to the template module. Firstly each template must be registered in the main function:

-- For convenient put the code for each template into it's own self contained module:
template.Register('main.html', config_html.html())
template.Register('script.js', config_javascript.script())
template.Register('configure.css', config_css.css())
template.Register('hello.html', '<html><body><p>Hello world!</p></body></html>')

Then I wrote a single handler function for serving up these templates:

function ServeFile(Request)
   local MimeType = mime.Type(Request.params.file)
   local Body = template.Recall(Request.params.file)
   net.http.respond{entity_type=MimeType, body=Body}
end

The MIME type is deduced off the file extension, i.e., js results in text/javascript etc.

The source code for the template module itself is:

require('closure')
--[[
This template module is intended to simplify the web development within the Translator by allowing on the fly
editing of Javascript, HTML and CSS templates within the editor without saving a milestone. 

The technique is to write files into the file system which the channel can server up when running in non production
mode. The editor is writing these files as they are changed and the running channel is serving them up. This means
one can develop much more iteratively.

In production mode the templates are served up out of memory only which is faster plus there is an experimental feature

]]

template={}

local TemplateTable={}

function template.Serve(Name, Content, ContentType)
   local FileName = "web_docs/"..Name
   if iguana.isTest() then
      local F = io.open(FileName, "w")
      F:write(Content)
      F:close()
   end
   local F = io.open(FileName, "r")
   local Body = F:read('*a')
   F:close()
   net.http.respond{entity_type=ContentType, body=Body} 
end

local function RegisterProd(Name, Content)
   if not TemplateTable[Name] then
      if Name:sub(#Name-2, #Name) == '.js' then
         TemplateTable[Name] = closure.compile(Content)
      else
         TemplateTable[Name] = Content
      end
   end
end

local function RecallProd(Name)
   return TemplateTable[Name]      
end

function template.Register(Name, Content)
   TemplateTable[Name] = true
   local FileName = "web_docs/"..Name
   if iguana.isTest() then
      local F = io.open(FileName, "w")
      F:write(Content)
      F:close()
   end
end

function template.Recall(Name)
   if not TemplateTable[Name] then
      return "Resource does not exist."   
   end
   local FileName = "web_docs/"..Name
   local F = io.open(FileName, "r")
   if not F then 
      return nil   
   end
   local Body = F:read('*a')
   F:close()
   return Body 
end

function template.SwitchProductionOn()
   template.Register = RegisterProd
   template.Recall = RecallProd
end