czwartek, 31 stycznia 2013

Programowanie webowe w Haskellu cz. 2

Oto kolejna część poświęcona programowaniu webowemu w Haskellu. Postaram się w niej opisać dwie rzeczy, pierwsza to jak poradzić sobie z wieczną rekompilacją kodu i druga to system szablonów Heist. Zatem do dzieła!

Programowanie bez kompilowania

W snapie możliwe jest pisanie kodu bez ciągłego wywoływania cabal install (czy też cabal build) po każdej zmianie. Aby to umożliwić przechodzimy do katalogu z projektem i wywołujemy:
$ cabal clean ; cabal install -f development
To może zająć chwilę z uwagi na to, że możecie nie mieć odpowiednich bibliotek. Gdy się już przekompiluje to uruchamiamy nasz projekt tak jak ostatnio (czyli ./dist/build//) i w przeglądarce wpisujemy http://localhost:8000. Powinniśmy ujrzeć nasze "Hello world!". No to teraz niech nasz projekt sobie dalej działa, a my zmieńmy nieco plik Site.hs. Odnajdźmy linijkę writeText "<h1>Hello world!</h1>" i zamieńmy ją na writeText "<h1>Hello new world!</h1>" po czym zapiszmy plik. Gdy odświeżymy stronę zmiany powinny zostać naniesione.

Spróbujmy teraz wywołać błąd. Dopiszmy w tej samej linijce np. cyfrę 1, tylko, że za cudzysłowem. O tak: writeText "<h1>Hello new world!</h1>" 1 i odświeżmy stronę. Zamiast "Hello new world!" dostaliśmy szereg informacji o błędach w kodzie, jakie by nam wypisał kompilator podczas kompilacji.

W ten sposób programuje się nieco szybciej przy małych zmianach w kodzie. Interpretacja jednak bardzo spowalnia aplikację więc nie da się w ten sposób używać aplikacji w produkcji. Gdybyśmy chcieli teraz normalnie skompilować aplikację to znów należy wykonać najpierw cabal clean, a później cabal install już bez dodatkowej opcji.

Heist

Jak już wspominałem w pierwszej części artykułu programowanie za pomocą writeText byłoby bardzo uciążliwe i czasochłonne. Pokażę Wam zatem lepszy sposób na wyświetlanie HTMLa.

Heist jest systemem szablonów używanym w snapie. Pliki szablonów to zwyczajne pliki htmlowe (bądź xmlowe) z tą tylko różnicą, że Heist sprawdza poprawność tych plików przy uruchomieniu serwera (czyli czy są podomykane odpowiednie znaczniki itp.) oraz można utworzyć własne znaczniki, które Heist nam podmieni. Z tym, że nie muszą być to po prostu wartości, możemy np. utworzyć sobie własny znacznik <if>, który tylko, gdy podany warunek będzie spełniony to wyświetli to co jest pomiędzy znacznikiem otwierającym, a zamykającym itp. itd.

Pliki szablonów są trzymane w katalogu snaplets/heist/templates i muszą mieć rozszerzenie tpl. Utwórzmy tam plik hello.tpl o treści <h1>My first template</h1>. Teraz w naszej funkcji handleHelloWorld (w pliku Site.hs) wprowadzimy wyświetlenie tego szablonu. W tym celu usuwamy dotychczasową treść funkcji i wpisujemy: render "hello". Możemy odświeżyć stronę by zobaczyć efekt.

Funkcja render jako argument przyjmuje nazwę szablonu bez rozszerzenia i po prostu wyświetla szablon. Teraz spróbujmy wprowadzić jakiś własny znacznik do szablonu, np <name/>. Dodajmy go gdzieś w pliku szablonu i powróćmy do naszej funkcji handleHelloWorld. Zamieńmy jej treść na coś takiego:
renderWithSplices "hello" [("name", I.textSplice "Ala")]
Funkcja renderWithSplices różni się od funkcji render tym, że pozwala dodać własne znaczniki (technicznie nazywają to splice, dlatego właśnie renderWithSplices). Znaczniki dodaje się w parach (nazwa, wartość). Jeżeli chcemy dodać ich więcej to po prostu piszemy po przecinku kolejne pary w liście, np: renderWithSplices "hello" [("name", I.textSplice "Ala"), ("urodziny", I.textSplice "jutro")]. Funkcja textSplice tworzy znacznik, który jest po prostu tekstem (może być jeszcze innym znacznikiem lub komentarzem). Gdybyśmy podali jej jakiś tekst, który byłby kodem HTML to zmieniłby go tak aby się wyświetlił jako tekst, np. "<br>" nie wyświetliłby nowej linii lecz po prostu napis "<br>".

UWAGA: jeżeli w szablonie wpisalibyśmy coś takiego:
<h1 class="<name>">My first template</h1>
to oczywiście nie jest to poprawny kod HTMLa i Heist by zwrócił błąd. Jeżeli chcemy używać własnych znaczników w atrybutach należy użyć specjalnej składni:
<h1 class="${name}">My first template</h1>

To tyle na dziś. Następnym razem spróbujemy utworzyć ciekawsze znaczniki jak np. wspomniany wcześniej if. Do tego czasu postarajcie się dzisiejszą lekcję dobrze opanować.

poniedziałek, 14 stycznia 2013

Programowanie webowe w Haskellu cz. 1

Początkujący haskellowiec poszukując polskich stron dotyczących Haskella napotyka na poważny problem. Praktycznie nie ma nic sensownego na ten temat. Być może znajdzie informacje (jak będzie dobrze szukał) o tym jak stawiać pierwsze kroki w tym języku ale poważniejszych przykładów brak. Pewnie wynika to z tego, że programistów Haskella w Polsce nie ma zbyt wielu, a ci którzy są raczej nie wiele publikują. Właśnie z tego powodu postanowiłem napisać krótki kurs programowania stron internetowych. Nie będzie on specjalnie trudny ale jednak aby go zrozumieć będzie wymagana pewna podstawowa wiedza, którą można zdobyć np. tu.

W Haskellu mamy do wyboru dwa podejścia do tworzenia stron. Pierwsze jest podobne do tego w PHP, pythonie, rubym itd. czyli wywoływanie programów przez serwer HTTP dzięki CGI czy też Fast CGI. Tego podejścia nie będę omawiał, ponieważ nie uważam tego za dobre podejście (m. in. przez duży narzut). Drugie podejście to postawienie własnego serwera HTTP. W ten sposób otrzymujemy jedną binarkę, którą wystarczy uruchomić. Oczywiście nadal możemy współpracować z Apachem, lighttpd czy nginxem ale o tym może kiedy indziej.

W Haskellu istnieją trzy większe frameworki do tworzenia takich rzeczy:
  • Happstack - najstarszy z nich, najbardziej rozbudowany ale jednocześnie niezbyt elegancki i szybki,
  • Yesod - mocno rozbudowany, najszybszy z dostępnych frameworków ale również niezbyt elegancki,
  • Snap - framework, który mi przypadł do gustu. Jest w miarę szybki, bardzo elegancki i niestety niezbyt rozbudowany.

Jako, że najbardziej lubię programować w Snapie to opiszę jak zbudować prostą stronkę w tym właśnie frameworku. Żeby ten kurs nie był znowu taki prymitywny to spróbujemy zbudować prostego CMSa. Zatem do dzieła!

1. Instalacja Snapa

Standardowo w Haskellu do instalacji wszelkich pakietów używa się programu o nazwie Cabal. Większość dystrybucji Linuxa posiada pakiet o nazwie cabal bądź cabal-install więc wystarczy go zainstalować. Co do wersji na windowsa to można ją ściągnąć stąd: http://www.haskell.org/cabal/download.html. Jak już go zainstalowaliśmy to wywołujemy:
$ cabal update
$ cabal install snap
To w zasadzie cała instalacja. W razie jakichkolwiek problemów napiszcie w komentarzach. Teraz spróbujmy coś uruchomić.

2. Hello world!

Utwórzmy sobie jakiś katalog na nasz projekt i przejdźmy do niego. Snap jest tak dobry, że utworzy nam w nim prostą i gotową do odpalenia stronkę. Wystarczy, że wywołamy: $ snap init default
Powinno nam się pojawić parę nowych katalogów oraz plik nazwa_katalogu.cabal W tym pliku znajdują się m. in. wszystkie nazwy pakietów, które wymaga nasza stronka, jakiś przykładowy opis strony i jej nazwa (taka jak nazwa katalogu) oraz nazwy plików źródłowych, które trzeba skompilować. No to kompilujemy:
$ cabal install
Gdy kompilacja się zakończy możemy uruchomić naszą stronkę:
$ dist/build/nazwa_katalogu/nazwa_katalogu
Powinniśmy otrzymać komunikat podobny do tego:
no port specified, defaulting to port 8000
User JSON datafile not found. Creating a new one.
Initializing app @ /
Initializing heist @ /
...loaded 7 templates from /home/karol/test/snaplets/heist/templates
Initializing CookieSession @ /sess
Initializing JsonFileAuthManager @ /auth

Listening on http://0.0.0.0:8000/
Snap informuje nas, że nie określiliśmy portu na jakim ma działać serwer więc będzie nasłuchiwał na porcie 8000. To odpalamy jakąś przeglądarkę i wpisujemy: http://localhost:8000


Snap ukazał nam domyślną stronę logowania, która oczywiście działa ale nie jest zbyt użyteczna, gdyż baza użytkowników znajduje się w pliku JSONa. Spróbujmy zatem coś z tym zrobić.

3. Pierwsze kroki

Czas na trochę kodu. Wyłączmy serwer poprzez naciśnięcie Ctrl+C i wyświetlmy plik src/Site.hs
W tym katalogu znajdują się jeszcze dwa inne pliki ale ich w ogóle nie będziemy ruszać więc na razie nie będę ich omawiał. W pliku Site.hs pierwszą rzeczą jaka nas interesuje to: To jest serce naszego systemu. Tutaj Snap decyduje co zrobić, gdy ktoś zażąda jakiegoś adresu. Proponuję wywalić pierwsze 3 wpisy i zamiast ich dodać pewien nowy w ten sposób: Informujemy w ten sposób Snapa, że gdy ktoś zażąda strony głównej to ma on wywołać funkcję o nazwie handleHelloWorld, która wyglądać będzie tak: Jeśli chodzi o typ funkcji to się tym na razie nie przejmujmy. Funkcja writeText to taki odpowiednik funkcji echo w PHP. Zanim zobaczymy efekt usuńmy wszystkie pliki z katalogu snaplets/heist/templates. Po czym wywołujemy jeszcze raz:
$ cabal install
i uruchamiamy:
$ dist/build/nazwa_katalogu/nazwa_katalogu
Teraz jak wejdziemy na http://localhost:8000 naszym oczom ukaże się przepiękne hello world!

Trudno byłoby programować dalej w ten sposób. Dlatego w kolejnej części kursu zajmiemy się systemem szablonów o nazwie Heist i pokażemy jak ułatwić sobie życie bez wiecznego przekompilowywania kodu. Tymczasem warto sobie poćwiczyć i dobrze opanować dzisiejszą lekcję. To do następnego razu!