PŘÍRODOVĚDECKÁ FAKULTA UNIVERZITY PALACKÉHO KATEDRA INFORMATIKY
DIPLOMOVÁ PRÁCE
Srovnání webových frameworků
2013
Petr Novosad
Anotace Cílem práce je vypracování odborné studie hodnotící a porovnávající vybrané webové frameworky (aplikační rámce pro tvorbu webu). Vybrané frameworky budou nad různými platformami. V rámci řešení bude ve všech frameworcích vzorově vytvořena vhodná modelová aplikace demonstrující různé aspekty a (pokročilejší) možnosti frameworků, jako např. snadnost vytvoření aplikace (včetně uživatelské podpory pro framework), nabízená funkcionalita (dynamické webové prvky, práce s databází, zabezpečení aj.), dostupná dokumentace apod.
Děkuji vedoucímu práce, Mgr. Janu Outratovi, Ph.D. za vedení mojí diplomové práce, cenné rady a čas obětovaný při konzultacích.
Od roku 1996 se stavá HTML standard, se kterým přichází požadavek dynamického webu. Tomuto standardu se začaly přizpůsobovat tehdejší platformy. Postupy řešení implementací se začaly opakovat, a proto začaly vznikat frameworky (pracovní rámce), které usnadnily programátorům práci. Nyní je tvorba webu jedno z nejčastějších odvětví praktického programování. V této práci bude rozebrán vývoj tří hojně užívaných frameworků. Cílem práce je jednotlivé frameworky porovnat a zjistit, které jsou vhodné pro ta která konkrétní užití. V této práci se budeme snažit o co nejvíce objektivní pohled, a to i přesto, že s Ruby on Rails mám největší zkušenosti a denně s tímto nástrojem pracuji.
2. 2.1.
Úvod do webových frameworků Programování webových aplikací
Webové aplikace se používají nejen v rámci internetu, ale také v rámci podnikových sítí intranetů. Důvodů, proč se od klasického programování stále více přechází na programování webových aplikací, je hned několik. Z pohledu koncových uživatelů není potřeba žádná instalace, nejsou žádné nároky na minimální konfiguraci koncového počítače, ani dokonce omezení operačního systému. Aplikace se totiž nachází na serveru a uživatelé k tomuto programu přistupují skrze internetový prohlížeč. Z pohledu programátora je usnadnění v uživatelském rozhraní, jelikož veškeré UI je zobrazováno přes HTML kód, případně s kaskádovými styly. Další rozšíření tohoto rozhraní je dáno Javascriptem a jeho rozšířením jQuery, pomocí kterých vytváříme dynamické prvky. Díky překladu aplikace do HTML kódu vznikly nové architektury aplikací a právě jedna z těchto nových architektur je v diplomové práci popsána a využita. Tyto webové aplikace by však neměly být výpočetně náročné, protože jsme omezeni ve většině případů rychlostí internetu, případně rychlostí sítě. Zkusme si představit, že by byla například aplikace typu photoshop ovládána přes toto rozhraní; museli bychom neustále čekat na odeslání informací na serveru a ze serveru u několika megabytových obrázků. Nicméně v dnešní době je většinová poptávka po nepřílíš výpočetně náročných aplikacích.
2.2.
Framework
Jelikož klasické programovací jazyky nebyly uzpůsobeny pro vytváření webových aplikací, bylo potřeba určitého rozšíření pro tento jiný styl programování. 8
Framework je softwarové rozšíření určité platformy, která se snaží tvořit co nejlepší nástroj pro práci s danou oblastí. Využívá základní principy dané platformy a přidává k nim podpůrné součásti, rozšířující funkce, knihovny, podporu pro návrhové vzory, návody pro konkrétní problémy apod.
2.3.
Výběr frameworku
Výběr frameworku nemusí být tak jednoznačný. Jako první prvkek výběru by měla být zkušenost programátora s určitou platformou, protože z ní vždy framework vychází a má stejný základ. Jako další kritérium výběru by měl být účel a rozsah aplikace. Některé frameworky mohou být zaměřeny na konkrétní oblast; to znamená, že v sobě mohou mít zahrnuto mnoho hotových součástí. Další kritérium může být existence konzole a případného testovacího nástroje. V neposlední řadě existuje možnost napsat si vlastní framework. Výhoda je, že bude vytvořen na míru a programátor může proniknout do pochopení konkrétní platformy mnohem více. Na druhou stranu existují u hojně využívaných frameworků komunity a fóra s rozborem konkrétních problémů a jejich možných řešení.
2.4.
Architektura MVC
Tato architektura je hojně využívaná až v posledních letech. Originální návrh je z roku 1979 a lze jej naleznout na této adrese: http://heim.ifi.uio.no/ trygver/2007/MVC Originals.pdf. Dle http://www.zdrojak.cz/clanky/uvod-do-architektury-mvc/ je tato architektura jedním z nejpoužívanějších vzorů užívaných pro tvorbu webových aplikací. Všechny zde testované frameworky používají tuto architekturu. Architektura MVC je rozdělena na tři logické části: • model - hlavní třída, její data a základní metody pro práci s nimi, • view (pohled) - reprezentuje uživatelské rozhraní, • controller (kontroler) - má na starosti aplikační logiku, tzn. pracuje s modelem, zpracovává uživatelské vstupy a aktualizuje pohled. Jak můžeme vidět na obrázku 1. kontroler zpracovává data modelu a pohled zobrazuje data modelu. Hlavní je vazba mezi kontrolerem a pohledem (někdy jednosměrná, někdy obousměrná).
9
Obrázek 1. Schéma MVC architektury Model nemůže nikdy mít vazbu na pohled ani na kontroler, velmi často je přímo propojen s tabulkou v databázi. Všechny akce, které se dějí v aplikaci, jako např. chyby, uživatelské vstupy, ajax, apod., zpracovává kontroler a předává zpět pohledu. Interakci s uživatelem můžeme vidět na obrázku 2..
Obrázek 2. MVC architektura - interakce s uživatelem Uživatel pracuje s aplikací skrze pohled. Jeho vstupy zpracovává kontroler.
2.5.
Srovnávané frameworky
2.5.1.
Ruby on Rails
Ruby on Rails (RoR) je založen na jazyce Ruby. Vytvořil jej programátor David Heinemeier Hansson při práci na projektu Basecamp. První verze vyšla v červenci 2004, ale práva na sdílení byla umožněna až v únoru 2005. V srpnu 2006 dosáhl framework mezníku, kdy společnost Apple uvedla, že budou využívat Ruby on Rals v operačním systému Mac OS X v10.5 „Leopard�. 10
Snaží se docílit maximální jednoduchosti a přehlednosti. Jelikož přinášejí abstraktní vrstvu na databázi, není třeba užití žádného SQL. „Ruby se snaží, aby programování bylo zábavné a Rails tuto snahu dotahují téměř k dokonalosti. To v důsledku znamená jak spokojenější programátory, tak taky podstatně jednodušší možnost vyjádřit myšlenku, protože psaní v Rails je mnohem podobnější přirozenému jazyku než cokoliv jiného�. Jakub Šťastný (2007) Ruby on Rails 2.0: Evoluce, nikoliv revoluce, http://www.root.cz/clanky/ruby-on-rails-2-0-evoluce-nikolivrevoluce/ V Ruby on Rails jsou napsány webové stránky jako např. Twitter, Github, Basecamp, Yellow Pages, . . . Základní vlastnosti Ruby on Rails: • konvence má přednost před konfigurací – tato vlastnost zahrnuje čitelnost a srozumitelnsot kódu, • balíčkový systém gemů – každý „gem� přestavuje rozšíření aplikace, které je velmi snadné, • automaticky mapují URL na vnitřní řídící prvky aplikace – to znamená jednoduché propojení uživatelského rozhraní a vrstvy, která zpracovává vstupy a výstupy uživatele, • generátory – RoR obsahují generátor pro každou hlavní část aplikace, který se spouští přes konzoli.
11
Základní kostra aplikace vypadá následovně:
Obrázek 3. Základní kostra aplikace Ruby on Rails Hlavní aplikace se nachází v adresáři app. Zde jsou umístěny všechny modely, kontrolery a pohledy. Kromě nich zde najdeme soubory Javascriptu, kaskádových stylů a dále pomocné třídy pro práci s aplikací (mailers, helpers.) Mimo adresář app je důležitý také config, ve kterém se nastavují konfigurační soubory, soubory pro rozšíření a propojování kontrolerů a pohledů. Další důležitý adresář je db, ve kterém je uloženo a nastaveno vše, co se týká databáze. Za zmínku stojí také soubor Gemfile, do kterého přidáváme rozšíření aplikace, zmíněné „gemy�. RoR používá velkou škálu metod a klíčových slov, které mohou programátorovi ze začátku dělat problém, nicméně díky snaze RoR - dělat co nejvíce srozumitelný kód - se rychle zautomatizují. 12
Jeden z typických znaků RoR je umístění kódu do pohledů v bloku �%%�. Pokud by chtěl někdo začít s RoR, doporučuji zhlédnout a projít tutoriál „Rails for Zombies� na adrese http://railsforzombies.org/, zahrnující cvičení po každé kapitole. 2.5.2.
Nette
Framework Nette je založen na jazyku PHP. Autorem je David Grudl. Každý měsíc se koná neformální setkání příznivců Nette frameworku nazývané „Poslední sobota�, pořádané v Praze, Brně i na jiných místech. Nette má lehce poupravenou architekturu MVC na architekturu MVP (ModelView-Presenter), kde presenter jen volá model a výsledek předává pohledu. Naproti tomu presenter nezpracovává uživatelské vstupy. Pohledy nazývá jako šablony. V Nette jsou napsány webové stránky jako např. Root.cz, Slevomat.cz, Recepty.cz, Sedmička.cz, CSFD.cz . . . Základní vlastnosti Nette • rychlost – dle nezávislého testu, který provedl server root.cz je Nette jedním z nejrychlejších frameworků vůbec, • rozšiřující ladící nástroj – tento ladící nástroj se objevuje na každé stránce a zobrazuje základní informace, • komponenty a znovupoužitelnost kódu – komponenta je součást aplikace mající vlastní presenter a dá se volat kdekoli v aplikaci, • Aktivní komunita uživatelů v ČR – je to český framework, po republice se konají přednášky o Nette a existují česká fora řešící problematiku tohoto frameworku a často můžeme řešit problémy přímo s autorem, • sandná rozšiřitelnost – snaží se vést programátora k takovému návrhu aplikace, aby bylo možné její jednoduché rozšíření, • podpora jmenných prostorů v PHP 5.3. 13
Základní kostra aplikace:
Obrázek 4. Základní kostra aplikace Nette V hlavním adresáři app se nachází komponenty, modely, presentery, šablony a konfigruační soubory. Dalším hlavním adresářem je www obsahující obrázky, soubory Javascriptu a kaskádových stylů. Nette vychází z PHP. Proto všichni, kteří se s PHP setkali alespoň okrajově, nebudou mít problém s tímto frameworkem začít pracovat. Výhoda Nette je „svoboda�, je pouze na programátorovi, do jaké míry framework využije. Pokud by chtěl někdo začít s tímto frameworkem, doporu14
čujeme tutoriál „Píšeme první http://doc.nette.org/cs/quickstart. 2.5.3.
aplikaci�
na
oficiálních
stránkách
Django
Django je založen na jazyku Python. Byl vyvíjen už od roku 2003 jako součást redakčních systémů kansaského deníku Lawrence Journal-World. O dva roky později byl zdrojový kód otevřen a poskytnut široké veřejnosti pod liberální BSD licencí. Jedná se o živý projekt. Django označuje pohledy jako šablony. Základní vlastnosti Django • automaticky generované administrační rozhraní – má integrované uživatelské rozhraní pro práci s databází, • výkonný šablonovací systém – udržuje všechny kontrolery pohromadě v jednom souboru, • vytváření tabulek v databázi skrze model – definováním modelů můžeme vytvářet a měnit tabulky v databázi, • generování formulářů z databázového modelu, automatická validace – programátor nemusí ručně vytvářet formuláře ani kontrolu správnosti jednotlivých vstupů.
15
Základní kostra aplikace:
Obrázek 5. Základní kostra aplikace Django V kořenovém adresáři se nachází konfigurační soubor a soubor pro propojení url adres s kontrolery urls.py. Adresář media obsahuje statické soubory jako jsou například kaskádové styly a obrázky. Šablony jsou umístěny v adresáři templates. Tato kostra nepředstavuje aplikaci, ale projekt, ve kterém se může nacházet více aplikací. Poslední adresář, který nebyl zmíněn, se nazývá votr. Zde se nalezají jednotlivé soubory, ve kterých definujeme modely, kontrolery a formuláře konkrétní aplikace projektu. Aplikace v jednom projektu mohou být navzájem propojené. Django obsahuje velké množství nástrojů pro usnadnění práce při vytváření aplikací. Pro každou součást existuje jeden společný soubor, ve kterém se tyto součásti definují. Pro začátek práce s tímto framworkem doporučujeme tutoriál na oficiálních stránkách Djanga https://docs.djangoproject.com/en/1.5/intro/.
3. 3.1.
Porovnání obecných aspektů frameworků Instalace a spuštění
Jak bylo popsáno výše, všechny tyto frameworky jsou nezávislé na operačním systému, nicméně níže uvedený popis se týká pouze instalace na operační systém Ubuntu.
16
3.1.1.
Ruby on Rails
RoR využívá balíčkový systém gemů, které představují knihovny. Ppokud chceme využívat určité gemy, musíme vědět, na které verzi Ruby tyto gemy pracují. Nejprve musíme nainstalovat verzovací software pro Ruby. Použil jsem Ruby Version Manager (RVM), na oficiálních webových stránkách RVM je přehledný popis instalace pomocí několika příkazů v terminálu. Dále musíme v RVM nainstalovat verzi ruby, kterou chceme používat. Poté můžeme vygenerovat kostru aplikace přímo v terminálu příkazem rails new. Tato kostra obsahuje soubor Gemfile, který obsahuje seznam všech gemů apliakce včetně gemu „rails�, který představuje framework a kterému nastavíme požadovanou verzi (př.: ’rails’, ’3.2.1’.) Dále musíme všechny gemy nainstalovat, případně nastavit v aplikaci. Provedeme v terminálu příkazem bundle install. Spuštění aplikace provedeme v terminálu příkazem rails server, nacházejícím se na adrese http://localhost:3000. 3.1.2.
Nette
Kromě PHP je potřeba nainstalovat Apache, který se stará o lokální server PHP. PHP a Apache lze najít v repozitáři systému, případně lze upravit složku, ze které se načítá lokální server (výchozí nastavení: /var/www.) Poté je potřeba z oficiálních webových stránek Nette stáhnout kostru aplikace (př. sandbox) a umístit ji do složky Apache pro localhost (/var/www/sandbox.) Samotný framework se nachází v knihovně stažené aplikace (př. /var/www/sandbox/lib/nette.) Aby fungoval lokální server, musí být spušťen Apache příkazem v terminálu sudo /etc/init.d/apache2 start. Spuštěná aplikace se nachází na adrese http://localhost/sandbox/www pro aplikaci sandbox. 3.1.3.
Django
Python i Django jsou umístěny v repozitářích systému. Pro spuštění lokálního serveru je nutno spustit konzoli Pythonu v terminálu příkazem python. V této konzoli pak můžeme vygenerovat základní kostru aplikace příkazem django-admin.py startproject. Aplikaci spustíme v terminálu příkazem python manage.py runserver na adrese http://localhost:8000. 3.1.4.
Shrnutí
Nejjednodušší instalaci má Django, nejsložitější RoR. Nicméně ani jedna z instalací není nijak zvláště složitá. 17
3.2.
API a Tutoriály
3.2.1.
Ruby on Rails
Ruby on rails má dobře popsanou API i s příklady na adrese api.rubyonrails.org/, jedinou nevýhodou je, že funkce, které mají základ v Ruby v tomto API nenajdeme. Musíme je hledat v API jazyka Ruby. Pokud jde o tutoriály, tak hlavní web www.railsguides.com obsahuje základní funkce a návody s přehledným popisem a příklady. Protože využíváme gemy, které nejsou základní součástí, existují tři základní webové stránky. Na adrese www.ruby-toolbox.com zadáme klíčové slovo a zobrazí se seznam gemů, seřazený podle poslední aktualizace. Na stránce www.github.com jsou zdrojové kódy těchto gemů s popisem instalace a většinou s příkladem použití. A konečně www.railcast.com, kde jsou videa s často využívanými součástmi aplikací a ukázkami použití. Žádný tutoriál v češtině jsem nenalezl. 3.2.2.
Nette
API Nette je dobře popsaná s příklady a obsahuje i funkce PHP na adrese api.nette.org/, případně odkazuje přímo na hlavní API PHP. U tutoriálů je hlavní výhoda ta, že existují v češtině. Hlavní web podporující Nette na adrese nette.org/ obsahuje samotný framework ke stažení, popis instalace, seriál „Píšeme první aplikace�, kde jsou rozebrány základní prvky. Jsou zde i příklady použití hlavních a rozšičujících prvků, které se v seriálu nenachází. Všechny návody jsou velmi dobře strukturované a pochopitelné. Další český tutoriál jsem nalezl na adrese www.zdrojak.cz. Je z roku 2009 a není aktualizovaný na aktuální verzi, ve které je pár podstatných změn a tudíž není ideální. 3.2.3.
Django
V Djangu je API rozšířeno a zahrnuje nejen seznam funkcí s příklady, ale je rozděleno i do kapitol, které jsou velmi podrobně popsány i s příklady. Hlavní dokumentace a tutoriály jsou na stejné adrese jako API https://docs.djangoproject.com/en/. Jako u Nette je zde také seriál o vytvoření základní aplikace v pěti kapitolách. Existuje i česká verze těchto stránek, ale bohužel byly poslední změny udělány v roce 2009 a obsahují jen část anglického překladu, takže je zastaralá a použitelná jen částěčně. Nachází se na adrese www.djangoproject.cz.
18
3.2.4.
Shrnutí
Nette má výhodu českého tutoriálu. Na druhou stranu by se měl každý programátor vyznat v anglických návodech. Když nebereme v potaz jazyk tutoriálu, ke všem frameworkům existují návody, jak pracovat s jednotlivými aspekty funkcionality. API je pro všechny frameworky dostatečné.
3.3.
Rozšiřující nástroje
Každý framework se neustálé vyvýjí. Tvůrci i komunita vytváří nejrůznější rozšíření. Hlavní požadavek je snadné zahrnutí rozšíření do frameworku. 3.3.1.
Ruby on Rails
Jak již bylo zmíněno, v RoR existují knihovny pojmenované gemy, které představují rozšíření. Pro toto rozšíření existuje soubor Gemfile, do kterého se přidá název gemu, případně s jeho verzí nebo umístěním. Spuštěním příkazu bundle install v konzoli je gem nainstalován do aplikace. Ukázka Gemfile: gem ’authlogic’ gem ’mysql2’ , ’0.3.10’ gem ’rails’, :git => ’git://github.com/rails/rails.git’ Od mysql2 thubu. použítí 3.3.2.
gem authlogic se nainstaluje poslední známá stabilní verze. Od gemu se nainstaluje verze 0.3.10. Gem rails se na nainstaluje ze zdroje na giVětšina gemů má zdrojové kódy na githubu, kde je popsáno základní s příkladem. Nette
Větština rozšíření existuje na oficiální stránce frameworku http://pla.nette.org/cs/. Jednotlivé rozšíření lze stáhnout a přidat do adresáře lib/nette. Jelikož na začátku každého souboru užíváme příkaz use Nette, knihovna se automaticky načte. 3.3.3.
Django
Většina rozšíření pro Django existuje na serveru https://github.com. Zde je popsán popis instalace a použití tohoto rozšíření. Nicméně ve většině případů stačí napsat příkaz pro instalaci v konzoli a k tomu název rozšíření: pip install django-extensions Instalace rozšíření „django-extensions�. 19
3.3.4.
Shrnutí
Rozšířit jednotlivé frameworky je velmi jednoduché a programátor si může napsat vlastní rozšíření. Všechna rozšíření sdílejí zdrojové kódy, proto si je každý programátor může upravit dle svých představ.
3.4.
Podpora komunity
Pokud máme nějaký problém řešení, nebo se nám vyskytne chyba v aplikaci, kterou chceme „vygooglit� a zjistit, jestli se s touto chybou už někdo nesetkal, je velikost komunity podstatná. Jelikož RoR a Django jsou světově rozšířenými frameworky, které jsou hojně používané, je tato komunita velmi rozšířená. Velmi často se odkazuje na web http://stackoverflow.com/, kde je většina problémů řešena často vícekrát. Můžeme zde nalézt řešení našeho problému nebo minimálně inspiraci k jeho řešení. Jelikož Nette je český framework, jeho podpora se se světovou komunitou nedá srovnávat. Má vlastní české fórum na řešení problémů, ale ne všechna jsou aktuální, protože se týkají starších verzí Nette. Komunita Nette v ČR stále stoupá. Programátor může využít možnosti účastnit se veřejných přednášek. Sezení RoR nebo Djanga jsou pro českého programátora logicky hůře dostupné. Jejich aktivita u nás není tak častá jako u Nette.
3.5.
Dostupnost serverů v ČR
V této kapitole rozeberu jen české servery, mimo ČR jich existuje spousta. 3.5.1.
Ruby on Rails
Pro tento framework jsem našel pouze jeden free a jeden placený hosting. Podpora Ruby on Rails v ČR není úplně ideální. 3.5.2.
Nette
Jelikož Nette je v aplikaci jen jako knihovna, stačí nalézt server, který podporuje stejnou verzi PHP, kterou aktuální aplikace využívá. 3.5.3.
Django
Pro Django existují v ČR jen 3 servery. Ani jeden z nich nemá free hosting. Ani Django nemá příliš velkou podporu v ČR.
20
4.
Testovací aplikace
Abychom mohli porovnávat frameworky, navrhl jsem testovací aplikaci, která obsahuje všechny níže uvedené prvky, popsané v následující kapitole. Tuto aplikaci jsem vytvořil v každém frameworku zvlášť, abych je mohl porovnávat na stejném konkrétním příkladě. Tato aplikace není hlavní část diplomové práce a slouží pouze jako nástroj pro porovnávání. Všechny aplikace jsou přiloženy na CD. V jednotlivých apektech porovnání se nacházejí ukázky kódu jednotlivých aplikací konkrétních frameworků. Tyto ukázky neodpovídají přesné podobě funkční aplikace. Jsou to pouze vybráné části kódu, které ukazují, jak funguje konkrétní funkcionalita. Tento způsob je zvolen kvůli přehlednosti popisu.
4.1.
Společné rysy frameworků
4.1.1.
DRY
DRY - „Don’t repeat yourself�. Každá funkce by měla být v aplikaci definována pouze jednou. Tento systém podporuje přehlednost, zmenšuje objem a usnadňuje jakoukoli změnu kódu. Nette tento prvek DRY využívá v takzvaných „komponentech� a RoR v „partialech�, přičemž zvolenou součást lze kdekoli využít. Příklad komponenty v Nette Controller kompenty class UserViewControl extends Nette\Application\UI\Control { private $messages; public function render() { $this->template->setFile(__DIR__ . ’/UserView.latte’); $this->template->messages = $this->messages; $this->template->render(); } } Při volání komponenty UserView se nejdříve zavolá tento kontroler který spustí metodu render. V té předá kontroler aktuální šabloně obsah pomocí setFile, nastaví proměnnou messages a vykreslí ji. View komponenty UserView.latte 21
{block content} {$messages->count()} {foreach $messages as $message} {$message->user_id} {$message->content} {$message->created_at} {/foreach} {/block} Výše vidíme samotný obsah komponenty. Vypíše počet zpráv a poté atributy jednotlivých zpráv. Příklad volání komponent {control userView} V místě, kde se zavolá tento příkaz, bude vyobrazen předchozí obsah. 4.1.2.
REST
REST - Representational State Transfer. Je to architektura rozhraní navržená pro distribuované prostředí. REST je použitelný pro jednotný a snadný přístup ke zdrojům, kterými mohou být data nebo stavy aplikace. Obsahuje také rozhraní a práci s voláním HTTP metod PUT, GET, POST, DELETE. Podpora REST je obsažena v RubyOnRails a Djangu, ale v Nette chybí. Příklad REST v Ruby on Rails Poznámka: Tabulka zpráv v databázi obsahuje cizí klíč, který představuje identifikátor uživatele. class Message < ActiveRecord::Base belongs_to :user end class User < ActiveRecord::Base have_many :messages end Pokud v modelech nastavíme tyto vzájemné vazby, REST nám umožní získat všechny zprávy konkrétního uživatele příkazem user.messages. Další příkladu RESTu:
22
User.all User.last User.find_by_email "[email protected]" Prvním příkazem nalezneme všechny všechny uživatele, druhým vybereme posledního uživatele v tabulce podle atributu id. Poslední řádek nám nalezne uživatele s daným emailem. REST v RoR automaticky definuje funkce hledání podle všech atributů tabulky v databázi.
4.2.
Omezení aplikace
4.2.1.
Servery
Aplikace se v těchto frameworcích nemusí nijak ručně překládat ani kompilovat. Abychom si mohli ověřit námi vytvořenou funkcionalitu a viděli spuštěnou aplikaci, potřebujeme k tomu spuštěný server, na kterém aplikace poběží. Zde se nabízejí dvě možnosti. První z nich je posílat neustále všechny editované změny přímo na kokrétní server, který má svoji doménu a web hosting. Na tuto aplikaci se musíme připojovat skrze internet. Druhá možnost je vytvořit si lokální server, který spustíme na svém počítači. Zde můžeme pracovat v „offline� režimu a nemusíme nikam posílat editované soubory, protože si je lokální server aktualizuje sám. Nevýhoda vzdáleného serveru je celkem zřejmá, a to neustálé obnovování souborů na vzdáleném serveru. Nevýhoda lokálního serveru je, že se server musí restartovat po každé upravě databáze. V obou případech jsou frameworky nezávislé na operačním systému. Při vytváření aplikací byl zvolen způsob lokálních serverů pro všechny frameworky. 4.2.2.
Webové prohlížeče
Všechny použité frameworky jsou nezávislé na prohlížečích. Jedinným omezením jsou kaskádové styly, které mají většinou problém se staršími verzemi Internet Exploreru.
4.3.
Prostředí pro testování
4.3.1.
Operační systém
Tato aplikace byla vytvořena a testována na operačním systému Linux Ubuntu 12.04 i386. Jako prohlížeč byl využíván Chromium, což je verze Google Chrome pro operační systém Linux. Využíváné lokální servery byly Ruby pro RoR, Apache pro Nette a Python pro Django. 23
4.3.2.
Verze platforem a frameworků
Při testování byly využíity následující verze: Ruby on Rails • Ruby - 1.9.2 • Ruby on Rails - 3.2.6 Nette • PHP - 5.3.10 • Nette - 2.0.8 Django • Python - 2.7.3 • Django - 1.3.1
4.4.
Popis aplikace
Obrázek 6. Návrh aplikace Aplikace byla navržena tak, aby zahrnovala základní funkční aspekty, se kterými se běžně pracuje ve většině webových aplikací. Výše zobrazený návrh aplikace je zjednodušený. Hlavní funkce aplikace je vytvářet a zobrazovat zprávy uživatelů. Třída User má atribut friends, který představuje serializované pole identifikátorů ostatních uživatelů. Pokud se id určitého uživatele nachází v tomto serializovaném poli, „ je přítel� konkrétního uživatele (tato vazba je jednosměrná). Konkrétní uživatel může vidět své zprávy a všechny zprávy svých přátel. Aplikace kromě nastavování přátel obsahuje přihlášení a registraci uživatelů s následným emailovým upozorněním. Nastavování uživatelů a načítaní zpráv je realizováno pomocí jQuery a Ajax.
24
4.5.
Popis funkcionality
Aplikace byla navržena tak, aby zahrnovala základní prvky, se kterými se běžně pracuje ve většině webových aplikací: práce s databází, formuláře a jejich ověřování, Ajax a jQuery, rozvržení stránky, uživatelské rozhraní, integrace kaskádových stylů, odeslání emailů, autorizace a autentizace.
4.6.
Způsoby řešení
Základní postup řešení je stejný: vytvořit databázi, vytvořit modely, kontrolery a pohledy. Dále je potřeba vytvořit formuláře pro vytváření zpráv, registraci a úpravu uživatelů, nastavování přátel Ajaxem, správu posílání emailů a nastavení uživatelského rozhraní. V těchto podkapitolách je základní popis postupu vývoje v jednotlivých frameworcích. Podrobněji rozebraný postup funkcionality jednotlivých částí aplikace je rozebrán v kapitole 5.. Předmětem diplomové práce není tutoriál řešení. V následujících částech je uveden pouze stručný souhrn řešení v jednotlivých frameworcích. 4.6.1.
Ruby on Rails
Vygenerujeme kostru nové aplikace. Nastavíme databázi v konfiguračním souboru. Pro uživatele a zprávy vytvoříme tabulky v databázi. K těmto tabulkám vytvoříme modely, ve kterých nastavíme základní funkce a omezení (např. nutnost nastaveného emailu před uložením.) V dalším kroku vytvoříme kontrolery a pohledy pro každou metodu kontroleru. Následně musíme nastavit propojení mezi metodami kontrolerů a jednotlivými pohledy. V kontrolerech vytvoříme a nastavíme obsloužení pro všechny základní proměnné, se kterými budeme v pohledech pracovat. Nyní máme základní kostru programu, kterou můžeme rozšířit. Do „Gemfile� přidáme všechny součásti, se kterými budeme chtít pracovat, a které nejsou součástí RoR (např. knihovna pro přihlašování.) Vytvoříme si formuláře pro přihlašování a registraci, metody pro jejich obsluhu v kontroleru uživatele a propojíme. Přidaná knihovna pro přihlašování nám zajistí jednoduchou autorizaci a autentizaci. V konfiguračním souboru upravíme nastavení pro posílání emailů, vytvoříme speciální třídu „mailer� a napíšeme obsah emailu, který budeme posílat. Pro obsluhu Ajaxu a jQuery si napíšeme skripty, které budeme opět obsluhovat kontrolerem. Tyto skripty vložíme do hlavičky základní stránky. Nakonec přidáme kaskádové styly a nastavíme, co se má kde zobrazit.
25
4.6.2.
Nette
Z oficiálního webu Nette si stáhneme kostru aplikace. Nastavíme databázi v konfiguračním souboru, kterou budeme využívat. V databázi vytvoříme tabulky pro uživatele a zprávy. Následně vytvoříme třídu Repository pro usnadnění práce s databází, ze které budou dědit modely. V konfigruračním souboru nastavíme pojmenování modelů. Vytvoříme presentery a v každém nastavíme, co je potřeba při volání presenterů načíst (např. model uživatele, přes který budeme pracovat s databází.) Nyní musíme pro každý pohled vytvořit metody action a render. Tuto kostru můžeme rozšířit vytvořením a obsluhou formulářů. Oboje umisťujeme jako metody presenteru, kterého se to týká. Jednotlivé pohledy i s obsluhou můžeme umístit do takzvaných komponent, které nám umožní je volat kdekoliv v aplikaci. V rámci přihlašování musíme vytvořit model, který se bude starat o správnost příhlašovacích údajů. Základní nástroj je již v Nette. My jen musíme vytvořit třídy pro obsluhu a rozhraní. Pro odesílání emailů nám stačí udat základní údaje, přes které se bude posílat samotná zpráva. Zde máme možnost umístit je lokálně přímo do metody, kde chceme odeslání zavolat. Ajax musíme nastavit pro konkrétní případy užití. Pro jeho obsluhu je potřeba vytvořit metody v presenterech. Nakonec rozvrhneme stránku, přidáme a nastavíme kaskádové styly. 4.6.3.
Django
Nejdříve vygenerujeme projekt aplikace, v něm potom vygenerujeme aplikaci, která bude spravována tímto projektem. Dále nastavíme databázi v konfiguračním souboru. V adresáři aplikace v souboru models.py vytvoříme modely jako třídy a nastavíme jim jejich atributy i s datovým typem. Následně synchronizujeme s databází, ve které budou vytvořeny tabulky na základě definovaných modelů. V souboru views.py vytvoříme metody, které představují kontrolery jednotlivých šablon. Tyto metody spárujeme se šablonami v souboru urls.py, který se nachází v adresáři projektu. K těmto metodám vytvoříme jednotlivé šablony. Jednotlivé formuláře definujeme v souboru forms.py, který se nachází adresáři aplikace. Tyto formuláře vytváříme i obsluhujeme v metodě šablony, která představuje kontroler. Pro přihlašování má Django vestavěné rozšíření. Stačí ho přidat do konfiguračního souboru, vytvořit šablonu a spárovat. K odesílání mailů musíme nastavit v konfiguračním souboru SMTP údaje. Poté stačí na konkrétním místě nadefinovat příjemce, obsah a předmět emailu a odeslat. 26
Pro Ajax a jQuery přidáme do hlavičky základní stránky zdrojový soubor a jednotlivé funkce definujeme do konkrétních šablon, ve kterých budeme funkce využívat. Nakonec rozvrhneme stránku, přidáme a nastavíme kaskádové styly.
4.7.
Uživatelské rozhraní
Pro nastavení uživatelského rozhraní budeme využívat Twitter Bootstrap. Je to přednastavený kaskádový styl, který by měl eliminovat rozdíly mezi prohlížeči. Má integrované nastavení pro všechny základní HTML elementy a rozvržení stránky. Byl vybrán, protože je hojně využívaný, doporučovaný a volně šiřitelný. Dokumentace a samotný kód se nachází na http://twitter.github.com/bootstrap/. Příklad Twitter Bootsrap:
<%= yield %>
<%= render ’users/show_users’ %>
Bootstrap rozděluje stránku na dvanáct sloupců a my si můžeme takto rozvrhnout jednotlivé zobrazí dvou elementů divů například na dvě a jednu třetinu.
V tomto příkladě nastavíme tabulce tyto dvě třídy, které zajistí rozvržení tabulky a zobrazení.
27
Obrázek 7. Nastylovaná tabulka
5.
5.1.
Porovnávání jednotlivých aspektů týkajících se testovací aplikace Práce s databází
Všechny tři frameworky mají jednoduše nastavitelnou databázi v konfiguračních souborech. Nastavení je vždy stejné: ADAPTER, SERVER, USER, PASSWORD, DATABASE. Nette i Django obsahují přehledný nástroj pro správu a nastavení databáze. Hlavní požadavek je propojení modelů a databáze. Dále jednoduchá práce s databází; např. upravování, vyhledávání, propojování apod. Jako příklad použijeme vytvoření tabulky zpráv v databázi a propojíme s modelem tak, aby bylo možné v aplikaci vyhledávat, vytvářet a mazat zprávy. Tabulka zpráv v databázi bude obsahovat identifikátor uživatele. 5.1.1.
Ruby on Rails
Ruby on Rails využívají takzvané migrace, pomocí kterých vytvářejí, upravují, případně i mažou tabulky v databázi. Migrace se generují v terminálu příkazem rails generate migration. V této migraci nastavíme sloupce tabulky s typy: class CreateMessages < ActiveRecord::Migration def self.up create_table :messages do |t| t.string :content t.belongs_to :user t.timestamps end 28
end def self.down drop_table :messages end end Jak je vidět z příkladu, chybí sloupec id a created at , tyto sloupce se přidají automaticky spolu se sloupcem updated at . Po spuštění terminálového příkazu rake db:migrate se provede „migrace�. To znamená, že ve všech souborech se spustí funkce self.up, ve kterých vytváříme, popř. upravujeme tabulku. Pro navrácení databázi do stavu před migrací spustíme příkaz rake db:rollback. Tím se spustí metoda self.down. Pokud chceme pracovat s tabulkou zpráv messages, vytvoříme model Message. REST nám zajistí přednastavení integrovaných funkcí jako all, delete all, new, create, . . . . Kromě základní funkce find, které hledá podle id, lze také použít například find by content, která hledá zprávy podle atributu content. Message.find 5 Message.find_by_content "obsah zprávy" První řádek najde zprávu s id 5. Druhý řádek najde zprávu s obsahem „obsah zprávy�. Pro vytvoření používá metoda new. user = User.first message = Message.new message.content = "nová zpráva" message.user = user message.save => #<Message id: 73, content: "nová zpráva", user_id: 1, created_at: "2013-04-01 08:40:04", updated_at: "2013-04-01 08:40:04"> V databázi vyhledáme prvního uživatele z tabulky dle id. Vytvoříme zprávu, nastavíme obsah, uživatele a uložíme. Na posledních dvou řádcích můžeme vidět uloženou reprezentaci zprávy. Pro smazání se využívá metoda delete. Message.last.delete Nalezeneme poslední řádek tabulky zpráv a vymažeme ho.
29
5.1.2.
Nette
Administrační rozhraní Nette se nachází na url app/www/adminer (př.: http://localhost/sandbox/www/adminer.) Kromě tohoto rozhraní nemá Nette žádný jiný nástroj pro práci s databází. Všechny tabulky včetně omezení je třeba si vytvořit ručně, případně přes administrační rozhraní. Přístupové funkce v aplikaci je třeba naprogramovat. Nette poskytuje vrstvu pro pohodlnější práci s databází. Modely, které budou pracovat s tabulkou nazveme jako repozitáře. Základní funkce pro nalezení tabulky v databázi abstract class Repository extends Nette\Object { protected function getTable() { // název tabulky odvodíme z názvu třídy preg_match(’#(\w+)Repository$#’, get_class($this), $m); return $this->connection->table(lcfirst($m[1])); } class MessageRepository extends Repository { } Z tohoto modelu budeme dědit repozitáře pro každou tabulku databáze a pomocí metody getTable() ji načteme. Metoda getTable() repozitáře zpráv MessageRepository vezme jeho název bez přípony „Repository� a najde tabulku s odpovídajícím názvem Message v databázi. Přes metodu getTable() pak pracujeme s tabulkou: class MessageRepository extends Repository { public function find($id) { return $this->getTable()->find($id); } } Tato metoda vyhledá zprávu v databázové tabulce User dle atributu id. Abychom mohli vytvářet objekt repozitáře, musíme ho nastavit v konfiguračním souboru config/config.local.neon: services: messageRepository: MessageRepository Nyní můžeme volat repozitář uživatele následujícím způsobem: $messageRepository = $this->context->messageRepostirory; $message = $messageRepository->find(5); 30
První řádkem vytvoříme objekt a druhým nalezneme v databázi uživatele s id 5. Pro mazání stačí na nalezený záznám zavolat delete(): class MessageRepository extends Repository { public function delete($id) { return $this->find($id)->delete(); } } messageRepository->delete(5); V repozitáři zpráv definujeme metodu mazání, kde nalezeeneme záznam a smažeme. Na posledním řádku je konkrétní příklad užití smazání zprávy s id 5. 5.1.3.
Django
Django také využívá REST, ale na rozdíl od předchozích frameworků má opačný přístup k vytváření tabulek v databázi. Zde se napřed nadefinují modely v souboru models.py. Příklad vytvoření modelu s omezením class Message(models.Model): user_id = models.ForeignKey(User) content = models.CharField(max_length=200) created_at = models.DateTimeField() Zde vidíme definici modelu zpráv, který bude mít atributy id uživatele, obsah a čas vytvoření. Všimněme si omezení u atributu content, ve kterém můžeme nastavit maximální délku. Příkazem python manage.py sql z konzole vytvoříme tabulky v databázi. Po tomto vytvoření nebo jakékoli úpravě pak stačí spustit příkaz python manage.py syncdb opět z konzole. Tabulku zpráv představuje Message.objects. Díky REST má Django integrované funkce jako např.: all(), count(), create(), . . . . Pro vyhledávání můžeme použít funkce get(), filter(), choice(), . . . . Message.objects.all() Message.objects.get(id=2) Message.objects.filter(content__startswith=’já’) První řádek vrátí všechny zprávy z databáze. Druhý najde zprávu s atributuem id 2 a všechny poslední zprávy s obsahem začínajícím řetězcem „ já�. Pro vytvoření objektu zavoláme název modelu a nastavíme atributy: 31
user = User.objects.get(id = 2) message = Message(content = "nová zpráva", user = user) message.save Díky REST nastavujeme nové zprávě objekt uživatele, nikoli číslo jeho atributu id. Message nám vytvoří objekt s konkrétními parametry, který posléze jen uložíme. Pro smazání stačí najít záznam v databázi a zavolat na něj metodu delte(): message = Message.objects.get(id = 5) message.delete() 5.1.4.
Shrnutí
Ve všech třech frameworcích lze dosáhnout požadavku propojení modelů a tabulek z databáze. Jak jsme se mohli přesvědčit Nette v tomto aspektu velmi zaostává, protože prgramátor si propojení a všechny potřebné funkce musí vytvořit a navíc pro každý model zvlášť. Proto bez znalosti SQL, by měl programátor problém řádně pochopit funkce a podstatu této vrstvy, aby ji mohl používat pro vlastní potřeby. Django má velkou výhodu v tom, že si může tabulky databáze upravovat přímo v modelu a tím pádem má vše na jednou místě. Naproti tomu RoR může libovolně měnit tabulky přes migrace a díky nim se vracet o několik změn zpět jednoduchým příkazem. Kdybychom vybírali framework na základě tohoto aspektu, přichází v úvahu RoR a Django díky snadnému integrovanému rohraní.
5.2.
Vytváření MVC konkrétní aplikace
Při vytváření MVC aplikace požadujeme nenáročné propojení modelů, kontrolerů a pohledů a případnou jednoduchou úpravu. Jako ukázku vytvoříme model uživatele, k němu kontroler a pohled, ve kterém budeme vypisovat všechny uživatele z databáze. 5.2.1.
Ruby on Rails
Samotný model představuje třída, která dědí z ActiveRecord::Base. V tomto modelu nastavujeme „validace� jednotlivých atributů tabulky. Tyto validace nám určují, co musí nový objekt modelu splňovat, aby mohl být uložen v databázi. Dále zde nastavujeme vazby v databázi, přístupové metody pro atributy a pomocné funkce. class User < ActiveRecord::Base
32
validates :username, :presence => true, :uniqueness => true validates :email, :presence => true, :uniqueness => true has_many :messages attr_accessible :username, :email, :password, :password_confirmation def friend?(id) friends.include? id.to_s end V modelu nastavujeme nutnost vyplnění a unikátnosti uživatelského jména a emailu při ukládání; bez těchto podmínek se nám uživatel neuloží. Dále se zde nastavuje public přístup k jednotlivým atributům pro čtení i zápis pomocí attr accessible. has many :messages nám umožní vybrat všechny zprávy daného uživatele z databáze. user = User.find 2 user.messages Načteme z databáze uživatele s id 2 a na dalším řádku nalezneme v databázi všechny jeho zprávy. Nakonec máme v modelu pomocnou metodu, která zjišťuje, zda daný uživatel je nebo není přítel. Kontroler se stará o vstupně/výstupní akce, nad kterými operuje uživatel. class UsersController < ApplicationController before_filter :find_user, :only => [:index, :show, :edit, :update, :destroy, :show_users] respond_to :html, :xml, :js def index @users = User.all end def show if !current_user flash[:notice] = "Musíte být přihlášen." redirect_to new_user_session_url else @users = User.all 33
@users.delete current_user end end def new @user = User.new end def create @user = User.new(params[:user]) if @user.save redirect_to root_url else render :action => ’new’ end end def edit end def update if @user.update_attributes(params[:user]) redirect_to @user, :notice => "Uživatel úspěšně editován." else render :action => ’edit’ end end def destroy @user.destroy end private def find_user begin @user = User.find(params[:id]) rescue ActiveRecord::RecordNotFound flash[:error] = ’Zaznam nenalezen’ redirect_to root_url end end end
34
Kontroler využívá DRY pomocí before filter, který přidá vyhledání konkrétního uživatele na začátek vybraných metod. Všechny tyto metody můžeme propojit s jednotlivými pohledy přes „routování�; to znamená, že propojíme url s konkrétní metodou v kontroleru. V metodě nastavíme požadované proměnné a vykreslíme obsah pohledu na stránku. Vykreslený pohled využívá nastavené proměnné. Routování se nastavuje v tomto souboru config/routes.rb, např.: resources :users Klíčové slovo resources nám zaručí spárování základních metod kontroleru index, show, new, edit s pohledy a metody create, update, destroy spáruje s modelem. Takže například tato url adresa localhost:3000/users/ vyhledá v kontroleru uživatele metodu index, ta vyhledá všechny uživatele v databázi a vykreslí pohled. Pohled už stačí jen správně umístit app/views/users/index.html.erb:
Username
Email
<% for user in @users %>
<%= user.username %>
<%= user.email %>
<% end %>
<%= link_to "New User", new_user_path %>
Tento pohled zobrazí všechny uživatele a vytvoří odkaz pro vytvoření nového uživatele. Klíčové slovo new user path nám vytvořila aplikace díky nastavení routování v souboru routes.rb. Je to takzvaná „cesta�, která představuje url adresu. Tato adresa nám zavolá metodu new v kontroleru uživatele. 5.2.2.
Nette
Jelikož Nette pracuje s databází na nízké úrovni. Ideální je vytvořit si třídu Repository, která nám umožní jednodušší vyhledávání v databázi a z ní pak budeme dědit naše modely.
35
Příklad funkce z Repository abstract class Repository extends Nette\Object { protected function getTable() { // název tabulky odvodíme z názvu třídy preg_match(’#(\w+)Repository$#’, get_class($this), $m); return $this->connection->table(lcfirst($m[1])); } public function findAll() { return $this->getTable(); } public function find($id) { return $this->getTable()->find($id); } } Model slouží jako nástroj pro načítání a ukládání do databáze. Dále jsou v něm pomocné funkce využívané v aplikaci. class UserRepository extends Repository { public function findByName($username) { return $this->findAll()->where(’username’, $username)->fetch(); } public function createUser($username, $email, $password) return $this->getTable()->insert(array( ’username’ => $username, ’email’ => $email, ’password’ => $password )); }
{
public function updateFriends($id, $friends) { return $this->getTable()->where(’id’, $id)->update(array( ’friends’ => implode("::",$friends) )); } Tento model dědí z třídy Repository. Zde jsou ukázky metod pro nalezení uživatele dle jména, vytvoření a upravení uživatele.
36
Nette využívá presentery místo kontrolerů, které slouží pouze jako prostředník mezi modelem a pohledem. Pohled nazývá šablonou. V těchto presenterech musíme vytvořit proměnné, do kterých načteme data z databáze a tyto proměnné předáme šabloně. Využívá dvě metody pro každou šablonu s předponami action a render. První pro načtení dat a druhou pro předání proměnných do šablony a vykreslení. Prezenter uživatele: class UserPresenter extends BasePresenter { private $userRepository; private $current_user; private $users; protected function startup() { parent::startup(); if (!$this->getUser()->isLoggedIn()) { $this->redirect(’Sign:in’); } $this->userRepository = $this->context->userRepository; } public function actionIndex() { $this->users = $this->userRepository->findAll(); } public function renderIndex() { $this->template->users = $this->users; } Metoda startup slouží jako konstruktor. V ní načteme námi vytvořený model a uložíme do proměnné. Pro šablonu index definujeme proměnné v metodě actionIndex(). V metodě renderIndex() této šabloně předáme proměnné a vykreslíme ji na stránku. Šablona, kterou má vykreslit metoda renderIndex(), se musí správně umístit do aplikace. V tomto případě zde: app/templates/user/index.latte. Navíc musí mít příponu .latte. {block content} {foreach $users as $user}
{$user->username}
37
{$user->email}
{/foreach} {/block} Tato šablona vypíše všechny uživatele a jejich atributy. 5.2.3.
Django
V kapitole 5.1.3. byl již model ukázán při vytváření databáze. Do modelu navíc můžeme přidat pomocné metody. Pohledy označujeme jako šablony. Přístup se od ostatních frameworků odlišuje tím, že všechny modely jsou definovány v jednom souboru models.py. Podobně jsou organizovány i kontrolery v jednom souboru views.py. Zde nastavujeme proměnné a voláme konkrétní šablonu. Příklad views.py def users(request, id): users = User.objects.all() return render_to_response(’users.html’, {’user’: users}) Metoda users načte všechny uživatele z databáze a vykreslí šablonu templates/users.html, které předá proměnnou users. Nyní musíme propojit konkrétní metodu z views.py s url adresou v souboru urls.py: urlpatterns += patterns(’project.votr.views’, (r’^users/$’, ’users’), ) Zaměříme se na závorku na prostředním řádku. První argument je url, druhý argument je název metody kontroleru. Url je definovaná pomocí regulárních výrazů. http://localhost:8000/users/ Zavolá metodu users v souboru views.py. Soubor templates/users.html: {% block content %} {% for user in users %} {{user.username}} {{user.email}} 38
{% endfor %} {% endblock %} Blok {{ }} nám vypisuje kód Djanga na stránku. Pomocí bloku {% %} využíváme kód Djanga, který nebude vypsán na stránce. Proměnné nám definoval a předal kontroler. V této šabloně vypíšeme atributy jednotlivých uživatelů pomocí cyklu. 5.2.4.
Shrnutí
Splnění požadavků ve všech frameworcích bylo splněno. Nejnáročnější způsob je v Nette, protože je potřeba definovat nejvíce kódu; skrze dvě metody pro každou šablonu a vytvářet rozhraní pro databázi. Na druhou stranu se nemusí nastavovat žádné propojování presenteru a šablon, stačí jen správně pojmenovat třídy a metody a správně umístit soubory. Nejpochopitelnější přístup má Django, v němž je malé úskalí v nastavování url adres přes regulární výrazy, které nejsou na první pohled jednoznačné. RoR má také velmi přehledný přístup a pochopitelný kód. S tím rozdílem, že oproti Djangu má každý kontroler vlastní soubor a omezení atributů modelu se nastavuje jinde, než se vytváří databáze. U Djanga se může vyskytnout větší nepřehlednost kódu u větších aplikací, kdy jsou všechny kontrolery v jednom souboru.
5.3.
Formuláře a ověřování
Jako příklad vytvoříme formulář pro registraci uživatele, který bude uložen do databáze. Musíme nastavit jednotlivé elementy formuláře podle typu atributů uživatele, který vyplňujeme. Dále musíme nastavit podmínky vyplnění, například nutnost uživatelského jména nebo shodnost hesla a jeho potvrzení. 5.3.1.
Ruby on Rails
Nejprve definujeme v kontroleru proměnnou, pro kterou budeme vytvářet formulář. V tomto případě vytvoříme objekt uživatele. def new @user = User.new end V souboru app/views/users/new.html.erb, který je propojen s metodou new, vytvoříme formulář.
<% end %> Každý atribut bude odpovídat jednomu řádku formuláře. Na tomto řádku definujeme popis , datový typ a „symbol�, který odpovídá danému atributu (symbol se označuje předponou :). Nakonec přídáme tlačítko, pojmenované registrovat, pro odeslání formuláře. Pokud chceme přidat nějaké omezení, případně podmínky vyplnění, můžeme v modelu přidat „validaci� k určitému attributu př.: presence, uniqueness, length, . . . . model User: validate :username, :presence => true, uniqueness => true validate :password, :format => { :minimum => 8 } V prvním řádku nastavujeme podmínku vyplnění a jednoznačnosti uživatelského jména. V druhém nastavujeme, že heslo musí mít minimálně 8 znaků. RoR má zabudovanou kontrolu emailu, který musí být vždy ve správném formátu. Pokud je nějaká podmínka porušena, formulář místo odeslání nahlásí chybu s komentářem, co je špatně vyplněno. Odeslaná data formuláře se nachází v proměnné params, ze které lze přímo vytvořit a uložit objekt. Formulář po odeslání volá metodu create stejného kontroleru, ve kterém byla metoda new. def create 40
@user = User.create params[:user] end Tato metoda vytvoří a uloží objekt s konkrétními parametry, které poslal formulář. 5.3.2.
Nette
Při vytváření formuláře využíváme DRY pomocí takzvaných „komponent� (komponenty rozebrány v kapitole 5.5.2.) Zjednodušeně přidáme předponu „createComponent� k metodě definující formulář, a pak tuto metodu budeme volat pomocí klíčového slova control.
protected function createComponentUserForm() { $form = new Form(); $form->addText(’email’,’Email:’, 40, 100) ->addRule(Form::FILLED, ’Je nutné zadat email’); $form->addPassword(’newPassword’, ’Nové heslo:’, 30) ->addRule(Form::MIN_LENGTH, ’Nové heslo musí mít alespoň %d znaků.’, 8); $form->addPassword(’confirmPassword’, ’Potvrzení hesla:’, 30) ->addRule(Form::FILLED, ’Nové heslo je nutné zadat ještě jednou pro potv ->addRule(Form::EQUAL, ’Zadaná hesla se musejí shodovat.’, $form[’newPass $form->onSuccess[] = $this->userFormSubmitted; return $form; } Vytvoříme objekt formuláře z integrované třídy Nette. Tomuto formuláři budeme přidávat jednotlivé prvky představující atributy uživatele, kterému nastavíme identifikátor, popis a případné omezení HTML elementů. Těmto prvkům formuláře můžeme přiřadit omezení nebo podmínky, které musí být splňeny před odesláním formuláře. Toto omezení přidáme pomocí addRule. Zde musí heslo splňovat délku minimálně 8 znaků. Na předposledním řádku můžeme vidět volání onSucces[]. Při správném vyplnění zavoláme metodu pro zpracování formuláře, jinak nám formulář zobrazí chyby s popisem, co je špatně vyplněno. public function userFormSubmitted(Form $form) { $values = $form->getValues(); $newPassword = $this->authenticator->calculateHash($values->newPassword); $this->userRepository->createUser($values->$user, $values->email, $newPassword); $this->flashMessage(’Uživatel vytvořen.’, ’success’); 41
$this->redirect(’Sign:in’); } Při zpracování získáme data z formuláře. Zašifrujeme heslo pomocí „autentikátoru� (rozebrán v kapitole 5.6.2.) a uložíme uživatele do databáze. Poslední řádek přesměruje aplikaci na url adresu přihlášení. Poslední věc, kterou musíme nadefinovat, je metoda pro vytvoření uživatele v repozitáři uživatele. public function createUser($username, $email, $password) { return $this->getTable()->insert(array( ’username’ => $username, ’email’ => $email, ’password’ => $password )); } Vidíme, že i přes vrstvu, která nám zjednodušuje práci s databází, musíme každý atribut tabulky nastavovat „ručně�. 5.3.3.
Django
Pro definování všech formulářů využíváme soubor forms.py v konkrétní aplikaci. Jednotlivé formuláře jsou definované jako třídy. Těmto třídám můžeme definovat pomocné metody. class UserForm(forms.Form): username = forms.CharField(label = ’Uživatelské jméno’, required=True) email = forms.EmailField(required=True) password1 = forms.CharField(label=’Heslo’, widget=forms.PasswordInput, required=True) password2 = forms.CharField(label=’Heslo znovu’, widget=forms.PasswordInput, required=True) def clean_password2(self): if self.cleaned_data.get(’password1’) != self.cleaned_data[’password2’]: raise forms.ValidationError(’Zadaná hesla se liší.’) return self.cleaned_data[’password2’] V definici třídy nastavíme, jaké prvky má formulář mít, včetně jejich typu. Můžeme přidat nastavení, například pro pojmenování prvku, nebo přidat omezení a podmínky vyplnění. Ke kolonce email nastavíme datový typ EmailField, který nám zaručí automatickou kontrolu formátu emailu. 42
Jelikož pracujeme se třídou, můžeme si definovat ověření hesel přímo ve formuláři metodou clean password2. Tento formulář budeme vytvářet a zpracovávat stejnou metodou v souboru views.py, který slouží jako kontroler. def user_form(request): if request.method == ’POST’: form = UserForm(request.POST) if form.is_valid(): username = form.cleaned_data[’username’] email = form.cleaned_data[’email’] password = form.cleaned_data[’password’] user = User(username = username, email = email, password = password) user.save() return HttpResponseRedirect(’/’) else: form = UserForm() return render_to_response(’user_form.html’, {’form’: form}, context_instance=RequestContext(request)) Při vytváření formuláře neposíláme metodu POST, proto jen vytvoříme objekt formuláře a zavoláme šablonu, které předáme tento vytvořený objekt. Pokud je daná metoda volána metodou POST (to znamená odesláním formuláře), získáme jednotlivé parametry formuláře a uložíme do příslušných atributů nového uživatele. Nakonec uživatele uložíme. Jakmile máme formulář uložený do proměnné, můžeme ho v šabloně vyvolat jedním z následujících způsobů: {{ form.as_table}} {{ form.as_p}} {{ form.as_ul}} Formulář se nám poté v šabloně přeloží do konkrétních HTML elementů. Zbývá jen propojit konkrétní url adresu s metodou kontroleru user form. (r’^user_form/$’, ’user_form’) Při zavolání localhost:8000/user form se nám zobrazí formulář. 43
5.3.4.
Shrnutí
Všechny frameworky požadavky splnily. Nejlépe pochopitelné vytváření formulářů má Django. Definice všech formulářů se nacházejí v jednom souboru. Vykreslení formuláře v šabloně se uskuteční jedním příkazem. Musíme však vytvářet nový kontroler, ve kterém definujeme obsloužení s šablonou včetně cesty. V RoR nastavujeme omezení a podmínky vyplnění v modelu. Vytváření formuláře řadíme mezi pohledy, nicméně nemusíme nastavovat žádné propojení pohledu a kontroleru. Obsloužení v kontroleru lze provést na jednou řádku. Proto má RoR nejlepší způsob práce s formuláři. Nette vytváří a nastavuje formulář na jednou místě jako „komponentu�, která se dá zavolat kdekoli. Musíme však nadefinovat propojení s databází a obsloužení formuláře.
5.4.
Ajax a jQuery
Ajax a jQuery jsou funkčním rozšířením javascriptu o další pomocné funkce a možnost obnovovat jen část stránky při změně dat. Toto rozšíření se používá ve většině běžných aplikací. Proto by nastavení a propojení s frameworkem nemělo být složité. Práci s těmito aspekty si ukážeme na nastavování přátel uživatele. 5.4.1.
Ruby on Rails
Do aplikace nainstalujeme gem „ jquery-rails�, který slouží jako základní rozšíření RoR pro Ajax a rozšíříme hlavičku stránky. <%= javascript_include_tag :defaults %> Tím je vše nastaveno. Funkce se zpracovávají v adresáři /app/assets/javascricpt v souborech s příponou .js. Případně přes funkce kontroleru v souborech nacházejících se v adresáři app/users/views s příponou .js.erb. Nejdříve musíme nadefinovat tlačítko, které bude nastavovat přítele. <%= button_to "nastav přítele", {}, {:remote => true, :class => "friend_button", :user_id => @user_id.to_i } %> Tlačítko nastavíme tak, že nemá nikam odkazovat pomocí symbolu remote. Nadefinujeme třídu a atribut user id také pomocí symbolu (symbolu začínajího předponou :). Kód nacházející se v /app/assets/javascript/application.js: 44
$(’input.friend_button’).click(function() { var user_id = $(this).attr(’user_id’); $.getScript(’users/set_friendship?button_user_id=’ + user_id); }); Tato funkce je spuštěna při nastavování přítele. Pokud se „klikne� na tlačítko s třídou friend button, zjistíme atribut představující hodnotu id přítele. Nakonec zavoláme skript přes kontroler uživatele. Přidáním „?button user id=’ + user id� za volání metody kontroleru nám zajistí uložení params[:button user id] pro kontroler. Kód v kontroleru uživatele: def set_friendship @user = current_user @button_user_id = params[:button_user_id] @button_user = User.find(@button_user_id) @td_id = "notice_user_" + @button_user_id end Zde si do proměnných uložíme aktuálního uživatele a uživatele, kterého chceme za přítele. Tohoto přítele jsme předali pomocí url adresy. Kód nacházející se v souboru /app/views/users/set friendship.js.erb <% unless @user.friend? @button_user_id %> <% @user.add_friend @button_user_id %> <% @td_id = "notice_user_" + @button_user_id %> $(’td#<%= @td_id %>’).text("přítel"); <% else %> <% @user.remove_friend @button_user_id %> <% @td_id = "notice_user_" + @button_user_id %> $(’td#<%= @td_id %>’).text("není přítel"); <% end %> V tomto skriptu už jen přidáme/odstraníme přítele a nastavíme popisek podle toho, jestli dotyčný uživatel je/není přítel. 5.4.2.
Nette
jQuery je zde integrováno. Ajax nastavíme přidáním následujícího kódu do souboru www/js/ajax.js. jQuery.ajaxSetup({ cache: false, 45
dataType: ’json’, success: function (payload) { if (payload.snippets) { for (var i in payload.snippets) { $(’#’ + i).html(payload.snippets[i]); } } } }); // odesílání odkazů $(’a.ajax’).live(’click’, function (event) { event.preventDefault(); $.get(this.href); }); Aby odkaz odesílal data Ajaxem a nenačítal znovu stránku, stačí do atributu třídy přidat „ajax�. id, $friend->id" class="friend_button ajax">přidat Poté stačí v presenteru volat funkci, která začíná s klíčovým slovem „handle�: public function handleSetFriend($id, $friend) { if( $this->presenter->isAjax()) { $this->userRepository->setFriend($id, $friend); $this->invalidateControl(); } } Tato metoda bude provádět daný kód jen pokud je volána Ajaxem. V repozitáři uživatele nastaví přítele. Poté aktualizuje na stránce pouze to, co bylo změněno pomocí invalidateControl. Repozitář uživatele: public function setFriend($id, $friend) { $user = $this->findById($id); if (in_array($friend, $user->friends)) { $user->friends = array_diff($user->friends, array($friend)); } else { array_push($user->friends, $friend); 46
} $this->updateFriends($user->id, $user->friends); } V této metodě najdeme daného uživatele a přidáme, případně odebereme přítele, který je předán druhým argumentem metody. 5.4.3.
Django
jQuery a Ajax musíme buď stáhnout v souboru, nebo se odkazovat na url adresu. <script type=’text/javascript’ src="http://code.jquery.com/jquery-1.9.1.min.js"> Tento řádek odkazuje na url adresu a bude v hlavičce základní stránky. Pokud chceme tyto nástroje využít, přidáme tento kód na konci šablony nad hlavní {% endblock %} do Javascriptového bloku � script � � /script �. Tlačítko volající Ajax: Tlačítku nastavíme třídu a atribut přítele friend.id. Konec šablony templates/profile.html zobrazující přihlášeného uživatele: <script> $(document).ready(function() { $(".set_friend").click(function() { var friend = $(this).attr("friend"); var profile = {{profile.id}}; $.ajax({ url: "/set_friend/"+ profile +"/" + friend, success: function(data) { set_label($(’tr#’+ friend +’ label’),true); } }); }); {% endblock %} 47
Pokud se klikne na tlačítko třídy set friend, získáme hodnoty atributu přítele a identifikátor přihlášeného užvatele. Pomocí Ajaxu zavoláme konkrétní url se získanými argumenty. Tato url nám spustí kontroler, který se postará o nastavení přítele. Pokud vše proběhne v pořádku, změníme popisky u nastavování přátel. Metoda kontroleru zpracovávající námi zavolané url: def set_friend(request, user, friend): profile = Profile.objects.get(id = user) friends = profile.get_friends() friend = int(friend) if friend in friends: friends.remove(friend) else: friends.append(friend) profile.save_friends(friends) profile.save() return render_to_response(’friends.html’, { ’friends’: friends }) Načteme konkrétního uživatele a jeho přátele. Pokud je friend mezi přáteli, odstraníme ho. V opačném případě ho přidáme a uložíme do databáze. Zbývá jen zavolat šablonu, ve které se změna projeví s konkrétně upravenou proměnnou. 5.4.4.
Shrnutí
Podpora Ajaxu je ve všech frameworcích velmi dobrá. Nejsnazší je použití v Nette, kde načteme konkrétní komponentu jednoduchým příkazem, aniž by se stránka musela načítat. V Djangu by mohla nastat situace, ve které budeme využívat stejný kód ve více šablonách, čímž porušujeme DRY. RoR má na první pohled nejsložitější způsob, ale dobře využitelný při kombinaci kódu JS a RoR.
5.5.
Prezentace dat a DRY
Tento prvek se týká samotných zobrazení pohledů a rozdělení stránek. Každý framework využívá jednu základní stránku, kde nastaví hlavičku, základní rozvržení stránky a blok, do kterého chceme umístit aktuálně zobrazenou součást. Hlavní požadavek je samotný DRY, což znamená: žádný kód nepsat dvakrát, ale na jednom místě definovat a volat z více míst. Na příkladě zobrazení zpráv konkrétního uživatele si ukážeme tyto aspekty. 48
5.5.1.
Ruby on Rails
Základní stránka je umístěna zde app/views/layout/application.html.erb. Využívá klasický HTML kód. Kromě vkládání JS, CSS souborů do hlavičky má klíčkové slovo yield, které značí místo, kam se má zobrazit hlavní blok. Kromě hlavního bloku můžeme definovat vlastní bloky, např. yield(:title), který můžeme upravit v lokálním souboru; tak, že na title zavoláme řetězec. <%= yield(:title) %> <%= stylesheet_link_tag "application", "bootstrap" %> <%= javascript_include_tag :application %> <%= csrf_meta_tag %> V hlavičce nám první řádek udává titul stránky, který můžeme předefinovat v pohledu. Druhý řádek nám vkládá do stránky soubory s kaskádovými styly. Třetí vkládá Javascriptové definice. Na čtvrtém jsou meta tagy, které představují takzvaný digitální podpis stránky.
<%= yield %>
Tam, kde na hlavní stránce umístíme hlavní blok yield, se bude zobrazovat aktuální pohled, v našem případě v elementu div. To znamená veškerý obsah, který je napsán v HTML kódu, nebo veškerý přeložený kód RoR, který se nachází v bloku �% = %�. V samotném pohledu můžeme volat takzvané „partialy�, což jsou pohledy, které mají jako první znak názvu souboru podtržítko. Volaný partial využívá aktuálně nastavených proměnných z pohledů. Pohled aktuálního uživatele: <% title "Zobrazení zpráv uživatele" %> <%= render ’messages’, :user => @user %> Nastavíme titul stránky pomocí title. Pomocí render zavoláme partial messages.html.erb, nacházející se ve stejném adresáři. Tomuto partialu můžeme nastavit lokální proměnné.
49
Partial můžeme volat kdekoli v aplikaci s tím rozdílem, že musíme funkci render zadávat cestu umístění souboru /views/users/messages. Partial /views/users/ messages.html.erb: <% @messages = user.messages %> <% @messages.each do |m| %> <%= m.content %> <%= m.created_at %> <% end %> Partial získá zprávy z proměnné, kterou jsme předali, a vypíše jednotlivé atributy pro každou zprávu. 5.5.2.
Nette
Hlavní šablona, představující základní stránku, se jmenuje @layout.latte a je umístěna v adresáři app/templates, stejně jako všechny ostatní šablony. Využíváme jeden hlavní blok a vedlejší bloky. Hlavní blok se definuje s klíčovým slovem content a jako jediný se volá pomocí include. Hlavička základní stránky: <meta charset="UTF-8"> <meta name="description" content=""> <meta name="robots" content="{$robots}" n:ifset="$robots"> {block title}Nette Application Skeleton{/block} <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"> <script type="text/javascript" src="{$basePath}/js/netteForms.js"> {block head}{/block} Když přeskočíme meta tagy, vidíme hned první blok pojmenovaný title, značíčí titul stránky. Pokud v jakékoli šabloně definujeme blok stejným způsobem, změní se hodnota titulu stránky. Následně vkládáme kaskádové styly a soubory Javascriptu, u kterých proměnná $basePath přestavuje umístění v aplikaci. 50
{$flash->message}
{include #content}
V tomto hlavním elementu div se budou zobrazovat oznámení, která mohou vzniknout například nesprávnou hodnotou ve formuláři. Hlavní obsah stránky se zobrazí pomocí příkazu include #content právě v tomto místě. Nette využívá takzvané komponenty, které se nalezají v adresáři app/components. Tyto komponenty mají v názvu příponu „Control� a dědí z integrované třídy Nette. V adresáři komponent nalezneme jak presenter, tak i jeho šablonu. Presenter komponenty Messages.php: class MessagesControl extends Nette\Application\UI\Control { private $messages; function __construct(MessageRepository $messageRepository, $id) { parent::__construct(); $this->$messages = $messageRepository->messagesByUser($id); } public function render() { $this->template->setFile(__DIR__ . ’/Message.latte’); $this->template->messages = $this->messages; $this->template->render(); } } Komponentě v konstruktoru předáváme repozitář zpráv a identifikátor uživatele, od kterého získáme zprávy. Tyto zprávy přiřadíme do proměnné. V metodě render jen přiřadíme šablonu, předáme proměnnou a vykreslíme šablonu. Šablona komponenty Messages.latte: {block content} {if $messages } 51
{foreach $messages as $message} {$message->content} {$message->created_at} {/foreach} {/if} {/block} Šablonu komponenty definujeme stejně jako jakoukoli jinou šablonu. Abychom mohli komponentu zavolat, musíme v konkrétním presenteru vytvořit metodu pro vrácení komponenty s konkrétními argumenty. protected function createComponentMessages() { return new MessagesControl( $this->messageRepository, $this->current_user->id); } Pak už jen stačí volat komponentu na konrétním místě. Obsah šablony zobrazující zprávy uživatele: {block title}Zprávy uživatele{/block} {block content} {control messages} {/block} Nastavíme titul stránky a v hlavním bloku zavoláme přednastavenou komponentu pomocí control. 5.5.3.
Django
Django stejně jako Nette pojmenovává pohledy jako šablony. Základní stránka base.html je umístěna v hlavním adresáři šablon templates. Využíváme tři základní klíčová slova šablonovacího systému {% block %}, {% extend %} a {% include %}. Block nám značí bloky kódu, které se do něj budou zobrazovat, přitom {% block content %} {% endblock %} nám značí hlavní blok pro zobrazování obsahu šablon. Pomocí include můžeme vkládat šablony do šablon. Extend nám udává základní kostru stránky při volání šablony, to znamená kde se bude šablona zobrazovat. Šablona templates/base.html: 52
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> {% block title %}Vítejte{% endblock %} <script type=’text/javascript’ src="http://code.jquery.com/jquery-1.9.1.min.js"> V hlavičce první řádek značí znakovou sadu. Druhý řádek blok pro nastavení titulu stránky. Třetí řádek načítá jQuery. Poslední řádek vkládá kaskádové styly, proměnná MEDIA URL se nastavuje v konfiguračním souboru a nastavuje cestu aplikaci pro dodatečné soubory.
{% block content %}{% endblock %}
V elementu div s identifikátorem content se budou zobrazovat všechny šablony, které mají na začátku extends této hlavní šablony. Šablona uživatele: {% extends "base.html" %} {% block title %}Zprávy uživatele{% endblock %} {% block content %} {% include "messages.html"%} {% endblock %} Nastavíme titul stránky a zavoláme šablonu zpráv pomocí include. Toto klíčové slovo volá pouze šablonu, nikoli kontroler. Šablona zpráv templates/messages.html: {% block content %} Zprávy {% for message in messages %} {{message.content}} {{message.created_at}} {% endfor %} {% endblock %} Šablona vypisuje atributy jednotlivých zpráv uživatele. Proměnnou messages je nutno nastavit v kontroleru šablony, ve které bylo voláno include. 53
5.5.4.
Shrnutí
Všechny tři frameworky mají podobný způsob rozvržení základní stránky a DRY. Při volání jednotlivých souborů v pohledech a šablonách má RoR funkcionalitu navíc - může lokálně předat proměnnou souboru, který volá. Django si všechny tyto proměnné může nastavit v kontroleru nebo před voláním šablony. Tento styl je účinný, ale není příliš přehledný. Nette si při vytváření komponenty musí nastavit presenter a šablonu komponenty. Zde se nabízí otázka, zda je to zbytečný kód navíc, nebo zda by se to dalo využít k něčemu užitečnějšímu než jen nastavení proměnných.
5.6.
Autentizace a autorizace
Pro většinu aplikací je třeba zajistit systém uživatelů, kteří budou pracovat s aplikací, případně budou mít svůj profil. Proto vzniká nárok na přihlašování, práce v konkrétním profilu a bezpečnost tohoto profilu proti cizímu přístupu. Toto přihlašování by mělo být napojeno s registrací uživatelů. 5.6.1.
Ruby on Rails
Využijeme nejvíce rozšířený gem Authlogic, jelikož samotné rails modul přihlašování nemá. Tímto gemem rozšíříme námi vytvořený model uživatele a rozšíříme tabulku uživatele v databázi pomocí migrací (migrace rozebrány v kapitole 5.1.1.) class User < ActiveRecord::Base acts_as_authentic end Tímto jednoduchým příkazem rozšíříme model. Dále rozšíříme tabulku uživatele. create_table "users", :force => true do |t| t.string "username" t.string "email" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.string "friends" t.boolean "verified", :default => false t.string "crypted_password" t.string "password_salt" t.string "persistence_token" t.string "perishable_token" end 54
Tabulku uživatelů rozšíříme o posledních 5 řádků. Tato dvě rozšíření nám zajistí propojení gemu s naším modelem. Následně vytvoříme nový model bez tabulky. Model představuje přihlášený profil uživatele, který dědí z Authlogicu. class UserSession < Authlogic::Session::Base end Případně do něj můžeme vložit vlastní upravení. Tomuto modelu přiřadíme pohled. Obsah souboru app/views/user session/new.html.erb: <% title "Přihlášení" %> <%= form_for @user_session do |f| %>
<% end %> Tento pohled nám vytvoří přihlašovací formulář. Poslední věc, kterou musíme udělat, je nastavit url adresu pro přihlášení a odhlášení v souboru config/routes.rb. match ’/logout’ => ’user_sessions#destroy’, :as => :logout match ’/login’ => ’user_sessions#new’, :as => :login Metody destroy a new jsou zajištěny authlogicem. Nyní máme vytvořen přihlašovací modul. Přihlášený uživatel je uložen v proměnné current user. Pokud je výraz current user.present? roven hodnotě false, nikdo není přihlášen. V případě, kdy vytváříme nového uživatele, neboli registrace, vyplníme základní údaje, heslo a jeho potvrzení. Authlogic se postará o minimální sílu, ověření a zašifrování hesla. Podmínky minimální síly hesla lze nastavit v našem modelu uživatele.
55
5.6.2.
Nette
Nette má vlastní nástroj pro autentizaci a autorizaci zvaný Autentikátor. V základní kostře aplikace je již třída Authenticator vytvořena. Jen upravíme metodu pro šifrování a autentizaci. public static function calculateHash($password, $salt = NULL) { return crypt($password, $salt ?: ’$2a$07$’ . Strings::random(22)); } public function authenticate(array $credentials) { list($username, $password) = $credentials; $row = $this->users->findByName($username); if (!$row) { throw new Security\AuthenticationException(’The username is incorrect.’, self::IDENTITY_NOT_FOUND); } if ($row->password !== $this->calculateHash($password, $row->password)) { throw new Security\AuthenticationException(’The password is incorrect.’, self::INVALID_CREDENTIAL); } unset($row->password); return new Security\Identity($row->id, NULL, $row->toArray()); } Metoda calculateHash nám zašifruje heslo, které bude začínat konkrétním řetězcem a následovat náhodným řetězcem o délce 22 znaků. Metoda authenticate jednoduše ověří existenci uživatelského jména, korektnost hesla a případně přihlásí uživatele. Vytvoříme presenter pro přihlašování, v něm přihlašovací formulář a metodu pro jeho obsloužení. class SignPresenter extends BasePresenter protected function createComponentSignInForm() { $form = new Form(); $form->addText(’username’, ’Uživatelské jméno:’, 30, 20); $form->addPassword(’password’, ’Heslo:’, 30); $form->addCheckbox(’persistent’, ’Pamatovat si mě na tomto počítači’); $form->addSubmit(’login’, ’Přihlásit se’); $form->onSuccess[] = $this->signInFormSubmitted; return $form; 56
} public function signInFormSubmitted($form) { try { $user = $this->getUser(); $values = $form->getValues(); if ($values->persistent) { $user->setExpiration(’+30 days’, FALSE); } $user->login($values->username, $values->password); $this->flashMessage(’Přihlášení bylo úspěšné.’, ’success’); $this->redirect("User:default", $user->id); } catch (NS\AuthenticationException $e) { $form->addError(’Neplatné uživatelské jméno nebo heslo.’); } } Rozbor práce s formuláři se nachází v kapitole 5.3.2. Následně musíme vytvořit šablonu, ve které budeme zobrazovat náš vytvořený formulář. Obsah souboru app/templates/Sign/in.latte:
Přihlášení
{form signInForm} {control $form errors} {label username /} {input username} {label password /} {input password} {input persistent} {label persistent /} {input login} {/form} Umístíme blok pro případné výpisy špatného přihlašování. Následně pole pro vypnění uživatelského jména a hesla s popisem. Nakonec přidáme možnost trvalého přihlášení a tlačítko „přihlásit�. Aktuálního přihlášeného uživatele získáme následovně: $this->getUser(); $this->getUser()->isLoggedIn(); 57
Druhým řádkem ověříme, jestli je někdo přihlášen. Pro funkci odhlášení musíme do základního presenteru přidat tuto metodu: public function handleSignOut() { $this->getUser()->logout(); $this->redirect(’Sign:in’); } Tato metoda odhlásí uživatele a přejde na přihlašovací stránku. Autentikátor neobsahuje registraci uživatele. Tu si musíme vytvořit v modelu i v presenteru uživatele pomocí formuláře. Pro šifrování hesla využijeme výše zmíněnou metodu calculateHash. 5.6.3.
Django
Pro autentizaci je v Djangu vestavěný modul django.contrib.auth. Tento modul obsahuje model User představující uživatele v aplikaci. Tento model obsahuje všechny základní atributy jako first name, last name, username, email atd. a základní metody jako set password(), get fullname() atd. Obsahuje také šifrování hesel, ověřování přihlašovacích údajů, metody pro přihlašování a odhlašování, a dokonce i formulář pro přihlášení. Nejprve musíme propojit url adresu s našimi vytvořenými šablonami pro přihlášení, případně odhlášení. urlpatterns += patterns(’’, (r’^/login/$’, ’django.contrib.auth.views.login’, {’template_name’: ’login.html’}), (r’^/logout/$’, ’django.contrib.auth.views.logout’, {’template_name’: ’logout.html’}), ) Šablona pro přihlášení je v souboru templates/login.html a pro odhlášení v souboru templates/logout.html. Obsah šablony login.html: {% block content %} {% if form.errors %}
Špatně zadané uživatelské jméno nebo heslo. Zkuste se prosím přihlásit znovu.
{% endif %} 58
{% endblock %} Nastavíme případné chybové hlášení a vložíme formulář v podobě tabulky. Nakonec přidáme tlačítko, které uživatele přihlásí. Přihlášení obslouží integoravný modul a provede přihlášení, případně vrátí chyby. Šablona pro odhlášení: {% block content %}
Byl(a) jste odhlášen(a). Nashle příště!
{% endblock %} Pokud chceme rozšířit pevně daný model User o nějaký vlastní atribut, vytvoříme vlastní model Profile. class Profile(models.Model): user = models.ForeignKey(User, unique=True, related_name=’profile’) friends = models.TextField() def create_user_profile(sender, instance, created, **kwargs): if created: profile, created = Profile.objects.get_or_create(user=instance) U tohoto modelu vytvoříme atribut cizího klíče user id, který se bude vázat na uživatele modulu a přidáme si vlastní atribut, do kterého budeme ukládat přátele. Metoda create user profile nám zajistí vytvoření nového profilu, když je vytvořen nový uživatel. Model Profile, představující přihlášeného uživatele, se ukládá do databáze. Přihlášený uživatel je aplikaci představován proměnnou request.user a námi rozšířený profil získáme pomocí requset.user.get get profile(). Zjištění, jestli je někdo přihlášen získáme pomocí request.user.is authenticated. 5.6.4.
Shrnutí
Požadavky pro autorizaci a autentizaci jsou ve všech frameworcích splněny. Pokud chceme v Djangu rozšiřovat uživatele, působí práce poměrně neobratně a ne vždy je úplně jasné, jestli máme pracovat s uživatelem, nebo jeho profilem. 59
Rozšíření v RoR je nenáročné a snadno pochopitelné, navíc se rozšíření postará skoro o vše, včetně formuláře přihlášení, který rozšíření generuje. Nette má autentizaci velmi dobře zpracovanou a přehlednou, včetně vytváření formuláře pro přihlášení. Nikde nepůsobí žádné komplikace.
5.7.
Odesílání emailů
Jedna ze součástí každého frameworku je možnost odesílání emailů. Ve všech frameworcích je tato funkcionalita zahrnuta. Nicméně je třeba využít externí registrovaný email, přes který se bude email posílat. To znamená: nastavit SMTP, port, uživatelské jméno a heslo. Tuto funkcionalitu využijeme pro odeslání informačního emailu po registraci uživatele. 5.7.1.
Ruby on Rails
Nastavení externího emailu provedeme v konfiguračním souboru. Obsah souboru /config/inititalizers/setup email.rb: ActionMailer::Base.smtp_settings = { :address => "smtp.gmail.com", :port => 587, :domain => "", :user_name => "", :password => "", :authentication => "plain", :enable_starttls_auto => true } V RoR existují speciální třídy zvané mailery, nacházející se v /app/mailers. Každý typ emailu definujeme jako metodu maileru a nastavíme mu požadované atributy, potřebné k odeslání. class Notifier < ActionMailer::Base def welcome_email(user) mail(:to => user.email, :subject => "vítejte v aplikace") end end Jako argument metodě předáme konkrétního uživatele. Uživatele využijeme pro zjištění adresy a v těle zprávy. Klíčové slovo :to nám značí adresáta, klíčové slovo :subject nám značí předmět zprávy.
60
Pro tělo zprávy využijeme pohled, který pojmenujeme stejně jako metodu a umístíme do adresáře s názvem třídy. Obsah souboru /app/views/notifier/welcome email.html.erb: Vitejte v aplikaci. váš login: <%= @user.username %>
příjemný den" Obsah zprávy píšeme jako klasický pohled; to znamená v HTML kódu s případným překladem kódu RoR. Využíváme proměnnou @user, která byla předána jako argument metodě v maileru. Pro samotné odeslání emailu zavoláme tuto metodu s konkrétním argumentem a přidáme na konec metodu deliver. @user = User.create @user.username = "novak3" @user.email ’[email protected] Notifier.welcome_email(@user).deliver Vytvoříme uživatele, natavíme mu patřičné atributy a odešleme námi vytvořený email. 5.7.2.
Nette
Pro odesílání emailu musíme vytvořit objekt, kterému nastavíme konfigurační údaje externího emailu. Tento objekt bude v konkrétním místě odesílat email. $mailer = new Nette\Mail\SmtpMailer(array( ’host’ => ’smtp.gmail.com’, ’username’ => ’’, ’password’ => ’’, ’secure’ => ’ssl’ )); Máme dvě možnosti, buď nastavit tuto proměnou $mailer někde globálně, nebo vytvářet pokaždé, když chceme odesílat nějaký email. Vytvoření emailu: use Nette\Mail\Message; $user = UserRepository->createUser("novak3", "[email protected]") 61
$mail = new Message; $mail->addTo($user->email) ->setSubject(’vítejte v aplikaci’) ->setBody("Vitejte v aplikaci \n\n váš login: " . $user->username . "\n\n příjemný den"); Nejprve musíme do souboru, kde vytváříme zprávu, přidat knihovnu. Od vytvořeného uživatele načteme jeho uživatelské jméno, jeho email a nastavíme je jako argumenty emailu. Nakonec nastavíme tělo emailu, které musíme psát jako řetězec. $mailer->send($mail); Pro odeslání emailu zavoláme metodu send, která bere jako argument posílanou zprávu. 5.7.3.
Django
Nastavíme konfigurační soubor. EMAIL_BACKEND = ’django.core.mail.backends.smtp.EmailBackend’ EMAIL_USE_TLS = True EMAIL_HOST = ’smtp.gmail.com’ EMAIL_PORT = 587 EMAIL_HOST_USER = ’’ EMAIL_HOST_PASSWORD = ’’ Následně nastavíme zprávu, kterou chceme odeslat. from django.core.mail import EmailMessage user = User(username = "novak3", email = "[email protected]") content = "Vitejte v aplikaci \n\n váš login: " + str(user.username) + "\n\n příjemný den" email = EmailMessage(’Vítejte v aplikace’, content, to=[user.email]) Využijeme knihovnu Djanga. Vytvoříme uživatele a tělo emailu. Před odesláním vytvoříme samotný email a uložíme do proměnné email. První argument metody EmailMessage je předmět zprávy, druhý tělo emailu a třetí adresa. email.send() Pro odeslání vytvořeného emailu zavoláme metodu send. 62
5.7.4.
Shrnutí
Django a Nette mají téměř identický způsob, který je přehledný a jednoduchý. RoR má sice složitější způsob, ale má tu výhodu, že může libovolně naformátovat tělo zprávy.
5.8.
Ladící nástroje
Všechny tyto frameworky mají vlastní výpis chybového stavu, včetně výpisu cesty, kde všude se vyskytla chyba. Tento stav většinou nastane při neexistující url adrese, špatném nebo neexistujícím argumentu některé metody apod. 5.8.1.
Ruby on Rails
Pro nucené vyvolání podmínky použijeme tyto příkazy: raise raise @user.id raise @user.inspect Tento příkaz nám vyvolá výjimku a zabrání vykonávání kódu, který náleduje za ní. Můžeme tento příkaz využít pro zjištění konkrétní hodnoty, například aktuální hodnota id paramateru uživatele. Pokud na proměnnou @user zavoláme metodu inspect, výjimka nám vypíše strukturu uživatele, jak je reprezentována v aplikaci.
63
Obrázek 8. Výjimka Ruby on Rails Chybový stav nám vypíše metodu, ve které nastala chyba, obsah výjimky, dále umístění výjimky v konkrétním souboru a případné parametry, nacházející se v url (:id uživatele). Kromě ladění přes uživatelské rozhraní mužeme použít konzoli. Ta nám vypíše, kde všude se projeví vyvolaná chyba.
64
5.8.2.
Nette
Pokud chceme nuceně vyvolat výjimku, využijeme tyto příkazy: throw new Exception(’Naše vyjímka’); dump($this->user); dump($this->user->username); Příkaz throw new Exception se používá pro chybové oznámení. Příkaz dump nám zjistí obsah proměnné a vypíše ho.
65
Obrázek 9. Výjimka Nette Chybový stav nám vypisuje obsah výjimky, umístění v kódu a zásobník. Ten značí, kde všude se chyba projeví. Dále výjimka vypisuje podrobné informace, které během výjimky probíhají v aplikaci. Tyto informace nejsou předmětem diplomové práce.
66
Nette navíc obsahuje Debugger Bar, který zobrazuje základní informace o aktuální stránce a lze ho přesouvat myší.
Obrázek 10. Debugger Bar
5.8.3.
Django
Příkazy pro nucené vyvolání výjimky: raise Exception({user}) print {user} print {user->id} Příkazy raise Exception a print jsou syntaktický cukr, čili jsou to totožné metody pro výjimku.
67
Obrázek 11. Výjimka Djanga Výjimka zobrazuje všechny potřebné informace: obsah výjimky, url adresu, umístění v souboru a cesty, kde se projeví v Pythonu. Tento výpis Pythonu je pro většinu chyb zbytečný. Výjimka také podrobně vypisuje všechny cookies a podrobná nastavení (ta však nejsou předmětem diplomové práce.) Také můžeme využít konzoli a v ní využít výpis cesty, kde všude se projeví vyvolaná chyba. 68
5.8.4.
Shrnutí
RoR má jednoduchý a přehledný výpis chyb, ale není tak podrobný jako u ostatních frameworků. U Djanga a Nette může výpis vypadat složitě, nicméně jsou zde detailně podány všechny potřebné informace. Stačí se v tomto výpisu naučit orientovat a můžeme získat daleko více informací, než je na první pohled zřejmé. Nette má navíc obrovskou výhodu pomocí Debugger Baru, kde můžeme vidět aktuální hodnoty proměnných a volání databáze, aniž bychom museli evokovat výjimku. Na druhou stranu RoR a Django může využívat konzoli a v ní testovat modelové příklady.
5.9.
Serializace
Serializace je způsob ukládání objektů nebo struktur do databáze. V tomto příkladě chceme uložit pole přátel uživatele do databáze (v databazí sloupec friends). Protože databáze nemá datový typ pole, musíme toto pole přeložit pomocí serializace do řetezce nebo textu. Mechanismus musí podporovat i deserializaci, která nám načtený řetězec z databáze opět přeloží do pole. Výběr ukládáme do řetězce (datový typ string) nebo do textu (datový typ text), záleží na velikosti objektu. String má pouze 255 znaků. Pokud chceme uložit objekt, který výčtem svých parametrů s hodnotami přesahuje tuto délku, využijeme datový typ text. V našem případě nám stačí řetězec. Běžný způsob ukládání do databáze je přes formáty XML, JSON nebo YAML (formát pro serializaci.) 5.9.1.
Ruby on Rails
RoR ukládá serializované objekty přes formát JSON. Jediné, co je potřeba udělat, je přidat do modelu klíčové slovo serialize, název atributu a typ, ve kterém s atributem pracujeme. Deserializace se provádí automaticky při načtení modelu z databáze. class User < ActiveRecord::Base serialize :friends, Array end Serializujeme atribut friends, který má typ Array, což je pole. 5.9.2.
Nette
Nette serializaci nepodporuje, nicméně můžeme částečnou serializaci a deserializaci implementovat. Využijeme metodu implode, která převede pole do řetězce 69
a přidá námi zvolený oddělovač mezi prvky a metodu explode, která vezme řetězec a z prvků mezi oddělovači vytvoří pole. class UserRepository extends Repository { public function findById($id) { $user = $this->find($id)->fetch(); if ( $user->friends !== NULL ) { $user->friends = explode("::", $user->friends); } return $user; } // 4::5::1 => [4,5,1] public function saveFriends($id, $friends) { return $this->getTable()->where(’id’, $id)->update(array( ’friends’ => implode("::",$friends) )); } // [4,5,1] => 4::5::1 } V první metodě nalezeneme v databázi konkrétního uživatele a atribut z databáze přeložíme do pole. V druhé metodě nalezneme konkrétního uživatele v databázi a upravíme přeložené pole přátel do řetězce. V komentářích pod metodou je příklad konkrétního překladu. 5.9.3.
Django
V Djangu využijeme JSON knihovnu a její dekodér. Musíme si vytvořit vlastní metody pro serializaci a deserializaci v modelu. import simplejson as json class Profile(models.Model): def save_friends(self, array_friends): self.friends = json.dumps(array_friends) def get_friends(self): jsonDec = json.decoder.JSONDecoder() array_friends = jsonDec.decode(self.friends) return array_friends
70
Třída Profile představuje aktuálního uživatele. Vložíme knihovnu JSON. Pro serializaci využijeme jednoduchou funkci na přeložení (metoda json.dumps()). Pro deserializaci musíme využít dekodér z knihovny. Vlastní překlad provádí funkce decode. 5.9.4.
Shrnutí
RoR jednoznačně v tomto aspektu vede. Stačí využít jednoho příkazu a funkcionalita je splněna. V Djangu je velmi jednoduché vytvořit rozhraní přes JSON a funkcionalita serializace je splněna. V Nette také nebyl problém vytvořit rozhraní pro serializaci, nicméně by zde mohl nastat problém, kdybychom chtěli serializovat nějaký složitější objekt než je pole.
5.10.
CSS
Jak je popsáno v kapitole 4.7., využíváme přednastavený styl Twitter Bootsrap. 5.10.1.
Ruby on Rails
CSS soubory umístíme do app/assets/stylesheet. V hlavičce načtene jednotlivé soubory tímto příkazem: <%= stylesheet_link_tag "application", "bootstrap" %> Application a bootstrap jsou názvy souborů uložené v app/assets/stylesheet. Využíváme standardně přiřazováním jednotlivým elementům HTML identifikátory a třídy. 5.10.2.
Nette
Nové css soubory je třeba umístit do adresáře www/css. Následně přidáme do hlavičky následující element. Proměnná $basePath nám představuje výše zmíněnou cestu umístění. Všimněme si důležitého atributu media, který zařídí nastavení kaskádových stylů v šablonách. Využíváme standardně přiřazováním jednotlivým elementům HTML identifikátory a třídy. 71
5.10.3.
Django
Pro vkládání CSS souborů do aplikace musíme nastavit cestu v konfiguračním souboru. Nastavujeme tyto parametry: import os PROJECT_PATH = os.path.realpath(os.path.dirname(__file__)) MEDIA_ROOT = os.path.join(PROJECT_PATH, ’media’) MEDIA_URL = ’/media/’ STATICFILES_DIRS = ( "/home/.../project/", ) Nastavíme relativní cestu k projektu, dále relativní cestu k samotným souborům. MEDIA URL nám značí, na které adrese si můžeme zobrazit jednotlivé soubory (např. localhost:8000/media/bootstrap.css). Nakonec musíme nastavit absolutní cestu, kde tyto statické soubory hledat. Využití je standardní, nastavováním tříd a identifikátorů elementům HTML. 5.10.4.
Shrnutí
U Djanga je velký problém s nastavování cesty statických souborů, ne vždy v pořádku funguje. Jinak je vkládání souborů a aplikace standardní a bezproblémové.
5.11.
Rychlost vytvoření aplikace
Rychlost vytvoření aplikace se odvíjí od toho, jak je programátor seznámen s frameworkem. V případě, kdy programátor není s frameworkem seznámen, trvá vytvoření aplikace víceméně stejnou dobu ve všech frameworcích. Proto se v této kapitole zaměřím na největší „překážky� každého frameworku. Nicméně pokud by programátor znal všechny frameworky a nemusel by se zaobírat studiem principů, syntaxe a funkcionality, rychlost vytvoření by dle mého názoru dopadla takto: Nejrychleji by byla vytvořena tato aplikace v Djangu, následně v RoR a nejdéle by trvala v Nette. 5.11.1.
Ruby on Rails
Ruby a RoR je relativně nový jazyk, který nevychází ze starších jazyků a snaží se o to, aby byl kód přehledný a co nejvíce pochopitelný. Má hodně součástí a metod a to nutí programátora neustále zkoumat API, které programování zdržuje. Na druhou stranu je výsledek velmi dobrý v rámci přehlednosti a rozšiřitelnosti.
72
Oproti ostatním frameworkům využívá nejvíce souborů, má najnáročnější nastavování a definování pro odesílání emailových zpráv. Dále má nejnáročnější práci s jQuery a Ajaxem. 5.11.2.
Nette
Nette využívá oproti ostatním frameworkům hodně kódu, je to vidět například u dvou metod pro každou šablonu nebo presenteru pro každou komponentu. Díky tomuto si musí programátor udržovat systém, aby se orientoval v aplikaci a nestala se nepřehlednou. Další úskalí je nastavení rozhraní databáze pro každý model zvlášť. Tento frmaework nepotřebuje nastavovat propojení url adres s presentery, čímž se líší od ostatních. Na druhou stranu se programátor musí seznámnit s tím, jak správně nastavovat odkazy. 5.11.3.
Django
U Djanga nastal největší problém v nastavování cesty statických souborů jako jsou kaskádové styly a soubory Javascriptu. Není to jednoznačné, protože v různých systémech může být nastavení odlišné. Další věc, se kterou se musí programátor seznámit, jsou regulární výrazy, kterých se využívá při propojování url a kontrolerů. Ne vždy je to jednoznačné. Programátor si musí dávat pozor na odsazování kódu, protože podle toho Django určuje začátky a konce metod a jednotlivých bloků, jako jsou například podmínky a cykly.
6. 6.1.
Shrnutí práce s frameworky Práce s jednotlivými frameworky
V tomto stylu programování lze snadno poznat vývoj programovacích jazyků. Některé se drží starších osvědčených postupů a některé přívádějí modernější postupy. 6.1.1.
Nette
Nette vychází ze starších postupů, které drží techniku naprogramovat si většinu práce dle sebe. Syntaxe se spíše přibližuje tomu jak pracuje počítač, než jak tomu rozumí člověk. Programátor musí plně rozumět funkcionalitě nejen aplikace, ale i dotyčného frameworku, a proto si framework může upravovat dle sebe. Zároveň vychází ze základů, které by měl každý programátor znát.
73
Úskalí nastává nejen v objemu kódu, který je potřeba pro základní součást, ale programátor si dbát na přehlednost kódu. Pokud programátor potřebuje nadstardardní funkce, musí si většinu naprogramovat sám. Při využití tohoto frameworku musíme počítat s malou komunitou, a proto musíme některé nevyřešené problémy vymýšlet sami. Nette však obsahuje všechny základní součásti, se kterými umí bez větších problémů pracovat, eventuelně je upravit. Rozšíření je velmi snadné. Framework je navíc jeden nejrychlejších, protože pracuje na nižší úrovni než většina ostatních frameworků. Při vytváření testovací aplikace se mi hodně líbil Debugger Bar, který mi hodně pomohl v tom, abych věděl, co všechno se na aktuální stránce stalo. Dále snadnost nastavení volání Ajaxu. Jako možné vylepšení, které by zpřehlednilo kód, by mohlo být sloučení dvou metod pro každou šablonu do jedné. Další usnadnění práce by mohlo být v lepší vrstvě pro snadnější práci s databází. Pro přehlednější implementaci šablon by šablony mohli mít příponu, kterou by podporovali obecně používané editory. Například v editoru Eclipse není přípona .latte podporována. 6.1.2.
Ruby on Rails
Jelikož Ruby je relativně nový jazyk a RoR z něho vychází. Syntaxe se snaží o co největší srozumitelnost. V některých případech jde kód číst skoro jako věta. Tento framework má velkou škálu metod a tříd se kterými pracuje. Pokud ale programátor do tohoto frameworku pronikne a naučí se s ním pracovat, dokáže celkem rychle a srozumitelně naprogramovat to co potřebuje. RoR využívá největší strukturu na počet souborů. Na první pohled se tato struktura zdá nadbytečná, ale při roustoucí aplikaci je udržena snadná orientace. Framework má skoro všechny základní součásti integrovány. Protože je komunita okolo frameworků velmi rozsáhlá, existuje spousta snadno dostupných rozšíření, které jsou neustále rozvíjena a dokumentována. Navíc ke stejným funkcionalitám existuje více rozšíření, takže si programátor může vybírat. Slabá stránka tohoto frameworku je rychlost. Aplikace v RoR by neměli obsahovat složité výpočty. Práce s tímto frameworkem se mi líbila nejvíce díky syntaxi jazyka. Např. pokud programátor potřebuje zjistit obsah metody, se kterou delší dobu nepracoval, hned vidí, jakou funkci provádí. Jako možné vylepšení frameworku by mohlo být zjednodušené propojení jQuery s kódem RoR. Zobrazení výjimky by mohlo poskytnout podrobnější informace o aktuální stavu aplikace. 6.1.3.
Django
Django je „střední cestou� mezi předcházejícími dvěma frameworky. Není tak rozsáhlý jako RoR, ale je kód je mnohem více srozumitelnější než v Nette. 74
Má vlastní přístup k MVC, který je nejvíce srozumitelný z hlediska propojení jednotlivých součástí. Díky tomuto aspektu se v něm programátor začne rychle orientovat a pochopí syntaxi. Má nejmenší obsah kódu, který je i přesto velmi srozumitelný. Kód by při větši aplikaci nemusel být přehledný. Všechny základní součásti jsou integrovány a snadno použitelné. Škála rozšíření není velká. Nicméně většina problémů je dohledatelná u komunity. Django bylo vytvořeno tak, aby nebylo náročné na zpracování. I rychlost zde vytváří „střední cestu� mezi předchozími frameworky. Líbilo se mi, že jsem se v tomto frameworku velmi rychle zorientoval i přes neznalost Pythonu. Dále se mi líbilo vytvoření a úprava tabulek přes model. Zlepšil bych přístup aktuálně přihlášeného uživatele a určitě nastavení umístění statických souborů.
6.2.
Výběr frameworku a doporučení
Při výběru frameworku musíme brát v potaz několik aspektů. Přední jsou zkušenosti programátora, velikost aplikace, náročnost aplikace a podpora komunity. Pokud je programátor zkušený a je takzvaný „hacker�, který chce mít vše po svém a navíc má zkušenosti s programovacími jazyky typu C, C++, . . . , jednoznačná volba pro něj je Nette. Pokud je programátor začátečník, nebo mu nevadí používat součásti, ke kterým nepotřebuje na 100% vědět, jak uvnitř pracují, volba pro něj je RoR nebo Django. Pokud má být aplikace relativně malá, o velikosti například této testovací aplikace, kde není potřeba žádné složité práce se serverem, nastovávní složitých účtů, práv a pod., doporučoval bych Django. Díky rychlosti vytvoření a jednoduchosti základních součástí je vhodný. Ruby on Rails bych volil pro složitou aplikaci, která má hodně součástí a funkcionalit. Pokud potřebujeme aplikace, které mají provádět složitější výpočty, je nejlepší použít Nette, protože je nejrychlejší. Pokud by složitých výpočtů nebylo tolik, Django by se také dalo použít. RoR bych nedoporučoval. Jako poslední hlavní aspekt je podpora komunity, a to hned ze dvou důvodů. První důvod je pomoc s řešením chyb a druhý je škála rozšíření. Tyto dva aspekty usnadňují práci a rychlost vytvoření aplikace. V tomto aspektu vede RoR s velkou komunitou, následně Django a nakonec Nette, který prozatím nemá moc velkou podporu.
75
Závěr Cílem této diplomové práce bylo vypracování odborné studie hodnotící a porovnávající vybrané webové frameworky na konkrétně vytvořené testovací aplikaci. Testovací aplikace byla vytvořena ve všech frameworcích se všemi porovnávanými aspekty. Ve shrnutí hodnocení a porovnání frameworku jsme nastínili konkrétní doporučení právě na základě požadovaných vlastností aplikace a zkušenosti programátora s frameworky. Dle očekávaní je závěr studie takový, že při výběru ideálního frameworku vždy záleži na vlastnostech cílové aplikace. Nelze zvolit jednoznačného vítěze, který by byl vhodný pro tvorbu všech aplikací.
76
Conclusions The goal of this thesis is focused on creating a professional study that evaluates and compares chosen web frameworks by means of a specifically designed testing application. This testing application was made in all frameworks with all the compared aspects. In the summary of evaluating and comparing the frameworks we outlined the particular recommendations on the grounds of the required characteristics of the application and the programmers’ experience with these frameworks. As expected, the study reached the conclusion that in order to choose the optimal framework one has to consider the characteristics of the targeted application. A definitive winner suitable for all applications, therefore, cannot be determined.
77
Reference [1] Zdroják.cz - Internetový portál zabývající se programováním. Bernard, Borek. Úvod do architektury MVC, 2009 URL: http://www.zdrojak.cz/clanky/uvod-do-architektury-mvc/ [citováno 11. dubna 2013]. [2] root.cz - Internetový portál zabývající se IT. Šťastný, Jakub. Ruby on Rails 2.0: Evoluce, nikoliv revoluce, 2007 URL: http://www.root.cz/clanky/ruby-on-rails-2-0-evoluce-nikoliv-revoluce/ [citováno 11. dubna 2013]. [3] Originální návrh architektury MVC. URL: http://heim.ifi.uio.no/ trygver/2007/mvcoriginals.pdf [4] Django - oficiální stránky URL: https://docs.djangoproject.com/ [5] Nette framework - oficiální stránky URL: http://doc.nette.org/cs/ [6] RailsGuides - průvodce Ruby on Rails URL: http://guides.rubyonrails.org/ [7] Wikipedie - otevřená encyklopedie URL: http://cs.wikipedia.org Klíčová slova: framework, ruby on rails, nette, django, dynamický web, MVC [8] Wikipedie - otevřená encyklopedie URL: http://en.wikipedia.org Klíčová slova: framework, ruby on rails, nette, django, dynamic web, MVC
78
A.
Obsah přiloženého CD
K práci je přiloženo CD, na kterém se nachází testovací aplikace v každém frameworku a elektronická verze tohoto dokumentu.
doc/ Dokumentace práce ve formátu PDF, vytvořená dle závazného stylu KI PřF pro diplomové práce, včetně všech příloh, a všechny soubory nutné pro bezproblémové vygenerování PDF souboru dokumentace (v ZIP archivu), tj. zdrojový text dokumentace, vložené obrázky, apod. app-django/ Testovací aplikace vytvořená ve frameworku Django zahrnující všechny soubory. app-nette/ Testovací aplikace vytvořená ve frameworku Nette zahrnující všechny soubory. app-ror/ Testovací aplikace vytvořená ve frameworku Ruby on Rails zahrnující všechny soubory. readme.txt/ Zdroj převzatých obrázků. U veškerých odjinud převzatých materiálů obsažených na CD jejich zahrnutí dovolují podmínky pro jejich šíření nebo přiložený souhlas držitele copyrightu. Pro materiály, u kterých toto není splněno, je uveden jejich zdroj (webová adresa) v textu dokumentace práce nebo v souboru readme.txt.