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, kteréžto nedávno způsobilo aféru kolem bezpečnostních děr
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
úžasný 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é:
class Post < ActiveRecord::Base
attr_accessible :content, :name, :title
end
Řádná dávka code smellu… Až mi bude opět někdo zdůrazňovat, že
Rails jsou intelektuálně úplně jinde, budu vědět, kde zhruba
je jinde.
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 mýlící se závěr, který pohladí ego, dokáže zcela pobláznit
obrovskou skupinu inteligentních lidí. Nakonec mýtus, že špenát má
obrovské 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.)
Vyplatí se používat CSS preprocesory? A pokud ano, který
zvolit? Pokračuji v rozboru tří nejznámějších konkurentů.
CSS preprocesor je nástroj, který vám ze zdrojového kódu zapsaného ve
vlastní syntaxi vygeneruje CSS pro prohlížeč. Mezi nejznámější patří
SASS, LESS
a Stylus. Mají řešit
praktické nedostatky samotného CSS.
V předchozí části jsme
si ukázali, jak preprocesory nainstalovat. Dnes se jim podívám na zoubek.
Syntaxe
Ač jednotlivé nástroje používají odlišnou syntax, všechny rozumí
klasickému CSS. To je nesmírně důležité! Můžete kopírovat
existující CSS fragmenty a budou fungovat. Ačkoliv…
SASS používá dvě různé syntaxe, jedna se nazývá SASS (stejně jako
preprocesor), nepoužívá středníky ani složené závorky {} a
místo toho odsazuje mezerami či tabulátory:
// SASS
#main
color: blue
font-size: 0.3em
Druhou syntaxí je SCSS, která vypadá jako klasické CSS a měla být
i plně kompatibilní (budu ji pro SASS používat v dalších příkladech).
Stejnou syntax má i LESS:
// SCSS, LESS a CSS
#main {
color: blue;
font-size: 0.3em;
}
Stylus také rozumí CSS syntaxi, nicméně upozorňuje, že nejde o 100%
kompatibilitu, především proto, že hodně bazíruje na odsazování. V jeho
syntaxi lze vynechat složené závorky, středníky a dokonce
i dvojtečky:
// Stylus
#main
color blue
font-size 0.3em
Ale pozor! Důležité je, že tyto znaky můžeme vynechat. Pokud
vám zápis připadá příliš hutný, klidně si dvojtečky doplňte. Nechcete
odsazovat? Vražte tam složené závorky. Velmi podobně funguje NEON. V tomto se zásadně liší od úsporné
SASS syntaxe, kde vynechání závorek a středníků je povinnost. Což
ji činí z praktického hlediska nepoužitelnou a chápu, proč tvůrci
o kompatibilitu s CSS usilovali, ale udělali to nešťastně zavedením
další, nekompatibilní, ukecané syntaxe SCSS.
Když už se totiž rozhodnu preprocesor používat, ocením, když mi
zjednoduší i syntaxi (alespoň nepovinné středníky), což bohužel umí
jen Stylus. Jeho syntax je nejpružnější a nejúspornější.
V kontrastu přísnosti LESS a SASS je zajímavé, že všechny tři
preprocesory podporují // řádkové komentáře.
Když udělám chybu
Pokud uděláte v dokumentu chibu, preprocesory vás na ni upozorní
chybovou hláškou. A v tomto směru se jednotlivé nástroje zásadně
liší. Pokud třeba zapomenu ve výše uvedeném kódu uzavírací
}, oznámí SASS:
Syntax error: Invalid CSS after "font-size: 0.3em;": expected "}", was ""
on line 4 of test.scss
ParseError: missing closing `}` in test.less:9:17
8
9 unexpected "eos"undefined
Nutno dodat, že LESS měl šťastnější chvilku. Pokud bych zapomněl
středník, skončí to u obvyklejšího
ParseError: Syntax Error on line 2 in test.less:2:1
1 #main {
2 color: blue
3 font-size: 0.3em
Výmluvné chybové hlášky jsou pro mě důležité a v tomto směru
s přehledem vede SASS. Ačkoliv se to z ukázky tak nejeví, hodně se
snaží i Stylus. Běžně však špatně spočítá číslo řádku a hlásí
chybu o pár řádků výš, než skutečně je. LESS se nejčastěji zmůže
na nicneříkající Syntax Error on line XY.
Občas jsem narážel na chyby v samotném preprocesoru. Pokud se mi je
podařilo izolovat, snažil jsem se je nahlásit (LESS, Stylus), ale ne vždy
to šlo. Zde se nejvíc projevila vyspělost jednotlivých nástrojů. Zatímco
nejstarší SASS se mi z rovnováhy nepodařilo vyvést nikdy, druhé dva
nástroje obsahují chyb bohužel požehnaně.
Co všechno umí?
Drtivou většinou užitečných vychytávek najdeme u všech tří
nástrojů, byť třeba nemusí být uvedeny v dokumentaci. V množství asi
vede SASS, ale takto se to hodnotit nedá. Třeba nested
properties nepotřebuji, naopak property
lookup a generátor data URI
ve Stylusu vypadá užitečně. Tedy do chvíle, než zjistíte, že převádí
buď všechny obrázky, nebo žádný.
Nechci sepisovat výčet jednotlivých vlastností ani srovnávat jejich
zápis, to najdete v dokumentaci nebo ve srovnávacím
článku na NetTuts. Zaměřím se místo toho na fundamentální rozdíly a
největší z nich se týkají tzv. mixinů.
Mixiny
Mixiny jsou z mého pohledu nejdůležitější vychytávkou preprocesorů,
protože mění způsob, jak píšeme HTML kód. Vysvětlím. Asi
nejznámější „mixin“ je tzv. clearfix, která se stará o to, aby
z bloku nevytékaly plovoucí prvky. Obvykle vypadá nějak takto:
(Poznámka: existují jednodušší i složitější varianty clearfixu.
Kupodivu mně vždycky fungovalo nastavit bloku overflow: auto a
clearfix jsem nepotřeboval. Ale v tuto chvíli jde jen
o příklad.).
Ale to neděláš dobře, Jaromíre. Clearfix by se měl aplikovat na straně
stylů, přímo pro #content, nikoliv v HTML. Jenže komu by se
chtělo kazit si krásný stylopis kopírováním ošklivého hacku? Mnoho
kodérů raději sáhne do HTML.
S preprocesory však netřeba nic kopírovat a vystačíme si s jedinou
instrukcí. Nejprve si clearfix uložíme jako tzv. mixin a poté ho v definici
#content zavoláme:
Což je velmi zajímavé! Když totiž později vendor prefix odstraníme,
nebo když naopak zjistíme, že určité vlastnosti potřebujeme vendorovou
variantu dodat, nemusíme změnit ani čárku v samotném stylu. Wow.
Jenže… co když mixin zavoláme s více parametry? Například uvedeme
border-radius: 2px 3px. Překvapivě Stylus bude 3px
v tichosti ignorovat. Proč tomu tak je?
V CSS existují dva způsoby, jak uvést více argumentů, lišící se
v oddělovači:
oddělené mezerama: border: 1px solid black
oddělené čárkama: rgb(10, 20, 30)
Totéž se rozlišuje i v preprocesorech. Ve Stylusu platí, že volání
mixinu border-radius: 2px 3px je ekvivalentní k
border-radius(2px, 3px) (a dává mi to smysl), obojí představuje
předávní dvou argumentů. Naopak border-radius(2px 3px) bez
čárky předává argument jeden. Náš mixin očekává pouze jeden parametr a
druhý tiše bez jakéhokoliv varování zahodil. Oprava spočívá v použití
užitečného klíčového slova arguments namísto předaného
argumentu arg.
SASS a LESS naopak předání více argumentů, než je mixinem očekáváno,
vnímají jako fatální chybu. V případě SASS to v důsledku vede
k podivným blokům $shadow-1, …, $shadow-10 patrným
v galerii Compass, zatímco LESS se s problémem snaží vypořádat
konstrukcí when(). Nic z toho mi nepřipadá ideální.
Už pár let jsem si pořádně nezakódoval a začalo mi to
chybět. Zachtělo se mi udělat stránky podle nejnovějších trendů.
Responsivní design okořeněný CSS preprocesory. Ale váhal jsem: jsou
preprocesory víc, než jen chvilková móda?
CSS preprocesor je nástroj, který vám ze zdrojového kódu zapsaného ve
vlastní syntaxi vygeneruje CSS pro prohlížeč. Mezi nejznámější patří
SASS, LESS
a Stylus.
Faktem je, že jakmile začne stylopis kynout do větších rozměrů, řada
věcí se řeší dosti nepohodlně. Je třeba vynaložit úsilí, aby zůstal
čitelný a srozumitelný. Aby se z něj nestal write-only soubor plný
magický konstant a hacků. Spoustu těchto nešvarů preprocesory řeší,
nejvíc se těším na vnořené
definice, matematické
výrazy, mixiny
a proměnné.
Vlastně je smutné, že preprocesory musely vzniknout. Ukazuje to na
zanedbanost vývoje CSS. Na druhou stranu, může z nich i těžit.
Preprocesory jsou mladé projekty procházející bouřlivým vývojem, reagují
na potřeby uživatelů a lze u nich, na rozdíl od standardu, tolerovat
i případné nekompatibilní změny. Ve finále tak mohou ukázat směr,
kterým se má vydat příští CSS.
Pokud se kódováním webů bavíte řadu let, máte vybudovanékonvence
pomáhající nedostatky obcházet. Preprocesory pak nemusí být úplně
samozřejmou volbou. Nicméně dnes je běžné používat různé CSS generátory a preprocesor nabízí
čistější cestu, než copy&pastovat vygenerovaný kód.
Rád zkouším nové věci, proto jsem dal preprocesorům šanci. Který ale
zvolit? Nejlepší je si všechny osahat.
Instalace
Začneme tedy rovnou instalací. Na webu SASS i LESS rychle najdete
vyčerpávající postup, jak knihovny získat. SASS je napsaný v Ruby, LESS v Node.js, takže prvním krokem bude instalace
překladače, což by neměl být v žádném operačním systému problém.
Preprocesor pak nainstalujete příkazem:
gem install sass
resp.
npm install less
Velmi snadné, PHP může závidět.
Naopak web Stylusu selhává, snaží se mást odkazy na Github, zatímco
informace, jak ho instalovat, je důmyslně skrytá kdesi vespod úvodní
stránky. Vězte tedy, že Stylus je také napsán v Node.js a nainstaluje se
obdobně příkazem npm install stylus.
Pozor na jednu věc, npm instaluje balíček do aktuálního adresáře, takže
abyste mohli preprocesory pohodlně spouštět z příkazové řádky, musíte
si cestu k lessc.cmd a stylus.cmd přidat do
proměnné PATH. Ve Windows se to dělá sparťansky, takže
spíš oceníte možnost nainstalovat balíčky do globálního adresáře
(pomocí přepínače -g, tj. npm install -g stylus),
ke kterému už cestu zaregistroval při instalaci překladač.
Příkazová řádka pro SASS a Stylus nabízí spoustu voleb, LESS umí jen
konvertovat vstupní soubor do výstupního CSS. Zmátlo mě, že SASS i Stylus
zavolané bez parametrů se nijak neohlásí a očekávají vstup
z klávesnice. Zavolejte je tedy s parametrem -h a vypíše se
nápověda všech voleb.
Vývoj a deployment
Preprocesory vyžadují, aby se mezi úpravou stylopisu a zobrazením
v prohlížeči udělat jeden krok navíc: kompilace do CSS.
Tento krůček může mnohé odradit. Pokud jste zvyklí rovnou editovat CSS
soubory na FTP serveru, nad preprocesorem vůbec neuvažujte. Existují sice
možnosti, jak třeba z LESS generovat CSS za běhu přímo v prohlížeči,
ale rozhodně nejsou určeny pro produkční nasazení.
Pokud máte deployment automatizovaný, stačí do procesu zařadit kompilaci
voláním příkazové řádky a je vystaráno.
Jak řešit onen krok navíc během vývoje? Kodéra rozhodně blbnutí
s příkazovou řádkou nezajímá a chce rovnou psát stylopis.
Jak jsem zmínil, LESS umí překládat stylopisy v prohlížeči, stačí
tedy zalinkovat soubor less.js a můžete rovnou připojovat soubory ve formátu
LESS (povšimněte hodnoty atributu rel):
SASS a Stylus zase nabízejí sledovací režim, ve kterém monitorují
adresář se styly ve svém formátu a při každé změně souboru je ihned
překládají do CSS.
// překlad souborů z adresáře /css.sass do /css (včetně podadresářů)
sass --watch css.sass:css
// překlad souborů z adresáře /css.stylus do /css
stylus --watch css.stylus --out css
Do vygenerovaného CSS lze pomocí přepínače --line-numbers
přidat pro lepší orientaci komentáře s číslem řádku zdrojového
souboru. Pokud vyvíjíte ve Firefoxu, ještě užitečnější je nainstalovat
FireStylus
a kompilovat s přepínačem --firebug. V záložce HTML by se
pak měly objevovat odkazy přímo na zdrojový soubor. Píšu měly, protože
mi to nefunguje.
Všechny tři preprocesory jsou seřazeny na startovní čáře. Který
z nich běží nejlépe? Pokračování příště.
I found a strange bug in Internet Explorer 9, which appears in the last
version 9.0.8112.16421. If the same cookie is sent twice, once with the
expiration at the end of the session and once with any other expiration, IE9
will remove cookie. Webpage must have a strict doctype.
Example:
<?php// expire at the end of the session
setcookie('test', 'value', 0);
// expire in 1 hour
setcookie('test', 'value', 3600 + time());
// switch browser and document mode to IE9
?>
<!doctype html>
Is cookie set? <?phpechoisset($_COOKIE['test']) ? 'yes' : 'no'?>
S pomocí jQuery lze vytvořit HTML element docela jednoduše:
var $el = $('<a href="http://phpfashion.com">blogísek</a>');
Do proměnné $el se přiřadí objekt jQuery obalující
vytvořený HTML element (proto jsem použil dolar v názvu proměnné),
k nativnímu DOM objektu se dostanete přes $el[0].
Co ale v případě, že potřebujeme jako hodnoty použít proměnné?
Přímočaré řešení by vypadalo takto:
// špatně: riziko XSS
var url = 'http://phpfashion.com';
var title = 'blogísek';
var $el = $('<a href="' + url + '">' + title + '</a>');
Jenže, jak uvádí komentář v kódu, koledujeme si tímto o průšvih
jménem Cross-site scripting (XSS). Stačí, aby například proměnná
url obsahovala uvozovky a výsledek bude nezamýšlený. Proto je
nutné proměnné escapovat, tj.
nahradit znaky mající speciální význam za HTML entity. Na to si můžeme
napsat funkci escapeHtml:
// správné, ale ne příliš přehledné
var escapeHtml = function(s) {
return s.replace('&', '&').replace('"', '"')
.replace("'", ''').replace('<', '<');
};
var $el = $('<a href="' + escapeHtml(url) + '">' + escapeHtml(title) + '</a>');
Skládání řetězců postrádá onu lehkost a srozumitelnost první
ukázky, nemluvě o riziku, že escapeHtml zapomeneme zavolat.
Naštěstí jQuery od verze 1.4 nabízí elegantní řešení: proměnné
uvedeme ve druhém parametru (viz dokumentace)
// dokonalé
var $el = $('<a>', {
href: url,
text: title
});
Příjemné je, že takto lze kromě HTML atributů definovat i kaskádové
styly a události: