$ clj
Clojure 1.11.2
user=>
Clojure は、以下のためのコマンドラインツールを提供します。
インタラクティブ REPL (Read-Eval-Print Loop) の実行
Clojure プログラムの実行
Clojure 式の評価
上記のすべてのシナリオで、他の Clojure および Java ライブラリ(依存関係または「deps」)を使用したい場合があります。これらは、ローカルで作成しているライブラリ、git(例:GitHub)にあるプロジェクト、または一般的には、Maven エコシステムで利用可能であり、Maven Central や Clojars などの中央リポジトリでホストされているライブラリである可能性があります。
すべての場合において、ライブラリを使用するには、
使用したいライブラリを、名前やバージョンなどの側面を指定して指定する必要があります。
git または Maven リポジトリから(一度だけ)ローカルマシンに取得する必要があります。
REPL またはプログラムの実行中に Clojure が見つけられるように、JVM クラスパスで使用できるようにする必要があります。
Clojure ツールは、(a) の構文とファイル (deps.edn
) を指定し、それに基づいて (b) と (c) を自動的に処理します。
ツールのインストール方法の詳細については、入門を参照してください。ここでは、入門方法について説明します。完全なリファレンスについては、Clojure CLI と deps.edn を参照してください。バージョン情報については、変更履歴を参照してください。
ツールをダウンロードしてインストールしたら、clj
ツールを実行して REPL を起動できます。
$ clj
Clojure 1.11.2
user=>
REPL で Clojure 式を入力して Enter キーを押すと、式が評価されます。REPL を終了するには、Control-D を押します。
user=> (+ 2 3) # press the `enter` key after typing the expression to evaluate it
5 # result of expression
user=> # type Ctrl-D here to exit the REPL (does not print)
$
必要になる可能性のあるほぼすべての機能へのアクセスを提供する、多くの Clojure および Java ライブラリが利用可能です。たとえば、日付と時刻を操作するための一般的な Clojure ライブラリ clojure.java-time を考えてみましょう。
このライブラリを使用するには、ツールがダウンロードされ、クラスパスに追加されるように、依存関係として宣言する必要があります。ほとんどのプロジェクトの README に、使用する名前とバージョンが示されています。deps.edn
ファイルを作成して、依存関係を宣言します。
{:deps
{clojure.java-time/clojure.java-time {:mvn/version "1.1.0"}}}
または、バージョンがわからない場合は、find-versions
ツールを使用すると、利用可能なすべての座標がソートされた順序で一覧表示されます。
$ clj -X:deps find-versions :lib clojure.java-time/clojure.java-time
...omitted
{:mvn/version "1.0.0"}
{:mvn/version "1.1.0"}
clj
ツールを使用して REPL を再起動します。
$ clj
Downloading: clojure/java-time/clojure.java-time/1.1.0/clojure.java-time-1.1.0.pom from clojars
Downloading: clojure/java-time/clojure.java-time/1.1.0/clojure.java-time-1.1.0.jar from clojars
Clojure 1.11.2
user=> (require '[java-time.api :as t])
nil
user=> (str (t/instant))
"2022-10-07T16:06:50.067221Z"
依存関係を初めて使用するときに、ライブラリがダウンロードされているというメッセージが表示されます。ファイルがダウンロードされると (通常は ~/.m2
または ~/.gitlibs
に)、今後再利用されます。同じプロセスを使用して、他のライブラリを deps.edn
ファイルに追加し、Clojure または Java ライブラリを調べることができます。
すぐに、これらのライブラリを使用する独自のコードを構築して保存したくなるでしょう。新しいディレクトリを作成し、この deps.edn
をコピーします。
$ mkdir hello-world
$ cp deps.edn hello-world
$ cd hello-world
$ mkdir src
デフォルトでは、clj
ツールは src
ディレクトリにあるソースファイルを検索します。src/hello.clj
を作成します。
(ns hello
(:require [java-time.api :as t]))
(defn time-str
"Returns a string representation of a datetime in the local time zone."
[instant]
(t/format
(t/with-zone (t/formatter "hh:mm a") (t/zone-id))
instant))
(defn run [opts]
(println "Hello world, the time is" (time-str (t/instant))))
このプログラムには、-X
を使用して clj
によって実行できるエントリ関数 run
があります。
$ clj -X hello/run
Hello world, the time is 12:19 PM
このアプリケーションの一部をライブラリに移動することに決めるかもしれません。clj
ツールは、ローカルディスクにのみ存在するプロジェクトをサポートするためにローカル座標を使用します。このアプリケーションの java-time 部分を、並列ディレクトリ time-lib 内のライブラリに抽出しましょう。最終的な構造は次のようになります。
├── time-lib │ ├── deps.edn │ └── src │ └── hello_time.clj └── hello-world ├── deps.edn └── src └── hello.clj
time-lib の下に、すでに持っている deps.edn
ファイルのコピーを使用し、ファイル src/hello_time.clj
を作成します。
(ns hello-time
(:require [java-time.api :as t]))
(defn now
"Returns the current datetime"
[]
(t/instant))
(defn time-str
"Returns a string representation of a datetime in the local time zone."
[instant]
(t/format
(t/with-zone (t/formatter "hh:mm a") (t/zone-id))
instant))
hello-world/src/hello.clj
のアプリケーションを更新して、代わりにライブラリを使用します。
(ns hello
(:require [hello-time :as ht]))
(defn run [opts]
(println "Hello world, the time is" (ht/time-str (ht/now))))
hello-world/deps.edn
を変更して、time-lib ライブラリのルートディレクトリを参照するローカル座標を使用します (自分のマシンのパスを必ず更新してください)。
{:deps
{time-lib/time-lib {:local/root "../time-lib"}}}
次に、アプリケーションを実行して、hello-world ディレクトリからすべてをテストできます。
$ clj -X hello/run
Hello world, the time is 12:22 PM
そのライブラリを他の人と共有できれば素晴らしいでしょう。プロジェクトをパブリックまたはプライベート git リポジトリにプッシュし、他の人が git 依存関係座標で使用できるようにすることで、これを実現できます。
まず、time-lib の git ライブラリを作成します。
cd ../time-lib
git init
git add deps.edn src
git commit -m 'init'
次に、パブリック git リポジトリホスト (GitHub など) に移動し、この git リポジトリを作成および公開するための指示に従います。
このリリースに意味のあるバージョンを持たせるために、タグを付けます。
git tag -a 'v0.0.1' -m 'initial release'
git push --tags
最後に、git 依存関係を代わりに使用するようにアプリを変更します。次の情報を収集する必要があります。
リポジトリライブラリ - Clojure CLI は、GitHub URL https://github.com/yourname/time-lib.git
に対して io.github.yourname/time-lib
のようなライブラリ名を使用する場合、URL を指定する必要がないという規則を使用します。
タグ - 上記で作成した v0.0.1
です。
sha - タグの短い sha です。リポジトリがローカルにある場合は git rev-parse --short v0.0.1^{commit}
で、リモートにある場合は git ls-remote https://github.com/yourname/time-lib.git v0.0.1
で見つけることができます。GitHub リポジトリを使用してタグとそのバッキングコミットを調べることもできます。
hello-world/deps.edn
を更新して、git 座標を代わりに使用します。
{:deps
{io.github.yourname/time-lib {:git/tag "v0.0.1" :git/sha "4c4a34d"}}}
これで、(共有) git リポジトリライブラリを使用して、アプリを再度実行できます。最初に実行すると、clj
がリポジトリとコミット作業ツリーをダウンロードしてキャッシュするときに、コンソールに追加のメッセージが表示されます。
$ clj -X hello/run
Cloning: https://github.com/yourname/time-lib
Checking out: https://github.com/yourname/time-lib at 4c4a34d
Hello world, the time is 02:10 PM
これで、友達も time-lib
を使用できます!
プログラムがより複雑になるにつれて、標準のクラスパスのバリエーションを作成する必要がある場合があります。Clojure ツールは、エイリアスを使用してクラスパスの変更をサポートします。これは、対応するエイリアスが提供された場合にのみ使用される deps ファイルの一部です。実行できることのいくつかを紹介します。
通常、プロジェクトのクラスパスには、デフォルトではプロジェクトのソースのみが含まれ、テストソースは含まれません。クラスパスの構築のクラスパス作成ステップで、プライマリクラスパスへの変更として追加のパスを追加できます。そのためには、追加の相対ソースパス "test"
を含むエイリアス :test
を追加します。
{:deps
{org.clojure/core.async {:mvn/version "1.3.610"}}
:aliases
{:test {:extra-paths ["test"]}}}
そのクラスパスの変更を適用し、clj -A:test -Spath
を呼び出して変更されたクラスパスを調べます。
$ clj -A:test -Spath
test:
src:
/Users/me/.m2/repository/org/clojure/clojure/1.11.2/clojure-1.11.2.jar:
... same as before (split here for readability)
これで、テストディレクトリがクラスパスに含まれるようになりました。
前のセクションの :test
エイリアスを拡張して、すべての clojure.test テストを実行するための cognitect-labs テストランナーを含めることができます。
:test
エイリアスを拡張します。
{:deps
{org.clojure/core.async {:mvn/version "1.3.610"}}
:aliases
{:test {:extra-paths ["test"]
:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
:main-opts ["-m" "cognitect.test-runner"]
:exec-fn cognitect.test-runner.api/test}}}
次に、デフォルト設定を使用してテストランナーを実行します (test/ ディレクトリの下の -test 名前空間のすべてのテストを実行します)。
clj -X:test
deps.edn
ファイルのエイリアスを使用して、クラスパスに影響するオプションの依存関係を追加することもできます。
{:aliases
{:bench {:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}}}
ここでは、:bench
エイリアスを使用して、追加の依存関係、つまり criterium ベンチマークライブラリを追加しています。
:bench
エイリアスを追加して、依存関係の解決を変更することで、この依存関係をクラスパスに追加できます。clj -A:bench
。
既存の deps.edn
ファイルに追加したり、ファイルを作成したりせずにライブラリを試してみるのに役立つ場合があります。
$ clojure -Sdeps '{:deps {org.clojure/core.async {:mvn/version "1.5.648"}}}'
Clojure 1.11.2
user=> (require '[clojure.core.async :as a])
nil
エスケープ規則のため、構成データを一重引用符で囲むのが最善であることに注意してください。
一部の依存関係では、クラスパスで使用する前に準備ステップが必要になります。これらのライブラリは、deps.edn
でこの必要性を明記する必要があります。
{:paths ["src" "target/classes"]
:deps/prep-lib {:alias :build
:fn compile
:ensure "target/classes"}}
トップレベルキー :deps/prep-lib
を含めると、tools.deps クラスパス構築に、このライブラリを準備するために追加が必要であり、:build
エイリアスで compile
関数を呼び出すことで実行できることが通知されます。準備ステップが完了したら、パス "target/classes"
を作成する必要があります。これは完了をチェックできます。
他のソースベースのライブラリ (git またはローカルの場合があります) と同じように、このライブラリに依存します。
{:deps {my/lib {:local/root "../needs-prep"}}}
次に、そのライブラリをクラスパスに含めようとすると、エラーが表示されます。
$ clj
Error building classpath. The following libs must be prepared before use: [my/lib]
次に、このコマンドを使用して CLI に準備するように指示できます (これは、特定のライブラリバージョンに対する 1 回限りのアクションです)。
$ clj -X:deps prep
Prepping io.github.puredanger/cool-lib in /Users/me/demo/needs-prep
$ clj
Clojure 1.11.2
user=>
複数のエイリアスを組み合わせて使用できます。たとえば、この deps.edn
ファイルは、古い core.async バージョンの使用を強制する :old-async
と、追加の依存関係を追加する :bench
の 2 つのエイリアスを定義します。
{:deps
{org.clojure/core.async {:mvn/version "0.3.465"}}
:aliases
{:old-async {:override-deps {org.clojure/core.async {:mvn/version "0.3.426"}}}
:bench {:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}}}
次のように両方のエイリアスをアクティブにします。clj -A:bench:old-async
。
データベースドライバのjarなど、Mavenリポジトリに存在しないディスク上のjarを直接参照する必要がある場合があります。
ローカルjarの依存関係は、ディレクトリではなくjarファイルを直接指すローカル座標で指定します。
{:deps
{db/driver {:local/root "/path/to/db/driver.jar"}}}
gen-classまたはgen-interfaceを使用する場合、Javaクラスを生成するために、Clojureソースを事前にコンパイルする必要があります。
これはcompile
を呼び出すことで実行できます。コンパイルされたクラスファイルのデフォルトの出力先はclasses/
であり、作成してクラスパスに追加する必要があります。
$ mkdir classes
deps.edn
を編集して、パスに"classes"
を追加します。
{:paths ["src" "classes"]}
src/my_class.clj
でgen-classを使用してクラスを宣言します。
(ns my-class)
(gen-class
:name my_class.MyClass
:methods [[hello [] String]])
(defn -hello [this]
"Hello, World!")
次に、別のソースファイルsrc/hello.clj
で:import
を使用してクラスを参照できます。名前空間も:require
に追加されていることに注意してください。これにより、コンパイル時に依存するすべての名前空間が自動的に検出されてコンパイルされます。
(ns hello
(:require [my-class])
(:import (my_class MyClass)))
(defn -main [& args]
(let [inst (MyClass.)]
(println (.hello inst))))
コンパイルはREPLで実行するか、スクリプトを実行して行うことができます。
$ clj -M -e "(compile 'hello)"
その後、hello名前空間を実行します。
$ clj -M -m hello
Hello, World!
完全なリファレンスについては、コンパイルとクラス生成を参照してください。
Clojureは、ソケットサーバーの実行、特にそれらを使用してリモートREPLをホストするための組み込みサポートを提供しています。
ソケットサーバーREPLを構成するには、次の基本構成をdeps.edn
に追加します。
{:aliases
{:repl-server
{:exec-fn clojure.core.server/start-server
:exec-args {:name "repl-server"
:port 5555
:accept clojure.core.server/repl
:server-daemon false}}}}
そして、エイリアスで呼び出すことでサーバーを起動します。
clojure -X:repl-server
必要に応じて、コマンドラインでデフォルトのパラメータをオーバーライドしたり(または追加のオプションを追加したり)することもできます。
clojure -X:repl-server :port 51234
別のターミナルから接続するには、netcatを使用できます。
nc localhost 51234
user=> (+ 1 1)
2
REPLを終了するにはCtrl-D、サーバーを終了するにはCtrl-Cを使用します。
組み込みの:deps
エイリアスには、プロジェクトで使用される推移的な依存関係の完全なセット(およびそのライセンス)を調べるのに役立つツールがいくつかあります。
クラスパスに含まれるすべての依存関係の完全なセットをリストするには、clj -X:deps list
を使用します。たとえば、このガイドの冒頭にあるhello-world
アプリケーションでは、次のようなものが表示されます。
% clj -X:deps list
clojure.java-time/clojure.java-time 1.1.0 (MIT)
org.clojure/clojure 1.11.2 (EPL-1.0)
org.clojure/core.specs.alpha 0.2.62 (EPL-1.0)
org.clojure/spec.alpha 0.3.218 (EPL-1.0)
time-lib/time-lib ../cli-getting-started/time-lib
アプリケーションで使用される推移的な依存関係の完全なセットが、バージョンとライセンスとともにアルファベット順にリストされます。追加の印刷オプションについては、APIドキュメントを参照してください。
依存関係のツリー構造と、バージョンの選択がどのように行われたかを理解したい場合は、clj -X:deps tree
を使用します。
% clj -X:deps tree
org.clojure/clojure 1.11.2
. org.clojure/spec.alpha 0.3.218
. org.clojure/core.specs.alpha 0.2.62
time-lib/time-lib /Users/alex.miller/tmp/cli-getting-started/time-lib
. clojure.java-time/clojure.java-time 1.1.0
ここではバージョンの選択は行われていませんが、必要に応じてツリーで選択がどのように説明されるかについて詳しくは、ドキュメントを参照してください。
これらのヘルパー関数はどちらも、clj -X:deps list :aliases '[:alias1 :alias2]'
のように、1つまたは複数のエイリアスを適用して依存関係のリストまたはツリーを調べたい場合に、オプションの:aliases
引数を取ります。
オリジナル著者: Alex Miller