Obsah
1. Leiningen: nástroj pro správu projektů napsaných v Clojure (pluginy pro Leiningen)
2. Plugin test2junit – jednotkové testy s výstupem kompatibilním s JUnit
3. Plugin lein-ring – automatizace úloh souvisejících s webovými aplikacemi
4. Automatické znovunačtení modulů při změně jejich zdrojového kódu
5. Vytvoření WAR souboru s webovou aplikací
6. Plugin codox – tvorba dokumentace v HTML formátu
7. Plugin cloverage – zjištění pokrytí kódu testy
8. Repositář s demonstračními příklady
9. Odkazy na předchozí části seriálu
1. Leiningen: nástroj pro správu projektů napsaných v Clojure (pluginy pro Leiningen)
V dnešní části článku o programovacím jazyku Clojure i o nástroji Leiningen se vrátíme k problematice pluginů vytvořených pro Leiningen. Tento nástroj je (minimálně ve své druhé verzi) řešen modulárním způsobem, což mj. znamená, že do Leiningenu je možné „zaregistrovat“ prakticky libovolnou aplikaci sloužící například pro správu projektů, správu či monitorování běžících aplikací, generování dokumentace, zjištění pokrytí aplikace testy apod. Po instalaci pluginu je Leiningen rozšířen o další příkazy a parametry – ty lze považovat za nové úlohy, které lze přes příkaz lein spustit. Naprostá většina programátorů využívajících programovací jazyk Clojure a nástroj Leiningen sice vlastní pluginy nevytváří, na druhou stranu použití již naprogramovaných pluginů je velmi jednoduché a co je možná ještě podstatnější – některé pluginy dokážou urychlit a zpříjemnit vývoj. Seznam pluginů lze nalézt na adrese https://github.com/technomancy/leiningen/wiki/Plugins.
2. Plugin test2junit – jednotkové testy s výstupem kompatibilním s JUnit
O pluginu nazvaném test2junit jsme se již zmiňovali v předchozích částech tohoto článku, takže si dnes jen stručně řekněme, v čem spočívá význam tohoto modulu. Leiningen již ve své původní variantě podporuje spouštění testů, ovšem formát výsledků testů není kompatibilní s dnes de facto standardem představovaným nástrojem JUnit. Právě z tohoto důvodu je plugin test2junit velmi užitečný, protože taktéž dokáže spustit testy a následně vygenerovat XML soubor s výsledky, přičemž formát tohoto souboru je kompatibilní s JUnit. Výsledný soubor je možné dále zpracovat dalšími nástroji, například lze velmi snadno přidat nový job s testy aplikace vytvořené v programovacím jazyku Clojure do Jenkinsu atd.
Pro úplnost se podívejme na jednoduchý demonstrační příklad obsahující jednu funkci, která je následně otestována dvojicí testů, u nichž se předpokládá pozitivní výsledek a dvojicí testů, u nichž se naopak očekává výsledek negativní.
Soubor project.clj. Zde si povšimněte především deklarace používaného pluginu – jedná se o zvýrazněný řádek (všechny pluginy Leiningenu se zapisují pod tento klíč):
(defproject factorial "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 factorial.core :target-path "target/%s" :plugins [[test2junit "1.1.0"]] :profiles {:uberjar {:aot :all}})
Zdrojový kód demonstračního příkladu (ten již známe z předchozích částí článku):
(ns factorial.core (:gen-class)) (defn factorial [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn -main "I don't do a whole lot ... yet." [& args] (doseq [i (range 0 10)] (println i "! = " (factorial i))))
Testy uložené v adresáři test/factorial; povšimněte si, že jsou použity jak pozitivní, tak i negativní testy (u nichž je zaručeno, že neprojdou):
(ns factorial.core-test (:require [clojure.test :refer :all] [factorial.core :refer :all])) (deftest factorial-test (testing "Factorial" (is ( = (factorial 0) 1) "beginning") (is ( = (factorial 1) 1) "beginning") (is ( = (factorial 2) (* 1 2)) "still easy") (is ( = (factorial 5) (* 1 2 3 4 5)) "5!") (is ( = (factorial 6) 720) "6!"))) (deftest negative-factorial-test (testing "Negative tests" (is ( = (factorial 0) 0) "negative test case #1") (is ( = (factorial 1) 0) "negative test case #2") (is ( = (factorial 2) 0) "negative test case #3"))) (deftest exception-test (testing "If factorial throws exception" (is (thrown? IllegalArgumentException (factorial -1))) (is (thrown? IllegalArgumentException (factorial -2))) (is (thrown? IllegalArgumentException (factorial -100))))) (deftest negative-exception-test (testing "(negative test) If factorial throws exception" (is (thrown? IllegalArgumentException (factorial 1))) (is (thrown? IllegalArgumentException (factorial 2))) (is (thrown? IllegalArgumentException (factorial 3)))))
Spuštění testů běžným způsobem:
lein test lein test factorial.core-test lein test :only factorial.core-test/negative-exception-test FAIL in (negative-exception-test) (core_test.clj:27) (negative test) If factorial throws exception expected: (thrown? IllegalArgumentException (factorial 1)) actual: nil lein test :only factorial.core-test/negative-exception-test FAIL in (negative-exception-test) (core_test.clj:28) (negative test) If factorial throws exception expected: (thrown? IllegalArgumentException (factorial 2)) actual: nil lein test :only factorial.core-test/negative-exception-test FAIL in (negative-exception-test) (core_test.clj:29) (negative test) If factorial throws exception expected: (thrown? IllegalArgumentException (factorial 3)) actual: nil lein test :only factorial.core-test/negative-factorial-test FAIL in (negative-factorial-test) (core_test.clj:15) Negative tests negative test case #1 expected: (= (factorial 0) 0) actual: (not (= 1 0)) lein test :only factorial.core-test/negative-factorial-test FAIL in (negative-factorial-test) (core_test.clj:16) Negative tests negative test case #2 expected: (= (factorial 1) 0) actual: (not (= 1 0)) lein test :only factorial.core-test/negative-factorial-test FAIL in (negative-factorial-test) (core_test.clj:17) Negative tests negative test case #3 expected: (= (factorial 2) 0) actual: (not (= 2 0)) Ran 4 tests containing 14 assertions. 6 failures, 0 errors. Tests failed.
Spuštění testů s vygenerováním výsledku kompatibilního s JUnit:
lein test2junit
Vygenerovaný soubor s výsledky testů:
<?xml version="1.0" encoding="UTF-8"?> <testsuite name="factorial.core-test" errors="0" failures="6" tests="4" time="0.0669" timestamp="2015-04-01_19:29:27+0200"> <testcase name="negative-exception-test" classname="factorial.core-test" time="0.0106"> <failure>expected: (thrown? IllegalArgumentException (factorial 1)) actual: nil at: AFn.java:18</failure> <failure>expected: (thrown? IllegalArgumentException (factorial 2)) actual: nil at: AFn.java:18</failure> <failure>expected: (thrown? IllegalArgumentException (factorial 3)) actual: nil at: AFn.java:18</failure> </testcase> <testcase name="negative-factorial-test" classname="factorial.core-test" time="0.0064"> <failure message="negative test case #1">negative test case #1 expected: (= (factorial 0) 0) actual: (not (= 1 0)) at: AFn.java:18</failure> <failure message="negative test case #2">negative test case #2 expected: (= (factorial 1) 0) actual: (not (= 1 0)) at: AFn.java:18</failure> <failure message="negative test case #3">negative test case #3 expected: (= (factorial 2) 0) actual: (not (= 2 0)) at: AFn.java:18</failure> </testcase> <testcase name="factorial-test" classname="factorial.core-test" time="0.0025"> </testcase> <testcase name="exception-test" classname="factorial.core-test" time="0.0013"> </testcase> </testsuite>
3. Plugin lein-ring – automatizace úloh souvisejících s webovými aplikacemi
Dalším zajímavým pluginem je modul nazvaný lein-ring. Název tohoto přídavného modulu již alespoň částečně napovídá, že se jedná o plugin určený pro správu aplikací využívajících Clojure Ring, což je knihovna používaná především pro tvorbu webových aplikací (možnosti této knihovny jsme si již poměrně dopodrobna popsali v předchozích dílech tohoto článku). Vraťme se však k modulu lein-ring. Po jeho instalaci je nástroj Leiningen rozšířen o další příkazy i o další funkcionalitu. Mezi nově nabízené možnosti patří zejména:
- spuštění webového serveru, nasazení aplikace a spuštění prohlížeče s otevřenou aplikací
- dtto, ovšem bez spuštění prohlížeče (takzvaný režim headless)
- detekce změn ve zdrojovém kódu a automatický update aplikace nasazené na serveru
- vytvoření souboru .war (web archive) určeného pro nasazení do servlet kontejneru
- vytvoření souboru „uberwar“, což je obdoba předchozího archivu, ovšem obsahujícího i všechny potřebné knihovny
Dvě z těchto vlastností si popíšeme v navazujících dvou kapitolách.
4. Automatické znovunačtení modulů při změně jejich zdrojového kódu
Nejprve si odzkoušíme první tři funkce nabízené výše zmíněným pluginem lein-ring. S pomocí příkazu lein new app ringapp1 vytvoříme nový projekt a upravíme jeho projektový soubor takovým způsobem, aby se používal plugin lein-ring. Navíc je zde uveden řádek začínající klíčem :ring, kterým je naznačeno, kde má plugin hledat definici webové aplikace. Tento řádek je využit při automatických změnách aplikace nasazené na webovém serveru ve chvíli, kdy se detekuje změna ve zdrojových kódech:
(defproject ringapp1 "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"] [ring/ring-core "1.3.2"] [ring/ring-jetty-adapter "1.3.2"] [hiccup "1.0.4"] [org.clojure/tools.cli "0.3.1"]] :dev-dependencies [[lein-ring "0.8.10"]] :plugins [[lein-ring "0.8.10"]] :main ^:skip-aot ringapp1.core :target-path "target/%s" :ring {:handler ringapp1.core/app} :profiles {:uberjar {:aot :all}})
Kód webové aplikace je poměrně jednoduchý a navíc jsme se s ním již setkali při popisu knihoven Clojure Ring a Hiccup. Jedná se o aplikaci zobrazující tabulku faktoriálů, přičemž uživatel může ve webovém formuláři specifikovat rozsah vstupních hodnot:
(ns ringapp1.core (:gen-class)) (require '[ring.adapter.jetty :as jetty]) (require '[ring.middleware.params :as http-params]) (require '[ring.util.response :as response]) (require '[hiccup.page :as page]) (require '[hiccup.form :as form]) (defn fact [n] (apply * (range 1M (inc n)))) (defn html-page [max-n] (page/xhtml [:head [:title "Ring app #1"] [:meta {:name "Generator" :content "Clojure"}] [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}] ] [:body [:h1 "Ring app #1"] (form/form-to [:get "/"] (form/text-field {:size "20"} "max-n" max-n) [:br] (form/submit-button "Recalculate")) [:br] [:table {:style "border:2px solid brown;background-color:#ace"} [:tr [:th "n"] [:th "n!"]] (for [n (range 0M (inc max-n))] [:tr [:td n] [:td {:style "text-align:right"} (fact n)]]) ] ])) (defn param->number "Prevod parametru specifikovaneho v param-name na cislo typu BigDecimal." [params param-name] (let [param (get params param-name)] (try (bigdec param) ; pokus o prevod na BigDecimal (catch Exception e nil)))) ; pokud se prevod nepovede, vraci se nil (defn handler "Zpracovani pozadavku." [request] (let [params (:params request) max-n (param->number params "max-n")] (println "Params: " params) (println "max-n: " max-n) (-> (response/response (html-page (if max-n max-n 10M))) (response/content-type "text/html; charset=utf-8")))) (def app "Datova struktura predstavujici kostru webove aplikace." (-> handler http-params/wrap-params)) (defn -main "Spusteni webove aplikace na portu 8080." [& args] (jetty/run-jetty app {:port 8080}))
Tuto aplikaci je možné spustit různými způsoby. Základní způsob již známe:
lein run
Na standardním výstupu by se měly objevit následující zprávy:
2015-04-01 21:25:36.267:INFO:oejs.Server:jetty-7.6.13.v20130916 2015-04-01 21:25:36.345:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080
Vidíme, že aplikace je otevřena na portu 8080.
Alternativně lze aplikaci spustit příkazem zajišťujícím provedení spuštění přes plugin lein-ring:
lein ring server
Po provedení tohoto příkazu by se měly stát dvě věci – měl by se otevřít webový prohlížeč s aplikací a paralelně s tím by se měly na standardním výstupu objevit informace o spuštění na portu 3000. Poslední dva řádky vypisuje samotná aplikace, protože byla otevřena v prohlížeči a tudíž klient poslal dotaz(y) serveru:
2015-04-01 21:26:11.472:INFO:oejs.Server:jetty-7.6.13.v20130916 2015-04-01 21:26:11.494:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000 Started server on port 3000 Params: {} max-n: nil Params: {} max-n: nil
Alternativně je možné následujícím příkazem provést prakticky stejnou operaci, akorát bez spuštění prohlížeče:
lein ring server-headless
2015-04-01 21:26:42.900:INFO:oejs.Server:jetty-7.6.13.v20130916 2015-04-01 21:26:42.923:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000 Started server on port 3000
Proč je však vhodné aplikaci spouštět přes lein ring server a nikoli pomocí pouhého lein run? Rozdíl spočívá v tom, že plugin lein-ring detekuje změny provedené ve zdrojových kódech a ihned tyto změny aplikuje na běžící aplikaci. To je velmi užitečné, ať již při ladění, tak i při interaktivním (či polointeraktivním) způsobu vývoje. Teoreticky je možné takto spuštěnou aplikaci nasadit na server a případné požadavky uživatelů řešit na „živém kódu“ :-) (kupodivu se to pro některé aplikace, zejména intranetové, osvědčilo).
5. Vytvoření WAR souboru s webovou aplikací
Další zajímavou funkcí modulu lein-ring je možnost vytvoření webových archivů (WAR) obsahujících aplikaci přeloženou do bajtkódu Javy. Takto zabalenou aplikaci lze nasadit na prakticky jakýkoli servlet kontejner, například na Tomcat. Vytvoření archivu je velmi snadné:
lein clean lein ring war Created /home/ptisnovsk/repos/clojure-examples/leinring1/target/ringapp1-0.1.0-SNAPSHOT.war
Vytvořený soubor má velikost 368kB a po jeho prohlédnutí je patrné, že skutečně obsahuje soubory .class a několik nezbytných konfiguračních souborů:
ls -l target/*.war -rw-rw-r--. 1 ptisnovs ptisnovs 376389 Apr 1 21:30 target/ringapp1-0.1.0-SNAPSHOT.war
Ve chvíli, kdy je nutné nasadit aplikaci na servlet kontejner neobsahující všechny potřebné knihovny, může být důležitější použití dalšího příkazu:
lein clean lein ring uberwar Created /home/ptisnovsk/repos/clojure-examples/leinring1/target/ringapp1-0.1.0-SNAPSHOT-standalone.war
Tento příkaz vytvoří mnohem větší soubor (5,3MB) obsahující kromě přeložené aplikace i všechny potřebné knihovny:
ls -l target/*.war -rw-rw-r--. 1 ptisnovs ptisnovs 5472405 Apr 1 21:31 target/ringapp1-0.1.0-SNAPSHOT-standalone.war -rw-rw-r--. 1 ptisnovs ptisnovs 376389 Apr 1 21:30 target/ringapp1-0.1.0-SNAPSHOT.war
6. Plugin codox – tvorba dokumentace v HTML formátu
Třetí přídavný modul určený pro nástroj Leiningen, s nímž se v dnešním článku seznámíme, se jmenuje codox. Tento plugin je v současnosti velmi populární, protože slouží pro tvorbu programové dokumentace pro mnoho knihoven naprogramovaných v Clojure. Dokumentace je vygenerována do souborů HTML, jedná se tedy o obdobu JavaDocu (tvorba programové dokumentace pro javovské aplikace) popř. Doxygenu (podobný nástroj určený pro programovací jazyky C, C++, Python, PHP atd.), samozřejmě s tím rozdílem, že codox pracuje se jmennými prostory, funkcemi, globálními deklaracemi i – alespoň do určité míry – s makry.
Podívejme se nyní na to, jak může vygenerovaná dokumentace vypadat. Vytvoříme novou aplikaci:
lein new app codoxtest
V aplikaci vytvoříme tři moduly: src/codoxtest/core.clj, src/codoxtest/module_a.clj a src/codoxtest/module_b.clj s prakticky stejnými proměnnými, makrem a funkcemi:
(ns codoxtest.core "Popis modulu 'core'") (defn -main "I don't do a whole lot ... yet." [& args] (println "Hello, World!")) (defn f1 "Popis funkce f1." []) (defn f2 "Popis funkce f2." [x] (inc x)) (defn f3 "Popis funkce f2." [x y] (+ x y)) (def answer "The Answer to the Ultimate Question of Life, the Universe, and Everything" 42) (defmacro unless "Makro prevzate z dokumentace Clojure." [pred a b] `(if (not ~pred) ~a ~b))
(ns codoxtest.module-a "Popis modulu se jmenem 'module-a'") (defn f1 "Popis funkce f1." []) (defn f2 "Popis funkce f2." [x] (inc x)) (defn f3 "Popis funkce f2." [x y] (+ x y)) (def answer "The Answer to the Ultimate Question of Life, the Universe, and Everything" 42) (defmacro unless "Makro prevzate z dokumentace Clojure." [pred a b] `(if (not ~pred) ~a ~b))
(ns codoxtest.module-b "Popis modulu se jmenem 'module-b'") (defn f1 "Popis funkce f1." []) (defn f2 "Popis funkce f2." [x] (inc x)) (defn f3 "Popis funkce f2." [x y] (+ x y)) (def answer "The Answer to the Ultimate Question of Life, the Universe, and Everything" 42) (defmacro unless "Makro prevzate z dokumentace Clojure." [pred a b] `(if (not ~pred) ~a ~b))
Projektový soubor project.clj musí vypadat následovně (povšimněte si nového pluginu):
(defproject codoxtest "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 codoxtest.core :target-path "target/%s" :plugins [[codox "0.8.11"]] :profiles {:uberjar {:aot :all}})
Vygenerování dokumentace se provede příkazem:
lein doc Generated HTML docs in /home/ptisnovsk/repos/clojure-examples/codoxtest/doc
Vygenerovanou dokumentaci je možné nalézt na adrese https://github.com/tisnik/clojure-examples/tree/master/codoxtest/doc.

Obrázek 1: Vygenerovaná dokumentace – seznam všech modulů.

Obrázek 2: Vygenerovaná dokumentace – popis vybraného modulu.
7. Plugin cloverage – zjištění pokrytí kódu testy
Posledním pluginem určeným pro nástroj Leiningen, který si dnes popíšeme, je modul nazvaný cloverage. Úkolem tohoto modulu je zjištění, které části programového kódu jsou pokryté testy, tj. opět se jedná o analogii k podobným nástrojům existujícím i pro další programovací jazyky, ovšem s tím rozdílem, že kvůli použití maker je zjištění pokrytí testy v programovacím jazyku Clojure složitější.
Funkci tohoto pluginu otestujeme jednoduše – použijeme upravený projekt pro výpočet faktoriálu, do nějž jsou přidány další dvě totožné funkce, které se od sebe odlišují pouze jménem:
(ns cloverage.core (:gen-class)) (defn factorial [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn factorial2 [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn factorial3 [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn -main "I don't do a whole lot ... yet." [& args] (doseq [i (range 0 10)] (println i "! = " (factorial i))))
Projektový soubor project.clj musí vypadat následovně (opět si povšimněte nového pluginu):
(defproject cloverage "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 cloverage.core :target-path "target/%s" :plugins [[lein-cloverage "1.0.2"]] :profiles {:uberjar {:aot :all}})
Nejzajímavější jsou jednotkové testy. Povšimněte si, že funkce factorial1 je otestována celá, tj. včetně obou větví, funkce factorial2 je otestována jen částečně (pouze jedna větev) a nakonec funkce factorial3 není otestována vůbec. Tyto rozdíly by se měly projevit ve výsledcích:
(ns cloverage.core-test (:require [clojure.test :refer :all] [cloverage.core :refer :all])) (deftest factorial-test (testing "Factorial" (is ( = (factorial 0) 1) "beginning") (is ( = (factorial 1) 1) "beginning") (is ( = (factorial 2) (* 1 2)) "still easy") (is ( = (factorial 5) (* 1 2 3 4 5)) "5!") (is ( = (factorial 6) 720) "6!"))) (deftest factorial2-test (testing "Factorial" (is ( = (factorial2 0) 1) "beginning") (is ( = (factorial2 1) 1) "beginning") (is ( = (factorial2 2) (* 1 2)) "still easy") (is ( = (factorial2 5) (* 1 2 3 4 5)) "5!") (is ( = (factorial2 6) 720) "6!"))) (deftest exception-test (testing "If factorial throws exception" (is (thrown? IllegalArgumentException (factorial -1))) (is (thrown? IllegalArgumentException (factorial -2))) (is (thrown? IllegalArgumentException (factorial -100)))))
Nyní je nutné spustit novou úlohu:
lein cloverage Loading namespaces: (cloverage.core) Test namespaces: (cloverage.core-test) Loaded cloverage.core . Instrumented namespaces. Testing cloverage.core-test Ran 3 tests containing 13 assertions. 0 failures, 0 errors. Ran tests. Produced output in /home/ptisnovsk/repos/clojure-examples/cloverage/target/coverage . HTML: file:///home/ptisnovsk/repos/clojure-examples/cloverage/target/coverage/index.html
Podívejme se nyní na výsledky – zdá se, že skutečně odpovídají testům:

Obrázek 3: Pokrytí zdrojového kódu testy – celková statistika.

Obrázek 4: Pokrytí zdrojového kódu testy – zelené řádky byly vyhodnoceny, červené nikoli (bílé řádky nepředstavují zdrojový kód).
8. Repositář s demonstračními příklady
Všechny dnes popsané demonstrační příklady byly, podobně jako v minulé i předminulé části tohoto článku, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/clojure-examples (dříve popsané příklady budou přidány později). V tabulce zobrazené pod tímto odstavcem naleznete na jednotlivé příklady přímé odkazy:
# | Příklad | Github |
---|---|---|
1 | factorial | https://github.com/tisnik/clojure-examples/tree/master/factorial |
2 | leinring1 | https://github.com/tisnik/clojure-examples/tree/master/leinring1 |
3 | codoxtest | https://github.com/tisnik/clojure-examples/tree/master/codoxtest |
4 | cloverage | https://github.com/tisnik/clojure-examples/tree/master/cloverage |
9. Odkazy na předchozí části seriálu
- Leiningen: nástroj pro správu projektů napsaných v Clojure
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/ - Programovací jazyk Clojure a databáze (1.část)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
10. Odkazy na Internetu
- Plugin cloverage
https://github.com/lshift/cloverage - Plugin codox
https://github.com/weavejester/codox - Plugin lein-ring
https://github.com/weavejester/lein-ring - Plugin test2junit
https://github.com/ruedigergad/test2junit - java.jdbc API Reference
https://clojure.github.io/java.jdbc/ - Hiccup
https://github.com/weavejester/hiccup - Clojure Ring na GitHubu
https://github.com/ring-clojure/ring - A brief overview of the Clojure web stack
https://brehaut.net/blog/2011/ring_introduction - Getting Started with Ring
http://www.learningclojure.com/2013/01/getting-started-with-ring.html - Getting Started with Ring and Compojure – Clojure Web Programming
http://www.myclojureadventure.com/2011/03/getting-started-with-ring-and-compojure.html - Unit Testing in Clojure
http://nakkaya.com/2009/11/18/unit-testing-in-clojure/ - Testing in Clojure (Part-1: Unit testing)
http://blog.knoldus.com/2014/03/22/testing-in-clojure-part-1-unit-testing/ - API for clojure.test – Clojure v1.6 (stable)
https://clojure.github.io/clojure/clojure.test-api.html - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - 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/ - 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/ - 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/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - 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/ - 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/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - 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/ - 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/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - 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/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/