Cloud208 Blog

Testing the Waters

So, after a slightly long-winded theoretical introduction to the Seaside, it is time for us to dip our feet in the water and see a few bits of the Seaside in use. Along the way we shall also see some Smalltalk syntax, though I will not dwelve into precise definitions and will rely more on my hand waving, and your programming instinct.

As an example we will look at the code for the component below:

++ Increment heading level
-- Decrement heading level

This is heading level: 1

As you may have guessed (or tried to guess), it is a small component that lets you try out various heading levels. It is a slight variation on the omnipresent Counter demo that is part of almost any self-respecting Seaside introduction.

In Smalltalk it is customary to start with a model code. In almost all cases that implies writing a class for a model. But since in our example the model is rather trivial (basically, a number to keep track of the current heading level), and to keep the example as brief as possible, we shall include model functionality in the presenter class.

In Seaside all component classes inherit from the WAComponent class, and so will our Cloud208HeadingLevelGizmo:

WAComponent subclass: #C208HeadingLevelGizmo
instanceVariableNames: 'headingLevel'
classVariableNames: ''
poolDictionaries: ''
category: 'Cloud208-examples'

In the code above, you can also see that instances of our class will have one variable named headingLevel, and that our class belongs to the Cloud208-examples category.

Now that we have defined the class, we shall start by adding simple methods that define minimum and maximum heading levels. They do not do anything fancy but just return constants. Note that in Smalltalk values are returned with the ^ caret symbol.

minHeadingLevel
^1
maxHeadingLevel
^5

It is a good idea to initialize our variables, and this is often done by the #initialize method:

initialize
super initialize.
headingLevel := 1

In the example above you can see that Smalltalk uses := as the assignment operator, and that statements are separated by . dot. Also, the construct receiver message, sends a message to the receiver. So, the super initialize sends an initialize message to the code defined in super classes and lets them also do their initialization.

Now might be a good time to add two methods to the increment/decrement heading level. Our methods will take care not to overstep minimum or maximum values.

incrementHeadingLevel
(headingLevel < self maxHeadingLevel)
ifTrue: [headingLevel := headingLevel + 1].
decrementHeadingLevel
(headingLevel > self minHeadingLevel)
ifTrue: [headingLevel := headingLevel - 1].

If you look into these two methods, you will notice that both of them have some statements surrounded by square brackets like this [headingLevel := headingLevel + 1]. This is called a block in Smalltalk, and it is roughly equivalent to the complex statements that may be found in many other languages. Interestingly, in Smalltalk, you are free to assign blocks to variables, pass them as message parameters and execute them at your will.

Up to now, we have been dealing with the model part of our component, and now it is time to move to the presenter and the view part. At the core of it there is a message: #renderContentOn:. This message has a similar role to the windows paint message. When the Seaside framework decides that it needs an html representation of a component, it sends this message to our component. As an argument for that message, Seaside also passes an object called the html canvas. This canvas object can be sent messages such as #paragraph, #anchor that the paint html constructs on. In response to #renderContentOn:, each component needs to paint (or render) itself on the canvas given as the argument to the message.

renderContentOn: html

html anchor
callback: [ self incrementHeadingLevel ];
with: '++ Increment heading level'.

html break.

html anchor
callback: [ self decrementHeadingLevel ];
with: '-- Decrement heading level'.

(html heading level: headingLevel)
with: 'This is heading level: ', headingLevel printString .

Let us take a more detailed look now. The following code is responsible for rendering the link that will increment the heading level:

	html anchor
callback: [ self incrementHeadingLevel ];
with: '++ Increment heading level'.

html anchor will create an anchor (link) brush and the #with: message that gets sent to it to define the text that will appear as the link. The most interesting part is #callback: [ some stuff here ]. This part defines the block that will be executed when the user clicks on the link. So, in our example, when the user clicks on the "Increment heading level" link, the message #incrementHeadingLevel will be sent to the self, i.e. to the instance of our component.

The following fragment just emits a </br> tag:

	html break.

The next fragment is responsible for displaying a sample of the heading at the current level. (html heading level: headingLevel) creates a brush for the heading of the level contained in the variable headingLevel. Another interesting point from this example is that in Smalltalk a comma , is used to concatenate strings.

	(html heading level: headingLevel)
with: 'This is heading level: ', headingLevel printString .

This completes our #renderContentOn: method, and now our component needs only one small final touch. In order to handle back the button correctly, our component needs to say what state needs to be saved with each link:

states
^ Array with: self

I guess this should do for this post. There is more to come in the future – till then, keep well.

Posted by rush at 22 April 2009, 10:47 am with tags Seaside, Smalltalk, Web, development link

Comments

Hi! I've started using Pier as a kind of a notebook. I scratch a few notes while I'm reading and learning and then I try to expand it and organize with hyperlinks. I also plan to include some Seaside 'scripts' in it, for calculating things and so on, but smalltalk (and seaside) are too hard and complicated for me right now. Maybe you could help me with one simple thing, since I am new in Squeak and also new in programing in general. Where does Pier put texts that I write inside its pages? Can I find them in Squeak and extract them in some kind of simple text file or something simillar? Can I use them again in another Pier installation? I wouldn't like to loose my work and I don't like not having control over my bits and pixels. You have a very nice blog. I think it's going to be a very good tutorial. Pozdrav, Dario

Posted by dario at 24 April 2009, 7:12 pm link

Dario,

I am afraid that there is more than one answer to your question. First thing you should check in your pier System Management page is what kind of persistency is set-up. By default trere are 3 options Null persistency as in no persistency, History persistency where Pier writes a kind of transaction log from which it can reproduce state of the pages, and image persistency which periodically saves whole squeak image that among other things includes all of your pier pages. On cloud208, I am running image persistency, exept when I ma poking around with some external code, while I switch to the null persistency. And I would advise you to do the same.

Second answer to your question is that all your pier pages are saved as objects inside Smalltalk image, and if you would like to get your hands dirty you could get access to those objects and make them suitable for storing in relational databes, or store them directly in some object database like Omnibase. Especially interesting is the combination is if you are running your Pier in GLASS virtual appliance. GLASS stands for: GemStone, Linux, Apache, Seaside, and Smalltalk. Gemstone is somehow difficult to describe in few words, but I usually try with phrase "persistent Smalltalk". It is smalltalk in which globaly reachable objects are transparently stored in object database. Alternative definition would be that Gemstone is object dataabse whose native language is Smalltalk. How ever you take, if you run your Pier in Gemstone (and that is rather straightforward with GLASS virtual appliance), all your page objects are automagically made persistent by Gemstone.

Third answer is that you should include PierSystem|Export/import component in your System management page. Once you do, you will be able to:

  • dowlnoad serialized content of your pages in single file
  • upload contents file to fill/replace existing pier.

It works very nice and I would advice that you download contents of your pier from time to time. Few words of advice though. Downloaded file does not contain various files uploaded to the Pier, you need to archive those by hand. Also this content transportation works within compatibile versions of pier. Maybe it works between largely differing pier versions, but I would probably try it before I would depend on it. Same thing would be for some pier add-ons that need to have starte of their own.

I hope this has helped you a bit. Thank you for your kind words, and Lijepi pozdrav!

Posted by rush at 24 April 2009, 11:55 pm link

Thanks, you really helped me! Export/import is something I was looking for, thanks for your tip. It looks a little bit clumsy, but it seems to work. I don't need real database for now, simple files will be o.k. I've heard about Glass, I just thought it was some kind of super exclusive and expensive environment for Nasa, Wall street banks and so on, not for amateurs like myself. I suppose they have a trial, or free (as beer) version. I'll check it out. Waiting for your next post! Bye

Posted by dario at 25 April 2009, 7:33 pm link