digital-scurf wikiAranha > Module Loader

Page Contents


The current documentation

Forms

Aranha loadable modules take one of two forms:

Both forms are loaded in the same way and all modules released by the Aranha team (and hopefully all 3rd party modules too) will behave in the same way when invoked.

Loading modules

To load a module, place a statement in your code of the form:

UseModule "Aranha.GD"

That would open the Aranha libgd binding and perform the default binding operation (which would create a table called 'gd' in the current global scope.

If you want to load a library into a different table or a different scope then there's the hard way (mess with the globals table in nasty ways) or the easy way:

agd = LoadModule "Aranha.GD"

The UseModule call also returns the name of the table which was created by the loading of the specified module.

If UseModule fails, it will raise an error, if LoadModule fails, it will return nil.

If UseModule or LoadModule are given a path rather than a module name then the path should be of the form:

UseModule "/path/to/my/modules/Aranha/GD"

This will cause the loader to look for GD.am and GD.so by appending the .am and .so extensions to the path given. Using this form of loading will skip the _MODULE checks, so there's no guarantee that the module you load will be the one you want.

There is one other call available. It is called ModuleInfo and when called it will return several results in a table.

  info = ModuleInfo "Aranha.GD"

If the module has not yet been loaded, ModuleInfo will return nil.

You can also use the full path to the module (ignoring the .so and .am extensions).

The table has the following defined fields:

FieldMeaning
nameThis is the name of the module.
pureThis is a boolean. True if the module is pure lua.
fileThis is a string indicating the source of the module.
libraryIf present, pure is false and this is a string indicating the filename of a shared object loaded for the module.
versionThis indicates the version of the library in use.
namespaceThis is the recommended namespace name for the module. (Note, not necessarily the namespace it is loaded into)
Any other entries added by the loader may be in there also. Recommended modules will specify...
authorString indicating who wrote the module
urlString indicating where to get the module from

Writing modules for Aranha

Writing modules for Aranha is slightly more involved. We will first consider pure Lua modules, since the considerations for those are important for mixed modules too.

When writing a module for Aranha you code in Lua, not LHTML. It is recommended that modules do not do anything in their main scope when they are loaded and they are at liberty to create 'global' names since during the initialisation of the module, the globals table will be swapped out and an empty one brought in. (If you *need* access to the real globals during load, they are in _REAL_GLOBALS)

There is a special function which are used by the loader. The function is called _IMPORT. Also there are special variables which *must* be specified in the global scope of the module file.

The first of the variables is called _MODULE and must contain the name of the module specified in this file. (This is checked by the loader and must match the name used to import the module or the import will fail).

The second of the variables required is called _VERSION and must contain the version number of the module. This information is stored by the loader in its special tables and can be retrieved using the ModuleInfo call.

The third required variable is called _NAMESPACE and must be a string (with no dots or illegal variable name characters) which will be used as the namespace to load the module into if UseModule is called.

If the module wishes to register any other entries in the info table then it should have a global variable called _INFO which should be a table to be merged into the info table for the function. Only string keys will be considered, and only those whose value is a string, boolean or number. It is recommended that such a table should contain an author and url entry as per the previous section of this document.

The _IMPORT function is called each time the module is UseModule'd or LoadModule'd. It takes one argument, a table which contains the functions created during the parsing of the module.

Here is an example module

--
-- Example Module
--

_MODULE    = "Aranha.Example"
_VERSION   = "1.0"
_NAMESPACE = "gd"

_INFO    = {
             author = "The Aranha Team <aranha@pepperfish.net>",
             url    = "http://aranha.pepperfish.net/"
           }

function say(name)
 dprint [[<span style="text-decoration: underline"><@name@></span>]]
end

function _IMPORT(funcs)
 -- Gratuitious use of _IMPORT to demonstrate the power of it.
 funcs.greet = function(name) funcs.say("Hello" .. name) end
end

--
-- End of module
--

Next we shall consider the C-api for adding extra interfaces to your modules.

In your C-code you should keep as many symbols local as possible (make them static) and those which can't be local should be namespaced with some unique prefix (e.g. _modulename).

There should be one function with a prototype of:

int _aranha_import( lua_State *L, int idx );

The parameter idx indicates where on L's stack the table to add your elements to is. (You shouldn't add/remove anything from the stack from here on toward 1. I.E. at the end of your function, lua_settop(L,idx) is a reasonable way of clearing your values)

This function should return zero for successful load or non-zero for a failure state of any kind.

(Note that some constants are defined in aranha.h of the form ARANHA_LOAD_<blah> and should be used wherever possible)

On the stack, the function will be passed (in order, starting at offset idx)

The table into which to load its functions. The name of the module as requested The version of the module's lua counterpart file

It is up to this function to verify that it's happy to act as that module with that version number and to create any functions in the table specified as it sees fit.

There should be one globally exported symbol of the form:

extern unsigned int _aranha_apiversion;

This should contain the version of the Aranha API needed to run the module. Since the module can load without knowing if it has the right API, it is essential that this variable be initialised with the value of ARANHA_APIVERSION when compiling the module.

Here is an example *simple* C module and its corresponding Lua file:

/*
 * Example C Aranha Module
 */

#include <aranha.h>

#include <unistd.h>

extern unsigned int _aranha_apiversion;
unsigned int _aranha_apiversion = ARANHA_APIVERSION;

static L_snooze( lua_State *L )
{
   if( lua_gettop(L) != 1 ||
       lua_type(L, 1) != LUA_TNUMBER ) {
      lua_pushliteral(L, "snooze(): Expecting single numeric argument");
      lua_error(L);
   }
   sleep( lua_tonumber(L, 1) );
   return 0;
}

int _aranha_import( lua_State *L, int idx )
{
   /* Check that the module is called "Aranha.Snooze" */
   if( strcmp( "Aranha::Snooze", lua_tostring(L, idx+1) ) != 0 )
      return ARANHA_LOAD_BAD_NAME;
   /* Check that the module is the right version */
   if( strcmp( "1.0", lua_tostring(L, idx+2) ) != 0 )
      return ARANHA_LOAD_BAD_VERSION;
   /* Okay, looks good, register the snooze function */
   lua_pop(L, 2);
   lua_pushliteral(L, "snooze");
   lua_pushcclosure(L, L_snooze, 0);
   lua_settable(L, idx);
   return ARANHA_LOAD_SUCCESS;
}

/* End of module */


--
-- Example Lua module with a C counterpart
--

_MODULE    = "Aranha.Snooze"
_VERSION   = "1.0"
_NAMESPACE = "snooze"

_INFO    = {
             author = "The Aranha Team <aranha@pepperfish.net>",
             url    = "http://aranha.pepperfish.net/"
           }

function _IMPORT(funcs)
 -- Gratuitious use of _IMPORT to demonstrate the power of it.
 funcs.sleep = funcs.snooze; -- The snooze function will be registered by now
end

--
-- End of module
--

Assuming the files above are: Snooze.c and Snooze.am and that Snooze.c is compiled into a shared object called Snooze.so, it should be installed into a directory structure of the form:

Aranha/
       Snooze.so
       Snooze.am

And the path to the 'Aranha' containing directory should be in aranha.conf's load_path string.

Then the module can be loaded with

UseModule "Aranha.Snooze"

A suitable compile like for Snooze.c could be (assuming various things)

gcc -I/usr/local/include/aranha -o Snooze.o Snooze.c gcc -shared -o Snooze.so Snooze.o