smalltalk programming for the web

MorphIDE - Dynamic Static IDE for Jtalk

3 September 2011

I last played with Jtalk a while ago. Jtalk has seen quite some development since.

The Jtalk documentation suggests to set up Apache WebDAV to support committing changes to disk with the web-based IDE; in particular, to handle Jtalk IDE's HTTP PUTs.

I didn't want to reconfigure Apache on my MBP. It is simply far more fun to doodle in Smalltalk.

Existing Smalltalk goodies such as Comanche, Zinc and Swazoo make it very easy to set up a static content HTTP server. Previously, for Seaside, I used Comanche. This time, I use Zinc to serve Jtalk's Javascript files.

To handle PUTs, I subclassed Zinc's ZnStaticFileServerDelegate, calling it PNjtServerIDE. The following method is key:

PNjtServerIDE>>handlePut: aRequest
    | upath lfile |

    upath := aRequest uri path.
    lfile := self directory fullNameFor: upath.
    self directory deleteFileNamed: upath.
    NSFileStream fileNamed: lfile 
        forWriting: true
        do: [ :stream | 
            stream binary.
            stream nextPutAll: (aRequest entity contents) ].
    ^ ZnResponse created: (ZnUrl fromString: aRequest requestLine uri path).

With this, I can PUT code to disk from the Jtalk IDE. However, if I add a new category in Jtalk, it doesn't show up automatically the next time I run the IDE.

Browsing Jtalk's source reveals the loadJtalk() function. Essentially, if I create a category called "PN-Smalltalking", Jtalk will PUT separate "PN-Smalltalking.js" and "" files. If I want the category to show up when I run Jtalk, I should invoke loadJtalk(["PN-Smalltalking.js"]).

Somehow, it just feels very unsatisfying to edit manually the loadJtalk() call in some HTML file.

Thus was born MorphIDE.

MorphIDE provides a static HTML file, imaginatively called morphIDE.html, with a button that invokes Jtalk's IDE, just like the Jtalk project's home page does. morphIDE.html starts out with loadJtalk().

As new categories are added to Jtalk, MorphIDE, while handling the PUTs, keeps track of these additions, and dynamically generates new versions of the static morphIDE.html that invokes, accordingly, loadJtalk(['New-Category1', 'New-Category2', ...]).

Thus, each time Jtalk is loaded, my code in New-Category1, New-Category2, etc., are automatically available.

For MorphIDE to tell existing and new categories apart, it must know what the existing categories are. Without wishing to write too much code (hey, I was only doodling), I modified Jtalk's init.js thusly:

var stcategories = '';
smalltalk.classes()._do_(function(each) {
    stcategories += each._category();
    stcategories += '\n';
    url: 'stcategories.txt',
    type: 'PUT',
    data: stcategories

This generates a string containing Jtalk's categories, and uses MorphIDE's PUT machinery to upload the string into a file. Within MorphIDE, during the PUT, the content of this file is read and cached. In this way, MorphIDE comes to know about the Jtalk categories that exist when Jtalk initialized.

What remains is for MorphIDE to track new categories as they are created, and generate morphIDE.html accordingly.

For now, MorphIDE doesn't do much exception handling. It also overwrites each Javascript and Smalltalk source file as they are PUT. I do intend to extend MorphIDE to invoke git, fossil, etc. using OSProcess/CommandShell.