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.
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
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
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
renderPizzaItemOn: would be called.
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:
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 ]]
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.
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 for
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 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 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'.
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.