The Preferences Pattern
When writing [my AppleScripts](http://applescript.plaidcow.net/) I try to allow for enough flexibility for each user to set the script up to his/her liking, without requiring that all of the questions be answered each time the action is performed. In other words: saved preferences.
In my latest scripts to be updated ([Rename Files][] and [Remove Missing Tracks][]), I have switched to a new form of preferences (writing a structure to a file) and have tried to create a series of functions and properties which can work together and be the same in all of the scripts.
[Rename Files]: http://applescript.plaidcow.net/iTunes/RenameFiles/
[Remove Missing Tracks]: http://applescript.plaidcow.net/iTunes/RemoveMissingTracks/
The `preferencesFilename` property stores the name of the file that will be stored in the preferences directory. All of these files names should begin with `net.plaidcow.`
The `LoadPreferences()` function is responsible for loading the preferences from the disk and assigning them into the global variables. A template of is shown in this code
(Edit):
on LoadPreferences()
SetDefaultPreferences()
set theFile to (path to preferences from user domain as string) ¬
& preferencesFilename as file specification
try
set thePreferences to item 1 of (read theFile as list)
-- Set all of the globals based on the data in the file,
-- wrapping each set statement in its own try block
try
-- Set the global to the data from the file
end try
end try
my UpdatePreferencesDependencies()
end LoadPreferences
The function starts off with a call to `UpdatePreferencesDependencies()` which will ensure that all of the preferences have been set to valid values. Since the `read` command is located within a try block, not error will be thrown if the file doesn’t exist. Assuming that the file was read, each of the parameters can then be read out of the structure that was read from the file. By wrapping each of the command to set a single parameter in its own try block, any parameter can not exist the the reading of the preferences will not fail. (This should allow the adding and removing of parameters between versions with no ill effects.)
The `SavePreferences()` function calls is a simple aggregation of the global preferences into a list, and then the writing of that to the `preferencesFilename`. In this sample code (Edit), the global `notificationType` is being saved:
on SavePreferences()
set thePreferences to {notificationType:notificationType}
set theFile to (path to preferences from user domain as string) ¬
& preferencesFilename as file specification
try
open for access theFile with write permission
set eof of theFile to 0
write (thePreferences) to theFile starting at eof as list
close access theFile
on error
try
close access theFile
end try
end try
end SavePreferences
`UpdatePreferences()` calls a series of user interface command to get the user to input new values for some or all of the preferences. After inputing these, the `UpdatePreferencesDependencies()` function is called. Unless another mechanism is being used, this is the time to cal `SavePreferences()`
The `ResetPreferences()` function calls the private `SetDefaultPreferences()`, followed by private `UpdatePreferencesDependencies()`. This code should be the same for every implementation (Edit):
on ResetPreferences()
SetDefaultPreferences()
UpdatePreferencesDependencies()
end ResetPreferences
The last of the preferences function are two that should not be called from outside of the other preferences functions. (Alas, there is no way to declare private functions, so just don’t do it.) `SetDefaultPreferences()` sets all of the global preferences to their default values. `UpdatePreferencesDependencies()` updates any of the other variables in the script which may depend on the value of one of the preferences. One example of this is the [notifier][1] object, which saves it’s type of notification in a global variable.
[1]: http://applescript.plaidcow.net/PCS/DialogLibrary/