Rozhodol som sa to skúsiť po anglicky na blog.coh.io.
Rozhodol som sa to skúsiť po anglicky na blog.coh.io.
Ani som nechcel tento článok napísať, lebože iba keď si tým obhyzdným peklom jeden prejde, pochopí že stav alarmujúci jest.
YUIDoc je semä hada britského, čiže potrebujete Python. Okrem toho jačí po 4 daľších knižniciach. Bez command prompt cez nasledovné neprejdete.
PATH si pridajte C:\Python26 a
C:\Python26\Scripts.setuptools je balíček, ktorý zjednodušuje sťahovanie a
inštaláciu ostatných balíčkov pre Python.
easy_install nevie nájsť, skúste nasledové spúšťať
z adresára C:\Python26\Scripts.easy_install Pygmentseasy_install SimpleJSONeasy_install CheetahMne v bode 4. Cheetah zlyhal na tom, že si nevedel nájsť
práve stiahnutý inštalátor pre Markdown:
... Downloading http://sourceforge.net/projects/python-markdown/files/markdown/2.0.3/Markdown-2.0.3.win32.exe/download Processing download error: Couldn't find a setup script in c:\docume~1\martin~1.bal\locals~1\temp\easy_install-ad29ft\download
Pre istotu som teda Markdown stiahol a nainštaloval samostatne. Sám netuším či to má význam.
Stiahnite si YUI Doc.
Niekam ho rozbaľte (ideálne keď cesta nebude obsahovať medzery v názvoch
súborov). Ja mám napríklad adresár D:\work\libraries kde mám
rôzne podobné taľafatky. Z neho mám urobený samostatný disk takto:
subst L: D:\work\libraries
Baťák sa mi spúšťa po štarte, takže L: mám vždy
po ruke.
YUIDoc má v sebe ukážkový spúšťačno-konfigurujúci
L:\yuidoc\bin\example.bat ktorý si je potrebné niekam
skopírovať a upraviť podľa potreby. A pretože YUIDoc vo verzii
1.0.0b1 používa deprecated modul Sets som si urobil
ešte malú úpravu na poslednom riadku, ktorá zakáže veselé
DeprecatedWarning ňuf-ňuf správičky. Tu máte môj
generate.bat:
@ECHO OFF REM ########################################################################## REM The location of your yuidoc install SET yuidoc_home="l:\yuidoc" REM The location of the files to parse. Parses subdirectories, but will fail if REM there are duplicate file names in these directories. You can specify multiple REM source trees: REM SET parser_in="c:\home\www\yahoo.dev\src\js c:\home\www\Event.dev\src" SET parser_in="" REM The location to output the parser data. This output is a file containing a REM json string, and copies of the parsed files. SET parser_out="..\..\temp\doc" REM The directory to put the html file outputted by the generator SET generator_out="..\..\dist\doc" REM The location of the template files. Any subdirectories here will be copied REM verbatim to the destination directory. SET template="%yuidoc_home%\template" REM The project version that will be displayed in the documentation. SET version="1.0.0" REM The version of YUI the project uses. SET yuiversion="2" python -W ignore::DeprecationWarning %yuidoc_home%\bin\yuidoc.py %parser_in% -p %parser_out% -o %generator_out% -t %template% -v %version% -Y %yuiversion%
Popis jednotlivých parametrov je v dodávke celkom jasný. Pre úplnosť uvediem ešte štruktúru môjho projektu:
dev\js Obsahuje moje JS súbory a aj uvedený
generate.bat, ktorý z toho adresára spúšťam (bacha, uvedené
cesty sú relatívne k tomuto adresáru).dist\doc Sem sa mi vyleje dokumentácia v HTML.temp\doc Sem tratí YUIDoc dočasné súbory.Mľaskavo vychutnávajte.
Od víkendu sa snažím si zazálohovať. Môj problém je, že súčasťou záloh je rákoš malých súborov (práca) a stádko veledôležitých veľkých súborov (2GB+).
Pochopiteľne som väčšinu toho vypálil na DVD, ale keďže mi tie staršie akosi zahnedli, predstavujú pravdepodobnosť straty iba o blšku nižšiu. Externé hard-disky sú tiež kazové, drahé a nepraktické. I siahol som po online riešení.
Prvé 4 dni som sa pokúšal pretlačiť dáta na Amazon S3 – zdanlivo výhodná cena a dostupnosť nástrojov. Veľké súbory sa mi však pretlačiť nepodarilo a pokusy o nový upload po každom zakolísaní spojenia človeka od nervov obkrúti o nohu stola. Skončilo to zmazaním bucketu a hľadadím alternatívy.
Priznám sa, že Storage od Brata nechcem.
Vyískal som mozy.com, neradno však písať predčasné závery. Veľké súbory to tiež moc nepožralo, ale aspoň tie 100MB rozrezky to, zdá sa, chrumká. Až to dopadne, napíšem.
A kam zálohujete vy?
Veľa zmätku nastalo okolo JavaScriptu. Jeho neobyčajné vlastnosti veľa nás radšej ignoruje, akoby mali pariť uzlinami. Našli sme si miesto toho zázračné formulky, ktoré nám pomáhajú priepasť prekonávať. Navyše, množstvo zdrojov uvádza nekompletné, či dokonca nesprávne postupy. Špecifikácia nie je veľkým pomocníkom a k tomu všetkému sú tu rozdiely medzi implementáciami.
Všetci vieme:
prototype.a strácame sa:
prototype v dedičnosti?Prototypová dedičnost, byť termín nesprávny, vhodný sťa metafora jest. (Používať budem i termíny ako napríklad „trieda“, ktoré nie sú tak celkom správne v kontexte JavaScriptu).
Na spodku zázrakov JavaScriptu je dedičnosť medzi objektami (nemýliť s triedami). Na to aby sme pochopili ako funguje dedenie tried, je dobré vedieť ako môže jeden objekt dediť od druhého. Príklad:
var fruit = { taste: "sweet" };
var apple = { color: "red" };
// Čil zázrak snaň sa!
alert( apple.taste ); // => "sweet"
Zázrak, ktorý sa musí stať je prepojenie objektov apple a
fruit. Čiže ak sa spýtam na apple.taste, JavaScript
musí vedieť, že ak taste nie je definovaná pre
apple tak sa musí pozrieť na fruit.
Existuje vlastnosť každého objektu v JavaScripte, o ktorej nám
súčasné implementácie viac-menej mlčia. Všetci sme už počuli
o vlastnosti prototype, málo je však počut o
__proto__. Je to preto, že __proto__ je
v špecifikácii ECMAScript
262 označená ako interná (skrytá) a nemali by sme teda na ňu ako
programátori šahať. (Špecifikácia hovorí o tejto vlastnosti ako o
[[Prototype]]).
I napriek tomu dve JavaScriptové implementácie (SpiderMonkey od Mozilly a V8 od Google) ju sprístupňujú a
pomenúvajú __proto__ (budem teda používať toto pomenovanie).
Rád by som tu zdôraznil, že ide o neštandardnú internú
vlastnosť, ale nám veľmi dobre poslúži na pochopenie toho čo sa
v JavaScripte deje.
Oný zázrak je teda v tom, že apple.__proto__ nastavíme na
fruit:
var fruit = { taste: "sweet" };
var apple = { color: "red" };
apple.__proto__ = fruit;
alert( apple.taste ); // => "sweet"

Objekt apple dedí od fruit, vďaka vlastnosti
__proto__. Ak sa spýtame na hodnotu vlastnosti taste,
JavaScript sa ju pokúsi nájsť na objekte apple. Ak ju objekt
apple nemá, mrkne sa do objektu apple.__proto__, ak
ani tam nič, tak apple.__proto__.__proto__atď. V našom prípade
sa zastaví na apple.__proto__, pretože ukazuje na objekt
fruit a ten vlastnosť taste má.
Ak chcem objektu apple zmeniť chuť na "sour",
nič mi nebráni. Objekt fruit bude stále sladký:
apple.taste = "sour";
alert( apple.taste ); // => "sour"
alert( fruit.taste ); // => "sweet"

V tomto momente apple dostáva vlastný taste a
JavaScript už nemusí snoriť po __proto__ aby ju našiel.
Je dôležité si uvedomiť, že s __proto__ sa za normálnych
okolností nestretneme. Budeme dokonca nútení ho nepoužívať, pretože nie
je definovaný vo všetkých implementáciách. V tejto sérii slúži hlavne
na vysvetlenie vnútorných pochodov v JavaScripte.
Ďalšia časť:
Tento článok je pokračovaním série o dedičnosti v JavaScripte:
Predsa však, bežnejšia, dostupnejšia a bezpečnejšia forma dedičnosti ako tá čisto objektová je pomocou konštruktorov. Kým sa dostanem k samotnej dedičnosti, nutno jest pochopiť konštruktory.
Konštruktor je objekt triedy Function, teda funkcia:
function Fruit( color )
{
this.color = color;
}
var apple = new Fruit( "red" );
alert(apple.color); // => "red"
Tým sme vytvorili objekt apple, inštanciu Fruit,
ktorý má vlastnosť color nastavenú na red. Za
operátorom new sedí kus kódu, ktorý okrem iného zavolá
funkciu Fruit tak, že jej nastaví this na
apple. Asi takto:
// Vytvorí obyčajný objekt
var apple = new Object();
// Zavolá nad ním metódu Fruit
Fruit.call( apple, "red" );
alert( apple.color ); // => "red"
Pomocou konštruktora teda pripravíme objekt tým, že mu nastavíme základné vlastnosti. (Špecifikácia ECMAScript 262 tento proces popisuje detailne v sekcii 13.2.2.)
Pridajme metódu yellColor, ktorá zareve farbu. Prvé čo nás
napadne je:
function Fruit( color )
{
this.color = color;
this.yellColor = function() // Toto je fakt že zle
{
alert( this.color );
};
}
var apple = new Fruit( "red" );
apple.yellColor(); // => "red"
Kód nám síce funguje, nie že nie. Predstavme si ale, že chceme vytvoriť celé vrece červeného ovocia. Povedzme takých 100ks:
var bag = [];
for( var i = 0; i < 100; i++ )
{
bag.push( new Fruit( "red" ) );
}
Pre každú jednu inštanciu vytvoríme jeho vlastnú metódu, čím si úspešne zahrávame s časom a nárokmi na pamäť. Skúsme si rozpísať čo sa vlastne deje:
var bag = [];
for( var i = 0; i < 100; i++ )
{
var apple = new Object();
apple.color = "red";
apple.yellColor = function()
{
alert( this.color );
}
bag.push( apple );
}
Vytvárať kvôli 100ks ovocia 100ks metód je celkom luxus, ktorý si
môžeme dovoliť iba málokedy. A nie je to len o rýchlosti a pamäti. Ak by
sme takýto kód nechali v našej knižnici a pre jeden z desiatich projektov
by sme potrebovali vypisovať farbu ovocia napríklad do konzoly
(console.log), neostáva nám iba vytvoriť novú vetvu kódu
s iným konštruktorom.

Riešením je definovať metódy na prototype
konštruktora (v ďalšej časti).
Ďalšie časti:
Tento článok je pokračovaním série o dedičnosti v JavaScripte:
Oveľa lepšie, ako pchať
metódy priamo do konštruktora, je na to využiť jeho vlastnosť
prototype. Robí sa to takto:
function Fruit( color )
{
this.color = color;
}
Fruit.prototype.yellColor = function()
{
alert( this.color );
};
var apple = new Fruit( "red" );
apple.yellColor(); // => "red"

Každá funkcia má vlastnosť prototype. Pri inštancovaní
operátorom new JavaScript nastaví novému objektu jeho spomínanú
vlastnosť __proto__ na prototype konštruktora.
Čiže na pozadí volania var apple = new Fruit( "red" ); sa udeje
niečo takéto:
// Vytvorí obycajný objekt
var apple = new Object();
// Nastaví __proto__ na prototyp konštruktora
apple.__proto__ = Fruit.prototype;
// Zavolá nad apple metódu Fruit
Fruit.call(apple, "red");
apple.yellColor(); // => "red"

Metóda yellColor() nie je nastavená priamo inštancii, a preto
sa JavaScript pozrie na jeho __proto__, ktorý ukazuje na
Fruit.prototype a tam ju bezpečne nájde a vykoná.
V príklade z minulej
časti sme ukazovali na problém 100ks ovocia. Ak to skúsime teraz,
uvidíme, že funkcia yellColor() je iba jedna, pre všetky
inštancie spoločná:

Ak by sme chceli prinútiť všetky inštancie vypisovať svoje farby do konzoly, stačí zmeniť prototyp konštruktora, trebárs aj v inej knižnici, či v projektovo-závislom kóde:
Fruit.prototype.yellColor = function()
{
console.log(this.color);
}
apple.yellColor(); // => "red" ale tentokrát už do konzoly
V ďalšej časti o tom ako funguje dedičnosť medzi triedami.
Ďalšie časti:
Tento článok je pokračovaním série o dedičnosti v JavaScripte:
Ako vyrobiť triedu Apple, ktorá bude dediť od
Fruit? Vieme ako bude vyzerať Fruit a zhruba ako
Apple:
function Fruit( color )
{
this.color = color;
}
Fruit.prototype.yellColor = function()
{
alert( this.color );
}
function Apple( color )
{
// Zavoláme konštruktor Fruit, aby inštancii nastavil farbu
Fruit.call( this, color );
}
var apple = new Apple("red");
apple.yellColor();
// Uncaught TypeError:
// Object #<an Apple> has no method 'yellColor'
A teraz ako to spojiť? Chceme aby objekt vytvorený triedou
Apple videl na metódu Fruit.prototype.yellColor().
Budeme navzájom prepájať prototype vlastnosti. Niekoho môže
napadnúť toto:
Apple.prototype = Fruit.prototype;
Lenže v tom prípade by sme prídávaním, či úpravou
Apple.prototype priamo prepisovali aj Fruit.prototype
a to nechceme. V prvej časti sme použili __proto__, to môžeme
aj teraz:
// Čokoľvek definované vo Fruit.prototype bude
// viditeľné i v Apple.prototype
Apple.prototype.__proto__ = Fruit.prototype;
Ale ako bolo písané v prvej časti tiež: __proto__
nie je štandardnou súčasťou implementácii. Vieme ho ale simulovať
tak, že medzi Apple a Fruit strčíme ešte jeden
pomocný konštruktor, jeho prototyp a inštanciu:
// Vytvoríme pomocný primitívny konštruktor `Q`
function Q() {};
// Jeho prototyp nastavíme na prototyp rodičovskej triedy `Fruit`
Q.prototype = Fruit.prototype;
// A prototyp detskej triedy nastavíme na novú inštanciu triedy `Q`
Apple.prototype = new Q;
// Opravíme referenciu na konštruktor (inak by ukazovala na Q)
Apple.prototype.constructor = Apple;

Diagram síce vyzerá hnusoprskne, ale v skutočnosti je tých
krokov podstatne menej. K Fruit.prototype sa totiž JavaScript
dostane už v druhom kroku: (new Q).__proto__, pretože (new
Q).__proto__ == Q.prototype == Fruit.prototype.
Keď rozpíšeme riadok Apple.prototype = new Q; podľa toho čo
už o operátore new vieme, zistíme, že JavaScript nám
vlastnosť __proto__ nastaví tak ako by sme radi:
function Q() {};
Q.prototype = Fruit.prototype;
Apple.prototype = new Object();
Apple.prototype.__proto__ = Q.prototype;
Treba si dávať pozor iba na to, že tento kód nám prepíše celý
Apple.prototype a preto je dôležité ho volať predtým ako
začneme do neho nastavovať metódy. Celokus, kde Apple dedí od
Fruit bez použitia __proto__ vypadá takto:
function Fruit( color )
{
this.color = color;
}
Fruit.prototype.yellColor = function()
{
alert( this.color );
}
function Apple( color )
{
// Zavoláme konštruktor Fruit, aby inštancii nastavil farbu
Fruit.call(this, color);
}
// Vytvoríme pomocný primitívny konštruktor `Q`
function Q() {};
// Jeho prototyp nastavíme na prototyp rodičovskej triedy `Fruit`
Q.prototype = Fruit.prototype;
// A prototyp detskej triedy nastavíme na novú inštanciu triedy `Q`
Apple.prototype = new Q;
// Opravíme referenciu na konštruktor (inak by ukazovala na Q)
Apple.prototype.constructor = Apple;
Apple.prototype.fallOnNewtonsHead = function()
//...
Pochopiteľne je možné si napísať jednoduchú funkciu, ktorá dedenie urobí krajšie. Takto:
function extends( Child, Parent )
{
function Q() {};
Q.prototype = Parent.prototype;
Child.prototype = new Q;
Child.prototype.constructor = Child;
}
extends( Apple, Fruit );
var apple = new Apple("red");
apple.yellColor(); // => "red"
Príklad funguje i keď inštancia apple nemá zadefinovanú
metódu yellColor(). Tá je definovaná až na triede
Fruit. JavaScript si ju však veselo nájde postupným
prechádzaním __proto__:
// JS sa najprv pozrie na objekt apple
apple
// Ak tam nie je, tak na jeho __proto__, ktoré ukazuje na Apple.prototype a to na q.prototype
apple.__proto__ = Apple.prototype
// Ak tam nie je, tak na jeho __proto__, ktoré ukazuje na Q.prototype a to zase na Fruit.prototype
apple.__proto__.__proto__ = Apple.prototype.__proto__ = Q.prototype = Fruit.prototype
Celé to však nie je iba o tom, aby JavaScript vedel danú vlastnosť
nájsť. Ide aj o možnosť zistiť o inštanciu akej triedy ide. Na to
slúži operátor instanceof. Objekt apple teda bude
inštanciou ako Apple, tak Fruit:
// Je apple inštanciou Apple?
alert( apple instanceof Apple ); // => true
// Je apple inštanciou Fruit?
alert( apple instanceof Fruit ); // => true
V súčasnosti už existuje množstvo článkov o tom, ako si zjednodušiť dedenie, či používať kopu ďalších pekných a užítočných postupov známych z iných jazykov. Úlohou tohto článku bolo odkryť čo sa deje pod kapotou. Ostatné je už na vašej chuti.
Doporučujem sa mrknúť na nedávnu trojsériu od Daniela Steigerwalda na Zdrojáku.
Predchádzajúce časti:
„When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name.“ – Ahá, tak už rozumiem.
Jednou z vecí na ktorú som sa tešil pri implementácii Intoru Forms je práve ElasticContainer (asi dočasný názov). V Nette totiž nie je možné urobiť kontajner s dynamickým počtom polí (ochrchlať sa to dá, ale je to škaradé).
Prvým príkladom budiž prílohy. Ak chcem dať užívateľovi možnosť pridať niekoľko príloh do formuláru, neviem vopred počet políčok, ten sa nastaví až u klienta. ElasticContainer to rieši tak, že svoje detské políčka nastaví až pri prijatí hodnôt. Každé políčko je klon nejakého vopred definovaného poľa – tzv. prototypu.
Ak chcem prílohy, urobím:
$form->add( 'files', new ElasticContainer( new File ) );
Ak chcem tagy, urobím:
$form->add( 'tags', new ElasticContainer( new String ) );
Ak chcem na prototype validácie, urobím validácie na prototype:
$form->add( 'emails', new ElasticContainer( new String ) )
->getPrototype()
->validates('email');
Validátorom count nad ElasticContainer môžem
obmedziť počet políčok:
$form->add( 'emails', new ElasticContainer( new String ) )
->validates( 'count', 3, 10 )
->getPrototype()
->validates('email');
Hodnoty získam a nastavím volaním get/setValue:
$c = $form->add( 'tags', new ElasticContainer( new String ) );
//...
$c->setValue( array('apple', '', 'juice', NULL, 'banana') );
//...
$c->getValue(); // => array('apple', 'juice', 'banana')
Šablona je potom jednoduchá:
{foreach $container as $field}
{form:text $field}
{/foreach}
Vypíše všetky nastavené políčka, minimálne a maximálne však toľko
koľko sa nastavilo cez ElasticContainer::setMinimum/setMaximum
alebo cez $container->validates( 'count', $minimum, $maximum
).
Centi dnes dostal nápad na vylepšenie
nášho FormHelper na vykreslovanie Intoru formulárov
v Nette. A myslím, že to bol dobrý nápad. Povážte: doteraz sme boli
nútení opakovane vymenovávať pre ktoré políčko ideme vykreslovať
popisok, chybky a samotný widget:
{form $form}
{form:label $form['document']['title']}Titulok{/form:label}
{form:error $form['document']['title']}
{form:text $form['document']['title'], 'width' => '100%'}
{/form}
Po novom sa veci značne zjednodušujú, vďaka párovému Latte helperu
{form:with ...} a pár ďalším novinkám:
{form $form}
{form:label 'document/title' }
{form:error 'document/title' }
{form:text 'document/title', 'width' => '100%'}
{* --- alebo --- *}
{form:with 'document/title'}
{form:label}
{form:error}
{form:text 'width' => '100%'}
{/form:with}
{* --- alebo --- *}
{form:with 'document'}
{form:label 'title'}
{form:error 'title'}
{form:text 'title', 'width' => '100%'}
{/form:with}
{* --- alebo --- *}
{form:with 'document'}
{form:with 'title'}
{form:label}
{form:error}
{form:text 'width' => '100%'}
{/form:with}
{/form:with}
{/form}
Všetky varianty sa dajú miešať jedna cez druhú a použiť to čo príde lepšie. Tu je ukážka kúsku z editora užívateľa:
{form:with 'email'}
<tr>
<th class="w-min">{form:label}E-mail:{/form:label}</th>
<td>
{form:error 'class' => 'mb-s w-text'}
{form:text 'width' => '30em'}
</td>
</tr>
{/form:with}
<tr>
<th class="w-min va-t">{form:label 'password'}Password{/form:label}</th>
<td>
<div class="mb-s">
{form:error 'password'}
{form:password 'password', 'width' => '15em', 'autocomplete' => 'off'}
</div>
<div>
{form:error 'password_confirmation'}
{form:password 'password_confirmation', 'width' => '15em', 'autocomplete' => 'off'}
</div>
</td>
</tr>