http://czasprogramistow.pl/Czas Programistów2017-12-03T13:06:08+00:00Motywacją do prowadzenia tego bloga jest chęć dołożenia choćby jednej cegiełki do zwiększenia potencjału polskiego zaplecza ITRafał PaliwodaJekyllhttp://czasprogramistow.pl/advent-of-code/Advent of Code2017-12-01T00:00:00+00:00Rafał PaliwodaChciałbym wszystkich niniejszym zachęcić do udziału w konkursie programistycznym <a href='http://adventofcode.com/'>Advent of Code</a>. Natrafiłem na niego przypadkiem, ale już wcześniej zdarzyło mi się o nim słyszeć. <br><br> W skrócie mamy 25 problemów do rozwiązania, jeden na każdy dzień adwentu. Wspaniała forma na wyczekiwanie i przygotowanie do świąt ;). <br><br> W Advent of Code nie musimy wysyłać kodu źródłowego. Wystarczy otrzymanie wyniku obliczonego dla podanych danych wejściowych. <br><br> Postanowiłem spróbować swoich sił. Takie wydarzenia, to na przykład wspaniała forma poznawania nowych języków programowania. Ja na pierwsze zadania wybrałem Groovy. Język łatwy do przyswojenia dla zaznajomionych z Javą. Jednocześnie dużo lżejszy i bardziej ekspresyjny.<h2 id="projekt">Projekt</h2>
<p>Zacząłem od inicjalizacji prostego projektu przy użyciu gradle.
Sam gradle nie jest konieczny, można by projekt stworzyć używając dowolnego innego sposobu.</p>
<div class="alert-box text radius ">
<p>Dla niewtajemniczonych gradle to system budowania i zarządzania zależnościami w projekcie,
podobnie jak maven. W tym wpisie nie będziemy sie nim zbytnio zajmować.</p>
</div>
<p>Do stworzenia projektu wystarczy polecenie:</p>
<div class="alert-box terminal radius ">
<p>gradle init –type groovy-library</p>
</div>
<p>Dostajemy po nim następującą strukturę:</p>
<pre>
.
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
│ └── groovy
└── test
└── groovy
</pre>
<p>Mamy zatem najprostszy jak to tylko możliwe project w Groovym.
Jak nietrudno się domyślić, implementację zadań umieścimy w <em>src/main</em>,
umieszczając testy w odpowiadających pakietach w <em>src/test</em>.</p>
<h2 id="groovy">Groovy</h2>
<p>Wiedzałem, że nie będę nigdzie wysyłał mojego kodu. Wystarczy zatem żeby zrobił on
to co do niego należy – przeprowadził kalkulacje zgodnie z podanym algorytmem.</p>
<p>W takiej sytuacji sam kod nie musiał być idealny, szybki, ponadprzeciętnie czytelny
czy też łatwy w utrzymaniu.</p>
<p>Najbardziej zależało mi na łatwości i zwinności w implementacji rozwiązania.
Na tym żebym się mogł skupić na algorytmie bez zbytecznego boilerplejtu.</p>
<p>Niezłym wyborem wydał się właśnie Groovy. Język funkcyjny z dynamicznym typowaniem,
znakomicie wspierający pracę z kolekcjami. Pozwalający na pisanie krótkich i eleganckich
rozwiazań.</p>
<p>Rozważałbym także pythona do tego typu zadań. Java wydaje się tutaj zbyt toporna.</p>
<h2 id="zadanie-1-inversed-captcha">Zadanie 1: Inversed Captcha</h2>
<p>Całość opakowana jest w zabawną fabułę. Otóż w przedświąteczną noc okazuje się,
że Świety Mikołaj nie jest stanie wydrukować jego “Naughty or Nice list”.</p>
<p>Na drodze do tego stoi 50 bugów, które trapi jego drukarkę. Naszym zadaniem będzie
ich eliminacja.</p>
<p>Pierwszym wyzywaniem jest przejście captchy. Składa sie ona z listy cyfr.
Poszukiwanym rozwiązaniem jest suma wszystkich elementów, które są powtórzone
na następnej pozycji. Listę należy traktować jako cykliczną.</p>
<p>Przykładowo 1122 daje wynik 3 (1+2), a 91212129 oblicza sie do 9.</p>
<h3 id="testy">Testy:</h3>
<p>Najpierw napisałem testy dla brzegowych przypadków oraz podanych, potem implementację – była dość prosta.
Następnie dodałem test z danymi przykładowymi, by upewnić się, że mój program działa prawidłowo.
W końcu napisałem test case dający ostateczny wynik.</p>
<p>Tak wyglądały testy:</p>
<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="kt">def</span> <span class="nf">shouldReturn0ForEmptyInput</span><span class="o">()</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="nl">expect:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">InversedCaptcha</span><span class="o">.</span><span class="na">calculateSum</span><span class="o">(</span><span class="s2">""</span><span class="o">)</span> <span class="o">==</span> <span class="mi">0</span><span class="o">;</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="kt">def</span> <span class="nf">shouldWorkForSingleDigitInput</span><span class="o">()</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="nl">expect:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">InversedCaptcha</span><span class="o">.</span><span class="na">calculateSum</span><span class="o">(</span><span class="s2">"1"</span><span class="o">)</span> <span class="o">==</span> <span class="mi">1</span><span class="o">;</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="kt">def</span> <span class="nf">shouldWorkForTwoSameDigitsInput</span><span class="o">()</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="nl">expect:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">InversedCaptcha</span><span class="o">.</span><span class="na">calculateSum</span><span class="o">(</span><span class="s2">"11"</span><span class="o">)</span> <span class="o">==</span> <span class="mi">2</span><span class="o">;</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="kt">def</span> <span class="nf">shouldWorkForTwoDifferentDigitsInput</span><span class="o">()</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="nl">expect:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">InversedCaptcha</span><span class="o">.</span><span class="na">calculateSum</span><span class="o">(</span><span class="s2">"12"</span><span class="o">)</span> <span class="o">==</span> <span class="mi">0</span><span class="o">;</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /><span class="nd">@Unroll</span><br data-jekyll-commonmark-ghpages="" /><span class="kt">def</span> <span class="nf">shouldWorkForGivenExamples</span><span class="o">()</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="nl">expect:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">InversedCaptcha</span><span class="o">.</span><span class="na">calculateSum</span><span class="o">(</span><span class="n">input</span><span class="o">)</span> <span class="o">==</span> <span class="n">expectedResult</span><span class="o">;</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="nl">where:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">input</span> <span class="o">|</span> <span class="n">expectedResult</span><br data-jekyll-commonmark-ghpages="" /> <span class="s2">"1122"</span> <span class="o">|</span> <span class="mi">3</span><br data-jekyll-commonmark-ghpages="" /> <span class="s2">"1111"</span> <span class="o">|</span> <span class="mi">4</span><br data-jekyll-commonmark-ghpages="" /> <span class="s2">"1234"</span> <span class="o">|</span> <span class="mi">0</span><br data-jekyll-commonmark-ghpages="" /> <span class="s2">"91212129"</span> <span class="o">|</span> <span class="mi">9</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span></code></pre></figure>
<p>Co powiecie na to jak znakomicie są czytelne? Jest tak za sprawą frameworku Spock,
który mam przyjemność wykorzystywać także w projektach Javowych.</p>
<p>Po testach zrobiłem mala refaktoryzajcę kodu.</p>
<h3 id="rozwiazanie">Rozwiazanie:</h3>
<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="kd">static</span> <span class="kt">int</span> <span class="nf">calculateSum</span><span class="o">(</span><span class="n">String</span> <span class="n">input</span><span class="o">)</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">input</span><span class="o">.</span><span class="na">toList</span><span class="o">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">.</span><span class="na">withIndex</span><span class="o">()</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">.</span><span class="na">collect</span> <span class="o">{</span> <span class="n">x</span><span class="o">,</span> <span class="n">i</span> <span class="o">-></span> <span class="n">x</span> <span class="o">==</span> <span class="n">input</span><span class="o">[(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span> <span class="o">%</span> <span class="n">input</span><span class="o">.</span><span class="na">size</span><span class="o">()]</span> <span class="o">?</span> <span class="n">x</span> <span class="k">as</span> <span class="n">Integer</span> <span class="o">:</span> <span class="mi">0</span> <span class="o">}</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">.</span><span class="na">sum</span><span class="o">(</span><span class="mi">0</span><span class="o">)</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span></code></pre></figure>
<p>Implementacja to prosta transformacja listy oraz jej zsumowanie.</p>
<h2 id="zadanie-2-corruption-checksum">Zadanie 2: Corruption Checksum</h2>
<p>Kolejne nietrudne zadanie. Dostajemy arkusz. Dla każdego wiersza musimy obliczyć
różnicę maksymalnego i minimalnego elementu. Różnice te po zsumowaniu dadzą nam ostateczny wynik.</p>
<p>Testy:</p>
<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="kt">def</span> <span class="nf">exampleTestCase</span><span class="o">()</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="nl">expect:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">CorruptionChecksum</span><span class="o">.</span><span class="na">calculate</span><span class="o">(</span><span class="n">input</span><span class="o">)</span> <span class="o">==</span> <span class="n">result</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="nl">where:</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">input</span> <span class="o">|</span> <span class="n">result</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">[[</span><span class="mi">5</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="mi">9</span><span class="o">,</span> <span class="mi">5</span><span class="o">],</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">[</span><span class="mi">7</span><span class="o">,</span> <span class="mi">5</span><span class="o">,</span> <span class="mi">3</span><span class="o">],</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">[</span><span class="mi">2</span><span class="o">,</span> <span class="mi">4</span><span class="o">,</span> <span class="mi">6</span><span class="o">,</span> <span class="mi">8</span><span class="o">]]</span> <span class="o">|</span> <span class="mi">18</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span></code></pre></figure>
<p>i rozwiązanie:</p>
<figure class="highlight"><pre><code class="language-groovy" data-lang="groovy"><span class="kd">static</span> <span class="nf">calculate</span><span class="o">(</span><span class="n">List</span><span class="o"><</span><span class="n">List</span><span class="o"><</span><span class="n">Integer</span><span class="o">>></span> <span class="n">input</span><span class="o">)</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">input</span><span class="o">.</span><span class="na">collect</span> <span class="o">{</span> <span class="n">it</span><span class="o">.</span><span class="na">max</span><span class="o">()</span> <span class="o">-</span> <span class="n">it</span><span class="o">.</span><span class="na">min</span><span class="o">()</span> <span class="o">}.</span><span class="na">sum</span><span class="o">()</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span></code></pre></figure>
<p>To sama przyjemność pisać tak zwięzły i czytelny kod.
Zobaczymy czy uda się zachować ten standard przy bardziej skomplikowanych zadaniach.</p>
<p>Zapraszam do śledzenia kolejnych wpisów. Moje rozwiązania można zobaczyć na
<a href="https://github.com/paliwodar/advent-of-code-2017">githubie</a>.
Polecam dołączyć do listy rankingowej <em>Czasu Programistów</em> używając kodu <code class="highlighter-rouge">232530-5e15782d</code>.</p>
2017-12-01T00:00:00+00:00http://czasprogramistow.pl/jak-nie-szukac-mieszkania-w-sztokholmie-1/Jak (nie) szukać mieszkania w Sztokholmie, cz. 12017-10-29T00:00:00+00:00Rafał PaliwodaW niniejszym artykule dzielę się swoimi doświadczeniami związanymi z poszukiwaniem mieszkania na wynajem w Sztokholmie. <br><br>Temat być może dla niektórych interesujący. Dla innych może po prostu przydatny. Szczególnie gdy im się kontrakt kończy. Tak jak to było w moim przypadku jeszcze nie tak dawno temu. <br><br> Wpis pisany z perspektywy upływu czasu. Z wygodnego skórzanego fotela i maca postawionego na dębowym stole w świeżo wynajętym mieszkaniu. A jakże! Nie jest to 40. piętro, więc panorama skromna, ale narzekać nie można. <br><br> No ale o tym później. Na początek zapodam preludium w postaci opisu sytuacji mieszkaniowej w stolicy Szwecji. <h2 id="fajnie-przyjechać-na-gotowe">Fajnie przyjechać na gotowe</h2>
<p>Zacznę od tego, że problem mieszkania w Szwecji w zasadzie dla mnie nie istniał. Przynajmniej nie przez pierwsze
dwa lata. A to za sprawą bogatego pakietu relokacyjnego, oferowanego przez moją firmę. Coś, co bardzo się przydaje.</p>
<p>Po przylocie, mieliśmy wraz z narzeczoną, zapewniony apartament tymczasowy i nagrane już mieszkanie, którego
zdjęcia widzieliśmy jeszcze będąc w Polsce. Dodam na marginesie, że to pierwsze mieszkanie okazało się
być dalekim od ideału. W ładnej okolicy – Djursholm, ale niestety już o nie tak ładnym wnętrzu.
Na szczęście jednak – po małych negocjacjach z firmą – zgodzono się na to, żebym mógł je zmienić.</p>
<p>Następne mieszkanie było w Kungsholmen i mogłem wynajmować za pośrednictwem mojej firmy
przez maksymalnie dwa lata. Potem należałoby przedłużyć umowę, podpisując ją bezpośrednio z właścicielką,
bądź też znaleźć coś nowego.</p>
<p>Naturalnie przy okazji zbliżającego się terminu, warto było się rozejrzeć za potencjalnie lepszymi opcjami.
I o tym właśnie będzie.</p>
<div class="alert-box text radius ">
<p>PS. Oczywiście, gdyby trzeba było, to myślę, że udałoby się do Sztokholmu przyjechać na własną rękę.
Tutaj jednak potrzebny byłby co najmniej jakiś nocleg na pierwsze kilka tygodni. Co może nie być najłatwiejsze,
kiedy akurat nie mamy żadnego znajomego na miejscu.</p>
</div>
<h2 id="mieszkanie-samemu-znaleźć-trochę-trudniej">Mieszkanie samemu znaleźć trochę trudniej</h2>
<p>Teraz kolej na przyjrzenie się sytuacji na rynku mieszkań. Jest ona delikatnie mówiąc niełatwa.
Jest to rynek wynajmujących, którzy mogą dowolnie przebierać w chętnych.
To znaczy, robiliby tak pewnie, gdyby nie tutejsze zamiłowanie do kolejek – a zatem
zwykle odbywa się to zgodnie z zasadą “kto pierwszy, ten lepszy”.</p>
<p>Czyli tak: jest mieszkanie, po godzinie już go nie ma. Znikają tym szybciej, im wyższy jest
stosunek jakości do ceny. Te za 15 000 SEK dostępne są przez nieco dłuższy okres czasu –
i mam tutaj na myśli kawalerki lub z maksymalnie jednym oddzielnym pokojem.
Mieszkania w tej cenie brałem wtedy pod uwagę niechętnie, jako że było to nieco więcej,
niż wtedy płaciłem za dotychczasowe lokum.</p>
<h2 id="gdzie-szukać--kolejki">Gdzie szukać – kolejki</h2>
<p>Teraz kolej na to gdzie szukać mieszkania. To znaczy gdzie ja szukałem.
Może szukałem nie tam gdzie trzeba ;)</p>
<p>Po pierwsze najprostsza forma: bostad. Stajemy w kolejce. I czekamy. Płacąc około 200 SEK rocznie.
Jeżeli nam się uda doczekać, to dostaniemy mieszkanie na wieczysty użytek, nazwijmy to “od miasta”.
Płacimy wtedy jedynie czynsz. Strzelam, że wynosić on może do kilku tysięcy koron.</p>
<p>Rozwiązanie to posiada jedną ogromną wadę – może być czasochłonne.
Długość oczekiwania – w zależności od dzielnicy – może wynieść 10-20 lat.
Nie mam zielonego pojęcia także, jaki jest stan mieszkań otrzymanych w ten sposób.
No ale jak użytkowanie jest wieczyste, to i nawet wyremontować można.</p>
<p>Ja nie mam planów długoterminowych, ale w kolejce stoję. Póki co.
Oprócz kolejki tej, są także inne, niektóre z nich nieodpłatne.
Jak np. www.forvaltaren.se – dotyczy zdaje się Solnej i Sundbybergu.
Czasy oczekiwania ponoć relatywnie krótsze.</p>
<h2 id="jedna-strona">Jedna strona</h2>
<p>Wiem, że są przeróżne strony, które za stosowną opłatą oferują dostęp do ich bazy.
Nie korzystałem z takowych, wypowiadać się zatem nie mogę.</p>
<p>Ostatecznie pozostaje – wg mojej wiedzy – jedna strona,
na której odbywa się większość transakcji: blocket.se.
Znajdziecie tam dużo różnych rzeczy, w tym także mieszkania.</p>
<p>Jedni powiedzieliby, że rynek jest bardzo płynny.
Inni – że panuje tam zażarta rywalizacja. Tak sobie przynajmniej wyobrażam,
gdyż spośród kilkudziesięciu lub nawet kilkuset wysłanych zapytań,
odpowiedzi dostałem zaledwie kilkanaście. Czyżby to był zwykły pech?</p>
<p>Na blockecie można stosować różne strategie szukania. Wstępnie stosowałem najprostszą -
odpowiadanie na ogłoszenia, które wydawały mi się interesujące.
Czasem dzwoniłem, ale podejrzewam, że tam wygrywali Ci z dobrym refleksem.</p>
<h2 id="uwaga-na-oszustów">Uwaga na oszustów</h2>
<p>Zanim przejdę do mieszkań, które widziałem, to wspomnę o tych których obejrzeć mi się nie udało.
Co więcej, których właściciele wcale nie planowali pokazywać.</p>
<p>Wyobraźcie sobie zatem sytuację, w której – mając miesiąc wypowiedzenia –
jeżeli chcę opuścić mieszkanie wraz z końcem maja, to muszę dać wypowiedzenie do końca kwietnia.</p>
<p>Wyobraźcie sobie następnie jegomościa, który ma obiecujące mieszkanie. To znaczy jeszcze go nie ma,
ale ma dostać klucze, powiedzmy 10 maja.</p>
<p>Miałem dwie rozmowy z właśnie takim scenariuszem, gdzie żeby wynająć u nich mieszkanie od początku czerwca,
byłbym zmuszony wypowiedzieć aktualne mieszkanie bez oglądania nowego.</p>
<p>Oczywiście oni nie widzieli w tym absolutnie żadnego problemu. Mieli nawet na to remedium.
Proponowali podpisanie wstępnego kontraktu i wpłacenie niewielkiego depozytu… oczywiście ciągle bez oglądania mieszkania!</p>
<p>Zapewniali możliwość zobaczenia wszystkich dokumentów i wizualizacji oraz – co niewiarygodne – “przejechanie się obok mieszkania” ;)</p>
<p>Oczywiście stwierdzałem, że ja się na coś takiego nie piszę i żeby się ze mną kontaktowali
pod warunkiem, że uda im się wymyślić jakieś rozwiązanie. Oczywiście odzewu nie było.</p>
<h2 id="co-dalej">Co dalej?</h2>
<p>To tyle wstępu. W <a href="/jak-nie-szukac-mieszkania-w-sztokholmie-2/">następnym odcinku</a> o tym jak poszło
i jak wyglądały wizyty w poszczególnych mieszkaniach.</p>
2017-10-29T00:00:00+00:00http://czasprogramistow.pl/clojure-webservice-testowanie/Mikroserwis w Clojure – testy jednostkowe2017-10-23T00:00:00+00:00Rafał PaliwodaKolejny raz o naszym serwisie w Clojure z wykorzystaniem Compojure. Ostatnio zobaczyliśmy <a href='/twoj-pierwszy-rest-serwis-w-clojure'>jak stworzyć projekt pozwalający na zarządzanie zasobami książkowymi</a>. <br><br> Pokażę teraz w jaki sposób możemy wzbogacić go o niezbędne testy jednostkowe. W praktyce pisane równocześnie z implementacją. Zasługujące jednak na samodzielny wpis.<h2 id="przypomnienie">Przypomnienie</h2>
<p>Podsumujmy, że nasz serwis obsługuje następujące endpointy:</p>
<pre>
GET /books
GET /books/[id]
POST /books
PUT /books/[id]
DELETE /books/[id]
DELETE /books/[id]
</pre>
<p>Definicje powyższych operacji zawarte są niemalże w zupełności w jednej funcji – handler (url do githuba)
i polegają w dużej mierze na delegacji zapytania do bazy danych.
Wzbogaconego miejscami o obsługę przypadków brzegowych (jak np. brak elementu w bazie)
oraz nadawanie id naszym zasobom.</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">defroutes</span><span class="w"> </span><span class="n">app-routes</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">context</span><span class="w"> </span><span class="s">"/books"</span><span class="w"> </span><span class="p">[]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">(</span><span class="nf">read-books</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">POST</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">{</span><span class="n">body</span><span class="w"> </span><span class="no">:body</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="nf">insert-new-book</span><span class="w"> </span><span class="n">body</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">context</span><span class="w"> </span><span class="s">"/:id"</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">(</span><span class="nf">read-book-by-id</span><span class="w"> </span><span class="n">id</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">PUT</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">{</span><span class="n">body</span><span class="w"> </span><span class="no">:body</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="nf">update-existing-book</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">body</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">DELETE</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">(</span><span class="nf">delete-book</span><span class="w"> </span><span class="n">id</span><span class="p">))))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">route/not-found</span><span class="w"> </span><span class="s">"Not Found"</span><span class="p">))</span></code></pre></figure>
<p>Rozsądnym wydaje się tutaj testowanie działania kodu z poziomu tegoż właśnie handlera.
Będziemy chcieli jednak uniknąć odwołania do prawdziwej bazy danych.</p>
<h2 id="jak-mockowaćzależności-w-clojure">Jak mockować zależności w Clojure?</h2>
<p>Bibliotek do mockowania w Clojure znajdziemy całkiem sporo. Niektóre zdają się
być napisane ze znacznym rozmachem (jak np. midje), inne są tylko nakładkami na
standardowe mechanizmy jakie oferuje sam język.</p>
<p>Mowa tutaj o funkcji <em>with-redefs</em>, która pozwala zmienić w locie definicję danej funkcji.
W naszym przypadku to właśnie jej użyjemy.</p>
<p>Podejściem naszym będzie, by zdefiniować w klasie testowej, kolekcję <em>books-data</em>,
która będzie odpowiednio modyfikowana podczas wywołania fukcji bazodanowych.</p>
<p>Zgodnie z powyższym kod testów może mieć następującą strukturę:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">deftest</span><span class="w"> </span><span class="n">test-app</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">books-data</span><span class="w"> </span><span class="p">(</span><span class="nb">ref</span><span class="w"> </span><span class="p">())]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">with-redefs</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="n">db/read-books</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="o">@</span><span class="n">books-data</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">db/read-book</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">filter</span><span class="w"> </span><span class="o">#</span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="nf">%</span><span class="w"> </span><span class="s">"id"</span><span class="p">)</span><span class="w"> </span><span class="n">id</span><span class="p">)</span><span class="w"> </span><span class="o">@</span><span class="n">books-data</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">db/insert-book</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="p">[</span><span class="n">book</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">dosync</span><span class="w"> </span><span class="p">(</span><span class="nb">alter</span><span class="w"> </span><span class="n">books-data</span><span class="w"> </span><span class="nb">conj</span><span class="w"> </span><span class="n">book</span><span class="p">)))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">...</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">testing</span><span class="w"> </span><span class="s">"Pierwszy test"</span><span class="w"> </span><span class="n">...</span><span class="w"> </span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">testing</span><span class="w"> </span><span class="s">"Drugi test"</span><span class="w"> </span><span class="n">...</span><span class="w"> </span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">...</span><span class="p">)))</span></code></pre></figure>
<p>Składnia jest następująca: po słowie kluczowym <em>with-redefs</em> podajemy listę funkcji wraz z ich nowym znaczeniem.
Następne argumenty to kod, który będzie objęty nowymi definicjami miast starych.</p>
<p>W tym przypadku <em>db/read-books</em> zwyczajnie zwróci zawartość zmiennej <em>books-data</em>,
podczas gdy <em>db/read-book</em> zwróci tylko te elementy na liście <em>books-data</em>, które posiadają podane id.
No i oczywiście <em>insert</em> podmienia <em>books-data</em> z listą o jeden element dłuższą.</p>
<p>Jako że modyfikuje referencję, to musi być wywołana w transakcji. Clojure posiada
elementy znakomicie ułatwiające programowanie wielowątkowe, takie jak np.
<a href="https://clojure.org/reference/refs"><em>Software Transactional Memory</em></a></p>
<p>Podsumowując, zamiast odwoływać się do bazy danych, operacje delegujemy do lokalnej listy książek.</p>
<h2 id="jak-pisać-testy">Jak pisać testy?</h2>
<p>Przy obecnym setupie, pisząc testy, mamy do wyboru różne podejścia.
Osobiście <strong>jestem zwolennikiem krótkich, niezależnych i wyspecjalizowanych testów</strong>.
Pewnie brakuje jeszcze kilku epitetów do uczynienia tej listy wyczerpującą.</p>
<p>Powinno być bezsprzecznie wiadomo jaką operację pokrywa dany test,
jakie są warunki wstępne i czego oczekujemy po tej operacji wykonaniu.</p>
<h5 id="testy-jako-dokumentacja">Testy jako dokumentacja</h5>
<p>Pamiętać trzeba, że <strong>testy to najlepsza z możliwych dokumentacji naszego systemu</strong>.
Dlatego też powinny być najbardziej przejrzyste jak to tylko możliwe.</p>
<p>Przykład: litania operacji z powplatanymi asercjami nie będzie dobrą praktyką.</p>
<h5 id="niezależne">Niezależne</h5>
<p>Co jeżeli testy korzytają ze wspólnego zasobu? Otóż nie powinno mieć to miejsca
w testach jednostkowych. Alternatywnie, w najgorszym wypadku, powinniśmy zadbać o to, żeby jego stan był resetowany.</p>
<p>I także w naszym przypadku - nie powinniśmy współdzielić naszego mocka do bazy
danych pomiędzy testami. Każdy powinien posiadać oddzielną instancję.</p>
<h5 id="z-precyzyjnymi-komunikatami">Z precyzyjnymi komunikatami</h5>
<p>I wreszcie – warto przyjrzeć się jakie komunikaty generują nasze testy w przypadku
niepowodzenia. Informacja zwrotna powinna być możliwie najbardziej precyzyjna.</p>
<h2 id="nasz-scenariusz-testowy">Nasz scenariusz testowy</h2>
<p>To powiedziawszy podzielę się następującym scenariuszem testowym.
Jest on napisany po części wbrew powyższym wytycznym. Składa się bowiem
z kolejnych, zależnych od siebie przypadków testowych.</p>
<p>Gdyby to miał być kod produkcyjny, to na pewno chciałbym go rozbić na niezależne części.
Możecie spróbować tego dokonać w ramach ćwiczeń.</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">testing</span><span class="w"> </span><span class="s">"nonexistent route results with 404"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">response</span><span class="w"> </span><span class="p">(</span><span class="nf">app</span><span class="w"> </span><span class="p">(</span><span class="nf">mock/request</span><span class="w"> </span><span class="no">:get</span><span class="w"> </span><span class="s">"/"</span><span class="p">))]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="no">:status</span><span class="w"> </span><span class="n">response</span><span class="p">)</span><span class="w"> </span><span class="mi">404</span><span class="p">))))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="nf">testing</span><span class="w"> </span><span class="s">"bookstore should be initially empty"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">response</span><span class="w"> </span><span class="p">(</span><span class="nf">app</span><span class="w"> </span><span class="p">(</span><span class="nf">mock/request</span><span class="w"> </span><span class="no">:get</span><span class="w"> </span><span class="s">"/books"</span><span class="p">))]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="no">:status</span><span class="w"> </span><span class="n">response</span><span class="p">)</span><span class="w"> </span><span class="mi">200</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="no">:body</span><span class="w"> </span><span class="n">response</span><span class="p">)</span><span class="w"> </span><span class="s">"[]"</span><span class="p">))))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="nf">testing</span><span class="w"> </span><span class="s">"a book insertion is successful"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">insertion-response</span><span class="w"> </span><span class="p">(</span><span class="nb">-></span><span class="w"> </span><span class="p">(</span><span class="nf">mock/request</span><span class="w"> </span><span class="no">:post</span><span class="w"> </span><span class="s">"/books"</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">mock/body</span><span class="w"> </span><span class="s">"{\"author\":\"Andrzej Sapkowski\", \"title\":\"Last Wish\"}"</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">mock/content-type</span><span class="w"> </span><span class="s">"application/json"</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">app</span><span class="p">)]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="no">:status</span><span class="w"> </span><span class="n">insertion-response</span><span class="p">)</span><span class="w"> </span><span class="mi">200</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="s">"Andrzej Sapkowski"</span><span class="w"> </span><span class="p">((</span><span class="nf">parse-string</span><span class="w"> </span><span class="p">(</span><span class="no">:body</span><span class="w"> </span><span class="n">insertion-response</span><span class="p">))</span><span class="w"> </span><span class="s">"author"</span><span class="p">)))))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="nf">testing</span><span class="w"> </span><span class="s">"there should be exactly one book in the bookstore after insertion"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">read-response</span><span class="w"> </span><span class="p">(</span><span class="nf">app</span><span class="w"> </span><span class="p">(</span><span class="nf">mock/request</span><span class="w"> </span><span class="no">:get</span><span class="w"> </span><span class="s">"/books"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">books</span><span class="w"> </span><span class="p">(</span><span class="nf">parse-string</span><span class="w"> </span><span class="p">(</span><span class="no">:body</span><span class="w"> </span><span class="n">read-response</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="n">book</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">books</span><span class="p">)]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">(</span><span class="nb">count</span><span class="w"> </span><span class="n">books</span><span class="p">)))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="nf">book</span><span class="w"> </span><span class="s">"author"</span><span class="p">)</span><span class="w"> </span><span class="s">"Andrzej Sapkowski"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nb">=</span><span class="w"> </span><span class="p">(</span><span class="nf">book</span><span class="w"> </span><span class="s">"title"</span><span class="p">)</span><span class="w"> </span><span class="s">"Last Wish"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">is</span><span class="w"> </span><span class="p">(</span><span class="nf">some?</span><span class="w"> </span><span class="p">(</span><span class="nf">book</span><span class="w"> </span><span class="s">"id"</span><span class="p">)))))</span></code></pre></figure>
<p>Jak widzimy, wywołania testowanych operacji oraz pomocnicze zmienne znajdują się na liście funkcji <code class="highlighter-rouge">let</code>.
Asercje na temat znajdziemy w bloku poniżej.</p>
<p>Tradycyjnie, całość kodu dostępna na <a href="https://github.com/paliwodar/clojure-bookstore">GitHubie</a>.</p>
2017-10-23T00:00:00+00:00http://czasprogramistow.pl/lifehacki-czasu-programistow-1/Sprawne kończenie rozmowy telefonicznej2017-10-18T00:00:00+00:00Rafał PaliwodaMacie czasem tak, że po obgadaniu prawdziwej przyczyny wykonania telefonu, rozmowa traci na tempie i zaczyna poruszać przypadkowe tematy lub zawierać frazy typu “i co tam jeszcze u ciebie”, choć tak naprawdę nie jest to zbytnio interesujące dla żadnej ze stron? <br><br>A może zdarza się wplatać <b>niepotrzebne tłumaczenia podjętej decyzji</b>, czy to biznesowej, czy prywatnej? <br><br>A jak już dojdzie do fazy pożegnania – tutaj szczególnie w przypadku zażyłych relacji – to następuje stanowczo zbyt długa wymiana różnych synonimów zwrotu “cześć”, niczym w problemie bizantyjskich generałów?<br><p>Przykłady powyższe mogą się wydawać nieco przejaskrawione.
Ale nawet jeżeli nie jest aż tak źle – polecam chwilę kontemplacji.</p>
<p>Po pierwsze – <strong>dlaczego tak się dzieje?</strong> Powodów może być wiele, zależnie od przypadku.</p>
<p>Od nieświadomości, niezorganizowania, po poczucie zobowiązania do zadośćuczynienia być
może po przekazaniu niezbyt dobrych wieści. Które to, być może zawiodły czyjeś oczekiwania.
Jak np. informacja o skorzystaniu z oferty konkurencji, do której mieliśmy pełne prawo.</p>
<p><strong>Czas z tym skonczyć i przyjąć zasadę szanowania swojego czasu i własnych wyborów</strong> poprzez
kończenie rozmowy w sposób zdecydowany. Np.:</p>
<blockquote>
<p>Super że udało nam się to ustalić, do widzenia i pozdrawiam.</p>
</blockquote>
<p>Po tym następuje odczekanie jednej lub dwóch sekund i rozłączenie.</p>
<p>Nie dość że oszczędzamy sobie kilka minut gadki-szmatki, to dodatkowo
<strong>budujemy wizerunek osoby pewnej i zdecydowanej</strong>.</p>
<p>Zdobywamy także cenne doświadczenie, które może zaprocentować przy szczególnie trudnych rozmowach,
np. o charakterze negocjacyjnym.</p>
2017-10-18T00:00:00+00:00http://czasprogramistow.pl/minimalizm/Minimalizm2017-09-25T00:00:00+00:00Rafał PaliwodaOstatnio słyszę i czytam na okrągło o minimaliźmie. Mimimalizm tu, minimalizm tam. Less is more, mniej tego, mniej tamtego. Paradoksalnie blogów, artykułów i szumu coraz więcej. Czarę zniecierpliwienia przelał jakiś kompletnie beznadziejny, autopromocyjny dokument, który obejrzałem na Netfliksie. <br><br>No dobra, muszę przyznać, że nie na okrągło (to apropos czytania i słyszenia). Byłoby tak z całą pewnością, gdybym tylko (o ironio) nie był na - modnej jak się ostatnio dowiedziałem - diecie informacyjnej. <br><br> Tak się składa, że chciałbym dodać swoje trzy centy mimochodem. Ale najpierw muszę się rozprawić z ogólnym brakiem ogarnięcia i pomieszaniem na tym polu.<h2 id="wszystko-w-jednym-worku">Wszystko w jednym worku</h2>
<p>Ano, minimalizacja według średniej z definicji z kilku różnych źródeł to sprowadzanie czegoś
do możliwie najmniejszych rozmiarów.</p>
<p>Zgodnie z tym, część minimalistów spędza niewiarygodnie dużo czasu na przegladaniu swoich rzeczy
i na licytacji czy powinni liczyć parę butów jako jeden przedmiot czy dwa.</p>
<p>Nie jestem pewien czy to najbardziej efektywne podejście, ale przynajmniej spójne z nazwą
i analogicznymi nurtami w innych dziedzinach. Coś jak dobrze znany już w latach 60. minimalizm
w literaturze, sztuce i muzyce.</p>
<p>Pozostała część ,,minimalistow’’ głosi jednak, coś po częsci sprzecznego.
Twierdząc, że ten co ma 98 rzeczy niekoniecznie lepszym jest od tego, co ma ich 99.
Bo trzeba być racjonalnym!</p>
<p>To podejście bardziej przypada mi do gustu. Mniej byłoby jednak nieporozumień,
gdybyśmy nie wrzucali tych dwóch kompletnie dla mnie różnych filozofii do jednego worka.</p>
<p>Jakby tego było mało, mam poważne wątpliwości czy chociaż w tym przypadku
mamy do czynienia z czymś choć w ułamku nowatorskim. No i dlaczego to takie <em>trendy</em>,
że blogów o tym jak grzybów po deszczu?</p>
<div>
<center>
<img class="t20" src="http://czasprogramistow.pl/images/photo-tools.jpeg" alt="" />
</center>
</div>
<p><br /></p>
<h2 id="100-things-vs-project-333">100 Things vs Project 333</h2>
<p>Dla niewtajemniczonych informacja że chodzi o dążenie do posiadania maksymalnie
100 rzeczy, bądź noszenia tylko 33 różnych części garderoby przez 3 miesiące.</p>
<p>Rozumiem i zdecydowanie zgadzam się z tym, że w dzisiejszym nadmiarze wszystkiego,
nieskomplikowane do granic możliwości zasady potrafią uprościć życie.</p>
<p>Potrafią one zagwarantować, że jeżeli tylko się do nich zastosujemy,
przezwyciężając trudy i znoje, to osiągniemy zadany cel.</p>
<p>Prosty do bólu algorytm, krew i pot wylane, cel osiągnięty.
Coś jak zrzucenie zbędnych kilogramów, podczas jedzenia wyłącznie brokułów, szpinaku i czarnej kawy.</p>
<p>Albo poprawa statytyk wypadków drogowych przy pomocy fabrycznego ograniczenia prędkosci samochodów do 30 km/h.</p>
<p>Wiecie już do czego piję? Te ograniczenia, tak jak dziesiątki innych reguł i wyzwań,
tak naprawdę nam nie służą. Nie sprzyjają bowiem wykorzystaniu pełni naszego potencjału,
kreatywności i umiejetności myślenia.</p>
<p>Wraz z eliminacją problemu, eliminują wiekszość korzyści z daną aktywnoscią związanych.</p>
<p>Jako że algorytmiczne podejście do rozwiązywania życiowych problemów obcym mi nie jest,
wiem, że niejednokrotnie potrzeba sprytniejszych i trochę bardziej subtelnych środków.</p>
<h2 id="rational-minimalism">,,Rational minimalism’’</h2>
<p>Kompilując informacje z kilku różnych źródeł, mogę stwierdzić, że zwykle minimalizm jest postrzegany jako</p>
<blockquote>
<p>Świadome promowanie wartości w życiu dla nas najważniejszych
i jednoczesna eliminacja wszystkiego co nas przed tym powstrzymuje</p>
</blockquote>
<p>Jest to definicja dość mętna i podatna na interpretacje.
Ale jak już wspomniałem wcześniej, nie chodzi o minimalizację, lecz o pewnego rodzaju wymianę<sup id="fnref:wymiana"><a href="#fn:wymiana" class="footnote">1</a></sup>.</p>
<p>Że możemy być w gruncie rzeczy oszczędni, ale czasem powinniśmy
sobie pozwolić na - operując artystyczną nomenklaturą - bogactwo form i środków ekspresji.</p>
<p>Byle tylko świadomie i zgodnie ze swoimi prawdziwymi potrzebami.</p>
<p>Zastanówmy się jednak dlaczego miałoby tak nie być? Czyżbyśmy byli nieracjonalni z natury?
A może to efekt dzisiejszych czasów?</p>
<h2 id="maksymalizm-nieracjonalny">Maksymalizm nieracjonalny</h2>
<p>Okazuje się że i to, i to. To temat na zupełnie inny artykuł, ale pozwolę sobie wymienić dwa znamienite fakty:</p>
<ol>
<li>Ewolucja nie zaprogramowała nas na bycie racjonalnymi, tylko na bycie w jak największym stopniu
zdolnymi do przekazania swoich genów nastepnym pokoleniom</li>
<li>Żyjemy w realiach coraz bardziej odbiegających od tych, w których ewoluowaliśmy.</li>
</ol>
<div class="alert-box text radius ">
<p>I tak - pewne emocje i zachowania, które miały sens w łowiecko-zbierackiej wiosce,
nie mają uzasadnienia w dzisiejszych czasach. A mimo to wciąż dajemy się im nieświadomie ponosić.</p>
</div>
<h2 id="dlaczego-ten-ruch-ma-sens">Dlaczego ten ruch ma sens?</h2>
<p>Bo adresuje faktyczny problem. W dzisiejszych czasach jesteśmy zalewani
informacjami. Reklamami itp. Media w tym społecznościowe wmawiają nam
co mamy lubić, robić, kupować, ogladać.</p>
<p>Biją się o nasza uwagę. Jesteśmy w sposób ciągły rozpraszani przez jeszcze
bardziej zapierający dech w piersiach filmik.</p>
<p>Podobnie widzimy znacznie więcej osób, niż tylko naszego sasiada, które kupują coraz bardziej wypasione
ciuchy czy też wybierają się na coraz bardziej odległe zakątki świata.</p>
<p>Całe media społecznościowe to nieskończony łańcuch wstępujący tego typu materiałów.
Stąd też naturalnie inicjatywy zachęcające nas do ograniczenia wszelkiego rodzaju konsumpcji.</p>
<p>Kiedyś mogliśmy przynajmniej przejrzeć wszystko, dobić do dna i zająć się czymś innym.
No albo np. kupić najbardziej opłacalny model i mieć spokój na rok czy dwa. Teraz to niemożliwe.</p>
<p>Coraz częściej odzywa się potrzeba zdecydowanych zmian.
Kroku, a może nawet skoku w kierunku powrotu do dawnego spokoju<sup id="fnref:dawny_spokoj"><a href="#fn:dawny_spokoj" class="footnote">2</a></sup>.</p>
<div>
<center>
<img class="t20" src="http://czasprogramistow.pl/images/minimalism-peace.jpg" alt="" />
</center>
</div>
<p><br /></p>
<h2 id="dlaczego-jest-tylu-wieszczów">Dlaczego jest tylu wieszczów?</h2>
<p>Wielu pragnie rozpocząć tzw. <em>online business</em>. Wielu chce znaleźć swoją niszę.
Są wśród nich osoby bardziej lub mniej zorganizowane. Bardziej lub mniej podane na sugestie i kopiowanie innych.</p>
<p>Niektórzy z nich mogliby powiedzieć: ,,Hej, to w sumie to, co ja robię od lat.
Inni dopiero teraz na to wpadli. Dopiero teraz zaczęli sobie to uświadamiać.’’
No i nie widzę powodu, żeby nie zaczęli pisać o tym swoim minimalistycznym życiu.</p>
<p>Niestety obserwuję, że z drugiej strony jest w tym temacie sporo naciąganej autokreacji
na osoby żyjące w ten sposób. Nakazuje się zatem - zgodny z minimalisycznym stylem -
dystans i umiarkowany poziom zaufania do treści sprzedawanej przez minimalistów.</p>
<p>W porządku natomiast, póki ktoś pisze tak, jak naprawdę u niego jest i bez zbytniego nadęcia.</p>
<p>Przyznam, że przeczytalem kilka książek w tym temacie.
Wiekszość z nich jednak w celu ćwiczenia angielskiego, który w tego typu literaturze jest dość prosty.</p>
<h2 id="czy-to-coś-nowego">Czy to coś nowego?</h2>
<p>Oczywiście, że to nic nowego. I nie jest to wcale na tyle odkrywcze żeby niektórzy guru
musieli ewangelizować innych z namaszczeniem i przekonaniem o odkryciu Ameryki.</p>
<p>To jest zwyczajnie powrót do normalności<sup id="fnref:normalnosc"><a href="#fn:normalnosc" class="footnote">3</a></sup>.</p>
<p>Pewnie jest takie prawo, które mówi, że ludzkość cyklicznie w nieskończoność
odkrywa wciąż te same rzeczy i koncepcje na nowo. A jeżeli jeszcze takowego nie ma,
to możecie je nazwać moim nazwiskiem.</p>
<div class="footnotes">
<ol>
<li id="fn:wymiana">
<p>No i faktem jest, że często chodzi o mniejszą ilość, ale lepszą jakość <a href="#fnref:wymiana" class="reversefootnote">↩</a></p>
</li>
<li id="fn:dawny_spokoj">
<p>Prawdopodobnie odniesienie do pewnych cech stanu rzeczy sprzed ery konsumpcjonizmu. <a href="#fnref:dawny_spokoj" class="reversefootnote">↩</a></p>
</li>
<li id="fn:normalnosc">
<p>j.w. <a href="#fnref:normalnosc" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>
2017-09-25T00:00:00+00:00http://czasprogramistow.pl/clojure-restful-crud-service/Clojure RESTful CRUD service2017-06-18T00:00:00+00:00Rafał PaliwodaRozwinięcie artykułu <a href='/twoj-pierwszy-rest-serwis-w-clojure'>o stawianiu webserwisu opartego na Compojure</a>.
Pokazałem wtedy jak stworzyć od podstaw projekt, który z grubsza jedynie wita się ze światem. <br><br> Przetransformujemy go teraz w minimalną działajacą aplikację. Będzie to fragment systemu obsługującego księgarnię. Celem jest napisanie REST-owego interfesju do zarządzania bazą książek.<p>Pozwoli on pobranie zbioru książek, a także na jego modyfikację –
dodanie nowej, usunięcie i modyfikację istniejących.
Nic skomplikowanego, ale od czegoś trzeba przecież zacząć.</p>
<p>Każda książka będzie miała autora, tytuł i rok wydania.
Na razie tyle nam wystarczy.</p>
<p>Przykładowy JSON:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="s2">"id"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"99a54b35-53b6-4d73-a640-607f42f14fb0"</span><span class="p">,</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="s2">"author"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Andrzej Sapkowski"</span><span class="p">,</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="s2">"title"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Last Wish"</span><span class="p">,</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="s2">"year"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"1993"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /></span><span class="p">}</span></code></pre></figure>
<h2 id="rest">REST?</h2>
<p>O pełną definicję się niestety nie pokuszę. Nie wiem czy w ogóle takowa istnieje.</p>
<p>Powiem jednak, że jest to implementacja jednego ze stylów architektury integracyjnej -
RPC, czyli zdalnego wywołania procedury<sup id="fnref:rpc"><a href="#fn:rpc" class="footnote">1</a></sup>. Oparta na protokole HTTP.</p>
<p>Mówimy z angielskiego, że serwis jest RESTful. No i może on być RESTful w różnym stopniu.
Możemy np. wystawić tylko jeden <em>endpoint</em> i obsługiwać zapytania inaczej w zależności
od przesłanej treści.</p>
<p>Można wyodrębnić oddzielne zasoby, posiadające odzielne URL-e, a także obsługiwać
poprawnie <em>HTTP verbs</em> jak GET, POST, PUT, etc w celu wykorzystania cache’owania.</p>
<p>Więcej o tym możemy przeczytać w <a href="https://martinfowler.com/articles/richardsonMaturityModel.html">artykule Martina Fowlera</a>
(XML) albo w <a href="https://dzone.com/articles/richardson-maturity-model-and-pizzas">tym artykule na DZone</a> (JSON).</p>
<h2 id="handler">Handler</h2>
<p>Przypomnijmy, że nasz handler wygląda do tej pory tak:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">defroutes</span><span class="w"> </span><span class="n">app-routes</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/greeting"</span><span class="w"> </span><span class="p">[</span><span class="nb">name</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">str</span><span class="w"> </span><span class="s">"Hello, "</span><span class="w"> </span><span class="p">(</span><span class="nb">or</span><span class="w"> </span><span class="nb">name</span><span class="w"> </span><span class="s">"World"</span><span class="p">)</span><span class="w"> </span><span class="s">"!"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">route/not-found</span><span class="w"> </span><span class="s">"Not Found"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">app</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">handler/api</span><span class="w"> </span><span class="n">app-routes</span><span class="p">))</span></code></pre></figure>
<p>Pierwsza rzecz jaką zrobimy jest dodanie obsługi jsona. Mam tutaj na myśli transformację
z tekstu np. <em>”{ "author":"Hickey" }”</em> na odpowiadajacą mu strukturę Clojure,
np. mapę <em>{ :author “Hickey” }</em>. I na odwrót dla <em>response</em>.</p>
<p>Istnieje wiele bibliotek, które mogą to dla nas załatwić jak np. chessire, data.json.
W naszym przypadku użyjemy tego, co oferuje nam <a href="https://github.com/ring-clojure/ring">Ring</a>,
na którym oparty jest Compojure, czyli funkcji:</p>
<p><code class="highlighter-rouge">(wrap-json-response), (wrap-json-body)</code></p>
<p>Zmienimy także <code class="highlighter-rouge">handler/site</code> and <code class="highlighter-rouge">handler/api</code>, który zgodnie z dokumentacją
zdaje się lepiej odpowiadać naszym potrzebom: <em>“Create a handler suitable for a web API(…)”</em>.</p>
<p>Funkcja <em>app</em> wyglada teraz tak:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="w"> </span><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">app</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nb">-></span><span class="w"> </span><span class="p">(</span><span class="nf">handler/api</span><span class="w"> </span><span class="n">app-routes</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">wrap-json-body</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">wrap-json-response</span><span class="p">)))</span></code></pre></figure>
<p>Poslużyliśmy się tutaj wygodnym niekiedy makrem <code class="highlighter-rouge">-></code>, które pozwala na bardziej czytelne
wyrażenie złożenia funkcji. Kod zwraca <code class="highlighter-rouge">(wrap-json-response (wrap-json-body (handler/api app-routes)))</code>.</p>
<p>W tym przypadku najpierw tworzymy <em>handlera</em> naszych zadań http aplikując funkcję
handler/api do instancji definicji routingu. Nastepnie ,,wzbogacamy’’ go
o obsługę jsona.</p>
<p>Dla większego zrozumienia warto porównać implementację <code class="highlighter-rouge">wrap-json-body</code> z <code class="highlighter-rouge">wrap-json-response</code>.</p>
<h2 id="routes">Routes</h2>
<p>Definicja routingu tworzy szkielet naszej aplikacji. Tutaj określamy
jakie mamy mieć <em>endpointy</em> i jakie funkcje je obsługują.</p>
<dl>
<dt>GET /books</dt>
<dd>Zwraca zbiór wszystkich książek. W Compojure wyrażony przez <code class="highlighter-rouge">(GET "/books" [] (read-books)</code>, gdzie
<em>read-books</em> odpowiada za operację czytania. Zobaczymy jej implementację niebawem.</dd>
<dt>GET /books/[id]</dt>
<dd>Zwraca książkę o podanym id. Definicja tej ruty to <code class="highlighter-rouge">(GET "/books/:id" [id] (read-book-by-id id))</code></dd>
<dt>POST /books</dt>
<dd>Dodanie nowej książki – <code class="highlighter-rouge">(POST "/books" {body :body} (insert-new-book body))</code></dd>
</dl>
<p>Możemy zauważyć, że wszystkie te operacje odnoszą się do tego samego zasobu <em>/books</em>.
Compojure pozwoli nam zgrupować je używając makra <code class="highlighter-rouge">context</code>.</p>
<p>Trzeba podkreślić ponadto, że starałem się oddzielić logikę operacji od definicji routingu,
przy użyciu pomocniczych funkcji. Jako zaawansowany początkujący w Clojure,
nie znam wszystkich konwencji, ale podowiadał mi to zdrowy rozsądek.</p>
<p>Podsumujmy zatem czego udało nam się dokonać do tej pory.
Poniżej kod routingu wraz z dodatkowymi operacjami usuwania i modyfikacji.</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">defroutes</span><span class="w"> </span><span class="n">app-routes</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">context</span><span class="w"> </span><span class="s">"/books"</span><span class="w"> </span><span class="p">[]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">(</span><span class="nf">read-books</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">POST</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">{</span><span class="n">body</span><span class="w"> </span><span class="no">:body</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="nf">insert-new-book</span><span class="w"> </span><span class="n">body</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">context</span><span class="w"> </span><span class="s">"/:id"</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">(</span><span class="nf">read-book-by-id</span><span class="w"> </span><span class="n">id</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">PUT</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">{</span><span class="n">body</span><span class="w"> </span><span class="no">:body</span><span class="p">}</span><span class="w"> </span><span class="p">(</span><span class="nf">update-existing-book</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="n">body</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">DELETE</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">(</span><span class="nf">delete-book</span><span class="w"> </span><span class="n">id</span><span class="p">))))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">route/not-found</span><span class="w"> </span><span class="s">"Not Found"</span><span class="p">))</span></code></pre></figure>
<h2 id="logika-biznesowa">,,Logika biznesowa’’</h2>
<p>Każda aplikacja musi posiadać <em>warstwę</em> logiki biznesowej<sup id="fnref:logika"><a href="#fn:logika" class="footnote">2</a></sup>.
Nasza nie będzie gorsza. Ta logika, to implementacja funkcji takich jak <code class="highlighter-rouge">read-books</code>.</p>
<p>W naszym przypadku operacje te będą głównie bazować na odwołaniach do bazy danych.
Być może z dodakiem pewnych dodatkowych detali.</p>
<p>No i właśnie ta pierwsza funkcja, podobnie jak ta odpowiadajaca za usuwanie książek
będą mieć trywialną implementację:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">read-books</span><span class="w"> </span><span class="p">[]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">db/read-books</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">delete-book</span><span class="w"> </span><span class="p">[</span><span class="n">book-id</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">db/delete-book</span><span class="w"> </span><span class="n">book-id</span><span class="p">))</span></code></pre></figure>
<p>Zwyczajnie odwołują się one do funkcji bazodanowych. Trochę ciekawiej to wygląda
w przypadku dostępu do konkretnego zasobu książkowego:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">read-book-by-id</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">let</span><span class="w"> </span><span class="p">[</span><span class="n">results</span><span class="w"> </span><span class="p">(</span><span class="nf">db/read-book</span><span class="w"> </span><span class="n">id</span><span class="p">)]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nf">empty?</span><span class="w"> </span><span class="n">results</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">{</span><span class="no">:status</span><span class="w"> </span><span class="mi">404</span><span class="p">}</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">ring.util.response/response</span><span class="w"> </span><span class="p">(</span><span class="nb">first</span><span class="w"> </span><span class="n">results</span><span class="p">)))))</span></code></pre></figure>
<p>Funkcja <code class="highlighter-rouge">db/read-book</code> jedynie wykonuje <em>select</em> zwracając listę wierszy z książkami.
A zatem pozostaje nam wyłuskac wynikowa książkę oraz obsłużyć przypadek braku wyników.
Zwracamy wtedy naturalnie kod 404, czyli <em>not found</em>.</p>
<h2 id="baza-danych">Baza danych</h2>
<p>Książki musimy gdzieś trzymać. Potrzebujemy zatem bazy danych.
Na potrzeby naszego przykładu będzie to H2, istniejąca tylko w pamięci - tzw. <em>in-memory database</em>.
Będziemy mogli w dowolnym momencie to zmienić, nie modyfikując kodu naszej aplikacji.</p>
<p>Dla lepszego uporządkowania cały kod związany z bazą znajdzie się w oddzielnym namespace – <code class="highlighter-rouge">bookstore-rest.db</code>.</p>
<h4 id="połączenie">Połączenie</h4>
<p>Po pierwsze zdefiniujmy parametry połączenia do naszej bazy.
Mogą one wygladać np. tak:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">db-spec</span><span class="w"> </span><span class="p">{</span><span class="no">:classname</span><span class="w"> </span><span class="s">"org.h2.Driver"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:subprotocol</span><span class="w"> </span><span class="s">"h2"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:subname</span><span class="w"> </span><span class="s">"mem:bookstore;DB_CLOSE_DELAY=-1"</span><span class="p">})</span></code></pre></figure>
<p>Widzimy powyżej mapę z nastepującymi parametrami: klasę sterownika połączenia do bazy, protokół oraz dodatkowe parametry bazy H2.</p>
<div class="alert-box text radius ">
<h6>Wskazówka</h6>
<smaller>Dla uproszczenia przykładu z bazą danych będziemy sie łaczyć na nowo przy każdym zapytaniu.
Prawidłową techniką byłoby wykorzytywanie puli istniejących połączeń. Rozwinięcie tematu niebawem.
Na teraz mogę wspomnieć, że mamy do dyspozycji np. ComboPooledDataSource z biblioteki C3P0 (java).
Stąd też "DB_CLOSE_DELAY=-1" – chcemy żeby baza dalej była dostępna, mimo braku aktywnych z nią połączeń.</smaller>
</div>
<p>Możemy więc już łączyć się z bazą. No ale najpierw musimy ją odpowiednio zainicjalizować.</p>
<h4 id="tworzenie-tabeli">Tworzenie tabeli</h4>
<p>Ring pozwala na zdefiniowanie funkcji wywoływanej przy starcie serwera. To idealne miejsce na umieszczenie
funkcji tworzącej tabele w bazie, która w końcu będzie żyła tak długo jak i serwer.</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">init</span><span class="w"> </span><span class="p">[]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/db-do-commands</span><span class="w"> </span><span class="n">db-spec</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/create-table-ddl</span><span class="w"> </span><span class="no">:books</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[[</span><span class="no">:id</span><span class="w"> </span><span class="s">"varchar(256)"</span><span class="w"> </span><span class="s">"primary key"</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="no">:author</span><span class="w"> </span><span class="s">"varchar(32)"</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="no">:title</span><span class="w"> </span><span class="s">"varchar(64)"</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="no">:year</span><span class="w"> </span><span class="s">"integer"</span><span class="p">]])))</span></code></pre></figure>
<p>Funkcja ta jest podpięta do ringa z poziomu project.clj. Alternatywnie możemy dodać
w razie potrzeby funkcję sprzątająca, wywoływaną podczas zatrzymania serwera.</p>
<h4 id="operacje-bazodanowe">Operacje bazodanowe</h4>
<p>Stworzyliśmy już szkielet routingu. Teraz pokażę implementacje funkcji odpowiadających za poszczególne operacje.
Nie będzie tutaj zbyt dużo magii. Ot, zwykłe wywołania zapytań SQL za pośrednictwem pakietu <code class="highlighter-rouge">clojure.java.jdbc</code>:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">read-books</span><span class="w"> </span><span class="p">[]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/query</span><span class="w"> </span><span class="n">db-spec</span><span class="w"> </span><span class="p">[</span><span class="s">"SELECT * FROM books"</span><span class="p">]))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">insert-book</span><span class="w"> </span><span class="p">[</span><span class="n">body</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/insert!</span><span class="w"> </span><span class="n">db-spec</span><span class="w"> </span><span class="no">:books</span><span class="w"> </span><span class="n">body</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">read-book</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/query</span><span class="w"> </span><span class="n">db-spec</span><span class="w"> </span><span class="p">[</span><span class="s">"SELECT * FROM books where id = ?"</span><span class="n">,</span><span class="w"> </span><span class="n">id</span><span class="p">]))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">delete-book</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/execute!</span><span class="w"> </span><span class="n">db-spec</span><span class="w"> </span><span class="p">[</span><span class="s">"DELETE FROM books where id = ?"</span><span class="n">,</span><span class="w"> </span><span class="n">id</span><span class="p">]))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">update-book</span><span class="w"> </span><span class="p">[</span><span class="n">id</span><span class="w"> </span><span class="n">body</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">jdbc/update!</span><span class="w"> </span><span class="n">db-spec</span><span class="w"> </span><span class="no">:books</span><span class="w"> </span><span class="n">body</span><span class="w"> </span><span class="p">[</span><span class="s">"id = ?"</span><span class="w"> </span><span class="n">id</span><span class="p">]))</span></code></pre></figure>
<h4 id="na-początek-wystarczy-ale">Na początek wystarczy, ale…</h4>
<p>O tym jak dodać obsługę <em>poolingu</em> połączeń przeczytamy więcej na
<a href="http://clojure-doc.org/articles/ecosystem/java_jdbc/connection_pooling.html">clojure-doc.org</a>.</p>
<p>Jeżeli wiążemy jakieś większe nadzieje z projektem, to powinniśmy
pomyśleć o obsłudze migracji. Być może przy użyciu jednej z bibliotek wymienionych
na dole <a href="http://clojure-doc.org/articles/ecosystem/java_jdbc/home.html#where-to-go-beyond-java-jdbc">tej samej strony</a>.</p>
<h2 id="możliwe-wariacje-api">Możliwe wariacje API</h2>
<p>Możemy się spotkać z nieznacznymi różnicami czy też usprawnieniami w realizacji tego typu serwisów. Takimi jak:</p>
<ul>
<li>brak <em>respose body</em> dla operacji POST, zamiast tego <em>Location header</em> wskazujący skąd pobrać nowo utworzony zasób</li>
<li>PUT - obsługa statusów 404, czy też 409</li>
<li>DELETE – odpowiedź 404 w przypadku braku obiektu</li>
<li>można mieć dylemat czy id powinno być częścią jsona i zwracane w GET czy może nie, szczególnie dziwnie może
może wyglądać PUT z różnymi id w URL i body</li>
<li>wykorzystanie ETAG do weryfikacji najświeższej wersji zasobu</li>
</ul>
<h2 id="przetestujmy">Przetestujmy!</h2>
<p>Sprawdźmy zatem jak działa nasz serwis w akcji. Użyjemy do tego zwyczajnie programu curl.</p>
<div class="alert-box terminal radius ">
<p>$ curl localhost:3000/books <br />
[] <br />
<br />
$ curl -X POST localhost:3000/books -d ‘{“author”:”Andrzej Sapkowski”,”title”:”Last Wish”, “year”: 1993}’ -H “Content-Type: application/json” <br />
{“id”:”af73fc98-1e12-4285-871a-25a201cf1bbc”,”author”:”Andrzej Sapkowski”,”title”:”Last Wish”,”year”:1993} <br />
<br />
$ curl localhost:3000/books <br />
[{“id”:”af73fc98-1e12-4285-871a-25a201cf1bbc”,”author”:”Andrzej Sapkowski”,”title”:”Last Wish”,”year”:1993}] <br />
<br />
$ curl -X DELETE http://localhost:3000/books/af73fc98-1e12-4285-871a-25a201cf1bb <br />
$ curl localhost:3000/books <br />
[]</p>
</div>
<h2 id="zwiastun">Zwiastun</h2>
<p>Z powyższego opisu wynikać może, że pominęliśmy do tej pory bardzo ważny aspekt: testowanie.</p>
<p>Powiemy sobie o nich niejako post factum, w <a href="/clojure-webservice-testowanie">jednym z kolejnych wpisów</a>.</p>
<p>Oczywiście nie znaczy to, że czekamy z testowaniem, aż skończymy pisać cały kod aplikacji.
Testy – w mojej opinii – powinny się przynajmniej implementacją zazębiać,
tak aby mieć pewność z kod działa jak należy z jak najmniejszym opóźnieniem.</p>
<p>Zwykle nie jest dla mnie aż tak istotne czy najpierw piszemy testy (TDD) czy też potwierdzamy
poprawne działanie już napisanego kodu. Coraz częściej – świadom wad i zalet – skłaniam się ku temu drugiemu.
Ale to temat na zupełnie inną okazję.</p>
<p>Póki co, całość kodu dostępna na <a href="https://github.com/paliwodar/clojure-bookstore">GitHubie</a>.</p>
<div class="footnotes">
<ol>
<li id="fn:rpc">
<p>Wiem, zwykle RPC kojarzy się z czymś innym, ale mam tutaj na myśli znaczenie ogólne. <a href="#fnref:rpc" class="reversefootnote">↩</a></p>
</li>
<li id="fn:logika">
<p>Przynajmniej było tak kiedyś. Teraz częściej mówi się o warstwie domenowej. <a href="#fnref:logika" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>
2017-06-18T00:00:00+00:00http://czasprogramistow.pl/twoj-pierwszy-rest-serwis-w-clojure/Piszemy nasz pierwszy REST-owy webserwis w Clojure2017-04-30T00:00:00+00:00Rafał PaliwodaW artykule tym spotkamy się po raz pierwszy z Clojure. Fantastycznym nowoczesnym dialektem Lispa, działającym na JVM. Mógłbym o nim mówić w samych superlatywach. <br><br> Na to jednak będziecie musieli trochę poczekać. Z pewnością nastąpi to w jednym z najbliższych postów. Teraz bowiem będzie krótko, zwięźle, do rzeczy i technicznie. <br><br> Napiszemy najprostszy możliwy REST-owy serwis. Cóż więc innego mielibyśmy napisać, jeżeli nie sztandarowe <i>hello world</i>?<h2 id="clojure">Clojure</h2>
<p>Dla ułatwienia zrozumienia kodu, który pojawi się niebawem, zapoznajmy się ze składnią Clojure.
A konkretnie z faktem, iż język ten opiera się na tzw. notacji polskiej, czyli prefiksowej.</p>
<p>Funkcje i operatory zawsze pojawiają się przed argumentami. Ponadto ujęte są w nawiasy,
w następujący sposób:</p>
<pre>
(funkcja argument1 argument2)
(println "Hello World!")
(+ 1 2)
(== (+ 1 2) 3)</pre>
<h2 id="cel">Cel</h2>
<p>Stworzymy bardzo prosty webserwis, który bedzie posiadał jeden endpoint: <em>/greeting</em>.</p>
<p>Będzie on pozdrawiał świat używając plain teksu. Będzie także reagował na parametr <em>name</em>.
I tak zachowanie opisać możemy następująco:</p>
<div class="alert-box terminal radius ">
<p>curl localhost:3000/greeting <br />
Hello, World! <br />
<br />
curl localhost:3000/greeting?name=Jack <br />
Hello, Jack!</p>
</div>
<h2 id="tworzymy-projekt">Tworzymy projekt</h2>
<p>Użyjemy leiningen – systemu budowania projektu i menadżera zależności dla Clojure.
To taki w przybliżeniu odpowiednik mavena czy gradle.</p>
<p>Pozwoli on nam szybko i bezboleśnie stworzyć projekt wykorzystujący Compojure,
czyli coś co wpada do kategorii <em>routing library</em> i
pozwala na przyporzadkowanie URL-om odpowiadających funkcji Clojure.</p>
<div class="alert-box terminal radius ">
<p>lein new compojure clojure-hello</p>
</div>
<p>Po uruchomieniu powyższego powinniśmy dostac świeżo utworzony projekt, który niebawem zmodyfikujemy.</p>
<p>Poniższa komenda pozwoli nam na uruchomienie tegoż serwisu.
Możemy takim go zostawić, będzie się on samoczynnie uaktualniał.</p>
<div class="alert-box terminal radius ">
<p>lein ring server</p>
</div>
<h2 id="leiningen">Leiningen</h2>
<p>Polecam w tym momencie przejście szybkiego tutoriala. Oczywiście najpierw instalacja.
A potem <code class="highlighter-rouge">lein help tutorial</code>. Przyda się jako, że jest to bardzo podstawowe narzedzie dla Clojure.</p>
<p>Pozwolę sobie tutaj wymienić kilka informacji. Niektóre z nich pojawiają się w samouczku.</p>
<dl>
<dt>Szukanie zależności</dt>
<dd>Informacje na temat np. wersji znajdziemy na <em>clojars.org</em>. Możemy
też wyszykiwać przy pomocy Leiningen np. <code class="highlighter-rouge">lein search clj-http</code></dd>
<dt>Checkout dependencies</dt>
<dd>Mechanizm pozwalający na sprawny devlopment więcej niż jednego projektu jednocześnie,
nie musimy wtedy restartować REPL-a<sup id="fnref:repl"><a href="#fn:repl" class="footnote">1</a></sup></dd>
<dt>Przydatne komendy</dt>
<dd><code class="highlighter-rouge">lein repl</code> – uruchomienie pętli REPL <br />
<code class="highlighter-rouge">lein run</code> – wywołanie kodu funkcji -main, jeżeli jest ona zdefiniowana <br />
<code class="highlighter-rouge">lein test</code> – uruchomienie testów</dd>
</dl>
<p>Znajdziemy tam także informacje, jak obchodzić się z projektem
w zależnosci czy ma on byc dostarczony końcowemu użytkownikowi,
udostepniony jako biblioteka, czy też uruchomiony na serwerze.</p>
<p>W zależności od zastosowania możemy chcieć utworzyć uberjar,
dodać bibliotekę to clojars lub zbudować projekt
bazując na wybranych szablonach projektów webowych,
jak compojure w naszym przypadku.</p>
<h2 id="do-kodu">Do kodu!</h2>
<p>Nasz świeżo utworzony projekt powinien posiadać następującą strukturę:</p>
<pre>
.
├── README.md
├── project.clj
├── resources
│ └── public
├── src
│ └── compo_standard
│ └── handler.clj
└── test
└── compo_standard
└── handler_test.clj
</pre>
<p>Najważniejsze pliki to project.clj i handler.clj.</p>
<p>Ten pierwszy to odpowiednik build.gradle czy też mavenowego pom.xml.
Określa on zależności naszego projektu i definiuje <em>handlera requestów</em>.
Początkowo ma on taką treść:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">defproject</span><span class="w"> </span><span class="n">compo-standard</span><span class="w"> </span><span class="s">"0.1.0-SNAPSHOT"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:description</span><span class="w"> </span><span class="s">"FIXME: write description"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:url</span><span class="w"> </span><span class="s">"http://example.com/FIXME"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:min-lein-version</span><span class="w"> </span><span class="s">"2.0.0"</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:dependencies</span><span class="w"> </span><span class="p">[[</span><span class="n">org.clojure/clojure</span><span class="w"> </span><span class="s">"1.8.0"</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="n">compojure</span><span class="w"> </span><span class="s">"1.5.1"</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="n">ring/ring-defaults</span><span class="w"> </span><span class="s">"0.2.1"</span><span class="p">]]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:plugins</span><span class="w"> </span><span class="p">[[</span><span class="n">lein-ring</span><span class="w"> </span><span class="s">"0.9.7"</span><span class="p">]]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:ring</span><span class="w"> </span><span class="p">{</span><span class="no">:handler</span><span class="w"> </span><span class="n">compo-standard.handler/app</span><span class="p">}</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="no">:profiles</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">{</span><span class="no">:dev</span><span class="w"> </span><span class="p">{</span><span class="no">:dependencies</span><span class="w"> </span><span class="p">[[</span><span class="n">javax.servlet/servlet-api</span><span class="w"> </span><span class="s">"2.5"</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="n">ring/ring-mock</span><span class="w"> </span><span class="s">"0.3.0"</span><span class="p">]]}})</span></code></pre></figure>
<p>Na nasze potrzeby wygląda nieźle. Gorliwi mogą dodać opis i link do ich strony.
Następnie zajrzymy do pliku handler.clj. Tak wygląda jego główna funkcja:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">defroutes</span><span class="w"> </span><span class="n">app-routes</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/"</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="s">"Hello World"</span><span class="p">)</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">route/not-found</span><span class="w"> </span><span class="s">"Not Found"</span><span class="p">))</span></code></pre></figure>
<p>Zmodyfikujemy ją zatem tak żeby odpowiadała naszym potrzebom:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">defroutes</span><span class="w"> </span><span class="n">app-routes</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/greeting"</span><span class="w"> </span><span class="p">[</span><span class="nb">name</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">str</span><span class="w"> </span><span class="s">"Hello, "</span><span class="w"> </span><span class="p">(</span><span class="nb">or</span><span class="w"> </span><span class="nb">name</span><span class="w"> </span><span class="s">"World"</span><span class="p">)</span><span class="w"> </span><span class="s">"!"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">route/not-found</span><span class="w"> </span><span class="s">"Not Found"</span><span class="p">))</span></code></pre></figure>
<p>I już, wszystko powinno śmigać jak należy. W szczegóły zagłębimy się być może przy następnej okazji.
A oto pełny kod handlera:</p>
<figure class="highlight"><pre><code class="language-clojure" data-lang="clojure"><span class="p">(</span><span class="nf">ns</span><span class="w"> </span><span class="n">clojure-hello.handler</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="no">:require</span><span class="w"> </span><span class="p">[</span><span class="n">compojure.core</span><span class="w"> </span><span class="no">:refer</span><span class="w"> </span><span class="no">:all</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="n">compojure.route</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="n">route</span><span class="p">]</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">[</span><span class="n">compojure.handler</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="n">handler</span><span class="p">]))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="nf">defroutes</span><span class="w"> </span><span class="n">app-routes</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">GET</span><span class="w"> </span><span class="s">"/greeting"</span><span class="w"> </span><span class="p">[</span><span class="nb">name</span><span class="p">]</span><span class="w"> </span><span class="p">(</span><span class="nb">str</span><span class="w"> </span><span class="s">"Hello, "</span><span class="w"> </span><span class="p">(</span><span class="nb">or</span><span class="w"> </span><span class="nb">name</span><span class="w"> </span><span class="s">"World"</span><span class="p">)</span><span class="w"> </span><span class="s">"!"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">route/not-found</span><span class="w"> </span><span class="s">"Not Found"</span><span class="p">))</span><span class="w"><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /></span><span class="p">(</span><span class="k">def</span><span class="w"> </span><span class="n">app</span><span class="w"><br data-jekyll-commonmark-ghpages="" /> </span><span class="p">(</span><span class="nf">handler/api</span><span class="w"> </span><span class="n">app-routes</span><span class="p">))</span></code></pre></figure>
<h2 id="zwiastun">Zwiastun</h2>
<p>W <a href="/clojure-restful-crud-service">następnym odcinku </a>spróbujemy czegoś bardziej skomplikowanego.
Jak np. bazy danych i obchodzenia się z JSON-em. <em>Stay tuned!</em></p>
<div class="footnotes">
<ol>
<li id="fn:repl">
<p>Skrót od read-eval-print loop <a href="#fnref:repl" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>
2017-04-30T00:00:00+00:00http://czasprogramistow.pl/o-prologu-arytmetyka-i-rekursja/O Prologu... – arytmetyka i rekursja2017-04-28T00:00:00+00:00Rafał PaliwodaMam nadzieję, że <a href='/o-prologu-slow-kilka'>poprzednim wpisem</a> udało mi się wzbudzić zainteresowanie tematem. Chciałbym dodać, że z Prologiem, podobnie jak z innymi językami o ciekawych paradygmatach, zetknąłem się po raz pierwszy na drugim semestrze informatyki na mojej uczelni. <br><br> Niniejszy wpis jest nieco krótszy, wynika to z tego, że jest on rezultatem połowienia planu pierwotnego. Tematem będzie wykorzystanie arytmetyki maszynowej w Prologu oraz rekursja. Całość bardzo elementarna. Następne zdecydowanie już takie nie będą.<p>Nie uważam się za znawcę Prologa. Ponadto dość sporo czasu minęło od ostatniego
<em>poważnego</em> programowania w tym języku. Pomimo to, ciągle czuję się dość pewnie
w temacie. I śmiało mogę stwierdzić, że nie zapomina się raz opanowanego
podejścia do rozwiązywania problemów.</p>
<h2 id="prawdziwa-arytmetyka">Prawdziwa arytmetyka</h2>
<p>Arytmetyka jaką zadaliśmy <a href="/o-prologu-slow-kilka">poprzednio</a> naturalnie nie mogłaby mieć większego zastosowania.
Chcielibyśmy żeby liczba 20000 zajmowała trochę mniej niz 20 KB i żeby operacje na niej były wykonywane efektywnie.
Prolog oczywiście takie rzeczy oferuje.</p>
<p>Istnieją operatory <code class="highlighter-rouge"><, >, >=, =<, =:=, =/=</code>. Po obu ich stronach muszą wystąpić termy, które obliczają się do liczb.
Mamy do dyspozycji także predykat <code class="highlighter-rouge">is/2</code>, którego działanie zobrazuję na przykładzie (SWI-Prolog):</p>
<div class="alert-box terminal radius ">
<p>?- X is 1+3. <br />
X = 4 <br />
Yes <br />
<br />
?- 1+3 is X. <br />
ERROR: Arguments are not sufficiently instantiated <br />
<br />
?- 1+3 is 1+3.<br />
No <br />
<br />
?- 4 is 1+3. <br />
Yes</p>
</div>
<p>W poprzedniej części jako motywacja wystąpiło obliczanie silnii. Pokażę teraz jak można do tego podejść.
Nie znając zbytnio prologowej arytmetyki moglibyśmy napisać np. tak:</p>
<figure class="highlight"><pre><code class="language-prolog" data-lang="prolog"><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="nv">F</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">N</span><span class="o">=:=</span><span class="m">0</span><span class="p">,</span> <span class="nv">F</span><span class="o">=</span><span class="m">1</span><span class="p">.</span><br data-jekyll-commonmark-ghpages="" /><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="nv">N</span><span class="o">*</span><span class="nv">F</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">N</span><span class="o">></span><span class="m">0</span><span class="p">,</span> <span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="o">-</span><span class="m">1</span><span class="p">,</span><span class="nv">F</span><span class="p">).</span></code></pre></figure>
<p>Wtedy wynikiem obliczania <code class="highlighter-rouge">4!</code> byłoby:</p>
<div class="alert-box terminal radius ">
<p>?- fact(4,X). <br />
X = 4* ((4-1)* ((4-1-1)* ((4-1-1-1)*1)))</p>
</div>
<p>Nie jest to jednak wynik nas satysfakcjonujący, nie mówiąc już o złożoności jego otrzymania.
Kolejne podejście może wyglądać np. tak:</p>
<figure class="highlight"><pre><code class="language-prolog" data-lang="prolog"><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="m">1</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">N</span><span class="o">=:=</span><span class="m">0</span><span class="p">.</span><br data-jekyll-commonmark-ghpages="" /><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="nv">F</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">N</span><span class="o">></span><span class="m">0</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="nv">N1</span> <span class="ss">is</span> <span class="nv">N</span><span class="o">-</span><span class="m">1</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="ss">fact</span><span class="p">(</span><span class="nv">N1</span><span class="p">,</span><span class="nv">F1</span><span class="p">),</span><br data-jekyll-commonmark-ghpages="" /> <span class="nv">F</span> <span class="ss">is</span> <span class="nv">N</span><span class="o">*</span><span class="nv">F1</span><span class="p">.</span></code></pre></figure>
<p>Jest to implementacja zgodna ze specyfikacją.
Widzimy, że na każdym poziomie rekursji <em>N1</em> jest ukonkretniane z wartością <em>N-1</em>, potem <em>F1</em> z wartością
silnii dla argumentu <em>N1</em> i wynik <em>F</em> z rezultatem obliczenia iloczynu <em>N</em> i wartości silnii dla <em>N-1</em>.
Jednak możemy zaimplementować nieco efektywniej eliminując lewostronną rekursję.</p>
<h2 id="rekursja-ogonowa">Rekursja ogonowa</h2>
<p>Z pojęciem <em>tail recursion</em> zetknął się chyba każdy programista języka wspierającego rekursję.
Polega ona na wyeliminowaniu konieczności używania stosu. W językach funkcyjnych na ogół uzyskuje
się ją poprzez użycie funkcji pomocnicznej. Oto dwa przykłady rekursji nieogonowej i ogonowej
odpowiednio w odwracaniu listy (OCaml):</p>
<figure class="highlight"><pre><code class="language-ocaml" data-lang="ocaml"><span class="k">let</span> <span class="k">rec</span> <span class="n">reverse</span> <span class="o">=</span> <span class="k">function</span><br data-jekyll-commonmark-ghpages="" /> <span class="bp">[]</span> <span class="o">-></span> <span class="bp">[]</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">|</span> <span class="n">x</span><span class="o">::</span><span class="n">xs</span> <span class="o">-></span> <span class="n">reverse</span> <span class="n">xs</span> <span class="o">@</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span><br data-jekyll-commonmark-ghpages="" /><span class="p">;;</span></code></pre></figure>
<p><code class="highlighter-rouge">[]</code> jest listą pustą, <code class="highlighter-rouge">x::xs</code> listą o głowe <em>x</em> i ogonie <em>xs</em>. Operator <code class="highlighter-rouge">@</code> łączy dwie listy.
Funkcja ta jest nieefektywna z dwóch powodów.</p>
<p>Pierwszym jest użycie <code class="highlighter-rouge">@</code> ,
którego czas działania jest liniowy względem pierwszego argumentu (co powoduje złożoność kwadratową)
oraz konieczność odkładania na stos częściowych obliczeń. Można to zilustorwać mniej więcej tak:</p>
<pre>
reverse [1;2;3]
reverse [2;3] @ [1]
reverse [3] @ [2] @ [1]
reverse [] @ [3] @ [2] @ [1]
[] @ [3] @ [2] @ [1]
[3] @ [2] @ [1]
[3;2] @ [1]
[3;2;1]
</pre>
<p>Lepszym jest poniższe rozwiązanie:</p>
<figure class="highlight"><pre><code class="language-ocaml" data-lang="ocaml"><span class="k">let</span> <span class="n">reverse</span> <span class="n">xs</span> <span class="o">=</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">let</span> <span class="k">rec</span> <span class="n">aux</span> <span class="n">acc</span> <span class="o">=</span> <span class="k">function</span><br data-jekyll-commonmark-ghpages="" /> <span class="bp">[]</span> <span class="o">-></span> <span class="n">acc</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">|</span> <span class="n">x</span><span class="o">::</span><span class="n">xs</span> <span class="o">-></span> <span class="n">aux</span> <span class="p">(</span><span class="n">x</span><span class="o">::</span><span class="n">acc</span><span class="p">)</span> <span class="n">xs</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">in</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">aux</span> <span class="bp">[]</span> <span class="n">xs</span><br data-jekyll-commonmark-ghpages="" /><span class="p">;;</span></code></pre></figure>
<p>Działanie wygląda tak:</p>
<pre>
reverse [1;2;3]
aux [] [1;2;3]
aux [1] [2;3]
aux [2;1] [3]
aux [3;2;1] []
[3;2;1]
</pre>
<h2 id="rekursja-w-prologu">Rekursja w Prologu</h2>
<p>W Prologu na ogół rozwiązanie jest analogiczne, tyle że skutkiem jest zwiększenie arności predykatu o 1.
Tak może wyglądać efektywna implementacja silnii:</p>
<figure class="highlight"><pre><code class="language-prolog" data-lang="prolog"><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="nv">F</span><span class="p">)</span> <span class="p">:-</span> <span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="m">1</span><span class="p">,</span><span class="nv">F</span><span class="p">).</span><br data-jekyll-commonmark-ghpages="" /><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="nv">F</span><span class="p">,</span><span class="nv">F</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">N</span> <span class="o">=:=</span> <span class="m">0</span><span class="p">.</span><br data-jekyll-commonmark-ghpages="" /><span class="ss">fact</span><span class="p">(</span><span class="nv">N</span><span class="p">,</span><span class="nv">A</span><span class="p">,</span><span class="nv">F</span><span class="p">)</span> <span class="p">:-</span> <span class="nv">N</span><span class="o">></span><span class="m">0</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="nv">N1</span> <span class="ss">is</span> <span class="nv">N</span><span class="o">-</span><span class="m">1</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="nv">A1</span> <span class="ss">is</span> <span class="nv">A</span><span class="o">*</span><span class="nv">N</span><span class="p">,</span><br data-jekyll-commonmark-ghpages="" /> <span class="ss">fact</span><span class="p">(</span><span class="nv">N1</span><span class="p">,</span><span class="nv">A1</span><span class="p">,</span><span class="nv">F</span><span class="p">).</span></code></pre></figure>
<p>Dodany został środkowy argument, który pełni funkcję akumulatora. Widzimy, że na ogół rekursję ogonową
otrzymuje się przez przesunięcie wywołania rekurencyjnego na sam koniec — dlatego jest też zwana prawostronną.</p>
<p>Jako dodatek jeszcze reverse w Prologu:</p>
<figure class="highlight"><pre><code class="language-prolog" data-lang="prolog"><span class="ss">reverse</span><span class="p">(</span><span class="nv">Xs</span><span class="p">,</span><span class="nv">Ys</span><span class="p">)</span> <span class="p">:-</span> <span class="ss">reverse</span><span class="p">(</span><span class="nv">Xs</span><span class="p">,[],</span><span class="nv">Ys</span><span class="p">).</span><br data-jekyll-commonmark-ghpages="" /><span class="ss">reverse</span><span class="p">([],</span><span class="nv">Xs</span><span class="p">,</span><span class="nv">Xs</span><span class="p">).</span><br data-jekyll-commonmark-ghpages="" /><span class="ss">reverse</span><span class="p">([</span><span class="nv">X</span><span class="p">|</span><span class="nv">Xs</span><span class="p">],</span><span class="nv">Ys</span><span class="p">,</span><span class="nv">Zs</span><span class="p">)</span> <span class="p">:-</span> <span class="ss">reverse</span><span class="p">(</span><span class="nv">Xs</span><span class="p">,[</span><span class="nv">X</span><span class="p">|</span><span class="nv">Ys</span><span class="p">],</span><span class="nv">Zs</span><span class="p">).</span></code></pre></figure>
<p>Pojawiły się tutaj listy — <code class="highlighter-rouge">[X|Xs]</code> oznacza listę o głowie <em>X</em> i ogonie <em>Xs</em>.</p>
<h2 id="zwiastun">Zwiastun</h2>
<p>Notka nieco przegadana i dość elementarna, ale w ten sposób zapewniłem sobie ciągłość i możliwość
zajęcia się rzeczami naprawdę ciekawymi bez szkody dla czytelnika.</p>
<p>Następny wpis (<em>in progress</em>) będzie traktował o sposobach manipulacji termami i podtermami oraz o strukturach
danych takich jak listy i tablice(!). Zapraszam.</p>
2017-04-28T00:00:00+00:00http://czasprogramistow.pl/5-restauracji-wartych-odwiedzenia-w-zadarze/5 restauracji wartych odwiedzenia w Zadarze2017-04-25T00:00:00+00:00Rafał PaliwodaDo przeczytania dla wszystkich wybierających się do Zadaru. Dla niewtajemniczonych dodam, że to w Chorwacji ;) Podaję baardzo subiektywną listę miejsc, o których wiem, że warto w nich coś zjeść. Wiem, bo byłem i próbowałem. <br><br>Od razu zaznaczam, że oprócz plusów pojawić się tam mogą też minusy. O tych, które są mi znane, postaram się wspomnieć. <br><br>Lista zdecydowanie nie jest wyczerpująca, jako że nie odwiedziłem jeszcze wszystkiego, co potencjalnie interesujące. Kolejność jakościowa z lekką nutką losowości. Zaczynamy!<h2 id="bruschetta">Bruschetta</h2>
<p>Dla mnie jak do tej pory najlepsza restauracja w Zadarze. Taka trochę z wyższej półki.
Zajmuje czołowe miejsca w różnego rodzaju rankingach, jak np. w Trip Advisor.</p>
<p>Koniecznie polecam ją odwiedzić. Jadałem tam już wielokrotnie. Zawsze towarzyszyły temu bardzo pozytywne wrażenia.
Można tam liczyć na bardzo dobre jedzenie i znakomitą obsługę.</p>
<p>Po ostatniej wizycie zrozumiałem np. jak wielkie znaczenie ma atmosfera, którą w mojej ocenie w największym
stopniu buduje kelner. Nie zawsze i wszędzie miewam przyjemność być obsługiwanym przez kelnera,
na którego twarzy widać 100% dobrych intencji i wręcz przyjacielskość.</p>
<p>Samo jedzenie, żeby nie być gołosłownym popieram zdjęciami. Oto na przykład
wieczorny <strong>omlet z dzikimi szparagami</strong> oraz <strong>deska regionalnych mięs i serów</strong>.</p>
<div class="row t30 b30">
<div class="large-6 columns">
<img src="/images/zadar/IMG_3823.jpg" />
</div>
<div class="large-6 columns">
<img src="/images/zadar/IMG_4017.jpg" />
</div>
</div>
<p>O tych pierwszych trzeba wiedzieć, że bywają gorzkawe i jest to normalne, należy tego wręcz oczekiwać.
Na tej klimatycznej – jakby od Flinstone’ów – desce znajduje się słynny ser z wyspy Pag, którego
koniecznie trzeba skosztować będąc w Chorwacji, następnie karkówka, panchetta, boczek i krem z oliwek.</p>
<p>Poniżej dania główne: <strong>muszle</strong> i filety z <strong>okonia morskiego</strong> z polentą.</p>
<div class="row t30 b30">
<div class="large-6 columns">
<img src="/images/zadar/IMG_4053.jpg" />
</div>
<div class="large-6 columns">
<img src="/images/zadar/IMG_4051.jpg" />
</div>
</div>
<p>Można tam liczyć też na wspaniałą kawę. Szczególnie jeżeli zdarzało się wam być zaskoczonym
po zamówieniu espresso macchiato. Tutaj dostałem, zgodnie z oczekiwaniami, jedynie muśnięcie mlecznej pianki.</p>
<div class="row t30 b30">
<div class="large-6 columns">
<img src="/images/zadar/IMG_4024.jpg" />
</div>
<div class="large-6 columns">
<img src="/images/zadar/IMG_4018.jpg" />
</div>
</div>
<p>Z deserów polecam <strong>czekoladowy suflet</strong>. Sam fanem deserów nie jestem i jem je z rzadka. Ale jak już się skuszam,
to na coś naprawdę dobrego:</p>
<div class="t30 b30">
<img src="/images/zadar/IMG_4052_2.jpg" />
</div>
<h6 id="ceny">Ceny<sup id="fnref:ceny"><a href="#fn:ceny" class="footnote">1</a></sup>:</h6>
<ul>
<li>deska mięs: 130</li>
<li>muszle: 90</li>
<li>seabass: 140</li>
<li>omlet: 60</li>
</ul>
<h2 id="konoba-dalmacja">Konoba Dalmacja</h2>
<p>Bardzo przyjemna, wydająca się jakby ,,domową’’ restauracja. Usytuowana na uboczu w przytulnym i urokliwym patio.
Z jednej strony trudno tam trafić przez przypadek, z drugiej znajduje się bardzo blisko Morskich Organów.</p>
<p>Miałem w niej przyjemność wraz z moją narzeczoną Dagmarą, spożyć bardzo klimatyczną kolację Wielkanocną.
Była to uczta składająca się z ryb i owoców morza. Zresztą, zobaczcie sami:</p>
<div class="t30 b30">
<img src="/images/zadar/IMG_8482.jpg" />
</div>
<p><strong>Doradę</strong> miałem okazję kosztować już wcześniej. To naprawdę bardzo fajna, słodkawa w smaku ryba.
Jeżeli chodzi o <strong>okonie</strong>, to znam bardzo dobrze smak tych łowionych w wodach śródlądowych.
W tym wypadku nieco się on różnił, smakował mi chyba nawet bardziej niż dorada.</p>
<p>Potem pora przyszła na <strong>kalmary</strong>. Muszę przyznać, że do tej pory jadłem je tylko w teorii.
Pojawiały się gdzieś jakieś krążki w jakimś chińskim daniu. Tutaj podane w całości, zgrillowane
do wystąpienia miejscami lekko pikantno-gorzkawego posmaku. Troszkę gumowate, ale zakładam, że takie po prostu są.
Ogółem smaczne.</p>
<p>No i <strong>łosoś</strong>. Niestety nie zapytałem jaki łosoś to był. W sumie w tamtej chwili nie zastanawiałem się nad tym zbyt długo.
Mógł być troszkę bardziej wysmażony. Ale ogólnie pycha.</p>
<p>Niestety kelnerka nie mówiła zbyt dobrze po angielsku. Ale przed przybyciem zrobiliśmy rekonesans,
decydując się zawczasu na ów półmisek zwany Seafood Symphony.
A zatem pozostała tylko kwestia białego wina i wody.</p>
<p>Rachunek za kolację wyniósł 450 kun.</p>
<h2 id="pet-bunara">Pet Bunara</h2>
<p>Restauracja w samym sercu Starego Miasta. Tak naprawdę planowałem wizytę w restauracji obok,
tańszej nieco ponoć. Ale i tu ceny nie wystraszyły. Szczególnie, że kelner polecił
dania wg niego regionalne, ze średniej półki cenowej.</p>
<p>Było to m.in. brodetto przedstawione na zdjęciu poniżej:</p>
<div class="row t30 b30">
<div class="large-6 columns">
<img src="/images/zadar/IMG_4056.jpg" />
</div>
<div class="large-6 columns">
<img src="/images/zadar/IMG_4055.jpg" />
</div>
</div>
<p>Całkiem fajne danie. Taki misz-masz m.in. z krewetkami, mątwą, groszkiem podany z polentą.</p>
<p>Obsługa na wysokim poziomie. Restauracja zalicza się bowiem do top 3 w Zadarze.</p>
<p>Restauracja posiada bardzo przytulny ogródek i jest dość długo czynna. W moim przypadku
to był zasłużony odpoczynek po locie ze Sztokholmu, przybyciu i zakwaterowaniu w apartamencie,
a następnie niespiesznym spacerze na Stare Miasto.</p>
<h6 id="ceny-1">Ceny:</h6>
<ul>
<li>brodetto: 85</li>
</ul>
<h2 id="sime">Sime</h2>
<p>To chyba miejsce z zupełnie innej beczki. Wiem o nim tylko dlatego, że zatrzymaliśmy się w willi
nieopodal na weekend podczas pierwszego pobytu w Zadarze. Jest jedną z najbardziej godnych uwagi
restauracji w swojej okolicy.</p>
<p>Tutaj już nie należy się spodziewać bynajmniej dworskiego zachowania obsługi. Kelnerzy bywają nonszalanccy,
czasem trochę nadęci. Może niezadowoleni, może aż nadto pewni swoich dań, a może to po prostu taki
bałkański styl zachowania – bez patyczkowania się.</p>
<p>Podobało mi się w każdym razie, gdy ostatnio kelner stwierdził, że nasze zamówienie jest dwa razy za duże.
Okazało się, że miał 100% racji. Gdzie indziej moglibyśmy zostać z mnóstwem jedzenia na wynos.</p>
<p>W każdym razie mają tam bardzo dobre pizze. Jedna duża, to aż nadto dla dwojga.
Szczególnie polecam te z truflami, żeby nie było że mówię o daniu zupełnie niezwiązanym z regionem.
By wrócić do smaku tej pizzy polanej ostrą oliwą nagiąłem nawet moje aktualne nawyki żywieniowe.</p>
<div class="row t30 b30">
<div class="large-6 columns">
<img src="/images/zadar/IMG_4081.JPG" />
</div>
<div class="large-6 columns">
<img src="/images/zadar/IMG_4082.JPG" />
</div>
</div>
<h6 id="ceny-2">Ceny:</h6>
<ul>
<li>pizza: 120</li>
<li>krewetki królewskie: 140</li>
</ul>
<h2 id="la-famiglia">La Famiglia</h2>
<p>To bardzo mała restauracja. Dysponuje wyłącznie stolikami pod gołym niebem. Zje się tam
pewne i niedrogie dania. Raczej niewykwintne i niekoniecznie zdrowe ;).</p>
<p>Z pewnością jednak smaczne. Polecam jeżeli ktoś ma akurat zapotrzebowanie na takowe. Będą to pizze, burgery, risotta i makarony.</p>
<p><br /><br /></p>
<h5 id="pozdrawiam-i-życzę-wszystkim-udanej-turystyki-kulinarnej"><em>Pozdrawiam i życzę wszystkim udanej turystyki kulinarnej.</em></h5>
<div class="footnotes">
<ol>
<li id="fn:ceny">
<p>Wszystkie ceny wyrażone w lokalnej walucie <a href="#fnref:ceny" class="reversefootnote">↩</a></p>
</li>
</ol>
</div>
2017-04-25T00:00:00+00:00http://czasprogramistow.pl/jak-wyjechalem-do-szwecji-cz-1/Jak wyjechałem do Szwecji, cz. 12017-03-12T00:00:00+00:00Rafał PaliwodaOd zawsze wiedziałem, że będę chciał wyjechać za granicę. Wyjechać i pomieszkać w zupełnie innym miejscu przez bliżej nieokreśloną ilość czasu. Sprawdzić czy gdzie indziej płynie on inaczej. <br><br> Poznawać i przebywać wśród ludzi o odmiennym spojrzeniu na świat. Uwarunkowanym innymi korzeniami, historią i kulturą, zwyczajami czy panującym tam klimatem.<br><br>
To także dla mnie jeden z filarów życiowej wolności: dzisiaj jestem tutaj, jutro mogę być gdzie zechcę...<p>To bardzo szeroki dla mnie temat, z którym związanych przemyśleń mam mnóstwo.
Teraz jednak zajmę się jednym szczególnym aspektem – zmianą pracy. A konkretnie
procesem rekturacji.</p>
<h2 id="początek">Początek</h2>
<p>Konkretne działania rozpocząłem zaraz po obronie magisterki.
Było to już ładnych parę lat po ukończeniu uczęszczania na studia.
Zwyczajnie nie potrafiłem zrobić tego w terminie. Do trzeciego roku
było sporo nauki, a od czwartego praca.</p>
<p>Większość w zasadzie działań odbyła się w <a href="https://se.linkedin.com/in/rafalpaliwoda">LinkedIn</a>.
Zacząłem od odpisania na kilkanaście nieodpowiedzianych wiadomości z ostatniego roku,
które dotyczyły zagranicznych ofert.</p>
<p>Zapytałem wtedy pewnego jegomościa czy nie ma czasem oferty z Holandii lub Niemiec.
On na to, że ma mnóstwo z Londynu (wiadomo) i jedną ze Szwecji.
No to już mieliśmy o czym rozmawiać, bo Skandynawia też mnie interesowała.</p>
<p>Poniżej materiał reklamowy na temat Sztokholmu jakim zostałem uraczony. Udany zresztą.</p>
<div class="flex-video">
<iframe width="420" height="315" src="//www.youtube.com/embed/CAkdWUjdJyA" frameborder="0" allowfullscreen=""></iframe>
</div>
<p>Na marginesie, wolałbym być może do jakiegoś kraju typowo anglojęzycznego.
Niestety nie ma żadnego fajnego niedaleko. W Szwecji natomiast każdy po angielsku śmiga.</p>
<h2 id="zdalna-częśćprocesu-rekrutacji">Zdalna część procesu rekrutacji</h2>
<p>Po odświeżeniu i wysłaniu mojego CV, nadszedł czas pierwszej rozmowy telefonicznej.
Była ona formalnością – wstępnym rozpoznaniem gruntu. Sprawdzeniem czy intencje obu stron
pokrywają się ze sobą. No i tak naturalnie było.</p>
<p>Następnie dostałem do rozwiązania <strong>test logiczny</strong>. Nie pamiętam dokładnie,
ale posługiwał się on czymś w stylu kropek na szachownicy.
Dla zadanej sekwencji czterech bodaj ustawień, trzeba było dopasować piąte.
Zadanie dość proste, ale wymagało skupienia.</p>
<h3 id="zadanie-programistyczne">Zadanie programistyczne</h3>
<p>Kolejnym krokiem w procesie było <strong>zadanie programistyczne</strong>. Dotyczyło ono zaprogramowania
prostego RESTowego webserwisu z jednym endpointem – <em>/decision</em>,
oczekującym JSONa z danymi użytkownika: emailem, imieniem i nazwiskiem oraz kwoty.</p>
<p>Miał on zwracać decyzję kredytową zgodnie z pewną prostą logiką,
a kolejne wywołania winny zmniejszać dostępny limit kredytowy.</p>
<p>Uproszczeniem był brak wymagania, co do zapisu stanu serwisu w bazie danych.
Językiem miała być Java, a framework dowolny.</p>
<p>Wybrałem Jersey używając minimum potrzebnych technologii. Oryginalny kod dostępny
<a href="https://github.com/paliwodar/risk-decision-process">na githubie</a>.
Podobał się chyba w miarę. Istotnym okazało się dobre pokrycie testami
oraz zapewnienie prawidłowego działania podczas wielu jednoczesnych połączeń.</p>
<p>Poniżej serce aplikacji: kod odpowiedzialny za pobranie aktualnego limitu, podjęcie decyzji
i uwzględnienie ewentualnych zmian w repo.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@POST</span><br data-jekyll-commonmark-ghpages="" /><span class="nd">@Consumes</span><span class="o">(</span><span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span><br data-jekyll-commonmark-ghpages="" /><span class="nd">@Produces</span><span class="o">(</span><span class="n">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span><br data-jekyll-commonmark-ghpages="" /><span class="kd">public</span> <span class="n">CreditDecisionDTO</span> <span class="nf">handleCreditDecisionProcess</span><span class="o">(</span><span class="n">CreditProposal</span> <span class="n">creditProposal</span><span class="o">)</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">performArgumentChecks</span><span class="o">(</span><span class="n">creditProposal</span><span class="o">);</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">CreditExposure</span> <span class="n">creditExposure</span> <span class="o">=</span> <span class="n">creditExposureRepository</span><span class="o">.</span><span class="na">fetchCreditExposureForEmail</span><span class="o">(</span><span class="n">creditProposal</span><span class="o">.</span><span class="na">getEmail</span><span class="o">());</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="n">CreditDecision</span> <span class="n">creditDecision</span> <span class="o">=</span> <span class="n">creditDecisionMaker</span><span class="o">.</span><span class="na">makeCreditDecision</span><span class="o">(</span><span class="n">creditProposal</span><span class="o">,</span> <span class="n">creditExposure</span><span class="o">.</span><span class="na">getExposure</span><span class="o">());</span><br data-jekyll-commonmark-ghpages="" /><br data-jekyll-commonmark-ghpages="" /> <span class="k">if</span> <span class="o">(</span><span class="n">creditDecision</span><span class="o">.</span><span class="na">isAccepted</span><span class="o">())</span> <span class="o">{</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">creditExposure</span><span class="o">.</span><span class="na">increaseExposure</span><span class="o">(</span><span class="n">creditProposal</span><span class="o">.</span><span class="na">getAmount</span><span class="o">());</span><br data-jekyll-commonmark-ghpages="" /> <span class="n">creditExposureRepository</span><span class="o">.</span><span class="na">persistExposure</span><span class="o">(</span><span class="n">creditExposure</span><span class="o">);</span><br data-jekyll-commonmark-ghpages="" /> <span class="o">}</span><br data-jekyll-commonmark-ghpages="" /> <span class="k">return</span> <span class="n">CreditDecisionDTO</span><span class="o">.</span><span class="na">from</span><span class="o">(</span><span class="n">creditDecision</span><span class="o">);</span><br data-jekyll-commonmark-ghpages="" /><span class="o">}</span></code></pre></figure>
<h3 id="rozmowa">Rozmowa</h3>
<p>Zwieńczeniem tej części była Skype’owa rozmowa z moją obecną menadżerką.
Dostałem sporo pytań. Nie pamiętam zbyt wielu szczegółów, ale kojarzę, że nie na wszystkie
było łatwo odpowiedzieć.</p>
<h4 id="jak-to-bywało">Jak to bywało</h4>
<p>Muszę przyznać, że do tej pory – o ile mnie pamięć nie myli – spotkałem się z dwoma rodzajami
rozmów rekrutacyjnych. Pierwszy typ, to pytania techniczne.
Jedyne jakie słyszałem na rozmowach np. w Microsofcie czy Amazonie.</p>
<p>Drugi to tzw. rozmowa <em>haerowa</em>, traktująca np. o tym co jest naszą największą wadą
lub gdzie widzimy siebie za 5 lat. Przeprowadzana głównie przez osoby,
które nie są decyzyjne, nie mają wiedzy o tym jak się pracuje w engineeringu,
a i też nie zajmują się poszukiwaniem talentów.</p>
<p>Zdarzyło mi się kilka razy, ładnych już parę lat temu, przyjechać do siedziby firmy
jedynie w celu przeprowadzenia tak mało znaczącej konwersacji.
Strzelam jednak, że dziś taką praktykę stosuje się coraz rzadziej.</p>
<h4 id="jak-było-teraz">Jak było teraz</h4>
<p>No to tutaj miałem do czynienia z typem trzecim. Sprawdzającym samoorganizację,
sprawność w komunikacji, motywację, dopasowanie do zespołu, wartości którymi się kieruję.</p>
<p>Miałem na przykład opowiedzieć o tym, jakie działania podjąłbym, gdybym się zorientował,
że naszemu zespołowi nie uda się dotrzymać jakiegoś terminu.
Jak widzimy, to nie jakieś <em>rocket science</em>. Do przejścia.</p>
<p>Te pytania generalnie może i nie różniły się diametralnie od tych z drugiej grupy.
Ale fakt, że były zadawane przez osobę osobiście odpowiedzialną za budowanie kilku zależnych
od siebie zespołów, zmienia całkowicie kontekst.</p>
<p>A był to okres dynamicznego rozwoju firmy. Jak się później okazało, miałem dołączyć do zespołu
w podobnym czasie co dwóch kolegów z Brazylii oraz jeden z Argentyny i jeden z Macedonii.</p>
<p><em>To be continued</em></p>
2017-03-12T00:00:00+00:00