How I used Magritte, Seaside, and Smalltalk for a class project

In the Human-Computer Interfaces class I have been taking for the last few months, I had to write up a software prototype for a restuarant 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 Magritted meta-description framework.

Database

As the database for this prototype, I used a class with only class methods, which is the Singleton pattern, or something like that. In any case, it was some variables which were Dictionaries and that was it. I didn’t 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.

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 in the project timeline, 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 or whatever? Then I realized 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 PizzaItemSaladItem, 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, thenrenderPizzaItemOn: would be called.

Magritte’s Help

Magritte is a meta-description framework for Seaside. It lets you describe the model of the object you want to display and then you can use different views that render those descriptions. It makes it easier to display. 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 administrative side, 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:

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

Very simple to use as you can see.

Magritte’s Trouble

Unfortunately, Magritte still felt cumbersome to work with in some ways. To me, it felt easier to create custom classes for rendering and to write some renderContentOn: methods than to deal with figuring out how Magritte’s description objects worked and how to re-arrange them for sub-classes (such as forItem and PizzaItem). With some more thought and planning, it probably wouldn’t have felt so difficult.

One particular problem that I had was there weren’t too many tutorials and examples of Magritte usage, aside from some blog posts and a tutorial from the main website. I didn’t like the tutorial because it was a PDF and it was a bunch of slides from a presentation. The information density in a Powerpoint presentation is very low and you end up speaking in bullet-points but aside from that, it was helpful.

Seaside’s Awesomeness

Seaside 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 isn’t too much to say, the awesomeness of Seaside is subtle, such as automatic session creation, composable components, proper tasks, etc.

Seaside’s Problemness

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'.

Conclusion

Overall, I found working with Smalltalk awesome. I worked on 3 different platforms; in gNewSense on my desktop (a GNU/Linux distro), in Windows XP on my desktop, and in ArchLinux on my laptop. On each platform I just had to install Squeak and then copy the image over to the computer and run it. No messy setup of databases, text editors, version control, etc.

Magritte was very helpful and let me avoid writing redundant admin/database managment code. I’ll have to sit down and force myself to figure it all out for the next Seaside application I write.