[Seaside] Seaside 2.2

Avi Bryant avi at beta4.com
Sun Feb 2 20:27:15 CET 2003


On Sun, 2 Feb 2003 tblanchard at mac.com wrote:

>
> On Sunday, February 2, 2003, at 02:57  AM, Derek Brans wrote:
>
> > Hi Avi,
> > In particular, I would like to know more about changes to state
> > handing and how it affects applications.

In Seaside 2.1, there was a class of objects (subclasses of
WASessionObject, including WAComponent), whose entire state was
"backtrackable" - if the user used the browser back button, the instance
variables of those objects would "rewind" their values to match.  This had
several problems: first of all, it was inefficient, in that the (shallow)
state of those objects had to be snapshotted before every response,
when most of it didn't need to be.  It was also difficult to port (for
example to Dolphin) because it relied on a modified compiler that trapped
instance variable reads and writes.  Finally, it didn't integrate well
with tools like the inspector and debugger, because the values weren't
stored in the actual instance variables of the object where those tools
expected to find them.

Seaside 2.2 makes you explicitly use StateHolder objects for variables
that you wish to have this "rewind" property.  It adds a small
inconvenience (for rewindable state, you must use #contents and #contents:
rather than directly accessing the instance variable), but gives you more
control over which variables needs to be treated this way and which
don't, and is much more straightforward to implement (and port).

In terms of how this affects your applications, it means that any UI state
(usually inst vars of your Components) that needs to be backtrackable,
needs to be managed with StateHolder objects.  The mechanics of doing this
are fairly simple:

- in your component's #initialize method, get the needed StateHolders from
the Session:

initialize
  count := session stateHolder

- use the "abstract instance variable" pattern, with accessors everywhere
instead of direct reads and writes:

increment
  self count: self count + 1

- write the accessors to use #contents(:)

count
 ^ count contents

count: aNumber
 count contents: aNumber

> While you're at it, could you elaborate on how seaside state management
> interacts with persistent object state management?
>
> For instance, 2 pages back, the user was working on an object to create
> that he can only create once...now he's saved it, so going back two
> pages to muck around isn't good.  Its just an example but I think you
> know what I mean - how to keep seaside state and db state in harmony.

As Julian pointed out, the trick is to use #isolate: blocks - you often
want to match a database transaction with a Seaside #isolate: transaction,
so that as soon as you commit a set of changes to the DB, all of the pages
involved in making those changes expire.  It occurs to me that it may be
worthwhile to add an #expireAll method to Session in some such cases as
well (#burnBridges?), but I prefer the localized expiry of #isolate:.

It's a complicated issue, however, and I don't pretend to have all the
answers.  One of the problems inherent in web applications, for example,
is that a user can open a transaction and then quit the browser and walk
away without ever having finished it; or, they can open several such
transactions in parallel and ignore most of them.  Writing code that's
robust in these cases can be frustrating; the most realistic solution
seems to be to abandon the idea of long running (database) transactions,
and wrap each request in BEGIN/COMMIT, but this isn't very satisfying.
It is also sometimes possible to have multiple concurrent transactions to
the same database from the same session, but the feasibility of this
depends a lot on how your OODB or OR Mapping system works (typically it
requires having the same instance look different to different Processes or
at least have its state jump back and forth as different transactions are
activated).

Avi



More information about the Seaside mailing list