Some tips on using Amber Smalltalk with nginx

Posted on April 11, 2012

Amber is an implementation of Smalltalk-80 language that compiles into JavaScript. Amber is extremely useful for client-side web development as a neat replacement for JS; it is integrated with jQuery out of the box and it is fully compatible with other JavaScript libraries.

Amber comes with a nice IDE that works directly in the browser. However, to save the code from the browser-based IDE on disk, Amber requires a HTTP server with WebDAV support. Though Amber comes with a tiny WebDAV server written in Amber, production environments require more powerful HTTP servers, like nginx or Apache.

In this post I will explain how to configure nginx to work with Amber.

Basic configuration

WebDAV functionality is provided in nginx by the ngx_http_dav_module module. This module is not built by default, so nginx needs to be rebuilt with --with-http_dav_module configure option.

However, Debian GNU/Linux and Ubuntu have nginx-extras package that includes a lot of modules, including this one.

Consider the following minimal configuration (file /etc/nginx/nginx.conf ):

server {
    root /opt/mysite;
    client_body_temp_path /opt/mysite/tmp;

    location / {
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_access  group:rw all:r;
    }
}

Here we defined a basic static site. The /opt/mysite directory should contain an index.html page with Amber Smalltalk included (see Getting started and A first application ). Notice that the /opt/mysite should be accessible for nginx (see more at “Permissions”).

Permissions

Now you should have something working at http://localhost . But when you will try to save any changes from the IDE, the commit operation will fail. We need to set the appropriate permissions on some directories to make it work.

Likely you and nginx work in the system under different users. In Debian and Ubuntu nginx runs under www-data , and you work as, for example, as user . The rest of this section is full of tautology, sorry .

On the development phase, the website’s files and directories usually belong to the user user , and user is free to modify it. To make user www-data also able to write to the specific directories, we need to make some simple steps:

  • Add user www-data to the user’s group. In modern Linux distros users belongs to eponymous groups, to by default a user user belongs to the group user. The following operation should be performed under user root:
# usermod -a -G user www-data
  • Modify the permissions for /st and /js subdirectiories. When a package committed, Amber stores there Smalltalk and JavaScript sources appropriately. The following operations should be performed under user user:
$ cd /opt/mysite
$ chmod 775 st js

The 775 mode allows to write into these directories for any users from any user ’s group. Not very secure, but enough for our needs. Since we’ve added user www-data to the group user , nginx process now can write there so commits should work.

A more sophisticated configuration

Suppose that we have a web application written on Smalltalk, Ruby, Python, or something else, and we want to use Amber there. Usually modern web frameworks are supplied with [their own] tiny HTTP servers to make the initial development and configuration easier. Not a problem! We still can serve WebDAV requests as well as static content (/images, JS and CSS files) with nginx and dynamic content with a web-application. Even more, separating the responsibilities is a good practice.

For example, our custom application runs on the same machine with nginx and uses port 8000. There are few changes required in the configuration:

server {
    root /opt/mysite;
    client_body_temp_path /opt/mysite/tmp;

    location ~ ^/(/images|js|st|css) {
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_access  group:rw all:r;
    }

    location / {
        proxy_pass http://localhost:8000;
    }
}

We consider that the static content is available under the following locations:

/opt/mysite/images
/opt/mysite/css
/opt/mysite/js
/opt/mysite/st

This is the default layout when using Amber. If the request will start with /images , /js , /st , or css , the underlying content will be served by nginx. We have used regular expressions for that. DAV methods are also allowed there (since by default Amber puts the files in /js and /st directories on commit).

All other requests will be proxied to our custom application. The only thing we will need to tune there it to include the Amber scripts in the pages generated by the custom application.

And even more sophisticated configuration

Imagine that our web application is growing and is becoming more complex, and it uses RESTful URLs for the dynamic content. For example, users of the web application can edit their profile data on page http://localhost/edit/profile .

If you will place Amber on a page under such location, you will fail to commit your Smalltalk and JavaScript code from the Amber IDE. The answer is easy: Amber will generate DAV requests and nginx will try to write the files into /opt/mysite/edit/st and /opt/mysite/edit/js directories.

Of course you can create this directories and any other directories for each level in your RESTfull web app. But since we already have /opt/mysite/st and /opt/mysite/js directories, I would like to store Amber sources for any page there. How we can achieve that?

Again, the solution is fairly easy. The updated nginx site configuration should look like:

server {
    root /opt/mysite;
    client_body_temp_path /opt/mysite/tmp;

    location ~ ^(/|/edit/)(/images|js|st|css) {
        rewrite /edit/(.*) /$1;
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_access  group:rw all:r;
    }

    location / {
        proxy_pass http://localhost:8000;
    }
}

Here, if the request starts with /edit and then continues as a normal Amber or static data request, we just drop the /edit part so the Smalltalk sources will be written into /opt/mysite/st directory, not into the /opt/mysite/edit/st . Note that GET requests like /edit/images/1.png will be also rewritten and in this case the file /opt/mysite/images/1.png will be accessed. To fix it, we just need to move rewrite and dav_* methods into a separate location, such as ^(/|/edit/)(js|st) .