Building advanced web applications is notoriously hard and complex. The current state of the art is a modified version of the model-view-controller paradigm, implemented with some kind of server pages, web actions and an object model. But even using the best framework in an advanced environment, there are some major problems. The request-response cycle of HTTP remains visible, the statelessness of HTTP requires often complex session management, form processing remains an annoying, repetitive task and there is no such thing as components that can be combined freely.
Enter Seaside, a Web Application Framework for Squeak Smalltalk. In this article we describe our first experiences exploring this framework. First we tried a very simple web application to compute factorials. Next we did something harder: we implemented a simple calculator as a web application. We were absolutely amazed at how easy it was: much, much easier than it would be in any other framework that we know of. We believe Seaside offers a dramatic improvement in abstraction and productivity.
Smalltalk, invented in 1972, is considered by many as the best object oriented programming environment. Smalltalk is a system that consists of a programming language, a large library of reusable objects, a integrated development environment, many end-user applications and a virtual machine. The main principles behind Smalltalk are simple:
That's it: the rest of Smalltalk follows from these principles, by following them to the end. Consider the following snippets of code:
The one aspect of Smalltalk that will unsettle most people is the way source and object code is handled. There simply are no source files, nor individual object files. In Smalltalk you work in an image, you browse to individual classes and work with one method at the time. Everything you do in Smalltalk is considered a change and is logged as such. When you're done, you save your image to keep your work for the next round. You should think of writing software in Smalltalk as adapting and extending an existing system so that it is now capable of doing what you want it to do.
Squeak is an open-source Smalltalk implementation in the best tradition of the original Smalltalk-80 standard. Squeak runs bit-identical on many platforms and is efficient enough to run multimedia applications. It is out of the scope of this article to try to explain all the things that Squeak can do, or to try to convince you of how great Squeak is - just try it out. Squeak does look and feel a bit weird, but remember that literally everything can be changed. There is also some lack of proper written documentation, but the internet and the source code will be your guide.
Figure 1 shows Squeak with some of its IDE functionality in action.
The large green window is a System Browser looking at the class Collection,
more particulary at the method #inject:into:.
The Workspace window at the bottom (a scratch area)
shows a test expression typed in there by the programmer.
The programmer selected the expression (highlighted in green) and
asked Smalltalk to evalute it and open an inspector on the result:
that is the 'SmallInteger: 45' window.
Seaside is an innovative and advanced Web Application Framework for Squeak Smalltalk. Seaside hides the HTTP request-response cycle and models the entire user session as a continuous piece of code, with natural, linear control flow. A web application consists of number of composable components with standard call-return semantics. Seaside can handle the backtracking and parallelism inherent to web browsers. An HTML generation framework and an advanced call-back mechanism for links, actions and forms further simplifies development.
The first example we (in true Pair Programming tradition, my colleague Hein Saris and myself) tried was simple: a web user interface to compute factorials. The first step is to make a new subclass of WAComponent, WAFactorial. We add a number instance variable to track the number that we start from. All WAComponents need a #renderContentOn: method to generate their HTML representation. This method is passed an WAHtmlRenderer object that helps you to generate HTML. See listing 1 for the implementation of this method for WAFactorial.
Without going into too much detail, we'll describe what is going on here. The first line just puts a heading 1 on the page. The second line wraps a block inside a form (the action is handled transparently). Inside the form, we place a text input field with as value our own number value, and as action a block that will store the new number value in ourself. This callback will be executed when the form is submitted. Finally we add a submit button with an action block that will be executed when form is submitted. What the block does is interesting: it sends a string like '5!=120' as argument with #inform:. Now what #inform: does is call out to another component to display the result. This other component will have an 'OK' button to simply return to the caller.
renderContentOn: html
html heading: 'Factorial Computation'.
html form: [
html
textInputWithValue: self number callback: [:v | self number: v];
submitButtonWithAction: [self inform: self number asString, '!=', self number factorial asString] text: '!'].
This is the whole web application. Note that we didn't see any request or response objects, nor did we directly see HTML. If we were to write the same interface for a normal GUI, the code would be similar (and probably longer). All we need to do is use the tool under /seaside/config (another Seaside application of course) to define a new web application with WAFactorial as main component. Figure 2 shows how we configured an application around WAFactorial.
All Seaside applications have a URL like the one in figure 2: the first number is the session id, the second number tracks the progress or control flow in the session (to support backtracking). Most Seaside applications are configured to have a toolbar at the bottom. This is accomplished by wrapping your component inside a containing component. The toolbar allows you do to some amazing stuff: like inspecting the actual Smalltalk objects behind the application, or browsing and modifying the code behind the application! Like any great framework, Seaside gives us an immense amount of functionality for free - all we did was make one subclass and implement one method.
We decided to test Seaside by trying to implement a simple four-function calculator as a web application. We started by implementing a Calculator object that models the calculator: it has methods for all keys (like #digit:, #operator:, #dot and #clear) and holds the state of the calculator (like what is the number on the display, what is the last operator pressed, and so on). It is actually a pretty simple class (see the code in resources).
Being good programmers, we immediately wrote some unit tests to see if the calculator did what we expected it to do. Should we have done true Test Driven Design, we should have implemented the tests before the implementation. Figure 2 shows a Squeak session where a programmer is working on the unit tests for Calculator. Two windows are visible. The first window is a SystemBrowser on the class CalculatorTest, showing the method #testDotAdditionOperatorHit. You can clearly see how the calculator object is being tested. The second window is an SUnit Test Runner. The class CalculatorTest is selected and all its 8 tests ran successfully.
renderContentOn: html
html divNamed: #calculator with: [
html heading: calculator display.
html divNamed: #numpad with: [
html divClass: #row with: [
#(7 8 9) do: [:i | html anchorWithAction: [calculator digit: i] text: i asString]].
html divClass: #row with: [
#(4 5 6) do: [:i | html anchorWithAction: [calculator digit: i] text: i asString]].
html divClass: #row with: [
#(1 2 3) do: [:i | html anchorWithAction: [calculator digit: i] text: i asString]].
html divClass: #row with: [
html anchorWithAction: [calculator digit: 0] text: '0'.
html anchorWithAction: [calculator dot] text: '.'.
html anchorWithAction: [calculator operator: #=] text: '=']].
html divNamed: #operations with: [
#(#+ #- #* #/) do: [:x |
html divClass: #row with: [html anchorWithAction: [calculator operator: x] text: x asString]].
html divClass: #row with: [html anchorWithAction: [calculator clear] text: 'C'.]]].
html tag: #p do: [
html text: 'If you make a mistake, you can use your browser''s back button to undo your last action']
Next came the web component WACalculator, yet another subclass of WAComponent. This time, our web component class holds a calculator model object in an instance variable. The most important method is again #renderContentOn: as shown in Listing 2. Apart from the syntax that maybe needs some getting used to, the basic idea is very simple. For each calculator key, we create a link that has a callback that sends one of the 'key' methods to the calculator model. The rest of the code organizes the links in rows and specific areas by wrapping them in divs.
initialize calculator := Calculator new. self session registerObjectForBacktracking: calculator.
By registering our calculator model object for backtracking (see listing 3), Seaside starts to perform some of its magic. It now becomes possible to use your browser's back button to effectively undo key clicks on the calculator. No matter how many steps you backtrack, Seaside keeps everything consistent.
A Seaside component can specify the style sheet to use through a method #style on WAComponent. The actual style sheet (CSS) code is longer than our #renderContentOn: method and can be found in the download in the resources section. The final result is shown in figure 3. You can find a live calculator at the following URL: http://blogs.inextenso.com/seaside/calculator/.
We 'discovered' Seaside only recently. After just one day of experimenting with it we were convinced about the dramatic improvement in abstraction and productivity it offers. We were absolutely amazed at how easy it was to do the examples described here: much, much easier than it would be in any other framework that we know of. We believe Seaside could be a killer application for (Squeak) Smalltalk.
The source code of the examples discussed in this article if available for free as a category file out: Seaside-Sven.st. Apart from Seaside's place on the web: http://beta4.com/seaside2/, the following blog was started by someone learning Seaside: http://blogs.inextenso.com/seaside/blog/learning (the blog runs as a Seaside application which is absolutely great).