One of the first tasks in an application is to load all sources, packages. and extensions needed while running that application.

Rig has a simple convention to help with this: Rig auto-loads all *.tcl files present in the same directory as the Rig.tcl file. Each of these files will be loaded as a rig, i.e. into a namespace with the same name as the source file, and with a global ensemble command for public access.

For example, assume there is a directory with these files:

    myapp/
        myapp.tcl
        Rig.tcl
        utils.tcl

When Rig.tcl is loaded, it will find each of these files and load them, defining rigs named "myapp" and "utils" (and "Rig" itself, of course).

Source files will usually contain just a few types of code:

  • comments
  • variable declarations
  • notification hooks
  • proc definitions

It is important to note that rig sources must be "re-loadable" at any time, i.e. they must not overwrite the values of current variables. This allows for very convenient reloading of code changes into a running app during development.

Naming conventions

The convention is that uppercase rig names are intended for general public use, while lowercase proc names are public methods inside the rig. This is not as contradictory as it might sound: the logic is really that your code, the stuff you work on in your application, is all about lowercase: lowercase rig names, and lowercase procs for access between the different rigs. Uppercase rig names are for stuff coming from somewhere else and uppercase proc names are for private methods within the rig. Only the case of the first character is considered in these rules.

To summarize:

  • start the module names used in your app with lowercase, i.e. rigs/sources
  • start the method names you want to access from other rigs as lowercase

Note that when written in the most general way, i.e. without hardcoded reference to the rig's namespace, then a rig can be renamed simply by renaming its files.

Loading additional rigs

It is very easy to use rigs from various other places, written by others or by you: simply call them whenever you need them. All you need to do, is set up one or more areas where these rigs are located.

One simple convention is to put all rigs in a directory called "local", and then add the following line in your main application startup rig:

    Rig autoload [Rig home]/local

This will scan the specified directory, and set up all rigs found there for auto-loading. The standard Tcl auto_index array is used for this.

For example, if you intend to use rigs called "Httpd" and "Web", then you could place those files in local/ and add the above command to your "myapp.tcl". The resulting layout of your application would then be:

    myapp/
        myapp.tcl
        Rig.tcl
        utils.tcl
        local/
            Httpd.tcl
            Web.tcl

Then simply call Httpd and Web and they'll automatically be loaded on first use. Here's a complete webserver example for myapp.tcl, based on the above:

    Rig autoload [Rig home]/local
    Rig hook main.Run {
      Httpd start 8080 [namespace code WebRequest]
    }
    proc WebRequest {obj} {
      $obj respond "<h1>Hello, world!</h1>"
    }

There are no package requires or other dependencies. The Httpd rig depends on the Web rig and will auto-load Web.tcl when it needs it. There is no explicit rig dependency handling, but that's where auto-downloading comes in.

Rigs vs. packages

Some rigs, such as Httpd and Web, turn out to be sufficiently general to be used by different applications, a bit like Tcl's packages (and 8.5's modules).

But there is a difference - rigs have to adhere to a set of conventions, and get loaded in a certain way. A rig wraps up functionality in its own namespace, with conventions for calling methods and using notifications to tie rigs together.

Packages, Tcl modules, and compiled extensions provide (lots of) functionality, but they tend to stand alone. Although many of them provide a clean, OO-ish, and well-document "API", there is less uniformity, especially regarding callbacks.

New rigs could be defined as wrappers for the packages and extensions that an application needs. This allows tying into Rig's notification system, as well as adjusting the original API to more closely match what the application needs.

In other words: the idea is to build applications in terms of rigs, many of which may well be quite simple and merely thin wrappers around "other" code. Such an approach allows dealing with version differences as new packages come out, but it also makes it possible to create rigs which download or even automtically build packages and modules available from other websites or remote repositories.

Wrappers are optional, Rig doesn't prevent you from using packages as before.

Rigs are not versioned: the one and only version is the code which is currently included in the application. Simply adjust anything as needed. And we all use a version control system, right?

Auto-downloading

Some Rigs will evolve to the point where they are general enough to be easily re-usable. That's where auto-downloading comes in. A contrib.mavrig.org HTTP public server has been set up to collect and serve these general purpose rigs. Its contents comes from a subversion repository.

The reason for auto-downloading is convenience. When Rig is used as above, it automatically sets up rig auto-downloading with the following command:

    Rig autoload [Rig home]/rigcache http://contrib.mavrig.org/

Auto-downloading works by fetching an "all.def" file on startup, listing all the available rigs. Rig then sets up auto_index entries to auto-download as needed.

As long as the rig remains in the cache, it will not be auto-downloaded again. For release deployment (e.g. a starkit/starpack), the cache can be included and auto-downloading turned off by dropping the url from the "Rig autoload" call.

Multiple auto-load and auto-download areas can be defined as needed.

Example

Now the above webserver example can be simplified further, since there is no need to obtain the Httpd and Web rigs up front. What follows are the complete instructions to create and launch a little webserver from scratch:

            Rig hook main.Run {
              Httpd start 8080 [namespace code WebRequest]
            }
            proc WebRequest {obj} {
              $obj respond "<h1>Hello, world!</h1>"
            }
  • (4) launch this application as follows:
            tclkit85 Rig.tcl webapp.tcl
  • (5) there is no step 5

When Httpd is called for the very first time, it'll be auto-downloaded. When Httpd later needs the Web rig, then that too will in turn be auto-downloaded.

The resulting directory layout after running this app will be:

    myapp/
        myapp.tcl
        Rig.tcl
        rigcache/
            jcw/Httpd.tcl
            jcw/Web.tcl

Note that there is an extra directory level, this is to avoid naming conflicts.

Extended rig names

There can be many rigs in an application. It is expected that in due time there will be even more downloadable rigs, often small and focused on a single task.

For this reason, every public rig download area is structured as a hierarchy, adding an extra level for each rig author/maintainer who has check-in access to the underlying svn repository. Furthermore, to deal with long term versioning of the entire collection of rigs, there is a third level for the version number, currently fixed as "0.x".

Here is an example rig download area, adapted from what's on contrib.mavrig.org:

    0.x/
        all.def
        Rig.tcl
        jcw/Httpd.tcl
        jcw/Web.tcl

When rigs are auto-downloaded, they will be stored in a directory with the initials of their authors/maintainers. In fact, when loaded they will be loaded in a namespace also containing those initials.

The (extended) name of the Httpd rig once auto-downloaded will be "jcw/Httpd". The namespace used will also be "::jcw/Httpd", i.e. including the slash. So will the name of the global command.

But there is a shortcut: IF the name "Httpd" turns out to be unique among all auto-loadable and auto-downloodable modules, THEN an alias will be set up so that the global proc "::jcw/Httpd" can also be accessed as "::Httpd".

Such a shortcut will not be created by Rig if there is any other module on the remote site with a conflicting name. This might even happen at a later date, causing a previously unique name to no longer work. Fortunately there is an easy solution, defining the shortcut yourself:

      interp alias {} Httpd {} jcw/Httpd

From then on, Httpd will refer the this same rig forever, while still allowing any rig to be used through its extended name.

The "all.def" index file is fetched by Rig to determine which rigs are available for downloading. This text file contains a list of all rigs, in this case:

    jcw/Httpd.tcl
    jcw/Web.tcl

On Unix-like systems it can be created as follows: 1

    cd contrib/0.x
    ls */*.tcl >all.def

Using this information, anyone can set up rig auto-download sources on any local or remote server. Rig only supports HTTP access (using Tcl's http::geturl).

Simple, eh?