CLOVER🍀

That was when it all began.

Leiningenを使って、Clojureスクリプトを実行する

Mavenなどを使うと、プロジェクトを用意して…とけっこう面倒な感じがするのですが、スクリプトとして実行が可能な言語なら、依存関係とかを解決しつつ使えると嬉しい!というのがあると思います。

Groovy、Scalaでは、一応そういう仕組みがあります。

Groovy(Grape)
http://groovy.codehaus.org/Grape

Scala(sbt-scalas)
http://www.scala-sbt.org/release/docs/Detailed-Topics/Scripts

で、ClojureといえばLeiningenですが、これを使って同じようなことをする方法を探してみました。

選択肢としては、以下のいずれかのLeiningenプラグインを使うとよいみたいです。

lein-exec
https://github.com/kumarshantanu/lein-exec

lein-oneoff
https://github.com/mtyaka/lein-oneoff

今回は、lein-execを使ってみることにしました。

インストールなどなど

$HOME/.lein/profiles.cljに、以下の内容を記述します。

{:user {:plugins [[lein-exec "0.3.1"]]}}

上位のバージョンを取るようにしたい場合は、こんな感じ。

{:user {:plugins [[lein-exec "[0.3.1,)"]]}}

ファイルが存在しない場合は、ファイル自体を作成してください。

あとは、これを利用した実行対象となるスクリプトを作成します。まずは、以下のようなuseを加えます。

(use '[leiningen.exec :only (deps)])

続いて、依存関係の定義をdeps関数を使って定義していきます。

(deps '[[ring/ring-core "1.0.0"]
        [ring/ring-jetty-adapter "1.0.0"]])
(deps '[[foo/bar "1.2.3"]]
      :repositories {"myrepo" "http://mycorp.com/repositories/"})

リポジトリの追加が必要な場合は、:repositoriesキーワードを使用します。

実行してみる

それでは、動作確認用のスクリプトを作成します。今回はこんなのを用意しました。
exec-script.clj

(use '[leiningen.exec :only (deps)])
(deps '[[org.apache.commons/commons-lang3 "3.1"]
        [org.seasar.container/s2-framework "2.4.46"]]
      :repositories {"The Seasar Foundation Maven2 Repository" "http://maven.seasar.org/maven2"})

(ns exec.core
  (:import (org.apache.commons.lang3 StringUtils)
           (org.seasar.framework.util StringUtil)))

(println (StringUtils/join '("Hello" "Leiningen!!"), ", "))
(println (StringUtil/equals "str" "str"))

(println (str "Hello "
              (if (> (count *command-line-args*) 1)
                (second *command-line-args*)
                "World")))

実行は、「lein exec スクリプト名」で行います。

$ lein exec exec-script.clj 
Retrieving lein-exec/lein-exec/0.3.1/lein-exec-0.3.1.pom from clojars
Retrieving lein-exec/lein-exec/0.3.1/lein-exec-0.3.1.jar from clojars
Hello, Leiningen!!
true
Hello World

初回は、プラグインのダウンロードが行われます。

2回目以降は、ダウンロードは発生しません。

$ lein exec exec-script.clj Clojure
Hello, Leiningen!!
true
Hello Clojure

起動引数は、「*command-line-args*」で受けてくださいね。

なお、スクリプトの実行に使用されるClojureのバージョンですが、どうもLeiningen(というか、leiningen-core?)が依存しているClojureのバージョンに引っ張られるようです。deps関数を使った依存関係の定義にClojureを加えても、華麗に無視されます。

その他、leinコマンドのexecタスクとして実行するのではなく、lein-execとしてスクリプトを用意する方法も紹介されていますが、「lein exec」のラッパーにすぎないので割愛…。

lein-exec
https://raw.github.com/kumarshantanu/lein-exec/master/lein-exec

導入したい場合は、ダウンロードしてPATHを通して実行権限を付与してって感じですね。

eval

小ネタ。「-e」スイッチで、eval的なことができます。

$ lein exec -e '(println "Hello World")'
Hello World

プロジェクト内のスクリプトを実行する

もう少しユニークな機能として、プロジェクト内に定義したスクリプトを実行することもできます。

まずは、適当なプロジェクトを作成。

$ lein new app exec-project
Generating a project called exec-project based on the 'app' template.

project.cljを修正。

(defproject exec-project "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.apache.commons/commons-lang3 "3.1"]
                 [org.seasar.container/s2-framework "2.4.46"]]
  :main exec-project.core
  :repositories {"The Seasar Foundation Maven2 Repository" "http://maven.seasar.org/maven2"})

前のスクリプトを、ちょっと名前空間だけ変えてsrcディレクトリに放り込みます。
src/exec_project/script.clj

(ns exec-project.script
(:import (org.apache.commons.lang3 StringUtils)
           (org.seasar.framework.util StringUtil)))

(println (StringUtils/join '("Hello" "Leiningen!!"), ", "))
(println (StringUtil/equals "str" "str"))

(println (str "Hello "
              (if (> (count *command-line-args*) 1)
                (second *command-line-args*)
                "World")))

このスクリプトを、「lein exec -p」で実行します。

$ lein exec -p src/exec_project/script.clj Clojure
Hello, Leiningen!!
true
Hello Clojure

project.cljで定義した依存関係を解決しつつ、スクリプトを実行することができました。

ちなみに、これをスクリプト化したlein-exec-pというものも存在します。

lein-exec-p
https://raw.github.com/kumarshantanu/lein-exec/master/lein-exec-p

やっていることは同じなので、こちらも割愛。

こんな感じで。