Auto-generating Translator API documentation…by using the Translator.

This is an interesting technique to use Iguana itself to serve the documentation of it’s own API (see comments in the code). If you are interested in this you may also want to checkout using _G to discover the entire API of the Translator.  Also see the Iguana Translator modules reference.

If you are curious about some ideas to document your own interfaces you might want to check out documenting an HL7 interface.

First make a channel with a Translator component (a To Translator is easiest).

Paste in the following code:

-- fetch help info from all functions
-- by calling them with no params and reaping the error message
-- PROBLEM some functions are missed because they have no params
--         eg iguana.isTest() and json.createObject()
-- HACK call that tries to guarantee invalid params
-- 2ND HACK in response to error introduced by first caused by
-- logic added to identify error and give improved feedback...
local G=util.guid(128)
local function createHelp(Input)
   print(G)
   local out = {}
   for i,v in pairs(Input) do
      if (type(v) == 'function') then
         -- some functions are missed = have no params
         --local test,err = pcall(v)
         -- HACK try to guarantee invalid call
         local test,err = pcall(v,{G=1},G,{G=G},asdf,'ÄdÉfÍ',[[n#@]]) --HACK
         print(err)
         if err:find("@@Unknown parameter: 'G'") then -- 2ND HACK
            test,err = pcall(v)
         end
         if not test then
            local sentinel = '%@%@%<%>%<%>ifware%-error%<%>%<%>%@%@'
            print(sentinel)
            local err_fixed = err:gsub(sentinel,'')
            out[i] = tostring(err_fixed)
         end
      elseif (type(v) == 'table') then
         print(v)
         local NextSet = createHelp(v)
         if (#NextSet) then out[i] = NextSet end
      end
   end
   return out
end

-- similar to pairs(), but gives a sorted set
local function pairsByKeys (t, f)
   local a = {}
   for n in pairs(t) do table.insert(a, n) end
   table.sort(a, f)
   local i = 0      -- iterator variable
   local iter = function ()   -- iterator function
      i = i + 1
      if a[i] == nil then return nil
      else return a[i], t[a[i]]
      end
   end
   return iter
end

-- writes out several files, in a hierachical fashion
local function writeHelpFiles(Doc,Key,BaseDir)
   -- write this, and links
   local out = '<HTML><TITLE>'..Key..'</TITLE><BODY>'
   out = out..'<H3>'..Key..'</H3><UL>'
   for i,v in pairsByKeys(Doc) do
      out = out..'<li>'
      if type(v) == 'table' then
         out = out..'<a href="'..Key..'.'..i..'.html">'..i..'</a>'
         --do one level down         
         out = out..'<table>'
         for j,k in pairsByKeys(v) do
            local summary = ''
            if type(k) == 'string' then
               local pattern = '([^n]*)(.*)'
               _,_,summary = k:find(pattern)
            end
            out = out..'<tr><td width="20"></td><td width="80"><a href="'
            ..Key..'.'..i..'.html#'..j..'">'..j..'</a></td><td>'..summary..'</td></tr>'
         end
         out = out..'</table>'

         writeHelpFiles(v,Key..'.'..i,BaseDir)
      else

         out = out..'<a name="'..i..'"><b>'..i..'</b></a><br><pre>'..v..'</pre>'
      end
      out = out..'</li>'
   end
   out = out..'</UL></BODY></HTML>'

   local fd = io.open(BaseDir..''..Key..'.html','wb')
   fd:write(out)
   fd:close()
end

-- writes everything to a single html string
local function writeAll(Doc,Key,Parent)
   Parent = Parent or 'base'
   local out = ''
   out = out..'<a name="'..Parent..'.'..Key..'"><H2>'..Key..'</H2></a><UL>'
   for i,v in pairsByKeys(Doc) do
      out = out..'<li>'
      if type(v) == 'table' then
         --do sub levels
         out = out..writeAll(v,i,Key)
      else
         out = out..'<a name="'..Parent..'.'..Key..'.'..i..'"><b>'..i..'</b></a><br><pre>'..v..'</pre>'
      end
      out = out..'</li>'
   end
   out = out..'</UL>'
   return out
end

-- writes a table of contents for the top of the OneHelpFile
local function writeToc(Doc,Key,Parent)
   Parent = Parent or 'base'
   local out = ''
   out = out..'<a href="#'..Parent..'.'..Key..'">'..Key..'</a><UL>'
   for i,v in pairsByKeys(Doc) do
      out = out..'<li>'
      if type(v) == 'table' then
         --do sub levels
         out = out..writeToc(v,i,Key)
      else
         local summary = ''
         local pattern = '([^n]*)(.*)'
         _,_,summary = v:find(pattern)
         out = out..'<a href="#'..Parent..'.'..Key..'.'..i..'">'..i..'</a> : '..summary
      end
      out = out..'</li>'
   end
   out = out..'</UL>'
   return out
end

-- write a single file
local function writeOneHelpFile(Doc,Key,BaseDir)
   local out = '<HTML><TITLE>'..Key..'</TITLE><BODY>'
   out = out..writeToc(Doc,Key)
   out = out..writeAll(Doc,Key)
   out = out..'</UL></BODY></HTML>'
   local fd = io.open(BaseDir..''..Key..'_all.html','wb')
   fd:write(out)
   fd:close()
end

local function deepcopy(object)
   local lookup_table = {}
   local function _copy(object)
      if type(object) ~= "table" then
         return object
      elseif lookup_table[object] then
         return lookup_table[object]
      end
      local new_table = {}
      lookup_table[object] = new_table
      for index, value in pairs(object) do
         new_table[_copy(index)] = _copy(value)
      end
      return setmetatable(new_table, getmetatable(object))
   end
   return _copy(object)
end

-- copy _G and remove Lua stuff so only Iguan modules remain
local function getIguanaModules()
   local my_G=deepcopy(_G)

   -- remove Lua stuff
   my_G.xpcall=nil
   my_G.string=nil
   my_G.tostring=nil
   my_G.package=nil
   my_G.os=nil
   my_G.unpack=nil
   my_G.require=nil
   my_G.print=nil
   my_G.getfenv=nil
   my_G.next=nil
   my_G.assert=nil
   my_G.setmetatable=nil
   my_G.setfenv=nil
   my_G.tonumber=nil
   my_G.gcinfo=nil
   my_G.io=nil
   my_G.rawequal=nil
   my_G._VERSION=nil
   my_G.collectgarbage=nil
   my_G.debug=nil
   my_G.getmetatable=nil
   my_G.collectgarbage=nil
   my_G.module=nil
   my_G.collectgarbage=nil
   my_G.cache=nil
   my_G.rawset=nil
   my_G.math=nil
   my_G.load=nil
   my_G.pcall=nil
   my_G.table=nil
   my_G.newproxy=nil
   my_G.type=nil
   my_G.coroutine=nil
   my_G._G=nil
   my_G.select=nil
   my_G.pairs=nil
   my_G.rawget=nil
   my_G.loadstring=nil
   my_G.ipairs=nil
   my_G.main=nil
   my_G.dofile=nil
   my_G.IntellisenseObject=nil
   my_G.error=nil
   my_G.loadfile=nil  
   my_G._=nil
   return my_G
end

-- remove known Iguana modules so new ones can be identified
-- and then can be documented
local function getNewIguanaModules()
   local my_G=getIguanaModules()
   --remove known Iguana modules
   my_G.ack=nil
   my_G.queue=nil
   my_G.net=nil
   my_G.iguana=nil
   my_G.util=nil
   my_G.xml=nil
   my_G.db=nil
   my_G.node=nil
   my_G.hl7=nil
   my_G.x12=nil
   my_G.json=nil
   my_G.chm=nil
   my_G.filter=nil

   return my_G
end

function main(Data)
   -- list out all namespaces in our code that we want to print
   -- note that this won't work with the _G table, because some
   -- functions will work with no parameters, and proceed to block indefinitely
   -- so we narrow down what we want to print
   -- HACK: see createHelp() - now uses weird params to catch *no-param* fn()s
   --       does not work with _G as it overflows the stack
   -- NOTE: use table annotation with print(_G) to inspect global environment
   --       use it to check for new modules and
   --       check you have printed out all functions in a module
   -- get all Iguana Modules
   getIguanaModules()
   -- get all NEW Iguana Modules
   -- add to list below
   getNewIguanaModules()

   local ifware = {
      -- remember to add new Iguana modules to list (see above)
      --['ack']=ack,
      --['xml']=xml,
      --['util'] = util,
      --['queue'] = queue,
      --['iguana'] = iguana, -- iguana.isTest() has no params
      ['node']=node,
      --['x12']=x12,
      --['chm']=chm
      --['hl7'] = hl7,
      --['db'] = db,
      --['net'] = net,
      --['json'] = json, -- json.createObject() has no params
      --['filter'] = filter,
      --['NEW modules'] = getNewIguanaModules(),
      --['ALL modules'] = getIguanaModules(),
      --['_G']=[_G] -- overflows the the stack - try it
   }
   print(_G) -- inspect global environment table
   local output_dir = [[c:program filesiNTERFACEWAREiguanaweb_docsdocs]]
   local base_name = 'iguana'

   -- collect up all the help info
   local out = createHelp(ifware,base_name)

   --write out a hierachical set of html files
   writeHelpFiles(out,base_name,output_dir)

   --write a single html file called <base_name>_all.html
   writeOneHelpFile(out,base_name,output_dir)
end

You may have some errors initially, just turn off annotations for now.

Then in the main function:

  • Modify the base directory so that it points to the iguana installation location. E.g., if you have  installed Iguana in c:iguana, change the base directory to ‘c:iguanaweb_docsdocs’
  • Go to the install directory, and there will be a web_docs subdirectory. In web_docs, make a docs folder.

Now turn on annotations, and make sure that it runs with no errors.

Now go back to the dashboard, and go to

http://<my ip>:<my iguana port>/docs/iguana_all.html

or

http://<my ip>:<my iguana port>/docs/iguana.html

or you can just open the files from the doc directory…

And you should see the API’s documented!