phpFashion

Na navigaci | Klávesové zkratky

SASS, LESS, Stylus or pure CSS? (3)

Journey into the heart of the three most known CSS preprocessors continues, though not in the way I originally planned.

CSS preprocessor is a tool that take code written in their own syntax and generates the CSS for the browser. The most popular preprocessors are SASS, LESS and Stylus. We have talked about installation and syntax + mixins. All three preprocessors have a fundamentally different way of mixins conception.

Each of them have gallery of finished mixins: For SASS there is a comprehensive Compass, the LESS has framework Twitter Bootstrap or small Elements a Stylus NIB.

… this was opening sentences of article I started write year and quarter ago and never finished. I came to the conclusion that all three preprocessors are useless. They required to do so many compromises that potential benefits seemed insignificant. Today I will explain it.

…pokračování


SASS, LESS, Stylus nebo čisté CSS? (3)

Cesta do nitra tří nejznámějších CSS preprocesorů pokračuje, i když ne tak, jak jsem původně plánoval.

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. Ukázali jsme si, jak je nainstalovat a naťukli téma syntaxe a mixinů. Všechny tři preprocesory nabízejí fundamentálně rozdílný způsob, jak programovat s mixiny. Každý je v tom jinak konzistentní a každý umí být jinak matoucí.

Pro každý preprocesor existuje galerie hotových mixinů, do kterých nahlédněte přinejmenším k posouzení jejich srozumitelnosti. Pro SASS existuje komplexní Compass, LESS má framework Twitter Bootstrap nebo drobné Elements a Stylus NIB.

…tak takhle začínal článek, který jsem rozepsal před rokem a čtvrt a nikdy nedokončil. Přišel jsem totiž k závěru, že všechny tři preprocesory jsou, alespoň zatím, nepoužitelné. Jejich nasazení by představovalo tolik ústupků, že by se vedle nich potenciální výhody dočista ztrácely. Dnes to vysvětlím.

…pokračování


Velestručné testování presenterů v Nette

Téma testování presenterů by vydalo na celý seriál, ale ušetříme si čas a místo toho popíšu, jak v několika krocích začít.

Jako testovací framework budu používat Nette Tester. Pochopitelně by šel použít třeba i PHPUnit.

A jako vzorovou aplikaci můžeme vzít třeba Nette Sandbox, protože jej najdete v každé distribuci Nette, nebo si ho můžete stáhnout, včetně frameworku, pomocí Composeru:

composer create-project nette/sandbox myApplication

V něm už máme připravený testovací bootstrap, který vytváří DI kontejner (a vlastně se moc neliší od klasického app/bootstrap.php).

Vyrobíme si tedy instanci presenteru. Buď ručně operátorem new a předáme všechny závislosti, nebo jednodušeji za využití PresenterFactory:

// z DI kontejneru, který vytvořil bootstrap.php, získáme instanci PresenterFactory
$presenterFactory = $container->getByType('Nette\Application\IPresenterFactory');

// a vyrobíme presenter Sign
$presenter = $presenterFactory->createPresenter('Sign');

A bude vhodné vypnout autoCanonicalize, aby presenter nepřesměrovával na kanonické URL:

$presenter->autoCanonicalize = FALSE;

A rovnou můžeme začít testovat, třeba akci Sign:in:

// zobrazení stránky Sign:in metodou GET
$request = new Nette\Application\Request('Sign', 'GET', array('action' => 'in'));
$response = $presenter->run($request);

Presenter je stavěn na jedno voláním run(), pro další requesty vytvoříme vždy nový presenter.

Ověříme, zda odpověď je skutečně šablona:

Assert::true( $response instanceof Nette\Application\Responses\TextResponse );
Assert::true( $response->getSource() instanceof Nette\Templating\ITemplate );

Necháme šablonu vygenerovat HTML kód:

$html = (string) $response->getSource();

A nyní třeba zkontrolujeme, zda se na stránce nacházejí formulářová políčka pro jméno a heslo. Syntax je stejná jako u CSS selektorů.

$dom = Tester\DomQuery::fromHtml($html);

Assert::true( $dom->has('input[name="username"]') );
Assert::true( $dom->has('input[name="password"]') );

Toliko úvodem.


FTP Deployment: nahrávejte přes FTP chytře

Není nic horšího, než uploadovat soubory na FTP ručně, například pomocí Total Commanderu. (Ačkoliv, ještě horší je editovat soubory přímo na serveru a pak se zoufale pokoušet o jakousi synchronizaci.) Jakmile totiž proces nezautomatizujete, stojí vás mnohem víc času a hrozí riziko chyby. Třeba, že některý soubor zapomenete nahrát.

Dnes už se používají sofistikované techniky nasazování aplikací na web, například pomocí Gitu, ale mnoho lidí stále zůstává u nahrávání jednotlivých souborů skrze FTP. Právě pro ně je určen nástroj FTP Deployment, který zautomatizuje a zjednoduší nahrávání aplikací přes FTP.

FTP Deployment je skript napsaný v PHP, který celý proces zautomatizuje. Stačí jen říct, který adresář (local) má kam nahrát (remote). Tyto údaje zapíšete do souboru deployment.ini, jehož odkliknutí můžete rovnou asociovat se spuštěním skriptu, takže deployment se stane věcí jednoho kliknutí:

php deployment.php deployment.ini

A jak vypadá soubor deployment.ini? Povinná je vlastně jen položka remote, všechny ostatní jsou nepovinné:

; remote FTP server
remote = ftp://user:secretpassword@ftp.example.com/directory

; local path (optional)
local = .

; run in test-mode? (can be enabled by option -t or --test too)
test = no

; files and directories to ignore
ignore = "
    .git*
    project.pp[jx]
    /deployment.*
    /log
    temp/*
    !temp/.htaccess
"
; is allowed to delete remote files? (defaults to yes)
allowdelete = yes

; jobs to run before file upload
before[] = http://example.com/deployment.php?before

; jobs to run after file upload
after[] = http://example.com/deployment.php?after

; directories to purge after file upload
purge[] = temp/cache

V testovacím režimu (při spuštění s parametrem -t) k uploadu nebo mazání souborů na FTP nedochází, můžete jej tedy použít k ověření, zda máte všechny hodnoty dobře nastavené.

Položka ignore používá stejný formát jako .gitignore:

  • log – ignoruje všechny soubory či adresáře log, i uvnitř všech podsložek
  • /log – ignoruje soubor či adresář log v kořenovém adresáři
  • app/log – ignoruje soubor či adresář log v podsložce app kořenového adresáře
  • data/* – ignoruje vše uvnitř složky data, ale samotnou služku na FTP vytvoří
  • !data/session – z předchozího pravidla učiní výjimku pro soubor či složku session
  • project.pp[jx] – ignoruje soubory či složky project.ppjproject.ppx

Před započetím uploadu a po jeho skončení můžete nechat zavolat skripty na vašem serveru (viz before a after), které mohou například server přepnout do maintenance režimu, kdy bude odesílat hlavičku 503.

Aby synchronizace i velkého množství souborů proběhla (v rámci možností) transakčně, všechny soubory se nejprve nahrají s příponou .deploytmp a poté, což už je rychlé, přejmenují. Zároveň se na server uloží soubor .htdeployment, kde jsou uloženy md5 otisky všech souborů a právě pomocí něj se nadále web synchronizuje.

Při dalším spuštění tedy nahrává pouze změněné soubory a maže smazané (pokud to nezakážeme direktivou allowdelete).

Nahrávané soubory je možné nechat zpracovat preprocesorem. Ve skriptu deployment.php jsou uvedeny pravidla, že všechny .css soubory se zkomprimují pomocí YUI Compressor a .js pomocí Google Closure Compiler. Tyto nástroje jsou už součástí distribuce, nicméně vyžadují přítomnost Javy. Před samotnou komprimací se ještě expandují základní mod_include direktivy Apache. Můžete tedy vytvořit například soubor combined.js:

<!--#include file="jquery.js" -->
<!--#include file="jquery.fancybox.js" -->
<!--#include file="main.js" -->

Který vám bude Apache na lokálním serveru za běhu sestavovat spojením tří uvedených souborů. Říci si o to můžete takto:

<FilesMatch "combined\.(js|css)$">
    Options +Includes
    SetOutputFilter INCLUDES
</FilesMatch>

Přičemž na server se nahraje už ve spojené a zkomprimované podobě. Vaše HTML stránka tak bude šetřit zdroje a načítat jediný JavaScriptový soubor.

V konfiguračním souboru deployment.ini můžete vytvořit i více sekcí, případně si udělat jeden konfigurák zvlášť pro data a jeden pro aplikaci, aby synchronizace byla co nejrychlejší a nemusel se vždy počítat otisk velkého množství souborů.

Nástroj FTP Deployment jsem si vytvořil před mnoha lety a plně pokrývá mé požadavky na deployovací nástroj. Zároveň je třeba zdůraznit, že FTP protokol tím, že přenáší heslo v čitelné podobě, představuje bezpečnostní riziko a rozhodně byste jej neměli používat třeba na veřejných Wi-Fi.


Objeví Rails Dependency Injection?

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.)

Viz také: Dependency injection není pouze o jednoduším testování

před 2 lety v rubrice Web | shlédnuto 15380×