First steps with Iliad (web development with gnu-smalltalk and Iliad)

Tagged:  •    •  

Hi!

The last one and a half week, I was playing with seaside, but now, Nicolas Petton introduced a new web-development framework which is called Iliad http://smalltalk.gnu.org/blog/nico/iliad-new-lightweight-web-framework-gnu-smalltalk that seems so interesting, that I decided to try it out for a new web application, which is currently in a planned status.

And because the documentation and howtos for Iliad is currently not widely accessible, I decided to write a bit about my expiriences.

Preparing the environment

My current web-server is using apache as the web-serving software, I decided to use apache as the frontend even for the new web-site. Therefore I configured apache in the same way as I would do it for seaside. I need the mod_proxy module to achieve this and configured it with the following lines of configuration in the virtual-server config-file for the new domain:

ProxyPass /urpics http://www.example.com:7777/UrPics
ProxyPassReverse /urpics http://www.example.com:7777/UrPics

That means, that every request for http://www.example.com/urpics will be rerouted from the front-end web-server software to http://www.example.com:7777/UrPics and vice versa.

I also need a directory for the apache-website, where the resources like css-files and images are stored. I configured the directory /www/urpics somewhere in my directory tree to hold these contents, and configured this directory in the virtual-server config file.

Important:
|-
|You can host the complete application with Swazoo alone. There's no need for using apache as the front-end. The reason for me using apache was, that I already have a running apache on my web-server on port 80 and therefore I liked to use it even for the new application. And the seperation between the smalltalk-code and the style is a little bit stronger...
Application hosting without apache:

Another step is, to download and install the iliad package as described in the blog-post from Nicolas http://smalltalk.gnu.org/blog/nico/iliad-new-lightweight-web-framework-gnu-smalltalk.

To help me to install all the available gnu-smalltalk packages in the Iliad source, I have a small shell-script which detects all the different packages under the current directory. This script is - in my case - also usefull for every other project, especially for sources with several different packages in one source.

make_packages.sh

GST_PACK=/opt/bin/gst-package

CURR_DIR=`pwd`
echo "Packaging in $CURR_DIR ..." > make_packages.log
for i in `find . -name package.xml`
do
    echo "packaging $i ..."
    $GST_PACK -t ~/.st $i >> make_packages.log
done

This script was inspiered by the mailing list post of Stefan Schmiedl.

Now we could go on with the interesting part of creating a Hello-World example with Iliad, but because I showed you here a helper-shell-script, let me show you 2 others, which I'm using for development.

First one, to create a package.xml file from my sources. (I think, you know the packaging system which is used by gnu-smalltalk...):

This scripts looks for the smalltalk source-files in and under the current directory and tries to create a package.xml file from the found sources. You - of course - have the possiblity, to provide a package-name, namespace and several prerequisite-packages to the script:

make_package.sh

#!/bin/sh
if [ $# == 0 ]; then
        echo " $0 [-n package_name] [-s namespace] [-p prerequisite_package]"
        exit 1
fi

NAME=''
NAMESPACE=''
PREREQ=''
while getopts n:p:s:h o
do        case "$o" in
        n)        NAME="$OPTARG";;
        s)        NAMESPACE="$OPTARG";;
        p)        PREREQ="$PREREQ:$OPTARG";;
        h)        echo "Usage:"
                echo " $0 [-n package_name] [-s namespace] [-p prerequisite_pack
age]"
                exit 1;;
        esac
done

{
echo "<package>"

if [ "$NAME" != "" ]; then
        echo "  <name>$NAME</name>"
fi

if [ "$NAMESPACE" != "" ]; then
        echo "  <namespace>$NAMESPACE</namespace>"
else
        if [ "$NAME" != "" ]; then
                echo "  <namespace>$NAME</namespace>"
        fi
fi

if [ "$PREREQ" != "" ]; then
        OIFS=$IFS
        IFS=:
        for p in $PREREQ
        do
                if [ "$p" != "" ]; then
                        echo "  <prereq>$p</prereq>"
                fi
        done
        IFS=$OIFS
fi

echo ""

for i in `find . -name "*.st"`
do
        i=`echo $i|sed -e 's/^\.\///'`
        echo "  <filein>$i</filein>"
done

echo ""

for i in `find . -name "*.st"`
do
        i=`echo $i|sed -e 's/^\.\///'`
        echo "  <file>$i</file>"
done

echo "</package>"
} > package.xml

This script could be used with the parameters:

  • -n for the package-name
  • -s for the namespace-name
  • -p for prerequisite packages

And, because I'm currently writing my source-files with vim, I have a shell-script, which recreates the package for my application, creates a new Image with it and Iliad and restarts the test-environment for testing my web-app.

start.sh

echo "Killing gst-remote --server"
gst-remote --eval 'Iliad.SwazooIliad stop'
sleep 2
gst-remote --kill

echo "Creating package.xml ..."
make_package.sh -n UrPics -p Iliad

echo "Creating package-file ..."
make_packages.sh

echo "Creating Image ..."
{
echo "PackageLoader fileInPackage: 'UrPics'."
echo "ObjectMemory snapshot: 'urpics.im'."
}|gst

echo "Starting server ..."
gst-remote -I urpics.im --server &
sleep 3

echo "Restarting Swazoo! startOn: 7777..."
gst-remote --eval 'Iliad.SwazooIliad startOn: 7777'
echo done!

This script can of course easily be altered to use gst instead of gst-remote. Just change the the part for the image-creation and restarting the gst-remote and Iliad.SwazooIliad with the following lines:

echo "Starting gst ..."
{
echo "PackageLoader fileInPackage: 'UrPics'."
echo "Iliad.SwazooIliad startOn: 7777."
echo "Processor activeProcess suspend."
} | gst

The interesting part, the first Iliad application

But now let's go on with the realy intersting part, the creative work of writing our brand new web-application.

First, we need to subclass the Iliad.Application class, which is the entry-point for our web-application:

Iliad.Application subclass: UrPicsApplication [

    UrPicsApplication class >> path [
        ^'/UrPics'
    ]

    index [
        <category: 'views'>
        ^[ :e |
            e add: UnknownHome new build
        ]
    ]

]

The class-method path is important for dispatching the application. It provides the path, under which the application should be accessible ("/UrPics" in this case. Please compare it with the apache-config for the mod_proxy.)

And the index-method, which is the entry-point for showing our contents.

Important:
|-
|This category code is realy important for the index-method, if you won't rewrite the selectorFilter method of the application.
<category: 'views'>

In this described index-method, we just call an additional widget of the application, which creates the content. You can see here, for every widget you use, you should call their build-method, to create the view-representation.

If you would like, you can even set the include of some of your stylesheets for the site and a title in creating an updatePage method with the following content:

updatePage [
    super updatePage.
    self page headElement title: 'Iliad - Hello World application!'.
    self page headElement stylesheet href: '/resources/style.css'.
]

The thing, we now only need is the source of the UnknownHome.st class. It only displays an html header line with 'Hello World' and looks like this:

Iliad.Widget subclass: UnknownHome [

    contents [
        ^[ :e |
            e h1: 'Iliad - Hello to the world out there!'.
        ]
    ]

]

Now run apache and the start.sh script and point your browser to http://www.example.com/urpics

Thats all for now, next things would be subclassing the session etc.

Happy coding!
Joachim.

updatePage has changed, now it should read

  updatePage: aPage [
    
    super updatePage: aPage.
    aPage head title: 'whatever'.
    aPage head stylesheet href: '/stylesheets/whatever.css'.
  ]



And it's something quite peculiar,
Something that's shimmering and white.
It leads you here, despite your destination,
Under the milky way tonight...

I use a one-liner for packaging:

function() {
  find "$1" -name package.xml | xargs -n 1 gst-package -t ~/.st
}

The inspiration for that one dates back quite a few years to an email interview conducted by yours truly, where a gentleman called Paolo Bonzini casually provided an amazing xargs combo as an example of productivity to an aspiring young author.

Being lazy and without short-term memory requires a slight modification to gst-pack-all: Use the working directory as default.

function gst-pack-all() {
  find "${1:-.}" -name package.xml | xargs -n 1 gst-package -t ~/.st
}

Then I tried my rewriting foo at the package file builder, which uses way too much vertical space :-)
Assuming that package.xml is not generated very often (according to the docs, file-in items have to be in the correct loading order), it might be wise to not overwrite an existing package.xml file. Another slight difference is that my package builder below only uses files in the working directory, whereas Joachim's original included .st-files in subdirectories.

function wrap() {
  echo "<$1>$2</$1>"
}

function gst-build-package() {
  package_name=${1:-MyPackage}
  namespace=${2:-MyNamespace}
  shift 2
  prereqs=$*
  package=package.xml
  [ -f ${package} ] && package=${package}.generated
  {
    echo ""
    wrap name "${package_name}"
    wrap namespace "${namespace}"
    for p in ${prereqs} ; do wrap prereq "${p}" ; done
    {
      for st in *.st ; do
        wrap filein "${st}"
        wrap file "${st}"
      done
    } | sort
    echo ""
  } > ${package}
}

Thanks Joachim... :)

Thanks Joachim...

seb

Very interesting article, thanks!
Would you allow me to include the first bash script into Iliad?

Thanks again,

Nico

Hello Nico,

thanks, was a pleasure for me! :-)

And - of course you are allowed to include this small contribution into Iliad!

Thanks to YOU for this nice piece of software!

Joachim.

User login