This is one of the few valid situations to employ a try
block in AppleScript to catch an error, however the reason why this could be necessary doesn’t get addressed by the above code.
When using open for access
to read from or write to a file, the filesystem initialises access to the file and allocates memory to be used by buffers. It returns a file handle, which is an integer that essentially serves as a pointer to the resource, but additionally allows operations on the file to be tracked should other processes be accessing the file concurrently. When no longer required, the file handle should be closed so that the filesystem can finish transferring whatever’s in the buffer or flush it, free up resources, and update the system to allow other processes whose operations were queued to progress.
I imagine an open file handle will be automatically closed once execution of the script is completed. This won’t necessarily be the case when script execution terminates unexpectedly, such as when an exception is thrown. This is the purpose of the try
block, which should enclose all code starting from the call to open for access
up to and including the corresponding close access
call, in order to catch any exception that arises whilst there’s an active file handle in use. If an exception is raised by any instruction contained within these lines of code, this will mean that execution has not yet reached the point at which the call to close access
was to be made, so the file remains open and in use. Therefore, upon catching the error, one of the first things that ought to happen is a call to close access
to the file handle, so it’s never left dangling.
Conversely, this try
block is unnecessary, as by this point, you’ve already acquired file access with write permission
. The only errors that could arise out of these lines would either be caused by insufficient memory or disk space, or the fact that you’re attempting to write to the same file to which you already have an open file handle that, for whatever reason, you’ve neglected. As far as I know, AppleScript doesn’t request exclusive access to files, so while the latter scenario is a little careless, it’s not likely to create any issues.
The write
handler below (equivalent to your get_Prefs()
handler) demonstrates how to make use of a file handle and ensure it gets released should the script terminate prematurely. The read
handler is, likewise, a modified version of your set_Prefs()
handler that is largely a refactoring to remove the try
blocks and the sequential blocks of if...then...else
.
Like yours, both of these modified handlers take a parameter that gets coerced into a string assigned to the variable pFile
. This can either be a filename (which will be processed relative to the folder path ~/Library/Preferences/ScriptPrefs
); or it can be an absolute file path or valid file reference, which will be used to read
from (provided it exists) or write
to. This provides the option of operating within the “ScriptPrefs” directory by default, while allowing the user to specify any other location of their choosing, e.g.
tell ScriptPrefs to write {"foo", "bar"} to "somefile.dat" --> Writes a list out to ~/Library/Preferences/ScriptPrefs/somefile.dat
read ScriptPrefs from "somefile.dat" --> Reads in the list from ~/Library/Preferences/ScriptPrefs/somefile.dat
tell ScriptPrefs to write {foo:"bar", bar:"foo"} to "~/Documents/somefile.dat" --> Writes a record out to ~/Documents/somefile.dat
read ScriptPrefs from "~/Documents/somefile.dat" --> Reads in the record from ~/Documents/somefile.dat
tell application id "com.apple.SystemEvents" to script ScriptPrefs
property parent : it
# System Events won't complain if the folder already exists
property pFolder : make new folder with properties ¬
{name:"ScriptPrefs"} at preferences folder
to read from (pFile as text)
local pPath, pType
tell pFolder's file pFile to if (exists) ¬
then set pFile to the POSIX path
tell file pFile to if not (exists) then ¬
error "File not found: " & pFile
set pPath to file pFile's POSIX path
tell the current application
# Reading in as "type" essentially reads in
# the first four bytes, then coerces this to
# an AppleScript class
set pType to read pPath as "type"
# Note the "as..." class can be supplied by
# way of a stored variable, and can either
# be an AppleScript class or a four-byte
# type code, e.g. record or "reco"
#
# This avoids the if...then...else ballache
if pType is not in [record, list] ¬
then set pType to "utf8"
read pPath as pType from 0
end tell
end read
to write PData to (pFile as text)
local pPath, pType, fh
tell pFile to if it starts with "~/" then set ¬
pFile to my home folder's POSIX path & ¬
text 2 thru -1
using terms from scripting additions
tell the POSIX path of pFile to if ("/") ¬
is not in text 2 thru -1 then set ¬
pFile to pFolder's «class posx» & ¬
it
set pPath to pFile's POSIX path
end using terms from
# Again, using a variable to facilitate
# writing out of data as an appropriate
# class without if...then...else-ing to
# hell and back
set pType to PData's class
if pType = text then set pType to "utf8"
tell the current application to try
open for access pPath with write permission
set fh to the result
set eof of fh to 0
write PData to fh as pType
close access fh
on error E number N
close access fh
display alert ("" & N & ": " & E) ¬
giving up after 10
return false
end try
return true
end write
end script