leiningen でプロジェクトのテンプレートを作ってみる
この記事は、Clojure Adven Calendar 2013 12/07日の記事です。
今日は、leiningen (github) を使って、自分の独自のプロジェクトのテンプレート(いわゆる オレオレテンプレート )を作る話をします
(leiningen のテンプレート作成に関するドキュメント(ここ) を見れば終わり、という話もありますが、自習メモの意味でもまとめておきたいと思います)。
プロジェクトのテンプレートについて
leiningen
で新規にプロジェクトを作成する場合、
$ lein new my-project
というように実行します。実行すると、my-project
というディレクトリが作成され、その配下に以下のように各種ファイルが作成されます。また、lein new
コマンドには、 テンプレート を指定することができます。
$ lein new $TEMPLATE_NAME my-project
上記 $TEMPLATE_NAME
のところにテンプレート名を指定すると、テンプレートに従った形で project.clj
や初期設定済みのソースファイル等がひな形として作成されます。テンプレートはデフォルトで、default
、plugin
、template
、app
、の4つが利用できますが、ローカルリポジトリもしくは maven、clojars といったリポジトリにあれば、それらのテンプレートも利用できます。有名なところでは、heroku
向けのテンプレートがあります(試しに lein new heroku heroku-proj
とやってみれば、heroku へデプロイできる様に準備された heroku-proj というプロジェクトが作成されます)。
独自テンプレートの作成
今回はオレオレテンプレートを作るのが目的なので、以下のようにしました。
$ lein new template oreore
Generating fresh 'lein new' template project.
$
作成されたものを見てみます。
- ディレクトリの構造
oreore/
| .gitignore
| LICENSE
| README.md
| project.clj
+- src/
+- leiningen/
+ new/
| oreore.clj
+- oreore/
| foo.clj
- project.clj
(defproject oreore/lein-template "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"}
:eval-in-leiningen true)
このproject.clj
は、 このプロジェクトが leiningen のプラグインであることを宣言するためのもの 、であることに注意してください。 oreore というテンプレートから作成されたプロジェクトの project.clj
のひな形ではありません。
注意点としては、defproject
のバージョン記述に気をつける必要があります。-SNAPSHOT
を付けると、後でローカルリポジトリにインストールするときに参照するのが面倒なので、今回は-SNAPSHOT
を消す、という方針で行きます。
- src/leiningen/new/oreore.clj
(ns leiningen.new.oreore
(:require [leiningen.new.templates :refer [renderer name-to-path ->files]]
[leiningen.core.main :as main]))
(def render (renderer "oreore"))
(defn oreore
"FIXME: write documentation"
[name]
(let [data {:name name
:sanitized (name-to-path name)}]
(main/info "Generating fresh 'lein new' oreore project.")
(->files data
["src/{{sanitized}}/foo.clj" (render "foo.clj" data)])))
これが lein new oreore
コマンドで実行される処理の本体になります。(defn oreore [name] ...)
が実際にテンプレート展開に使われる関数になります。パラメータname
には、lein new oreore
に続く引数、すなわち新規のプロジェクト名が設定されます。
このソースはfoo.clj
というファイル1つのみを生成するシンプルな例となっています。処理の要は->files
関数です。この関数の第1パラメータには、mustache
形式で指定された文字列テンプレートのキーと値の対応付けを行う map を指定し、第2パラメータ以降で具体的に生成するファイル(群)を指定します。サブディレクトリについては自動で作成されます。
Clojure では、名前空間に -
が含まれていると、実際に配置されるディレクトリもしくはソースファイル名は _
に置き換える必要がありますが、name-to-path
関数がその処理を行っています。
上記はシンプルすぎる例ですが、より具体的なサンプルは、デフォルトのテンプレート(ここ) を見るとよいでしょう。
- src/leiningen/new/oreore/foo.clj
これは、oreore.clj
ファイルから->files
関数で展開される元となるテンプレートファイルです。
(def {{name}} :foo)
namespace
や require
の指定とか、デフォルトで用意しておくべき関数(単独アプリケーションなら-main
とか、Compojure をつかったWebアプリケーションならdefroute
とか)を記述しておくと良いでしょう。
最終的には、以下のような感じにしました。
- ディレクトリの構造
oreore/
| .gitignore
| LICENSE
| README.md
| project.clj
+- src/
+- leiningen/
+ new/
| oreore.clj (1)
+- oreore/
| .gitignore
| README.md
| core.clj (2) <-- 元々あった foo.clj の代わりに作成
| project.clj (3)
- (1) src/leiningen/new/oreore.clj
(ns leiningen.new.oreore
(:require [leiningen.new.templates :refer [renderer name-to-path ->files]]
[leiningen.core.main :as main]))
(defn oreore
"おれおれテンプレート"
[name]
(let [render (renderer "oreore")
data {:name name
:sanitized (name-to-path name)}]
(main/info "Generating fresh 'lein new' 俺俺 project.")
(->files data
["src/{{sanitized}}/core.clj" (render "core.clj" data)]
["project.clj" (render "project.clj" data)]
["README.md" (render "README.md" data)]
[".gitignore" (render ".gitignore" data)])))
- (2) src/leiningen/new/oreore/core.clj
コマンドラインツールとして作る目的で、tools.cli を使うコードを入れています。また、後に STANDALONE jar を作るつもりで :gen-class
も入れています。
(ns {{name}}.core
(:gen-class)
(:require [clojure.string :as str]
[clojure.tools.cli :refer [cli]]))
(defn -main
[& args]
"{{name}} のメイン処理"
(let [[opts args banner]
(cli args
"FIXME: help banner message"
["-h" "--help" "Show Help" :default false :flag true]
;; additional options
)]
(when (:help opts)
(println banner)
(System/exit 0))
(println "Hello, {{name}}")))
- (3) src/leiningen/new/oreore/project.clj
(defproject {{name}} "0.1.0-SNAPSHOT"
:description "オレオレプロジェクト({{name}})"
:license "Eclipse Public License 1.0"
:url "http://example.com/FIXME"
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/tools.cli "0.2.4"]]
:aot :all
:main {{name}}.core)
コマンドラインからの起動を考えて、:main
の指定と :aot :all
を入れています。
独自テンプレートのインストール
ファイルの準備ができたら、oreore
プロジェクトのところで、lein install
とすると、ローカルリポジトリに oreore
テンプレートがインストールされます。
$ lein install
Created /Users/masao/oreore/target/lein-template-0.1.0.jar
Wrote /Users/masao/oreore/pom.xml
$
動かしてみる
$ lein new oreore fuga
Generating fresh 'lein new' 俺俺 project.
$ cd fuga
$ lein run
Retrieving org/clojure/tools.cli/0.2.4/tools.cli-0.2.4.pom from central
Retrieving org/clojure/tools.cli/0.2.4/tools.cli-0.2.4.jar from central
Compiling fuga.core
Hello, fuga
$ lein uberjar
Created /Users/masao/fuga/target/fuga-0.1.0-SNAPSHOT.jar
Created /Users/masao/fuga/target/fuga-0.1.0-SNAPSHOT-standalone.jar
$ java -jar target/fuga-0.1.0-SNAPSHOT-standalone.jar
Hello, fuga
$
上手く行ったようです。
所感など
テンプレート化すると、基本的にコピペ開発になってしまうのでよろしくない面も多々あるのですが、「ちょっとした日常の業務」で Clojure を使いたい、というときには、それなりの俺俺テンプレートを作っておくと、役立つ場面があると思っています。
本日の記事はこんなところで。
明日は、puriketu99 さんの記事になります。よろしくお願いします。