Málokdo má takovou potřebu zdůrazňovat svou domnělou
nadřazenost, jako právě Railisti. Abyste mě nechápali špatně, jde
o dobrou marketingovou strategii. Nepříjemné je, když jí podlehnete do té
míry, že zbytek světa vnímáte jen jako upachtěné kopírovače bez šance
se vám někdy přiblížit. Svět takový totiž není.
Příkladem je Dependency Injection. Zatímco lidé kolem PHP nebo
JavaScriptu objevili DI se zpožděním, Ruby on Rails zůstávají dosud
nepolíbené. Bylo mi záhadou, proč framework s tak pokrokovou image
zůstává kdesi pozadu a začal v tom pátrat. Odpověď mi dala řada zdrojů
na Google nebo karmiq
a zní:
Ruby je tak dobrý jazyk, že vůbec Dependency Injection nepotřebuje.
Fascinující argument, který je navíc v elitářském prostředí
sebepotvrzující. A je skutečně pravdivý? Nebo jde jen o zaslepení
pýchou, stejné zaslepení, jaké způsobilo nedávno přetřásané
bezpečnostní díry v Rails?
Říkal jsem si, že je možné, že Ruby znám natolik málo, aby mi
nějaký klíčový aspekt unikl, a že skutečně jde o jazyk, který DI
nepotřebuje. Jenže primárním smyslem Dependency
Injection je zřejmé předávání závislostí, aby byl kód
srozumitelný a předvídatelný (a je pak i lépe testovatelný). Jenže
když se podívám do dokumentace Rails na tutoriál „blog za pár minut“,
vidím
tam třeba:
def index
@posts = Post.all
end
Tedy pro získání blogpostů používají statickou metodu
Post.all, která odněkud (!) vrátí seznam článků.
Z databáze? Ze souboru? Vyčaruje je? Nevím, protože se tu nepoužívá DI.
Místo toho se tu vaří nějaké statické peklo. Ruby je bezesporu
šikovný jazyk, ale DI nenahrazuje.
V Ruby lze za běhu přepisovat metody (Monkey patch; obdobně jako
v JavaScriptu), což je forma Inversion of control (IoC), která třeba pro
potřeby testů dovolí podstrčit jinou implementaci statické metody
Post.all. Tohle ale nenahrazuje DI, kód to zřejmější
neudělá, spíše naopak.
Mimochodem, zaujala mě i třída Post tím, že reprezentuje jak jeden
článek na blogu, tak funguje jako repozitář (metoda all), což je porušení
Single
Responsibility Principle jako vyšité.
Jako odůvodnění, proč Ruby nepotřebují DI, se často odkazuje na
článek LEGOs,
Play-Doh, and Programming. Důkladně jsem ho pročetl, sledoval, jak autor
párkrát zaměňuje „DI“ s „DI frameworkem“ (tedy něco jako
zaměňovat „Ruby“ s „Ruby on Rails“) a nakonec zjistil, že
k závěru, že Ruby Dependency Injection nepotřebují, vůbec nepřišel.
Psal, že nepotřebují DI frameworky, jaké zná z Javy.
Jeden mylně interpretovaný závěr, pokud pohladí ego, dokáže zcela
pobláznit obrovskou skupinu inteligentních lidí. Nakonec mýtus, že špenát
obsahuje neobyčejné množství železa, se taky drží od roku 1870.
Ruby je velmi zajímavý jazyk, vyplatí se v něm jako v každém jiném
používat DI a existují pro něj i DI frameworky. Rails je zajímavý
framework, který zatím neobjevil DI. Až ho objeví, půjde o velké téma
některé z příštích verzí.
(Po pokusu diskutovat o DI s Karmim, kterého pokládám za
nejinteligentnějšího Railistu, nechávám komentáře uzavřené,
omlouvám se.)
Kdo z vás si myslí, že funkce vrátí false, protože
regulární výraz běží v jednořádkovém režimu a nepovoluje v řetězci
žádné jiné znaky krom číslic, ten se mýlil.
Malinko odbočím. Regulární výrazy v jazyce Ruby mají jednu nectnost
(nesoulad s de facto standardem PERLu): znaky ^ a $
neoznačují začátek a konec řetězce, ale jen jednoho řádku v něm.
Neznalost tohoto faktu může způsobit bezpečnostní zranitelnost, jak třeba
upozorňuje dokumentace
Rails. PHP se chová standardně, ale málokdo už ví, co přesně ono
standardní chování znamená. Dokumentace meta-znaku $ je totiž
nepřesná.
(už opraveno)
Správně má být, že znak $ znamená konec řetězce nebo
ukončující odřádkování; ve víceřádkovém režimu (modifikátor
m) znamená konec řádku.
Skutečný konec řetězce chytá sekvence \z.
Nebo je možné použít dolar společně s modifikátorem
D.
Pokud vám někdo na GitHubu pošle commit, je vhodné ho před začleněním
do hlavní větve ověřit nebo třeba upravit. Jak si snadno commit z GitHubu
vyzobnout do svého repozitáře? Používám k tomu skript
remotepick.php, který spustím v repozitáři a jako parametr
uvedu URL commitu. Což je adresa, na kterou vede například odkaz
363413a na této
stránce. Takže spustím:
Pokud máte v Git repozitáři víc vývojových větví, je rozhodně
dobré je průběžně aktualizovat oproti masteru. V řeči Gitu jde o
rebase. Aktualizované větve se pak mnohem snáze aplikují
(merge) do hlavní vývojové větve. A pokud máte větví opravdu hodně,
aktualizovat každou zvlášť je zdlouhavé. Pomoci může skript
rebaseall.php, který stačí zavolat někde uvnitř
repozitáře:
<?php
// zjisti seznam větví
exec('git branch', $branches);
// postupně každou rybejzuj proti masteru
foreach ($branches as $branch) {
$branch = trim($branch, "* \r\n");
passthru("git rebase master $branch", $errorCode);
if ($errorCode) {
exit;
}
}
// a nakonec se vrať na master
passthru('git checkout master');
Je možné, že se to dá zapsat i jedním příkazem z bashe, ale já rád
skriptuju v PHP a bash nemám.
Dependency
Injection je technika, která řeší určité problémy, ale zároveň
přináší těžkosti nové. Ty se pak snaží eliminovat DI kontejner, který od vás vyžaduje
změnit pohled na objektový návrh.
Pokud vás netrápí problémy, které DI řeší, tak jeho nasazení budete
vnímat jen jako zbytečnou obtíž. Třeba právě v nutnosti osvojení
nového pohledu na objektový návrh.
Nicméně zdá se, že pokud vás netrápí problémy, které DI řeší,
máte vážný problém. Což zjistíte, až to zjistíte.
Znáte ty nářky vývojářů, že jejich klient nemá jasnou
představu a neustále mění zadání zakázky? To pláčou nad vlastní
neschopností. Když je slyším, nejraději bych popřál nebohému klientovi
lepšího dodavatele.
Klient nemá jasné zadání, protože není odborník na webdesign.
Zajímalo by mě, kolik webdesignérů se vyzná v podnikání svého klienta
tak dobře, že by dokázalo vytvořit precizní zadání, kdyby se karty
obrátily.
Pokud klient zadání průběžně mění, znamená to, že ho projekt
zajímá a baví, že nad ním neustále přemýšlí. Je pak větší
pravděpodobnost, že vznikne něco skutečně užitečného. A především:
bude poptávat další a další práci.
Pokud si vývojář tyto věci uvědomí, pak pochopí, že je to právě on,
kdo musí přizpůsobit svůj styl práce. Třeba zjednodušit přidání kolonky PSČ na web, ač
to v původním zadání nebylo.
„Ty bys měl školit Nette,“ řekl mi Vašek WebExpo Stoupa a já se pak
přes půl roku rozhoupával, než v listopadu 2008 uskutečnil první
školení Vývoj
webových aplikací v Nette Framework. Od té doby prošlo kurzem asi
300 kolegů programátorů.
„Ty bys měl školit jQuery,“ řeklo mi pár účastníků školení
Nette a já se pak přes půl roku rozhoupával, než vypsal první školení jQuery a AJAX, na
které vás tímto zvu. (Než jsem se rozhoupal k napsání tohoto článku,
kurz je z poloviny naplněn. Málo houpu.)
Co vám kurz dá? Vím, že to zní jako otřepaná fráze, ale rád bych
ukázal, že oživování webové stránky pomocí JavaScriptu může být
skutečně zábavné. Seznámím vás s nejpopulárnějším JavaScriptovým
frameworkem jQuery na praktických příkladech.
Přitom budeme dbát na čistý návrh a znovupoužitelnost, protože ve
chvíli, kdy programátor zabředne do bastlení, je se zábavou smyčec. Nebo
šmitec. Celým kurzem se potáhne ústřední motiv user experience,
tj. vysvětlíme si a ukážeme, kterou cestou se vydat, aby výsledek byl pro
uživatele co nejintuitivnější. Protože o nic jiného vlastně nejde.
Bylo by fajn, kdybyste na školení vyrazili se základní znalostí
JavaScriptu (stačí rozumět tomuto
článku bez kapitol Properties a Dědičnost). Pokud zatím neznáte jQuery
a rádi byste to napravili, jsem vám k službám!
Schválně, který software má v dokumentaci uvedeno, že se jedná
o voodoo? No jistě, jde o mod_rewrite. Ze
zkušenosti mohu říci, že programátoři se dělí do dvou skupin:
ti, kteří mod_rewrite nerozumí
ti, kteří si myslí, že mod_rewrite rozumí, avšak mýlí se
Do které skupiny patříte vy? Zkuste nahlédnout do svých souborů
.htaccess a podívejte se, zda vám u pravidel pro
přesměrování (příznak
R) nechybí také příznak NE (noescape)?
Vysvětlím na příkladu: do kořenového adresáře webu
www.example.cz vložím soubor .htaccess s pravidlem
pro přesměrování:
RewriteEngine On
RewriteRule .* http://www.example.com/$0 [R=301] #tohle je spatne!
Server pak přesměruje
z http://www.example.cz/index.php?title=d%C3%ADvka (parametr
title obsahuje slovo dívka)
na http://www.example.com/index.php?title=d%25C3%25ADvka
(parametr title obsahuje řetězec d%C3%ADvka)
Jak vidíte, mod_rewrite ublížil dívce! Je to jeho přirozené chování,
aby to nedělal, musíte mu říct NE:
RewriteEngine On
RewriteRule .* http://www.example.com/$0 [R=301,NE] #tohle uz je spravne
Mám ve zvyku vyvíjet a spouštět webové aplikace na doménách
s příponou .l, takže třeba vývojová verze
https://nette.org mi běží na http://nette.l. Což
znamená přidat do souboru hosts řádek pro každou
subdoménu, např.:
To je přinejmenším otravné. Kéž by hosts podporoval zápis
pomocí wildcards, stačilo by napsat
*.l 127.0.0.1
a měl bych vystaráno. Jenže tohle nefunguje. Hledal jsem proto jiné
řešení. K velkému překvapení, internet se návody nejen že nehemží,
nenašel jsem vůbec nic.
Bylo zřejmé, že budu potřebovat najít lokální DNS server, který toto
umožní. Narazil jsem na Simple DNS
Plus. Po instalaci je potřeba jej manuálně aktivovat, tj. říci
síťovému připojení, že má používat DNS server na adrese
127.0.0.1. Poté přímo v aplikaci v Tools / Options / Plug-Ins vytvořit
instanci pluginu Regular Expressions a určit, že maska \.l$ se
bude mapovat na adresu 127.0.0.1.
Funguje to výborně, jen cena $79 mi nepřipadá odpovídající,
využívám-li okrajové vlastnosti jinak našlapaného programu.
Další možností je instalace multiplatformního opensource DNS serveru BIND. Stáhl jsem si distribuci pro
Windows a nainstaloval do výchozího adresáře. Ovšem zapomeňte na nějaké
klikací prostředí, BIND zná jen příkazovou řádku a textové
konfigurační soubory. Pro mě španělská vesnice. Naštěstí mě pohled do
dokumentace neodradil rovnou a
podařilo se mi vytvořit konfigurační soubory. Ty se nacházejí
v podadresáři etc, v mém případě je to
c:\Windows\System32\dns\etc\. Na vašem počítači může být
cesta odlišná.
Soubor etc\named.conf
options {
directory "C:\Windows\System32\dns\etc"; // změňte pokud používáte jinou cestu.
};
zone "localhost" {
type master;
file "localhost";
};
zone "l" {
type master;
file "localhost";
};
zone "0.0.127.in-addr.arpa" {
type master;
file "localhost.rev";
};
Dovolte mi malý experiment. Týká se všech programátorů, které baví
návrh aplikací a OOP. Zadám vám velmi jednoduchý úkol, který má mnoho
možných řešení. A spíš než konkrétní kód mě zajímá způsob
uvažování. Budu rád, když se zapojí programátoři používající různé
jazyky. Proto také zadání zapíši v pseudokódu.
Mějme třídu WebPage, které zadáme URL a ona načte stránku
a vrátí jeji obsah, hlavičky a dokonce i náhled v podobě obrázku.
Příklad použití:
Chápejme url, body, headers a
thumbnail jako vlastnosti (properties, accessors) třídy
WebPage. Je asi zřejmé z logiky věci a principu zapouzdření,
že zatímco url umožňuje zápis i čtení, ostatní vlastnosti
lze pouze číst.
Protože funkčnost třídy WebPage je výkonnostně i časově
náročná, je vhodné jednou získaná data ukládat do databáze. Jak to ale
implementovat? Sice nejsnadnější by bylo rovnou upravit kód třídy
WebPage, jenže takový
postup je špinavý. Proč by třída s jednou srozumitelnou
funkcionalitou měla navíc ještě komunikovat s databází? Pověříme tím
tedy třídu WebPageStorage. Ta nám bude, mimo jiné, schopna
dodat objekt WebPage rovnou z databáze:
Zopakuji, že se pídíme po nejčistším řešení. Jak docílit toho, aby
metoda load mohla vytvořit objekt a nastavit mu read-only
vlastnosti body, headers a thumbnail na
hodnoty načtené z databáze? Vytvořením setterů nebo zpřístupněním
vnitřních proměnných by se porušil princip zapouzdření. Navrhli byste
redesign třídy WebPage? Jaký? Navrhli byste redesign
WebPageStorage? A co když úkol zkomplikujeme tím, že
thumbnail se z databáze načte až při vyžádání?
Věřím tomu, že pro řadu čtenářů je zadání naprosto triviální.
Přesto se zkuste zamyslet nad nejčistším řešením a vysvětlete jej
v komentářích. Klidně obšírně. Díky!