Talking Meta

Meta talk about Smalltalk, Seaside, Magritte, Pier and related things.

Disk Filesystem

A while ago Colin Putney announced the Filesystem framework, a nice and extensible replacement for the ugly FileDirectory class in Pharo. While all core classes are well commented, there is a quick start missing that explains how end users are supposed to adopt the framework. This blog post should fill that gap.

First we need to load the package:

 Gofer new
wiresong: 'mc';
package: 'Filesystem';
load.

The framework supports different kinds of filesystems that can be used interchangeably and that can transparently work with each other. The most obvious one is the filesystem on your hard disk. We are going to work with that one for now:

 working := FSDiskFilesystem current working.

Put the above code into a workspace and evaluate it. It assigns a reference of the current working directory to the variable working. References are the central object of the framework and provide the primary mechanism of working with files and directories. All code below works on FSReference instances.

Navigating the Filesystem

Now lets do some more interesting things. To list all children of your working directory evaluate the following expression:

 working children.

To iterate over all children recursively evaluate:

 working allChildren.

To get a reference to a specific file or directory within your working directory use the slash operator:

 cache := working / 'package-cache'.

Navigating back to the parent is easy:

 cache parent.

You can check for various properties of the cache directory by evaluating the following expressions:

 cache exists.             "--> true"
cache isFile. "--> false"
cache isDirectory. "--> true"
cache basename. "--> 'package-cache'"

To get additional information about the filesystem entry evaluate:

 cache entry creation.     "--> 2010-02-14T10:34:31+00:00"
cache entry modification. "--> 2010-02-14T10:34:31+00:00"
cache entry size. "--> 0 (directories have size 0)"

The framework also supports locations, late-bound references that point to a file or directory. When asking to perform a concrete operation, a location behaves the same way as a reference. Currently the following locations are supported:

 FSLocator desktop.
FSLocator home.
FSLocator image.
FSLocator vmBinary.
FSLocator vmDirectory.

If you save a location with your image and move the image to a different machine or operating system, a location will dynamically adapt and always point to the place you would expect.

Opening Read- and Write-Streams

To open a file-stream on a file ask the reference for a read- or write-stream:

 stream := (working / 'foo.txt') writeStream.
stream nextPutAll: 'Hello World'.
stream close.
 stream := (working / 'foo.txt') readStream.
stream contents.
stream close.

Please note that #writeStream overrides any existing file and #readStream throws an exception if the file does not exist. There are also short forms available:

 working / 'foo.txt' writeStreamDo: [ :stream | stream nextPutAll: 'Hello World' ].
 working / 'foo.txt' readStreamDo: [ :stream | stream contents ].

Have a look at the streams protocol of FSReference for other convenience methods.

Renaming, Copying and Deleting Files and Directories

You can also copy and rename files by evaluating:

 (working / 'foo.txt') copyTo: (working / 'bar.txt').

To create a directory evaluate:

 backup := working / 'cache-backup'.
backup createDirectory.

And then to copy the contents of the complete package-cache to that directory simply evaluate:

 cache copyAllTo: backup.

Note, that the target directory would be automatically created, if it was not there before.

To delete a single file evaluate:

 (working / 'bar.txt') delete.

To delete a complete directory tree use the following expression. Be careful with that one though.

 backup deleteAll.

That’s the basic API of the Filesystem library. If there is interest we can have a look at other features and other filesystem types in a next iteration.

Posted by Lukas Renggli at 14 February 2010, 2:10 pm with tags filesystem, pharo, smalltalk, tutorial 5 comments link

Dynamic Web Development with Seaside

cover.png The PDF version of the book Dynamic Web Development with Seaside is available to download now:

http://book.seaside.st/book/introduction/pdf-book

At the end of the payment process (PayPal) you will be redirected to the download area where you are able to get the latest builds of the PDF version of the book. If you bookmark the page you will be able to download fixes and extra chapters as we integrate them into the online version. By buying the PDF version you support our hard work on the book.

We wish to thank the European Smalltalk User Group, inceptive.be, Cincom Smalltalk and GemStone Smalltalk for generously sponsoring this book. We are looking for additional sponsors. If you are interested, please contact us. If you are a publisher and interested in publishing this material, please let us know.

Posted by Lukas Renggli at 27 January 2010, 3:27 pm with tags seaside, book, pdf comment link

Smalltalk/Ruby BlockCamp in Paris

Blockcamp.jpg I’ll be attending the BlockCamp the coming Saturday in Paris, a joint event between Smalltalk and Ruby. I am prepared to give 3 short presentations:

  1. Seaside is the framework for developing sophisticated web applications in Smalltalk. This can be either a short introductions into core features of Seaside, or a demo of the new features in the upcoming Seaside 3.0 release.
  2. PetitParser is a novel parser framework for Smalltalk (and Javascript). It embraces object-oriented design practices (composition, reuse) and combines several existing parser technologies (scannerless parsers, parser combinators, parsing expression grammars, packrat parsers) picking the best parts of each.
  3. Helvetia is a lightweight approach to embed new languages into a host language and the existing tools. This talk is for people interested into language models, language transformations, and tool integration.

I would like to thank ESUG for sponsoring my trip to Paris. I’ll be around Friday evening, so drop me a line if you’re in Paris and want to meet up. Hope to see you in Paris!

Posted by Lukas Renggli at 26 November 2009, 9:43 am with tags smalltalk, ruby, esug, paris, talk 5 comments link

Programmatically Run Lint

While most people prefer to run SmallLint (Smalltalk Code Critics) from within OmniBrowser, the question arises from time to time on how to do the same from within a workspace script:

1. Select one or more rules to run from the class hierarchy below RBLintRule. For example, the following expression would instantiate a single rule that searches for questionable message sends:

rule := RBBadMessageRule new

The following expression could be used to search for both classes that implement #= but not #hash and methods that do float equality comparisons:

rule := RBCompositeLintRule rules: (Array
with: RBDefinesEqualNotHashRule new
with: RBFloatEqualityComparisonRule new)

2. Next select the scope to run the rules in. In the most simple case this is the complete image:

environment := BrowserEnvironment new

You can however restrict the scope further, for example to the collection hierarchy:

environment := BrowserEnvironment new
forClasses: Collection withAllSubclasses

If you have the OmniBrowser refactoring tools installed you can easily browse a restricted environment by evaluating:

environment open

3. Finally you perform the actual search by evaluating the following expression:

SmalllintChecker runRule: rule onEnvironment: environment

Note that this might take a while depending on the size of the environment and the number of rules you run.

4. If you have the OmniBrowser refactoring tools installed you can have a look at the result by evaluating the following expression:

rule open

If you have an image without OmniBrowser loaded open an Inspector on the result to see the matching code. If you are running a transformation rule, use the following script to perform the change in your system:

change := CompositeRefactoryChange new.
change changes: rule changes.
change execute
Posted by Lukas Renggli at 19 November 2009, 6:04 pm with tags lint, refactoring, smalltalk, tutorial 1 comment link

Continue and Break in Ottawa

The second part of the Superpowers Workshop of the Ottawa Smalltalk user’s group caught my interest. The attendees are discussing on how to implement break and continue in Smalltalk.

Unfortunately the quality of the video was not good enough to be able to steal their example, so I had to build my own. The commented parts should trigger the well known loop operations and continue with the next iteration of the loop, respectively break the execution of the loop.

1 to: 5 do: [ :index |
index = 2
ifTrue: [ " continue " ].
index even
ifTrue: [ " break " ].
Transcript show: index; cr ]

The above example exercises the two hypothetical constructs. If you execute the code in your brain you should print out the numbers 1 and 3 on the transcript.

The first possible solution that comes to my mind are exceptions. Smalltalk has very powerful exceptions that can do all kind of magic things, but here we just need to signal and catch them at the right place. I created two new exception classes ContinueLoop and BreakLoop, then I put a handler inside the loop to continue, and a handler outside the loop to break. This gives us the first working solution. Admittedly it looks very ugly:

[ 1 to: 5 do: [ :index |
[ index = 2
ifTrue: [ ContinueLoop signal ].
index even
ifTrue: [ BreakLoop signal ].
Transcript show: index; cr ]
on: ContinueLoop
do: [ :err | ] ] ]
on: BreakLoop
do: [ :err | ]

Somebody of the Ottawa Smalltalk user’s group suggested to use continuations and they eventually come up with a pretty complicated solution. In fact, if you have the Seaside classes loaded in your image, the use of continuations provides a pretty strait-forward solution to the problem. You wrap the complete loop into a continuation to break, and the body of the loop in a continuation to continue. Evaluating the block arguments break and continue jumps right to the place after you created the respective continuations and resumes execution from there. This gives us the expected behavior:

Continuation currentDo: [ :break | 
1 to: 5 do: [ :index |
Continuation currentDo: [ :continue |
index = 2
ifTrue: [ continue value ].
index even
ifTrue: [ break value ].
Transcript show: index; cr ] ] ]

The solution is quite readable, but it basically uses a sledgehammer to crack the nut. Also continuations are pretty heavyweight to use in tight loops like this, because they essentially create a snapshot of the complete execution stack. And in this example we are not interested into the stack at all, we just want to jump back to the place where the continuation was defined. Luckily jumping back is something plain old Smalltalk blocks can do as well:

withEscaper: aBlock
aBlock value: [ ^ nil ]

This small helper methods evaluates aBlock and passes another block into it that returns nil. It is important to note that a return in Smalltalk always returns from the lexical scope, that means it returns from the method the return was defined in. In this example, evaluating the block [^ nil ] returns from the method #withEscaper:. Of course, this only works if the method, #withEscaper: is still on the execution stack (if not, we had to use continuations).

We can use the above helper method to implement the final version of our loop. This time without using magic, but simple and fast Smalltalk blocks:

self withEscaper: [ :break | 
1 to: 5 do: [ :index |
self withEscaper: [ :continue |
index = 2
ifTrue: [ continue value ].
index even
ifTrue: [ break value ].
Transcript show: index; cr ] ] ]
Posted by Lukas Renggli at 20 October 2009, 9:13 pm with tags smalltalk, continuation 4 comments link
<< 1 2 3 4 5 6 7 8 9 10 11 12 >>