Toolatra

https://raw.githubusercontent.com/timkoi/toolatra/master/logo.png

Toolatra

Toolatra is a modern Sinatra -like micro web framework for writing HTTP Tcl web applications.

Features

  • Sinatra-like DSL
  • does not depend on anything, except for Tcllib (though, Toolatra works only with Tcl 8.5 and newer)
  • a built-in template engine and layout engine
  • a built-in web server that easily integrates with Apache or nginx

Installation

On macOS

It is highly, highly recommended that you use ActiveTcl 8.6 or 8.5 and not the macOS' built-in Tcl 8.5. While Toolatra does work without any problems with macOS' own Tcl build, ActiveTcl is way more modern and comes bundled with some useful packages that can be used with Sinatra.

For macOS-only, there is a script called install-macos.tcl that installs Toolatra into $HOME/Library/Tcl/toolatra-master and does not require root privileges.

To install Toolatra, clone the repo and just run:

$ tclsh install-macos.tcl

That's it, you have Toolatra installed!

On Linux

On Linux, you'll have to install it system-wide. The path to the folder with Tcl packages highly depends on the distro.

  • If you use Ubuntu, Toolatra needs to be installed into /usr/share/tcltk/toolatra
  • If you use Fedora or RedHat/CentOS, Toolatra needs to be installed into /usr/share/tcl8.6/toolatra

For convenience, create a temporary variable TOOLATRA_HOME and set it to the correct installation path.

$ export TOOLATRA_HOME=/usr/share/tcltk/toolatra # Ubuntu users
$ export TOOLATRA_HOME=/usr/share/tcl8.6/toolatra # Fedora/RHEL users

Obviously, you need Tcl installed via your distro's package manager.

First, remove the installation prefix if it already exists:

$ test -d $TOOLATRA_HOME && sudo rm -rvf $TOOLATRA_HOME

Now create the installation prefix and copy all *.tcl files from the repo into the prefix:

$ sudo mkdir -pv $TOOLATRA_HOME
$ sudo cp -v *.tcl $TOOLATRA_HOME/

That's it!

Templates and layouts

Toolatra comes with an addon ToolatraTemplates that provides an easy-to-use template/layout system tightly integrated with the framework. The template markup is called ETcl (Embedded Tcl).

Templates can not only contain the markup for the page itself, but they also can contain inline Tcl code.

Here is an example (let's call it viewer.html):

<html>
 <head><title>Some kind of blog or newspaper</title></head>
 <body>
   <center><h1>Some kind of blog or newspaper</h1></center>
   <center><i style="color: lightgrey;">
   @
      set toShow {}
      if {$auth} {  
           set toShow "You're signed in. Hey there, $name!"
      } else {
          set toShow "You're not signed in."
      }
   @
   </i></center>
   <div>
     @[email protected]
   </div>
</body>
</html>

As you might've probably guessed, Tcl code and variables are wrapped around with @s. To substitute a variable, you need to wrap around its name in @ without any other symbols. To run Tcl code, you also wrap it with @s.

To avoid repetitive static content in your templates, you can seperate into layouts. Layouts support all the same syntax as templates.

Here are two example layouts: head.html and foot.html.

head.html:

<html>
 <head><title>Some kind of blog or newspaper</title></head>
 <body>
   <center><h1>Some kind of blog or newspaper</h1></center>
   <center><i style="color: lightgrey;">
   @
      set toShow {}
      if {$auth} {  
           set toShow "You're signed in. Hey there, $name!"
      } else {
          set toShow "You're not signed in."
      }
   @
   </i></center><div>

foot.html:

</div></body></html>

Then we can include our layouts into our template by wrapping around the basename of the layout in @ and putting the exclamation mark right before the specified layout name. In our case, we can now include head.html and foot.html layouts from viewer.html template like this:

Now, we have templates and layouts, but how can we render it?

Pretty simple - using the etcl command:

package require Toolatra
package require ToolatraTemplates

get / {
     etcl viewer.html [dict create content {<p>Welcome!</p>} auth 0]
}

run

As you can see, etcl command can accept either one or two arguments. These are:

  1. the basename of the template that we want to render
  2. (optional) the context dict that contains variable values that we will be accessing from the template

The latter is needed if the variables that you want to access are not global. If they are global, you might want to omit the context dict.

All layouts must be stored in the layouts folder, all the templates must be stored in the templates folder.

Some examples

Hello world

app.tcl code:

#!/usr/bin/env tclsh
package require Toolatra

get / {
     render {Hello world!}
}

get /+name {
     render "Hello, [dict get $params name]!"
}

post / {
     if {$rawData != {}} {
         render "Hello, $rawData (via POST request)!" 
     } else {
         render {Hello world (via POST request)!} 
     }
}

run

How to run:

$ tclsh8.5 app.tcl

The app will be running at http://localhost:5050

  • If you go to http://localhost:5050 via your web browser, you should see Hello world!
  • If you go to http://localhost:5050/Tim via your web browser, you should see Hello, Tim!
  • If you send an empty POST request to http://localhost:5050 , you should get Hello world (via POST request)!
  • If you send a POST request with Jane sent as the data, you should get Hello, Jane (via POST request)!

Cat Language Translator

Both the current and the previous version of Cat Language Translator are written 100% in Tcl and Toolatra. The source code of the first version can be found here: https://github.com/timkoi/catlanguage-web

Serving Toolatra web apps with Apache (on Linux)

First, specify the port where Toolatra's HTTP server shall be running. In the application, specify the port as the argument to the run command, like this:

run 5056

Replace 5056 with your preferred port number.

Now, create a user and a systemd service for the web app:

$ sudo useradd -M -N toolatrarunner
$ sudo chsh -s /sbin/nologin toolatrarunner
$ sudo mkdir -pv /srv/mytoolatraapp
$ mv -v * /srv/mytoolatraapp
$ sudo chown -v -R toolatrarunner:users /srv/mytoolatraapp
$ cat >> /etc/systemd/system/mytoolatraapp.service << EOF
[Unit]
Description=My Toolatra web app
After=httpd.service

[Service]
Type=simple
ExecStart=/bin/sh -c "cd /srv/mytoolatraapp && LC_ALL=C /usr/bin/env tclsh app.tcl"
User=toolatrarunner

[Install]
WantedBy=multi-user.target
EOF
$ sudo systemctl enable mytoolatraapp
$ sudo systemctl start mytoolatraapp

Obviously, replace mytoolatraapp with the short name of your Toolatra web app.

Now create a reverse proxy with Apache:

$ sudo sh
$ echo 'ProxyPreserveHost On' >> /etc/httpd/conf/httpd.conf
$ echo 'ProxyPass /mytoolatraapp/ http://localhost:5056/' >> /etc/httpd/conf/httpd.conf
$ echo 'ProxyPassReverse /mytoolatraapp/ http://localhost:5056/' >> /etc/httpd/conf/httpd.conf
$ systemctl restart httpd
$ exit

Obviously, replace 5056 with the port you've specified and mytoolatraapp with the name of your web app.

That's it!

Links