Reference external script file - applescript

I'd like to implement this script (also listed below) as a separate file. How do I formulate the reference to it as a POSIX path?
tell application "ASObjC Runner"
activate
set chooserResult to run the script {chooseFilesOrFolders} with response
-- the above line would have to reference something like RemoteVolume/test.scpt
end tell
The referenced script itself existing as separate file "test.scpt":
script chooseFilesOrFolders
tell current application's NSOpenPanel's openPanel()
setTitle_("Choose Files or Folders") -- window title, default is "Open"
setPrompt_("Choose") -- button name, default is "Open"
setCanChooseFiles_(true)
setCanChooseDirectories_(true)
setAllowsMultipleSelection_(true) -- remove if you only want a single file/folder
get its runModal() as integer -- show the panel
if result is current application's NSFileHandlingPanelCancelButton then error number -128 -- cancelled
return URLs() as list
end tell
end script

You must use the fFinder path for your script test.scpt, and then call the library using "load script file" command:
In your test.scpt file :
On ChooseFilesOrFolders
-- all your script lines here
return URLs() as list -- to send back result of your sub routine
end ChooseFileOrFolders
In your main script :
Set Script_Lib to "HD:Users:me:Desktop:test.scpt" -- the complete path to your text script.
Set My_Lib to (load script file Script_Lib)
-- insert here you main script lines, and when you want to call the function :
tell My_Lib to Set chooserResult to ChooseFilesOrFolders
-- Here, chooserResult will contain the list returned from test script
Also note that your script "test can also contains many other subroutines which can be called as fonctions in your main script.
I hope it helps.

Related

How to wait for osascript to produce a result

sometimes my AppleScript code seems to fail, on other machines is works.
I run this AppleScript code as heredoc from inside a shell script. The shell script is a postinstall script run by a pkg installer and runs as root:
#!/bin/sh
set -x
logfile="/Library/Logs/EZEEP Connector Installer.log"
LogMessage()
{
echo $(date) $1 >> "${logfile}"
}
LogMessage "................................."
LogMessage "Installer postinstall started ..."
LogMessage "................................."
# set file system tag 'Printing' on app bundle:
/usr/bin/osascript <<'EOD'
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
on addTags:tagList forPath:posixPath -- add to existing tags
set aURL to current application's |NSURL|'s fileURLWithPath:posixPath -- make URL
#display dialog aURL as string
-- get existing tags
set {theResult, theTags} to aURL's getResourceValue:(reference) forKey:(current application's NSURLTagNamesKey) |error|:(missing value)
if theTags is not missing value then -- add new tags
set tagList to (theTags as list) & tagList
set tagList to (current application's NSOrderedSet's orderedSetWithArray:tagList)'s allObjects() -- delete any duplicates
end if
aURL's setResourceValue:tagList forKey:(current application's NSURLTagNamesKey) |error|:(missing value)
end addTags:forPath:
tell application "Finder"
set thePath to (POSIX path of (application file id "com.thinprint.ezeep.Connector" as alias))
my addTags:{"Printing"} forPath:thePath
end tell
EOD
#sleep 10
sudo -u ${USER} /usr/bin/osascript -e 'tell application "/Applications/ezeep Connector.app" to launch'
LogMessage "................................."
LogMessage " Installer postinstall finished ."
LogMessage "................................."
Sometimes the targeted app bundle gets a tag, sometimes not. If I uncomment the last sleep in my code sample, then everything works as expected.
Even on 2 different VMs we have the tag always created on one VM and failing to create on the other.
Is there a proper way to wait for the result of the add tag operation and then proceed with the script? Or is the sleep 10 command a reliable solution to have it run on dozens or event hundreds of Macs without failing on even a few?
kind regards,
Robert
First, it's always good idea to avoid scripting the Finder (a busy app that can produce odd delays and errors in scripts). Since you're using AppleScriptObjC anyway, you can get the URL you need from NSWorkspace. Then all you need to do is add a repeat/check/delay loop that looks to see if the tag has been added.
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
set theURL to my findAppURLFromID:"com.thinprint.ezeep.Connector"
my addTags:{"Printing"} forURL:theURL
my checkTag:"Printing" forURL:theURL
(* get app package URL from NSWorkspace *)
on findAppURLFromID:appID
set sharedWorkspace to (current application's NSWorkspace's sharedWorkspace)
set appURL to sharedWorkspace's URLForApplicationWithBundleIdentifier:appID
return appURL
end findAppURLFromID:
(* altered to accept URL directly *)
on addTags:tagList forURL:aURL
set {theResult, theTags} to aURL's getResourceValue:(reference) forKey:(current application's NSURLTagNamesKey) |error|:(missing value)
if theTags is not missing value then
set tagList to (theTags as list) & tagList
end if
(*
I moved the following statement out of the 'if' block. This would not be
called if the package file has no tags, so the script would feed an unaltered
AppleScript list to 'setResourceValue:forKey:error'. That may work, dunno
but better to put everything into the correct object format
*)
set tagList to (current application's NSOrderedSet's orderedSetWithArray:tagList)'s allObjects() -- delete any duplicates
aURL's setResourceValue:tagList forKey:(current application's NSURLTagNamesKey) |error|:(missing value)
end addTags:forURL:
(* checks every half second to see if the list of tags contains "Printing" *)
on checkTag:tagName forURL:aURL
repeat 120 times
set {theResult, theTags} to aURL's getResourceValue:(reference) forKey:(current application's NSURLTagNamesKey) |error|:(missing value)
set tagList to (theTags as list)
if tagList contains "Printing" then return
delay 0.5
end repeat
display alert "tagging timed out after 1 minute"
end checkTag:forURL:
I fixed one (potential) error in your script; see the text comment inline.
This assumes that you are only adding a single tag. If you want to add (and check) for multiple tags, you'll need to complexity checkTag:forURL. Also, I haven't done much with error handling (any case where for some reason adding the tag fails) except toss up an alert. I used a contingent repeat loop to keep it from going on infinitely on failure, but you may want it to fail silently, or do something else at that point.

How to wait for a file to be added to iTunes in Applescript

I have the following AppleScript to add a new file to iTunes
tell application "iTunes"
launch
set the_track_ref to add the_filename as POSIX file as alias
-- delay 5 -- This prevents "Error: error in user parameter list (paramErr:-50)"
set the_track to contents of the_track_ref
set the name of the_track to the_track_name -- This sometimes results in "Error: error in user parameter list (paramErr:-50)"
set the album of the_track to the_track_album
set the artist of the_track to the_track_artist
set the genre of the_track to the_track_genre
end tell
The file to add is an audio file, typically 2 hours long. When I run the script, I often get the error Error: error in user parameter list (paramErr:-50) on the line set the name of the_track to the_track_name. My guess is that there is a delay in copying the file from a temporary location to the iTunes library and the_track is not yet available.
As you can see, I have tried adding a delay of 5 seconds, but this still does not prevent the problem. I can recreate the problem 50%-75% of time by running the script manually. iTunes is running at the time of the error.
I think that I should create a loop after the add statement to wait for the import to be valid, but I don't know what to check for.
I don't see any direct way to test whether the file is ready to be processed, but it's easy enough to test for the error in a loop:
tell application "iTunes"
launch
set the_track_ref to add the_filename as POSIX file as alias
set the_track to contents of the_track_ref
repeat
try
set the name of the_track to the_track_name
exit repeat
on error errstr number errnum
if errnum = -50 then
delay 0.5
else
display alert "Error " & errnum & ": " & errstr
end if
end try
end repeat
end tell
If renaming the track errors out, the loop jumps to the error section. If the error is the expected '-50' the loop delays a half-second and tries again; if some other error occurs, the script displays an error dialog. If renaming the track succeeds, the script exits the loop and continues processing.
You can put the other commands inside the try block or after it, as you see fit. I can see pros and cons either way, depending on the kinds of errors you're likely to see.

Change the variable in a workflow from a separate workflow

I have two workflows and I need to pass a value generated in one workflow to another workflow.
In my first workflow, I have an AppleScript which returns a number I want to put into a second workflow which I call from the first workflow like so:
My second workflow (Create Class in iStudiez) has a variable 'Class Number' which I want to change when I call it from my first workflow with the return value of the AppleScript pictured above.
Since you are using Automator and AppleScript in Automator, and you have not posted any actual code, it's difficult to give you an exact answer of what you're looking for.
There may be an easier solution but the solution I came up with was to create one script which will save a variable into a new script file (which will automatically be created on your desktop with the name of “Stored_Variable.scpt”. The second script loads the value of the variable stored in the “Stored_Variable.scpt” file.
Simply paste the code from this first script, directly into the code which contains the variable you want to copy. Be sure to paste the code after the code which sets the value of the variable you want copied.
-- Comment Out This Next Line Before
-- Placing This Code Into Your Script
-- Which Contains The Variable You Want Copied
set originalVariable to (path to desktop) -- Testing Purposes Only
-- Replace "originalVariable" with the
-- Name Of Your Actual Variable You Want To Pass
-- To The Next Script
set saveThisVariable to originalVariable
storeTheVariable()
-- The Following Code Belongs At The Very Bottom Of Your Script
on storeTheVariable()
set storedVariabeFileLocation to (path to desktop as text) & "Stored_Variable.scpt"
----------------------
script theVariable
set saveThisVariable to saveThisVariable
end script
----------------------
store script theVariable in ¬
file storedVariabeFileLocation with replacing
end storeTheVariable
Place this is second code inside the code of your AppleScript in which you are trying to retrieve the variable stored from the first AppleScript code
-- Gets The Variable Which Was Previously Stored
-- From The Other Applescript And Stores It In A
-- New Variable... getVariableNow
set getVariableNow to run loadTheVariable
-- -----------------------------------
-- Place Whatever Commands Here, That You Will Be Using
-- The New Variable... getVariableNow with
-- -----------------------------------
-- The Following Code Belongs At The Very Bottom Of Your Script
script loadTheVariable
property storedVariabeFileLocation : (path to desktop as text) & "Stored_Variable.scpt"
property theRetrievedVariable : missing value
on getStoredVariable()
set theScript to load script file storedVariabeFileLocation
set theRetrievedVariable to saveThisVariable of (theVariable of theScript)
end getStoredVariable
set theRetrievedVariable to loadTheVariable's getStoredVariable()
end script

Embed a bash shell script in an AppleScriptObjC application with Xcode

I have attempted to follow the instructions on this post but I am falling short of understanding how some of the posters instructions work.
I want to be able to package the app with a prewritten bash script and then execute it, but don't follow from Step 4 onwards.
Post writes:
4. Also in your AppleScriptObjC script, add the following where appropriate:
property pathToResources : "NSString" -- works if added before script command
5. Where appropriate, also add the following in your AppleScriptObjC script:
set yourScript to pathToResources & "/yourScriptFile.sh"
-- gives the complete unix path
-- if needed, you can convert this to the Apple style path:
set yourScriptPath to (((yourScript as text) as POSIX file) as alias)`
6. As an aside, you could then open the file for read using
tell application "Finder"
open yourScriptPath
end tell
Questions:
Where do I add the line:
property pathToResources : "NSString"
Do I add which of the following, and where?
set yourScript to pathToResources & "/yourScriptFile.sh"
OR
set yourScriptPath to (((yourScript as text) as POSIX file) as alias)
How is it possible to execute the script itself? The mention As an aside, you could then open the file for read using only covers the Apple style path, it does not cover using the aforementioned style.
Can anyone shed a bit more light on this for me, or post a static copy of a AppDelegate.applescript file that shows how the original poster required the base code to be used? I have tried his method and looked across the internet for the past 3 weeks to no avail. I don't want to have to convert all my code for specific tools from bash scripts into AppleScript, as this would take a lot of work.
I only need to know how to reference to the script file (for example myBashScript.sh) in my app, which would reside in the application and be included by Xcode at time of compilation.
I think you should use the command path to resource <specifiedResource>.
See Standard Additions, path to resource.
You could set it by set myVariableName to path to resource "myBashScript.sh" or just use the command instead of your property so it points always to the right place (a user could move your app while running... lol).
ADDITION:
I did it that way in my AppleScript-Application:
on run_scriptfile(this_scriptfile)
try
set the script_file to path to resource this_scriptfile
return (run script script_file)
end try
return false
end run_scriptfile
Whenever I want to run a script that is bundled within my app I do this:
if my run_scriptfile("TestScript.scpt") is false then error number -128
run_scriptfile(this_scriptfile) returns true when everything worked.
I ended up bringing all the information together and now have a solution.
This takes into consideration the following facts:
firstScript = variable name that points to a script called scriptNumberOne.sh
scriptNumberOne.sh = the script that I have embedded into my application to run
ButtonHandlerRunScript_ = the name of the Received Action in Xcode
pathToResources = variable that points to the internal Resources folder of my application, regardless of it's current location
Using this information, below is a copy of a vanilla AppDelegate.applescript in my AppleScriptObjC Xcode project:
script AppDelegate
property parent : class "NSObject"
property pathToResources : "NSString"
on applicationWillFinishLaunching_(aNotification)
set pathToResources to (current application's class "NSBundle"'s mainBundle()'s resourcePath()) as string
end applicationWillFinishLaunching_
on ButtonHandlerRunScript_(sender)
set firstScript to pathToResources & "/scriptNumberOne.sh"
do shell script firstScript
end ButtonHandlerRunScript_
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return current application's NSTerminateNow
end applicationShouldTerminate_
end script

Global preferences for AppleScript app

Is it possible to save some kind of settings for an app create in AppleScript?
The settings should be loaded at the beginning of the script and be saved at the end of the script.
Example:
if loadSetting("timesRun") then
set timesRun to loadSetting("timesRun")
else
set timesRun to 0
end if
set timesRun to timesRun + 1
display dialog timesRun
saveSetting("timesRun", timesRun)
Where the dialog would show 1 the first time running the script, 2 the second time...
And the functions loadSetting and saveSetting would be the functions i need.
Script properties are persistent, though the saved value is overwritten by the value specified in the script whenever you re-save the script. Run:
property |count| : 0
display alert "Count is " & |count|
set |count| to |count| + 1
a few times, re-save it then run it a few more.
If you want to use the user defaults system, you can use do shell script "defaults ..." commands or (if using Applescript Studio) default entry "propertyName" of user defaults. In Applescript Studio, you bind values to user defaults.
This is also working well (check the first comment to the hint):
http://hints.macworld.com/article.php?story=20050402194557539
It uses the "defaults" system and you get your preferences in the ~/Library/Preferences
Applescript supports natively reading and writing plists through System Events:
use application "System Events" # Avoids tell blocks, note: 10.9 only
property _myPlist : "~/Library/Preferences/com.plistname.plist
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
set plistItemValue to plistItemValue + 1
set value of property list item "plistItem" of contents of property list file _myPlist to plistItemValue
The only problem with this is that it can't create the plists so if the existence of the plist is not certain you need to wrap it on a try.
try
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
on error -1728 # file not found error
do shell script "defaults write com.plistname.plist plistItem 0"
set plistItemValue to get value of property list item "plistItem" of contents of property list file _myPlist
end try

Resources