Hlavní navigace

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

Pavel Tišnovský

Šestá část článku (vlastně již seriálu) o využití nástroje Leiningen a programovacího jazyka Clojure je věnována převážně knihovně Hiccup, s jejímž použitím lze poměrně snadno generovat HTML stránky. Hiccup je navíc pěkným a současně i velmi jednoduchým příkladem doménově specifického jazyka (DSL).

Obsah

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

2. Generování HTML stránek s využitím knihovny Hiccup

3. První demonstrační příklad – koncepty, na němž je postavena knihovna Hiccup

4. Druhý demonstrační příklad – kombinace statických a programově generovaných dat

5. Třetí demonstrační příklad – nastavení stylů k vybraným prvkům

6. Čtvrtý demonstrační příklad – formulář vytvořený s využitím hiccup.form

7. Vytvoření jednoduché webové aplikace založené na Clojure RingHiccup

8. Zpracování požadavku

9. Dynamické generování HTML stránky

10. Úplný zdrojový kód demonstračního příkladu webapp13

11. Repositář s demonstračními příklady

12. Odkazy na Internetu

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

Knihovna Clojure Ring, která byla popsaná v předchozích částech článku o nástroji Leiningen i o programovacím jazyku Clojure, má jediný úkol – zajistit čtení a zpracování požadavků přicházejících od klienta a následně odeslat odpověď serveru zpět ke klientovi. Díky jasně vymezenému úkolu, který má knihovna Clojure Ring 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. Ve skutečnosti nám však pro tvorbu skutečných (rozsáhlejších) webových aplikacích schází minimálně dvě funkce – podpora pro vytváření HTML stránek posílaných zpět klientovi a taktéž podpora pro zápis popř. i pro dynamickou změnu pravidel pro rozhodování, jaké funkce (handlery) se mají volat pro jednotlivé dotazy (přihlášení do aplikace, registrace nového uživatele atd.). Dnes se budeme zabývat především prvním zmíněným tématem, tj. tvorbou HTML stránek posílaných serverem zpět ke klientovi. Pokud by bylo nutné posílat JSON či XML, je situace v některých ohledech ještě jednodušší a taktéž se o ní později zmíníme.

2. Generování HTML stránek s využitím knihovny Hiccup

Tvorba HTML stránek, resp. přesněji řečeno textových řetězců odpovídajících HTML stránce poslané klientovi, se může provádět několika způsoby. S nejprimitivnějším způsobem jsme se již seznámili v předchozích dvou částech tohoto článku – HTML stránka byla představovaná řetězcovým literálem popř. řetězcem sestaveným z více částí díky použití funkce str. Příkladem může být tato webová aplikace (resp. její handler):

(defn create-html-page
    "Vygenerovani HTML stranky."
    [counter]
    (str
"    <html>
         <head>
             <title>Powered by Ring!</title>
         </head>
         <body>
             <h1>Powered by Ring!</h1>
             <p>Counter: " counter "</p>
         </body>
     </html>
"))
 
(defn handler
    "Zpracovani pozadavku."
    [request]
    (let [params      (:params  request)
          session     (:session request)
          counter     (:counter session 0)]
        (println "Params:  " params)
        (println "Session: " session)
        (println "Counter: " counter)
        (-> (response/response (create-html-page counter))
            (response/content-type "text/html; charset=utf-8")
            (assoc :session {:counter (inc counter)}))))
 

Druhá 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 jazyků a 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 existuje ještě další způsob, kterým se budeme zabývat dnes a 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 ve 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.

3. První demonstrační příklad – koncepty, na němž je postavena knihovna Hiccup

Pro odzkoušení základních vlastností knihovny Hiccup vytvoříme demonstrační aplikaci nazvanou htmltest1, a to pomocí nám již známého příkazu:

lein new app htmltest1

Soubor s konfigurací projektu nazvaný project.clj je zapotřebí upravit následujícím způsobem – vektor uložený pod klíčem :dependencies je rozšířen o nový prvek se specifikací jména a verze požadované knihovny:

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

Příkazem:

lein deps

se zajistí stažení nové knihovny a popř. i dalších balíčků, na kterých tato knihovna závisí.

Samotný zdrojový kód aplikace je jednoduchý, protože aplikace má za úkol po svém spuštění vytvořit nový soubor nazvaný test.html. Připomeňme si, že pro uložení řetězce (či jiné hodnoty a datové struktury) do souboru se používá funkce spit:

(ns htmltest1.core
    (:gen-class))
 
(require '[hiccup.page :as page])
 
(defn html-page
    []
    (page/xhtml
        [:head
            [:title "Hiccup test #1"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Hiccup test #1"]
            [:div "Hello world!"]
        ]))
 
(defn -main
    [& args]
    (spit "test.html" (html-page)))

Nejzajímavější je tělo funkce html-page. Zde můžeme vidět volání další funkce hiccup.page/xhtml (alternativní možnosti jsou hiccup.page/html4, hiccup.page/html5 apod.). Tato funkce očekává jako parametr vektor či vektory a jejím výstupem je řetězec představující validní HTML či XHTML stránku. Obsah dvou vektorů předaných do hiccup.page/xhtml odpovídá stromové struktuře stránky, takže oba vektory obsahují jako své prvky další podvektory. První prvek těchto podvektorů je symbol, který svým názvem připomíná názvy značek v (X)HTML. Význam druhého a popř. dalších prvků již závisí na konkrétní značce – například u vektoru [:title „xxx“] je druhým prvkem obsah značky „title“, zatímco u vektoru [:meta] je druhým prvkem mapa s atributy.

4. Druhý demonstrační příklad – kombinace statických a programově generovaných dat

Předchozí demonstrační příklad byl vlastně velmi jednoduchý, protože funkce nazvaná html-page pouze prováděla transformaci z jedné reprezentace HTML stránky do reprezentace jiné. Ve skutečnosti jsou však možnosti, které má programátor využívající knihovnu Hiccup k dispozici, mnohem širší, a to především díky možnosti generovat část stromu reprezentujícího HTML stránku programově. Podívejme se na příklad druhý – ten po svém spuštění vytvoří soubor s HTML stránkou obsahující tabulku faktoriálů konstant 0..19. Ve funkci html-page je použita generátorová notace seznamu (sekvence) reprezentovaná funkcí for (pozor: nejedná se o programovou smyčku!). Datová struktura je „dotvořena“ až po zavolání funkce html-page. Právě díky možnosti kombinace statických dat a dynamicky generovaných dat je možné tvořit některé typy webových aplikací velmi snadno:

(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)))

5. Třetí demonstrační příklad – nastavení stylů k vybraným prvkům

Již v předchozích kapitolách jsme si řekli, že u symbolů ve vektoru představujícím „šablonu“ HTML stránky lze uvést nepovinný prvek reprezentující nějaké atributy výsledné HTML značky. Tento prvek je představován mapou, což je v syntaxi programovacího jazyka Clojure datová struktura začínající a končící složenou závorkou. V dnešním třetím demontračním příkladu je takových map, které se převededou na atributy HTML značek, použito větší množství. Příkladem může být tento řádek:

[:table {:style "border:2px solid brown;background-color:#ace"}

popř.:

[:td {:style "text-align:right"} "obsah buňky v tabulce"]

Při použití Bootstrapu se můžeme setkat například s následujícím zápisem:

[:div {:class "alert alert-danger"} "Not found"]
[:div {:class "alert alert-success"} description]
[:div {:class "col-md-10"} "foo"]
[:div {:class "label label-primary"} "bar"]

Kromě atributu :style lze samozřejmě používat i další atributy. Asi nejdůležitější je to u značek :a a :img, kde je nutné specifikovat adresu odkazu popř. adresu obrázku:

[:a {:href "/" :class "navbar-brand"} "Rychle pryč"]

Zdrojový kód třetího demontračního příkladu vypadá takto:

(ns htmltest3.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 #3"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Hiccup test #3"]
            [:table {:style "border:2px solid brown;background-color:#ace"}
                [:tr [:th "n"] [:th "n!"]]
                (for [n (range 0 20)]
                    [:tr [:td n] [:td {:style "text-align:right"} (fact n)]])
            ]
        ]))
 
(defn -main
    [& args]
    (spit "test.html" (html-page)))

6. Čtvrtý demonstrační příklad – formulář vytvořený s využitím hiccup.form

Nedílnou součástí prakticky všech webových aplikací jsou formuláře. Ty lze v knihovně Hiccup samozřejmě taktéž vytvářet, ovšem kromě jmenného prostoru hiccup.page se musí použít i jmenný prostor hiccup.form, v němž se (kromě jiného) nachází i funkce form/form-to. Podívejme se ihned na použití této funkce pro tvorbu formuláře, ve kterém je vytvořeno textové pole pojmenované „n“ a odesílací tlačítko. Tyto dva prvky nejsou kupodivu představovány symboly :text-field a :submit, ale funkcemi hiccup.form/text-fieldhiccup.form/submit-button:

(ns htmltest4.core
    (:gen-class))
 
(require '[hiccup.page :as page])
(require '[hiccup.form :as form])
 
(defn fact
    [n]
    (apply * (range 1 (inc n))))
 
(defn html-page
    []
    (page/xhtml
        [:head
            [:title "Hiccup test #4"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Hiccup test #4"]
            (form/form-to [:get "/"]
                (form/text-field {:size "20"} "n" 10)
                [:br]
                (form/submit-button "Recalculate"))
            [:br]
            [:table {:style "border:2px solid brown;background-color:#ace"}
                [:tr [:th "n"] [:th "n!"]]
                (for [n (range 0 20)]
                    [:tr [:td n] [:td {:style "text-align:right"} (fact n)]])
            ]
        ]))
 
(defn -main
    [& args]
    (spit "test.html" (html-page)))

V reálných aplikacích se lze samozřejmě setkat i s dalšími prvky, popř. s ostylovanými prvky formuláře, například:

(form/hidden-field "package" (str package))
(form/text-area {:cols "120" :rows "10"} "description" description)
(form/submit-button {:class "btn btn-danger"} "Update description")

7. Vytvoření jednoduché webové aplikace založené na Clojure RingHiccup

Nyní již máme dostatek informací k tomu, abychom vytvořili jednoduchou webovou aplikaci pro výpočet faktoriálů, v níž se o zpracování požadavků a odesílání odpovědí bude starat knihovna Clojure Ring a o vytváření HTML stránky (která je samozřejmě součástí odpovědi) pak knihovna Hiccup. Nejprve vytvoříme kostru nové aplikace:

lein new webapp13

A následně upravíme soubor s deklarací projektu takovým způsobem, aby byly zmíněny všechny potřebné externí knihovny:

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

Pro jistotu si necháme potřebné knihovny stáhnout a nainstalovat:

lein deps

8. Zpracování požadavku

Aplikace je rozdělena na několik částí. První část se stará o zpracování požadavku v handleru. Tuto problematiku již známe, protože jsme se jí dopodrobna věnovali již minule, takže jen pro úplnost:

(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}))

Samotný handler se stará o načtení a zpracování parametrů požadavku, v nichž se hledá parametr nazvaný max-n. Pokud tento parametr není nalezen, doplní se namísto něj hodnota 10M (jejíž typ nám zajistí výpočet prakticky neomezeného faktoriálu):

(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"))))
 

Funkci pro převod parametru (což je řetězec) na numerickou hodnotu jsme si již taktéž ukazovali, takže si jen pro úplnost doplňme její zdrojový kód:

(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

9. Dynamické generování HTML stránky

Výpočet faktoriálu již taktéž známe, nová je však úprava funkce html-page, které se předává hodnota parametru max-n. Tato hodnota je použita hned dvakrát. Povrvé pro automatické předvyplnění nové hodnoty ve formuláři:

(form/text-field {:size "20"} "max-n" max-n)

A podruhé pro vytvoření tabulky s faktoriálem pro vstupní hodnoty od nuly až po zadanou hodnotu. Povšimněte si zde použití funkce inc pro výpočet druhého parametru funkce range:

(for [n (range 0M (inc max-n))]
    [:tr [:td n] [:td {:style "text-align:right"} (fact n)]])

(znovu připomínám, že for je generátor seznamu/sekvence a nikoli programová smyčka)

Druhá část webové aplikace, která se má starat o vygenerování HTML stránky, bude vypadat takto:

(defn fact
    [n]
    (apply * (range 1M (inc n))))
 
(defn html-page
    [max-n]
    (page/xhtml
        [:head
            [:title "Hiccup test #4"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Hiccup test #4"]
            (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)]])
            ]
        ]))

10. Úplný zdrojový kód demonstračního příkladu webapp13

Celý zdrojový kód dnešní poslední demonstrační aplikace je relativně krátký – 62 programových řádků:

(ns webapp13.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 "Hiccup test #4"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Hiccup test #4"]
            (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}))

11. Repositář s demonstračními příklady

Všech pět dnes popsaných demonstračních příkladů bylo uloženo 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:

12. Odkazy na Internetu

  1. Hiccup
    https://github.com/weavejester/hiccup
  2. Clojure Ring na GitHubu
    https://github.com/ring-clojure/ring
  3. A brief overview of the Clojure web stack
    https://brehaut.net/blog/2011/rin­g_introduction
  4. Getting Started with Ring
    http://www.learningclojure­.com/2013/01/getting-started-with-ring.html
  5. Getting Started with Ring and Compojure – Clojure Web Programming
    http://www.myclojureadven­ture.com/2011/03/getting-started-with-ring-and-compojure.html
  6. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  7. 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/
  8. 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/
  9. 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/
  10. 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/
  11. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  12. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  13. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  14. Leiningen: úvodní stránka
    http://leiningen.org/
  15. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  16. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  17. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  18. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  19. 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/
  20. 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/
  21. 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/
  22. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  23. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  24. 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/
  25. 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/
  26. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  27. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  28. 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/
  29. 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/
  30. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  31. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  32. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  33. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  34. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  35. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  36. 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/
  37. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
Našli jste v článku chybu?

20. 3. 2015 13:36

jb (neregistrovaný)

Budu muset někdy zkusit nějakou tu webovku v clojure až si přestanu hrát s overtone a play-clj...

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Vitalia.cz: Vychytané vály a válečky na vánoční cukroví

Vychytané vály a válečky na vánoční cukroví

120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

DigiZone.cz: ČRo rozšiřuje DAB do Berouna

ČRo rozšiřuje DAB do Berouna

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET