Friday, 20 May 2011

User group marketing

The Toronto Smalltalk User Group has been active for 20 years. At our peak we had about 45 people attending meetings. Lately we've averaged about a dozen.

What can we do to get more people to attend?

I suspect we already know all Smalltalkers in the Greater Toronto Area, if not personally then by just one level of separation. Our best bet is to target new people that may have an interest. We've had some success with Ryerson students. The two professors that sponsor our group encourage the students that take their OO course to attend. It has some Smalltalk content (Pharo) and a couple of students have come out.

Some of the TSUG crowd have attended XP Toronto events. They have regular meetings, pair programming pub nights and they organize well run Agile conferences. Those that know about Smalltalk respect it because of XP's roots, but they tend to be surprised that it's still an active language.

Yanni Chiu has been something of  a TSUG ambassador, representing Smalltalk at things like the "Dynamic Languages Smack Down". He and Chris Cunnington signed up for Toronto Startup Weekend and plan to show some Seaside app. I had some tentative plans to talk to the Durham Ruby group (something I need to follow up on).

Whenever it's appropriate I hand out TSUG pamphlets. They list all the Smalltalk dialects, web frameworks and projects I know of. I print off a new batch for each TSUG meeting, with updated meeting schedules and conference information (ESUG in in the current one). It's an MS Publisher document which I print off on heavier stock paper which has a light blue tint. Here is what part of it looks like...


I'd love to get suggestions on how to promote the group.

Malcolm Gladwell in his book Outliers makes the point that people with very high IQs are not more likely to be successful than people with just 'good' IQs (120, evidently). Once you're smart enough, other factors like people skills and communication skills make the difference.  Smalltalk has an IQ above 180, the other tools are probably in the 120 range. Smalltalk needs to work on its people and communication skills.

Tuesday, 3 May 2011

Pushing Smalltalk

The next meeting of the Toronto Smalltalk User Group is May 9 with Don MacQueen talking about JWARS.
See the web site for details.

I've been involved with the Toronto Smalltalk User Group for 20 years now, the past dozen or so as the primary organizer. Lately we've had a few people show up that were new to Smalltalk and wanted to learn more. We talk to them about the simple syntax, show them Pharo and Seaside, tell them about the other dialects, and try whatever we can to get them enthused.

But there is one core strength that I value which I cannot easily demo: the lack of brick walls.

When working with tools like MS's Visual Studio, I'm constantly frustrated by the lack of universal object inspection and 'down to primitive' code tracking. Coding something new and with unfamiliar APIs gets a bit messy. You don't always know what you need until you trip over its absence. I find myself adding diagnostic code to show me stuff, which in Smalltalk I'd just debug and inspect. And sometimes in VS you just can't get what you want; you hit a brick wall. If it were not for Google and people posting esoteric workarounds, I don't know how I'd get anything done. And I love the examples that say "don't forget the comma, or else it won't work"... no error message, no warning, just no output.

Thing is, that's not the kind of thing you appreciate until you try something complex, which does not happen to a new user. I wonder if we would benefit from having code examples with pre-defined bugs, written in various languages, and then using them to show how you'd debug the problem. Show just how few barriers we have in Smalltalk to understanding what is going on in our code (and, more importantly, in other people's code).

I used to tell people that I saw a lot of similarity between programming in MVS 360 assembler and Smalltalk. For me, transparency was the big stick that I could use to whack the problem. From what I've seen of other IDEs (an admittedly short list), Smalltalk still does that the best.

Friday, 22 April 2011

Smalltalk trivia

I miss the days of The Smalltalk Report and discussions on comp.lang.smalltalk. It was a time of lively debate about syntax, naming conventions, coding patterns, dialects ... all the signs of an energetic community. It's not like that any more. I think it's due to the fragmentation of the Smalltalk forums. The threads are interesting, but specialised.

My coding is strongly influenced by Kent Beck and his coding patterns. I remember reading his column in The Smalltalk Report and seeing the evolving consensus of how to code Smalltalk. My iteration block still have :each or :each<something> as the binding variable.  In his "Fundamentals of Smalltalk Programming Technique",  Andres Valloud makes the point that code formatting is a pointless debate and that we should be comfortable reading any Smalltalk code. And he's right. But I'd still like to know what others find more readable. It's like learning to write gooder English.

For example, I trip over the extra white spaces inside blocks that most of the Seaside code has. Where I'd write a method like...

aBoolean

    ifTrue: [self doThis]
    ifFalse: [self doThat].
aList do: [:each | self doSomethingWith: each]

...I find the Seaside code is formatted as...

aBoolean

    ifTrue: [ self doThis ]
    ifFalse: [ self doThat ].
aList do: [ :each | self doSomethingWith: each ]

...I wonder why that convention was adopted. In my eyes the block is just not hugging the code enough. I don't find that style in other Smalltalk code. And it really does not matter; I'd just like to understand the though behind it. 

Another example: In our VW application,  the framework code uses abbreviations for local and parameter variables, so I see a lot of code like printOn: aStrm and | errMsg idx t v m s | ... drives me nuts. But it was adopted because of how easy it used to be to name a local variable the same as an instance variable. We do refactor code this kind of code now as we maintain it, but it's easier to accept when the thought behind it is understood. 

And here's a pattern question: when is it better to double-dispatch? In our VW code was have an #echo method which writes a printString of the receiver to the Transcript, like the newish VW #out method (unfortunate name conflict with an Opentalk method). We also have #echo: which writes both the receiver and the argument to the Transcript, so...
    'Time' echo: Time now
...shows in the Transcript as...
    Time 10:10:54 AM

I added the same code to our VA application. So what's the best way to write this method? We don't want strings to be printed with single quotes, like...

Transcript cr; show: 'this string' printString  

'this string'
vs.
Transcript cr; show: 'this string'
this string

...so we can implement #echo on Object as Transcript cr; show: self printString and on String as Transcript cr; show: self. But what about #echo: ? We could implement on Object as...

Object>>echo: anObject

    Transcript cr; show: self printString, ': ', anObject printString

...(I prefer the : delimiter to just a space) and we'd need...

String>>echo: anObject

    Transcript cr; show: self , ': ', anObject printString


...but what if anObject is a String? You would end up with superfluous single quotes. You could add a new method like #asEchoString with an Object and String implementation and send that instead of #printString, which is easy to read, or you could try double dispatching, which I decided to do (mostly out of curiosity). In this case I don't think it's a better pattern, but it's interesting (and very useful for more complex problems). And I like that they are all one line method.

Here is what my implementation (no value returned so that a := b echo answers b)...

Object>>echo
self printString echo

String>>echo

Transcript cr; show: self.

Object>>echo: anObject

anObject echoAfter: self printString

String>>echo: anObject

anObject echoAfter: self

Object>>echoAfter: aString

aString echoString: self printString

String>>echoAfter: aString


aString echoString: self

String>>echoString: aString

Transcript cr; show:  self , ': ', aString


'test' echo: 123    >> 'test: 123''test' echo: 'this' >> 'test: this'123 echo: 'this'    >> '123: this'123 echo: 456       >> '123: 456'  



Tuesday, 19 April 2011

VXML server isolation

Our IVR VXML Seaside server needs to handle up to 700 calls per hour. Normally that's not a problem, but we do get a few timout errors per week, which is why we're moving to a three image load balanced deployment.

Recently we had another issue with our current setup: the server hosting the file share, to which call files are written, failed. Calls were still processed, but the final write failed. Write are done in a forked block with an exception handler, so the image dealt with the errors cleanly. And the call data was all written to a log, so no data was lost.

However, there was a performance hit on the server image and we started to get timeout errors on more than 10% of the calls. So we turned off the Seaside server which triggered an automatic failover to our AWS hosted server. It works, but it's a bit slow. Callers could wait several seconds for the call prompts (we'll be upgrading it soon).

This was the first time we had an error with the file share and it pointed out a dependency that we'd prefer to avoid. Ideally, the VXML servers would continue to process calls even if other servers are down; they should as isolated as possible.

Our strategy is to use the VW Opentalk image to image interface and have a data server gather call information from the three VXML servers. It will also send them updated validation files. This way, the VXML servers have no folder share dependencies,  the data server can show status across all three VXML server, and the VXML servers can function even if the entire back office is down. And we can host the data & VXML servers with different service providers.

We've been using Opentalk in our client patch system for a couple of years now. It's familiar and reliable. And ya gotta love the deployment flexibility you get with Smalltalk.

Friday, 15 April 2011

IVR VXML from Seaside

We use Seaside to serve anything that takes data from an http call. One of these services is an IVR system hosted by Voxeo. It requires a VXML file that defines the audio script and records answers. We have a sublcass of WARequestHandler that answers static VXML content for the initial request:


handleRequest: aRequestContext
| response | 
response := aRequestContext response. 
response 
contentType: self contentType; 
nextPutAll: self document requestVXML.
aRequestContext respond.


...and builds contents for the response, which contains a simple 'Thank you' if all the data is valid...


handleResponse: aRequestContext
| response result request | 
request := aRequestContext request.
result := self processCall: request.
response := aRequestContext response. 
response 
contentType: self contentType; 
nextPutAll: result.
aRequestContext respond.


Our production system, which our client uses to record at-home service calls, peaks at about 600 calls per hour. And the same image that serves the VXML file also has a conventional Seaside interface for monitoring the system and for configuration. Very handy. For example, we have a Google graph to show one day's call distribution.



Recently we added more validation to the VXML script, so that shifts and employee numbers could be checked during the IVR dialog. The new interaction places more demands on the Seaside image, so we're testing a deployment with three images and simple round-robin load balancing (the current single image deployment gets about two timeout errors per week). Each VXML call is RESTful so we have no need for session affinity. Testing all went well, until something interesting came up.

Testing was all done on a Windows server and deployment is on a Linux box. No big deal for VW. But when we tried the new deployment, the IVR system said the VXML content had errors. Looking at the VXML file on our Seaside display showed extra blank lines, but the content was correct. Inspecting the file content as read showed that the <cr><lf>s were all replaced with <cr><cr> (you gotta love all the delimiter differences between Windows, Linux and Smalltalk). I didn't think that IVR server would have an issue with that, but to be sure I replaced the file read from...
    ^file contentsOfEntireFile
...to...
    ^file contentsOfEntireBinaryFile asString
...and it turns out that fixed the problem.

Makes we wonder what people do who work with tools that don't allow the deep diving that Smalltalk does. I suspect that when it all works their productivity is high, but there must be more frustration when things break.

Thursday, 14 April 2011

VA Smalltalk and Seaside

I support a VA Smalltalk app for which we're adding a web interface, using Seaside. The first feature we've ported is a 'Site Map' view, a hierarchy of GIF images with configured 'hot point' rectangles for navigation. Users click to drill down to more other images or to display data in a table.

In VA the display is updated by replacing the 'image area' #labelPixmap and adjusting the window shell width and height. 'Hotpoints' are managed by mapping 'Pointer Motion' and 'Button Press' callbacks to a collection of objects that define the rectangle, which shows an action specific mouse pointer, and and triggers the action when the mouse button is pressed. All of this is user configurable using a view where they add image, draw rectangles and define actions.

A few years ago we added a feature which optionally displayed the current value of a measurement in the displayed image. It eliminated the need to click on a data 'hot point' and was handy for images with several data points. We've now added the same capability to the Seaside view.

In VA, the data is displayed by first defining instances of CwLabel in the imageArea (a WkImageWidget) at the correct position. The #labelString and #backgroundColor are then set based on user parameters, like the display date. Background color is used to show the 'quality' of value, so a grey background indicates incomplete data. To hide the data, the CwLabel's #visible property is set to false.

This is what it looks like in VA...


To do this in Seaside, we use a style 'position:absolute; left:10px; top:140px; z-index:-1;' for the GIF and then render the displayed data with...
 'position: absolute; top: %1px; left: %2px; z-index:2; border: 2px solid black; background-color: %3'
...using #bindWith:with:with: to set the top, left and background-color. The result looks like this...


I continue to be impressed with how easy it is to do interesting web work in Seaside.

Monday, 11 April 2011

Dynamic Wiki

We use an internal MediaWiki web site for document and status reporting. Combining our static wiki text with dynamic output from our issue library and patch manager has worked very well for us. We use a MediaWiki webservice extension to add graphs, tables and issue lists.

For example, this code will get a list of my issues...

{{#webservice:<our internal web server>/Issues?wiki=user:Bob_N.| %//div%}}

...the output can be tested by using the URL directly in a browser, which answers wiki formatted output...
==Bob N. - current== 
{| ! ID ! System ! Client ! Title ! Stage ! Text ! P. ! Assigned ! Date ! Type |- | [http://<our internal web server>/Issues?issue=3353 3353] || BID || CS || HTS Customer Portal: changes to project display || [[image:Blueplay.png]] current || '' '' || 3 || Bob N. || 2011-04-11 || Modification |- |...etc.

...and, when parsed by MediaWiki, shows as a list of issues...


...with links back to the issue library. We combine the web service extension with Google charts to add live charts to our wiki, using an entry like...


{{#webservice:<our internal web server>/Issues?Issues?wikichart=backlog| %//div%}}

...which generates wiki formatted text that contains a Google Chart API call...


<websiteFrame> website=http://chart.apis.google.com/chart?&cht=bvo&chs=800x350&chg=0,10,4,0&chtt=Working+Issue+Backlog&chts=19293D,20&chbh=24,30&chco=00FFFF,00CCFF,0099FF,0066FF,0033FF,0000CC,000066&chdl=a: week|b: 2 weeks|c: month|d: 2 months|e: quarter|f: 2 quarters|g: 6 months&chd=t:0,0,0,0,0,0,0,0,0,0,0,0,795|0,0,0,0,0,0,0,0,0,0,0,766,767|0,0,0,0,0,0,0,0,0,0,0,730,699|0,0,0,0,0,0,0,0,0,0,660,661,639|0,0,0,0,0,0,0,0,0,540,543,491,474|0,0,0,0,0,0,290,465,456,398,381,344,335|141,182,214,254,282,289,256,235,76,72,69,54,54&chds=0,795&chxt=x,y&chxl=0:|Apr 2010|May 2010|Jun 2010|Jul 2010|Aug 2010|Sep 2010|Oct 2010|Nov 2010|Dec 2010|Jan 2011|Feb 2011|Mar 2011|Apr 2011|1:|0|79|158|237|316|395|474|553|632|711|790 name=Working Issue Backlog align=middle height=380 width=800 border=0 scroll=no longdescription=chart </websiteFrame>

...again, this can be tested directly in a browse to show the expected charted (the data shows a ramp up of using the issue library)...