What is Google Apps Script?
Google Apps Script (以下 Apps Script) は、Google の server 上での serverless な JavaScript 實行環境です。serverless な JavaScript 實行環境ですので Cloud Function の仲間です。函數を起動できる event の種類が限られている、また SLA (Service Level Agreement) が不明である為、Cloud Function のやうに大規模な servise を作る事は、できません。代はりに、Docs や Gmail 等[など]の Google Apps と連携するやう簡單に設定できる為、この意味で VBA (Visual Basic for Applications) の仲間でもあります。更に Google Apps が使へる環境であれば無料です。その代はり一日当りの實效囘數等が制限されてゐます。
Apps Script と云へば Google Apps 向けの VBA である、と思はれる向きもあり、Gmail と Spreadsheet を同時に見たり更新したりできると云ふ使ひ途もあるのですが、cron の代はりとして Google Apps に全く觸らない函數も作れます。であるので、Google Apps account で使へる serverless FaaS (Function as a Service) だと見るのがよいです。今の所書ける言語は JavaScript だけです。
Google Apps の web site 上に Apps Script の editor があるのですが、ただの text editor であり、version 管理も難しく、複數人で編集したり test を書いたり library を使ったりする事ができません。programmer ですので、日々使ってゐる editor (Emacs) で書き、日々使ってゐる version 管理 system (Git) で管理し、人間にも讀める code を書きたいものです。これをお膳立てしてくれるのが clasp です。
Apps Script 用の CLI tool であり、local の script を upload する、Google Apps 上の script を download する、Google Apps 上の script を實行する、Google Apps 上の現在の version に tag を打つ、Apps Script が吐いた log を見る、等の事ができます。まともな開發に要る事はできさうです。人間でゐませう (發狂せずとも濟みさうと云ふ意)。
What is ClojureScript?
ClojureScript (a.k.a. cljs) は Clojure の實裝の一つです。Clojure と云へば JVM 上の Lisp の一つとして生まれましたが、その後.NET CLR 上でも實裝され、JavaScript への transpiler も作られました。この二つは Clojure と同じ或いは近しい community で作られてゐます。他にも Erlang VM 上で動くclojerlや、C に transpile するclojurec (これの開發は停まってゐます) もあります。廣く實行環境を見れば、Android 上で動かすlein-droid (これも開發が停まってゐます)、React Native で動かすRe-Natal、Unity で動くArcadia等があり、更には Graal で JVM 用の Clojure を native code に落とせるので (Graal が MySQL に搭載されたらそこでも動く) (clojure-clr を Mono LLVM に流すと云ふ手も在ります)、どこででも動きますね (Ethereum の contract 自體を書く方法は見附からないので EthereumVM では動きませんね)。Cloduinoで Arduino も弄れるみたいです。珍しい所ではQuilから Processing で映像を描けたり、Overtoneから SuperCollider で音樂を生成したりできます。
Clojure は immutable な data を基礎とし、vector や map 等の data 構造 & 使ひ易い記法を持った Lisp です。Lisp である事が我々にとってはまづ一つの利点ですし、data が immutable ですから、program を抽象化し易く、concurrency を簡單に扱へます。ClojureScript は Clojure から JavaScript への transpiler です。JVM の API は使へませんが、Clojure の core API の大半が使へますし、JavaScript を簡單に呼び出せます。無論 NPM も使へます。
Isomorphic と云ふ言葉は懐かしいですが、懐かしくなったからといって重要で無くなった譯ではありません。Reagent 等便利な Web frontend framework が在ります。
Develop Apps Script in ClojureScript.
以下の二つを install します。
- cljs : Mac であれば
brew install clojurescript
で ClojureScript を、或いはbrew install leiningen
で Leiningen を入れて project 毎に install します。 - clasp : NPM を入れ、
npm install -g @google/clasp
で入れます。
私は Leiningen で project を作ったので、こんなproject.clj
ができます。
(defproject apple-is-dead "0.1.0" :description "Appleの障害情報を監視し通知する" :url "〜〜〜" :license {:name "Do What The F*ck You Want To Public License" :url "http://sam.zoy.org/wtfpl/COPYING"} :plugins [[jonase/eastwood "0.3.3"] [lein-ancient "0.6.15"] [lein-cljfmt "0.5.7"] [lein-cljsbuild "1.1.7"]] :dependencies [[org.clojure/clojure "1.9.0"] [org.clojure/clojurescript "1.10.439"]] :hooks [leiningen.cljsbuild] :aliases {"lint" ["run", "-m" "apple-is-dead.tasks.lint/go"] "release" ["run", "-m" "apple-is-dead.tasks.release/go"]} :cljsbuild {:builds [{:source-paths ["src"] :compiler {:main apple-is-dead.main :output-to "release/main.js" :output-dir "target" :optimizations :advanced :pretty-print false :foreign-libs [{:file "src/entrypoint.js" :provides ["apple-is-dead.entrypoint"]}] :externs ["src/extern.js"]}}]})
lein-cljsbuild が、Leiningen に ClojureScript を build するやり方を教えます。
:hooks [leiningen.cljsbuild] :cljsbuild {:builds 〜〜〜
と hook すると、lein compile
で ClojureScript の file である*.cljs
を探して JavaScript に transpile します。
:aliases
の lint では
prettier --write src/*.js
lein cljfmt fix
lein ancient check
lein eastwood
のやうな事をさせてゐ、release では
lein compile clasp push
のやうな事をさせてゐます。
作った ClojureScript の project 内でclasp create 〜〜〜
を叩き、Apps Script project を作ります。この時に作られる file 達が大切なので、これらを cp しておき、これらが在る diractory で以後clasp
command を叩きます。
あとは ClojureScript と Apps Script の referense を御覽ください。Apps Script では log を見たり error を mail に通知する事ができますので、そこを適切に作ると運用が樂になります。error を catch する時は(js/console.error err)
で log を吐く、實行の重要な step 毎に(js/console.info message)
で log を吐く、catch すべきでない error を catch しない、retry されてもよいやうに冪等にする等、batch 處理を書く時と似た事を注意する筈です。
:foreign-libs
と:externs
は載せておきます。
:foreign-libs [{:file "src/entrypoint.js" :provides ["apple-is-dead.entrypoint"]}] :externs ["src/extern.js"]
:foreign-libs
は JavaScript から ClojureScript を呼ぶ時に要ります。Apps Script は書かれたものが JavaScript であらうと前提して呼び出しますから、JavaScript 側から呼べる interface を開けておかなければなりません。ここに指定した file に JavaScript を書いておくと、それは Closure Compiler (ClojureScript が Closure Compiler を呼び出して、code を圧縮したり dead code を消したりします) で變換されずに、出力される JavaScript にそのまま載ります。
// entrypoint.js function main(evt) { apple_is_dead.main.main(); }
; main.cljs (defn ^:export main [] (doseq [service ((apple.get-status) :services)] (check-apple-service service)))
これで Apps Script はmain()
を呼び出せ、それによりapple_is_dead.main.main()
、すなはち(apple-is-dead.main.main)
を實行できます。
:externs
は、Closure Compiler が圧縮してはいけない變數名を列擧する file です。これらは外の環境に global に定義されてゐるから、このままの名前で呼び出さなければならない、例えば JavaScript の core に定義された module 達ですが、これらはどうやら Closure Compiler が既に知ってゐます。しかし Closure Compiler は Apps Script の事は知りません。それでも Closure Compiler はたいていうまくやりますが、一部見逃してしまふやうです。私の場合はCacheService
の事は教えてやらねばなりませんでした (他にも色々呼び出したのですが、これ以外はうまく、圧縮せずにゐてくれました) (Closure Script の實裝を読んでゐないのが惡いのですが)。
// extern.js var CacheService = {}; CacheService.getScriptCache = function () {};
これで ClojureScript で Apps Script を開發できると思ひます。