11. července 2017

Clojure web development: Ring Middleware

V minulém článku jsme se podívali na úplně nejzákladnější základy webového vývoje v Clojure - jak zpracovat HTTP request a response pomocí knihovny Ring. Tu nejzajímavější část Ringu - Middleware - jsme ale zmínili jen letmo a byla by škoda se do tohoto zajímavého konceptu trochu více neponořit.

Připomenutí základních konceptů

Než zrychlíme z 0 na 100, připomenu čtyři základní komponenty Ringu:
  • Handler - funkce, která přijímá mapu reprezentující HTTP request a vrací mapu představující HTTP response.
  • Request - mapa reprezentující HTTP request, která obsahuje sadu "standardních" klíčů, které nám budou povědomé ze Servletu: :server-port, :server-name, :remote-address, :request-method ad.
  • Response - mapa, představující HTTP response, která obsahuje tři klíče: :status, :header, :body.
  • Middleware - funkce, která handleru přidá dodatečné funkcionality. Věci jako session, cookies, či parametry jsou řešené middlewarem.

Anatomie Middlewaru

Middleware je funkce vyššího řádu, která přijímá jako parameter handler a vrací jiný handler (jako anonymní funkci), který nějakým způsobem obohatil buď request, nebo response. V posdtatě se dá na middleware nahlížet jako na takový funkcionální decorator.

Pokud se podíváme na obecnou podobu middlewaru, má často následující strukturu (kudos to StackOverflow):


Balení middlewaru

Middleware většinou bývá jenom velmi jednoduchá funkce, takže komplexnější chování dostaneme zřetězením jednotlivých middlewarů a to tak, že je postupně zabalujeme do sebe:


Způsob, jak middlewary do sebe zabalit je dvojí - buď klasické zanořené funkce, nebo častější způsob je pomocí thread-first makra (->):


A jen pro úplnost - zabalit se anglicky řekne "wrap", proto se používá konvence, že middlewary začínají prefixem wrap-.

Ilustrační příklad

Řekněme, že bysme psali RESTovou službu, která přijímá data pomocí PUT. Pomineme funkční logiku, zpracování dat i routování a budeme se soustředit jen na middleware, který se bude chovat následovně:
  • Pokud request metoda není PUT, vrátí middleware status 405 a text "Method Not Allowed".
  • Pokud je request metoda PUT, vrátí status 204 (No Content) a prázdné body.

Aby middlewary byly malé a znovupoužitelné, rozdělíme si každou funkčnost, popsanou v předešlých odrážkách, do samostatné funkce:
  • wrap-no-content bude vracet status 204 a prázdné body.
  • wrap-put-allowed vrátí buď 405 (a popis v body), pokud metoda není PUT, nebo jenom zavolá původní handler.


Teď už stačí jen dát tyto dva middlewary dohromady, abychom dostali požadované chování:


GitHub projekt

Pokud vám výše popsané principy neštymují dohromady, mrkněte na GitHub, kde je malý spustitelný projektík, plus pár middleware unit testů:

Co příště?

O Ringu a jeho middlewarech by se dalo psát ještě dlouho, ale je čas pokročit. Logicky následným tématem je Compojure - routovací knihovna pro Ring webové aplikace.

Související články


1. května 2017

Clojure web development: Ring

Webový vývoj v Clojure je dobře etablovaný. Nebylo by to ale Clojure, kdyby si věci nedělalo trochu po svém. A tak nabízí, místo rozsáhlých aplikačních frameworků, množinu knihoven, které se dají pospojovat dohromady. Trochu to připomíná unixovou filozofii - malé, jednoúčelové prográmky, které lze propojovat do komplexnějších řešení.

Když jde o web, tak jde v první řadě o HTTP. Clojure na to jde od podlahy a jeho odpovědí je Ring - "Clojure HTTP server abstraction". Možná teď nebudu úplně přesný: Ring je Clojure implementací abstrakcí HTTP protokolu a zároveň je částečně kompatibilní s Java Servlety.

Ring vládne čtyřem komponentám

Každá Ring aplikace se skládá ze čtyř základních částí:
  • Handler - funkce, která přijímá mapu reprezentující HTTP request a vrací mapu představující HTTP response.
  • Request - mapa reprezentující HTTP request, která obsahuje sadu "standardních" klíčů, které nám budou povědomé ze Servletu: :server-port, :server-name, :remote-address, :request-method ad.
  • Response - mapa, představující HTTP response, která obsahuje tři klíče: :status, :header, :body.
  • Middleware - funkce, která handleru přidá dodatečné funkcionality. Věci jako session, cookies, či parametry jsou řešené middlewarem.

Ring Hello, world!

Začneme tradičně, vytvořením Leiningen projektu:
$ lein new blog-ring
a v souboru project.clj doplníme dependency na Ring:


Dále upravíme soubor src/blog_ring.core.clj, aby obsahoval náš Hello, world handler. Přidáme také funkci -main, abychom mohli aplikaci rovnou spustit:


Aplikaci spustíme příkazem:
$ lein run
a můžeme si ji prohlédnout v browseru na URL http://localhost:3000.

Request

Práce s requestem je přímočará - request je v Ringu prezentován mapou, takže stačí pomocí klíče vytáhnout požadovanou hodnotu a nějak ji zpracovat.


Předešlý handler nám vrátí následující stránku:

Response

V hello world příkladu jsme si celou response mapu sestavili sami. Ring nabízí řadu funkcí, soustředěných v namespace ring.util.response, které práci s response usnadňují:


Middleware

Tak, to bylo triviální. Co si teď střihnout nějaký middlewérek? Middleware je, podle definice, "funkce vyššího řádu, která přidává handleru dodatečné funkcionality. Prvním argumentem midddleware funkce je handler a její návratovou hodnotou je nový handler, který bude volat handler původní."

Seznam standard middleware funkcí se dá najít na wiki stránce Ringu, nebo na stránce API (všechno co je ring.middleware).

Pro potřebu článku jsem si vybral dva middlewary:
  • wrap-params, který rozparsuje parametry z formuláře a query stringu a přidá do requestu klíče :query-params, :form-params a :params
  • wrap-keyword-params, který zkonvertuje stringové klíče v mapě :params na keyword klíče (protože všichni přece máme rádi keyword klíče v Clojure mapách).
Middleware funkce se dají dobře testovat pomocí funkce identity, kdy není potřeba funkci podstrčit celý handler a následně request, ale jen část (request) mapy, která nás z hlediska middlewaru zajímá.

Tady je ukázka, co s requestem dělají uvedené funkce wrap-params a wrap-keyword-params:


Jak naznačuje definice, midddlewary jsou navržený tak, aby šly pospojovat za sebe. Je to v podstatě analogie Java (servlet) filtrů. Jak middlewary zřetězíme? Můžeme funkce jednoduše zanořit do sebe. Ale čitelnější, i používanější, je použití thread-first makra:


Kompletní kruh

Tak, a jsme na konci. Probrali jsme všechny čtyři komponenty, ze kterých se Ring skládá - handler, request, response a middleware. Pokdu je všechny spojíme do jednoduché, spustitelné aplikace, může to vypadat takto:


A výsledek v browseru:

Co příště?

Můj původní záměr o příštím článku bylo napsat o routování pomocí Compojure. Když jsem se ale ponořil do psaní sekce o middleware a připravoval si příklady, začalo se tohle pod-téma dost rozrůstat. Middleware je hodně zajímavý koncept - jednoduchý a velmi silný - a proto by bylo škoda se mu nevěnovat samostatně.

GitHub projekt

Pokud vám výše popsané principy neštymují dohromady, mrkněte na GitHub, kde je malý spustitelný projektík:

Související články

5. dubna 2017

Kniha Living Clojure

Jsem beznadějný čtenář knih. Musím denně číst a ačkoliv jsou dnes k dispozici tuny textů v různých formách, mě nejvíc vyhovují knihy.

Jedinou úlitbou modernosti je, že pro odbornou literaturu mi více vyhovují ty digitální. Přece jenom - do Kindlu se toho vejde kupa a zastaralé vydání vám nepřekáží v reálném světě.

Back to the Future

Ani nevím, co mě to cvrnklo přes nos, ale po pětileté přestávce se vracím ke Clojure. Pět let je v softwarovém inženýrství dlouhá doba. Technologie můžou zaniknout, či se nečekaně rozvinout, nebo nabrat úplně jiný směr. A těší mě, že Clojure stále rozkvétá.

Říkal jsem si - jak se do toho zase dostat? No dobře, je to řečnická otázka. Spíš jsem si říkal, kterou knihu si vybrat? Když jsem s Clojure začínal, byla na trhu jediná kniha - Programming Clojure od @StuartHalloway. Byla to tehdy bible Clojure. A možná ještě je - letos vyjde třetí edice!

Udělal jsem si seznam a jako první volbu jsem hodně zvažoval oceňované Joy of Clojure, jejíž první edici jsem kdysi nedočetl. Nakonec ale převážila náhoda. (Mimochodem, kdyby vás ten seznam zajímal, najdete ho u mne na Goodreads. Nebo ve webové verzi zaskrolujte po pravé straně dolů.)

Gigasquid!

Že jsem zase vlítnul do Clojure je více méně náhoda. Nebo osud. Minulý podzim jsem se byl po čase podívat na Java konferenci - český GeeCON - a tam jsem potkal Carin Meier, aka @Gigasquid. (Už jsem o tom psal na svém druhém blogu - GeeCON Prague 2016, den 2.)

Carin stojí za to sledovat. Kromě výše a níže zmíněné knihy, pracuje v Cognitectu. Ano, v té firmě, kde se koncentruje Clojure smetánka (posuďte sami), včetně polo-boha Riche Hickeyho.

Pokud budete mít možnost, určitě si s Carin popovídejte. Zvládl jsem to i já, zavilý introvert. Zakecal jsem se s ní tak moc, až jsem v podstatě prošvihnul následnou přednášku.

Alice in Wanderland

No dobře, to bylo keců... co ta kniha? Je dobrá, dal jsem jí na Goodreads 5 hvězd. Samozřejmě, v daném kontextu - je to dobrá kniha pro začátečníky, nebo pro někoho, kdo si potřebuje po čase Clojure oživit.

Kniha prochází všechny základní témata, která byste v učebnici čekali a skládá je v rozumném pořadí:
  • Kolekce a sekvence
  • Funkcionální programování
  • Stav a konkurence
  • Java interoperabilita
  • Leiningen a Clojure projekt
  • Asynchronni komunikace s core.async
  • Clojure web applikace
  • Makra
Napsáno pěkně, stručně, přehledně. Kromě toho, ale přináší druhá část knihy tréninkový program: Clojure v 7 týdnech.

Jak píše Carin v úvodu, učení se nového jazyka má silné paralely, jako když chcete zčista jasna začít běhat: důležitá je motivace, pravidelnost a... nepřetrénovat se.

Ohledně běhání, na webu je dnes spousta tréninkových plánů od 5 kilometrů až po maraton. Carin k tomu přistupuje obdobně - začít zvolna a postupně navyšovat objemy a obtížnost.

Prvních pár týdnů jde o řešení problémů na 4clojure. Pak se přesuneme na GitHub, kde má Carin několik kata, inspirovaných příběhem Alenky v říši divů: wonderland-clojure-katas. Poslední týden je pak věnovaný deploymentu Clojure aplikace na Heroku.

A je to! Kniha je přečtená a vy jste fluent v Clojure :-)  No, tak jednoduché to není. Ale je to dobrý začátek. Jeden z možných.

25. března 2017

Clojure concurrency: Vars

Významnou vlastností Clojure jsou neměnitelné datové struktury. Je to taková dvojsečná vlastnost (i když benefity výrazně převažují). Na jednu stranu to vývoj zjednodušuje, protože se nemusíme bát, že se nám data změní pod rukama.

Nadruhou stranu vyvstává otázka, jak s neměnitelnými daty pracovat - nic v našem světě není neměnné (a všechno jednou pomine). Jak tedy Clojure řeší změnu stavu a s tím související konkurenci?

K dispozici jsou čtyři mechanizmy, jak měnit stav:
  • Vars jako globální úložiště, pro eventuální per-thread změny.
  • Atoms pro synchronní, nekoordinované změny.
  • Refs pro synchronní, v transakci koordinované změny.
  • Agents pro asynchronní, nekoordinované změny.
Postupně bych se chtěl podívat na všechny způsoby, ale dnes začneme tím nejjednodušším, čemu se žádný začátečník v Clojure nevyhne.

Vars

Var je způsob, jakým Clojure odkazuje na úložiště. Toto úložiště obsahuje "standardní", neměnná data. Var, ale můžeme dočasně přesměrovat na jiné úložiště. Podstatnou vlastností je, že toto dočasné přesměrování - binding - je vidět jenom v rámci aktuálního vlákna, ostatní vlákna vidí pořád původní hodnotu.

Statické Vars

Klasické Var se vytvoří speciálním formem def: (def x 42). Takto definováno, je Var statické - pokud bychom ho chtěli svázat s jiným úložištěm, vyhodí Clojure výjimku IllegalStateException.


Dynamické Vars

Pokud chceme, aby Var ukazovalo na jiné úložiště, musíme ho explicitně definovat jako dynamické pomocí instrukce ^:dynamic. Samotné přesměrování se provádí makrem binding. Po dobu trvání bloku binding směřuje Var na nové úložiště, aby se po jeho skončení vrátilo ke své původní hodnotě.

Dle konvence se dynamické Vars uzavírají do *earmuffs*. Označují se tak věci, určené pro re-binding.


Vars jsou globální

Vars jsou globální - viditelné pro všechny thready. Pokud tedy vytvoříme Var v jiném vlákně, je dosažitelné také ze všech ostatních vláken.

V následujícím příkladu vytvoříme Var v jiném vlákně pomocí makra future:


Namespace

V rámci namespace, v němž byly vytvořeny, jsou Vars přístupné svým názvem. Pokud se přepneme do jiného namespacu, je potřeba na Var odkazovat plně kvalifikovaným názvem, nebo ho "naimportovat" z původního namespace pomocí funkce refer.


Funkce jsou také Vars

Ve Vars se neukládají jenom data, ale také funkce. Všechno výše napsané tedy platí pro funkce úplně stejně, včetně toho, že je můžeme dynamicky re-bindovat.

To může být zajímavý mechanismus, jak v runtimu dynamicky měnit chování funkcí. Nicméně, užívejte s mírou - jazyk to umožňuje, ale je to výjmečné řešení.


A kde je ta konkurence?

Se samotnými Vars si moc konkurentního programování neužijeme. Je to ale základní stavební kámen, od kterého se odvíjejí ostatní způsoby. Konec konců, všechny v úvodu zmíněné mechanizmy - Atomy, Refs a Agenti - jsou ve výsledku uloženy ve Vars.

GitHub projekt

Clojure concurrency chci pojmout jako mini-seriál (viz Související články níže), který bude podložený projektem na GitHubu, kam budou postupně přibývat jednotlivé příklady:

Související články

  • TBD: Clojure concurrency: Atoms
  • TBD: Clojure concurrency: Refs
  • TBD: Clojure concurrency: Agents

Starší související články

18. března 2017

Catalanova čísla a syntax highlighting

Je to téměř 5 let, co jsem naposled napsal něco na tento blog. Ale doufám, že se to teď změní - moje láska ke Clojure opět propukla s neztenčenou silou a tak snad ponese nějaké užitečné ovoce.

Chvilku ale potrvá, než uvedu blog do použitelného stavu, takže začnu takovým zahřívacím tématem. Tedy, dvěma tématy - sice to není úplně fér, ale vrazil jsem do titulku dva nesouvisející motivy.

Syntax highlighting 3.0

Když jsem s Clojure před šesti lety začínal, bylo ve verzi 1.2 a jeho podpora v různých nástrojích byla nízká, nebo žádná. Jednu z věcí, které jsem tehdy řešil, bylo jak zobrazit na blogu syntax highlighting.

Moje první volba padla na tehdy všudypřítomný SyntaxHighlighter. Musel jsem použít jakýsi externí Clojure brush, protože SyntaxHighlighter dodnes nemá nativní podporu Clojure.

S výsledkem jsem nebyl spokojen, takže jsem po čase zvolil nové řešení - jako letitý uživatel famózního editoru Vim, jsem se rozhodl pro trochu ruční práce a zaangažoval vimovskou funkci :TOhtml. S tou jsem maximálně spokojen a používám ji i na svém druhém blogu SoftWare Samuraj. Přesto jsem se rozhodl potřetí pro změnu syntax highlightingu.

Ten třetí důvod má konsekvence s oživením blogu. Aniž bych si tady vylíval srdíčko, jeden z důsledků mých aktuálních rozhodnutí je, vybudovat si na svém GitHubu jakési Clojure portfolio (už jsem o tom kdysi psal).

Volba GitHubu pro mne nebyla úplně přímočará - nejsem "git guy" a pokud můžu, preferuji Mercurial. Pravdou ale je, že veškerá Clojure komunita je GitHubu, tak proč se tomu vzpírat. No, a když GitHub, tak Gist, to dá rozum.

Catalanova čísla

V rámci svého Clojure-zmrtvýchvstání jsem procházel své staré Clojure kódy a narazil jsem na jeden, který se mi líbil - funkce pro výpočet Catalanových čísel. Pikantní je, že si po těch pěti letech nepamatuju, proč jsem to napsal, nicméně funkce se mi líbily natolik, že jsem se rozhodl se o ně - v rámci zahřátí na provozní teplotu - podělit.

Catalanova čísla jsou sekvencí přirozených čísel, která má zajímavé využití v kombinatorice - binární stromy, průchod mřížkou(?), rozdělení polygonu na trojúhelníky ad. Sekvence je definována následujícím vztahem:


Pokud, tak jako já, nejste úplně kovaní v matematice, tak chviličku googlování potrvá, než zjistíte, že pro implementaci tohoto vzorečku budete potřebovat kombinační číslo (binomial coefficient) a tím pádem faktoriál. Pak už je jednoduché poskládat tyto základní funkce do sebe, jako matrojšku:


Když už jsem se v tom tak vrtal, říkal jsem si: nedá se to udělat nějak jednodušeji, efektněji, víc "Clojure"? A dá. Jen je potřeba vyjít z jiného vzorečku, který definuje Catalanova čísla rekurzivně:
Při použití posledního vzorečku nám tak vyjde pěkná Clojure rekurze. Klíčová slova jsou speciální operátory loop a recur:


GitHub project

Pokud si chcete pohrát s Catalanovými čísly trochu víc, anebo dostat malý bonus navíc, podívejte se na můj projekt na GitHubu:
Navíc dostanete Leiningen projekt, testy v Midje a lazy sekvenci Catalanových čísel.

Související články