Seaside supports continuation-based programming. In simple terms, this means that instead of having to throw away or explicitly save all local state after a web page is generated and recreating it all again on the next hit, you can pretend the web isn’t even involved. For example, a common sequence might look like:
Again, this code reads almost exactly as what it’s doing: Show the first page (probably containing a form), and when the page is submitted, determine if the form elements are valid. If not, keep showing the correction page until it comes back correct. Finally, show the last page.
Now, here’s the interesting part of that sequence: showFirstPage is clearly returning a pile of HTML back to the user (using canvas and brushes like we saw earlier). But as the page is being returned to the user, Seaside notes that it’s about to execute the second line of this method and transparently arranges for the form submission to restore this method execution right at that spot. If there were any local variables, they’d also be properly restored, and while in the second loop, execution continues in loop’s test on the way back. It’s as if the Web is completely out of the way.
This also works for anchors and input fields. You can attach server-side behavior directly to the elements, and Seaside arranges for the proper execution context to be saved and restored so that the behavior works properly. For example, an HTML anchor to log out could be:
html anchor callback: [self logout]; with: 'log me out'.
The dynamically generated URL for this anchor is associated with the block of code. When the link is followed, the callback is executed, with the proper object receiver state entirely in place.
Similarly, a text input field provides a callback to save the incoming value:
html textInput callback: [:e | aValue := e].
Seaside generates a unique parameter name for each element, and associates this particular callback block with that parameter name. When the form is submitted, the value is automatically de-entitized and passed in to the block, which is then copied to a local or instance variable of your choice. This is actually the lowest level of the mechanism; other frameworks live on top of this to make it even simpler to associate entire objects with form elements representing their instance variables with customized editors.
The key thing here is that you never need to figure out the name of your intra-application URLs or params, or how to save your state. Seaside just manages all of that for you.
During development, unhandled exceptions land you in the debugger, as described before. But you can use this continuation mechanism to solve the “unhandled exceptions in deployed code”, too. Simply write an exception handler that notifies the user that “something went wrong, but don’t worry, we already know about it”, store the debugging URL into your favorite bug tracking system, and tell Seaside to “persist the objects for this hit a bit longer than usual”.
Now, when you come in in the morning, you can review your bug track log, and see that you had two unhandled exceptions overnight. To debug, just follow that URL in the bug report, and you’re in the precise conditions (live debugging once again) from a hit from the night before. Marvelous.
Persistence and Scaling
Any non-trivial webapp needs to have persistent data, and scale well.
If your existing data is in a traditional SQL database, the open source GLORP object-relational mapper can be used to map objects onto database rows, generating 90 percent of the SQL directly for most applications. Cincom is building an ActiveRecord-style layer on top of GLORP, allowing much of the GLORP “glue” code to be autogenerated, provided your database follows some familiar conventions.
However, if you’re building your application from scratch and don’t need to interface with existing SQL databases, you can use various object persistence layers to avoid having to “rectangle-ize” your data. Squeak can easily save an object tree as a file, for example, and reload it later.
For larger solutions, the open source Magma package allows a client-server object store, using remote object calls to search for, retrieve, and update objects to and from files. The object store can be shared among multiple Seaside processes, even running on separate machines.
For really big solutions, the GemStone/S server is essentially a database that uses Smalltalk as stored procedures. When GemStone/S is running Seaside (as a stored procedure!), the created objects simply persist as if by magic and everything can be distributed and have high-availability fail-over. One example of a GemStone/S application involves 1.5 billion objects accessed by 150 offices all over the globe.
Seaside has an active development community, including developer and end-user mailing lists, IRC channels, and websites. Seaside packages include most modern AJAX toolkits (such as Scriptaculous and jQuery), and provide support for data meta-modelling (Magritte) and content management (Pier). You can even get free hosting for your Seaside application at seasidehosting.st. (Some restrictions apply, of course. See website for details.)
For more information on Seaside, visit the primary website at seaside.st. You’ll find FAQs and tutorials linked from there, and some interesting success stories.