Enlive – výkonný šablonovací systém pro jazyk Clojure

Pavel Tišnovský 17. 9. 2015

Při popisu knihovny Hiccup jsme se seznámili s možností generování dynamických HTML stránek v jazyce Clojure. Jednalo se o využití doménově specifického jazyka. Opačný postup nalezneme v knihovně Enlive, kterou je možné považovat za kombinaci šablonovacího systému a nástroje pro transformaci HTML stránek.

Obsah

1. Enlive – výkonný šablonovací systém pro jazyk Clojure

2. Běžné šablonovací systémy

3. Přístup použitý v knihovně Enlive

4. První demonstrační příklad: aplikace jednoduché šablony

5. Další typy selektorů

6. Druhý demonstrační příklad: výběr tagů na základě jejich třídy a ID

7. Snippety použitelné pro výpis sekvencí do HTML stránky

8. Třetí demonstrační příklad: použití snippetu pro výpis seznamu, explicitní selektory

9. Repositář s dnešními demonstračními příklady

10. Odkazy na předchozí části tohoto seriálu

11. Odkazy na Internetu

1. Enlive – výkonný šablonovací systém pro jazyk Clojure

Při popisu tvorby serverových aplikací s využitím programovacího jazyka Clojure jsme se seznámili s dvojicí důležitých a v mnoha projektech i užitečných knihoven. První knihovna se jmenuje Clojure Ring a jejím hlavním úkolem je zajistit čtení a zpracování požadavků přicházejících od klienta a následně odeslat odpověď serveru zpět klientovi. Díky jasně vymezenému úkolu (od requesturesponse), který má tato knihovna zajišťovat, bylo možné provést její návrh čistě funkcionálně a bez nutnosti návrhu a implementace složitého a rozsáhlého API, o čemž jsme se ostatně mohli přesvědčit na několika demonstračních příkladech. Serverové aplikace, které mají nějakým způsobem komunikovat s webovým prohlížečem na straně klienta, však musí dokázat dynamicky generovat HTML stránky (popř. poskytovat data pro tyto stránky, například ve formátu JSON). V tomto případě si již s možnostmi nabízenými knihovnou Clojure Ring nevystačíme, což je korektní, protože generování obsahu vlastně nemá nic společného se zpracováním požadavků a odpovídáním na ně (Clojure Ring se nesnaží o napodobení některých monstrózních webových frameworků).

Pro dynamické generování HTML stránek na straně serveru v programovacím jazyce Clojure lze využít hned několik knihoven, které se od sebe odlišují způsobem, jakým je „předepsána“ výsledná podoba stránky. Jedna z možností spočívá ve využití připravených šablon HTML stránek, do nichž se pouze na určená místa doplňují dynamicky generovaná data. Jedná se pravděpodobně o nejpoužívanější postup, který byl nejprve implementován v PHP a později se rozšířil i do dalších programovacích jazyků a jejich frameworků (JSP atd. atd.). Použití šablon HTML stránek samozřejmě přináší mnohé výhody, například možnost oddělení práce designera stránek od programátora, ovšem ve skutečnosti existuje ještě další způsob, který je podporován v knihovně s názvem Hiccup. Tato knihovna totiž obsahuje několik funkcí, které na svém vstupu dostanou běžný vektor či seznam jazyka Clojure a na základě dat uložených v tomto vektoru/listu vytvoří korektní podobu HTML stránky. Na první pohled sice tento postup může vypadat složitě, ve skutečnosti je však v mnoha případech velmi efektivní, zejména ve chvíli, kdy je prakticky celý design stránek umístěn v samostatných CSS.

Jen pro připomenutí způsobu, jakým se s knihovnou Hiccup pracuje, se podívejme na příklad, kde se DSL (doménově specifický jazyk) podporovaný knihovnou Hiccup používá. Jedná se o jednoduchý program, který po svém spuštění vytvoří novou HTML stránku uloženou do souboru nazvaného „test.html“. Tato stránka bude obsahovat tabulku s hodnotami faktoriálu. Vidíme, že funkci hiccup.page/xhtml se předává vektor obsahující symbolicky zapsanou kostru stránky; uvnitř samozřejmě můžeme použít i programový kód (což není vlastnost knihovny Hiccup, ale homoikonického jazyka Clojure):

(ns htmltest2.core
    (:gen-class))
 
(require '[hiccup.page :as page])
 
(defn fact
    [n]
    (apply * (range 1 (inc n))))
 
(defn html-page
    []
    (page/xhtml
        [:head
            [:title "Hiccup test #2"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Hiccup test #2"]
            [:table
                [:tr [:th "n"] [:th "n!"]]
                (for [n (range 0 20)]
                    [:tr [:td n] [:td (fact n)]])
            ]
        ]))
 
(defn -main
    [& args]
    (spit "test.html" (html-page)))

2. Běžné šablonovací systémy

Zcela odlišný způsob zvolili autoři knihovny Enlive, jejímž stručným popisem se budeme zabývat dnes. Namísto návrhu doménově specifického jazyka kombinujícího programový kód (výše se jedná o výpočet faktoriálu) se statickou částí představovanou symboly :head, :body, :h1, :div atd., je knihovna Enlive založena na HTML šablonách, které se na základě zadaných selektorů (v mnoha ohledech podobných selektorům používaným v CSS) transformují do podoby výsledné HTML stránky. Ovšem šablony v podání knihovny Enlive se v jednom ohledu liší od šablon, které můžeme znát například z různých frameworků pro programovací jazyky PHP, Python (Cheetah), Javu (JSP: JavaServer Pages, JSTL: JavaServer Pages Standard Tag Library) atd. Například ve zmíněných frameworcích Cheetah a JSP (JSTL) se přímo v šabloně kombinuje statický HTML kód se značkami šablonovacího systému či přímo s programovým kódem tak, jak to známe již z prvních verzí PHP.

Podívejme se na příklady, jak takové šablony kombinující statické HTML a „dynamickou“ část měněnou šablonovacím systémem mohou vypadat (příklady byly použity v dokumentaci pro framework Cheetah):

  <html>
    <head><title>$title</title></head>
    <body>
      <table>
        #for $client in $clients
        <tr>
          <td>$client.surname, $client.firstname</td>
          <td><a href="mailto:$client.email">$client.email</a></td>
        </tr>
        #end for
      </table>
    </body>
  </html>
  <html>
    <head><title><%=title%></title></head>
    <body>
      <table>
        <% for client in clients: %>
        <tr>
          <td><%=client['surname']%>, <%=client'[firstname']%></td>
      <td><a href="mailto:<%=client['email']%>">
           <%=client['email']%></a></td>
        </tr>
        <%end%>
      </table>
    </body>
  </html>

3. Přístup použitý v knihovně Enlive

Naproti tomu se v knihovně Enlive jako šablona použije zcela běžná HTML stránka, kde pouze musíme zajistit, aby se ty tagy, jejichž obsah se má změnit, daly jednoznačně najít pomocí selektoru. Nejjednodušší je použití ID prvků/tagů, existují však i další možnosti, o nichž se dále zmíníme. Toto je příklad šablony pro Enlive:

  <html>
    <head><title>jakýkoli titulek, který se změní</title></head>
    <body>
      <table id="clients">
        <tr id="client">
          <td id="name">vyplní se později</td>
          <td id="address"><a href="">i adresa se vyplní později</a></td>
        </tr>
      </table>
    </body>
  </html>

Jak je patrné, tak největším rozdílem mezi původními dvěma šablonami a šablonou poslední je fakt, že v poslední šabloně (určené pro knihovnu Enlive) nenajdeme žádnou speciální značku ani vkládaný kód. Tuto šablonu lze vytvořit v jakémkoli nástroji pro tvorbu HTML stránek a může ji vytvořit designér prakticky bez konzultace s vývojářem (výjimkou jsou ID u prvků, ale ani to by v tomto případě nebylo nutné, jak uvidíme za chvíli). Dokonce ani není nutné nechávat ty tagy, jejichž obsah se má měnit, prázdné, což je opět výhoda – designér totiž může vyzkoušet například i to, zda se do daného místa na stránce skutečně všechny požadované údaje vejdou, samotná šablona se použije jako mockup atd. atd.

4. První demonstrační příklad: aplikace jednoduché šablony

Před zdlouhavým vysvětlováním, jak přesně se šablona transformuje na výslednou HTML stránku, si ukažme funkční demonstrační příklad. Kostra příkladu se vytvoří jednoduše nám již známým příkazem:

lein new app enlive1

Do projektového souboru se přidá knihovna Enlive:

(defproject enlive1 "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"]
                   [enlive "1.1.1"]]
    :main ^:skip-aot enlive1.core
    :target-path "target/%s"
    :profiles {:uberjar {:aot :all}})

Do adresáře resources (to je důležité a nesmíme na to zapomenout!) se přidá šablona, což je běžná a ničím zvláštní HTML stránka:

<html>
    <head>
        <title>Testovaci stranka</title>
        <meta name="Author" content="Pavel Tisnovsky">
        <meta name="Generator" content="vim">
    </head>
    <body>
        <h1>Testovaci stranka</h1>
        <div>
            Text v prvnim odstavci.
        </div>
        <div>
            Text ve druhem odstavci.
        </div>
    </body>
</html>

Nejdůležitější je samozřejmě vlastní zdrojový kód, kde se provádí transformace šablony na výslednou HTML stránku:

(ns enlive1.core
    (:gen-class))
 
(require '[net.cgrand.enlive-html :as html])
 
(html/deftemplate test-page "test.html"
    [data-for-page]
    [:title] (html/content (:title data-for-page))
    [:h1]    (html/content (:title data-for-page))
    [:div]   (html/content (:paragraph data-for-page)))
 
(def new-data
    {:title "Zcela novy titulek stranky"
     :paragraph "xyzzy"
    })
 
(defn -main
    [& args]
    (println (reduce str (test-page new-data))))

Tento zdrojový kód obsahuje jednu funkci (-main), jednu mapu navázanou na jméno new-data a konečně deklaraci transformace šablony, kterou jsme nazvali test-page. Tato část (jak jste si pravděpodobně všimli, jedná se o makro a nikoli o funkci) je nejdůležitější, protože je v ní deklarováno:

  • Obsah všech tagů <title> nahraď daty, která najdeš v předané mapě pod klíčem :title (ovšem tento tag bude na stránce umístěn jen jedenkrát, a to konkrétně v hlavičce).
  • Obsah všech tagů <h1> nahraď stejným obsahem. Zde konkrétně se bude jednat o řetězec „Zcela novy titulek stranky“ a tag se taktéž vyskytuje pouze jednou.
  • Obsah všech tagů <div> nahraď daty, která najdeš v předané mapě pod klíčem :paragraph. Konkrétně budou nahrazeny obsahy obou odstavců.

Ještě jednodušeji by bylo možné šablonu nadeklarovat takto (nyní bez dynamicky předávaných dat):

(html/deftemplate test-page "test.html"
    [neni-zapotrebi]
    [:title] (html/content "A jeste jiny titulek stranky")
    [:h1]    (html/content "Tak tak, dalsi titule")
    [:div]   (html/content "Novy obsah odstavce"))

Navíc si povšimněte transformace výsledku šablony test-page na řetězec – jednoduše spojíme celou sekvenci postupnou aplikací funkce str. Po spuštění získáme následující výstup:

<html>
    <head>
        <title>Zcela novy titulek stranky</title>
        <meta content="Pavel Tisnovsky" name="Author" />
        <meta content="vim" name="Generator" />
    </head>
    <body>
        <h1>Zcela novy titulek stranky</h1>
        <div>xyzzy</div>
        <div>xyzzy</div>
    </body>
</html>

5. Další typy selektorů

V předchozím příkladu jsme používali velmi jednoduché selektory typu [:title] či [:div] (vždy se jedná o vektory). K dispozici jsou však i další možnosti, jak vybrat ty tagy, s nimiž se má manipulovat:

# Selektor Význam
1 [:div] všechny elementy <div>
2 [:title] všechny elementy <title> (tento element se vyskytuje jen jedenkrát)
3 [:div.danger] všechny elementy <div> s třídou „danger“
4 [:div#summary] všechny elementy <div> s ID „summary“ (opět by se mělo jednat jen o jediný element)
5 [:div :span] výběr vnitřního tagu <span> umístěného v elementu <div>
6 [:div.menu :ul :li :span] kombinace předchozích dvou možnosti
7 [:table#userlist :tr :td] kombinace předchozích dvou možnosti (zde praktičtější)

Možností výběrů elementů je ještě více, o některých z nich se zmíníme příště.

6. Druhý demonstrační příklad: výběr tagů na základě jejich třídy a ID

Ve druhém demonstračním příkladu budou elementy (tagy) vybírány na základě jejich třídy a popř. ID (což je mnohem praktičtější). Projektový soubor vypadá následovně:

(defproject enlive2 "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"]
                   [enlive "1.1.1"]]
    :main ^:skip-aot enlive2.core
    :target-path "target/%s"
    :profiles {:uberjar {:aot :all}})

Šablona umístěná v adresáři resources se odlišuje od šablony, s níž jsme se setkali v prvním příkladu. Povšimněte si toho, že některé odstavce (<div>) mají přiřazeno ID a některé zase třídu či třídy:

<html>
    <head>
        <title>Testovaci stranka</title>
        <meta name="Author" content="Pavel Tisnovsky">
        <meta name="Generator" content="vim">
    </head>
    <body>
        <h1>Testovaci stranka</h1>
        <div id="paragraph1">
            Text v prvnim odstavci.
        </div>
        <div id="paragraph2">
            Text ve druhem odstavci.
        </div>
        <div class="paragraphs">
            Text ve tretim odstavci.
        </div>
        <div class="classX, paragraphs">
            Text ve ctvrtem odstavci.
        </div>
    </body>
</html>

Vlastní zdrojový kód používá v makru html/deftemplate odlišné typy selektorů. První odstavec je vybrán podle svého ID „paragraph1“, druhý odstavec taktéž, kdežto další selektor zafunguje na odstavce se třídou „paragraphs“:

(ns enlive2.core
    (:gen-class))
 
(require '[net.cgrand.enlive-html :as html])
 
(html/deftemplate test-page "test.html"
    [data-for-page]
    [:title] (html/content (:title data-for-page))
    [:h1]    (html/content (:title data-for-page))
    [:div#paragraph1]   (html/content (:paragraph1 data-for-page))
    [:div#paragraph2]   (html/content (:paragraph2 data-for-page))
    [:div.paragraphs]   (html/content (:paragraphs data-for-page))
    )
 
(def new-data
    {:title "Zcela novy titulek stranky"
     :paragraph1 "xyzzy"
     :paragraph2 ""
     :paragraphs "42"
    })
 
(defn -main
    [& args]
    (println (reduce str (test-page new-data))))

Zajímavé se bude podívat na výsledek, především na to, zda poslední selektor vybere i odstavec, k němuž jsou přiřazeny dvě třídy:

<html>
    <head>
        <title>Zcela novy titulek stranky</title>
        <meta content="Pavel Tisnovsky" name="Author" />
        <meta content="vim" name="Generator" />
    </head>
    <body>
        <h1>Zcela novy titulek stranky</h1>
        <div id="paragraph1">xyzzy</div>
        <div id="paragraph2"></div>
        <div class="paragraphs">42</div>
        <div class="classX, paragraphs">42</div>
    </body>
</html>

Vidíme, že šablona pracuje korektně (tedy alespoň zatím :-).

7. Snippety použitelné pro výpis sekvencí do HTML stránky

V obou předchozích demonstračních příkladech se provádělo nahrazení obsahu explicitně zadaných elementů (tagů) nějakými skalárními daty. V praxi by se například mohlo jednat o políčko se jménem přihlášeného uživatele, políčko s celkovou cenou nákupu atd. Ovšem v mnoha aplikacích musíme umět pracovat i se sekvencí dat. Může se například jednat o požadavek zobrazení seznamu uživatelů, zobrazení tabulky se zbožím (nákupním košíkem) atd. V tradičně pojatých šablonovacích systémech se v tomto případě používají programové smyčky, ať již zapsané explicitně (viz druhou kapitolu s ukázkami) nebo s využitím nějakých speciálních značek (v JSTL se například používá speciální značka <c:forEach>). Ovšem v knihovně Enlive tuto možnost nemáme, a to z toho prostého důvodu, že šablonou je běžný HTML soubor. Jak tedy implementovat například požadavek na zobrazení seznamu herců v nějaké divadelní hře? Řešením jsou takzvané snippety, u nichž se specifikuje počáteční a koncový element v HTML a poté již snippet dokáže opakovat tu část HTML stránky, která je uvnitř specifikovaných elementů.

8. Třetí demonstrační příklad: použití snippetu pro výpis seznamu, explicitní selektory

Použití jednoduchého snippetu bude ukázáno v dnešním třetím a současně i posledním demonstračním příkladu, který by po svém spuštění měl vypsat herecké obsazení v divadelní hře „Vražda v salonním coupé“. V reálné aplikaci by se tato data načetla z databáze či přenesla z nějakého jiného systému, my si pro jednoduchost vystačíme s vektorem deklarovaným přímo ve zdrojovém kódu.

Vytvoření kostry demonstračního příkladu:

lein new app enlive3

Projektový soubor třetího demonstračního příkladu se nijak zvláště neodlišuje od obou předchozích projektových souborů:

(defproject enlive3 "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"]
                   [enlive "1.1.1"]]
    :main ^:skip-aot enlive3.core
    :target-path "target/%s"
    :profiles {:uberjar {:aot :all}})

Šablona je ovšem odlišná – máme zde „obalový“ odstavec s ID nastaveným na „roles“, v němž se nachází další odstavec s ID „role“. Tento odstavec bude ve výsledné HTML stránce zopakován tolikrát, kolik jmen herců do transformačního makra předáme:

<html>
    <head>
        <title>Testovaci stranka</title>
        <meta name="Author" content="Pavel Tisnovsky">
        <meta name="Generator" content="vim">
    </head>
    <body>
        <h1>Hra <span></span></h1>
        <div id="roles">
            <div id="role">
                <span id="actor">herec</span>
                <span id="character">postava</span>
            </div>
        </div>
        <div>Pokracovani</div>
    </body>
</html>

Zdrojový kód demonstračního příkladu obsahuje tabulku (vektor) s rolemi a dále pak datovou strukturu s titulkem divadelní hry a referencí na právě zmíněnou tabulku s rolemi:

(ns enlive3.core
    (:gen-class))
 
(require '[net.cgrand.enlive-html :as html])
 
(def roles [
    {:actor-name "Zdeněk Svěrák"   :character "inspektor Trachta  "}
    {:actor-name "Petr Brukner"    :character "praktikant Hlaváček"}
    {:actor-name "Miloň Čepelka"   :character "praktikant Hlaváček"}
    {:actor-name "Bořivoj Penc"    :character "továrník Bierhanzel"}
    {:actor-name "Jaroslav Weigel" :character "továrník Bierhanzel"}
    {:actor-name "Jan Hraběta"     :character "továrník Meyer"}
    {:actor-name "Václav Kotek"    :character "steward"}
    {:actor-name "Genadij Rumlena" :character "steward"}])
 
(def vrazda-v-salonnim-coupe
    {:title "Vražda v salonním coupé"
     :roles roles
    })

Nyní následuje důležitější část. Nejprve snippet, v němž je deklarováno, který element a případné podelementy se budou opakovat. Deklarováno je, že opakovat se bude jen odstavec s ID nastaveným na „role“, pravidla pro vyplňování daty (selektor+způsob naplnění) již známe:

(html/defsnippet one-record "test.html"
    {[:div#role]     ; zacatek
     [:div#role]}    ; konec
    [record]
    [:span#actor]     (html/content (:actor-name record))
    [:span#character] (html/content (:character record)))

Vlastní deklarace transformace šablony se taktéž změnila, a to kvůli poslednímu řádku, kterým je deklarováno (zjednodušeně řečeno) opakování elementů umístěných v odstavci s ID nastaveným na „roles“:

(html/deftemplate test-page "test.html"
    [data-for-page]
    [:title]      (html/content (:title data-for-page))
    [:h1 :span]   (html/content (:title data-for-page))
    [:div#roles]  (html/content (map one-record (:roles data-for-page))) ; vnitrek odstavce bude duplikovan
)
 
(defn -main
    [& args]
    (println (reduce str (test-page vrazda-v-salonnim-coupe))))

Výsledek běhu demonstračního příkladu (povšimněte si opakování IDček):

widgety

<html>
    <head>
        <title>Vražda v salonním coupé</title>
        <meta content="Pavel Tisnovsky" name="Author" />
        <meta content="vim" name="Generator" />
    </head>
    <body>
        <h1>Hra <span>Vražda v salonním coupé</span></h1>
        <div id="roles"><div id="role">
                <span id="actor">Zdeněk Svěrák</span>
                <span id="character">inspektor Trachta  </span>
            </div><div id="role">
                <span id="actor">Petr Brukner</span>
                <span id="character">praktikant Hlaváček</span>
            </div><div id="role">
                <span id="actor">Miloň Čepelka</span>
                <span id="character">praktikant Hlaváček</span>
            </div><div id="role">
                <span id="actor">Bořivoj Penc</span>
                <span id="character">továrník Bierhanzel</span>
            </div><div id="role">
                <span id="actor">Jaroslav Weigel</span>
                <span id="character">továrník Bierhanzel</span>
            </div><div id="role">
                <span id="actor">Jan Hraběta</span>
                <span id="character">továrník Meyer</span>
            </div><div id="role">
                <span id="actor">Václav Kotek</span>
                <span id="character">steward</span>
            </div><div id="role">
                <span id="actor">Genadij Rumlena</span>
                <span id="character">steward</span>
            </div></div>
        <div>Pokracovani</div>
    </body>
</html>

O způsobech vylepšení tohoto příkladu se zmíníme příště.

9. Repositář s dnešními demonstračními příklady

Všechny tři dnes zmíněné demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/clojure-examples. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy jednotlivých demonstračních příkladů přímé odkazy:

10. Odkazy na předchozí části tohoto seriálu

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    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 (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/
  10. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/
  11. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/
  12. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/
  13. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/
  14. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  15. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/
  16. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/
  17. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  18. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  19. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  20. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  21. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  22. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  23. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/
  24. Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech
    http://www.root.cz/clanky/programovaci-jazyk-clojure-nektere-uzitecne-triky-pouzitelne-nejenom-v-testech/

11. Odkazy na Internetu

  1. Clojure Cookbook: Templating HTML with Enlive
    https://github.com/clojure-cookbook/clojure-cookbook/blob/master/07_webapps/7–11_enlive.asciidoc
  2. An Introduction to Enlive
    https://github.com/swannodette/enlive-tutorial/
  3. Enlive na GitHubu
    https://github.com/cgrand/enlive
  4. Expectations: příklady atd.
    http://jayfields.com/expectations/
  5. Expectations na GitHubu
    https://github.com/jaycfi­elds/expectations
  6. Lein-expectations na GitHubu
    https://github.com/gar3thjon3s/lein-expectations
  7. Testing Clojure With Expectations
    https://semaphoreci.com/blog/2014/09/23/tes­ting-clojure-with-expectations.html
  8. Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
    https://www.reddit.com/r/Clo­jure/comments/1viilt/cloju­re_testing_tddbdd_librari­es_clojuretest_vs/
  9. Testing: One assertion per test
    http://blog.jayfields.com/2007/06/tes­ting-one-assertion-per-test.html
  10. Rewriting Your Test Suite in Clojure in 24 hours
    http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/
  11. Clojure doc: zipper
    http://clojuredocs.org/clo­jure.zip/zipper
  12. Clojure doc: parse
    http://clojuredocs.org/clo­jure.xml/parse
  13. Clojure doc: xml-zip
    http://clojuredocs.org/clojure.zip/xml-zip
  14. Clojure doc: xml-seq
    http://clojuredocs.org/clo­jure.core/xml-seq
  15. Parsing XML in Clojure
    https://github.com/clojuredocs/guides
  16. Clojure Zipper Over Nested Vector
    https://vitalyper.wordpres­s.com/2010/11/23/clojure-zipper-over-nested-vector/
  17. Understanding Clojure's PersistentVector implementation
    http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation
  18. Understanding Clojure's PersistentHashMap (deftwice…)
    http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html
  19. Assoc and Clojure's PersistentHashMap: part ii
    http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html
  20. Ideal Hashtrees (paper)
    http://lampwww.epfl.ch/pa­pers/idealhashtrees.pdf
  21. Clojure home page
    http://clojure.org/
  22. Clojure (downloads)
    http://clojure.org/downloads
  23. Clojure Sequences
    http://clojure.org/sequences
  24. Clojure Data Structures
    http://clojure.org/data_structures
  25. The Structure and Interpretation of Computer Programs: 2.2.1 Representing Sequences
    http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-15.html#%_sec2.2.1
  26. The Structure and Interpretation of Computer Programs: 3.3.1 Mutable List Structure
    http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-22.html#%_sec3.3.1
  27. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  28. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  29. 4Clojure
    http://www.4clojure.com/
  30. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  31. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  32. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  33. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  34. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  35. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  36. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  37. Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-i-getting.html
  38. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  39. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  40. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  41. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  42. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  43. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  44. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  45. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  46. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  47. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  48. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  49. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  50. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  51. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  52. Třída java.lang.String
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­g.html
  53. Třída java.lang.StringBuffer
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuffer.html
  54. Třída java.lang.StringBuilder
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuilder.html
  55. StringBuffer versus String
    http://www.javaworld.com/ar­ticle/2076072/build-ci-sdlc/stringbuffer-versus-string.html
  56. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  57. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  58. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  59. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  60. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  61. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  62. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  63. Leiningen: úvodní stránka
    http://leiningen.org/
  64. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  65. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  66. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  67. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  68. 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/
  69. 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/
  70. 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/
  71. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  72. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  73. 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/
  74. 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/
  75. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  76. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  77. 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/
  78. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  79. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  80. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  81. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  82. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  83. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  84. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  85. 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/
  86. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  87. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  88. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  89. Clojure.org: Atoms
    http://clojure.org/Atoms
  90. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  91. Transient Data Structureshttp://clojure.or­g/transients
Našli jste v článku chybu?
Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

120na80.cz: Pálení žáhy: která jídla ne a co nás uzdraví?

Pálení žáhy: která jídla ne a co nás uzdraví?

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

Vitalia.cz: Voda z Vltavy před a po úpravě na pitnou

Voda z Vltavy před a po úpravě na pitnou

DigiZone.cz: Numan Two: rozhlasový přijímač s CD

Numan Two: rozhlasový přijímač s CD

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

Podnikatel.cz: Byla finanční manažerka, teď cvičí jógu

Byla finanční manažerka, teď cvičí jógu

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

Podnikatel.cz: Tyto pojmy k #EET byste měli znát

Tyto pojmy k #EET byste měli znát

DigiZone.cz: Digi Slovakia zařazuje stanice SPI

Digi Slovakia zařazuje stanice SPI

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

Vitalia.cz: Pryč se zastaralým stravováním ve školách

Pryč se zastaralým stravováním ve školách

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

Vitalia.cz: Jsou vegani a vyrábějí nemléko

Jsou vegani a vyrábějí nemléko