Dynamic Web Development with Seaside

25.3.1The SandstoneDB API

SandstoneDB has a very simple API for querying and iterating on the classes representing the repository for their instances:

Class Query API. The API looks a lot like the standard Smalltalk collection protocol slightly renamed to make it clear these queries could potentially be more expensive than just a standard collection.

  • atId: and atId:ifAbsent: allow one to access an instance of the receiver based on its ID. Here is an example of atId: use.
handleAccountEnable: aRequest
| auction |
(aRequest url endsWith: '/enable-account') ifTrue:
[ auction := CAAuction atId: (self fieldsAt: #id).
self session pendingAction: 'User has been enabled!' ->
[ self session user isAdmin ifTrue:
[ auction seller
isDisabled: false;
commit ] ] ]
  • do: iterates over all the instances of the class but does a copy in case the do modifies the collection.
  • find: returns the first instance satisfying the predicate, as do find:ifAbsent: and find:ifPresent:. findAll: returns all the instances that match a predicate. Here is an example of findAll:.
isValidAuctionVin: aVin
^ (CAAuction findAll: [ :each | each vin = aVin ])
allSatisfy: [ :each | each isClosed ]
  • findAll returns all the instances of the class.

Instance API. There’s a simple API for the instance side:

  • id returns a UUID string in base 36 which uniquely identifies the instance.
  • createdOn and updatedOn return the timestamps of the creation and last update of the instance.
  • version returns the version of the instance. The version is increased for each save. It is useful in critical sections to validate you’re working on the version you expect.
  • indexString returns all instance variable’ asStrings as a single string for easy searching.

Instance Actions. Here is the list of actions you can perform on a record.

  • save saves the instance but is not thread safe.
  • critical: grabs or creates a Monitor for thread safety.
  • commit is just a save in a critical: session.
  • commit: is similar to commit but you can pass a block if you have other work you want done while the object is locked.
  • abortChanges rolls back to the last saved version.
  • delete deletes the instance.
  • validate is a hook that subclasses can override to specify validation action and throw exceptions to prevent saves.

Here are some trivial examples of using an SDActiveRecord.

person := Person find: [ :each | each name = 'Joe' ].
person commit.
person delete.
user := User
            find: [ :each | each email = 'Joe@Schmoe.com' ]
            ifAbsent: [ User named: 'Joe' email: 'Joe@Schmoe.com' ].
joe := Person atId: anId.
managers := Employee findAll: [ :each | each hasSubordinates ].

The framework offers some hooks that you can override on record life cycle events. But pay attention to invoke the superclass methods.

  • onBeforeFirstSave
  • onAfterFirstSave
  • onBeforeSave
  • onAfterSave
  • onBeforeDelete
  • onAfterDelete

There is also a testing method you might find useful: isNew answers true prior to the first successful commit.

Copyright © 19 March 2024 Stéphane Ducasse, Lukas Renggli, C. David Shaffer, Rick Zaccone
This book is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 license.

This book is published using Seaside, Magritte and the Pier book publishing engine.