Web Development with Smalltalk and Seaside Framework

While taking a part-time computer science class, I built a Smalltalk-based web app using the Seaside.st web framework.

The project was a software prototype for a restaurant ordering system. The goal was to design a user interface that allowed a customer in the restaurant to use their iPhone or some other smart phone to quickly order food.

For this prototype, I used Squeak Smalltalk, the Seaside web framework, and the Magritte meta-description framework.

Database in Smalltalk

As the database for this prototype, I used a class with only class methods. It used Dictionaries to store the data, just a plain key-store database.

I did not see a need to explore the object-oriented database systems available nor did I want to force the instructor of the course to setup an SQL database on his computer just to test this prototype.

Update 2019: Today I would create a database and connect the Smalltalk image and Seaside-based web app to a SQL or MongoDB instance.

Smalltalk Components and Tasks

There were several processes and screens that needed to be mapped to Seaside WAComponents and WATasks.

For example, placing an order is a process/task that will first force you to login, then allow you to browse a menu, and when you’re done ordering, it will place the order into the system for the kitchen staff to handle. Within that, there would be a “Login”, “Menu Selection”, “[Menu Selected] Items”, and “Review Order” screens/components. This mapping made it easy to figure out where to sub-class components and where to jump into different tasks.

Later on, I realized that Magritte might make things easier for me. I was trying to figure out how to model menu items with varying options, such as pizzas that came in only 2 different sizes, salads with a choice of dressings, and burgers with a choice of “toppings” (with tomatoes? with mayo?).

I wanted to write a generalized MenuItem class with a render method that could display check boxes or radio buttons depending on what was needed. This would have allowed me to add or remove option groups because what if the restaurant no longer wants to give people a choice of pizza sizes?

Then I realized that this was too customizable and cut back, because there was only one place where the menu item was viewed with its options.

In the end, I created a MenuItem class and then sub-classed it into PizzaItem, SaladItem, etc. with each one supporting different options.

In the ViewItem class, the renderContentOn: method would call other methods depending on the class of the menu item. So if the item’s class was PizzaItem, then renderPizzaItemOn: would be called.

Magritte’s Help

Magritte is a meta-description framework for the Seaside web framework. It lets you describe the model of the object you want to display and then use different views that render those descriptions. It makes it easier to display an object.

Instead of making an EditObjectView or a CreateObjectView, and then calling them, you just call the asComponent method on the Object, and Magritte will generate the HTML for you. The view that it uses is either a table- or CSS-based view.

I used Magritte to create the admin tool and the control panel where I modified the database. I defined the descriptions for all the objects and then let Magritte create the forms so I could create or edit them. For example, the Menu object had 2 class methods:

  • descriptionName
  • descriptionItems

Magritte will automatically look for class methods that start with “description” and use them as part of a default view (which is found in the class method description). On the control panel WAComponent, I rendered a list of menus and then had a link that said “Add Menu”, something like this:

renderContentOn: html
    html heading level1; with: 'Menus'.
    menus do: [ :menu | self renderMenu: menu on: html ].
    html paragraph: [ html anchor callback: [ self createNewMenu ]]

The createNewMenu class is where the Magritte-related magic happens and it looked like this:

    | menu |
    menu := self call: (Menu new asComponent addValidatedForm).
    menu ifNotNil: [ menus add: menu ]

Simple to use as you can see.

Seaside’s and Smalltalk’s Awesomeness

Seaside web framework was wonderful to work with. I found it slow at first but now that I’ve grown more used to it, it isn’t that bad and I feel like I could write some simple web applications much more quickly in Seaside/Smalltalk than in Django/Python.

There is not too much to say, the awesomeness of Seaside is subtle, such as automatic session creation, composable components, proper tasks, etc.

Seaside’s Problems

Seaside’s documentation is lacking. I couldn’t find comments for some components that I thought I wanted to use.

Seaside also has a precedence problem when you are HTML rendering. I ran into this when I tried to use radio buttons. When rendering a view in HTML, the order of the methods called matters. The with: method should be the last call made to an HTML tag object since it renders the object. I had reversed the order and was creating radio buttons like this:

renderContentOn: html
    | group |
    group := html radioGroup.
    group radioButton
        with: 'Some Text';
        value: 'Hello'.

That’s incorrect because the radio button will be rendered before the value: method is called on it. What I really should have done was this:

renderContentOn: html
    | group |
    group := html radioGroup.
    group radioButton
        value: 'Hello';
        with: 'Some Text'.

Smalltalk is Multi-Platform

Overall, I found working with Smalltalk awesome. I was able to work on the project on multiple platforms and operating systems without any changes. Using Ruby or Python or other languages usually requires tweaking of package/module/library paths and settings, but with the Smalltalk images that wasn’t a problem.

On each platform all I had to do was install Squeak Smalltalk. Then I had to copy the image over to the computer and run it. No messy setup of databases, text editors, version control. Just download, install and start the Smalltalk image for the project!

Magritte helped me avoid writing redundant admin/database management code. So it saved time and effort to complete the project.

Update 2017: I currently work with Ruby on Rails, and previously worked on frontend web apps using React, Angular, Elm and Ember.js. Before that I worked with Django. After all these web app frameworks, I still remember the joy of using Smalltalk’s excellent IDE and the Seaside.st web framework.