Methods for "remembering" in an AppleScript

I understand that in an AppleScript compiled as an application, properties are retained until the script is recompiled when they take on their initialized values again. That’s a bit awkward for a script in progress.

I see two alternatives: call a second, rather simple compiled script with nothing to do but retain the data in properties, or write the data to be retained from a previous run to a text document so it can be read back from there.

Which of these is the better course given that I don’t need a database for this, just storage for a few words of data and a date stamp?

It’s up to individual applications if they want to do this or not. Applets do it. Studio-based apps don’t. Smile does it with scripts embedded in Smile dialogs, though not compiled scripts executed from its script menu. I can’t remember offhand if Mail does it with Mail Action scripts or not, nor if System Events does it with Folder Action scripts or not.

Not clear what you mean by that, though I’m guessing you mean you want to edit an applet’s code without losing persistent preferences every time you do. If you mean you want an applet to store persistent state externally, have it write that info to a preference file of some sort as you describe.

The simplest solution would be to convert your SE applet to a Studio applet and use Studio’s defaults system take care of everything for you.

If you really want to keep it as an SE applet, the next simplest solution is probably something like:

property _prefs : {foo:1, bar:"hello"}-- [a record containing your persistent preferences]

on _prefsFile()
	return POSIX file (POSIX path of (path to preferences) & "MyAppletPrefs") -- [your prefs filename here]
end _prefsFile

on loadPrefs()
	try
		set _prefs to read _prefsFile() as record
	on error number -43 -- file not found; use default values
	end try
end loadPrefs

on savePrefs()
	set f to open for access _prefsFile() with write permission
	write _prefs to f as record
	close access f
end savePrefs

where the applet keeps its persistent preferences in the _prefs record and calls loadPrefs() at startup and savePrefs() on shutdown. This is trivial for batch-processing applets, though if it’s a stay-open applet then guaranteeing loadPrefs is always called can be tricky and requires additional work. The prefs file isn’t human-readable, but that probably won’t matter for most cases.

HTH

I agree with hhas that the user defaults system is the way to go, although I’m not sure that you need to go so far as to use studio to do it. As of 10.4, system events has a set of property list commands that can read to and write from plists without casting values and in only a couple of lines of code. Check out “Property List Scripting - Tiger” for some basic examples. Plists are native, easy to implement, and handle any value class automatically simply by sending it to the command. The only disadvantage is that it’s only available in tiger, so you’ll have to find alternatives for lesser systems. You can use the defaults unix command with the shell script, but this is really only practical with strings, as it’s impossible to get arrays or dict’s parsed out of it easily. If you have to use arrays or dictionaries, and are bound to writing for 10.3-, then studio may be your only native option. As you’ve eluded, writing to a plain text database file is also a valid option, but has a lot of overhead in parsing the text, and can be error prone if your reading and writing routines aren’t solid. I consider keeping a separate script just to hold values a severe hack, and would never personally go that route myself. Seems unnecessary and dangerous.

j

Thank you for the link and advice, Jobu. As I’m only interested in doing this on a few Tiger machines, the plist route is the way to go.

PS: I considered the external script an extreme hack also except that unlike a text file, it’s not easily readable. has says plists aren’t either.

a

Hi Adam,

If you’re interested, an idea came to me that you can save properties of a script app in a script server. I started working on a subroutine that adds a record to the script server’s property where it stores properties from script apps or from a run in the Script Editor. It seemed like it would be easy, but the old hurdle arose where you can’t create a record label from text. I’ll probably continue trying for at least a week, and the script may need some workarounds, but if you’re interested in storing a script’s properties in a script server, then reply.

Here’s an article on script servers:

http://www.applescriptsourcebook.com/tips/scriptserver.html

Basically, the script server is a stay open app that scripts querry for subroutines and properties.

Almost got it,

The advantage of Studio is that it handles loading and saving of preferences automatically.

I wouldn’t call storing preferences in a serialised record, or even serialised script object, a ‘hack’; it’s a perfectly valid solution to the problem. The files aren’t easily readable, but that’s not an issue unless they need to be human-tinkerable.

Not here. It is true though that plists can be binary format as well as XML; it’s one more reason why one should use the official APIs to deal with plists, since they’ll deal with such details for you.

Script server’s unnecessary; everything you need to save serialised AppleScript data to disk already exists. The script I gave will store most types of data; the only stuff I can think of offhand which it can’t store is script objects and application references, which aren’t the sorts of things you’d normally want to keep as preferences anyway. And if it is an issue, you can rework the thing to use ‘load script’ and ‘store script’ and store all the settings as a serialised script object; I just used a record 'cos it’s a bit simpler and easier to understand.

Records aren’t associative lists, and you shouldn’t try to use them as if they were. If you need an associative list, use one; they’re not too hard to write, and you’ll find a couple of good, robust ready-made ones in AppleMods’ Types library that you can use for free.

has

Hello,

As an alternative, I’ve used the unix defaults command to do this in the past.

set myPrefDomain to "com.myUniqueDomain.myPrefs"
-- something that's not likely to be used by anyone else

set myVar to text returned of (display dialog "What do you want saved?" default answer "I'd like this saved.")
--Write to your Prefs folder
do shell script "defaults write " & myPrefDomain & " myVar " & quoted form of myVar

--Read from your prefs folder
set myStored to do shell script "defaults read " & myPrefDomain & " myVar"
display dialog myStored

It is important that the name of the domain is unique. ‘defaults write’ writes a plist file in your preferences folder, if one already exists with that name the preference will be written into it (overwriting one with the same key if it exists).

See man defaults in the Terminal.

Best wishes

John M

Thanks kel, hhas & John M for the replies. For the purposes of the script I was working on, hhas’ suggested “loader-saver” is more than adequate: I just want to save a few variables as records from a script run from a FastScripts hot key, don’t care much about a readable or XML format as long as I can get the record back easily, and only have to run this on one machine running Tiger.

I’ve just lifted hhas’ script and put it into my own. Saving the data this way substantially reduces the need for a bunch of external date verifications because the script produces a series of locations and timings for a medication injected cyclically but infrequently (hence the need for a reminder) that depend on the system clock. A record like this provides a strong way to produce the result in two entirely different ways, only one of which depends on the clock. I wrote a second scriptlet to initialize the data and to check that I’m catching possible errors. In addition, the person using it is compos mentis, so there’s a third check.

I’m a serious noob when it comes to Applescript and coding in general. Can someone give me a quick run-thru of how this might work in a script. Calling the handlers, what gets passed to the handlers, etc…

TIA
jlindeman

Model: Powermac G5 Dual 2.0
Browser: Safari 417.9.2
Operating System: Mac OS X (10.4)

JL;

Best I can do on short notice below. I’ve used John M’s version from above. The script, when first run, looks for a file called
“com.J_Lindeman.ScptA.prefs” that you’ll later be able to find in your ~/Library/Preferences folder. It doesn’t find it and so errors with a dialog telling you that. It asks for the data and stores it, then presents what it stored, then modifies the data and saves it again and asks you to quit the Script Builder and then restart this script, run it again, and see the latest values.

-- This is a modification of a script by John M.
-- set up some properties - these have the advantage of working anywhere, whereas an ordinary variable or handler name will not carry into a handler normally.
property Height : missing value
property Weight : missing value
property myPrefFile : "com.J_Lindeman.ScptA.prefs"
-- a name that's not likely to be used by anyone else

-- Test for a first run
try
	set Height to do shell script "defaults read " & myPrefFile & " Height"
	set Weight to do shell script "defaults read " & myPrefFile & " Weight"
	set flag to "Data From File"
on error theError
	display dialog theError
	set Height to text returned of (display dialog "What is your Height in Inches" default answer "")
	set Weight to text returned of (display dialog "What is your Weight in Pounds" default answer "")
	do shell script "defaults write " & myPrefFile & " Height " & quoted form of Height
	do shell script "defaults write " & myPrefFile & " Weight " & quoted form of Weight
	set flag to "Used Fresh Data"
end try
display dialog Height & space & Weight & space & flag
-- now do something to the data
set Weight to Weight * 1.1 as string
set Height to Height * 1.1 as string
-- store it again
do shell script "defaults write " & myPrefFile & " Height " & quoted form of Height
do shell script "defaults write " & myPrefFile & " Weight " & quoted form of Weight

display dialog Height & space & Weight & return & return & "Now quit, reopen the script and run it again."

Thank you Adam for the (suprisingly) quick response. That was extremely helpful. Now I just to make it work with the ugly little script I’ve hacked together. I appreciate your taking the time to help.
Jlindeman

A learning experience for me too JL; I had never used John M’s version, and I like it. No such thing as an “ugly” script, though some are more elegant than others.