<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Something to Talk About &#187; Seaside</title>
	<atom:link href="http://kentreis.wordpress.com/category/seaside/feed/" rel="self" type="application/rss+xml" />
	<link>http://kentreis.wordpress.com</link>
	<description>Pure and Simple</description>
	<lastBuildDate>Thu, 08 Oct 2009 04:36:52 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='kentreis.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/0a3c531eed0ca4e8b119892e3534995f?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>Something to Talk About &#187; Seaside</title>
		<link>http://kentreis.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://kentreis.wordpress.com/osd.xml" title="Something to Talk About" />
		<item>
		<title>Deep SIXX with XMLPullParser</title>
		<link>http://kentreis.wordpress.com/2009/06/01/deep-sixx-with-xml-pull-parser/</link>
		<comments>http://kentreis.wordpress.com/2009/06/01/deep-sixx-with-xml-pull-parser/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 23:11:38 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[GLASS]]></category>
		<category><![CDATA[Seaside]]></category>
		<category><![CDATA[Smalltalk]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/?p=77</guid>
		<description><![CDATA[At our company, we develop our GLASS apps in Pharo and then deploy to a GLASS repository on one of our servers, so we sometimes need to copy model objects from one environment to the other. One of our applications also performs regular imports from a third-party database, so we fetch it into a 32-bit Squeak [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=77&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>At our company, we develop our <a href="http://seaside.gemstone.com">GLASS</a> apps in <a href="http://www.pharo-project.org">Pharo</a> and then deploy to a GLASS repository on one of our servers, so we sometimes need to copy model objects from one environment to the other. One of our applications also performs regular imports from a third-party database, so we fetch it into a 32-bit Squeak image via ODBC and push it up into the GLASS repository from there.</p>
<p>We needed a platform-independent serialization format, and <a href="http://www.mars.dti.ne.jp/~umejava/smalltalk/sixx/index.html">SIXX</a> fit the bill. It works in Squeak/Pharo right out of the box, and there&#8217;s an <a href="http://seaside.gemstone.com/ss/SIXX.html">official GemStone port</a> courtesy of <a href="http://gemstonesoup.wordpress.com">Dale Henrichs</a> and <a href="http://selfish.org">Norbert Hartl</a>.</p>
<p>The only problem we&#8217;ve had is that <strong>SIXX reading consumes a lot of memory</strong>. In Pharo, we have sometimes had to raise the maximum VM heap size. In GemStone, we were bumping up against the default VM temporary object memory ceiling. Dale deals with this issue in general in <a href="http://gemstonesoup.wordpress.com/2008/11/19/gemstone-101-managing-out-of-memory-situations/">an excellent blog post</a>. The size limit on temporary object memory is configurable, but the real solution is &#8230; well, not use so much temporary memory.</p>
<p>For SIXX in particular, Dale modified the SIXX reader to use a persistent root for storage of SIXX objects during read, and he posted a script[<a href="http://kdt.s3.amazonaws.com/Blog/2009-05-29/sixx.gs">1</a>] to the mailing list that auto-commits when you approach the ceiling. This moves your temporary objects to permanent storage, kind of like using swap space. t&#8217;s like using swap space. You&#8217;re out of RAM? The OS will save some of your pages to disk and load them on demand.</p>
<p>OK, I know it&#8217;s more complicated than that, but that&#8217;s the basic idea.</p>
<p>Even using this approach, our ODBC import process was still hitting temporary memory limits. I confess that I didn&#8217;t spend much time analyzing the situation. Instead, I decided to throw a new tool at the problem: XMLPullParser.</p>
<h3>XMLPullParser</h3>
<p>Now before I go further, I should mention that XML is not exactly a passion of mine. When I have spare brain cycles, I don&#8217;t spend them on this sort of thing. There are XML-related acronyms that I couldn&#8217;t even define for you, much less explain. So if I get some details wrong here, please correct me in case somebody else cares about them.</p>
<p><a href="http://www.cincomsmalltalk.com/userblogs/antony/blogView">Antony Blakey</a> built an <a href="http://www.cincomsmalltalk.com/userblogs/antony/blogView?entry=3368495147">XML parser for VisualWorks</a> with a <a href="http://www.xmlpull.org/">pull-based API</a>. He describes it in detail in his blog post, so I won&#8217;t go into much detail here. Essentially, your application drives the parsing process (that&#8217;s the &#8220;pull&#8221;) rather than having the parser try to notify you of what it found (&#8220;push&#8221;). The application pulls events from the parser, where events are things like &#8220;tag opened&#8221;, &#8220;text&#8221;, &#8220;tag closed&#8221;, like having a kid read the XML to you one piece at a time.</p>
<p>&#8220;What&#8217;s next, Johnny?&#8221;<br />
&#8220;Uh, &lt;/person&gt;&#8221;<br />
&#8220;OK, if the next is a &lt;politician&gt;, skewer it.&#8221;</p>
<p>It&#8217;s a depth-first traversal, and it can be done on the fly without first loading the entire DOM. This means that you can read arbitrarily large XML files without high parser overhead.</p>
<p>Now, in Antony&#8217;s implementation, he simply wrapped the VisualWorks SAX parser&#8217;s output with a stream. This got him the API he wanted, but his hope was to eventually &#8220;really pull, without the SAX hack&#8221;.</p>
<p>With his permission, I ported XMLPullParser to Squeak, and it&#8217;s now <a href="http://www.squeaksource.com/XMLPullParser.html">available on SqueakSource</a>. In my port, I mashed his work together with the XMLTokenizer class from YAXO, so the <strong>Squeak version really does pull</strong>.</p>
<p>The implementation is probably incomplete, but it&#8217;s parsed everything I&#8217;ve thrown at it so far. If you find a missing capability, you can probably just copy a method from XMLTokenizer &#8212; simply change senders of &#8220;next&#8221; to &#8220;nextChar&#8221;.</p>
<p>There are a few simple test cases in the package, but please don&#8217;t look to them for a good example of how to drive the parser. They use the lowest-level &#8220;what&#8217;s next&#8221; API to test the tokenizing only. Real-world usage of the parser involves higher-level operators like match:take:, if:peek:, etc.</p>
<pre>parseResponseFrom: stream
  | parser |
  parser := XMLPullParser parse: stream.
  parser next.
  parser match: 'Response'
    take:
      [parser if: 'Errors'
        take:
          [parser while: 'Error' take: [errors add: parser text].
          ^self].
      parser whileAnyTake: [:tag | ... ]].</pre>
<p>There are better examples out there, but hopefully this gives a little taste of what the pull parsing API feels like.</p>
<h3>Adapting to SIXX</h3>
<p>Back the problem at hand: how to attach this to SIXX. The SIXX code is fairly indifferent to the actual XML parser used, with all parser-specific details handled through a subclass of SixxXmlParserAdapter. But the entire SIXX framework expects that you&#8217;ll be dealing with fleshed out DOM nodes, so I had no choice but to modify some core parts of SIXX itself.</p>
<p>My goals were to keep the SIXX <span style="text-decoration:line-through;">damage</span> modifications to a minimum, so I had to make some tradeoffs. But with the changes described below, I was able to get all of the SIXX features working except one: truncated XML recovery. And the unit tests indicate that it still works when running against YAXO.</p>
<p>The current version of this SIXX fork is on SqueakSource in the XMLPullParser project.</p>
<h3>Initial Results</h3>
<p>Let&#8217;s get pathological for a few minutes here. I have a 98MB SIXX file (standard SIXX mode, not compact) representing model objects from a small application in Pharo. If we log free space at several points during a simple read, we can tell a little about the actual memory used:</p>
<pre>|rs root|
"1" rs := SixxReadStream readOnlyFileNamed: 'models.sixx'
"2" [root := rs next] ensure: [rs close].
"3" rs := nil. "4"</pre>
<p>If we take the free space at &#8220;1&#8243; as our baseline, then we can use the following rough interpretations:</p>
<ul>
<li>Baseline minus free space at &#8220;2&#8243; is the DOM and stream overhead</li>
<li>Baseline minus free space at &#8220;3&#8243; is the space used by the DOM, root model and stream</li>
<li>Baseline minus free space at &#8220;4&#8243; is the actual memory consumed by the root model we loaded.</li>
</ul>
<p><strong>Run 1: Pharo/YAXO:</strong> DOM and stream overhead is 441 MB (!), the load took 14 minutes on my 2.33GHz Core 2 Duo laptop. It turns out that the root model consumes about 18MB in Pharo. Yes, SIXX in standard mode turns this into 98MB, which is a pretty low signal to noise ratio.</p>
<p><strong>Run 2: Pharo/XMLPullParser</strong><strong>:</strong> DOM and stream overhead is 2KB, and the load took 17 minutes. We took 3 minutes longer, which may come from the more spotty I/O (we&#8217;re not reading the entire file at once) and the extra compare/become phase (see below). But it <strong>saved us 440 MB</strong> of memory.</p>
<p>One other note on Run 2: The root model consumed a little over 24MB instead of the 18MB it took in Run 1. This is a consequence of the way we build collections in my tweaked version of SIXX; each growable collection has more empty space. More details below.</p>
<p>In GemStone, the test isn&#8217;t quite as simple, because the situation isn&#8217;t quite so simple.</p>
<p><strong>Run 3: GemStone/YAXO:</strong> Dale&#8217;s script loads the XML string on the server, then launches the SIXX reader. I ran it and analyzed the memory usage using statmonitor/vsd.</p>
<p><span style="color:#0000ee;text-decoration:underline;"><a href="http://kentreis.files.wordpress.com/2009/06/vsd-yaxo.png"></a><a href="http://kentreis.files.wordpress.com/2009/06/vsd-yaxo.png"><img class="alignnone size-full wp-image-92" title="VSD graph: SIXX Load with YAXO" src="http://kentreis.files.wordpress.com/2009/06/vsd-yaxo.png?w=500&#038;h=153" alt="VSD graph: SIXX Load with YAXO" width="500" height="153" /></a></span></p>
<p> </p>
<p>What you see here is a graph of the VM temporary memory usage (in red) and auto-commit occurrences (spikes in cyan). The process took almost exactly 10 minutes.</p>
<p><strong>Run 4: GemStone/XMLPullParser:</strong> With the XMLPullParser, we can use the same XML string load and auto-commit handler from Dale&#8217;s script but replace the guts of the SIXX load with the following: </p>
<pre>rs := SixxReadStream on: (ReadStream on: (UserGlobals at: #SIXX_LOAD_STRING)).
(UserGlobals at: #SIXX_PERSISTENCE_ARRAY) add: rs contextDictionary.
System commitTransaction ifFalse: [ nil error: 'Failed commit - persisting cached objects' ].
rootObject := rs next.</pre>
<p>(Putting the SIXX context dictionary in a persistent root is the same trick the current GemStone port uses when you use Object class&gt;&gt;readSixFrom:persistentRoot:. The object graph gets saved </p>
<p>The statmonitor/vsd analysis now looks like this:</p>
<p><a href="http://kentreis.files.wordpress.com/2009/06/vsd-xpp.png"><img class="alignnone size-full wp-image-93" title="Graph of memory usage with SIXX/XmlPullParser" src="http://kentreis.files.wordpress.com/2009/06/vsd-xpp.png?w=500&#038;h=153" alt="Graph of memory usage with SIXX/XmlPullParser" width="500" height="153" /></a></p>
<p> </p>
<p>Things started out similarly while we loaded the file, but then memory usage climbed in a much more tame pattern, just as we expected. Auto-commits occur when the size of the model itself is too large to hold entirely in temporary memory. Also, the whole load happened in 9 minutes instead of 10. Why is this? Somebody who knows more about GemStone internals will have to answer specifically, but it no doubt involves the overhead of moving objects back and forth.</p>
<h3>Conclusion</h3>
<p>The benefits of using this sort of parsing approach are pretty obvious. In both environments, you can load a much larger object graph using SIXX this way without either raising memory ceilings or &#8220;swapping&#8221; to permanent storage. For my pathological case, the swapping was still necessary but far less of it was needed.</p>
<p>If anyone is interested in the GemStone port of this work, I&#8217;ll put it up on GemSource. Since all of my initial work was done in Pharo, and the GemStone port of SIXX has departed from SIXX 0.3 in several key ways, bringing my branch into GemStone has been an adventure. It works for me, but it has a couple of key test failures that I haven&#8217;t had a chance to fix yet.</p>
<h3>Gory Details</h3>
<p>As I mentioned above, SIXX delegates the actual XML element interpretation to a subclass of SixxXmlParserAdapter. Messages sent to SixxXmlUtil class forward to the parser adapter as needed.</p>
<p>This is a good start, but it assumes that you&#8217;ve already got fleshed out DOM element nodes in hand. In fact, the entire SIXX architecture expects this, with the parser adapters doing little more than return sub-elements from them, fetch attributes from them, etc.</p>
<p>All of the SIXX methods for instance creation and population take an argument, called &#8220;sixxElement&#8221;, representing the DOM element in whatever parser framework you use. In my case, I chose to use the entire parser as the sixxElement. The parser knows the current element, so implementation of the forwarders for element name and attribute access were easy enough.</p>
<p>Next, I had to add hooks for tag consumption, essentially letting the SIXX framework indicate when it was done processing a particular tag event. Other parser adapters does nothing with these, but the XMLPullParser adapter advances its stream upon receipt of these messages. There were only a couple of places in the core SIXX framework where I had to hook these in.</p>
<p>SixxReadStream expected to stream over a whole collection of top-level DOM elements, so it had to be replaced. I built a custom SixxXppReadStream and augmented the parser adapter framework to allow for custom read stream classes. SixxXppReadStream allows every operation that SixxReadStream does except #size. Many streams can&#8217;t tell you their size anyway, so I didn&#8217;t consider this a major loss.</p>
<p>Next, I had to get rid of any place where SIXX asked for all sub-elements of the current node. In most cases, the pattern was something like:</p>
<pre><span style="font-family:Georgia;line-height:19px;white-space:normal;">(</span>SixxXmlUtil subElementsFrom: sixxElement)
  do: [:each | ... ]</pre>
<p>This was converted to a more stream-friendly pattern of #subElementsFrom:do:, which the XMLPullParser could implement as a further traversal, but other cases weren&#8217;t so straightforward.</p>
<p>When SIXX creates an object, it first instantiates it, registers it in a dictionary by ID (for later reference by other objects), then populates it. This lets SIXX deal with circular references, but it creates a problem for on-the-fly creation of collections. In the happy world of fully-populated DOM elements, the creation step can create a collection that&#8217;s the proper size by counting sub-elements. Then during the population step, it uses #add: or #at:put: to fill it in.</p>
<p>We don&#8217;t have the luxury of being able to look down the DOM tree twice, so in this case I have the instantiation step return an empty collection. If we&#8217;re dealing with a growable collection (Set, OrderedCollection, etc) then all is good. But if this is an Array, for example, the population step can optionally return a different object &#8212; the real object. If we detect that it&#8217;s different from the original, we use #become: to convert references from the empty object to the fully populated one.</p>
<p>Why do it this way? In GemStone 2.3, <a href="http://gemstonesoup.wordpress.com/2008/10/30/gemstone-101-allinstances-become-and-friends/#become">self become: other is not allowed</a>, which is why the #become: is triggered based on the return value instead of being implemented in the collection population method itself. This means that every populating method needs to return self, and we pay performance penalties for the identity check and #become:.</p>
<p>The other consequence is that our collections aren&#8217;t created with perfectly-tuned sizes (e.g. Set new: 25). Instead, they grow like normal, so they will inevitably have more internal &#8220;empty space&#8221;. In my Pharo tests, the model was 38% bigger. To me, this isn&#8217;t a very big deal; these collections will likely grow in the future anyway. We could solve it by more complex creation (e.g. store all elements in a temporary collection, then create final objects using #withAll: and such), but the extra code doesn&#8217;t seem worth it.</p>
<h3>Credits</h3>
<p>Everything useful that I&#8217;ve ever learned about GemStone has come from the documentation (which is excellent) or has been spoon-fed to me by Dale Henrichs and Joseph Bacanskas. Thanks everyone.</p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/77/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/77/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/77/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=77&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2009/06/01/deep-sixx-with-xml-pull-parser/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>

		<media:content url="http://kentreis.files.wordpress.com/2009/06/vsd-yaxo.png" medium="image">
			<media:title type="html">VSD graph: SIXX Load with YAXO</media:title>
		</media:content>

		<media:content url="http://kentreis.files.wordpress.com/2009/06/vsd-xpp.png" medium="image">
			<media:title type="html">Graph of memory usage with SIXX/XmlPullParser</media:title>
		</media:content>
	</item>
		<item>
		<title>Mold: Form Validation for Seaside</title>
		<link>http://kentreis.wordpress.com/2008/08/27/mold-form-validation-for-seaside/</link>
		<comments>http://kentreis.wordpress.com/2008/08/27/mold-form-validation-for-seaside/#comments</comments>
		<pubDate>Thu, 28 Aug 2008 06:11:22 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Seaside]]></category>
		<category><![CDATA[Smalltalk]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/?p=13</guid>
		<description><![CDATA[A long time ago, I asked about systems for form validation that aren&#8217;t &#8220;model-based&#8221;. By &#8220;model-based validation&#8221;, I mean that the rules for whether a certain sort of input is acceptable are declared in (or attached to) a domain model that the form is operating on. This is the way that ActiveRecord (Rails) and Magritte (Seaside) [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=13&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>A long time ago, I <a href="http://kentreis.wordpress.com/2007/06/22/alternatives-to-model-based-validation/">asked</a> about systems for form validation that aren&#8217;t &#8220;model-based&#8221;. By &#8220;model-based validation&#8221;, I mean that the rules for whether a certain sort of input is acceptable are declared in (or attached to) a domain model that the form is operating on. This is the way that ActiveRecord (Rails) and Magritte (Seaside) work.</p>
<p>I don&#8217;t like the whole approach, for reasons I discussed in my earlier post. It breaks down when you want to edit an object in stages, and you end up managing lots of UI-related things down in the model code. But forms are boring and validation can be tedious, which is why frameworks like these have been built in the first place.</p>
<p>What I really wanted was a set of simple helpers that made this work easier &#8212; a &#8220;less is more&#8221; approach to the problem. That didn&#8217;t exist (at least in Seaside), so I built my own.</p>
<p>It&#8217;s called &#8220;Mold&#8221;, and it&#8217;s available both on SqueakSource and in the Cincom Public Repository. The name is a play on words: it&#8217;s a synonym for &#8220;form&#8221;, but it makes most normal people think of green fuzzy fungae. </p>
<h3>Design Principles</h3>
<p>In building Mold, I had several specific goals:</p>
<ol>
<li><strong>No new components</strong>. I didn&#8217;t want to have to remember to add anything to #children just because I was using this framework.</li>
<li><strong>Keep track of what the user types</strong>, even if it&#8217;s not valid, to allow for easy correction of mistakes. &#8220;r56&#8243; might not parse to a valid integer, but it&#8217;s better to let the user delete the &#8220;r&#8221; than force it to &#8220;0&#8243; and complain at him.</li>
<li><strong>Emit real objects</strong>, not just strings. If I&#8217;m asking the user to enter a date or a time, I want a Date or Time object when all is said and done.</li>
<li><strong>Use block-based validation criteria</strong> to keep things flexible. Error messages should allow blocks to be used too, so that your can put dynamic behavior in there too.</li>
<li><strong>Correlate errors closely to the problematic fields</strong>. It&#8217;s more helpful to show an error message right next to the field than to show it at the top of the page.</li>
<li><strong>Strip leading and trailing whitespace</strong>, and convert empty strings to nil. Semantically, an empty string almost always means &#8220;nothing&#8221; and is rarely worth keeping around.</li>
<li><strong>Callback or dictionary-like access to valid data</strong>. Sometimes you want to grab bits of data out of the form by name, but most of the time it&#8217;s nice to have the form dump valid results right into your model in one step.</li>
<li><strong>Don&#8217;t require all-or-nothing use</strong>. I might want to use the helpers to build part of the form, but handle other parts myself. It should be possible to completely customize the look of each form without sacrificing the benefits of the framework.</li>
</ol>
<h3>The Basics</h3>
<p>To use a mold, you typically instantiate and hold a Mold in an instance variable of your component. For a simple form with no underlying model, you might build the mold when the component is initialized.</p>
<pre>initialize
  super initialize.
  self buildMold</pre>
<p>For editors with a model under the hood, it makes sense to build the mold when the model is passed in:</p>
<pre>account: anAccount
  account := anAccount.
  self buildMold</pre>
<p>The mold itself has a canvas-like API for declaring a form&#8217;s inputs and validation conditions.</p>
<pre>buildMold
  mold := Mold new.
  (mold stringField)
    label: 'Username:';
    on: #username of: account;
    beRequired.
  (mold passwordField)
    label: 'Password:';
    on: #password of: account;
    beRequired. </pre>
<p>In this simple form, we only ask for 2 things, and we hook them directly to accessor methods on the model using #on:of:. This works just like it does in a regular Seaside form, and behind the scenes it simply creates a callback block. You can also create a custom callback block yourself.</p>
<p>When it comes time to render your form, you have to hand the `html` canvas to the mold, and then it gives you lots of handy shortcuts. Basic use will look something like this:</p>
<pre>renderContentOn: html
  html form:
    [mold canvas: html.
    mold paragraphs.
    (html submitButton)
      callback: [self save];
      value: 'Save']</pre>
<p>This usage tells the mold to render those fields as HTML paragraphs, like so:</p>
<p><img class="size-full alignnone" src="http://kentreis.files.wordpress.com/2008/08/picture-1.png?w=181&#038;h=166" alt="Generated with Mold, using the #paragraphs helper" width="181" height="166" /></p>
<p><em></em></p>
<p>The labels are real HTML &lt;label&gt; tags, and each group is a single paragraph (&lt;p&gt;&lt;label for=&#8221;&#8230;&#8221;&gt;Username:&lt;/label&gt;&lt;br /&gt;&lt;input &#8230; /&gt;&lt;/p&gt;) We could have also used #tableRows:</p>
<pre>renderContentOn: html
  html form:
    [mold canvas: html.
    html table:
      [mold tableRows.
      html tableRow:
        [html
          tableData;
          tableData: 
            [(html submitButton)
             callback: [self save];
             value: 'Save']]]]</pre>
<p>It&#8217;s more work to build the framework around the table, but the end result looks like this:</p>
<p><span style="text-decoration:underline;"><img class="alignnone size-full wp-image-25" src="http://kentreis.files.wordpress.com/2008/08/picture-21.png?w=252&#038;h=93" alt="" width="252" height="93" /></span></p>
<p>These are the only two &#8220;canned&#8221; looks for an entire mold, but it&#8217;s also possible to take the reins yourself and ask Mold to render single components for a completely custom look. More on that below.</p>
<p>The final step in using this mold is hooking up the #save callback. Let&#8217;s assume you&#8217;re using the super cool <a href="http://onsmalltalk.com/programming/smalltalk/sandstonedb-simple-activerecord-style-persistence-in-squeak/">SandstoneDb</a> framework to save your models:</p>
<pre>save
  mold isValid ifFalse: [^self].
  account save: [mold save].
  self answer </pre>
<p><span style="font-family:'Lucida Grande';line-height:19px;white-space:normal;">That&#8217;s all there is to it. The mold can tell you whether its inputs were valid, and if not, it will display error messages on subsequent renders. If it is valid, telling it to save will fire all of its callbacks, thereby applying the changes to the underlying model.</span></p>
<p>The way I use Glorp, the save method looks nearly identical, but you have to register your model object in a unit of work. Using the mold&#8217;s save actions to apply your changes inside the unit of work keeps Glorp&#8217;s deep dark change-tracking voodoo working.</p>
<pre>save
  mold isValid ifFalse: [^self].
  self database inUnitOfWorkDo:
    [:db |
      db register: account.
      mold save].
  self answer</pre>
<h3>Improving Looks</h3>
<p>Let&#8217;s look at what interactions with the mold look like. We declared that both fields should be required, so if you don&#8217;t type anything (and just click &#8220;save&#8221;) you&#8217;ll see error messages by each field:</p>
<p><a href="http://kentreis.files.wordpress.com/2008/08/picture-3.png"><img class="alignnone size-full wp-image-26" src="http://kentreis.files.wordpress.com/2008/08/picture-3.png?w=461&#038;h=160" alt="" width="461" height="160" /></a></p>
<p><span style="font-weight:normal;">This form is a little bland, and it&#8217;s spacing is awkward because Mold uses unordered lists inside those table cells. Let&#8217;s apply some simple CSS:</span></p>
<pre>style
  ^'
label.required { font-weight: bold; }
label.required:after { content: "*"; color: red; }
.error { background-color: #ecc; }
.errors { color: red; margin: 0; }
' 
<span style="text-decoration:underline;"><img class="alignnone size-full wp-image-29" src="http://kentreis.files.wordpress.com/2008/08/picture-5.png?w=443&#038;h=100" alt="" width="443" height="100" /></span></pre>
<p><span style="font-family:'Lucida Grande';line-height:19px;white-space:normal;">That looks better &#8212; we call attention to the required fields, error messages are shown in red, and fields with errors have a reddish background too. Let&#8217;s make the username field a little wider. We&#8217;ll do this by adding a #customize: block in the mold declaration:</span></p>
<p> </p>
<pre>buildMold
  mold := Mold new.
  (mold stringField)
    label: 'Username:';
    on: #username of: account;
    customize: [:tag | tag size: 40];
    beRequired.
  (mold passwordField)
    label: 'Password:';
    on: #password of: account;
    beRequired. </pre>
<p><span>Now the next time we build one of these components (remember, we built the mold when the component was initialized, so it won&#8217;t automatically be rebuilt just from a browser refresh), our form will look like this:</span></p>
<div><a href="http://kentreis.files.wordpress.com/2008/08/picture-6.png"><img class="alignnone size-full wp-image-30" src="http://kentreis.files.wordpress.com/2008/08/picture-6.png?w=410&#038;h=102" alt="" width="410" height="102" /></a></div>
<p> </p>
<h3>More Conditions and Inter-Relationships</h3>
<p>Let&#8217;s modify the field a little further. If we require usernames have to be at least 3 characters long and passwords to have a digit in them, we need some more conditions on these fields. We should also make the user type the password twice to guard against typos.</p>
<p> </p>
<pre>buildMold
  | passwordField confirmPasswordField |
  mold := Mold new.
  (mold stringField)
    label: 'Username:';
    on: #username of: account;
    customize: [:tag | tag size: 40];
    beRequired;
    addCondition: [:input | input size &gt;= 3] labeled: 'Usernames must be at least 3 characters long'.
  (passwordField := mold passwordField)
    label: 'Password:';
    on: #password of: account;
    beRequired;
    addCondition: [:input | input matchesRegex: '.*\d.*']
      labeled: 'Please make sure your password has at least one number in it'.
 (confirmPasswordField := mold passwordField)
    label: 'Confirm Password:';
    beRequired.
  passwordField
    addCondition: [:input | input = confirmPasswordField input]
    labeled: 'Passwords did not match'.
  confirmPasswordField
    addCondition: [:input | input = passwordField input]
    labeled: 'Passwords did not match'. </pre>
<div><span>There&#8217;s a bit more going on here, but it&#8217;s all pretty straightforward to use. A few things to note:</span></div>
<div>
<ol>
<li>We&#8217;ve added conditions on required fields, so these won&#8217;t be evaluated unless some input is actually given. If these fields were optional, we&#8217;d have to check `input` to make sure it wasn&#8217;t nil before asking it for its size.</li>
<li>The fields can refer to each other, even out of order. We didn&#8217;t technically have to put conditions on both fields, but it makes the error messages look nicer if we do.</li>
<li>There is no callback on the confirmation field. It simply exists for use by the main password field.</li>
<li>When referring to the other fields, we asked them for their #input. This is the string the user typed (having been trimmed of leading and trailing whitespace and converted to nil if it was empty). We could have also asked for its #value, but the value is only valid when the field is valid (incidentally, fields also understand #isValid).</li>
</ol>
<p>The resulting form looks like this:</p>
<div><a href="http://kentreis.files.wordpress.com/2008/08/picture-71.png"><img class="alignnone size-full wp-image-32" src="http://kentreis.files.wordpress.com/2008/08/picture-71.png?w=499&#038;h=93" alt="" width="499" height="93" /></a></div>
<h3>One &#8220;Gotcha&#8221;</h3>
<p>Under the hood, the out-of-order field processing is done with a hidden input with a low-priority callback. This is hooked up when the mold&#8217;s canvas is set (mold canvas: html). The callback doesn&#8217;t fire until all of the other input&#8217;s callbacks are processed, but it fires before the action callback from the button.</p>
<p>That means that you must set the mold&#8217;s canvas inside the form: [] block. Failure to do so will mean that your validations never get run, and the mold will always answer `true` when sent #isValid.</p>
<p>There might be a cleaner way to do this, but I haven&#8217;t found it yet.</p>
<h3>Advanced Moldiness</h3>
<p>Mold fields can also be given a symbol for a key, which lets you refer to them directly. This makes custom rendering possible, with messages like:</p>
<pre>mold paragraph: #confirmPassword.
mold widget: #username
mold errors: #someOtherField</pre>
<p>When you use #on:of: to hook up a callback, the key for the widget is automatically assigned to the selector you passed as the first argument. You can also set the key directly using #key:, and a subsequent send of #on:of: will not clobber it.</p>
<p>Error messages can also be blocks, which means that you can put the user&#8217;s input into the error message:</p>
<pre>(mold stringField)
  key: #username;
  addCondition: [:input | (Account find: [:each | each username = input]) notNil]
    labeled: [:input | 'The username "', input, '" is already taken'].</pre>
<p>There&#8217;s also no reason why you can&#8217;t add two or more molds to a component &#8212; say, one for basic settings and one for advanced settings that aren&#8217;t shown unless the user clicks &#8220;more choices&#8221;.</p>
<p>Adding fields is a matter of adding a new protocol on Mold and optionally a new Field subclass. I say &#8220;optionally&#8221; because some fields can be built as specialized versions of existing fields, like a string field with a default validation rule. The emailField is currently implemented this way.</p>
<h3>In Summary</h3>
</div>
<p>I know not everyone believes that model-based validation is a problem. But if all you&#8217;re looking for is a simple way to build a custom form, you might find Mold helpful. We&#8217;ve been using it internally for nearly a year, so I figured it was time to touch it up and share it.</p>
<p><span style="font-family:'Lucida Grande';line-height:19px;white-space:normal;">Mold makes no requirements of your components or your model objects. It doesn&#8217;t use any metadata; it allows you to choose how you want each form rendered; and it doesn&#8217;t require you to use it to build the entire form. It&#8217;s just a helper, and it gets out of your way when you don&#8217;t need it. </span></p>
<p>Just don&#8217;t leave it alone in a cool dark place for too long, or it might start to grow on you. :)</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/13/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/13/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/13/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/13/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/13/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/13/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/13/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/13/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/13/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/13/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/13/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/13/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=13&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2008/08/27/mold-form-validation-for-seaside/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>

		<media:content url="http://kentreis.files.wordpress.com/2008/08/picture-1.png" medium="image">
			<media:title type="html">Generated with Mold, using the #paragraphs helper</media:title>
		</media:content>

		<media:content url="http://kentreis.files.wordpress.com/2008/08/picture-21.png" medium="image" />

		<media:content url="http://kentreis.files.wordpress.com/2008/08/picture-3.png" medium="image" />

		<media:content url="http://kentreis.files.wordpress.com/2008/08/picture-5.png" medium="image" />

		<media:content url="http://kentreis.files.wordpress.com/2008/08/picture-6.png" medium="image" />

		<media:content url="http://kentreis.files.wordpress.com/2008/08/picture-71.png" medium="image" />
	</item>
		<item>
		<title>Showing a &#8220;Session Expired&#8221; Notice in Seaside</title>
		<link>http://kentreis.wordpress.com/2008/01/22/showing-a-session-expired-notice-in-seaside/</link>
		<comments>http://kentreis.wordpress.com/2008/01/22/showing-a-session-expired-notice-in-seaside/#comments</comments>
		<pubDate>Tue, 22 Jan 2008 19:08:48 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Seaside]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/2008/01/22/showing-a-session-expired-notice-in-seaside/</guid>
		<description><![CDATA[When a Seaside session expires, many of its links become invalid. Sure, you can use the hooks to make certain URLs bookmarkable, but most URLs are session-specific out of necessity. When you click on one after the session expires, Seaside kicks you back to the starting point for the application with no explanation whatsoever.  [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=19&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>When a Seaside session expires, many of its links become invalid. Sure, you can <a href="http://kentreis.wordpress.com/2007/06/28/meaningful-seaside-links-after-session-expiry/">use the hooks to make certain URLs bookmarkable</a>, but most URLs are session-specific out of necessity. When you click on one after the session expires, Seaside kicks you back to the starting point for the application with no explanation whatsoever.  It&#8217;d be more polite to tell the user why this happened.</p>
<p>Hat tip to <a href="http://leftshore.wordpress.com">Boris Popov</a> for the initial implementation.</p>
<p>In Seaside, the class WAApplication (via its superclass, WARegistry) knows when an expired session is being accessed, so in order to implement a notice like this, you first have to make your own custom WAApplication subclass.</p>
<p><code>WAApplication subclass: #MyApplication<br />
&nbsp;&nbsp;instanceVariableNames: ''<br />
&nbsp;&nbsp;classVariableNames: ''<br />
&nbsp;&nbsp;poolDictionaries: ''<br />
&nbsp;&nbsp;category: 'MySeasideStuff'<br />
</code></p>
<p>When a session expires, the core handling happens in WARegistry. But it gives us a chance to specify the URL path we want to use when something expires. In this case, we&#8217;ll tack the keyword &#8220;expired&#8221; onto the end:</p>
<p><code><b>MyApplication&gt;&gt;expiryPathFor: aRequest</b><br />
&nbsp;&nbsp;^aRequest url , '/expired'<br />
</code></p>
<p>Once this is done, you have to register your Seaside application as an instance of MyApplication instead of WAApplication. This is how I did it:</p>
<p><code><b>MyRootClass class&gt;&gt;applicationNamed: aString</b><br />
&nbsp;&nbsp;| application |<br />
&nbsp;&nbsp;application := MyApplication named: aString.<br />
&nbsp;&nbsp;application configuration addAncestor: WARenderLoopConfiguration new.<br />
&nbsp;&nbsp;application preferenceAt: #rootComponent put: self.<br />
&nbsp;&nbsp;^application<br />
</code></p>
<p>Then it&#8217;s just a matter of implementing the proper handling in your root component or one of its initial presenters. In my case, my root component holds a task, and the task calls a login component to prompt for username and password. The login component is already set up to display error messages, so we&#8217;ll use that here:</p>
<p><code><b>MyTask&gt;&gt;initialRequest: aRequest</b><br />
&nbsp;&nbsp;super initialRequest: aRequest.<br />
&nbsp;&nbsp;(aRequest url last: 7) = 'expired'<br />
&nbsp;&nbsp;&nbsp;&nbsp;ifTrue:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[loginDialog error: 'Your session has expired. Please sign in again.']<br />
</code></p>
<p>This works the way we intend, but it has a side effect. Since we are using [self session expire] when a user clicks a &#8220;logout&#8221; link, even legitimate logouts look like a session expiration. We end up showing our expiration message every time somebody logs out, which is not what we want.</p>
<p>Instead, we need to force a redirect after a legitimate logout:</p>
<p><code><b>MySession&gt;&gt;logout</b><br />
&nbsp;&nbsp;self expire.<br />
&nbsp;&nbsp;self redirectTo: self currentRequest url.<br />
</code></p>
<p>Now we change our logout callbacks, and we&#8217;re done!</p>
<p><code><b>MyComponent&gt;&gt;renderContentOn: html</b><br />
&nbsp;&nbsp;(html anchor)<br />
&nbsp;&nbsp;&nbsp;&nbsp;callback: [self session logout];<br />
&nbsp;&nbsp;&nbsp;&nbsp;with: 'Logout'.<br />
</code></p>
<p>We can test this by logging into the application, clicking the logout link, and then using the back button to get back to an application screen. Clicking any link on the page takes us to the back to the initial screen with the new notice shown.</p>
<p><img src="http://kentreis.files.wordpress.com/2008/01/sessionexpired.png" alt="Dialog showing expired session message" /></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/19/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/19/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/19/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=19&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2008/01/22/showing-a-session-expired-notice-in-seaside/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>

		<media:content url="http://kentreis.files.wordpress.com/2008/01/sessionexpired.png" medium="image">
			<media:title type="html">Dialog showing expired session message</media:title>
		</media:content>
	</item>
		<item>
		<title>Seaside Presentation at 3CLUG</title>
		<link>http://kentreis.wordpress.com/2007/12/06/seaside-presentation-at-3clug/</link>
		<comments>http://kentreis.wordpress.com/2007/12/06/seaside-presentation-at-3clug/#comments</comments>
		<pubDate>Thu, 06 Dec 2007 22:18:43 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Seaside]]></category>
		<category><![CDATA[Smalltalk]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/2007/12/06/seaside-presentation-at-3clug/</guid>
		<description><![CDATA[Travis Griggs and I will be giving a Seaside presentation titled &#8220;Lay Rails to REST&#8221; for the Tri-Cities Linux Users Group this coming Saturday (December 8th, 2007). If you&#8217;re in southeastern Washington state and interested in learning more about Seaside, and how it compares to Rails in particular, stop by West 248 at the WSU [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=18&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><a href="http://www.cincomsmalltalk.com/userblogs/travis/blogView" title="Travis Griggs' Blog">Travis Griggs</a> and I will be giving a Seaside presentation titled &#8220;Lay Rails to REST&#8221; for the <a href="http://www.3clug.org" title="3CLUG Website">Tri-Cities Linux Users Group</a> this coming Saturday (December 8th, 2007). If you&#8217;re in southeastern Washington state and interested in learning more about Seaside, and how it compares to Rails in particular, stop by West 248 at the WSU Tri-Cities campus at 1:00. It should be a fun and informative presentation.</p>
<p>Update: Yes, that&#8217;s south*eastern* Washington. Sorry for the typo earlier.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/18/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/18/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/18/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=18&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2007/12/06/seaside-presentation-at-3clug/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>
	</item>
		<item>
		<title>One-Field Forms and IE Quirks</title>
		<link>http://kentreis.wordpress.com/2007/09/08/one-field-forms-and-ie-quirks/</link>
		<comments>http://kentreis.wordpress.com/2007/09/08/one-field-forms-and-ie-quirks/#comments</comments>
		<pubDate>Sun, 09 Sep 2007 06:37:42 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Seaside]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/2007/09/08/one-field-forms-and-ie-quirks/</guid>
		<description><![CDATA[I encountered an old quirk in Internet Explorer again &#8212; one I hadn&#8217;t seen since I was doing most of my web programming in PHP. I had developed a style of PHP (and later Rails) coding that avoided it, but Seaside&#8217;s architecture makes you more likely to fall victim.
We often take for granted that the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=15&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I encountered an old quirk in Internet Explorer again &#8212; one I hadn&#8217;t seen since I was doing most of my web programming in PHP. I had developed a style of PHP (and later Rails) coding that avoided it, but Seaside&#8217;s architecture makes you more likely to fall victim.</p>
<p>We often take for granted that the Enter key submits our forms. The Enter key always acts as if the first button in the form had been pressed. Whether this is written into the HTML spec or whether it just developed as a convention, I don&#8217;t know. But users expect it to work something like that, in that Enter is always expected to perform a normal submission (i.e. &#8220;Save&#8221;) instead of an exceptional one (i.e. &#8220;Cancel&#8221;).</p>
<p>Internet Explorer has a funny quirk with this submit-on-enter mode, and although many people claim to have found it and worked around it, many people seem to over-generalize it. You&#8217;ll find many web design blogs and forum posts asserting that &#8220;IE doesn&#8217;t post the submit button names if you press Enter&#8221;. That&#8217;s partly true. If you push Enter on some forms, IE leaves off the button data entirely. Click a button and IE faithfully submits the button data, but press Enter and the posted form data tells nothing of the user&#8217;s intent.</p>
<p>In PHP or Rails, I typically structured my forms with the &#8220;Save&#8221; button appearing first, and the &#8220;Cancel&#8221; button appearing second. When handling the POST, the code checked for Cancel and, if it wasn&#8217;t found, proceeded with the operation. That meant that the forms really didn&#8217;t care about any specific button&#8217;s value except &#8220;Cancel&#8221;, which always requires a real button click anyway. Problem solved.</p>
<p>But in Seaside, each button has a specific callback attached. If you don&#8217;t get the button data in the form posting, Seaside doesn&#8217;t evaluate any button callbacks.</p>
<p>Fortunately, there is a workaround: make sure you use two or more input fields on your form. When there are multiple input fields, IE is happy to behave in a sane manner and submit the name of the first (i.e., the expected &#8220;default&#8221;) button. It&#8217;s only in one-field forms that this problem occurs.</p>
<p>The only problem is that, well, some forms need to be one-field forms.</p>
<p>Seaside solves this problem rather nicely using the defaultAction callback. If you create a form and assign it a defaultAction, Seaside does two things: First, it creates a submit button as the very first child of the form that is set to fire the callback. Secondly, it creates an empty text input. Both of these get positioned wildly off the page using CSS, so they&#8217;re never visible but they still have their desired effects.</p>
<p>By assigning a default action, you can actually break the mold and arrange your submit buttons in whatever order you like. Or you can create forms that submit on Enter but don&#8217;t have any (user-visible) buttons at all. It&#8217;s a cool trick, and the fact that it solves the one-field form quirk seems secondary to its main purpose. Most of my forms don&#8217;t do anything fancy like that, so a default action isn&#8217;t necessary.</p>
<p>But forms with only one field need it for IE usability.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/15/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/15/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/15/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=15&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2007/09/08/one-field-forms-and-ie-quirks/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>
	</item>
		<item>
		<title>Meaningful Seaside Links after Session Expiry</title>
		<link>http://kentreis.wordpress.com/2007/06/28/meaningful-seaside-links-after-session-expiry/</link>
		<comments>http://kentreis.wordpress.com/2007/06/28/meaningful-seaside-links-after-session-expiry/#comments</comments>
		<pubDate>Thu, 28 Jun 2007 21:26:53 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Seaside]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/2007/06/28/meaningful-seaside-links-after-session-expiry/</guid>
		<description><![CDATA[There&#8217;s something about Seaside&#8217;s URLs that has bothered me for a long time, and I just discovered a solution. I haven&#8217;t seen anything about this anywhere else, so I figured I&#8217;d write it down in case it&#8217;s useful to anyone else.
Since your URLs are managed by your Seaside session, they all become invalid when your [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=12&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>There&#8217;s something about Seaside&#8217;s URLs that has bothered me for a long time, and I just discovered a solution. I haven&#8217;t seen anything about this anywhere else, so I figured I&#8217;d write it down in case it&#8217;s useful to anyone else.</p>
<p>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.</p>
<p>The well-known pattern for creating &#8220;bookmarkable URLs&#8221; 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&#8217;s visible by default). However, that only solves the problem of <strong>returning to your current page</strong> and has nothing to do with <strong>meaningful links on your current page</strong> after your session expires.</p>
<p>For most applications, this sort of behavior is not a big deal. Your session times out, you click something, and you&#8217;re kicked back to the &#8220;please sign in&#8221; page. Users tolerate this, because they&#8217;re used to the idea that their computer shouldn&#8217;t stay signed in forever.</p>
<p>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&#8217;t get back to the page for several hours or days. The <a href="http://www.squeaksource.com">SqueakSource</a> 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!</p>
<p>Thankfully, there are hooks in Seaside that make meaningful links possible. But they&#8217;re not very well known, and I can&#8217;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.</p>
<p>You can use them like this:<br />
<code><br />
(html anchor)<br />
&nbsp;callback: [self registerVolunteer];<br />
&nbsp;extraPath: 'volunteer';<br />
&nbsp;with: 'Become a Volunteer!'<br />
</code></p>
<p>If you&#8217;re already using the hooks for bookmarkable URLs, then it&#8217;s simply a matter of putting in the right path or parameters when you build the link.</p>
<p>It&#8217;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&#8217;s really not. It just feels hard, because Seaside gives you such a nice abstraction the rest of the time.</p>
<p>And if you&#8217;re building a content management system, you&#8217;ll likely be creating an abstraction that hides these details anyway.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/12/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/12/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/12/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/12/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/12/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/12/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/12/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/12/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/12/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/12/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/12/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/12/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=12&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2007/06/28/meaningful-seaside-links-after-session-expiry/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>
	</item>
		<item>
		<title>Alternatives to Model-Based Validation?</title>
		<link>http://kentreis.wordpress.com/2007/06/22/alternatives-to-model-based-validation/</link>
		<comments>http://kentreis.wordpress.com/2007/06/22/alternatives-to-model-based-validation/#comments</comments>
		<pubDate>Fri, 22 Jun 2007 23:03:23 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Seaside]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/2007/06/22/alternatives-to-model-based-validation/</guid>
		<description><![CDATA[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 (&#8220;must be two characters long&#8221;) or in relation to each other (&#8220;password can&#8217;t be blank for administrators&#8221;). I&#8217;ve written several of my own extensions to these for [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=11&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>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 (&#8220;must be two characters long&#8221;) or in relation to each other (&#8220;password can&#8217;t be blank for administrators&#8221;). I&#8217;ve written several of my own extensions to these for validating phone numbers, email addresses, tracking numbers, and the like.</p>
<p>It works fine, at first. But model-based validation breaks down in several situations. Here are a few examples:</p>
<p>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&#8217;s validation code (&#8220;If I&#8217;m only asking for the person&#8217;s name and address right now, don&#8217;t try to validate his email address&#8221;).</p>
<p>2. In Rails, you can&#8217;t save your models unless they satisfy all of the validity conditions. This problem probably won&#8217;t arise at first, but as your code base evolves, it can have some nasty side-effects. Like when a user can&#8217;t change his password, because the model has lately been changed to require a variable that used to be optional.</p>
<p>Seaside itself provides nothing to help you with validation. Search some archives and you&#8217;ll find people recommending <a href="http://www.lukas-renggli.ch/smalltalk/magritte">Magritte</a>, 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.</p>
<p>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&#8217;s validation rules are satisfied.</p>
<p>But I&#8217;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 &#8220;password&#8221; and &#8220;password confirmation&#8221;? 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 <strong>validation belongs in the form, not in the model</strong>. In Seaside, that means that validation would happen at the component level.</p>
<p>So I&#8217;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&#8217;ve seen so far is <a href="http://www.djangoproject.com/documentation/newforms/">the &#8220;newforms&#8221; framework in Django</a>, which couples form inputs to their validation.</p>
<p>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.</p>
<p>Are there any other frameworks that provide validation hooks but are not model-based?</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/11/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/11/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/11/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=11&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2007/06/22/alternatives-to-model-based-validation/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>
	</item>
		<item>
		<title>My Full-Circle Journey Back to Smalltalk</title>
		<link>http://kentreis.wordpress.com/2007/02/12/my-full-circle-journey-back-to-smalltalk/</link>
		<comments>http://kentreis.wordpress.com/2007/02/12/my-full-circle-journey-back-to-smalltalk/#comments</comments>
		<pubDate>Tue, 13 Feb 2007 05:55:11 +0000</pubDate>
		<dc:creator>Ken Treis</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Seaside]]></category>
		<category><![CDATA[Smalltalk]]></category>

		<guid isPermaLink="false">http://kentreis.wordpress.com/2007/02/12/my-full-circle-journey-back-to-smalltalk/</guid>
		<description><![CDATA[I suppose it&#8217;s time to tell my story. I was a Smalltalk zealot in the late 90&#8217;s, but I left it behind when I started my own business. I&#8217;ve now come full-circle, and I&#8217;m finding once again that Smalltalk is the best tool for most of the programming work I do.
I learned Smalltalk programming from [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=7&subd=kentreis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I suppose it&#8217;s time to tell my story. I was a Smalltalk zealot in the late 90&#8217;s, but I left it behind when I started my own business. I&#8217;ve now come full-circle, and I&#8217;m finding once again that Smalltalk is the best tool for most of the programming work I do.</p>
<p>I learned Smalltalk programming from <a href="http://www.cincomsmalltalk.com/userblogs/travis/blogView" title="Travis Griggs's Blog at Cincom">Travis Griggs</a> while working at <a href="http://www.key.net">Key Technology</a>. 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.</p>
<p>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&#8217;m good at it and the team really needed somebody in that role.</p>
<p>But the end result was that I didn&#8217;t get much time to do any significant programming. The more time passed, the more I missed it. I didn&#8217;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.</p>
<p>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.</p>
<p>I built my first web applications in Smalltalk. Travis and I had written the original <a href="http://wiki.cs.uiuc.edu/VisualWorks/WikiWorks">WikiWorks</a> code, and I used our experience there to build my own HTTP 1.1 server. This later became the basis for the <a href="http://www.swazoo.org/">Swazoo</a> server, which was built on my code at <a href="http://camp.smalltalk.org">Camp Smalltalk</a> 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.</p>
<p>At the time, there wasn&#8217;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.</p>
<p>This was what drew me towards PHP, Apache, and PostgreSQL. The promise of safety in numbers was attractive &#8212; I would never have to wonder if my server was fully compliant with the HTTP specs. But the main attraction for me was PHP&#8217;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 <em>vim</em> was easier over low-bandwidth connections (like a cell phone data connection) than making a VNC connection to a headful server image, too.</p>
<p>So I left Smalltalk behind and built a few large PHP apps. Nearly all of them relied heavily on PHP&#8217;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 &#8212; while the studio crew saw up-to-the-minute totals on their screens.</p>
<p>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.</p>
<p><a href="http://kentreis.files.wordpress.com/2007/02/seaside.jpg" title="seaside.jpg"><img src="http://kentreis.files.wordpress.com/2007/02/seaside.thumbnail.jpg" alt="seaside.jpg" align="right" border="0" hspace="5" vspace="5" /></a>At 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 <em>didn&#8217;t get it</em>. I saw how <span style="font-style:italic;">call:</span> and <span style="font-style:italic;">answer:</span> 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&#8217;t use CSS effectively.</p>
<p>I missed the larger picture, so I went back to PHP.</p>
<p>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 &#8212; a thriving community, lots of support for graphics formats, easy database connectivity, and a nice clean dynamic language.</p>
<p>These days, that would sound like the end of the story. But it&#8217;s not. The truth is, I&#8217;m disappointed with Ruby. Rails is <a href="http://onsmalltalk.com/programming/smalltalk/rails-vs-seaside/">&#8220;classic web development done very cleanly&#8221;</a>, as Ramon Leon says. And Ruby is my favorite non-Smalltalk language. But it&#8217;s not Smalltalk. In fact, I&#8217;ve come to see that Ruby missed the mark on several points.</p>
<ol>
<li><strong>File-based source control</strong>. 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.</li>
<li><strong>Reflection</strong>. Ruby&#8217;s reflection is OK, but it&#8217;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).</li>
<li><strong>Speed</strong>. 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.</li>
<li><strong>No Keyword Messages</strong>: Travis and I <a href="http://www.cincomsmalltalk.com/userblogs/travis/blogView?showComments=true&amp;printTitle=Whats_key_about_a_word&amp;entry=3335042013">ran some tests</a> using keyword-style messaging using Ruby&#8217;s last-argument-collapsed-as-hash, and the performance was terrible.</li>
<li><strong>Blocks</strong>. Ruby simply missed the mark on on these. All blocks should be first-class objects, not just a layer of syntax.</li>
<li><strong>Live Interaction</strong>. Irb is good, but Smalltalk workspaces are much better.</li>
<li><strong>Debugger</strong>. The Ruby debugger feels like <em>gdb</em> compared to Smalltalk. Most of the time, people opt to debug using console output and interactive exploration using Irb. It&#8217;s better than printf, but not much.</li>
<li><span style="font-weight:bold;">Class Library</span>: The Ruby class library is much younger than the extensive class library in Squeak or VisualWorks.</li>
</ol>
<p>As far as Rails is concerned, I have far fewer criticisms. But once you&#8217;ve looked at <a href="http://www.seaside.st">Seaside</a>, 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.</p>
<p>I&#8217;ve also come to see that unless you&#8217;re a light user of a technology, <span style="font-weight:bold;">you have to be able to support yourself</span>. This is not a downside for Smalltalk, it&#8217;s a reality of the complex environment we work in. Smalltalk&#8217;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&#8217;t. I have been digging in the Ruby 1.8 source tree more often than most, and it&#8217;s not nearly as easy to navigate as the Smalltalk base. Remember, most of the Smalltalk base is implemented in Smalltalk.</p>
<p>Seaside is certainly more advanced than Rails in terms of raw technology, to the point that it&#8217;s hard to wrap your head around the concepts. It took me two attempts, and I&#8217;m fairly young and intimately familiar with Smalltalk. Rails is smoother &#8212; 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.</p>
<p>Don&#8217;t be surprised if Seaside just looks weird to you at first. Leave it for a little while and then come back. You&#8217;ll be glad you did. And when it clicks for you, beware. You won&#8217;t ever look back.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/kentreis.wordpress.com/7/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/kentreis.wordpress.com/7/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kentreis.wordpress.com/7/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kentreis.wordpress.com/7/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kentreis.wordpress.com/7/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kentreis.wordpress.com/7/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kentreis.wordpress.com/7/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kentreis.wordpress.com/7/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kentreis.wordpress.com/7/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kentreis.wordpress.com/7/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kentreis.wordpress.com/7/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kentreis.wordpress.com/7/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kentreis.wordpress.com&blog=753510&post=7&subd=kentreis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kentreis.wordpress.com/2007/02/12/my-full-circle-journey-back-to-smalltalk/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/5c813cb5506f0c0f2159f4dd5b63c857?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Ken Treis</media:title>
		</media:content>

		<media:content url="http://kentreis.files.wordpress.com/2007/02/seaside.thumbnail.jpg" medium="image">
			<media:title type="html">seaside.jpg</media:title>
		</media:content>
	</item>
	</channel>
</rss>