June 28, 2007

Meaningful Seaside Links after Session Expiry

There’s something about Seaside’s URLs that has bothered me for a long time, and I just discovered a solution. I haven’t seen anything about this anywhere else, so I figured I’d write it down in case it’s useful to anyone else.

Since your URLs are managed by your Seaside session, they all become invalid when your session times out. Leave your browser alone for a while, then return and click a link. Instead of taking you where it said it would, the link ends up taking you back to the first page of the app.

The well-known pattern for creating “bookmarkable URLs” is part of the solution here. You override #updateUrl: in your component, and then you override #initialRequest: in your root component or one of its children (i.e. something that’s visible by default). However, that only solves the problem of returning to your current page and has nothing to do with meaningful links on your current page after your session expires.

For most applications, this sort of behavior is not a big deal. Your session times out, you click something, and you’re kicked back to the “please sign in” page. Users tolerate this, because they’re used to the idea that their computer shouldn’t stay signed in forever.

But on a public area of a site, this sort of behavior really can get frustrating. I often pull up a page, get interrupted, and don’t get back to the page for several hours or days. The SqueakSource site has done this to me more than once. Browse to a SqueakSource page, go have lunch, and then come back and click a link. You get shown the homepage instead. Argh!

Thankfully, there are hooks in Seaside that make meaningful links possible. But they’re not very well known, and I can’t find any reference to them anywhere online. The methods are on WAAnchorTag, named #extraPath: and #extraParameters:. These two methods allow you to mangle the URL on the front side, just like you can with #updateUrl: on the other end.

You can use them like this:

(html anchor)
 callback: [self registerVolunteer];
 extraPath: 'volunteer';
 with: 'Become a Volunteer!'

If you’re already using the hooks for bookmarkable URLs, then it’s simply a matter of putting in the right path or parameters when you build the link.

It’s entirely possible to build meaningful URLs in Seaside. The tools are all there. And even though it seems harder than in other frameworks, it’s really not. It just feels hard, because Seaside gives you such a nice abstraction the rest of the time.

And if you’re building a content management system, you’ll likely be creating an abstraction that hides these details anyway.

June 22, 2007

Alternatives to Model-Based Validation?

Rails has an decent set of validation rules for its database models. You can specify that particular variables must be present or satisfy certain constraints, either by themselves (“must be two characters long”) or in relation to each other (“password can’t be blank for administrators”). I’ve written several of my own extensions to these for validating phone numbers, email addresses, tracking numbers, and the like.

It works fine, at first. But model-based validation breaks down in several situations. Here are a few examples:

1. Wizard-like interfaces break the rules. By definition, a wizard decomposes the process into several steps. Your model objects are often invalid until you finish the last step. I have yet to see a solution to this. Most people end up adding some sort of knowledge about the wizard to the model’s validation code (“If I’m only asking for the person’s name and address right now, don’t try to validate his email address”).

2. In Rails, you can’t save your models unless they satisfy all of the validity conditions. This problem probably won’t arise at first, but as your code base evolves, it can have some nasty side-effects. Like when a user can’t change his password, because the model has lately been changed to require a variable that used to be optional.

Seaside itself provides nothing to help you with validation. Search some archives and you’ll find people recommending Magritte, which is a metadata descriptor system for model objects. It was written by Lukas Renggli, who is one of the lead developers of Seaside itself.

Magritte improves the problematic situations above in a couple of significant ways. For one, Magritte descriptions can be configured on any object, not just on database models. In Seaside, a wizard is likely to be implemented with several components (one per page), so a corresponding model could be built with just the validation rules it needs. Additionally, Magritte is completely separate from your persistence layer, so you are free to save your model objects regardless of whether Magritte’s validation rules are satisfied.

But I’m still not satisfied with the approach. Model-based validation has a bad smell to me, though in some limited ways I am sure it is useful. But the more I look at systems that work this way, the more I see what looks like UI-specific code in the model. Should your model really be concerned whether the user has entered matching strings for “password” and “password confirmation”? What if I want to use real first-class objects to represent a phone number? Validation should happen before the data ever gets to the model. It strikes me that validation belongs in the form, not in the model. In Seaside, that means that validation would happen at the component level.

So I’m looking for a better way. How are others handling validation in Seaside? Beyond that, how do other frameworks help with form validation? The most intriguing thing I’ve seen so far is the “newforms” framework in Django, which couples form inputs to their validation.

It seems that this would be fairly easy to implement in Seaside. You could use blocks to make its behavior pluggable. If you end up using one particular form in several places, make your own subclass of Form and reuse it. Or if you only need it once, build the Form instance programmatically.

Are there any other frameworks that provide validation hooks but are not model-based?

February 12, 2007

My Full-Circle Journey Back to Smalltalk

I suppose it’s time to tell my story. I was a Smalltalk zealot in the late 90’s, but I left it behind when I started my own business. I’ve now come full-circle, and I’m finding once again that Smalltalk is the best tool for most of the programming work I do.

I learned Smalltalk programming from Travis Griggs while working at Key Technology. He mentored me in Smalltalk and other general programming methods, and I introduced him to Linux. Travis likes to give me more credit than I deserve for some of the work that we did together, but one thing was certain: we had a blast working together.

Key went through a difficult aquisition, and I spent most of my energy working to bring our two software teams to common ground. My wife and I travelled back and forth between Medford, Oregon and our home in Walla Walla (with our newborn son, our first) while I worked with developers in both locations. Diplomacy is not something I particularly enjoy, but I’m good at it and the team really needed somebody in that role.

But the end result was that I didn’t get much time to do any significant programming. The more time passed, the more I missed it. I didn’t get to work with Travis as much anymore, and our team was becoming fractured as our project list grew. Despite all of my efforts to bring people together, our team was dissolving under the increasing project load.

I had always wanted to work from home, and I was becoming burned out in my Key position, so I left Key and took up consulting work. Most of my clients needed web applications, so I became a web developer.

I built my first web applications in Smalltalk. Travis and I had written the original WikiWorks code, and I used our experience there to build my own HTTP 1.1 server. This later became the basis for the Swazoo server, which was built on my code at Camp Smalltalk 2000 in San Diego. I implemented my own Smalltalk Active Pages (abbreviated SAP, to distinguish them from ASP sites) and built several sites using them.

At the time, there wasn’t much web application work being done in the Smalltalk world, and the frameworks people were developing were unattractive to me. I had to support myself, from top to bottom, and this was starting to feel painful.

This was what drew me towards PHP, Apache, and PostgreSQL. The promise of safety in numbers was attractive — I would never have to wonder if my server was fully compliant with the HTTP specs. But the main attraction for me was PHP’s support for for various open source libraries. PHP made it easy to handle image uploads in multiple formats, draw custom graphs, and build other visuals on the fly. Editing PHP apps in vim was easier over low-bandwidth connections (like a cell phone data connection) than making a VNC connection to a headful server image, too.

So I left Smalltalk behind and built a few large PHP apps. Nearly all of them relied heavily on PHP’s graphics library bindings for generation of custom reports and editable page components. I built content management systems that let my customers edit their page titles, for example, and behind the scenes PHP would generate new navigation buttons from template PNGs and TrueType fonts. Another app managed a database of donors for a non-profit radio network and allowed call-center volunteers to easily enter pledges from supporters — while the studio crew saw up-to-the-minute totals on their screens.

Things went smoothly at first but got rougher as the applications got bigger. I made heavy use of the flimsy object model in PHP 4 and built my own object-relational layer, and this helped me survive as long as I did. But I grew increasingly frustrated as I tried to scale my PHP applications. There was no way around the ugliness of the code.

seaside.jpgAt that time, somebody pointed me at one of the early versions of Seaside. I had taken a look at Seaside a few years ago, and while it looked interesting, I confess that I didn’t get it. I saw how call: and answer: worked to give you a certain level of modalism in your application, but that was all I saw. The Seaside HTML writers were less mature then, too, and that was a deterrent to me. I liked being able to interleave PHP and HTML code in template files, but that was mostly because my pages didn’t use CSS effectively.

I missed the larger picture, so I went back to PHP.

When Ruby on Rails started to gain popularity, it was like a breath of fresh air to me. Rails is a very clean framework for web applications, and it required no paradigm shift. Much of the work I had done in PHP already followed some of the Rails patterns, but now I had a full-fledged object-oriented language at my disposal again. I thought I had finally found the best of both worlds — a thriving community, lots of support for graphics formats, easy database connectivity, and a nice clean dynamic language.

These days, that would sound like the end of the story. But it’s not. The truth is, I’m disappointed with Ruby. Rails is “classic web development done very cleanly”, as Ramon Leon says. And Ruby is my favorite non-Smalltalk language. But it’s not Smalltalk. In fact, I’ve come to see that Ruby missed the mark on several points.

  1. File-based source control. In a Smalltalk image, all of the classes and code libraries your application needs are available to you immediately. Tools like the Refactoring Browser are much harder, if not impossible, in Ruby.
  2. Reflection. Ruby’s reflection is OK, but it’s much weaker than the reflection in Smalltalk. If more of the Ruby base code was itself written in Ruby, and Ruby allowed better reflection, a Ruby code browser would undoubtedly be easier to write (and more powerful once written).
  3. Speed. The Ruby interpreter is slow. In Rails, I still end up writing more complicated database queries than I should have to, because the Ruby interpreter is so slow to process data in memory.
  4. No Keyword Messages: Travis and I ran some tests using keyword-style messaging using Ruby’s last-argument-collapsed-as-hash, and the performance was terrible.
  5. Blocks. Ruby simply missed the mark on on these. All blocks should be first-class objects, not just a layer of syntax.
  6. Live Interaction. Irb is good, but Smalltalk workspaces are much better.
  7. Debugger. The Ruby debugger feels like gdb compared to Smalltalk. Most of the time, people opt to debug using console output and interactive exploration using Irb. It’s better than printf, but not much.
  8. Class Library: The Ruby class library is much younger than the extensive class library in Squeak or VisualWorks.

As far as Rails is concerned, I have far fewer criticisms. But once you’ve looked at Seaside, and you really understand the approach Seaside is taking, Rails just looks old. I never want to marshal data through a URL again if I can help it.

I’ve also come to see that unless you’re a light user of a technology, you have to be able to support yourself. This is not a downside for Smalltalk, it’s a reality of the complex environment we work in. Smalltalk’s web and graphics tools have come a long way lately, but ultimately Smalltalk empowers you to support yourself in a way that Ruby just doesn’t. I have been digging in the Ruby 1.8 source tree more often than most, and it’s not nearly as easy to navigate as the Smalltalk base. Remember, most of the Smalltalk base is implemented in Smalltalk.

Seaside is certainly more advanced than Rails in terms of raw technology, to the point that it’s hard to wrap your head around the concepts. It took me two attempts, and I’m fairly young and intimately familiar with Smalltalk. Rails is smoother — it gives a better out-of-the-box experience in the short term. But for larger web apps, you need to be thinking further than that.

Don’t be surprised if Seaside just looks weird to you at first. Leave it for a little while and then come back. You’ll be glad you did. And when it clicks for you, beware. You won’t ever look back.

February 8, 2007

Identity and Equality in Ruby and Smalltalk

One important concept in object-oriented languages is the difference between equality and identity. The concept isn’t complicated, but the English words we use to talk about it are imprecise. The thesaurus says that “identity” and “equality” are synonyms. So let’s back up and make sure we’re being clear on the concepts.

We often talk about variables as if they “hold” a certain object, but that’s not a very good metaphor. You can put the same object “inside” any number of different variables. If we’re working with containment as a metaphor, we run into problems here. It’s as if we’re saying that the same person is in two different houses at the same time.

Instead, we need to think of variables as names. Different people use different names for the same thing (Puma, Mountain Lion, Cougar) or the same name for multiple things (my “home” is not the same house as your “home”). Names are just a reference to something real, a way to address things.

We say that two variables are identical when they both refer to the same object. If they refer to different objects that represent the same value, we say that they are equal. Consider, for example two points that have the same X and Y coordinates. They are equal, but not identical. Objects themselves can tell you whether they’re identical or equal.

In Smalltalk, the relevant messages are:

= The objects are equal
== The object(s) are identical
hash A number that must the same for all equal objects, typically used by collections

The Smalltalk virtual machine is the sole place where object identity can be determined, so the #== method is implemented as a primitive and never overridden by any class. But scores of classes implement their own method for #= and #hash. A classic Smalltalk programming mistake is to override #= but not #hash, thus breaking the requirement that two equal objects have the same hash value.

In Ruby, there are actually significantly more equality messages that you may encounter. The definitions I’m using here come from pp. 95 and 571 of Programming Ruby:

== Test for equal value
=== Used to compare each of the items with the target in the when clause of a case statement
<=> General comparison operator. Returns -1, 0, or +1, depending on whether its receiver is less than, equal to, or greater than its argument.
.eql? True if the receiver and the argument have both the same type and equal values. 1 == 1.0 returns true, but 1.eql?(1.0) returns false.
.equal? True if the receiver and argument have the same object ID
hash Generates a Fixnum hash value for this object. This function must have the property that a.eql?(b) implies a.hash == b.hash.

This is a little overwhelming. Ruby’s :equal? method is a test for identity, and like Smalltalk, it is implemented in Object and never overridden. That gets us started.

The methods for :eql? and :hash are probably the most similar to Smalltalk. If you override one, you need to override the other. Ruby’s collections use them to determine equality for things like Array#uniq or Hash lookup keys.

This has bitten me more than once. I’ve implemented a new sort of object and overridden ==, only to find later that

  • I can’t use those objects for Hash keys, and
  • Arrays return unexpected results when sent :uniq

It seems that instead of overriding ==, I should have overridden eql? and hash. This makes my Hashes and Array#uniq results work like I expected. But wait… The default Ruby implementation of == defers to equal?, which is not what I want either:


>> g1 = GridAddress.new(:key)
=> #<GridAddress:0xb7b569c8 @key=":key," @row="nil">
>> g2 = GridAddress.new(:key)
=> #<GridAddress:0xb7b50028 @key=":key," @row="nil">
>> g1.eql?(g2)
=> true
>> g1.hash == g2.hash
=> true

>> g1 == g2
=> false

There’s no deferral from one of these messages to another. In other words, the behavior I want requires me to override eql?, hash, and ==. These are independent, parallel implementations. The difference is primarily semantic. With eql, there is no attempt to coerce the objects to a similar type. Methods for ==, on the other hand, typically try to coerce objects to the same types first before making comparisons.

Finally, we come to the “spaceship” operator, <=>. This is used by a mixin called Comparable, and you automatically get various other comparison operators (including ==) if you include Comparable and implement <=>. The only objects that understand it are those for which less-than and greater-than comparisons actually make sense.

In my opinion, Ruby complicates things here with little payoff. Most programs compare using ==, and the details of other equality comparisons are lost on most Ruby programmers. I think it’s a fair generalization that most Ruby developers don’t use their own objects as hash keys, probably due to a sort of bias towards the more “primitive” object types like Ramon Leon talks about (see “Obsession with Simple Types” in this post).

It’s also worth noting that if you override eql? or ==, you’re expected to check to make sure that the objects have the same type before you start comparing any details. This is the common pattern in Smalltalk, too. Most Smalltalk implementations of #= first check in some way to see that the two objects are the same kind of thing and then proceed to compare relevant details.

February 6, 2007

First Post

I don’t normally think of myself as the type of person who would do this. Writing for nobody in particular, sending emails to myself, speaking in front of groups… these are images that I associate with blogging. Even the word blogging seems strange coming from me, like teenage slang from a grandparent.

Beyond that, there’s the permanence of it all. What I type in this glowing box may be stored somewhere forever. Now, I’ve lived long enough to realize that I’m not always right. Why should I go on the record about — well, anything — if there’s no need?

Nevertheless, here I am. Why? I’m not really sure yet. Part of my motivation is simply to try something new. Learning is something that I hope I never outgrow. And if somebody learns something useful from one of these posts, I will have repaid a small part of the large debt I owe to other authors in various media.

That’s enough to put my fears of archival to rest, at least for now. If you’re linked on this site, consider yourself thanked many times over.

I just hope none of this comes back to haunt me later.