Skip to content

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.

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.