The easiest way I know to build small DBs in AppleScript is to use the ScriptDB.osax by Custom Flow Solutions (Yotam Rosenthal). Costs $12; There is a nice Manual. Recommended.
After working with Database Events I have learned a few things. By following a few simple lessons I have found Database Events to be stable and extremely easy to work with. The initial “instability” that I experienced was because I didn’t know these lessons and therefore did not follow them. I would say that the fault lies not in Database Events itself but the lack of documentation available for it.
Database Events Lessons Learned
Always “launch” Database Events before you try to open an existing database, i.e. Tell application “Database Events” to launch
reason: Normally if an application isn’t running, and you tell application “X” to open a document, the application will first launch and then open the document. This doesn’t happen with Database Events so you will get an error if Database Events isn’t already running.
Always refer to a database by its name and not by a database number
reason: the number of open databases varies by the method in which they are opened therefore you can never be sure the number of opened databases. To explain: if you create a new database then the number of open databases is 1. If you open an existing database then the number of open databases is 2… it varies so you can never be sure of a database’s number. So never refer to database 1, always use its name.
Always save your database (if you want the changes saved!) before you quit Database Events
reason: it may be obvious, but Database Events doesn’t automatically save your changes. Other databases do auto-save, such as SQLite3.
In the beginning of your script always set the “quit delay” to 0 which effectively disables Database Events from automatically quitting itself. Make sure to quit Database Events at the end of your script also.
reason: Database Events will automatically quit itself after 5 minutes of inactivity, so if it quits itself and you haven’t saved your changes then they will be lost and your databases will be closed.
This is my latest version of the script. It takes into account all of the “lessons learned” that I posted in the above post. Plus it combines the 2 scripts into one so it’s much cleaner now.
(* This script is used to help you search the contents of your applescripts. You can either update or search the database *)
(* It basically allows you to search your scripts by their contents much like Spotlight allows you to search your files by their contents. It works by creating a database of the code from your scripts found in your user "Scripts" folder. Initially each script is opened in Script Editor and the code is copied from the script into the database. Once the database is created then you can perform searches for code snippets and the scripts containing those snippets are presented to you in the Finder. You will need to update your database as changes occur to your script code, or as scripts are added/deleted from your Scripts folder. *)
(* Searching the database: A dialog box is presented where you input your search terms. There are 2 types of searches you can perform using the search terms: 1) "As One Word" will search for scripts that contain all of the search terms next to each other, and 2) "Individual Words" finds scripts that contain all of the search terms, but the terms do not have to be next to each other. Just separate your search terms with spaces, no special characters or punctuation is needed. The results of the database search are presented in a Finder window. The window is populated with alias files to the original scripts found via the search. *)
(* Updating the database: This updates/creates the script database using "database events". The database is saved in your user's "application support" folder in a folder called "Script Search". This takes time so be patient. You will be notified when the process is complete. Do not use the "Script Editor" application during this time. *)
property ScriptEditorApp : "Script Editor"
property dbName : "scriptDB"
property dbFolderName : "Script Search"
property appSupportFolder : path to application support folder from user domain as Unicode text
property dbFolderPath : appSupportFolder & dbFolderName
property dbPath : appSupportFolder & dbFolderName & ":" & dbName & ".dbev"
property scriptsFolder : path to scripts folder from user domain as Unicode text
property resultWindowName : "Found Scripts"
property resultWindowPath : appSupportFolder & dbFolderName & ":" & resultWindowName
property searchTermsBox : ""
property field1Name : "fileName"
property field2Name : "modDate"
property field3Name : "scriptText"
-- database events require at least MacOSX 10.4
tell (system attribute "sysv") to set vSys to (("1" & it mod 4096 div 256) as string) & "." & it mod 256 div 16 & "." & it mod 16
if vSys < "10.4" then
display dialog "This script requires the installation of Mac OS X 10.4 or higher." buttons {"OK"} default button 1 with icon 2
return
end if
-- decide whether to search or update the db
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Search or Update the Database?" & return & return & "Note: the update process takes time so please be patient. You will be alerted when the update is finished. Do not use the \"Script Editor\" application during the update process." buttons {"Cancel", "Update", "Search"} default button 3 with icon note
set button_returned to button returned of the result
if button_returned is "Search" then --> (* SEARCH THE DATABASE *)
-- check for required files and folders
tell application "Finder"
if not (exists file dbPath) then
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "The Script Database does not exist! Please create the database before performing a search." buttons {"OK"} default button 1 with icon stop
return
end if
if not (exists folder resultWindowPath) then make new folder at folder dbFolderPath with properties {name:resultWindowName}
end tell
repeat
-- get the search terms
repeat
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Enter your search terms:" default answer searchTermsBox with icon note buttons {"Cancel", "As One Word", "Individual Words"} default button 3 with title "Script Search"
set {text_entered, button_pressed} to {text returned, button returned} of the result
if text_entered is not "" then
set searchTermsBox to text_entered
exit repeat
end if
end repeat
-- make them into a list according to button pressed
if button_pressed is "As One Word" then
set searchTerms to {text_entered}
else
set searchTerms to words of text_entered
end if
-- conduct the search
set foundScripts to {}
tell application "Database Events"
launch
open database (POSIX path of dbPath)
tell database dbName
repeat with i from 1 to (count of records)
set theCheck to false
tell record i
set scriptPath to value of field "name"
set scriptName to value of field field1Name
set scriptText to value of field field3Name
repeat with sw in searchTerms
if scriptText contains sw or scriptName contains sw then
set theCheck to true
else
set theCheck to false
exit repeat
end if
end repeat
if theCheck then set end of foundScripts to scriptPath
end tell
end repeat
end tell
end tell
-- clean up database events
tell application "Database Events" to quit
-- create alias's to the results in the result window
if foundScripts is not {} then
-- delete previous search results
tell application "Finder" to set aliasList to every file of folder resultWindowPath
repeat with anAlias in aliasList
do shell script ("rm -rf " & quoted form of (POSIX path of (anAlias as Unicode text)))
end repeat
-- make the alias's
repeat with aScript in foundScripts
try
tell application "Finder" to make new alias file at folder resultWindowPath to file aScript
on error
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "The following script could not be found and will not be included in your search results." & return & return & aScript & return & return & "Do you need to update your database?" buttons {"OK"} default button 1 with icon note
end try
end repeat
-- open the Finder window
tell application "Finder"
activate
open folder resultWindowPath
end tell
exit repeat
else
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display alert "No results were found!" message "Please try different search terms." as warning
end if
end repeat
else --> (* UPDATE THE DATABASE *)
-- opening parameters
set newRecs to 0
set updatedRecs to 0
set deletedRecs to 0
set inTime to current date
tell application ScriptEditorApp to launch
-- get a list of all the files in the scripts folder
set allFiles to my allFiles_ofFolder(scriptsFolder)
-- disable auto-quitting for database events and close all open databases
tell application "Database Events"
launch
set quit delay to 0
end tell
-- create the necessary folders and open the database
set updateDB to true
tell application "Finder"
if not (exists folder dbFolderPath) then make new folder at folder appSupportFolder with properties {name:dbFolderName}
if (exists file dbPath) then -- check for the database and create/open it
tell application "Database Events"
open database (POSIX path of dbPath)
set origRecordCount to count of records of database dbName
end tell
else
tell application "Database Events"
make new database with properties {name:dbName, location:dbFolderPath}
set updateDB to false
end tell
set origRecordCount to 0
end if
end tell
-- create or update the database
if updateDB then -- update the database so checking for existing records takes place
repeat with aFile in allFiles
set fileInfo to (info for file aFile)
if kind of fileInfo is "script" or file creator of fileInfo is "aplt" then -- check for a "script editor" file
set {fileName, fileModDate} to {name of fileInfo, modification date of fileInfo}
tell application "Database Events" to tell database dbName
if (exists record aFile) then
tell (first record whose name = aFile)
set recordsModDate to value of field "modDate"
if fileModDate > recordsModDate then -- use modification date to see if a record needs updating
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set updatedRecs to updatedRecs + 1
set value of field field1Name to fileName
set value of field field2Name to fileModDate
set value of field field3Name to scriptText
end if
end if
end tell
else -- a record doesn't exist for this script so create a new record for it
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set newRecs to newRecs + 1
set thisRecord to make new record with properties {name:aFile}
tell thisRecord
make new field with properties {name:field1Name, value:fileName}
make new field with properties {name:field2Name, value:fileModDate}
make new field with properties {name:field3Name, value:scriptText}
end tell
end if
end if
end tell
end if
end repeat
-- get a list of the non-existent records, i.e. scripts in the database that are no longer found in the Finder
tell application "Database Events" to tell database dbName
save
set deleteList to {}
set recordCount to count of records
repeat with i from 1 to recordCount
set thisPath to value of field "name" of record i
tell application "Finder" to if not (exists file thisPath) then set end of deleteList to thisPath
end repeat
-- remove non-existent records from the database
if deleteList is not {} then
repeat with i from 1 to number of items in deleteList
set this_item to item i of deleteList
delete record this_item
set deletedRecs to deletedRecs + 1
end repeat
end if
end tell
else -- we're creating a new database so no checking of the records is necessary
repeat with aFile in allFiles
set fileInfo to (info for file aFile)
if kind of fileInfo is "script" or file creator of fileInfo is "aplt" then -- check for a "script editor" file
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set {fileName, fileModDate} to {name of fileInfo, modification date of fileInfo}
tell application "Database Events" to tell database dbName
set newRecs to newRecs + 1
set thisRecord to make new record with properties {name:aFile}
tell thisRecord
make new field with properties {name:field1Name, value:fileName}
make new field with properties {name:field2Name, value:fileModDate}
make new field with properties {name:field3Name, value:scriptText}
end tell
end tell
end if
end if
end repeat
end if
-- clean up database events
tell application "Database Events"
tell database dbName
save
set recordCount to count of records
end tell
quit
end tell
-- notify user it's done updating the db and specifics of the process
set outTime to current date
set totaltime to (outTime - inTime)
set theTime to my secs_to_hms(totaltime)
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "The database has been updated." & return & "Update time: " & theTime & return & return & "Old Number of Records in DB: " & origRecordCount & return & "New Number of Records in DB: " & recordCount & return & "Records Deleted: " & deletedRecs & return & "Records Updated: " & updatedRecs & return & "Records New: " & newRecs buttons {"OK"} default button 1 with icon 1
end if
(*=================== SUBROUTINES =====================*)
on get_scriptText(theScript)
tell application ScriptEditorApp
try
open theScript
set scriptText to text of document 1
on error
set scriptText to ""
end try
try
close document 1
end try
end tell
return scriptText
end get_scriptText
on secs_to_hms(the_secs)
if the_secs is less than 360000 then
set hr to text 2 thru 3 of ((100 + the_secs div hours) as string)
set min to text 2 thru 3 of ((100 + the_secs mod hours div minutes) as string)
set sec to text 2 thru 3 of ((100 + the_secs mod minutes div 1) as string)
set fraction to text 2 thru 3 of ((100 + the_secs mod 1 * 100) as string)
return hr & ":" & min & ":" & sec & "." & fraction
else
return false
end if
end secs_to_hms
on allFiles_ofFolder(aFolder)
set searchFolder to aFolder as string
set everyFile to {}
tell application "Finder"
try -- there's an error if nothing is in the folder when you get entire contents
set tempVar to files of entire contents of folder searchFolder
if (count of tempVar) is 0 then -- answer is 0 when there's only 1 item???
set end of everyFile to tempVar as string
else
repeat with i from 1 to (count of tempVar)
set end of everyFile to (item i of tempVar) as string
end repeat
end if
end try
end tell
return everyFile
end allFiles_ofFolder
Here is the first draft of the ideas above,
The show skipped button should only show if the are skipped.
There may be some redundant bits, not much , but it seems to work.
And I am sure it can be streamlined.
Let me know what you think
Cheers
(* This script is used to help you search the contents of your applescripts. You can either update or search the database *)
(* It basically allows you to search your scripts by their contents much like Spotlight allows you to search your files by their contents. It works by creating a database of the code from your scripts found in your user "Scripts" folder. Initially each script is opened in Script Editor and the code is copied from the script into the database. Once the database is created then you can perform searches for code snippets and the scripts containing those snippets are presented to you in the Finder. You will need to update your database as changes occur to your script code, or as scripts are added/deleted from your Scripts folder. *)
(* Searching the database: A dialog box is presented where you input your search terms. There are 2 types of searches you can perform using the search terms: 1) "As One Word" will search for scripts that contain all of the search terms next to each other, and 2) "Individual Words" finds scripts that contain all of the search terms, but the terms do not have to be next to each other. Just separate your search terms with spaces, no special characters or punctuation is needed. The results of the database search are presented in a Finder window. The window is populated with alias files to the original scripts found via the search. *)
(* Updating the database: This updates/creates the script database using "database events". The database is saved in your user's "application support" folder in a folder called "Script Search". This takes time so be patient. You will be notified when the process is complete. Do not use the "Script Editor" application during this time. *)
property ScriptEditorApp : "Script Editor"
property dbName : "scriptDB"
property dbFolderName : "Script Search"
property appSupportFolder : path to application support folder from user domain as Unicode text
property dbFolderPath : appSupportFolder & dbFolderName
property dbPath : appSupportFolder & dbFolderName & ":" & dbName & ".dbev"
property scriptsFolder : path to scripts folder from user domain as Unicode text
property resultWindowName : "Found Scripts"
property resultWindowPath : appSupportFolder & dbFolderName & ":" & resultWindowName
property searchTermsBox : ""
property field1Name : "fileName"
property field2Name : "modDate"
property field3Name : "scriptText"
property field4Name : "skip"
property SkippedscriptText : "Skipped"
property notSkippedscriptText : "notSkipped"
property bugcheck : false
-- database events require at least MacOSX 10.4
tell (system attribute "sysv") to set vSys to (("1" & it mod 4096 div 256) as string) & "." & it mod 256 div 16 & "." & it mod 16
if vSys < "10.4" then
display dialog "This script requires the installation of Mac OS X 10.4 or higher." buttons {"OK"} default button 1 with icon 2
return
end if
-- decide whether to search or update the db
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Search or Update the Database?" & return & return & "Note: the update process takes time so please be patient. You will be alerted when the update is finished. Do not use the \"Script Editor\" application during the update process." buttons {"Cancel", "Update", "Search"} default button 3 with icon note
set button_returned to button returned of the result
if button_returned is "Search" then --> (* SEARCH THE DATABASE *)
-- check for required files and folders
tell application "Finder"
if not (exists file dbPath) then
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "The Script Database does not exist! Please create the database before performing a search." buttons {"OK"} default button 1 with icon stop
return
end if
if not (exists folder resultWindowPath) then make new folder at folder dbFolderPath with properties {name:resultWindowName}
end tell
repeat
-- get the search terms
repeat
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Enter your search terms:" default answer searchTermsBox with icon note buttons {"Cancel", "As One Word", "Individual Words"} default button 3 with title "Script Search"
set {text_entered, button_pressed} to {text returned, button returned} of the result
if text_entered is not "" then
set searchTermsBox to text_entered
exit repeat
end if
end repeat
-- make them into a list according to button pressed
if button_pressed is "As One Word" then
set searchTerms to {text_entered}
else
set searchTerms to words of text_entered
end if
-- conduct the search
set foundScripts to {}
--set Skipped to {}
tell application "Database Events"
launch
open database (POSIX path of dbPath)
tell database dbName
repeat with i from 1 to (count of records)
set theCheck to false
tell record i
set scriptPath to value of field "name"
set scriptName to value of field field1Name
set scriptText to value of field field3Name
--set Skippedscript to value of field field4Name
repeat with sw in searchTerms
if scriptText contains sw or scriptName contains sw then
set theCheck to true
else
set theCheck to false
exit repeat
end if
end repeat
if theCheck then
--if Skippedscript is SkippedscriptText then
-- set end of Skipped to scriptPath
--end if
set end of foundScripts to scriptPath
end if
end tell
end repeat
end tell
end tell
-- clean up database events
--tell application "Database Events" to quit
-- create alias's to the results in the result window
if foundScripts is not {} then
-- delete previous search results
tell application "Finder" to set aliasList to every file of folder resultWindowPath
repeat with anAlias in aliasList
do shell script ("rm -rf " & quoted form of (POSIX path of (anAlias as Unicode text)))
end repeat
--** make the alias's
repeat with aScript in foundScripts
try
tell application "Finder" to make new alias file at folder resultWindowPath to file aScript
on error
tell application "Database Events" to tell database dbName
tell (first record whose name = aScript)
set value of field field4Name to SkippedscriptText
end tell
end tell
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "The following script could not be found and will not be included in your search results. And Marked as Skipped" & return & return & aScript & return & return & "Do you need to update your database?" buttons {"OK"} default button 1 with icon note
end try
end repeat
-- open the Finder window
tell application "Finder"
activate
open folder resultWindowPath
end tell
exit repeat
else
set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display alert "No results were found!" message "Please try different search terms." as warning
end if
end repeat
else --> (* UPDATE THE DATABASE *)
-- opening parameters
set newRecs to 0
set updatedRecs to 0
set deletedRecs to 0
set inTime to current date
tell application ScriptEditorApp to launch
-- get a list of all the files in the scripts folder
set allFiles to my allFiles_ofFolder(scriptsFolder)
-- disable auto-quitting for database events and close all open databases
tell application "Database Events"
launch
set quit delay to 0
end tell
-- create the necessary folders and open the database
set updateDB to true
tell application "Finder"
if not (exists folder dbFolderPath) then make new folder at folder appSupportFolder with properties {name:dbFolderName}
if (exists file dbPath) then -- check for the database and create/open it
tell application "Database Events"
open database (POSIX path of dbPath)
set origRecordCount to count of records of database dbName
end tell
else
tell application "Database Events"
make new database with properties {name:dbName, location:dbFolderPath}
set updateDB to false
end tell
set origRecordCount to 0
end if
end tell
-- create or update the database
if updateDB then -- update the database so checking for existing records takes place
repeat with aFile in allFiles
set fileInfo to (info for file aFile)
if kind of fileInfo is "script" or file creator of fileInfo is "aplt" then -- check for a "script editor" file
set {fileName, fileModDate} to {name of fileInfo, modification date of fileInfo}
tell application "Database Events" to tell database dbName
if (exists record aFile) then
tell (first record whose name = aFile)
set recordsModDate to value of field "modDate"
if fileModDate > recordsModDate then -- use modification date to see if a record needs updating
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set updatedRecs to updatedRecs + 1
set value of field field1Name to fileName
set value of field field2Name to fileModDate
set value of field field3Name to scriptText
set value of field field4Name to notSkippedscriptText
--set value of field field4Name to notSkippedscriptText -- new record now so not skipped
end if
end if
end tell
else -- a record doesn't exist for this script so create a new record for it
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set newRecs to newRecs + 1
set thisRecord to make new record with properties {name:aFile}
tell thisRecord
make new field with properties {name:field1Name, value:fileName}
make new field with properties {name:field2Name, value:fileModDate}
make new field with properties {name:field3Name, value:scriptText}
make new field with properties {name:field4Name, value:notSkippedscriptText} -- new record not skipped
end tell
end if
end if
end tell
end if
end repeat
-- get a list of the non-existent records, i.e. scripts in the database that are no longer found in the Finder
set skipped_count to 0
tell application "Database Events" to tell database dbName
save
set SkipList to {}
set recordCount to count of records
repeat with i from 1 to recordCount
set thisPath to value of field "name" of record i
set thisSkipcheck to value of field "skip" of record i
tell application "Finder"
if not (exists file thisPath) then
set end of SkipList to thisPath
end if
end tell
end repeat
if SkipList is not {} then
repeat with i from 1 to count of SkipList
set this_rec to item i of SkipList
tell (first record whose name = this_rec)
set value of field field4Name to SkippedscriptText
set skipped_count to skipped_count + 1
end tell
end repeat
end if
---
(*
set recordCount to count of records
repeat with i from 1 to recordCount
set thisSkipcheck to value of field "skip" of record i
log thisSkipcheck
if thisSkipcheck is SkippedscriptText then set skipped_count to skipped_count + 0
end repeat
set skipped_count to skipped_count as string
--
-- remove non-existent records from the database
if SkipList is not {} then
repeat with i from 1 to number of items in SkipList
set this_item to item i of sList
delete record this_item
set deletedRecs to deletedRecs + 1
end repeat
end if
*)
end tell
else -- we're creating a new database so no checking of the records is necessary
if bugcheck then say 1
repeat with aFile in allFiles
set fileInfo to (info for file aFile)
if kind of fileInfo is "script" or file creator of fileInfo is "aplt" then -- check for a "script editor" file
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set {fileName, fileModDate} to {name of fileInfo, modification date of fileInfo}
tell application "Database Events" to tell database dbName
if bugcheck then say 2
set newRecs to newRecs + 1
set thisRecord to make new record with properties {name:aFile}
tell thisRecord
if bugcheck then say 3
make new field with properties {name:field1Name, value:fileName}
make new field with properties {name:field2Name, value:fileModDate}
make new field with properties {name:field3Name, value:scriptText}
make new field with properties {name:field4Name, value:notSkippedscriptText}
if bugcheck then say 4
end tell
end tell
end if
end if
end repeat
end if
-- clean up database events
tell application "Database Events"
tell database dbName
if bugcheck then say 6
save
set recordCount to count of records
if bugcheck then say 7
end tell
end tell
-- notify user it's done updating the db and specifics of the process
--set skipped_count to "?"
set outTime to current date
set totaltime to (outTime - inTime)
set theTime to my secs_to_hms(totaltime)
set frontApp to displayed name of (info for (path to frontmost application))
set skipbutton to {"Finished"}
if SkipList is not {} then
set skipbutton to {"Show Skipped", "Finished"}
end if
tell application frontApp to display dialog "The database has been updated." & return & "Update time: " & theTime & return & return & "Old Number of Records in DB: " & origRecordCount & return & "new Number of Records in DB: " & recordCount & return & "Records with files : " & (recordCount - skipped_count) & return & "Skipped Records : " & skipped_count & return & "Records Updated: " & updatedRecs & return & "Records New: " & newRecs buttons skipbutton default button 1 with icon 1
set the button_pressed to the button returned of the result
my showSkipped(button_pressed, SkipList, dbName, deletedRecs, frontApp)
end if
-- clean up database events
tell application "Database Events"
tell database dbName
if bugcheck then say 9
save
set recordCount to count of records
if bugcheck then say 10
end tell
if bugcheck then say 11
quit
end tell
(*=================== SUBROUTINES =====================*)
on showSkipped(button_pressed, SkipList, dbName, deletedRecs, frontApp)
repeat until button_pressed is "Exit"
if the button_pressed is "Show Skipped" then
tell application frontApp to set theChosen to choose from list SkipList with prompt "Choose Record/s to Recompile or Delete" default items {item 1 of SkipList} OK button name "Actions" cancel button name "Exit" with multiple selections allowed
if result is false then exit repeat
log theChosen
set still_To_Action to {}
repeat with i from 1 to number of items in SkipList
set this_skipitem to item i of SkipList
if this_skipitem is not in theChosen then
copy this_skipitem to end of still_To_Action
end if
end repeat
set SkipList to still_To_Action
tell application frontApp to display dialog "Choose Action for the selected Records." buttons {"Exit", "Delete", "Recompile"} default button 2 with icon 1
set the button_pressed to the button returned of the result
repeat with i from 1 to number of items in theChosen
set this_rec to item i of theChosen
tell application "Database Events" to tell database dbName
if the button_pressed is "Delete" then
delete record this_rec
set deletedRecs to deletedRecs + 1
else if the button_pressed is "Recompile" then
log this_rec
tell record this_rec to set thisText to value of field "scriptText"
tell record this_rec to set thisName to value of field "name"
set thisName to do shell script "basename " & quoted form of (POSIX path of thisName)
tell application "Script Editor"
make document with properties {name:thisName, text:thisText}
end tell
else
exit repeat
end if
end tell
end repeat
else
exit repeat
end if
end repeat
end showSkipped
on get_scriptText(theScript)
tell application ScriptEditorApp
try
open theScript
set scriptText to text of document 1
on error
set scriptText to ""
end try
try
close document 1
end try
end tell
return scriptText
end get_scriptText
on secs_to_hms(the_secs)
if the_secs is less than 360000 then
set hr to text 2 thru 3 of ((100 + the_secs div hours) as string)
set min to text 2 thru 3 of ((100 + the_secs mod hours div minutes) as string)
set sec to text 2 thru 3 of ((100 + the_secs mod minutes div 1) as string)
set fraction to text 2 thru 3 of ((100 + the_secs mod 1 * 100) as string)
return hr & ":" & min & ":" & sec & "." & fraction
else
return false
end if
end secs_to_hms
on allFiles_ofFolder(aFolder)
set searchFolder to aFolder as string
set everyFile to {}
tell application "Finder"
try -- there's an error if nothing is in the folder when you get entire contents
set tempVar to files of entire contents of folder searchFolder
if (count of tempVar) is 0 then -- answer is 0 when there's only 1 item???
set end of everyFile to tempVar as string
else
repeat with i from 1 to (count of tempVar)
set end of everyFile to (item i of tempVar) as string
end repeat
end if
end try
end tell
return everyFile
end allFiles_ofFolder
I checked out the script and it works pretty well except for a few comments I listed below. I’m still not convinced of the need for keeping deleted scripts in the database. I’m still deciding in my own mind if that’s a capability I want. It’s an interesting idea but I’m not sure I’ll use it yet. But I’ll certainly work with you to perfect it and in the end we’ll have a choice of the capabilities we want… which is a good thing.
Here’s my comments after using your changes.
When creating a new database:
I got errors. The ending dialog box which shows the results of the update process was missing 2 variables. The fix was to add the following under the “opening parameters” section under the “update the database” part of the code.
Set skiplist to {}
Set skipped_count to 0
When searching:
If my search terms are found in a skipped script then I need an option to recompile the script. Now it only tells me that the script is missing but that’s when I would want to recompile it. Now the only time I can recompile a script is when I’m updating the database.
A thought about a possible error:
Suppose I add a new script to my scripts folder, and it has the same name as a script I have skipped in my database, what happens? Right now what happens is that the “skipped” record gets overwritten with the new scripts text but there is not alert message telling me I’m about to overwrite one of my skipped records. I could be accidentally losing valuable data that I wanted to keep.
Boy that’s a lot to chew over, my head is hurting… :o
Try this
One thing I notice is.
How would you handle file that are older than a record or a skipped record, but have been moved into the files folder.
if fileModDate < recordsModDate then
Cheers
(* This script is used to help you search the contents of your applescripts. You can either update or search the database *)
(* It basically allows you to search your scripts by their contents much like Spotlight allows you to search your files by their contents. It works by creating a database of the code from your scripts found in your user "Scripts" folder. Initially each script is opened in Script Editor and the code is copied from the script into the database. Once the database is created then you can perform searches for code snippets and the scripts containing those snippets are presented to you in the Finder. You will need to update your database as changes occur to your script code, or as scripts are added/deleted from your Scripts folder. *)
(* Searching the database: A dialog box is presented where you input your search terms. There are 2 types of searches you can perform using the search terms: 1) "As One Word" will search for scripts that contain all of the search terms next to each other, and 2) "Individual Words" finds scripts that contain all of the search terms, but the terms do not have to be next to each other. Just separate your search terms with spaces, no special characters or punctuation is needed. The results of the database search are presented in a Finder window. The window is populated with alias files to the original scripts found via the search. *)
(* Updating the database: This updates/creates the script database using "database events". The database is saved in your user's "application support" folder in a folder called "Script Search". This takes time so be patient. You will be notified when the process is complete. Do not use the "Script Editor" application during this time. *)
property ScriptEditorApp : "Script Editor"
property dbName : "scriptDB"
property dbFolderName : "Script Search"
property appSupportFolder : path to application support folder from user domain as Unicode text
property dbFolderPath : appSupportFolder & dbFolderName
property dbPath : appSupportFolder & dbFolderName & ":" & dbName & ".dbev"
property scriptsFolder : path to scripts folder from user domain as Unicode text
property resultWindowName : "Found Scripts"
property resultWindowPath : appSupportFolder & dbFolderName & ":" & resultWindowName
property searchTermsBox : ""
property field1Name : "fileName"
property field2Name : "modDate"
property field3Name : "scriptText"
property field4Name : "skip"
property SkippedscriptText : "Skipped"
property notSkippedscriptText : "notSkipped"
property bugcheck : false
tell application "System Events"
set frontApp to name of the first process whose frontmost is true and background only is false
end tell
global SkipList, frontApp, button_pressed, overwrite
set deletedRecs to {}
-- database events require at least MacOSX 10.4
tell (system attribute "sysv") to set vSys to (("1" & it mod 4096 div 256) as string) & "." & it mod 256 div 16 & "." & it mod 16
if vSys < "10.4" then
display dialog "This script requires the installation of Mac OS X 10.4 or higher." buttons {"OK"} default button 1 with icon 2
return
end if
-- decide whether to search or update the db
--set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Search or Update the Database?" & return & return & "Note: the update process takes time so please be patient. You will be alerted when the update is finished. Do not use the \"Script Editor\" application during the update process." buttons {"Cancel", "Update", "Search"} default button 3 with icon note
set button_returned to button returned of the result
if button_returned is "Search" then --> (* SEARCH THE DATABASE *)
-- check for required files and folders
tell application "Finder"
if not (exists file dbPath) then
--set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "The Script Database does not exist! Please create the database before performing a search." buttons {"OK"} default button 1 with icon stop
return
end if
if not (exists folder resultWindowPath) then make new folder at folder dbFolderPath with properties {name:resultWindowName}
end tell
repeat
-- get the search terms
repeat
--set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Enter your search terms:" default answer searchTermsBox with icon note buttons {"Cancel", "As One Word", "Individual Words"} default button 3 with title "Script Search"
set {text_entered, button_pressed} to {text returned, button returned} of the result
if text_entered is not "" then
set searchTermsBox to text_entered
exit repeat
end if
end repeat
-- make them into a list according to button pressed
if button_pressed is "As One Word" then
set searchTerms to {text_entered}
else
set searchTerms to words of text_entered
end if
-- conduct the search
set foundScripts to {}
set Skipped to {}
tell application "Database Events"
launch
open database (POSIX path of dbPath)
tell database dbName
repeat with i from 1 to (count of records)
set theCheck to false
tell record i
set scriptPath to value of field "name"
set scriptName to value of field field1Name
set scriptText to value of field field3Name
set Skippedscript to value of field field4Name
repeat with sw in searchTerms
if scriptText contains sw or scriptName contains sw then
set theCheck to true
else
set theCheck to false
exit repeat
end if
end repeat
if theCheck then
if Skippedscript is SkippedscriptText then
set end of Skipped to scriptPath
end if
set end of foundScripts to scriptPath
end if
end tell
end repeat
end tell
end tell
-- clean up database events
--tell application "Database Events" to quit
-- create alias's to the results in the result window
if foundScripts is not {} then
-- delete previous search results
tell application "Finder" to set aliasList to every file of folder resultWindowPath
repeat with anAlias in aliasList
do shell script ("rm -rf " & quoted form of (POSIX path of (anAlias as Unicode text)))
end repeat
--** make the alias's
repeat with aScript in foundScripts
try
tell application "Finder" to make new alias file at folder resultWindowPath to file aScript
on error
tell application "Database Events" to tell database dbName
tell (first record whose name = aScript)
set value of field field4Name to SkippedscriptText
if aScript is not in Skipped then copy aScript to end of Skipped
end tell
end tell
--**__
end try
end repeat
if Skipped is not {} then
set SkipList to Skipped
--set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display dialog "Some scripts or script could not be found and will not be included in your search results, and Marked as Skipped" & return & return & SkipList & return & return & "Do you want to view them with the Option to Recompile them now?" buttons {"Recompile script", "Continue"} default button 1 with icon note
set the button_pressed to the button returned of the result
my showSkipped(button_pressed, SkipList, dbName, deletedRecs, frontApp)
end if
-- open the Finder window
tell application "Finder"
activate
open folder resultWindowPath
end tell
exit repeat
else
--set frontApp to displayed name of (info for (path to frontmost application))
tell application frontApp to display alert "No results were found!" message "Please try different search terms." as warning
end if
end repeat
else --> (* UPDATE THE DATABASE *)
-- opening parameters
set newRecs to 0
set updatedRecs to 0
set deletedRecs to 0
set SkipList to {}
set skipped_count to 0
set inTime to current date
tell application ScriptEditorApp to launch
-- get a list of all the files in the scripts folder
set allFiles to my allFiles_ofFolder(scriptsFolder)
-- disable auto-quitting for database events and close all open databases
tell application "Database Events"
launch
set quit delay to 0
end tell
-- create the necessary folders and open the database
set updateDB to true
tell application "Finder"
if not (exists folder dbFolderPath) then make new folder at folder appSupportFolder with properties {name:dbFolderName}
if (exists file dbPath) then -- check for the database and create/open it
tell application "Database Events"
open database (POSIX path of dbPath)
set origRecordCount to count of records of database dbName
end tell
else
tell application "Database Events"
make new database with properties {name:dbName, location:dbFolderPath}
set updateDB to false
end tell
set origRecordCount to 0
end if
end tell
-- create or update the database
if updateDB then -- update the database so checking for existing records takes place
repeat with aFile in allFiles
set fileInfo to (info for file aFile)
if kind of fileInfo is "script" or file creator of fileInfo is "aplt" or type identifier of fileInfo is "com.apple.applescript.text" then -- check for a "script editor" file
set {fileName, fileModDate} to {name of fileInfo, modification date of fileInfo}
tell application "Database Events" to tell database dbName
if (exists record aFile) then
tell (first record whose name = aFile)
set thisSkipcheck to value of field "skip"
set recordsModDate to value of field "modDate"
if fileModDate > recordsModDate then
set overwrite to true
log overwrite
if thisSkipcheck is SkippedscriptText then -- checking so as not to overwrite a skipped script by mistake
--set frontApp to displayed name of (info for (path to frontmost application))
my skippdialog(frontApp, aFile)
--my showSkipped(button_pressed, SkipList, dbName, deletedRecs, frontApp)
end if
-- use modification date to see if a record needs updating
set scriptText to my get_scriptText(aFile)
if scriptText is not "" and overwrite is true then
log overwrite
set updatedRecs to updatedRecs + 1
set value of field field1Name to fileName
set value of field field2Name to fileModDate
set value of field field3Name to scriptText
set value of field field4Name to notSkippedscriptText
--set value of field field4Name to notSkippedscriptText -- new record now so not skipped
end if
end if
end tell
else -- a record doesn't exist for this script so create a new record for it
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set newRecs to newRecs + 1
set thisRecord to make new record with properties {name:aFile}
tell thisRecord
make new field with properties {name:field1Name, value:fileName}
make new field with properties {name:field2Name, value:fileModDate}
make new field with properties {name:field3Name, value:scriptText}
make new field with properties {name:field4Name, value:notSkippedscriptText} -- new record not skipped
end tell
end if
end if
end tell
end if
end repeat
-- get a list of the non-existent records, i.e. scripts in the database that are no longer found in the Finder
set skipped_count to 0
tell application "Database Events" to tell database dbName
save
set SkipList to {}
set recordCount to count of records
repeat with i from 1 to recordCount
set thisPath to value of field "name" of record i
set thisSkipcheck to value of field "skip" of record i
tell application "Finder"
if not (exists file thisPath) then
set end of SkipList to thisPath
end if
end tell
end repeat
if SkipList is not {} then
repeat with i from 1 to count of SkipList
set this_rec to item i of SkipList
tell (first record whose name = this_rec)
set value of field field4Name to SkippedscriptText
set skipped_count to skipped_count + 1
end tell
end repeat
end if
---
(*
set recordCount to count of records
repeat with i from 1 to recordCount
set thisSkipcheck to value of field "skip" of record i
log thisSkipcheck
if thisSkipcheck is SkippedscriptText then set skipped_count to skipped_count + 0
end repeat
set skipped_count to skipped_count as string
--
-- remove non-existent records from the database
if SkipList is not {} then
repeat with i from 1 to number of items in SkipList
set this_item to item i of sList
delete record this_item
set deletedRecs to deletedRecs + 1
end repeat
end if
*)
end tell
else -- we're creating a new database so no checking of the records is necessary
if bugcheck then say 1
repeat with aFile in allFiles
set fileInfo to (info for file aFile)
if kind of fileInfo is "script" or file creator of fileInfo is "aplt" or type identifier of fileInfo is "com.apple.applescript.text" then -- check for a "script editor" file
set scriptText to my get_scriptText(aFile)
if scriptText is not "" then
set {fileName, fileModDate} to {name of fileInfo, modification date of fileInfo}
tell application "Database Events" to tell database dbName
if bugcheck then say 2
set newRecs to newRecs + 1
set thisRecord to make new record with properties {name:aFile}
tell thisRecord
if bugcheck then say 3
make new field with properties {name:field1Name, value:fileName}
make new field with properties {name:field2Name, value:fileModDate}
make new field with properties {name:field3Name, value:scriptText}
make new field with properties {name:field4Name, value:notSkippedscriptText}
if bugcheck then say 4
end tell
end tell
end if
end if
end repeat
end if
-- clean up database events
tell application "Database Events"
tell database dbName
if bugcheck then say 6
save
set recordCount to count of records
if bugcheck then say 7
end tell
end tell
-- notify user it's done updating the db and specifics of the process
--set skipped_count to "?"
set outTime to current date
set totaltime to (outTime - inTime)
set theTime to my secs_to_hms(totaltime)
--set frontApp to displayed name of (info for (path to frontmost application))
set skipbutton to {"Finished"}
if SkipList is not {} then
set skipbutton to {"Show Skipped", "Finished"}
end if
tell application frontApp to display dialog "The database has been updated." & return & "Update time: " & theTime & return & return & "Old Number of Records in DB: " & origRecordCount & return & "new Number of Records in DB: " & recordCount & return & "Records with files : " & (recordCount - skipped_count) & return & "Skipped Records : " & skipped_count & return & "Records Updated: " & updatedRecs & return & "Records New: " & newRecs buttons skipbutton default button 1 with icon 1
set the button_pressed to the button returned of the result
my showSkipped(button_pressed, SkipList, dbName, deletedRecs, frontApp)
end if
-- clean up database events
tell application "Database Events"
tell database dbName
if bugcheck then say 9
save
set recordCount to count of records
if bugcheck then say 10
end tell
if bugcheck then say 11
quit
end tell
(*=================== SUBROUTINES =====================*)
on skippdialog(frontApp, aFile)
tell application frontApp to display dialog "The script " & return & ¬
" is marked as skipped, A newer file with the same name exist," & return & return & aFile & return & return & "Do you want to view the record as a recompiled script first" buttons {"View Script", "Continue"} default button 1 with icon note
set the button_pressed to the button returned of the result
if the button_pressed is "View script" then
set this_rec to aFile
my recompile(this_rec)
set overwrite to false
log overwrite
else
-- action for 2nd button goes here
end if
end skippdialog
on showSkipped(button_pressed, SkipList, dbName, deletedRecs, frontApp)
repeat until button_pressed is "Exit"
if the button_pressed is "Show Skipped" or the button_pressed is "Recompile script" then
tell application frontApp to set theChosen to choose from list SkipList with prompt "Choose Record/s to Recompile or Delete" default items {item 1 of SkipList} OK button name "Actions" cancel button name "Exit" with multiple selections allowed
if result is false then exit repeat
log theChosen
set still_To_Action to {}
repeat with i from 1 to number of items in SkipList
set this_skipitem to item i of SkipList
if this_skipitem is not in theChosen then
copy this_skipitem to end of still_To_Action
end if
end repeat
set SkipList to still_To_Action
tell application frontApp to display dialog "Choose Action for the selected Records." buttons {"Exit", "Delete", "Recompile"} default button 2 with icon 1
set the button_pressed to the button returned of the result
repeat with i from 1 to number of items in theChosen
set this_rec to item i of theChosen
tell application "Database Events" to tell database dbName
if the button_pressed is "Delete" then
delete record this_rec
set deletedRecs to deletedRecs + 1
else if the button_pressed is "Recompile" then
log this_rec
my recompile(this_rec)
else
exit repeat
end if
end tell
end repeat
else
exit repeat
end if
end repeat
end showSkipped
on recompile(this_rec)
tell application "Database Events" to tell database dbName
tell record this_rec to set thisText to value of field "scriptText"
tell record this_rec to set thisName to value of field "name"
set thisName to do shell script "basename " & quoted form of (POSIX path of thisName)
tell application "Script Editor"
make document with properties {name:thisName, text:thisText}
end tell
end tell
end recompile
on get_scriptText(theScript)
tell application ScriptEditorApp
try
open theScript
set scriptText to text of document 1
on error
set scriptText to ""
end try
try
close document 1
end try
end tell
return scriptText
end get_scriptText
on secs_to_hms(the_secs)
if the_secs is less than 360000 then
set hr to text 2 thru 3 of ((100 + the_secs div hours) as string)
set min to text 2 thru 3 of ((100 + the_secs mod hours div minutes) as string)
set sec to text 2 thru 3 of ((100 + the_secs mod minutes div 1) as string)
set fraction to text 2 thru 3 of ((100 + the_secs mod 1 * 100) as string)
return hr & ":" & min & ":" & sec & "." & fraction
else
return false
end if
end secs_to_hms
on allFiles_ofFolder(aFolder)
set searchFolder to aFolder as string
set everyFile to {}
tell application "Finder"
try -- there's an error if nothing is in the folder when you get entire contents
set tempVar to files of entire contents of folder searchFolder
if (count of tempVar) is 0 then -- answer is 0 when there's only 1 item???
set end of everyFile to tempVar as string
else
repeat with i from 1 to (count of tempVar)
set end of everyFile to (item i of tempVar) as string
end repeat
end if
end try
end tell
return everyFile
end allFiles_ofFolder