A variation on the same principle is to simply serialize your data when it changes. Here is a 6 method long solution. There are several frameworks available: SIXX saves objects in XML format, ReferenceStream (SmartRefStream) in Pharo, BOSS (Binary object storage system) in VisualWorks, and ObjectDumper in GNU Smalltalk allow one to serialize objects in a binary format. These frameworks support cyclic references.
To illustrate this simple method, we show how Conrad, the conference registration system for ESUG (http://www.squeaksource.com/Conrad), applies such an approach. The root of the model is
CRConference. Whenever the application changes something in the model it calls
CRConference>>snapshot which saves the complete model. At start up we make sure that the latest version is always loaded.
CRConference class>>load: loads a specific version.
CRConference class>>initialize and
CRConference class>>startup make sure that always the latest version is loaded.
Since Conrad only manages one conference, it uses an singleton that can be accessed using the message
default and reset using the message
^ Default ifNil: [ Default := self new ]
Default := nil
CRConference class>>load: aString
| stream |
stream := ReferenceStream fileNamed: aString.
[ Default := stream next ] ensure: [ stream close ]
Afterwards we make sure that at image start up, the latest version is loaded. To do so, we specialize the class method
startUp which is invoked when the image is starting up.
| files |
files := FileDirectory default fileNamesMatching: '*.obj'.
files isEmpty ifFalse: [ self load: files asSortedCollection last ]
Smalltalk addToStartUpList: self
Now we save the data, by creating a reference stream in which we add date and time information for tracing purposes.
| stream |
stream := ReferenceStream fileNamed: (String streamContents: [ :filename |
format: #( 3 2 1 $- 1 1 2).
filename space; nextPutAll: (Time current print24
collect: [ :char | char = $: ifTrue: [ $- ] ifFalse: [ char ] ]).
filename nextPutAll: '.obj' ]).
[ stream nextPut: self ] ensure: [ stream close ]
We illustrated the principles using
ReferenceStream but it would be the same with another object serializer.
Again with 6 methods you get a working and robust solution that has some limits: first this is the responsibility of the developer to track when to save the objects, second, saving all the data each time one single element changes is not optimal and works for small models. Third with large data, saving subpart can be tedious because you may reload parts and you may have to swap the one in the image with the one loaded.