Leiningen: nástroj pro správu projektů napsaných v Clojure

12. 2. 2015
Doba čtení: 17 minut

Sdílet

Při vývoji aplikací v programovacím jazyce Clojure je možné ke správě projektů přistupovat různým způsobem. Někteří programátoři dávají přednost používání vlastních skriptů, využívání funkcí nabízených IDE atd., ovšem dnes je nejlepším nástrojem určeným pro tento účel Leiningen, téma dnešního článku.

Obsah

1. Leiningen: nástroj pro správu projektů napsaných v Clojure

2. Základní vlastnosti nástroje Leiningen

3. Instalace Leiningenu z balíčku distribuce

4. Instalace v případě, že balíček není k dispozici

5. Otestování základní funkčnosti instalace Leiningenu

6. Vytvoření nového projektu

7. Automatické stažení závislých balíčků a spuštění projektu

8. Struktura adresáře ~/.m2

9. Přidání nové knihovny, na níž projekt závisí

10. Automatické stažení závislé knihovny

11. Otestování upraveného projektu

12. Odkazy na Internetu

1. Leiningen: nástroj pro správu projektů napsaných v Clojure

V dnešním článku si popíšeme vybrané základní vlastnosti nástroje Leiningen, který je v současnosti používán poměrně velkým množstvím projektů vyvinutých s využitím programovacího jazyka Clojure. Leiningen se typicky používá pro vytváření nových projektů na základě předem připravené šablony, automatické stahování všech potřebných knihoven (i jejich závislostí), spouštění projektů, spouštění testů a v neposlední řadě taktéž pro spuštění interaktivní smyčky REPL, která vývojářům nabízí více možnosti, než standardní implementace REPLu dostupná přímo v Clojure (mezi přidané vlastnosti patří zejména automatické nastavení cest ke knihovnám apod.). Leiningen je multiplatformním produktem, což znamená, že i ty vývojové týmy, jejichž členové pracují s různými operačními systémy, mohou možnosti nabízené Leiningenem využít. K Leiningenu lze v případě potřeby připojovat i další přídavné moduly (plugins), takže například existuje podpora pro spuštění webové aplikace takovým způsobem, že se změny provedené ve zdrojových kódech ihned promítnou do běžící aplikace bez nutnosti jejího restartu, což samozřejmě umožňuje rychlejší vývoj i testování.

2. Základní vlastnosti nástroje Leiningen

O některých vlastnostech nabízených nástrojem Leiningen jsme se zmínili již v předchozí kapitole, zkusme si ovšem jeho základní vlastnosti postupně uvést v jednotlivých bodech:

  1. Vytvoření nového projektu na základě vybrané a předem připravené šablony. K dispozici jsou šablony běžné aplikace (doplněné o jednotkové testy), přídavného modulu pro samotný Leiningen atd. Základním příkazem je lein new.
  2. Automatické stažení všech knihoven a jejich závislostí na základě konfigurace zapsané do souboru project.clj. Tuto funkci zajistí příkaz lein deps.
  3. Spuštění projektu s možností předání parametrů příkazového řádku (viz ukázkový příklad). Příkaz je jednoduchý: lein run.
  4. Spuštění jednotkových testů, které mohou být vytvořeny společně s projektem (kostra jednoho testu je připravena automaticky). Snadno uhádnutelný příkaz, který testy spustí, se jmenuje lein test.
  5. Relativně rychlá kontrola syntaxe zdrojových kódů i kontrola existence volaných metod (lein check).
  6. Lokální instalace projektu do specifikovaného adresáře (lein install).
  7. Příprava Java archivu (souboru s koncovkou .jar) takovým způsobem, aby bylo možné aplikaci nasadit i na jiném počítači. V rámci přípravy archivu se provádí překlad vybraných modulů do javovského bajtkódu (lein jar).
  8. Příprava Java archivu obsahujícího i všechny závislé knihovny včetně samotného Clojure. Takto vytvořený „uberjar“ je posléze možné nasadit na jakémkoli počítači vybaveném pouze JRE (běhovým prostředím jazyka Java). Zajistí příkaz lein uberjar.
  9. Spuštění smyčky REPL s nastavením cest ke všem potřebným knihovnám a modulům (lein repl).
  10. Každý příkaz Leiningenu je v rozumné míře popsán v nápovědě (lein help příkaz).

3. Instalace Leiningenu z balíčku distribuce

Pojďme si nyní říci, jakým způsobem lze Leiningen nainstalovat. Nejjednodušší je to v případě, že se balíček s tímto nástrojem již nachází v repositářích distribuce. Postup je nasnadě – Leiningen se nainstaluje (i se svými závislostmi) stejným způsobem, jako prakticky jakýkoli jiný balíček. Podívejme se například na způsob instalace Leiningenu na počítači s (primárně) desktopovou distribucí Linux Mint. Předpokládejme, že aktuálně přihlášený uživatel může získat práva roota, tedy:

$ su -
Password:

Instalace je v tomto případě až triviálně snadná:

apt-get install leiningen

Po chvilce získáme seznam všech balíčků, které se nainstalují společně s Leiningenem:

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  ant bsh clojure-contrib clojure1.2 clojure1.4 java-wrappers junit junit4
  libapache-pom-java libasm3-java libatinject-jsr330-api-java
  libavalon-framework-java libbatik-java libbsf-java libbsh-java
  ...
  ...
  ...
  ...
  ...
  ...
  libxalan2-java libxbean-java libxerces2-java libxml-commons-external-java
  libxml-commons-resolver1.1-java libxmlgraphics-commons-java rlwrap
0 upgraded, 76 newly installed, 0 to remove and 85 not upgraded.
Need to get 56,1 MB of archives.
After this operation, 75,2 MB of additional disk space will be used.
Do you want to continue? [Y/n]

Zajímavé je, kolik závislých balíčků se k Leiningenu doinstaluje. Ve skutečnosti nejsou některé tyto balíčky potřebné, o čemž se ostatně můžete sami přesvědčit při manuální instalaci popsané v následující kapitole (tímto chováním trpí i další javovské balíčky, třeba Eclipse apod.).

Na počítačích s operačním systémem Microsoft Windows lze buď použít Cygwin a manuální postup popsaný dále, popř. lze sáhnout po aplikaci leiningen-win-installer dostupné na adrese http://leiningen-win-installer.djpowell.net/ (osobně ji však nemám kde odzkoušet :-), takže prosím znalejší čtenáře, aby se o svoji pozitivní či negativní zkušenost podělili v diskuzi pod článkem).

4. Instalace v případě, že balíček není k dispozici

Zajímavější a vlastně i poučnější je manuální instalace Leiningenu v případě, že příslušný balíček není k dispozici popř. uživatel preferuje mít větší kontrolu nad celým procesem. Dále uvedený postup je možné po mírné úpravě využít i v případě, že uživatel nemá práva roota. Postup byl otestován na Debianu, Fedoře 19, Fedoře 20 a Linux Mintu, měl by však fungovat ve všech distribucích s nainstalovaným Bashem (i v Cygwinu). Nejprve je nutné stáhnout skript lein, který představuje samotné jádro Leiningenu:

wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein

Chvíli počkáme na (doufejme) zdárný výsledek:

--2015-02-09 20:31:45--  https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 23.235.43.133
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|23.235.43.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12015 (12K) [text/plain]
Saving to: 'lein'
 
100%[=========================================================>] 12 015      --.-K/s   in 0,002s
 
2015-02-09 20:31:46 (5,85 MB/s) - 'lein' saved [12015/12015]

Nyní máme na výběr dvě možnosti. Buď je možné skript lein uložit do libovolného lokálního adresáře, který je součástí proměnné PATH (lze snadno zjistit přes echo $PATH), popř. lze tento skript „nainstalovat“ takovým způsobem, aby byl dostupný i pro všechny ostatní uživatele. První možnost je jednoduchá, předpokládejme instalaci do ~/bin a udělejme skript spustitelný:

chmod u+x lein
mv lein ~/bin

Druhá možnost – instalace tak, aby byl lein dostupný i pro další uživatele – samozřejmě vyžaduje příslušná práva roota:

sudo cp lein /usr/local/bin
sudo chmod a+x /usr/local/bin/lein

Pro jistotu lze samozřejmě zkontrolovat, co se v tomto skriptu provádí :-)

5. Otestování základní funkčnosti instalace Leiningenu

I po úspěšné instalaci skriptu lein ještě není vše připraveno k okamžitému použití. Ve skutečnosti totiž skript lein ke své činnosti potřebuje i další soubor, jímž je (minimálně v současnosti) javovský archiv nazvaný leiningen-2.x.x-standalone.jar (x.x se postupně mění). Tento archiv je však stažen a uložen zcela automaticky při prvním spuštění Leiningenu. Můžeme si to ostatně snadno otestovat:

lein

Zahájí se stahování a posléze se vypíšou všechny možnosti, které Leiningen v základní instalaci svým uživatelům nabízí:

Downloading Leiningen to /home/tester/.lein/self-installs/leiningen-2.5.1-standalone.jar now...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   406    0   406    0     0    749      0 --:--:-- --:--:-- --:--:--   749
100 14.5M  100 14.5M    0     0  1618k      0  0:00:09  0:00:09 --:--:-- 1510k
Leiningen is a tool for working with Clojure projects.
 
Several tasks are available:
change              Rewrite project.clj by applying a function.
check               Check syntax and warn on reflection.
classpath           Print the classpath of the current project.
clean               Remove all files from project's target-path.
compile             Compile Clojure source into .class files.
deploy              Build and deploy jar to remote repository.
deps                Download all dependencies.
do                  Higher-order task to perform other tasks in succession.
help                Display a list of tasks or help for a given task.
install             Install the current project to the local repository.
jar                 Package up all the project's files into a jar file.
javac               Compile Java source files.
new                 Generate project scaffolding based on a template.
plugin              DEPRECATED. Please use the :user profile instead.
pom                 Write a pom.xml file to disk for Maven interoperability.
release             Perform :release-tasks.
repl                Start a repl session either with the current project or standalone.
retest              Run only the test namespaces which failed last time around.
run                 Run a -main function with optional command-line arguments.
search              Search remote maven repositories for matching jars.
show-profiles       List all available profiles or display one if given an argument.
test                Run the project's tests.
trampoline          Run a task without nesting the project's JVM inside Leiningen's.
uberjar             Package up the project files and dependencies into a jar file.
update-in           Perform arbitrary transformations on your project map.
upgrade             Upgrade Leiningen to specified version or latest stable.
vcs                 Interact with the version control system.
version             Print version for Leiningen and the current JVM.
with-profile        Apply the given task with the profile(s) specified.
 
Run `lein help $TASK` for details.
 
Global Options:
  -o             Run a task offline.
  -U             Run a task after forcing update of snapshots.
  -h, --help     Print this help or help for a specific task.
  -v, --version  Print Leiningen's version.
 
See also: readme, faq, tutorial, news, sample, profiles, deploying, gpg,
mixed-source, templates, and copying.

Výše zmíněný soubor leiningen-2.x.x-standalone.jar naleznete v adresáři ~/.lein:

$ tree ~/.lein
 
/home/ptisnovsk/.lein
├── repl-history
└── self-installs
    └── leiningen-2.5.0-standalone.jar
 
1 directory, 2 files

6. Vytvoření nového projektu

Nyní si již konečně můžeme popsat způsob práce s projekty s využitím nástroje Leiningen. Základním příkazem je vytvoření nového projektu v aktuálním adresáři. Tuto činnost zajišťuje příkaz lein new, kterému se navíc musí předat typ projektu:

lein new

V základní instalaci Leiningenu (tj. ve chvíli, kdy prozatím nejsou nainstalovány žádné další přídavné moduly) by měly být k dispozici minimálně následující čtyři typy projektů:

Subtasks available:
template   A meta-template for 'lein new' templates.
default    A general project template for libraries.
app        An application project template.
plugin     A leiningen plugin project template.

Zkusme si nyní vytvořit běžný projekt představující aplikaci naprogramovanou v Clojure. Pro vytvoření projektu použijeme následující příkaz:

lein new app clojure_test_1
Generating a project called clojure_test_1 based on the 'app' template.

V případě úspěchu by se měl v aktuálním adresáři vytvořit podadresář s názvem clojure_test1, který má tuto strukturu:

.
├── doc
│   └── intro.md
├── LICENSE
├── project.clj
├── README.md
├── resources
├── src
│   └── clojure_test_1
│       └── core.clj
└── test
    └── clojure_test_1
        └── core_test.clj
 
6 directories, 6 files

Soubory doc/intro.md, LICENCEREADME.md jsou základem dokumentace, ovšem z hlediska vývojáře existuje v projektu dvojice mnohem důležitějších souborů. Prvním z nich je project.clj. Tento soubor je zpracováván Leiningenem při mnoha činnostech, zejména při spouštění aplikace, instalaci závislostí apod. Podívejme se nyní na obsah tohoto souboru:

(defproject clojure_test_1 "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.6.0"]]
  :main ^:skip-aot clojure-test-1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Vidíme, že se jedná o běžný zdrojový soubor programovacího jazyka Clojure obsahující mapu, v níž jednotlivé klíče mají předem daný význam. Důležitý je především klíč :main, kterým se specifikuje soubor s funkcí pojmenovanou main-. Tato funkce je zavolána po spuštění aplikace a navíc jsou této funkci předány parametry z příkazové řádky (takže jejich zpracování je velmi jednoduché, není totiž nutné používat *command-line-args* atd.). Dále zde můžeme vidět další důležitý klíč :dependencies, o jehož významu se přesvědčíme v navazujících kapitolách.

Soubor src/clojure_test1/core.clj obsahuje základní kostru aplikace. Především zde nalezneme specifikaci jmenného prostoru (což je pochopitelné) a taktéž výše zmíněnou funkci main- tvořící vstupní bod do právě vytvořené aplikace:

(ns clojure-test-1.core
  (:gen-class))
 
(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

Poznámka: povšimněte si, že ve jménech adresářů a souborů se používají podtržítka, kdežto ve specifikacích jmenných prostorů pomlčky. Na tuto konvenci je nutné si zvyknout, při jejím nedodržení totiž Clojure nenajde všechny skripty a budou vypisována mnohdy kryptická chybová hlášení.

7. Automatické stažení závislých balíčků a spuštění projektu

Právě vytvořený projekt lze spustit příkazem lein run zavolaného v adresáři s projektem, tedy:

cd clojure_test_1
lein run

Před prvním spuštěním se automaticky vyhodnotí všechny závislé balíčky, ty se stáhnou a použijí (vidíme, že se mj. stahuje i samotné Clojure):

Retrieving org/clojure/clojure/1.6.0/clojure-1.6.0.pom from central
Retrieving org/sonatype/oss/oss-parent/7/oss-parent-7.pom from central
Retrieving org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.pom from central
Retrieving org/clojure/pom.contrib/0.1.2/pom.contrib-0.1.2.pom from central
Retrieving clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.pom from clojars
Retrieving org/clojure/tools.nrepl/0.2.6/tools.nrepl-0.2.6.jar from central
Retrieving org/clojure/clojure/1.6.0/clojure-1.6.0.jar from central
Retrieving clojure-complete/clojure-complete/0.2.3/clojure-complete-0.2.3.jar from clojars
Hello, World!

Po druhém spuštění stejné aplikace příkazem:

lein run

Se již žádné další balíčky nestahují, ale dojde skutečně přímo ke spuštění:

Hello, World!

8. Struktura adresáře ~/.m2

Zajímavé bude zjistit, kam se vlastně uložily všechny balíčky, na nichž běh aplikace závisí. Projekt Leiningen ve skutečnosti používá stejnou strukturu jako Maven (resp. přesněji řečeno je přímo využívána funkcionalita Mavenu), takže balíčky nalezneme v adresáři ~/.m2 (který vám bude časem utěšeně bobtnat :-). Obsah adresáře těsně po spuštění demonstračního příkladu vypadá takto:

tree ~/.m2
.
└── repository
    ├── clojure-complete
    │   └── clojure-complete
    │       └── 0.2.3
    │           ├── clojure-complete-0.2.3.jar
    │           ├── clojure-complete-0.2.3.jar.sha1
    │           ├── clojure-complete-0.2.3.pom
    │           ├── clojure-complete-0.2.3.pom.sha1
    │           └── _maven.repositories
    └── org
        ├── clojure
        │   ├── clojure
        │   │   └── 1.6.0
        │   │       ├── clojure-1.6.0.jar
        │   │       ├── clojure-1.6.0.jar.sha1
        │   │       ├── clojure-1.6.0.pom
        │   │       ├── clojure-1.6.0.pom.sha1
        │   │       └── _maven.repositories
        │   ├── pom.contrib
        │   │   └── 0.1.2
        │   │       ├── _maven.repositories
        │   │       ├── pom.contrib-0.1.2.pom
        │   │       └── pom.contrib-0.1.2.pom.sha1
        │   └── tools.nrepl
        │       └── 0.2.6
        │           ├── _maven.repositories
        │           ├── tools.nrepl-0.2.6.jar
        │           ├── tools.nrepl-0.2.6.jar.sha1
        │           ├── tools.nrepl-0.2.6.pom
        │           └── tools.nrepl-0.2.6.pom.sha1
        └── sonatype
            └── oss
                └── oss-parent
                    └── 7
                        ├── _maven.repositories
                        ├── oss-parent-7.pom
                        └── oss-parent-7.pom.sha1
 
16 directories, 21 files

Po několika týdnech práce na různých projektech se však můžete dopracovat i k následujícímu stavu:

/home/ptisnovsk/.m2
└── repository
    ├── args4j
    │   ├── args4j
    │   │   └── 2.0.16
    │   │       ├── args4j-2.0.16.jar
    │   │       ├── args4j-2.0.16.jar.sha1
    │   │       ├── args4j-2.0.16.pom
    │   │       ├── args4j-2.0.16.pom.sha1
    │   │       └── _maven.repositories
    │   └── args4j-site
    │       └── 2.0.16
    │           ├── args4j-site-2.0.16.pom
    │           ├── args4j-site-2.0.16.pom.sha1
    │           └── _maven.repositories
    ├── cljsbuild
    │   └── cljsbuild
    │       └── 1.0.3
    │           ├── cljsbuild-1.0.3.jar
    │           ├── cljsbuild-1.0.3.jar.sha1
    │           ├── cljsbuild-1.0.3.pom
    │           ├── cljsbuild-1.0.3.pom.sha1
    │           └── _maven.repositories
    ├── clj-stacktrace
    │   └── clj-stacktrace
    │       └── 0.2.5
    │           ├── clj-stacktrace-0.2.5.jar
    │           ├── clj-stacktrace-0.2.5.jar.sha1
    │           ├── clj-stacktrace-0.2.5.pom
    │           ├── clj-stacktrace-0.2.5.pom.sha1
    │           └── _maven.repositories
    ├── clojure-complete
    │   └── clojure-complete
    │       └── 0.2.3
    │           ├── clojure-complete-0.2.3.jar
    │           ├── clojure-complete-0.2.3.jar.sha1
    │           ├── clojure-complete-0.2.3.pom
    │           ├── clojure-complete-0.2.3.pom.sha1
    │           └── _maven.repositories
    ├── com
    │   └── google
    │       ├── code
    │       │   └── findbugs
    │       │       └── jsr305
    │       │           └── 1.3.9
    │       │               ├── jsr305-1.3.9.jar
    │       │               ├── jsr305-1.3.9.jar.sha1
    │       │               ├── jsr305-1.3.9.pom
    │       │               ├── jsr305-1.3.9.pom.sha1
    │       │               └── _maven.repositories
    │       ├── google
    │       │   └── 1
    │       │       ├── google-1.pom
    │       │       ├── google-1.pom.sha1
    │       │       └── _maven.repositories
    │       ├── guava
    │       │   ├── guava
    │       │   │   └── 15.0
    │       │   │       ├── guava-15.0.jar
    │       │   │       ├── guava-15.0.jar.sha1
    │       │   │       ├── guava-15.0.pom
    │       │   │       ├── guava-15.0.pom.sha1
    │       │   │       └── _maven.repositories
    │       │   └── guava-parent
    │       │       └── 15.0
    │       │           ├── guava-parent-15.0.pom
    │       │           ├── guava-parent-15.0.pom.sha1
    │       │           └── _maven.repositories
    │       ├── javascript
    │       │   └── closure-compiler
    │       │       └── v20131014
    │       │           ├── closure-compiler-v20131014.jar
    │       │           ├── closure-compiler-v20131014.jar.sha1
    │       │           ├── closure-compiler-v20131014.pom
    │       │           ├── closure-compiler-v20131014.pom.sha1
    │       │           └── _maven.repositories
    │       └── protobuf
    │           └── protobuf-java
    │               └── 2.4.1
    │                   ├── _maven.repositories
    │                   ├── protobuf-java-2.4.1.jar
    │                   ├── protobuf-java-2.4.1.jar.sha1
    │                   ├── protobuf-java-2.4.1.pom
    │                   └── protobuf-java-2.4.1.pom.sha1
    ├── fs
    │   └── fs
    │       └── 1.1.2
    │           ├── fs-1.1.2.jar
    │           ├── fs-1.1.2.jar.sha1
    │           ├── fs-1.1.2.pom
    │           ├── fs-1.1.2.pom.sha1
    │           └── _maven.repositories
    ├── javassist
    │   └── javassist
    │       └── 3.12.1.GA
    │           ├── javassist-3.12.1.GA.jar
    │           ├── javassist-3.12.1.GA.jar.sha1
    │           ├── javassist-3.12.1.GA.pom
    │           ├── javassist-3.12.1.GA.pom.sha1
    │           └── _maven.repositories
    ├── lein-cljsbuild
    │   ├── cljs-compat
    │   │   └── 1.0.0-SNAPSHOT
    │   │       ├── cljs-compat-1.0.0-20140402.162347-24.jar
    │   │       ├── cljs-compat-1.0.0-20140402.162347-24.jar.sha1
    │   │       ├── cljs-compat-1.0.0-20140402.162347-24.pom
    │   │       ├── cljs-compat-1.0.0-20140402.162347-24.pom.sha1
    │   │       ├── cljs-compat-1.0.0-SNAPSHOT.jar
    │   │       ├── cljs-compat-1.0.0-SNAPSHOT.pom
    │   │       ├── maven-metadata-clojars.xml
    │   │       ├── maven-metadata-clojars.xml.sha1
    │   │       ├── _maven.repositories
    │   │       └── resolver-status.properties
    │   └── lein-cljsbuild
    │       └── 1.0.3
    │           ├── lein-cljsbuild-1.0.3.jar
    │           ├── lein-cljsbuild-1.0.3.jar.sha1
    │           ├── lein-cljsbuild-1.0.3.pom
    │           ├── lein-cljsbuild-1.0.3.pom.sha1
    │           └── _maven.repositories
    └── org
        ├── apache
        │   ├── apache
        │   │   └── 9
        │   │       ├── apache-9.pom
        │   │       ├── apache-9.pom.sha1
        │   │       └── _maven.repositories
        │   └── commons
        │       ├── commons-compress
        │       │   └── 1.3
        │       │       ├── commons-compress-1.3.jar
        │       │       ├── commons-compress-1.3.jar.sha1
        │       │       ├── commons-compress-1.3.pom
        │       │       ├── commons-compress-1.3.pom.sha1
        │       │       └── _maven.repositories
        │       └── commons-parent
        │           └── 22
        │               ├── commons-parent-22.pom
        │               ├── commons-parent-22.pom.sha1
        │               └── _maven.repositories
        ├── clojure
        │   ├── clojure
        │   │   ├── 1.3.0
        │   │   │   ├── clojure-1.3.0.jar
        │   │   │   ├── clojure-1.3.0.jar.sha1
        │   │   │   ├── clojure-1.3.0.pom
        │   │   │   ├── clojure-1.3.0.pom.sha1
        │   │   │   └── _maven.repositories
        │   │   ├── 1.4.0
        │   │   │   ├── clojure-1.4.0.pom
        │   │   │   ├── clojure-1.4.0.pom.sha1
        │   │   │   └── _maven.repositories
        │   │   ├── 1.5.1
        │   │   │   ├── clojure-1.5.1.pom
        │   │   │   ├── clojure-1.5.1.pom.sha1
        │   │   │   └── _maven.repositories
        │   │   └── 1.6.0
        │   │       ├── clojure-1.6.0.jar
        │   │       ├── clojure-1.6.0.jar.sha1
        │   │       ├── clojure-1.6.0.pom
        │   │       ├── clojure-1.6.0.pom.sha1
        │   │       └── _maven.repositories
        │   ├── clojurescript
        │   │   └── 0.0-2197
        │   │       ├── clojurescript-0.0-2197.jar
        │   │       ├── clojurescript-0.0-2197.jar.sha1
        │   │       ├── clojurescript-0.0-2197.pom
        │   │       ├── clojurescript-0.0-2197.pom.sha1
        │   │       └── _maven.repositories
        │   ├── data.json
        │   │   └── 0.2.3
        │   │       ├── data.json-0.2.3.jar
        │   │       ├── data.json-0.2.3.jar.sha1
        │   │       ├── data.json-0.2.3.pom
        │   │       ├── data.json-0.2.3.pom.sha1
        │   │       └── _maven.repositories
        │   ├── google-closure-library
        │   │   └── 0.0-20140226-71326067
        │   │       ├── google-closure-library-0.0-20140226-71326067.jar
        │   │       ├── google-closure-library-0.0-20140226-71326067.jar.sha1
        │   │       ├── google-closure-library-0.0-20140226-71326067.pom
        │   │       ├── google-closure-library-0.0-20140226-71326067.pom.sha1
        │   │       └── _maven.repositories
        │   ├── google-closure-library-third-party
        │   │   └── 0.0-20140226-71326067
        │   │       ├── google-closure-library-third-party-0.0-20140226-71326067.jar
        │   │       ├── google-closure-library-third-party-0.0-20140226-71326067.jar.sha1
        │   │       ├── google-closure-library-third-party-0.0-20140226-71326067.pom
        │   │       ├── google-closure-library-third-party-0.0-20140226-71326067.pom.sha1
        │   │       └── _maven.repositories
        │   ├── pom.contrib
        │   │   └── 0.1.2
        │   │       ├── _maven.repositories
        │   │       ├── pom.contrib-0.1.2.pom
        │   │       └── pom.contrib-0.1.2.pom.sha1
        │   ├── tools.nrepl
        │   │   └── 0.2.6
        │   │       ├── _maven.repositories
        │   │       ├── tools.nrepl-0.2.6.jar
        │   │       ├── tools.nrepl-0.2.6.jar.sha1
        │   │       ├── tools.nrepl-0.2.6.pom
        │   │       └── tools.nrepl-0.2.6.pom.sha1
        │   └── tools.reader
        │       └── 0.8.3
        │           ├── _maven.repositories
        │           ├── tools.reader-0.8.3.jar
        │           ├── tools.reader-0.8.3.jar.sha1
        │           ├── tools.reader-0.8.3.pom
        │           └── tools.reader-0.8.3.pom.sha1
        ├── javassist
        │   └── javassist
        │       ├── 3.12.1.GA
        │       └── 3.18.0
        ├── json
        │   └── json
        │       └── 20090211
        │           ├── json-20090211.jar
        │           ├── json-20090211.jar.sha1
        │           ├── json-20090211.pom
        │           ├── json-20090211.pom.sha1
        │           └── _maven.repositories
        ├── mozilla
        │   └── rhino
        │       └── 1.7R4
        │           ├── _maven.repositories
        │           ├── rhino-1.7R4.jar
        │           ├── rhino-1.7R4.jar.sha1
        │           ├── rhino-1.7R4.pom
        │           └── rhino-1.7R4.pom.sha1
        └── sonatype
            └── oss
                └── oss-parent
                    ├── 5
                    │   ├── _maven.repositories
                    │   ├── oss-parent-5.pom
                    │   └── oss-parent-5.pom.sha1
                    └── 7
                        ├── _maven.repositories
                        ├── oss-parent-7.pom
                        └── oss-parent-7.pom.sha1
 
89 directories, 150 files

uff…

Adresář ~/.m2 je samozřejmě možné pročistit a znovu u všech projektů spustit lein deps (popř. i Maven v případě, že programátor současně vyvíjí i projekty v Javě).

Stručné shrnutí: při práci s Leiningenem se typicky používají čtyři adresáře:

  1. Adresář zmíněný na PATH, kde je uložen spustitelný skript lein.
  2. Adresář ~/.lein obsahující soubory potřebné pro běh Leiningenu.
  3. Adresář ~/.m2 s knihovnami vyžadovanými jednotlivými projekty.
  4. Adresář s projektem, který musí obsahovat především soubor project.clj.

9. Přidání nové knihovny, na níž projekt závisí

Podívejme se nyní na způsob řešení závislostí. Pokusíme se do našeho demonstračního příkladu přidat knihovnu org.clojure/data.json, kterou lze použít při zpracování dat uložených či přenášených v populárním formátu JSON. Nová knihovna se přidá do vektoru navázaného na klíč :dependencies, přičemž se nesmí zapomenout na to, že i samotná deklarace knihovny je vektor obsahující jméno a verzi – pozor tedy na správné uzávorkování (viz zvýrazněný řádek):

(defproject clojure_test_1 "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.6.0"]
                  [org.clojure/data.json "0.2.5"]]
  :main ^:skip-aot clojure-test-1.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Upravíme i zdrojový kód projektu takovým způsobem, aby tuto knihovnu využíval. Knihovnu je nutné uvést v atributu :require (opět viz zvýrazněný řádek) s případným uvedením symbolu sloužícího jako alias k plnému názvu jmenného prostoru knihovny. To znamená, že namísto clojure.data.json/pprint se ve zdrojovém kódu může psát pouze json/pprint atd.:

(ns clojure-test-1.core
  (:gen-class)
  (:require [clojure.data.json :as json]))
 
(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (let [article {:title "What's Good About Clojure?"
                 :url   "http://www.catalysoft.com/articles/goodAboutClojure.html"
                 :last-checked (.toString (new java.util.Date))}]
  (json/pprint article)))

10. Automatické stažení závislé knihovny

Pokud se nyní (opět v adresáři s projektem) zadá následující příkaz…

lein deps

… je podle zpráv vypisovaných na terminál patrné, že Leiningen otevře a zpracuje soubor project.clj, načte všechny knihovny specifikované v tomto souboru, vyhodnotí, které knihovny ještě nejsou lokálně dostupné a následně tyto knihovny stáhne a uloží do správného podadresáře v ~/.m2:

Retrieving org/clojure/data.json/0.2.5/data.json-0.2.5.pom from central
Retrieving org/clojure/clojure/1.4.0/clojure-1.4.0.pom from central
Retrieving org/sonatype/oss/oss-parent/5/oss-parent-5.pom from central
Retrieving org/clojure/data.json/0.2.5/data.json-0.2.5.jar from central

11. Otestování upraveného projektu

Nyní již máme všechny potřebné knihovny nainstalované lokálně a můžeme přistoupit ke spuštění naší demonstrační aplikace:

bitcoin školení listopad 24

lein run

Z výpisu provedeného na standardní výstup je patrné, že se funkce json/pprint skutečně správně zavolala (vyhodnotila):

{"title":"What's Good About Clojure?",
 "url":"http:\/\/www.catalysoft.com\/articles\/goodAboutClojure.html",
 "last-checked":"Mon Feb 09 21:12:08 CET 2015"}

Ve druhé části článku si ukážeme další možnosti, které projekt Leiningen programátorům nabízí.

12. Odkazy na Internetu

  1. Leiningen: úvodní stránka
    http://leiningen.org/
  2. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  3. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  4. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  5. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  6. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  7. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  8. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  9. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  10. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  11. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  12. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  13. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  14. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  15. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  16. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II
    2) http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  17. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  18. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  19. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  20. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  21. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  22. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  23. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  24. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.