Automation with AppScript

KosmicTask Help

Automation with AppScript

Automation with AppScript

Appscript is no longer supported as of KosmicTask v1.2.5. The appscript project is no longer actively supported.

So far we have automated our application with the help of both AppleScript and the ScriptingBridge framework. KosmicTask also supports another very capable means of achieving automation - appscript. Appscript is supported by both Ruby and Python and makes a compelling alternative to the ScriptingBridge. 

A good question would be why should I use appscript when the ScriptingBridge is already available? The simple answer is that appscript often provides a more precise way of generating automation events. This often makes it easier to turn the terminology displayed in an application's dictionary into a working script. The ScriptingBridge, though useful and moderately efficient, sometimes makes assumptions about the way that application scripting works that effectively renders scripting some operations more difficult and confusing.

The most obvious point about both Ruby appscript and Python appscript is that the order of the commands and the parameters is much more closely aligned with AppleScript than is the case than when using the ScriptingBridge. This alone makes it easier to transcribe the information in an application's dictionary into a working script.

So here is our Ruby powered Pages task using rb-appscript for automation:

require 'appscript'

# get application - for more information see the Ruby Usage document
app = Appscript.app('Pages')

pagesDocFilePath = ARGV[0]
rtfDocFilePath = pagesDocFilePath + ".rtf"

# open pages
myDoc = app.open(pagesDocFilePath)

# save document
app.save(myDoc, {:as => "SLDocumentTypeRichText", :in => rtfDocFilePath})

# close document
app.close(myDoc, {:saving => false})

# define our result file
resultFile = "result.html"

# convert our file
system "textutil -convert html -output \"#{resultFile}\" \"#{rtfDocFilePath}\""

# return our result file
puts "---"
puts "{kosmicFile: #{resultFile}, kosmicInfo: file returned}"

Each automation command we send is represented by a single Apple Event. These low level events take one default parameter and a number of often optional named parameters. When saving a document the document itself is the default parameter (aka the direct object). The required format (as) and the location (in) are the named parameters. In Ruby appscript exposes our command (save) as a function that takes a single object as the default parameter (myDoc document) and a hash of named parameters (as, in). Appscript requires that the named parameter hash keys be Ruby symbols rather than strings hence the colon prefixes to the key names. 

# save document
app.save(myDoc, {:as => "SLDocumentTypeRichText", :in => rtfDocFilePath})

Appscript supports both Ruby and Python. So here is the Python powered variant of our Pages script using py-appscript:

import os
import sys
import appscript

# get application - for more information see the Python Usage document
app = appscript.app('Pages')
pagesDocFilePath = sys.argv[1]
rtfDocFilePath = pagesDocFilePath + ".rtf"

# open pages
myDoc = app.open(pagesDocFilePath)

# save document, note we use as_ and in_ to avoid keyword collision
app.save(myDoc, as_="SLDocumentTypeRichText", in_=rtfDocFilePath)

# close document
app.close(myDoc, saving=False)

# define our result file
resultFile = "result.html"

# convert our file
command = "textutil -convert html -output \"%s\" \"%s\"" % (resultFile, rtfDocFilePath)
os.system(command)

# return a YAML inline format dictionary with filename
print "--- {kosmicFile: %s, kosmicInfo: file returned}" % resultFile

Note the following syntactical detail when saving our document:

# save document, note we use as_ and in_ to avoid keyword collision
app.save(myDoc, as_="SLDocumentTypeRichText", in_=rtfDocFilePath)

When passing in the command's named parameters appscript makes use of Python's support for named optional arguments rather than using a hash as is the case with Ruby. It turns out that 'as' and 'in' are both Python keywords and therefore cannot be used as named arguments. Hence appscript accepts 'as_' and 'in_' as equivalents.