Archive for the 'Smalltalk' Category

Small Scriptaculous API Change for Seaside 2.8

Yesterday I was upgrading one of my applications to the latest version of Scriptaculous and Seaside 2.8, at first everything seemed to go OK but shortly thereafter I noticed that some of the Ajax in the application had stopped working. After a bit of testing I traced the problem to multi element Ajax updates where I’m using the evaluator. Stuff like this occasionally happens so it was time for some investigation.

I cracked open an older image and checked the version I’d been using and started reading the commit comments for each version looking for clues. You can do this from SqueakSource but I usually just do it in Monticello directly. After a bit of digging I find in Scriptaculous-lr.232.mcz the information I’m looking for, namely…

- NOTE: SUElement>>#render: does not call #update: anymore, directly use #update:, #replace:, #insert:, and #wrap: now. These methods finally accept any renderable object (string, block, …) and also encode the contents correctly.

Seems Lukas changed the API to make things more intention revealing. A quick trip through the app looking for evaluators and changing #render: to #update: and everything started working again. Having made the necessary changes, and looking at the new code for a few minutes, I liked it and agree with API change.

What I want to point out is the importance of good commit comments (thanks Lukas) that allow those who use your frameworks to work out their problems. Commit comments are the best place to share your thoughts about why you changed something or decided to go in a particular direction because they are, or should be, the first thing a developer reads before loading a new version of that code.

I also what to point out the process itself. Being open source code, when things go wrong it’s often up to you to solve your own problems. Had I not found what I needed in the comments, I’d have started Googling and searching the archives of the Seaside-Dev list to see if anyone else had run into this issue. If that fails, then I’d post to Seaside-Dev asking for help.

There’s not a lot of documentation on Seaside and Scriptaculous in comparison to some other frameworks, but there’s plenty of help to be found with just a little bit of effort on your part to do your homework and a great community ready and willing to help you out when you need it. But always do your homework first, in case your question has been answered many times over.

13 April 2008 > Squeak Image Updated

Just a quick notification that I updated my squeak image.

It’s based on Damien Cassou’s latest Squeak Dev Image (Squeak 3.9.1), an awesome base image with all the necessary goodies a developer needs. This image is a bit smaller than previous ones because I’ve taken Glorp out since I’m not currently using it anymore. I’ve also dumped the windows native fonts and use different default fonts due to my working from both Windows and Linux these days.

rST (Remote Smalltalk) is now loaded because I plan on experimenting with it a bit. OSProcess is part of my default setup as well, on the Linux side, it’s quite useful.

Simple File Based Application Configuration

I thought this was too simple to bother posting about, but someone requested it, so here it is. Having made my journey to Linux about a year ago, I’ve found a new appreciation for the simplicity of plain old text files and the file system in general.

Some of the tools I’ve come to rely on like Daemontools from Daniel Bernstein also show this appreciation of the Unix way, small simple reliable tools that just work and are configured easily with plain text files. The most recent versions of Apache have also broken up the monolithic httpd.config into much smaller simpler individual files spread out over various directories that greatly ease automation by keeping individual site settings in separate files and using simple symbolic links to enable or disable individual sites.

Inspired by the simplicity and ease of managing these tools, I decided my Smalltalk programs, while ungodly easy to write, were much too hard to configure and deploy because configuration was based either on code in the image or in the case of Seaside, its custom configuration system, neither of which are simple or easy to move around between images and servers. I also like how Ruby on Rails configuration system allows multiple configurations that you can easily switch between to run your code in your various environments.

Rather than trying to design some ultimate configuration system, I just looked at how I write and deploy *my* applications, which is always one image per application in its own directory (possibly launched multiple times on various ports) and decided the simplest thing that could possibly work for me was just a directory named config, in my image directory, with three subdirectories named prod, dev, and test, and a single file named config which tells me which config subdirectory is active.

So I’ll declare a configuration class…

Object subclass: #SSConfig
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'SentorsaSeaside-Configuration'

and a class side #create method to setup this structure on the filesystem…

create
    | configDir |
    configDir := (FileDirectory default directoryNamed: #config) assureExistence.
    FileStream
        forceNewFileNamed: (configDir fullNameFor: #config)
        do: [ : f | f nextPutAll: 'dev' ].
    (configDir directoryNamed: #prod) assureExistence.
    (configDir directoryNamed: #dev) assureExistence.
    (configDir directoryNamed: #test) assureExistence

Next, I want a simple dictionary style API, “SSConfig at: #someSetting”, so I’ll need the core class side method #at: which grabs the current configuration and uses the key you hand it to looks for the file name that matches, opening it in read only mode if it exists or simply creating it if it doesn’t.

at: aKey
	| currentConfig configDir value |
	currentConfig := FileStream
		readOnlyFileNamed: ((FileDirectory default directoryNamed: #config) fullNameFor: #config)
		do: [ : f | f contentsOfEntireFile ].
	configDir := (FileDirectory default directoryNamed: #config) directoryNamed: currentConfig.
	value := (configDir fileExists: aKey)
		ifTrue:
			[ FileStream
				readOnlyFileNamed: (configDir fullNameFor: aKey)
				do: [ : f | f contentsOfEntireFile ] ]
		ifFalse:
			[ FileStream
				forceNewFileNamed: (configDir fullNameFor: aKey)
				do: [ : f | f contentsOfEntireFile ] ].
	^ (value endsWith: Character lf asString)
		ifTrue: [ value allButLast ]
		ifFalse: [ value ]

Next I want to be able to ask for a value and provide a default in case there is no configured value, built simply upon the previous method…

at: aKey default: aValue
    ^(self at: aKey)
        ifEmpty: [ aValue ]
        ifNotEmptyDo: [ : it | it ]

I also want to be able to ask for a config value typed to a boolean (SSConfig can: #useBlaBla) for the common case of true/false settings, another simple method built upon #at:…

can: aKey
    ^(self at: aKey) = 'true'

And of course, having strings and booleans, I now want a couple of simple methods for asking for integers from config…

numAt: aKey
    ^(self at: aKey)
        ifEmpty: [ 0 ]
        ifNotEmptyDo: [ : it | it asInteger ]

numAt: aKey default: aValue
    ^(self at: aKey)
        ifEmpty: [ aValue ]
        ifNotEmptyDo: [ : it | it asInteger ]

These are all class side methods, and so far I’ve not found the need for anything more than strings, bools, and integers. If I do, I’ll extend it more, however, this seems to be all I need for all of my configuration needs thus far. It’s simple, every setting is its own file, and it allows me to easily switch between various configs and easily share configurations between various images with nothing more than a simple file copy. I can also easily edit the configuration from the shell, where I do most of my administration tasks.

NOTE: This method is not application aware, so it won’t work well if you run multiple applications within a single image, but I’m not writing code for use cases I don’t have and it works well for how I deploy my applications.

Upgrading a Running Squeak Image

One of the challenges you’ll face when deploying an application in Seaside, especially if you’re running headless on a Linux box somewhere, is how to upgrade your production application to the latest version of your code. For the longest time, I’ve done this the easy way, upgrade the production image off-line, upload it, and restart the services. It’s simple and straight forward, but it blows away all active sessions and kicks off any users currently online. There are other more interesting ways.

Some people use VNC to connect to their production image, fire up Monticello, and hot upgrade the code. Personally, I’ve never been able to get a stable version of VNC going, there are always issues, the latest being that the client is messed up by the nice Squeak UI Enhancements hiding the menus. Too much hassle, I gave up, I don’t trust VNC at all.

There’s a simple web based method within Seaside under /tools/versionuploader, you can use the web version of Monticello to load packages into the running image and then save the image from there. It’s simple and effective, if not a bit clunky, but it works great if you’re willing to expose that to the internet wrapped with just basic http authentication. I’ve been using this, but I thought I could have a little fun hacking up something a little more interesting.

So what are we talking about here, just loading a few Monticello packages and saving the image, how hard could that be to automate? Turns out, not so hard if I use the Installer that comes in Damien’s dev image (that my image is based upon). I decide to make my images upgrade themselves from a staging directory where I can just secure copy or ftp up the new versions plus an empty trigger file named “load” to kick the process off. I’ve been playing with it a few days now, seems to work pretty well. Here’s what it takes to make a self upgrading image (assuming you use Monticello to package all your code, as I do).

It’s just two methods really, one to load all the packages deleting them after each one is installed, assuming the load file exists…

loadChanges
    | dir |
    dir := (FileDirectory default directoryNamed: 'load-package') assureExistence.
    dir fileNames
        select: [ : ea | ea endsWith: '.mcz' ]
        thenDo:
            [ : fileName |
            ((dir fileExists: fileName) and: [ dir fileExists: #load ]) ifTrue:
                [Transcript show: (fileName , ': ').
                Installer installFile: (dir fullNameFor: fileName).
                Transcript cr.
                dir deleteFileNamed: fileName ] ].
    (dir fileExists: #load) ifTrue:
        [ dir deleteFileNamed: #load.
        SmalltalkImage current saveSession]

And a second to kick off a background process that runs continually, say every few seconds, that kicks off #loadChanges.

watchForChanges
    [[ (Delay forSeconds: 5) wait.
    [ self loadChanges ]
        on: Error
        do: [ : error | Transcript show: error ] ] repeat ]
        forkAt: Processor userBackgroundPriority + 1
        named: ‘code loader’

Then I call watchForChanges in a class side #initialize somewhere on a utility class so the background process is kicked off automatically in any image I load my code into. Seems pretty slick so far, just upload the new packages, upload an empty file named load, the image upgrades itself, deletes all the files in the staging directory, saves itself, and chugs along like nothing happened at all. No session are lost, no users booted, if anything they might notice a slight pause for a bigger package loading.

This all of course assumes your production image is basically read only, i.e. has no code changes in it, as mine are. Enjoy!

Next Page »