Revert within procedure problem

I’m another who often used paired save and revert statements in my Pan6 procedures. revert was listed as implemented in PanX a couple months back, but it’s not working reliably in my converted code. I offer a simple reproducible example of revert not working properly although I’m not certain my converted code’s problem is identical.

Make a new empty database. Set the value of the one record for field A to “start”.
Enter the following procedure, save the db then run the procedure from the action menu:

Save
A="changed"
revert
alertsheet "reverted"

The value of A is now “changed.” It doesn’t revert to “start” and the alertsheet doesn’t run. Choosing Revert To Last Saved Version from the File menu doesn’t turn A back to “start” But Revert to Browse All Versions does offer a version with “start.”

After fiddling with it a bit (mainly manually editing, undoing and redoing the value a few times) and rerunning the procedure a few times the alertsheet eventually ran, although I’m not sure what actually got the code working.

I could, in part, work around this problem by using Saveacopyas instead of Save, then importdatabase theCopy,"Existingdata","Replace" instead of revert. I looked at StartDatabaseChange for an alternative. It works well for manually undoing procedures, but I don’t see any interface for using its undo within procedures. undo isn’t listed as a statement in Help and gives a syntax error when typed into a procedure window.

Panorama X uses Apple’s code for all opening, saving and reverting of files. This is done thru an Objective-C class called NSDocument. When a program uses NSDocument, it automatically gets all of the standard document handling features – auto-save, versioning, etc. The problem is that Apple designed this in such a way that it is very difficult to get in and customize what is happening, and Apple’s engineers never imagined that an application would offer a programming language that would allow end-users to get in an try to make customizations.

When using NSDocument, the revert command rewinds the file back to the last time File>Save was used. I think the problem may be that the save statement in Panorama’s programming language isn’t quite the same thing as using File>Save. Unfortunately, Apple’s documentation on this is poor – non-existant really.

There is a way that you can invoke the File>Save command in programming code, using the sendaction statement. Here’s a possible revised version of your code:

sendaction "saveDocument:"
A="changed"
revert
alertsheet "reverted"

You should try this, but my guess is that it won’t work, or won’t work reliably. I believe that the File>Save command is asynchronous – in other words, the saving happens in the background. So it’s possible that the save operation might still be going at the time the revert statement is run.

The saving code used by the save statement is also asynchronous, but this statement has code that waits until the save is complete before letting the program advance to the next statement.


As you mention, there is no undo statement, but you can use sendaction to trigger an undo. So you might try this.

startdatabasechange "currentrecord" // adjust this if more than one record will change, your app probably needs "allrecords"
A="changed"
sendaction "undo:"

Again, there may be an asynchronous aspect of undo, I’m not sure, so if there are additional statements after the sendaction, they may run before the undo is finished. Probably best if the undo can be the final statement in the code.


The sendaction statement is basically a way to access internal code in Panorama and the operating system. In fact, in both of these examples, Apple code is being directly triggered. So this isn’t something that I can support, but it does give you options to try.

Jim,
Thanks for the bug fixes in today’s release! And thanks for a very informative answer here, even though you can’t offer a direct fix. Such is the price we pay for your strictly adhering to Apple standards (which I understand and endorse!) Provue, and I suspect many of its users, is more into ‘pushing the limits’ than Apple so is apt to run into Apple limits. But, if we can identify those limits, the history of this forum suggests your users are apt to find ways around them. I certainly can experiment with your sendaction suggestions and I’ve got a couple more alternatives to run by you.

Your answer suggests my above backup plan of converting ‘save/revert’ pairs to ‘saveacopyas/importdatabase’ pairs may run into the same Apple problem, if saveacopyas is also asynchronous. I presume the problem is that Apple doesn’t document a way to determine when an asynchronous save process has completed. Might the Finder modification date and time (which PanX can access) be useful for that? I’d expect it to change sometime during a successful save. Might PanX check that before the save and sit in a loop of some sort until it changes? Or do you have reason to expect that wouldn’t work well?

The advantage of the old save/revert was it undid any changes in data and database structure back to a known starting point. My procedures are intended to end up with the starting database structure, although their data may end up different and their intermediate structure may vary. With a little thought, I should be able to programatically reverse any structural changes (eg. field names, types, order, sort order, summary level, etc.) I made along the way, one step at a time. Or can I save the starting structure in a dictionary or a blueprint then restore that in one step? I can’t reverse data changes that way, but if I had a pre-made copy of the database I could importdatabase the main db to the copy, in place of save and then importdatabase the reverse, in place of revert. Thus avoiding any intermediate save statements or even disk access. I think importdatabase is synchronous as procedures wait for it to finish. If both copies have the exact same database structure I’d guess the default sequential option of importdatabase would be faster than the matchingfields one. I’d save the copy with all data deleted at the procedure end to not waste diskspace.

Well, it is definitely a bug (at least I assume it is, I haven’t independently verified it yet). I’ve filed a bug report.

The detailed explanation is because I find it rather embarrassing that I don’t know how to immediately fix this. Certainly in the previous “classic” versions of Panorama revert was a fairly simple, straightforward operation – just load the file back into RAM and update the display. Getting Panorama X to work within the NSDocument framework was a fairly major endeavor, it took months, and it has been cantankerous to deal with at every turn. I think the effort is well worth it for the benefits, but I wouldn’t mind if Apple had made it a bit easier!!

I thinkimportdatabase is synchronous

Yes, it is, everything happens in RAM. In fact, NSDocument is not involved in that case.

I’d guess the default sequential option ofimportdatabasewould be faster than the matchingfields one.

Yes, but whether that difference is significant or not would depend on how many fields there were.

As far as restoring the data goes, you could also just use

// save data
local tempdata
arraybuild tempdata,cr(),"",exportline()
...
...
... modify the data
...
...
// restore data
importtext tempdata

I believe that should be reliable and fast no matter what type of data you have in your database, and would not involve any disk i/o.

Just reporting followup results on my example db. Using sendaction "saveDocument:" instead of Save resulted in A being “changed” although the alertsheet did display.

Using StartDatabaseChange and sendaction "undo:"
resulted in the procedure working as intended. A ended up “A” and the alert sheet did display.

I’ll do more testing with that method, although not initially in the routine where I first discovered this. That routine is an old kluge. I have many less confusing (to me!) examples of my save/revert routines on which to test this possible workaround.