Archive for the tag 'Smalltalk'

A Simple File Based Wiki in Seaside

There can never be enough simple sample apps to help beginners learn Seaside. In that spirit, here’s a simple file based Wiki written in pure Seaside (i.e. no Magritte and not overly abstracted to the point you can’t figure out what’s going on).

It has bookmarkable URLs, uses regex (regex package found on SqueakSource) to make WikiWords into links, keeps line breaks, and accepts raw HTML. Pages are stored on the file system under your image directory based upon the app name.

For a production quality Wiki, use Pier, this one is super simple and only intended for learning. It was written in about two hours (not counting some changes made during the writing of this article) as a single Seaside component.

OK, here we go, broken up into code sections by method category, first declare the class…

WAComponent subclass: #WikiPage
    instanceVariableNames: 'isEditing currentContent currentPage'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'SimpleFileWiki'

Setup the app on the class side…

canBeRoot
    ^ true  

initialize
    self registerAsApplication: #wiki

Initialize instances of the class…

initialize
    super initialize.
    currentPage := ''.
    isEditing := false

Create some accessing methods we’ll need…

currentContent
    ^ currentContent ifNil: [currentContent := '']

currentContent: aString
    currentContent := aString   

style
    ^ ‘ textarea {width:90%;height:500px;}’

Then some fancier accessors that ensure our file system is setup and reads pages from it…

pageDirectory
    ^ (FileDirectory default
        directoryNamed: self session application name , #Pages) assureExistence 

pageAt: aPage
	isEditing := (self pageExists: aPage) not.
	isEditing ifTrue: [^ ''].
	^ FileStream readOnlyFileNamed: (self pageDirectory fullNameFor: aPage)
		do: [:file | file contentsOfEntireFile]

If a page doesn’t exist, the Wiki kicks into editing mode to create it. A testing method use by the above…

pageExists: match
    ^ self pageDirectory fileExists: match

A couple of actions (our controller methods)…

loadPage: aPage
    isEditing := false.
    currentPage := aPage.
    self currentContent: (self pageAt: aPage)   

savePage
	self currentContent
		ifEmpty:
			[ (self pageExists: currentPage) ifTrue:
				[ self pageDirectory deleteFileNamed: currentPage.
				self loadPage: #FrontPage ] ]
		ifNotEmpty:
			[ FileStream
				forceNewFileNamed: (self pageDirectory fullNameFor: currentPage)
				do: [ :file | file nextPutAll: self currentContent ].
			isEditing := false ]    

cancel
	isEditing := false

Deleting the contents of a page, deletes the page as well.

Now we’re ready for rendering. Let’s start with page title in the head…

updateRoot: aRoot
    super updateRoot: aRoot.
    aRoot title: currentPage

And setting up the url…

updateUrl: aUrl
    super updateUrl: aUrl.
    aUrl addToPath: currentPage withFirstCharacterDownshifted

Now that the URL looks valid, lets make it work by parsing new requests, those that don’t include the session key (_s) or includes an expired session key. Once the session key and the continuation key (_k) are present, the URL is no longer necessary and will be ignored. Should this URL be bookmarked and returned to later, after the session has expired, #initialRequest: will be invoked, a new session started, and the correct page served…

initialRequest: aRequest
	| page url |
	url := aRequest url stringAfter: self application basePath.
	page := (url beginsWith: '/')
		ifTrue: [ url allButFirst copyAfterLast: $/ ]
		ifFalse: [ url copyAfterLast: $/ ].
	self loadPage: (page ifEmpty: [ 'FrontPage' ] ifNotEmpty: [ page ])

This uses an extension method #stringAfter that I have loaded in all my images, and it relies on another #split that is also in my images. Here they are…

String>>stringAfter: aDelim
    | list |
    list := self split: aDelim.
    ^ list isEmpty ifTrue: [self] ifFalse: [list last withBlanksTrimmed]

String>>split: aString
    | index lastIndex |
    index := lastIndex := 1.
    ^ Array streamContents:
            [:stream |
            [index <= self size] whileTrue:
                    [index := self findString: aString startingAt: lastIndex.
                    index = 0 ifTrue: [index := self size + 1].
                    stream nextPut: (self copyFrom: lastIndex to: index - 1).
                    lastIndex := index + aString size]]

Now our main render method which decides which mode the Wiki is in…

renderContentOn: html
    isEditing
        ifTrue: [self renderEditorOn: html]
        ifFalse: [self renderViewerOn: html]

And either edits the Wiki page…

renderEditorOn: html
	(html heading)
		level1;
		with: ((self pageExists: currentPage)
					ifFalse: ['Page ' , currentPage , ' hasn''t been created yet, go for it!']
					ifTrue: ['Editing ' , currentPage]).
	html form:
			[html textArea on: #currentContent of: self.
			html break.
			html submitButton on: #savePage of: self.
			html text: ' or '.
			html anchor on: #cancel of: self]

Or renders the viewer which also parses the text for WikiWords and line breaks…

renderViewerOn: html
    self withLineBreaks: (self currentContent
                copyWithRegex: '[A-Z][a-z]+([A-Z][a-z]+)+’
                matchesTranslatedUsing:
                    [:match |
                    (self pageExists: match)
                        ifTrue: ['<a href="' , (html urlForAction: [self loadPage: match]) displayString, ‘”>’, match , ‘</a>’]
                        ifFalse: [match , '<a href="' , (html urlForAction: [self loadPage: match]) displayString, ‘”>?</a>’]])
        on: html.
    html paragraph:
            [(html anchor)
                callback: [isEditing := true];
                text: ‘Edit’.
            html space.
            (html anchor)
                callback: [self loadPage: #FrontPage];
                text: ‘FrontPage’]

The editor and viewer could have been separate components, but I’m going for simple here, one class. And finally, the method for breaking lines…

withLineBreaks: aString on: html
    | stream |
    stream := aString readStream.
    [stream atEnd] whileFalse:
            [html html: stream nextLine.
            stream atEnd ifFalse: [html break]]

And there we have it, a simple file based Wiki that covers quite a few things you’d want to do in a web app and should be easily digestible for the Seaside beginner. There are probably bugs, I didn’t do a ton of testing and its only intended use is this blog post.

According to the message “WikiPage linesOfCode”, that’s 90 lines of code total (and that’s including the HTML and CSS). Here’s a file out of the code for anyone interested. Make sure to manually add the two extension methods to String for this to work.

An Excellent Smalltalk Primer By Alan Lovejoy

Alan Lovejoy has released an excellent primer on Smalltalk that seems to really focus on what’s important, message passing, and why OOP in Smalltalk isn’t like OOP in other languages. For those who don’t know, OOP isn’t about objects and classes, it’s about message passing between objects. Bad OO code is the result of not grasping this vital distinction and the source of much FUD about OO. It’s also full of references for those looking to dig deeper into particular concepts, or find other resources in the Smalltalk world. I highly recommend it.

New GNU Smalltalk Site Launched

GNU Smalltalk has launched a new site. I thought it was about time to give it a try so I can write my shell scripts in Smalltalk. I work in Windows but I use Cygwin so I can have a real shell handy, fortunately GNU Smalltalk runs under Cygwin. So I fire up a shell and grab the latest version, unzip and untar it…

wget ftp://ftp.gnu.org/gnu/smalltalk/smalltalk-2.3.6.tar.gz
gunzip smalltalk-2.3.6.tar.gz
tar -xf smalltalk-2.3.6.tar

So far so good. The instructions say I need autoreconfig, so I fire up Cygwin setup and hit the Devel category and install it, just to be safe I install gcc compilers for C and C++ as well. Time to build Smalltalk (that sounds weird to me).

autoreconf -fvi
./configure
make

Takes a minute, checks a gazillion things, but all is well, build seems to work fine. Time to run the unit tests…

make check

Good to go, so lets install…

make install

OK, now just type “gst” to get a REPL, play around for a bit, and write my first simple shell script, a quick loop printing 1 to 10000 on stdout…

#!/usr/local/bin/gst -f
(1 to: 10000) do:[:it | it printOn: stdout].
!

Sweet, works like a charm. I can now fall back on Smalltalk for shell scripting instead of ruby or pearl when I need more power than simple bash scripts.

Stealing a sample from the Wiki shows a nice scripting syntax making use of blocks for declaring class and method bodies…

Object subclass: Person [
    | name age |
    Person class >> name: aString age: anInteger [
        ^(self new)
            name: aString;
            age: anInteger;
            yourself
    ]

    name [ ^name ]
    name: aString [ name := aString ]
    age [ ^age ]
    age: anInteger [ age := anInteger ]

    printOn: aStream [
        aStream << ('%1 (%2)' % {name. age})
    ]
]

I think I like it! Only time will tell, but I think this will be my new scripting language of choice.

Using Magritte With Seaside

One of the more tedious things one is faced with while writing web applications is input forms. Forms suck because they’re boring and repetitive. Building them manually is the same thing over and over, but they’re often just different enough that fully automating them is rather difficult. I can’t say I’ve ever seen a perfect solution, but I’ve come to consider Magritte a pretty good solution if your forms don’t need to be too highly customized.

For an in depth look at the design of Magritte you should read Lukas’ Master’s thesis, however, it’s a bit much if you’re just trying to get something done and just want to understand how to get going with the basics. Magritte’s capable of more than you’ll likely use if you’re just wanting to build forms with Seaside. I’m going to ignore those other abilities because frankly, I don’t use them and don’t plan to.

The basic design of Magritte involves a few classes that you’ll need to be familiar with and often subclass to extend and customize.

MADescription

With Magritte you describe your objects with subclasses of MADescription, this is where you’ll always start, just describing the fields of your domain objects that you’ll want generated forms for. Descriptions contain the extra metadata necessary to create user interfaces, and which component will be used to render it. A simple description looks like this…

descriptionIsClosed
    ^ (MABooleanDescription new)
          selectorAccessor: #isClosed;
          label: 'Is Closed';
          priority: 1000;
          yourself

Generally speaking you’ll keep these descriptions on the class side of your domain object. On occasion, you’ll want some property of the description to be dynamic and depend on the actual value of an instance side value, so you’ll move the description to the instance side. Strictly speaking, it doesn’t matter where you put them, sometimes you’ll use them to build forms when you don’t even have a domain object, but this isn’t the normal case.

There are two basic ways to create a form dynamically, one is simple and includes all descriptions found on an object…

buildComponentFor: aModel
    ^aModel asComponent addValidatedForm

The other, a little more complex, but much more useful because you’ll find that rarely do you want “every” described field in a form. Often you’ll have multiple UI representations of a domain object within an app. Descriptions are composable using a comma so you can build a form any subset of the fields…

buildComponentFor: aModel
    ^((ModelClass descriptionFieldOne, ModelClass descriptionFieldTwo, ModelClass descriptionFieldThree)
            asComponentOn: aModel)
        addValidatedForm;
        yourself

A Magritte form is generally wrapped with a form decoration via #addValidateForm, which by default gives you a #save and #cancel button. Magritte forms don’t directly work on your domain objects. They work on a memento of the values pulled from your object using the descriptions. When you call #save, the values are validated using the descriptions, and only after passing all validation rules are the values committed to your domain object by the momentos via the accessors, which I’ll describe shortly. For those interested, this is a good place to plug in a custom container component and integrate the #commit with Glorp, but that’s another post.

Conditions

Descriptions can have predicates associated with them to validate the input data which it calls conditions. There are two basic kinds of conditions (there are actually more, but the block forms are what I use), both are simply blocks that returns true or false to validate the data. One is the single field condition which is passed the pending value directly and attached directly to your descriptions. It’s as simple as…

addCondition:[:value | value withBlanksTrimmed size > 3]

The other is the multi field validation which works on the memento which I’ll describe below.

	addCondition: [:memento |
		(memento cache at: CAUser descriptionPassword) isNil or:
			[(memento cache at: CAUser descriptionPassword)
				= (memento cache at: CAUser descriptionConfirmedPassword)]]
	labelled: ‘Passwords must match’;

I’ve posted about these previously. They are attached to your containers description which has access to all the fields.

Conditions are one of the few things about Magritte that I’m still iffy on. There are advantages to having your rules outside your domain objects, especially if you’re taking advantage of Magritte as an Adaptive Object Model where users can build rules from the UI. It also allows the mementos to easily test data for validity outside the domain object and gives you a nice place to hook into the validation system in the components. It bothers me a little because it weakens the domain model, I’m still trying to find my balance here and decide just how much I’ll allow as conditions.

Magritte is very pluggable, should I decide I want all my rules in my domain objects, I could plug in my own memento subclass to handle that, but I haven’t felt the need just yet.

MAMemento

When components read and write to domain objects, they do it using mementos rather than working on the objects directly. The default memento is MACheckedMemento, a subclass of MACachedMemento. The mementos give Magritte a place to store invalid form data prior to allowing the data to be committed to the object. It also allows Magritte to detect concurrency conflicts for shared in image objects. By never committing invalid data to domain objects, there’s never a need to roll anything back. Should you feel the need, just override #mementoClass on your domain object to provide your own.

MAAccessor

When a memento writes valid data to its model, it does so through accessors which are also defined by the descriptions. MAAccessor subclasses allow you to define how a value gets written to your class. For example, while I have a read selector for my collections like #users, I prefer to encapsulate #add and #remove on the domain object itself rather than having a writable selector for #users:. When creating a description for that collection, I can specify my custom #accessor: rather than using the default #selectorAccessor: which then writes to my domain object with #addUser: and #removeUser:.

Another valuable use for accessors can be found when you want to flatten several domain objects into a single form. Say you have a User object with an Address object, and a CreditCard object but you want to edit them all as a single form. You can create your form and dynamically wire up some MAChainAccessors like so…

userTemplate
	^((((CAUser descriptionEmail, CAUser descriptionPassword , CAUser descriptionConfirmedPassword)
		addAll: (CAAddress description collect: [:each |
			each copy accessor:
                            (MAChainAccessor accessor: (MASelectorAccessor selector: #address) next: each accessor)]);
		addAll: (CACreditCard description collect: [:each |
			each copy accessor:
                             (MAChainAccessor accessor: (MASelectorAccessor selector: #creditCard) next: each accessor)]))
                yourself)
            asComponentOn: self session user)
            addValidatedForm: {  (#save -> ‘Update Profile’)};
            yourself.

Which basically just composes the users descriptions with the addresses and credit cards descriptions while chaining the accessors together to allow the addresses fields to be written to the address object rather than directly to the customer object. Any non trivial UI will contain many such forms and this is the ability I’m the most grateful for in Magritte. Also notice in the example above how I changed the label for the save button to “Update Profile”, also something you’ll need to do often that isn’t immediately obvious how to do.

MADescriptionComponent

Components control how your objects display. Magritte widgets are subclasses of this component. Some descriptions have an obvious one to one relationship with a UI component while others could easily be shown by several different components. For example, an MAMemoDescription would be represented by a text area, but a MABooleanDescription could be a checkbox, or a drop down with true and false, or a radio group with true and false.

Each description defaults to a component, but allows you to override and specify any component you choose, including any custom one you may write. For example, suppose I built a special component to render checkboxes as fancy graphics rather than ordinary html checkboxes. I could plug it in by changing the description as follows.

descriptionIsClosed
    ^ (MABooleanDescription new)
          selectorAccessor: #isClosed;
          label: 'Is Closed';
          priority: 1000;
          componentClass: CAFancyCheckbox;
          yourself

These are essentially meta components written in standard Seaside. They work on mementos and have access to your descriptions. This is where all that meta data comes in handy. If you’re building a dropdown list, it’ll get its option list from the description. When you render an element’s label, that’s also read from the description.

When you build your own descriptions, you’ll add fields to them for custom data that you’ll want available in your custom components. At first, you’ll mostly use built in descriptions and components, but as you grow comfortable with Magritte you’ll start wanting to build your own descriptions and components because you’ll want to do things beyond the basic framework like Ajax. Magritte provides a solid foundation for extension.

MAComponentRenderer

By default, Magritte forms are rendered using MATableRenderer which renders the fields in a table with errors at the top and labels to the left of input fields. This is the default because it’s the easiest thing to do automatically that looks halfway decent. This can be overridden like so…

buildComponentFor: aModel
    ^((ModelClass descriptionFieldOne, ModelClass descriptionFieldTwo, ModelClass descriptionFieldThree)
            componentRenderer: MACssRenderer;
            asComponentOn: aModel)
        addValidatedForm;
        yourself

This hook allows you to render the form as you please, should you want to display errors differently, or change how elements are wrapped, or how the outer container itself is wrapped. It’s handy to control how container classes are tagged with css classes to have the form dynamically change its appearance for various phases like valid/invalid. If you don’t want your forms to look like auto-generated forms, you will use this.

For each of the classes I just described, you’ll want to pull up a hierarchy browser and explore all the subclasses to see examples of how they are specialized. Find some example projects in SqueakSource that use Magritte to see how others create custom descriptions and components. As with any framework, successful use depends on understanding it, and understanding how the pieces fit together. Magritte will not doing everything you want out of the box, it isn’t meant to, but it’s an excellent foundation to base your application on. It will save you many hours of tedious work building forms by hand. It will also give your objects extra metadata that you may find handy for purposes other than building forms.

Along with Seaside, Magritte is also an excellent example of extremely well written object oriented code that is worth reading to see exactly what it takes to make a truly flexible and pluggable framework. Because it was originally a masters thesis, it’s also far better commented than you’d likely find in a similar “real” world framework.

« Previous PageNext Page »