Note:
Storing values in script objects, are not securing them from prying eyes, you don’t store passwords, or account numbers, and anything you deem sensitive, in a script object.
Description Of The Script Cache Storage Library
This Library contains handlers and acts like a module for for storing and retrieving lists
from script objects stored on the disk. The motivation, is to be able to store any kinds of
lists permanently on disk, and retrieve values from them as well. The script library also
supports storing lists in one central location, and provides a means for letting you see the
contents of the lists, to easily, see what is in which list.
It is totally up to you what kind of items you store in the list, be it records, or single chuncks of text, or other lists for instance.
The library itself only provides for basic the operations like loading a script object with the list from storage, or saving the contents of the script object back to disk, along with a handler that dumps the list as text, and a handler, for just adding something you want to have added to a script object. The items are added at the end of the list.
The library in itself does not provide any means for duplicating, editing, inserting or deleting values in alist, a library at a higher level tailor made for your needs are supposed to do this for
you.
The handlers are intended do be as easy to use as possible, so if you just specify a filename, the handlers, assumes they are going to find that file, in the centralized location you have specify. You must create and specify a centralized location for this to work however.
The handlers will however, take aliases, hfspaths, posix paths, also like ‘~/misc/myobj.scpt’ or a finder reference, say from a selection of a finder window. All of this are good and well, as long as the filename ends with “.scpt”. The handlers will then ignore the centralized location, and use the supplied path.
Important
A cache is made when you first try to retrieve it from disk, so there are no warnings about non-existent caches. You are encouraged to test for its existance on your own, at this point in time. Because, the addItemToScriptStorage, and storeScript handlers, writes over any contents, or creates it indiscriminantly with the same name, and a cache that has disappared, might be a situation you’d want to resolve before storing something new.
Advantages of persistent lists
The advantages of having persistent lists are many:
-
You don’t loose list values if you have to edit your script.
-
You can share lists between scripts.
-
You can have lists of lists, or list with records, this is where using textfiles for list values
may become awkward.
It can provide for some flexibility during scripting:
You can have one script construct the list for you, that you let (an) other script(s) use,
keeping as much scaffolding code away from the end product as possible.
For example: Having a list with say 1000 random integers you want to use for testing between several algorithms stored in different scripts. Then you can just create it once, and load it into the ‘client scripts’, there is a script supplied with this library, that makes the load statement for the library, and call statements of the handlers for you
Accessing your list items from the module.
When you load the contents of your list, you are returned a script object, inside that
script object is a property named itemsList
, that is the property that holds your list,
so, if you have called your scriptObject for say cache
then you get at the list with
cache's itemsList
.
-
to loadScript from cacheName
If this doesn’t find the cache, it will create a new cache object, with an empty list
in itemslist, that it returns. -
to storeScript from cacheName against changed_cache
I call this, whenever I have modified a list, by editing an item, added or deleted an
item. Returns true if success, false if not, so that you can dump the list to the clipboard,
if you like save your work. -
to addItemToScriptStorage for newItem against cacheName
Saves an item directly to a ScriptObject, and creates the list if it doesn’t exist.
Returns true if success, false if not, so that you can dump the list to the clipboard,
if you like save your work. I save the list back whenever I have done some changes to it. -
to returnListAsText from cacheName
It is well worth noting that this returns what is stored in the itemsList on disk,
not the list you may have changed in memory.
A suite of accompanying scripts
I have stored this suite of scripts in the /Library/Scripts/ScriptEditor Scripts folder (I have named it CacheStorage) , so that they are easily accessible from the context menu of Script Editor.
CacheStorage:
Driver for cacheStorage’s basic functionality.scpt
Open ScriptCache’s Storage Folder.scpt
Put load Statement on clipboard.scpt
Show contents of a script cache object.scpt
Show ScriptCache’s StoragePath.scpt
The various scripts in that folder are there to assist using or implementing the script objects.
One script lets you see the contents of the various script objects thru a choose from
file dialog, (Open ScriptCache’s Storage Folder.scpt) that are pointed to the folder with the script objects for storage thru the path you have entered in the library file.
-
One script that tests that the library is correctly set up with the path, to the centralized
storage (Driver for cacheStorage’s basic functionality.scpt) run it from the Script editor. -
One script, as mentioned above, that puts a template, based on your choice of script object onto the clipboard, that you can paste into your script.
-
One script shows the path of your script objects in a display alert (Show ScriptCache’s StoragePath.scpt).
-
One script that shows the contents of a chosen script object in a display alert (Show contents of a script cache object.scpt).
Where to store the script objects
Obviously, it is the best thing to store script objects in one central location,
that you “hard code” into the library, that way, as maybe months has gone by without giving
this any thought, it is easy to retrace where they are. You may like to do this differently,
and trust that you have some value in the script, that you can use in a Spotlight search
like this kind:osa somevalue
though.
I prefer to use ~/Library/Application Support/Script Cache Storage/
that I will refer to as the “caches folder” hereafter, to keep it somewhere in line with the policies of the folder structure of the ~/Library folder
.
(Your local library folder).
Installing the Script Cache Storage Library
- Open the Library in Script Editor, save it as “Script Cache Storage” in the ~/Library/Script
Libraries folder. N.B! You will get an error message during the first save, because the contents of the placeholder for the ScriptCacheStoragePath doesn’t point to a valid folder at this moment. Thi is ok, we are going to change that soon.
# http://macscripter.net/viewtopic.php?pid=178732#p178732
# Copyright © 2015 McUsr
property parent : AppleScript
use AppleScript version "2.3"
use scripting additions
-- Once you have created a folder to hold your centralized script objects for storing lists,
-- you drag that path into the commented out propety line below , from Finder, and then uncomment the property line.
property ScriptCacheStoragePath : POSIX file "INSERT-PATH-TO-YOUR-SCRIPT-CACHE-FOLDER-HERE" as alias
(*
If this doesn't find the cache, it will create a new cache object, with an empty list
that is, a list that is owned by the property itemslist. It returns the cache object.
(See the private handler __makeEmptyListStorage() below.)
*)
to loadScript from cacheName
set probe to my __hfsPathOfAnythingAsText(cacheName) -- we may have hit an alias
if probe contains ":" then
set cachePathName to probe
else
set cachePathName to (my ScriptCacheStoragePath as text) & cacheName
end if
if cachePathName does not end with ".scpt" then error "loadScript Error : Only filenames that ends with \".scpt are supported. (May have been called from my addItemToScriptStorage or my returnListAsText.)\""
local script_cache
try
set script_cache to load script alias cachePathName
on error
set script_cache to my __makeEmptyListStorage()
end try
return script_cache
end loadScript
(*
Stores a modifed list back onto the disk.
I call this, whenever I have modified a list, by editing an item, added or deleted an
item. Returns true if success, false if not, so that you can dump the list to the clipboard,
if you like save your work, should something have gone wrong.
*)
to storeScript from cacheName against changed_cache
local probe, cachePathName
set probe to my __hfsPathOfAnythingAsText(cacheName) -- we may have hit an alias
if probe contains ":" then
set cachePathName to probe
else
set cachePathName to (my ScriptCacheStoragePath as text) & cacheName
end if
if cachePathName does not end with ".scpt" then error "storeScript Error : Only filenames that ends with \".scpt are supported.\" (May have been called from my addItemToScriptStorage.)"
try
store script changed_cache in file cachePathName replacing yes
return true
on error e number n
tell application (path to frontmost application as text)
display alert "storeScript:
Couldn't store script " & cachePathName & "
" & e & "number : " & n
end tell
try
tell application id "MACS" to reveal file cachePathName -- Finder
end try
(* If you are happy with this behaviour, uncomment the lines below
set the clipboard to (__literalList of me from (changed_cache's itemslist))
display notification "storeScript :Something went wrong during saving the list, it is copied to the clipboard as text"
*)
return false
end try
end storeScript
-- You can use this handler to create a new list.
(*
Saves an item directly to a ScriptObject, and creates the list if it doesn't exist.
Returns true if success, false if not, so that you can dump the list to the clipboard,
if you like save your work. I save the list back whenever I have done some changes to it.
*)
to addItemToScriptStorage for newItem against cacheName
set curCache to loadScript of me from cacheName
set end of curCache's itemslist to newItem
return (storeScript from cacheName against curCache)
end addItemToScriptStorage
(*
It is well worth noting that this returns what is stored in the itemsList **on disk**,
not the list you may have changed in memory.
*)
to returnListAsText from cacheName
set tempCache to loadScript from cacheName
set listAsText to __literalList of me from (tempCache's itemslist)
return listAsText
end returnListAsText
(* Private Handlers, grab them and store them somewhere else, if you see a need for them.*)
on __hfsPathOfAnythingAsText(anyFileOrFolderPath)
-- Thanks to Yvan Koenig and Nigel Garvey for suggestions. ("'~/tmp:Nick\\'s files'")
-- Constraints:
-- Some (unusual) things regarding paths in unix doesn't work here:
--And you aren't creative ~bin, goes wrong
-- returns:
-- the full hfs pathname of anything if the file exists
local tids, theFile, lastItem, tidsToUse, singleQuoteCheck -- Thanks to Yvan Koenig :)
local firstPiece
set singleQuoteCheck to false
set tidsToUse to ":"
try
(get class of anyFileOrFolderPath)
on error number -1728 -- it was a filereference
set fileOrFolderPath to fileOrFolderPath as alias as text
return fileOrFolderPath
end try
set anyFileOrFolderPath to "" & anyFileOrFolderPath -- doesn't harm it may have been a list with one item.
if anyFileOrFolderPath starts with "'" and anyFileOrFolderPath ends with "'" then
set anyFileOrFolderPath to text 2 thru -2 of anyFileOrFolderPath
set singleQuoteCheck to true
end if
if anyFileOrFolderPath does not start with "/" and anyFileOrFolderPath does not start with "~" then
return anyFileOrFolderPath -- we had a hfspath
else
set tidsToUse to "/"
end if
set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, tidsToUse}
if singleQuoteCheck is true then
set AppleScript's text item delimiters to "'\\''"
set anyFileOrFolderPath to text items of anyFileOrFolderPath
set AppleScript's text item delimiters to "'"
set anyFileOrFolderPath to anyFileOrFolderPath as text
-- just an escaped tick this time
set AppleScript's text item delimiters to "\\'"
set anyFileOrFolderPath to text items of anyFileOrFolderPath
set AppleScript's text item delimiters to "'"
set anyFileOrFolderPath to anyFileOrFolderPath as text
end if
-- We check for ":"'s in posix paths,those needs to be converted into "/"'s in hfsPaths!
-- thanks to Nigel Garvey for reminding me!
set AppleScript's text item delimiters to ":"
set anyFileOrFolderPath to text items of anyFileOrFolderPath
set AppleScript's text item delimiters to character id 0
set anyFileOrFolderPath to anyFileOrFolderPath as text
-- character id 0 temporarily replaces ":"'s in a posix path, to avoid conflicts with "real" ":"'s
set AppleScript's text item delimiters to tidsToUse
if "~" is in text item 1 of anyFileOrFolderPath then
set firstPiece to text item 1 of anyFileOrFolderPath
if (length of firstPiece) is 1 then
set anyFileOrFolderPath to {item 1 of (list disks)} & text items 2 thru -2 of (POSIX path of (path to home folder) as text) & text items 2 thru -1 of anyFileOrFolderPath
else
set anyFileOrFolderPath to {item 1 of (list disks)} & {text 2 thru -1 of firstPiece} & text items 2 thru -2 of (POSIX path of (path to home folder) as text) & text items 2 thru -1 of anyFileOrFolderPath
end if
else if text item 2 of anyFileOrFolderPath is "Volumes" then
set anyFileOrFolderPath to text items 3 thru -1 of anyFileOrFolderPath
else
set anyFileOrFolderPath to {item 1 of (list disks)} & text items 2 thru -1 of anyFileOrFolderPath
end if
set AppleScript's text item delimiters to ":"
set anyFileOrFolderPath to anyFileOrFolderPath as text
-- we exchange the character id 0's with "/"'s in order to have a corresponding hfs path.
set AppleScript's text item delimiters to character id 0
set anyFileOrFolderPath to text items of anyFileOrFolderPath
set AppleScript's text item delimiters to "/"
set anyFileOrFolderPath to anyFileOrFolderPath as text
set AppleScript's text item delimiters to tids
return anyFileOrFolderPath
end __hfsPathOfAnythingAsText
to __makeEmptyListStorage()
script newScriptCache
property parent : AppleScript
property itemslist : {}
end script
end __makeEmptyListStorage
on __literalList from someList
-- Thanks to a clever suggsetion by Nigel Garvey
-- I were able to remove some ugly, more time consuming code.
-- This handler is his, with copyright and everything!
-- I just borrow.
if class of someList is not list then
error "'__literalList' requires a list" number -1703
else
try
text 0 of someList
on error e
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "{"
set textOfSomeList to "{" & text from text item 2 to -1 of e
set AppleScript's text item delimiters to "}"
set textOfSomeList to text 1 thru text item -2 of textOfSomeList & "}"
set AppleScript's text item delimiters to astid
return textOfSomeList
end try
end if
end __literalList
-
Create a folder named ‘Script Cache Storage’ in your ~/Library/Application Support Folder.
-
Drag that Folder from the Finder window, and into the Script Cache Storage script in the
Script Editor, so that it end up between the pair of quotes in the property declaration of the
ScriptCacheStoragePath Property, which instigated the previous error message when you saved it. -
Compile, Save & Close it, unless you want to take a closer look. (I lock my library scripts, when there is nothing to edit).
-
Install the script below into the /Library/Script/Script Editor Scripts folder, somwhere, (I have a folder I have remarkably have called CacheStorage.
Run it, and see if it works, or see to that it will!
-- Name: Show ScriptCache's StoragePath
-- Store this script in /Library/Scripts/Script Editor/Script Cache Storage
-- Or whatever you think denotes those scripts best!
-- Purpose: Test that the _ScriptCacheStoragePath is initialized properly
-- Author: McUsr
use AppleScript version "2.3"
use scripting additions
use store : script "Script Cache Storage"
tell application (path to frontmost application as text)
try
display alert "The Cache storage path is:
" & POSIX path of store's ScriptCacheStoragePath
on error e number n
display alert "Show ScriptCache's StoragePath: The \"_ScriptCacheStoragePath\" variable seems to not be initalized, (run then save the library: " & "
Error: " & e & " Number: " & n
end try
end tell
- Maybe fool around with the driver script below, or use that as a vantage point. At least run it once, so you have some contents to view with the scripts in #7.
(It should be stored the same place.)
-- Name: Driver for cacheStorage's basic funcitionality
-- Store this script in /Library/Scripts/Script Editor/Script Cache Storage
-- Or whatever you think denotes those scripts best!
-- Purpose: Usage demonstration of the list library.
-- Author: McUsr
use AppleScript version "2.3"
use scripting additions
use cacheLib : script "Script Cache Storage"
if (addItemToScriptStorage of cacheLib for "^.*" against "TestCache.scpt") then
display notification "Added an item to the storage"
else
display alert "something went wrong during adding and item"
return
end if
set asText to returnListAsText of cacheLib from "TestCache.scpt"
set myCache to loadScript of cacheLib from "TestCache.scpt"
set end of myCache's itemslist to "!$"
if (storeScript of cacheLib from "TestCache.scpt" against myCache) then
display notification "Saved the cache to disk"
else
display alert "something went wrong during saving the list back to disk."
return
end if
set myCache to loadScript of cacheLib from "TestCache.scpt"
- Save the two scripts below to the same the same folder as in #5: ( /Library/Script/Script Editor Scripts folder), play with them, and you should be good to go.
-- Name: Open ScriptCache's StoragePath
-- Store this script in /Library/Scripts/Script Editor/Script Cache Storage
-- Or whatever you think denotes those scripts best!
-- Purpose: Test that the _ScriptCacheStoragePath is initialized properly
-- Author: McUsr
use AppleScript version "2.3"
use scripting additions
use store : script "Script Cache Storage"
tell application id "MACS" to open store's ScriptCacheStoragePath
do shell script "open -b \"com.apple.finder\" &>/dev/null &"
--- Name: Put load Statement on clipboard
-- Store this script in /Library/Scripts/Script Editor/Script Cache Storage
-- Or whatever you think denotes those scripts best!
-- Purpose: Create template code, ready to use as a vantage point.
-- Author: McUsr
use AppleScript version "2.3"
use scripting additions
use cacheLib : script "Script Cache Storage"
property scriptTitle : "Put Load Statment on Clipboard"
on run
set startText to "
use AppleScript version \"2.3\"
use scripting additions
use cacheLib : script \"Script Cache Storage\"
"
set callStatements to "
set myCache to loadScript of cacheLib from \"--PLACEHOLDER--\"
storeScript of cacheLib from \"--PLACEHOLDER--\" against myCache
addItemToScriptStorage of cacheLib for \"Some list item\" against \"--PLACEHOLDER--\"
set asText to returnListAsText of cacheLib from \"--PLACEHOLDER--\"
-- >> You access the list thru myCache's itemslist <<
"
tell application id "sevs" to set bid to bundle identifier of first process whose frontmost is true and visible is true
tell application id "MACS"
set targetFolder to (cacheLib's ScriptCacheStoragePath as text)
set ids to get id of every Finder window
script o
property l : ids
end script
repeat with i from 1 to (count ids)
try
if (target of Finder window id (item i of o's l as integer) as text) = targetFolder then
close (Finder window id (item i of o's l))
end if
end try
end repeat
tell (make new Finder window)
set target of it to folder (cacheLib's ScriptCacheStoragePath as text)
-- this is the simple solution
end tell
end tell
do shell script "/usr/bin/open -b \"com.apple.finder\" ; sleep 0.3 " -- ; /usr/bin/open -b \"" & bid & "\""
tell application (path to frontmost application as text)
set answ to button returned of (display dialog "Is there a already a ScriptObject that you want to reuse?" buttons {"Make New", "Cancel", "Yes"} cancel button 2 default button 3 with title my scriptTitle with icon 1)
end tell
do shell script "/usr/bin/open -b \"" & bid & "\" ; sleep 0.3 "
tell application (path to frontmost application as text)
if answ = "Yes" then
set chosenScriptObject to (choose file default location cacheLib's ScriptCacheStoragePath of type {"osas"} with prompt "Choose a Script Object to use as Cache for the Call statements") as text
else
set chosenScriptObject to text returned of (display dialog "Enter the Name of your new ScriptCacheObject" default answer "ScriptObjectName" with title scriptTitle with icon 1)
if chosenScriptObject is "" then
display alert "You need to enter a name for your script object, please try again."
return
else
if ".scpt" is not in chosenScriptObject then
set chosenScriptObject to chosenScriptObject & ".scpt"
end if
end if
end if
end tell
set astid to my text item delimiters
set my text item delimiters to ":"
set theFn to text item -1 of chosenScriptObject
set my text item delimiters to "--PLACEHOLDER--"
set trueCalls to text items of callStatements
set my text item delimiters to theFn
set trueCalls to trueCalls as text
set my text item delimiters to astid
set the clipboard to (startText & trueCalls)
tell application (path to frontmost application as text)
display alert "Load statements and call statements for the script object: " & chosenScriptObject & " have been put onto the clipboard."
end tell
end run
The script that lets you view the contents of the script objects that you have stored in a centralized location!
-- Name: Show contents of a script cache object
-- Store this script in /Library/Scripts/Script Editor/Script Cache Storage
-- Or whatever you think denotes those scripts best!
-- Purpose: Show the contents of the list of a script cache object
-- Author: McUsr
use AppleScript version "2.3"
use scripting additions
use cacheLib : script "Script Cache Storage"
tell application (path to frontmost application as text)
set chosenScriptObject to (choose file default location cacheLib's ScriptCacheStoragePath of type {"osas"} with prompt "Choose a Script Object to display the List of.")
set asText to returnListAsText of cacheLib from chosenScriptObject
display alert asText
end tell
8.) This was just presentation of the basic functionality, read the code in the library, and see how you can override the standard path (and what kind of paths it can take), for your convenience. (Then you must modify other scripts, should you wish to peruse their contents, and such.)
Edit
27th of february 2015:
- Removed a redundant comment, (a comment that was a copy of the line above).
- Removed a constraint from the hfsPathOfAnything, as the constraint isn’t there anymore, you can have a quoted tick, inside a a filename surrounded by ticks: '~/tmp:Nick\‘s files’
- Removed a line of code, that was redundant, and had been formerly commented out.