Skip to content

Dylan Web in 60 Seconds

This will be a series of posts about how to do web programming in Dylan. It was inspired by a version of the same thing written for Twisted Python. In Twisted, it is often the case that very little code is required to get something basic running, which was also an inspiration to create similarly simple APIs for the Dylan web infrastructure.

  1. Serving Static Content
  2. Serving Dynamic Content
  3. Static URL Routing

Fixing Ubuntu Unity

If you’re like me, you like to be able to see the controls that are part of the User Interfaces on your computer screen.  If you’ve recently upgraded to the Ubuntu Unity desktop you’ve realized that not everyone cares as much about this as you do.  Least of all the Unity developers.  Here are a few tips to help you restore some sanity to your Unity desktop.

  1. Scroll Bars
    Unity comes with scroll bars that hide themselves until you move the mouse over them.  This is stoopid.  To be fair, this isn’t a feature of Unity, because if you switch to a different window manager like xfce they’ll still be there.  Anyway,  do this to restore sanity:

    gsettings set org.gnome.desktop.interface ubuntu-overlay-scrollbars false

    Now log out and log back in.  Voilà!  Sanity restored!

  2. Application Menus
    Unity developers thought it would be awesome if you had no idea where the application menu was, so they moved it to the top task bar and made it invisible, unless you move the mouse over it by accident.  Nice going Unity developers!

    sudo apt-get remove indicator-appmenu

    Now log out and then back in.  Sanity restored!

  3. Task Switcher
    Naturally when you’re using the Alt-Tab task switcher you don’t give a care about being able to tell which application icon is highlighted, amiright?  Unity developers agreed!  Anyway, the easiest way I found to fix this problem (far easier than editing PNG files in gimp, thank you very much) is to log out and next to your name where you can login again is a cryptic icon.  Click it and select “Ubuntu 2D”.Sanity restored!

You’re welcome.

Static URL Routing

This is the third in a series of articles about web programming in Dylan. This example will show how URL routing works in the Dylan web server, and how to handle optional URL elements for your resources.

I will skip the library and module definitions since they’re essentially the same as in the previous examples, but they are included in the full code listing at the end.

In the Dylan web server, add-resource maps a URL to a resource. The default implementation of add-resource builds up a tree structure whose paths are defined by URL path elements and whose leaves are <resource> objects. (Idea stolen from twisted.web. I hope to add a simple regular expression based router in the future, for comparison.)

For this example we’ll use a hypothetical wiki as our web application and add three different URLs for it. First, we need a $wiki-app resource that will be the root of all wiki URLs, and specialized resource classes to provide behavior. We’ll implement page, user and group resources for the wiki:

 
define constant $wiki-app = make(<resource>);
define class <page> (<resource>) end;
define class <user> (<resource>) end;
define class <group> (<resource>) end;

Now wiki resources can be added as children of $wiki-app:

 
add-resource($wiki-app, "page/{action}/{title}/{version?}", make(<page>));
add-resource($wiki-app, "user/{action}/{name}", make(<user>));
add-resource($wiki-app, "group/{action}/{name}", make(<group>))

The URL path elements surrounded by curly braces are "path variables". Let’s decompose the first URL above: page/{action}/{title}/{version?}. The first element, "page" must be matched literally. The {action} and {title} elements are required path variables; if either is missing 404 is returned. The last element, {version?} is optional, as indicated by the ‘?’ character. (Two more path variable types that aren’t shown here are available: {v*} matches zero or more path elements and {v+} matches one or more.)

In order to define the behavior of our various resources we define methods on the respond generic function. Note that each path variable in the URL passed to add-resource corresponds to a keyword in the respond method for the resource being added. (For our purposes the behavior will be to simply display the values of all the path variables.)

 
define method respond
    (resource :: <page>, #key action, title, version)
  output("<html><body>action = %s, title = %s, version = %s</body></html>",
         action, title, version);
end;

The respond methods for <user> and <group> are similar. Notice that version may be #f but action and title will always be strings.

Lastly, we’ll connect $wiki-app to the root URL (/) and start the server:

 
define constant $server = make(<http-server>, listeners: #("0.0.0.0:8888"));
add-resource($server, "/", $wiki-app);
start-server($server);

That’s it. Run the server and click on some of these URLs to see the corresponding behavior:

Here’s the full code listing:

 
-----------library.dylan------------
Module: dylan-user
 
define library web60-static-routing
  use common-dylan;
  use koala;
end;
 
define module web60-static-routing
  use common-dylan;
  use koala;
end;
 
-----------static-routing.dylan------------
Module: web60-static-routing
 
define constant $wiki-app = make(<resource>);
 
define class <page> (<resource>) end;
define class <user> (<resource>) end;
define class <group> (<resource>) end;
 
add-resource($wiki-app, "page/{action}/{title}/{version?}", make(<page>));
add-resource($wiki-app, "user/{action}/{name}", make(<user>));
add-resource($wiki-app, "group/{action}/{name}", make(<group>));
 
define method respond
    (resource :: <page>, #key action, title, version)
  output("<html><body>action = %s, title = %s, version = %s</body></html>",
         action, title, version);
end;
 
define method respond
    (resource :: type-union(<user>, <group>), #key action, name)
  output("<html><body>action = %s, name = %s</body></html>",
         action, name);
end;
 
define constant $server = make(<http-server>, listeners: #("0.0.0.0:8888"));
add-resource($server, "/", $wiki-app);
start-server($server);

Previous: Serving Dynamic Content

Back to top

Broken shortcuts on gnome with dvorak

Let’s say you’re one of the three people who uses the dvorak keyboard layout and Gnome on Linux. (I apparently have a weakness for, how shall I say it, esoteric choices. c.f., my Dylan-related posts.) You try to use the Keyboard Preferences control panel to setup your keyboard layout switcher and suddenly notice your terminal windows keep disappearing on you. WTF?

You do some fiddling and eventually discover that even though your keyboard is nominally in dvorak layout, the keyboard shortcuts are still using qwerty layout and Control-d was really sending Control-d instead of the expected Control-e. Although some people claim this makes sense, I can only assume this was a mistake and those people are on crack. (Okay, maybe not crack…maybe they just don’t touch type, but is it really that different?)

It turns out that there’s a relatively easy solution IF you have both the “USA” and “USA Dvorak” layouts listed in your “Selected layouts”. Make sure “USA Dvorak” is listed first and “USA” is listed second. You can do this by removing the “USA” layout and re-adding it.

Intuitive, right?

As an added bonus to this solution, the keyboard switcher widget in the task bar now calls the “USA” layout “USA2″ and calls the “USA Dvorak” layout “USA”.

Happy dvoraking.

Serving Dynamic Content

This is the second in a series of articles about web programming in Dylan. This example will show how to dynamically generate the contents of a web page.

First, the ever-exciting library and module definitions. In addition to common-dylan and koala (the HTTP server) we need streams for writing data to the response and date so we can show something dynamic happening:

define library web60-dynamic-content
  use common-dylan;
  use io, import: { streams };
  use koala;
  use system, import: { date };
end;

define module web60-dynamic-content
  use common-dylan;
  use date, import: { as-iso8601-string, current-date };
  use koala;
  use streams, import: { write };
end;

A web page is a resource mapped to a URL inside the web server. To create a resource we subclass <resource>:

define class <clock-page> (<resource>) end;

To make our resource do something we define a method on respond. (If we only wanted to implement the GET request method we could define a method on respond-to-get instead.)

define method respond (page :: <clock-page>, #key)
  let stream = current-response();
  let date = as-iso8601-string(current-date());
  write(stream, concatenate("<html><body>", date, "</body></html>"));
end;

current-response() returns the active <response> object. To send data back to the client we write to the current response.

let server = make(<http-server>,
                  listeners: list("0.0.0.0:8888"));
add-resource(server, "/", make(<clock-page>));
start-server(server);

In the previous example we already saw how to create and start a server, so the new bit here is using add-resource to map a URL to a <resource>. The first argument to add-resource is the URL router. (In Routes terminology it would be a "mapper".) For convenience, an <http-server> is a kind of router so we can add resources directly to the server. In a future example, I will show how to do more complex URL routing, which will explain the reason for the mysterious #key in the respond definition above.

Here’s the complete code:

---- File: library.dylan ----
Module: dylan-user

define library web60-dynamic-content
  use common-dylan;
  use io, import: { streams };
  use koala;
  use system, import: { date };
end;

define module web60-dynamic-content
  use common-dylan;
  use date, import: { as-iso8601-string, current-date };
  use koala;
  use streams, import: { write };
end;

---- File: main.dylan ----
Module: web60-dynamic-content

define class <clock-page> (<resource>)
end;

define method respond (page :: <clock-page>, #key)
  let stream = current-response();
  let date = as-iso8601-string(current-date());
  write(stream, concatenate("<html><body>", date, "</body></html>"));
end;

let server = make(<http-server>,
                  listeners: list("0.0.0.0:8888"));
add-resource(server, "/", make(<clock-page>));
start-server(server);

Run this example and point your browser at http://127.0.0.1:8888/.

Previous: Serving Static Content

Back to top

Serving Static Content

This example will show you how to use the Dylan web server (koala) to
serve static content from the file-system. First I’ll go through the
process line by line and then will show the complete code.

First, we need an HTTP server. We’ll make it listen to all interfaces
on port 8888:

let server = make(<http-server>,
                  listeners: list("0.0.0.0:8888"));

Instances of <resource> are responsible for generating HTTP
responses. To serve static content we create a
<directory-resource> whose file-system directory is /tmp and which
allows directory listings:

let resource = make(<directory-resource>,
                    directory: "/tmp",    // c:\tmp on Windows
                    allow-directory-listing?: #t);

Next we connect the resource to a specific URL on the server:

add-resource(server, "/", resource);

Last, we start the server:

start-server(server);

If you wanted to start the server in a separate thread you could say
this instead:

start-server(server, background: #t);

The entire example, including the library and module definitions,
looks like this:

---- File: library.dylan ----
Module: dylan-user

define library web60-static-content
  use common-dylan;
  use koala;
end;

define module web60-static-content
  use common-dylan;
  use koala;
end;

---- File: main.dylan ----
Module: web60-static-content

define function main ()
  let server = make(<http-server>,
                    listeners: list("0.0.0.0:8888"));
  let resource = make(<directory-resource>,
                      directory: "c:/tmp",
                      allow-directory-listing?: #t);
  add-resource(server, "/", resource);
  start-server(server);
end;

main();

Run this example and point your browser at http://127.0.0.1:8888/.

Note that serving static content is one of the things built into koala itself, so if that’s all you need to do this will accomplish the same thing:

koala --listen 0.0.0.0:8888 --directory /tmp

My Soup

I have a new place to put links to things I think are worth keepin track of: http://carlgay.soup.io

Follow

Get every new post delivered to your Inbox.