Combining Specialties

Different applications are good at different things. You typically don’t perform every task in a single application. Some applications do attempt to behave as “Swiss army knives” and be all things to all people (for example, Microsoft Word, a word processor, includes image-processing facilities); but on the whole, applications are specialized. Very often the point of using AppleScript is to get two or more applications working together. By sharing data, each application can contribute its own particular excellence to an operation’s workflow.

Here’s an example. In order to design the stop list for a web-based search engine, I needed to know the most frequently used words in each web page on the site. The web pages were easily stripped of their HTML, but then the question was how to count the occurrences of each word in a file and record the totals for the 30 most frequent words. The counting, I decided, could best be done with Ruby, a Unix scripting language I’m fond of. I wrote a little Ruby script that could be called from the command line with a file’s pathname as argument:

#!/usr/bin/ruby
class Histogram
        def initialize
                @tally = Hash.new(0)
        end
        def analyze(s)
                s.split.each do |word|
                        myword = word.downcase.gsub(/[^a-z0-9]/, "")
                        @tally[myword] = @tally[myword] + 1 if !myword.empty?
                end
                @tally.sort { |x,y| y[1]<=>x[1] }
        end
end
analysis = Histogram.new.analyze(File.new(ARGV[0]).read)

The hash of words and their frequencies has now been sorted by frequency and is sitting in the variable analysis. But what to do with this data? It was decided that the most flexible approach would be to store it in an Excel worksheet for later retrieval, manipulation, and analysis. So the Ruby script now goes on to hand the data over to Excel, using AppleScript. Here’s the second part of the same Ruby script:

counter = 1
oneline = ""
analysis[0..29].each do |entry|
        oneline = oneline + "set the value of cell 1 of " +
                "row #{counter.to_s} to \"#{entry[0]}\"\n"
        oneline = oneline + "set the value of cell 2 of " +
                "row #{counter.to_s} to #{entry[1].to_s}\n"
        counter = counter + 1
end
script = <<DONE
        tell application "Microsoft Excel"
                activate
                tell worksheet 1
                        #{oneline}
                end tell
        end tell
DONE
`osascript -ss -e '#{script}'`

The strategy here, typical when using AppleScript from a Unix script, is to construct the entire AppleScript code as a string and then hand it over to osascript for execution. (We’ll come back to this in Chapter 23.) The script sets one cell at a time in the Excel worksheet until a two-by-thirty range of cells is filled up, showing the thirty most frequently used words and the number of times each occurs (Figure 1-1).

Data communicated to Excel by Ruby

Figure 1-1. Data communicated to Excel by Ruby

I next wrap an AppleScript frontend around this Ruby script. The reason is that AppleScript has the ability to present to the user a dialog from which to choose the file to be analyzed. So it presents this dialog, transforms the pathname of the chosen file to a Unix-type pathname, and calls the Ruby script with that pathname as argument:

set thePath to POSIX path of (choose file)
tell application "Microsoft Excel" to Create New document
set s to "ruby ~/Desktop/histoChart.ruby '" & thePath & "'"
do shell script s

At this point someone has a great idea. (I hate when that happens.) It might be useful to include in this worksheet a graph of the data. So having called the Ruby script, which has inserted the data into the Excel spreadsheet, the script proceeds to talk to Excel directly. This is the second half of the AppleScript code:

tell application "Microsoft Excel"
        tell Worksheet 1
                Select Range "A1:B30"
                set c to Create New Chart
                set Type of c to xlBar
                set HasTitle of c to true
                set text of Characters of ChartTitle of c to "30 Most Frequent Words"
                set HasLegend of c to false
                ApplyDataLabels c Type xlShowLabel without LegendKey
                set numAxes to count Axes of c
                repeat with ix from 1 to numAxes
                        set thisAxis to Axis ix of c
                        set HasMajorGridlines of thisAxis to false
                        set HasMinorGridlines of thisAxis to false
                        set HasTitle of thisAxis to false
                end repeat
                set Size of Font of DataLabels of Series 1 of c to 14
                set HasAxis of c to {{false, false}, {false, false}}
        end tell
end tell

First we select the range of cells where we know the Ruby script has just deposited the data; then we generate and format a chart of that data (Figure 1-2).

Excel chart generated with AppleScript code

Figure 1-2. Excel chart generated with AppleScript code

Let’s sum up the example. We start with AppleScript. The script puts up a dialog where we choose a file (because that’s something AppleScript is good at). The script takes the pathname of that file and calls the Ruby script. The Ruby script makes a histogram of word usage in the file (because that’s something Ruby is good at). The Ruby script then constructs and calls some AppleScript code to store the first 30 rows of the histogram in Excel (because storing and dealing with this kind of data is something Excel is good at). The Ruby script ends, and it’s now back to AppleScript; here, we tell Excel to graph the data (because that’s something else Excel is good at).

There are many other ways to solve the given problem, but the virtue of this approach is that it was easy to assemble using the tools, knowledge, and resources I had on hand. Ruby and Excel and AppleScript were all right there waiting to be used, so I used them. Furthermore the pieces of the problem were solved one at a time, in the order shown, without knowing precisely what else I might want to do. AppleScript thus acts as a kind of flexible glue, allowing me to assemble code modules into a workflow that performs some larger task.

An interesting variation on the theme of combining specialities arises when one of the specialized applications isn’t on your computer. This is possible because AppleScript can send messages to remote applications. Details on the different ways this can be done appear later in this book (Chapter 21). For this example, we’ll have the remote application be a web service. AppleScript supplies a built-in way to talk to any web service that implements an XML-RPC or SOAP interface.

Tip

A good clearinghouse for finding SOAP-enabled web services is http://www.xmethods.net.

Sometimes, in sending an email message, I want to append a random quotation to the end of the message instead of my normal signature. I happen not to have a random quotation generator on my computer—and I don’t need one, because there are several on the web. I’ll just choose one of them for this example.

The script starts by creating a new outgoing email message, using Mailsmith. Then it goes out on the Web and uses SOAP to fetch today’s random quote from a random quotation web service; it formats the results and inserts them into the email message.

tell application "Mailsmith"
        set m to make new message window
        set use signature of m to false
end tell
set sig to ""
try
        tell application ¬
        "http://www.swanandmokashi.com/HomePage/WebServices/QuoteOfTheDay.asmx"
                set returnValue to call soap ¬
                        {method name: ¬
                                "GetQuote", ¬
                        method namespace uri: ¬
                                "http://www.swanandmokashi.com", ¬
                        SOAPAction: ¬
                                "http://www.swanandmokashi.com/GetQuote"}
        end tell
        set sig to return & "-----------------------------------"
        set sig to sig & return & quoteoftheday of returnValue
        set sig to sig & return & "           -- "
        set sig to sig & author of returnValue & return
end try
tell application "Mailsmith"
        copy sig to after text of m
        select insertion point before character 1 of m
end tell

In this example, the call soap command takes a record of three items. That’s the material in curly braces; an AppleScript record is a list of paired names and values (Chapter 13). To learn what the values of those items needed to be, I consulted the web service’s documentation (see http://www.swanandmokashi.com/HomePage/WebServices/QuoteOfTheDay.asmx?op=GetQuote). The result arrives into the variable returnValue as another record, consisting of two items called quoteoftheday and author. The script extracts the two items, formats them into a nice string, and appends them to the email message back in Mailsmith. If anything goes wrong, it goes wrong inside a try block, so there’s no harm done: an empty string is appended to the message. The selection point is then set to the start of the message, ready for me to type. Figure 1-3 shows the result of running the script.

Mail message with a quotation supplied by a web service

Figure 1-3. Mail message with a quotation supplied by a web service

For another example of combining specialties, see Section 2.3. In the example there, the tasks of database and mail client are assigned to two different applications, FileMaker Pro and Mailsmith, so that each program can do what it does best; the two applications are able to share data, so that for purposes of a particular task—sending an email message to someone in the database—they cooperate and act as one.

Get AppleScript: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.