A look into Spec

Today I’m going to write about implementing the UI.
Since I want to have a nice UI in Pharo I choose Spec to build it, because it’s an on going project and there are lot of effort there, also I want to try it :).
Searching I found some tutorials and demos but some were quite old and not fully compatible with current version of Spec, so I will do a brief tutorial.

Spec was developed by Benjamin Van Ryseghem, you can find more information in: https://pharo.fogbugz.com/default.asp?spec.5.5.2

Anyway I think that the best place to take a look is in the examples that comes under Spec in Spec-Examples-PolyWidgets (mainly) and Spec-Examples-Widgets.

Doing the minimun for opening a Window
We want an empty window… but our empty window, so… how?
1. We are going to create a class extending ComposableModel

ComposableModel subclass: #TestSpec
instanceVariableNames: ”
classVariableNames: ”
poolDictionaries: ”
category: ‘Flamel-UI’

2. We have to implement 2 methods
2.1. initializeWidgets which responsability is to setup the components we will use (right now nothing)

initializeWidgets
“nothing to do here”

2.2 defaultSpec (class side) which returns a Layout saying which components will be render. There is a number of different layouts implemented: SpecColumnLayout, SpecRowLayout, and in the class methods of SpecLayout you can find a lot more. We will choose a very easy one:

defaultSpec
<spec>
^ SpecLayout composed

In fact you can name the method as you want, only need to be annotated with the <spec> pragma.

3. Open the window, we evaluate in a workspace:

TestSpec new openWithSpec

And we obtain:

emptyWindow

Nice… but quite empty… let’s put something inside!

Adding a component
1. Choose the widget to add, you can see some in Spec-Widgets, we want a text area so we will choose a TextModel.
2. In order to render the component we need to: create it and the tell the layout that we want to render it
2.1. Create the widget: define an instance variable and instantiate it during the initializeWidget using the Spec way to do it:

initializeWidgets

self instantiateModels: #(
text TextModel
)

We need to define text as an instance variable because instantiateModels assume that and try the elements of the array as an association: variable – kind where variable is a property in the object and makes it point to a new instance of the kind, in order to avoid the troubles we will define the variable.

Be careful with this (as Sean mention) if you define the variable while you are debugging you can get some nasty errors, so the advice is to define the variable first and then open the window.

At this point if we get anxious to see our cool text component and we open the window we will see that is still empty, this is because we never added it to the layout! So it’s not rendered.
2.2. Add the component in the layout, for this we will modify the defaultSpec class method and add a getter for the text property:

defaultSpec
^SpecColumnLayout new
add: #text;
yourself

We need the getter because the layout tries to access the variable sending the symbol as a message
3. Open it

withComponent

Communicating Components
Now we can open a window and have our components, but now we want to have several components that depends between them.
We will build a window with a text editor, when the content in the text editor changes we will see the message “The text changed” we can accept that pressing a button that says “Ok” and then the message will change to “You are up to date” (until someone change the content in the text …).
For doing this we will use:
– a TextModel called: example
– a LabelModel called: changes, starts with the text: “You are up to date”
– a ButtonModel called: acceptChanges, starts disabled and with the label: “Ok”

initializeWidgets
self instantiateModels: #(
example TextModel
changes LabelModel
acceptChanges ButtonModel ).

changes text: ‘You are up to date’ .
acceptChanges
label: ‘Ok’;
disable.

This is like we had before, only that we use other components that needs a little more of configuration.

The real challenge here is to link some effects: when the contents of example changes we want to change the message in changes and enable acceptChanges.
For defining this kind of interactions we should define the method initializePresenters defining the interactions, lucky our models understand several useful message to define an action to perform for an event, an example for a TextModel #whenTextIsAccepted: , #whenTextChanged:, …
Let’s define the actions:

initializePresenter
example whenTextIsAccepted: [ changes text: ‘The text Changed’. acceptChanges enable ].
acceptChanges action: [ changes text: ‘You are up to date’. acceptChanges disable ].

In this part we are done, now remember that in order to render the components we need to add it to the layout:

defaultSpec
<spec>

^ SpecColumnLayout new
add: #example;
add: #changes;
add: #acceptChanges;
yourself.

And we see:

3iguales

Quite easy and good, but lets improve the layout and play a little:

defaultSpec
<spec>
^SpecLayout composed
newColumn: [ :mainColumn| mainColumn
add: #example;
newRow: [ :feedbackRow | feedbackRow
add: #changes;
add: #acceptChanges]];
yourself.

2iguales

Better but still a little detail:

defaultSpec
<spec>
^SpecLayout composed
newColumn: [ :mainColumn| mainColumn
add: #example;
newRow: [ :feedbackRow | feedbackRow
add: #changes;
add: #acceptChanges
] height: 26.];

yourself.

final

A lot better! And It was quite easy, we can see that is well separated the layout from the components, and that we have a nice proposal for organizing our UI-Code:

  • We define the layout in #defaultSpec
  • We instantiate the components we are going to use in #initializeWidget
  • We define the interactions in: #initializePresenter.

This is very nice, but now I have a UI that separates the logic but still has a lot of responsibilities, is the UI who knows if I’ve accepted a change or if I’m up to date, also knows exactly what to tell me… I don’t think that this is the UI responsibility I want to model a class and that the UI shows it and not all in one like we have now.
And how do we do it?

Using a Model
We will have a model behind our window in 3 easy steps:
1. Create the model when initializing (you can create it or receive it, as you want, I will show the simplest way)

initialize
model := AcceptableText new. “replace for your domain class”
super initialize .

2. define the widgets, is almost the same that before

initializeWidgets
self instantiateModels: #(
example TextModel
changes LabelModel
acceptChanges ButtonModel ).

acceptChanges
label: ‘Ok’;
disable.

3. define the interaction

initializePresenter
example whenTextChanged: [:changed|
model text: changed.
self updateContents].

acceptChanges action: [
model acceptChanges.
self updateContents ]

updateContents
changes text: model status .
acceptChanges enabled: model hasUnreadChanges .

Look that when we are defining the actions we do 2 things:

1) modify the model
2) update the content of the widgets to show the new state of the model

We can see this actions that are using blocks as controllers (in a MVC pattern) since they modify the model from an input in the view and then update the view to show the new state in the model, in other frameworks we can use Bindings that do exactly the same.

In fact Ben pointed that Spec is far more closer from a MVP pattern than an MVC

In order to write a good UI we have to be very careful in how much code goes there, we probably will like to have small controllers delegating in the model instead of big controllers that does all the work for the model if we are not careful there we may end with anemic objects and spaghetti uis.

But to much talk and see how it ends:

final

Basically the same that before but now we have the linked ui using our domain object! Quite nice, and really really really simple to use.

Advertisements

2 thoughts on “A look into Spec

  1. Very nice primer! A few things:
    – if you annotate your default spec with , you can name the method whatever you want i.e. not necessarily #defaultSpec
    – I would mention adding the inst vars and their getters all at the same time. With some Spec versions, if you create the accessor in the debugger, it can lock up the image
    – In my experience, starting right away with a UI model is overengineering. One of Spec’s strengths is the ability to prototype quickly and most UI’s never seem to get to the complexity level to justify it. Your ComposableModel subclass basically *is* the UI model (i.e. the presenter).

    – S

    • I didn’t know about the annotation, I will edit the post!
      I agree with you, and Spec reaches the point in making easy to define a prototype, has nice widget and dealing with layouts is easy and we don’t have the layout manipulation while we define a component. Lots of points in that.
      And for this example we didn’t have the need of a ui model, but usually we have domain objects and then we think in how to show them, and is very common that we have one view that shows more than “one” object, in this point it’s nice to see one way to implementing it.
      I just wanted to show an example, thinking in the UI that I wanted to build for Flamel where I can match, edit, find, select scopes, change the example expression and apply a transformation and surely I will need more the collaboration of many objects trying to keep clean the ui and put less logic in these components, because then it’s hard to test.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s