osacompile changing the AppleScript output so it won't run - applescript

I have a semi-long AppleScript that I run each morning to launch all of my apps etc. One of the things that it does is launch a few apps and then immediately minimize them. When I paste the .applescript source into Script Editor and run it, everything works fine:
-- snip:
tell application "Mail"
launch
minimize(window 1) of me
check for new mail
end tell
-- 'minimize' defined as:
on minimize(w)
set the miniaturized of w to true
end minimize
But when I compile the AppleScript source as follows:
osacompile -o ~/Library/Scripts/myscript.scpt myscript.applescript
... the compiler munges minimize to be:
on minimize(w)
set |miniaturized| of w to true
end minimize
And I get this error:
error "Mail got an error: Can’t make |miniaturized| of window id 30936 into type reference." number -1700 from |miniaturized| of window id 30936 to reference
Anyone have any clue what I'm doing wrong here? For purposes of version control, I need to run the scripts through osacompile.
UPDATE: To clarify, what seems to be happening is that Script Editor is compiling the method differently than osacompile on the command line. Is it known whether they compile different (e.g., using scope inferences or some such thing)?

There is nothing wrong with your code - I suspect this is a bug in osacompile and I suggest you file a bug report with Apple - as I've done.
You can verify that your code works correctly by using AppleScript Editor to save it as a *.scpt file directly and then running it with osascript.
[Updated] By contrast, passing the *.applescript source-code file directly to osascript does exhibit the problem.
There is no good reason I can think of for AppleScript Editor-based compilation to work differently from osacompile (and on-demand compilation in osascript), and the former's behavior is the expected and desired one in this case.
There are 2 workarounds:
Enclose the reference to miniaturized in a using terms from application "System Events" block:
This is a generic workaround that should work with windows from any AppleScriptable application.
on minimize(w)
using terms from application "System Events" # WORKAROUND
set miniaturized of w to true
end using terms from
end minimize
Inline the miniaturization command instead of calling a subroutine:
set miniaturized of window 1 to true

on minimize(w)
set the miniaturized of w to true
end minimize
This is not correct. You have "miniaturized" outside any application tell block of code, which means that it's an applescript command. It isn't an applescript command though. It's a command of Mail and other applications. It's amazing that it works properly in AppleScript Editor. It really shouldn't. When you have a command that doesn't make sense applescript is good enough to try to make sense of it and sometimes it can overcome your coding mistake. Obviously osacompile can't overcome your coding mistake.
So the way to fix the osacompile issue is to eliminate the coding mistake. You need to have the miniaturized command inside of an application tell block of code.
To see what I mean open the "standard additions" applescript dictionary and try to find the miniaturized command. you won't find it. Now open Mail's applescript dictionary and you will find it. Thus the command belongs to Mail, not applescript.

Related

How to remove mapping/binding of application in AppleScript

I have some AppleScript which I run via osascript on the command line.
The script itself looks like:
on run argv
set appname to item 1 of argv
set tmp to item 2 of argv
set jsfl_path to POSIX file tmp
if application appname is running then
tell application appname
with timeout of 600 seconds
open jsfl_path
end timeout
end tell
end if
end run
I updated Adobe CC Animate to the latest version. I was previously running the 2018 version. It turns out they renamed the file pattern. It used to be Adobe Animate CC 2018. Now it is Adobe Animate 2019.
Here is where the problem starts. In my script I was sloppy and change the name to Adobe Animate CC 2019.
When I ran the AppleScript, it produced a dialog to Choose Application. In my haste, I accidentally mapped it to the wrong program.
I'd like to remove this mapping. What I cannot find is where this mapping is stored. Does anyone know where this type of mapping gets saved?
It isn’t really stored anywhere, the Script Editor uses your declarations to determine where to load the scripting terminology from. Recompiling the script after making an edit should reset things, otherwise you can restart the Script Editor. Note that you need to use a specific bundle identifier or application name - using a variable to specify the application can be problematic, as the terminology is loaded at compile time.

exploring avaliable commands in an applescript program

I have an application and I can do the following command on (I know this because I googled for it):
tell app "TextMate" to reload bundles
What I would really like is to be able to ask the "TextMate" program:
tell app "TextMate" to list all commands
and have it list out all the things I can ask it to do:
... 'reload bundles', 'exit', 'open files'...
is there a way to do that with applescript?
The way to find all the commands of an app is to open its dictionary in your script editor. Usually "Open Dictionary ... " in the File menu, or drop the application onto the script editor.
[EDIT]
For applications that have AppleScript support, you can actually script opening the app itself with the Script Editor, a la:
set pathToApp to (choose file of type "APPL")
tell application "Script Editor"
open pathToApp
end tell
BUT this will be problematic with a non-scriptable app. You'll get an error, but Script Editor will actually open some part of the app (and it will be slow about it), then give you an unusable document. There's no way to catch this error. If you use the Smile script editor, you can use this method ...
set p to (choose file of type "APPL")
try
OpenDictionary(alias (p as string))
on error e
end try
... to open the dictionary of an app, and if it doesn't work (if the app doesn't have a dictionary), it returns an error but doesn't do anything else (but again, you can't catch the error and not have it complain, without hacking Smile)
[EDIT 2]
A rabbit hole to go down is trying System Events or the Finder to check for boolean of has scripting terminology property of a process, but I don't recommend it because I haven't found it to be reliable.
[EDIT 3]
Ach! I knew there was another method, but forgot what it was. As #mklement0 points out (thank you), you can do this to check for an app's script-ability prior to opening the app in Script Editor:
set pathToApp to (choose file of type "APPL") as text
set isScriptable to false
try
class of application pathToApp
-- only AppleScriptable applications have a class property
set isScriptable to true
end try
isScriptable

Expected end of line but found “"”. is showing up on apple script editor

tell application "Finder" to say "this is a test"
tell aplication"finder"
activate
repeat 5 times
make new Finder window
end repeat
end tell
I am just learning different coding and know quite a bit about html, css, and javascript. I am completely new to apple script editor.
Since you're new to applescript I'll give you a basic tip to learn. Only tell an application to do something that it knows how to do. Each application knows how to do specific things and applescript knows how to do things by itself too.
I tell you this because the "say" command is an applescript command, not a Finder command. So there's no reason to tell the Finder to say anything. As you get more complex in your scripts you will find errors if you tell the wrong application to do something. As such you can run the say command by itself. Try this and it will work by itself...
say "this is a test"
The easiest way to know what each application understands is to look in the dictionaries. In Script Editor, under the file menu choose "open dictionary". You can choose any application but for this example open the Finder dictionary. You can search through it to find what the Finder knows how to do. You'll notice it doesn't have the "say" command thus you know not to tell the Finder to use the say command. You can type "say" into the search field and you'll see it doesn't return any results.
If you open the dictionary for "StandardAdditions" you'll find say in there. That's additional things applescript knows by itself.
Good luck.
The part tell aplication"finder" has two typos and is missing a space. It should be tell application "Finder"
The whole think should look like this which builds for me.
tell application "Finder" to say "this is a test"
tell application "Finder"
activate
repeat 5 times
make new Finder window
end repeat
end tell

Create AppleScript for a program that isn't installed on the current computer

I'm trying to make two copies of an AppleScript, one that works for Entourage and one for out Outlook. I only have Entourage installed on the current computer.
According to the info on Microsoft's site, both applications have the same library of AppleScript commands, and I should be able to simply change the application name referenced within the script.
Changing:
Tell application "Microsoft Entourage"
to
Tell application "Microsoft Outlook"
Prevents me from saving the script because I don't have outlook installed on this computer. Is there any way around this? Do I need to use a text editor to edit the actual script file and change it there?
Thanks!
The following work-around may do the trick. On the computer where Entourage is installed, a using terms directive will let you compile the script, even if Outlook is not installed:
set theApp to a reference to application "Microsoft Outlook"
using terms from application "Microsoft Entourage"
tell theApp
get version
...
end tell
end using terms from
Upon compiling and saving the script the AppleScript Editor will bug you about the missing Outlook application, but it will nevertheless produce a compiled AppleScript file (.scpt).
Applescript is a pre-complied file format, meaning that every time you click "Save" it runs through a series of steps to ensure the script will work, but just short of actually running through the script's logic. Part of those steps is to look for the application to see if it exists on the Mac.
In short, if you want to save the script as an Applescript, you need the target application installed, otherwise you can save the script as a text file and move the file over to the target Mac to save as an Applescript over there.
It should be possible to make one script that works with both Entourage and Outlook, without bugging you if one isn't found either when you compile or when you run. I don't have either Entourage or Outlook but it should work like this:
using terms from application "Microsoft Entourage"
script theScript
tell application "Finder" to try
set theApp to application file id "Entourage's Bundle ID" as text
on error
set theApp to application file id "Outlook's Bundle ID" as text
end try
tell application theApp
-- do stuff
end tell
end script
end using terms from
store script theScript in "MyScript.scpt"
"using terms from" is only relevant when compiling the script - it isn't needed when running, though for some reason you'll still get bugged if that app isn't found. So by wrapping it around a script object and then writing out that script to file, the resultant script will still run but won't contain "using terms from" and so won't bug the user.
For getting a reference to the right app, Finder can look for it by ID and simply error if it isn't found rather than bugging the user. You'll need to insert the proper ID's there, I don't know what they are.

Google SketchUp close file

The Ruby API to Google SketchUp has a function, open_file, but I can't find a close_file function. Since I have to batch process many files, I want to close each file before moving on to the next, otherwise the program will crash from memory exhaustion.
What is the best way to close SketchUp files programmatically?
I am using Mac OS X and am willing to use AppleScript functions to signal the window to close.
EDIT
I am considering a few approaches that have proven fruitless so far.
Using the appscript Ruby gem, as described in this question. The problem here is that I cannot get SketchUp to recognize my installed gems.
In a similar vein, I am trying to use osascript (a bash program that executes AppleScripts from the shell) to close the window. That is, I call out to the shell from SketchUp's Ruby console window using one of the following:
%x[osascript -e 'tell application "SketchUp" to close window 1']
%x[osascript -e 'tell application "SketchUp" to close window 1' &]
%x[osascript -e 'tell application "SketchUp" to close every window']
%x[osascript -e 'tell application "SketchUp" to close every window' &]
Whenever I try this second approach, SketchUp just freezes. However, when I execute any of these commands from an IRB or directly from the Bash prompt outside of SketchUp, I get the desired behavior: the model window closes (incidentally, the Ruby console window remains open, which is fine).
Have a master script that launches a slave script to process each model. The slave will run within the Google SketchUp program while the master waits. When the slave is finished, it signals the master, and the master closes the SketchUp file. To do this interprocess communication, I tried using drb. However, when I try to require drb within SketchUp, I get the following message:
Error: LoadError: (eval):5:in 'require': no such file to load -- drb
EDIT 2
Having a separate process continuously running that closes Google Sketchup windows using AppleScript when signaled is clumsy for a number of reasons. First, it's ugly to have to have a separate process devoted to closing Sketchup windows. Second, the only effective way of communicating with the external script is through the creation of files, which is wasteful and the disk access may be slowing things down.
However, the most severe issue is that Sketchup is slow at responding to AppleScript commands. I have a pretty computation intensive script running in Sketchup, and it seems to starve the AppleScript response, which means that the osascript times out before the windows close. Sketchup only gets around to responding to AppleScript when there is a dialogue box prompt in Sketchup that pauses the execution of my computationally intensive script.
EDIT 3
I have modified my close_file function to pause execution of the script by displaying a dialog box. This essentially yields the current thread and allows the thread that responds to AppleScript commands to execute:
def close_file()
f = '/temp/mutex.txt' # for finer control, use different mutex for each window you want closed
File.new(f, 'w').close
result = UI.messagebox "Click OK when window has closed."
end
Then the separate ruby script that closes windows via AppleScript will additionally have to click "OK" in the dialog box. AppleScript to do that is:
tell application "System Events"
tell process "SketchUp"
set frontmost to true
keystroke return
end tell
end tell
This modification is an improvement. It corrects the "most severe issue" mentioned in EDIT 2, but the other issues remain.
There is an important limitation with using an external AppleScript to force SketchUp to do something. SketchUp will not accept any user input while a menu script is running.
I found this out trying to setup an automated rendering solution. I added a menu item which opens a WebDialog to get a list of models to download from a server. It then steps through the list and uses cURL to download each model. Once downloaded it loads the model, renders it out, uploads the images using cURL again, and goes to the next model.
What I wanted was to activate the AppleScript after each model is rendered (using the "mutex" file solution shown above) and have it close the model window. Unfortunately, since the menu script is still running, SketchUp will not respond to anything the AppleScript tells it to do. Only once all of the models have been processed and the menu script exits, will the AppleScript finally run.
This would not be so bad if all of the calls to the AppleScript were queued up, but they aren't. It seems that only the last two get honored (that number may be just an accident dependent on the timing of when my calls to the AppleScript were made relative to when the menu script finished).
Because of this limitation, I was not able to use the mutex wait() function in my menu Ruby code. Since the AppleScript was not able to execute, it never created its "I'm done" mutex file, and therefore the wait() function in Ruby never got the signal that it could continue. The result was a deadlock between AppleScript and SketchUp, such that SketchUp just freezes up (well, actually it is just stuck in a really tight loop in the wait() function).
So, when approaching this problem, think of AppleScript as a cleanup tool you can call when all of your processing is done.
One solution is to have a separate process continuously running that will close Google Sketchup windows using AppleScript when signaled. The approach described here uses files to do the interprocess communication (drb did not seem to work).
First, you have to make sure Sketchup supports AppleScripting. To do this, execute the following at the command prompt (with appropriate substitutions for different Sketchup versions, etc.):
$ defaults write /Applications/Google\ SketchUp\ 8/SketchUp.app/Contents/Info NSAppleScriptEnabled -bool YES
Now, create a separate file that will close Sketchup windows. Here is my closer.rb file:
#!/usr/bin/ruby
## closer.rb ##
def wait(f)
while !File::exists?(f)
end
File.delete(f)
end
def signal(f)
File.new(f, 'w').close
end
while true
wait('~/temp/mutex.txt')
msg = %x[osascript -e 'tell application "SketchUp" to close every window']
signal('~/temp/conf.txt')
end
Run this script from the shell. It waits until the file ~/temp/mutex.txt is created. Once the file is created, it runs osascript (essentially AppleScript) to close all the windows in Sketchup, and then signals that the windows were closed by creating a ~/temp/conf.txt file.
Here is the client code (which can be placed in your Sketchup plugins) that signals the closer script:
def wait(f)
while !File::exists?(f)
end
File.delete(f)
end
def signal(f)
File.new(f, 'w').close
end
def close_file
signal('~/temp/mutex.txt')
wait('~/temp/conf.txt')
end
The close_file signals the closer script and then waits for a confirmation that the file was closed before returning. Now you can close files in Sketchup.
You can do a lot with SketchUp using applescript and/or Automator.
tell application "SketchUp"
activate
tell application "System Events"
delay 1.0 --close window, adjust delay to suit
--key code 13 using command down -- Press ⌘W or you can use
keystroke "w" using command down
end tell
end tell
I have a number of status bar items for open new, paste to console, closing, killing SU etc.
They're tiny files that trigger the SU shortcut keys, and can be used externally, from 'Ruby Console' or as part of a plugin.
I'm not sure what kind of 'batch convert' your trying to achieve, but
I used Automator to convert most of my old v5, V6, v7 files to v8 today...
100's of them,
and your probably aware, on opening you get 'alert' warning that saving will convert the file to the latest version (which is what I wanted).
It's tricky to click this "OK" programatically.
Automator on it's own is quite restricted, but you can add an applescript to the workflow and I've found that if you use it's 'record function' to demonstrate what you need to do.
You can then copy/paste that code into 'Script Editor', get rid of the 'dowithTimeout' bits, copy it back into the workflow with delays either side and let it run.
I had to play around with delays, to accommodate some larger files, but achieved >95% success. [Success being able to select all, right click, Create Icon and Preview.]
Unfortunately I then binned the workflow, but could recreate it, if you want to have a look.
success, in Ruby Console
system("osascript -e 'tell application \"suoff\"' -e 'activate' -e 'end tell'")
and this script inside an Automator.app in applications folder....
on run
-- Make sure SU is foremost, don't click on anything else either
tell application "System Events"
tell process "SketchUp"
set frontmost to true
-- gives a message if you try to select when SU's not running
delay 0.2
tell application "SketchUp" to quit saving no
-- no dialog box etc...
delay 0.1
end tell
end tell
end run
the problem is SU has an anti self destruct ruby, that mines any linked files and aborts all efforts to shut it down... from inside itself.
so you need to bury it...
this combination works on 10.5.8 with SU8.1.
if you have a look at SketchUcation [Developer] Forum, I left a call for mac/applescript testers... I've rewritten the app since then but if you PM me I'll send you an open copy of all the bits I've got working..
john
I had to convert hundreds of .skp files with a batch processing script. I faced similar issue, since every time I opened a new file it remained opened. For me it was enough to close the active_model and the file goes with it.
This was true for SketchUp 2019 Pro Mac OS X version.
model = Sketchup.active_model
model.save(filename, Sketchup::Model::VERSION_2015)
model.close
Hope it will help

Resources