Toolatra is a modern Sinatra -like micro web framework for writing HTTP Tcl web applications.
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!
If you want ToolatraMustache module to work, though, you'll need to manually install mustache.tcl library, too:
$ git clone https://github.com/ianka/mustache.tcl mustache-tcl $ cd mustache-tcl $ sed "s+INSTALL_PATH=.*+INSTALL_PATH=$HOME/Library/Tcl/mustache-tcl+g" Makefile > Makefile.fixed $ make -f Makefile.fixed install
On Linux, you'll have to install it system-wide. The path to the folder with Tcl packages highly depends on the distro.
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!
ToolatraMustache, though, depends on mustache.tcl, which has to be installed seperately:
$ git clone https://github.com/ianka/mustache.tcl mustache-tcl $ cd mustache-tcl $ sudo make install
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> @content@ </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:
@!head.html@ @content@ @!foot.html@
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:
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.
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
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!