OSX automator in clojurescript

October 30, 2014

I don’t quite remember how I found the post, but anyways I landed upon this blog by @mbcrump about how automator now supports Javascript as a language. I don’t even remember how I got the idea of playing with clojurescript to get it to automate tasks, but definitively read it somewhere. Update: Now I was reminded: @borkdude retweeted

(let [mail (new js/Application "com.apple.mail") inbox (.. mail -inbox -messages) first-message (aget inbox 0)](.subject first-message)) 2/2

— Daniel Szmulewicz (@danielszmu) October 29, 2014

So how did I go about this?

Starting a new Clojurescript project

This is always a pain, since I hardly ever do it. I took a chance on David Nolens mies plugin for leinigen

$ lein new mies applescript

This gives you a new project called applescript with a minimal directory-structure.

Transforming js to clojurescript

Given the js-code from @mbcrump’s post:

var Mail = Application('Mail');

content = "This is my message to you";

msg = Mail.OutgoingMessage({
    subject: "Thanks",
    content: content,
    visible: true
});

Mail.outgoingMessages.push(msg);

Mail.activate();

it was quite easy to translate it to clojurescript

(ns applescript.core)

(def mail (js/Application "Mail"))
(def content "This is my message to you!")

;; Note the use of the #js reader macro to make a
;; clojure map into a js-map
(def msg (.OutgoingMessage mail #js{:subject "Thanks"
                                    :content content
                                    :visible true}))

;; Needed aget to achieve
;; mail.outgoingMessages.push(msg)
(.push (aget mail "outgoingMessages") msg)
(.activate mail)
$ lein cljsbuild once

builds it, but bob is not your uncle yet, since it will not run.

Making the stuff run

According to the post, it should only be a matter of running

$ osascript -l JavaScript file.js

to execute the script. Problem was, how to achieve this, telling leiningen to include all neccessary js into a file that osascript could run?

The solution

was in project.clj. changing

  :cljsbuild {
    :builds [{:id "applescript"
              :source-paths ["src"]
              :compiler {
                :output-to "applescript.js"
                :output-dir "out"
                :optimizations :none ;; change this to :simple
                :source-map true}}]}) ;; remove this

to

  :cljsbuild {
    :builds [{:id "applescript"
              :source-paths ["src"]
              :compiler {
                :output-to "applescript.js"
                :output-dir "out"
                :optimizations :simple}}]})

gave me one file, applescript.js, containing everything, which I could run as

osascript -l JavaScript applescript.js

#Bonus Just for fun, having a look at applescript.js, all the way at the bottom, you find:

var applescript = {core:{}};
applescript.core.mail = Application("Mail");
applescript.core.content = "This is my message to you!";
applescript.core.msg = applescript.core.mail.OutgoingMessage({visible:!0, content:applescript.core.content, subject:"Thanks"});
applescript.core.mail.outgoingMessages.push(applescript.core.msg);
applescript.core.mail.activate();

Which translates pretty nicely to the original js-code.