There's a good video on building a simple app with Seaside (on Squeak Smalltalk) at this blog.

 

Here's the missing voice-over:

 

  1. Start your web server (SSKom startOn: 91.)
  2. Create a new category named "SeasideBlog"
  3. Change the generic class definition to declare BlogView a subclass of WAComponent. Accept.
  4. Define a class method canBeRoot, returning true. Accept. (I’m going to stop saying, “Accept.”
  5. Define and accept an instance method

renderContentOn: html

html text: 'Hello world!'

  1. Browse to your seaside/config site (http://localhost:91/seaside/config, ID=admin/seaside)
    1. Add an application entry point “seasideBlog”
    2. Set Root Component = BlogView and click Done

i. If you don’t see BlogView as a choice, you probably defined canBeRoot as an instance method. Oops!

    1. Click your new seasideBlog link and confirm that you see “Hello world!”
  1. Create a BlogPost class, subclassing Object, with instance variables title, body, and comments.
    1. I don’t know what a Magritte description is, but whenever it asks, I tell it to create them.
  2. Create a BlogComment class, subclassing Object, with instance variables name and comment.
  3. Select name, right-click/selection/create accessors biz simple
  4. Select comment, right-click/selection/create accessors biz simple
  5. In BlogComment’s class method descriptionName, change priority to 10.
  6. In BlogComment’s class method descriptionComment, change priority to 20, and change MAStringDescription to MAMemoDescription
  7. BlogPost, (instance): create accessors biz simple for the 3 instance variables.
  8. Remove the “comments:” method but not “comments” (without the colon)
  9. Select class, delete descriptionComments,
  10. Change priority on descriptionTitle to 10
  11. Change priority on descriptionBody to 20, and change MAStringDescription to MAMemoDescription
  12. Still on BlogPost, create a database method category, and create a class method

repository

^repository ifNil:[repository := OrderedCollection new]

    1. When you accept, it will whine about repository. Tell it to create an instance variable.
  1. Alphabetize your method categories
  2. Insert the following into BlogPost’s descriptionTitle, just before beRequired:

addCondition: [:value | (BlogPost repository contains: [:each | each title = value]) not ]

labelled: 'Post with that title already exists';

  1. Select instance and BlogView and renderContentOn and make it’s body look like:

renderContentOn: html BlogPost repository reversed do: [:eachPost | html div: [html heading: eachPost title level: 2; text: eachPost body. html div: [html bold: 'Comments'. eachPost comments do: [:eachComment | html heading: eachComment name level: 4; text: eachComment comment]]]]. html anchor on: #newPost of: self <ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">Add a method:</li></ol> newPost | post |post := self call: (BlogPost new asComponent addValidatedForm; yourself ). post ifNotNil: [BlogPost repository add: post] <ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">Back in your browser: <ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">Refresh your browser and add a new post (making some sample mistakes along the way). Click Save.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Whoops! We have an error. Click the link for Debug and the debugger opens in Squeak.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">We can see where the problem is, so close the debugger and go to BlogPost’s comments method, and change the body to “ ^comments ifNil: [comments := OrderedCollection new]”</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Refresh your browser and view your new post.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Try and create a posting with the same title. Can’t do that! Change the title and save it.</li></ol></li><li class="MsoNormal" style="margin: 0in 0in 0pt">Select BlogView, renderContentOn, and make it look like this:</li></ol> renderContentOn: html BlogPost repository reversed do: [:eachPost | html div: [html heading: eachPost title level: 2; text: eachPost body. html div: [html bold: 'Comments'. eachPost comments do: [:eachComment | html heading: eachComment name level: 4; text: eachComment comment]]. (html anchor) callback:[self addCommentTo: eachPost]; text: 'Add Comment']]. <ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">Back to your browser:<ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">Refresh</li></ol></li><li class="MsoNormal" style="margin: 0in 0in 0pt">Create addCommentTo:</li></ol> <p style="margin: 0in 0in 0pt" class="MsoNormal">addCommentTo: aPost</p><p style="margin: 0in 0in 0pt" class="MsoNormal"> | comment |</p><p style="margin: 0in 0in 0pt" class="MsoNormal"> comment := self call: (BlogComment new asComponent addValidatedForm ; yourself).</p><p style="margin: 0in 0in 0pt" class="MsoNormal"> comment ifNotNil: [aPost comments add: comment]</p> <ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">Create a new method category named actions, and move addCommentTo and newPost into it.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Back to your browser<ol style="margin-top: 0in"><li class="MsoNormal" style="margin: 0in 0in 0pt">New session</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Add a comment and save it.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Add a second comment and save it.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Add and save one final posting.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Turn halos on. Click on S for source.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">Click on configure and set it to deployment mode.</li><li class="MsoNormal" style="margin: 0in 0in 0pt">With a new session, no more development tools available.</li></ol></li></ol>