ruby kompendium

76
E N C Y K L O P E D I E Z O N E R P R E S S RUBY RUBY Hal Fulton Hal Fulton kompendium znalostí pro začátečníky i profesionály kompendium znalostí pro začátečníky i profesionály © Foto: Jiří Heller

Upload: zoner-software-as

Post on 27-Mar-2016

266 views

Category:

Documents


8 download

DESCRIPTION

Ruby kompendium

TRANSCRIPT

Page 1: Ruby kompendium

E N C Y K L O P E D I E Z O N E R P R E S S

RUBYRUBY

H a l F u l t o nH a l F u l t o n

kompendium znalostí pro začátečníky i profesionálykompendium znalostí pro začátečníky i profesionály

© Foto: Jiří Heller

Page 2: Ruby kompendium

„Tato kniha, mimo jiné, skvěle vysvětluje metaprogramování, jeden z nejzajímavějších aspektů Ruby. Prvním vydáním této knihy bylo inspirováno mnoho raných myšlenek k Rails. Díky této knize se do-stáváte na horskou dráhu mezi „Jak to můžu použít“ a „To je tak skvělé!“. Jakmile na tuto dráhu jed-nou nastoupíte, není už žádné cesty zpět.“

David Heinemeier Hansson,Tvůrce Ruby on Rails,Společník 37signals

„Druhé vydání této knihy je vzrušující událostí pro všechny programátory v Ruby – a pro milovníky skvělé technické literatury obecně. Hal Fulton přináší poutavý a jasný styl s důrazem na dokonalé a úzkostlivě přesné vysvětlení Ruby. Zřetelně cítíte přítomnost učitele, který má obrovské množství znalostí, a jenž vám chce opravdu pomoci, abyste je měli také.“

David Alan Black, Autor Ruby for Rails

„Je to vynikající zdroj informací o tom, jak Ruby funguje. I já, jako člověk, který s Ruby pracuje po několik let, jsem zde našel plno nových triků a technik. Tuto knihu je možné nejenom číst od začátku do konce, ale také ji používat jako referenční příručku, ke které můžete kdykoliv sáhnout a naučit se něco nového.“

Chet Hendrickson, Průkopník agilního softwaru

„Často používám první vydání této knihy o Ruby, abych zjistil podrobnosti o tomto programovacím jazyku, protože pokrývá mnoho témat, která nemohu najít nikde jinde. Nové vydání je ještě komplex-nější a bude ještě hodnotnější.“

Ron Jeffries, Autor a řečník

„Ruby je báječný jazyk, ale někdy prostě potřebujete jen něco udělat. Halova kniha vám poskytne řešení a poučí vás, proč je tohle řešení správné Ruby.“

Martin Fowler, Chief Scientist, ThoughtWorks,Autor „Patterns of Enterprise Application Architecture“

Page 3: Ruby kompendium
Page 4: Ruby kompendium

Ruby kompendium znalostí pro začátečníky i profesionály

Hal Fulton

Page 5: Ruby kompendium

THE RUBY WAY, SECOND EDITIONHal Fulton

Authorized translation from English language edition, entitled RUBY WAY, SECOND EDITION, THE: SOLUTION AND TECHNIQUES IN RUBY PROGRAMMING, 2nd Edition, 0672328844 by FULTON, HAL, published by Pearson Education, Inc, publishing as Addison Wesley Professional, Copyright © 2007. All rights reserved. No part of this book may be repro-duced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, INC. CZECH language edition by ZONER SOFTWARE S.R.O., Copyright © 2009.

Autorizovaný překlad anglického vydání nazvaného RUBY WAY, SECOND EDITION, THE: SOLUTION AND TECHNI-QUES IN RUBY PROGRAMMING, druhé vydání, 0672328844, autor FULTON, HAL, vydal Pearson Education, Inc, ve vydavatelství Addison Wesley Professional, Copyright © 2007. Všechna práva vyhrazena. Žádná část této publikace nesmí být reprodukována nebo předávána žádnou formou nebo způsobem, elektronicky ani mechanicky, včetně fotokopií, natáčení ani žádnými jinými systémy pro ukládání bez výslovného svolení Pearson Education, INC. České vydání ZONER SOFTWARE S.R.O., Copyright © 2009.

Ruby – kompendium znalostí pro začátečníky i profesionályAutor: Hal Fulton

Copyright © ZONER software, s.r.o. Vydání první v roce 2009. Všechna práva vyhrazena.

Zoner PressKatalogové číslo: ZR724

ZONER software, s.r.o.Nové sady 18, 602 00 Brno

Překlad: Jiří KoutnýOdpovědný redaktor: Miroslav KučeraOdborná korektura: Miroslav Kučera a Dalibor ŠrámekŠéfredaktor: Ing. Pavel KristiánDTP: Miroslav Kučera

Zdrojové soubory ke knize: http://www.zonerpress.cz/download/ruby-kompendium.zip

Informace, které jsou v této knize zveřejněny, mohou byt chráněny jako patent. Jména produktů byla uvedena bez záruky jejich volného použití. Při tvorbě textů a vyobrazení bylo sice postupováno s maximální péčí, ale přesto nelze zcela vyloučit možnost výskytu chyb. Vydavatelé a autoři nepřebírají právní odpovědnost ani žádnou jinou záruku za použití chybných údajů a z toho vyplývajících důsledků. Všechna práva vyhrazena. Žádná část této publikace nesmí být reprodukována ani dis-tribuována žádným způsobem ani prostředkem, ani reprodukována v databázi či na jiném záznamovém prostředku či v jiném systému bez výslovného svolení vydavatele, s výjimkou zveřejnění krátkých částí textu pro potřeby recenzí.

Veškeré dotazy týkající se distribuce směřujte na:

Zoner Press ZONER software, s.r.o. Nové sady 18, 602 00 Brno

tel.: 532 190 883, fax: 543 257 245 e-mail: [email protected] http://www.zonerpress.cz

ISBN 978-80-7413-018-2

Page 6: Ruby kompendium

Mým rodičům, bez kterých bych tu nebyl

Page 7: Ruby kompendium

6

Obsah Předmluva 23

Předmluva k druhému vydání 23

Předmluva k prvnímu vydání 23

Poděkování 25

Poděkování pro druhé vydání 25

Poděkování pro první vydání 26

O autorovi 27

Úvod 28

O druhém vydání 28

Jak pracovat s touto knihou 31

Zdrojové kódy ke stažení 32

Poznámka redakce k českému vydání 33

Sdělte nám svůj názor 33

Co je "cesta Ruby"? 34

Kapitola 1 Ruby ve zkratce 39

1.1 – Úvod do objektové orientace 40

1.1.1 – Co je objekt? 40

1.1.2 – Dědičnost 41

1.1.3 – Polymorfismus 43

1.1.4 – Ještě několik pojmů 44

1.2 – Základní syntaxe a sémantika Ruby 45

1.2.1 – Klíčová slova a identifikátory 46

1.2.2 – Komentáře a vestavěná dokumentace 46

1.2.3 – Konstanty, proměnné a typy 47

1.2.4 – Operátory a priorita 49

1.2.5 – Ukázkový program 50

1.2.6 – Cykly a větvení 53

1.2.7 – Výjimky 57

1.3 – OOP v Ruby 59

1.3.1 – Objekty 60

1.3.2 – Zabudované třídy 60

1.3.3 – Moduly a mixiny 62

1.3.4 – Vytváření tříd 62

Page 8: Ruby kompendium

7

1.3.5 – Metody a atributy 67

1.4 – Dynamické aspekty Ruby 68

1.4.1 – Programování v době běhu programu 69

1.4.2 – Reflexe 70

1.4.3 – Chybějící metody 72

1.4.4 – Svoz odpadků (garbage collection) 72

1.5 – Trénink intuice: co si zapamatovat 73

1.5.1 – Záležitosti syntaxe 73

1.5.2 – Perspektiva při programování 75

1.5.3 – Příkaz case v Ruby 78

1.5.4 – Rubyismy a idiomy 81

1.5.5 – Výrazy a další rozmanité záležitosti 86

1.6 – Hantýrka Ruby a slang 88

1.7 – Závěr 91

Kapitola 2 Práce s řetězci 93

2.1 – Reprezentace běžných řetězců 94

2.2 – Reprezentace řetězců s alternativní notací 94

2.3 – Víceřádkové řetězce (here-documents) 95

2.4 – Zjištění délky řetězce 97

2.5 – Zpracování řetězce po řádcích 97

2.6 – Zpracování řetězce po bajtech 97

2.7 – Provádění specializovaného porovnávání řetězců 98

2.8 – Rozdělení řetězce na znaky 100

2.9 – Formátování řetězce 101

2.10 – Řetězce jako IO objekty 102

2.11 – Malá a velká písmena 102

2.12 – Zpřístupňování a přiřazování podřetězců 103

2.13 – Nahrazování v řetězcích 105

2.14 – Prohledávání řetězce 106

2.15 – Konverze mezi znaky a ASCII kódy 107

2.16 – Implicitní a explicitní konverze 108

2.17 – Připojení položky do řetězce 110

2.18 – Odstranění přebytečných znaků pro nový řádek a dalších znaků 110

2.19 – Odstranění prázdných znaků z řetězce 111

2.20 – Opakování řetězců 112

Page 9: Ruby kompendium

8

2.21 – Vkládání výrazů do řetězců 112

2.22 – Odložené vyhodnocení při vkládání výrazu 113

2.23 – Analýza dat oddělených čárkou 113

2.24 – Konverze řetězců na čísla (dekadická a jiná) 114

2.25 – Kódování a dekódování textu rot13 116

2.26 – Šifrování řetězců 117

2.27 – Komprimace řetězců 117

2.28 – Počítání znaků v řetězci 118

2.29 – Obrácení řetězce 119

2.30 – Odstraňování duplicitních znaků 119

2.31 – Odstraňování znaků 119

2.32 – Tisk speciálních znaků 120

2.33 – Generování po sobě jdoucích řetězců 120

2.34 – Výpočet 32bitového CRC 121

2.35 – Výpočet haše řetězce pomocí MD5 121

2.36 – Výpočet Levenshteinovy vzdálenosti mezi dvěma řetězci 122

2.37 – Kódování a dekódování řetězců base64 124

2.38 – Kódování a dekódování řetězce (uuencode/uudecode) 125

2.39 – Konverze znaků Tab na mezery a naopak 125

2.40 – Zalomení řádků textu 126

2.41 – Závěr 127

Kapitola 3 Práce s regulárními výrazy 129

3.1 – Syntaxe regulárních výrazů 129

3.2 – Kompilování regulárních výrazů 131

3.3 – Ošetření speciálních znaků 132

3.4 – Používání kotev (anchors) 133

3.5 – Používání kvantifikátorů 134

3.6 – Pozitivní a negativní vyhlížení 136

3.7 – Přístup ke zpětným referencím 137

3.8 – Používání tříd znaků 140

3.9 – Rozšířené regulární výrazy 141

3.10 – Zachytávání znaku pro nový řádek pomocí tečky 142

3.11 – Použití modifikátoru na část regulárního výrazu 143

3.12 – Použití vložených podvýrazů 143

3.13 – Ruby a Oniguruma 144

Page 10: Ruby kompendium

9

3.13.1 – Testování přítomnosti Onigurumy 144

3.13.2 – Přidání Onigurumy 145

3.13.3 – Několik nových funkcionalit enginu Oniguruma 146

3.13.4 – Pozitivní a negativní zpětné vyhlížení 146

3.13.5 – Více o kvantifikátorech 147

3.13.6 – Pojmenované výsledky porovnání 148

3.13.7 – Rekurze v regulárních výrazech 149

3.14 – Několik ukázkových regulárních výrazů 150

3.14.1 – Porovnání IP adresy 150

3.14.2 – Porovnání páru atribut=hodnota 151

3.14.3 – Porovnání římských číslic 152

3.14.4 – Porovnání číselných konstant 153

3.14.5 – Porovnání řetězce datum/čas 153

3.14.6 – Detekce duplicitních slov v textu 154

3.14.7 – Porovnání slov psaných velkými písmeny 154

3.14.8 – Porovnání čísel verzí 155

3.14.9 – Několik dalších vzorů 155

3.15 – Závěr 156

Kapitola 4 Internacionalizace v Ruby 157

4.1– Pozadí a terminologie 158

4.2 – Kódování ve světě po éře ASCII 161

4.2.1 – Knihovna jcode a $KCODE 162

4.2.2 – Běžné operace s řetězci a regulárními výrazy 163

4.2.3 – Detekce kódování znaků 167

4.2.4 – Normalizace řetězců Unicode 167

4.2.5 – Problémy s řazením řetězců 169

4.2.6 – Konverze mezi kódováními 172

4.3 – Používání katalogů zpráv 174

4.3.1 – Pozadí a terminologie 175

4.3.2 – Začínáme s katalogy zpráv 175

4.3.3 – Lokalizace jednoduché aplikace 176

4.3.4 – Další poznámky 180

4.4 – Závěr 181

Page 11: Ruby kompendium

10

Kapitola 5 Vykonávání číselných výpočtů 183

5.1 – Reprezentace čísel v Ruby 183

5.2 – Základní operace s čísly 184

5.3 – Zaokrouhlování hodnot s pohyblivou řádovou čárkou 185

5.4 – Porovnání čísel s pohyblivou řádovou čárkou 187

5.5 – Formátování hodnot pro výstup 189

5.6 – Formátování čísel s čárkou 189

5.7 – Práce s velmi velkými celými čísly 190

5.8 – Použití BigDecimal 190

5.9 – Práce s racionálními čísly 192

5.10 – Práce s maticemi 193

5.11 – Práce s komplexními čísly 197

5.12 – Používání knihovny mathn 198

5.13 – Hledání faktorizace, GCD a LCM 199

5.14 – Práce s prvočísly 200

5.15 – Implicitní a explicitní číselné konverze 201

5.16 – Vynucení číselné hodnoty 202

5.17 – Provádění bitových operací na číslech 203

5.18 – Provádění konverze základů 205

5.19 – Hledání odmocnin 205

5.20 – Určení pořadí bajtů architektury 206

5.21 – Numerický výpočet určitého integrálu 207

5.22 – Trigonometrie ve stupních, radiánech a gradiánech 208

5.23 – Pokročilejší trigonometrie 209

5.24 – Výpočet logaritmu s libovolným základem 209

5.25 – Výpočet průměru, mediánu, a modusu ze souboru dat 210

5.26 – Rozptyl a směrodatná odchylka 211

5.27 – Výpočet korelačního koeficientu 212

5.28 – Generování náhodných čísel 213

5.29 – Knihovna memoize pro cachování 214

5.30 – Závěr 215

Kapitola 6 Symboly a rozsahy 217

6.1 – Symboly 217

6.1.1 – Symboly jako výčty 219

6.1.2 – Symboly jako metahodnoty 219

Page 12: Ruby kompendium

11

6.1.3 – Symboly, proměnné a metody 220

6.1.4 – Konverze na/z symboly 221

6.2 – Rozsahy 222

6.2.1 – Otevřené a uzavřené rozsahy 222

6.2.2 – Hledání koncových bodů 223

6.2.3 – Iterace přes rozsah 223

6.2.4 – Testování příslušnosti k rozsahu 224

6.2.5 – Konverze na pole 224

6.2.6 – Opačné rozsahy 225

6.2.7 – Flip-flop operátor 225

6.2.8 – Uživatelské rozsahy 228

6.3 – Závěr 231

Kapitola 7 Práce s datem a časem 233

7.1 – Určení aktuálního času 234

7.2 – Práce se specifickými časy 234

7.3 – Určení dne v týdnu 235

7.4 – Určení data Velikonoc 236

7.5 – Hledání n-tého dne v měsíci 237

7.6 – Konverze mezi sekundami a většími jednotkami 238

7.7 – Konverze na a z epochy 238

7.8 – Práce s přestupnými sekundami? Raději ne! 239

7.9 – Nalezení čísla dne v roce 239

7.10 – Ověřování data a času 240

7.11 – Hledání týdne v roce 240

7.12 – Detekce přestupných roků 241

7.13 – Získávání časových pásem 242

7.14 – Práce s hodinami a minutami 242

7.15 – Porovnávání datových a časových hodnot 243

7.16 – Přidávání intervalů k datovým a časovým hodnotám 243

7.17 – Výpočet rozdílu dvou datových/časových hodnot 244

7.18 – Práce se specifickými daty 244

7.19 – Konverze mezi třídami Time, Date a DateTime 245

7.20 – Získávání hodnot data a času z řetězce 246

7.21 – Formátování a tisk hodnot data a času 247

7.22 – Konverze časových pásem 248

Page 13: Ruby kompendium

12

7.23 – Určení počtu dnů v měsíci 248

7.24 – Rozdělení měsíce na jednotlivé týdny 249

7.25 – Závěr 250

Kapitola 8 Pole, haš a ostatní výčty 251

8.1 – Práce s poli 251

8.1.1 – Vytvoření a inicializace pole 252

8.1.2 – Zpřístupnění a přiřazení prvků pole 252

8.1.3 – Nalezení velikosti pole 254

8.1.4 – Porovnávání polí 254

8.1.5 – Řazení polí 256

8.1.6 – Výběr z pole na základě kritéria 259

8.1.7 – Použití specializovaných indexovacích funkcí 260

8.1.8 – Implementace řídkých matic 262

8.1.9 – Pole jako matematické množiny 263

8.1.10 – Náhodné uspořádání prvků pole 266

8.1.11 – Používání vícerozměrných polí 267

8.1.12 – Hledání prvků pole, které nejsou prvky jiného 268

8.1.13 – Transformace nebo mapování polí 269

8.1.14 – Odstranění hodnot nil z pole 269

8.1.15 – Odstranění specifických prvků z pole 269

8.1.16 – Zřetězení a připojení do polí 271

8.1.17 – Použití pole jako zásobníku nebo fronty 272

8.1.18 – Iterace skrz pole 272

8.1.19 – Oddělovače prvků pole při tvorbě řetězce 273

8.1.20 – Převrácení pole 273

8.1.21 – Odstranění duplicitních prvků z pole 274

8.1.22 – Prokládání polí 274

8.1.23 – Zjišťování počtu výskytů hodnot v poli 274

8.1.24 – Převrácení pole do podoby haše 275

8.1.25 – Synchronizované řazení několika polí 275

8.1.26 – Nastavení výchozí hodnoty pro nové prvky pole 276

8.2 – Práce s hašem 277

8.2.1 – Vytvoření nového haše 277

8.2.2 – Nastavení výchozí hodnoty pro haš 278

8.2.3 – Zpřístupnění a přidávání dvojic klíč-hodnota 279

Page 14: Ruby kompendium

13

8.2.4 – Odstranění dvojic klíč-hodnota 280

8.2.5 – Iterace skrz haš 281

8.2.6 – Invertování haše 281

8.2.7 – Detekce klíčů a hodnot v haši 281

8.2.8 – Extrahování haše do polí 282

8.2.9 – Výběr dvojice klíč-hodnota na základě kritéria 282

8.2.10 – Řazení haše 283

8.2.11 – Slučování dvou hašů 283

8.2.12 – Vytvoření haše z pole 284

8.2.13 – Hledání rozdílu nebo průniku klíčů haše 284

8.2.14 – Použití haše jako řídké matice 284

8.2.15 – Implementace haše s duplicitními klíči 285

8.3 – Výčty obecně 288

8.3.1 – Metoda inject 289

8.3.2 – Používání kvantifikátorů 290

8.3.3 – Metoda partition 290

8.3.4 – Iterování přes skupiny 291

8.3.5 – Konverze na pole nebo množiny 292

8.3.6 – Používání enumerátorů 293

8.3.7 – Používání generátorů 294

8.4 – Závěr 295

Kapitola 9 Pokročilejší datové struktury 297

9.1 – Práce s množinami 297

9.1.1 – Jednoduché množinové operace 298

9.1.2 – Pokročilejší množinové operace 299

9.2 – Práce se zásobníky a frontami 301

9.2.1 – Implementace přísnějšího zásobníku 302

9.2.2 – Detekce nevyrovnaného použití párových znaků 303

9.2.3 – Zásobníky a rekurze 304

9.2.4 – Implementace přísnější fronty 305

9.3 – Práce se stromy 306

9.3.1 – Implementace binárního stromu 307

9.3.2 – Řazení s použitím binárního stromu 309

9.3.3 – Používání binárního stromu jako vyhledávací tabulky 311

9.3.4 – Konverze stromu na řetězec nebo pole 311

Page 15: Ruby kompendium

14

9.4 – Práce s grafy 312

9.4.1 – Implementace grafu jako matice sousednosti 313

9.4.2 – Určení, zdali je graf souvislý 316

9.4.3 – Určení, zdali má graf Eulerovu kružnici 317

9.4.4 – Určení, zdali má graf Eulerovu cestu 318

9.4.5 – Nástroje Ruby pro práci s grafy 319

9.5 – Závěr 319

Kapitola 10 I/O a uložení dat 321

10.1 – Práce se soubory a adresáři 322

10.1.1 – Otevírání a zavírání souborů 322

10.1.2 – Aktualizace souboru 323

10.1.3 – Připojení k souboru 324

10.1.4 – Náhodný přístup k souborům 324

10.1.5 – Práce s binárními soubory 325

10.1.6 – Zamykání souborů 327

10.1.7 – Vykonávání jednoduchých I/O 327

10.1.8 – Bufferované a nebufferované I/O 328

10.1.9 – Manipulace s vlastnictvím a přístupovými právy souboru 329

10.1.10 – Získávání a nastavování informací o časových údajích 331

10.1.11 – Kontrola existence souboru a velikosti 332

10.1.12 – Speciální vlastnosti souborů 333

10.1.13 – Práce s rourami (pipes) 335

10.1.14 – Vykonávání speciálních I/O operací 337

10.1.15 – Používání neblokujících I/O 337

10.1.16 – Použití metody readpartial 338

10.1.17 – Manipulace s cestami 339

10.1.18 – Používání třídy Pathname 340

10.1.19 – Manipulace se soubory na úrovni příkazů 341

10.1.20 – Čtení znaků z klávesnice 342

10.1.21 – Načtení celého souboru do paměti 343

10.1.22 – Iterace skrz soubor po řádcích 343

10.1.23 – Iterace skrz soubor po bajtech 344

10.1.24 – Práce s řetězcem jako se souborem 344

10.1.25 – Čtení dat vestavěných v programu 345

10.1.26 – Čtení zdroje programu 345

Page 16: Ruby kompendium

15

10.1.27 – Práce s dočasnými soubory 346

10.1.28 – Změna a nastavení aktuálního adresáře 346

10.1.29 – Změna aktuálního kořene 347

10.1.30 – Iterace skrz položky adresáře 347

10.1.31 – Získání seznamu položek adresáře 347

10.1.32 – Vytvoření sledu adresářů 348

10.1.33 – Rekurzivní odstranění adresáře 348

10.1.34 – Hledání souborů a adresářů 348

10.2 – Vysokoúrovňový přístup k datům 349

10.2.1 – Jednoduchý marshaling 349

10.2.2 – Složitější marshaling 351

10.2.3 – Omezené rekurzivní kopírování pomocí modulu Marshal 352

10.2.4 – Lepší perzistence objektů s PStore 352

10.2.5 – Práce s daty ve formátu CSV 354

10.2.6 – Marshaling s YAML 356

10.2.7 – Prevalence objektů s Madeleine 357

10.2.8 – Použití knihovny DBM 358

10.3 – Použití knihovny KirbyBase 359

10.4 – Spojení s externími databázemi 362

10.4.1 – Rozhraní k SQLite 362

10.4.2 – Rozhraní k MySQL 363

10.4.3 – Rozhraní k PostgreSQL 366

10.4.4 – Rozhraní k LDAP 368

10.4.5 – Rozhraní k Oracle 370

10.4.6 – Používání modulu DBI 371

10.4.7 – ORM (Object-Relational Mapper) 372

10.5 – Závěr 374

Kapitola 11 OOP a dynamické rysy Ruby 375

11.1 – Každodenní OOP úlohy 376

11.1.1 – Používání vícenásobných konstruktorů 376

11.1.2 – Vytváření atributů instance 377

11.1.3 – Používání pokročilých konstruktorů 378

11.1.4 – Vytváření vlastností a metod na úrovni třídy 380

11.1.5 – Dědění z nadtřídy 383

11.1.6 – Testování třídy objektů 385

Page 17: Ruby kompendium

16

11.1.7 – Testování rovnosti objektů 387

11.1.8 – Správa přístupu k metodám 389

11.1.9 – Kopírování objektu 391

11.1.10 – Používání initialize_copy 392

11.1.11 – Jak porozumět metodě allocate 393

11.1.12 – Práce s moduly 394

11.1.13 – Transformace a konverze objektů 397

11.1.14 – Vytváření čistě datových tříd (Structs) 400

11.1.15 – Zmrazení objektů 401

11.2 – Pokročilejší techniky 402

11.2.1 – Posílání explicitních zpráv objektu 403

11.2.2 – Specializování individuálních objektů 404

11.2.3 – Vnořování tříd a modulů 408

11.2.4 – Vytváření parametrických tříd 408

11.2.5 – Využití pokračování (continuation) pro implementaci generátoru 411

11.2.6 – Ukládání kódu jako objektů 414

11.2.7 – Jak funguje přidání modulu 415

11.2.8 – Detekce výchozích parametrů 417

11.2.9 – Delegování nebo přesměrování 417

11.2.10 – Automatické definování přístupových metod k atributům na úrovni třídy 420

11.2.11 – Pokročilejší programovací disciplíny 421

11.3 – Práce s dynamickými rysy 423

11.3.1 – Dynamické vyhodnocení kódu 423

11.3.2 – Používání const_get 425

11.3.3 – Dynamická instanciace třídy podle jména 425

11.3.4 – Získávání a nastavování proměnných instance 426

11.3.5 – Používání define_method 427

11.3.6 – Používání const_missing 430

11.3.7 – Odstraňování definicí 431

11.3.8 – Získávání seznamů definovaných entit 434

11.3.9 – Prozkoumání zásobníku 435

11.3.10 – Monitorování vykonávání programu 436

11.13.11 – Procházení prostorem objektů 438

11.3.12 – Správa volání neexistujících metod 438

11.3.13 – Sledování změn definice třídy nebo objektů 439

11.3.14 – Definování finalizerů pro objekty 443

11.4 – Závěr 444

Page 18: Ruby kompendium

17

Kapitola 12 Grafická rozhraní pro Ruby 445

12.1 – Ruby/Tk 446

12.1.1 – Přehled 446

12.1.2 – Jednoduchá aplikace s okny 447

12.1.3 – Práce s tlačítky 449

12.1.4 – Práce s textovými poli 452

12.1.5 – Práce s ostatními widgety 456

12.1.6 – Několik poznámek 459

12.2 – Ruby/GTK2 459

12.2.1 – Přehled 460

12.2.2 – Jednoduchá aplikace s okny 461

12.2.3 – Práce s tlačítky 462

12.2.4 – Práce s textovými poli 463

12.2.5 – Práce s ostatními widgety 466

12.2.6 – Další poznámky 470

12.3 – FXRuby (FOX) 472

12.3.1 – Přehled 473

12.3.2 – Jednoduchá aplikace s okny 473

12.3.3 – Práce s tlačítky 475

12.3.4 – Práce s textovými poli 477

12.3.5 – Práce s dalšími widgety 478

12.3.6 – Další poznámky 487

12.4 – QtRuby 487

12.4.1 – Přehled 487

12.4.2 – Jednoduchá aplikace s okny 488

12.4.3 – Práce s tlačítky 488

12.4.4 – Práce s textovými poli 490

12.4.5 – Práce s ostatními widgety 492

12.4.6 – Další poznámky 497

12.5 – Další GUI toolkity 497

12.5.1 – Ruby a X 498

12.5.2 – Ruby a wxWidgets 498

12.5.3 – Apollo (Ruby a Delphi) 498

12.5.4 – Ruby a Windows API 499

12.6 – Závěr 499

Page 19: Ruby kompendium

18

Kapitola 13 Vlákna v Ruby 501

13.1 – Vytváření a manipulace s vlákny 502

13.1.1 – Vytvoření vláken 502

13.1.2 – Přístup k lokálním proměnným vlákna 503

13.1.3 – Dotazování a změna stavu vlákna 505

13.1.4 – Čekání na dokončení (a zachycení návratové hodnoty) 508

13.1.5 – Práce s výjimkami 509

13.1.6 – Použití skupiny vláken 511

13.2 – Synchronizace vláken 512

13.2.1 – Jednoduchá synchronizace s kritickými sekcemi 513

13.2.2 – Synchronizace přístupu ke zdrojům (mutex.rb) 514

13.2.3 – Použití tříd předdefinovaných synchronizovaných front 518

13.2.2 – Použití podmínkových proměnných 520

13.2.5 – Použití dalších synchronizačních technik 521

13.2.6 – Časový limit operace 524

13.2.7 – Čekání na událost 525

13.2.8 – Pokračování ve zpracování během I/O 526

13.2.9 – Implementace paralelních iterátorů 527

13.2.10 – Paralelní rekurzivní mazání 529

13.3 – Závěr 530

Kapitola 14 Skriptování a správa systému 531

14.1 – Spuštění externího programu 531

14.1.1 – Použití system a exec 532

14.1.2 – Substituce výstupu příkazu 533

14.1.3 – Manipulace s procesy 534

14.1.4 – Manipulace se standardním vstupem/výstupem 536

14.2 – Volby a argumenty příkazového řádku 537

14.2.1 – Analýza voleb příkazového řádku 537

14.2.2 – Práce s ARGF 539

14.2.3 – Práce s ARGV 539

14.3 – Knihovna Shell 540

14.3.1 – Použití Shell pro přesměrování I/O 540

14.3.2 – Několik poznámek k shell.rb 542

14.4 – Přístup k proměnným prostředí 543

14.4.1 – Získávání a nastavování proměnných prostředí 543

Page 20: Ruby kompendium

19

14.4.2 – Ukládání proměnných prostředí jako pole nebo haš 544

14.4.3 – Import proměnných prostředí jako globálních proměnných 545

14.5 – Skriptování v Microsoft Windows 545

14.5.1 – Používání Win32API 546

14.5.2 – Použití Win32OLE 547

14.5.3 – Používání ActiveScriptRuby 550

14.6 – Instalátor na jedno kliknutí pro Windows 551

14.7 – Knihovny, o kterých potřebujete vědět 552

14.8 – Práce se soubory, adresáři a stromy 553

14.8.1 – Několik slov k textovým filtrům 553

14.8.2 – Kopírování adresářovéhostromu (včetně symbolických odkazů) 554

14.8.3 – Mazání souborů podle data nebo jiného kritéria 555

14.8.4 – Zjištění volného místa na disku 556

14.9 – Různé skriptovací úlohy 557

14.9.1 – Ruby řešení v jediném souboru 557

14.9.2 – Roura do interpretu Ruby 558

14.9.3 – Získávání a nastavování návratových kódů 559

14.9.4 – Testování, jestli program běží interaktivně 560

14.9.5 – Určení platformy operačního systému 560

14.9.6 – Použití modulu Etc 561

14.10 – Závěr 562

Kapitola 15 Ruby a datové formáty 563

15.1 – Analýza XML pomocí REXML 564

15.1.1 – Analýza stromu 565

15.1.2 – Analýza proudu 566

15.1.3 – XPath a další 567

15.2 – Práce s RSS a Atom 568

15.2.1 – Standardní knihovna rss 568

15.2.2 – Knihovna feedtools 571

15.3 – Manipulace s obrázky pomocí RMagick 572

15.3.1 – Běžné grafické úkoly 573

15.3.2 – Speciální efekty a transformace 576

15.3.3 – Kreslicí API 579

15.4 – Tvorba PDF dokumentů pomocí PDF::Writer 582

15.4.1 – Základní koncepty a techniky 582

Page 21: Ruby kompendium

20

15.4.2 – Vzorový dokument 585

15.5 – Závěr 592

Kapitola 16 Testování a odstraňování chyb 593

16.1 – Testování pomocí Test::Unit 594

16.2 – Nástroje ZenTest 598

16.3 – Ruby debugger 601

16.4 – Používání irb jako debuggeru 605

16.5 – Měření vytížení kódu 606

16.6 – Měření výkonu 607

16.7 – Uživatelsky příjemná reprezentace objektů 611

16.8 – Závěr 612

Kapitola 17 Balíčkování a distribuce kódu 613

17.1 – Používání RDoc 613

17.1.1 – Jednoduché značky 615

17.1.2 – Pokročilejší formátování 617

17.2 – Instalace a balíčkování 618

17.2.1 – Knihovna setup.rb 619

17.2.2 – RubyGems 620

17.3 – RubyForge a RAA 622

17.4 – Závěr 623

Kapitola 18 Síťové programování 625

18.1 – Síťové servery 626

18.1.1 – Jednoduchý server: aktuální čas 627

18.1.2 – Implementace serveru s vlákny 628

18.1.3 – Případová studie: peer-to-peer šachový server 629

18.2 – Síťoví klienti 638

18.2.1 – Získávání opravdu náhodných čísel z webu 638

18.2.2 – Spojení s oficiálním časovým serverem 641

18.2.3 – Interakce s POP serverem 642

18.2.4 – Posílání e-mailu prostřednictvím SMTP 644

18.2.5 – Interakce s IMAP serverem 647

18.2.6 – Kódování a dekódování příloh 648

18.2.7 – Případová studie: brána mezi e-mailovou konferencí a diskusní skupinou 651

Page 22: Ruby kompendium

21

18.2.8 – Získávání webové stránky z URL 656

18.2.9 – Používání knihovny Open-URI 657

18.3 – Závěr 657

Kapitola 19 Ruby a webové aplikace 659

19.1 – CGI programování v Ruby 659

19.1.1 – Představení knihovny cgi.rb 661

19.1.2 – Zobrazení a zpracování formulářů 662

19.1.3 – Práce s cookies 663

19.1.4 – Práce s relacemi uživatele 664

19.2 – Používání FastCGI 665

19.3 – Ruby on Rails 667

19.3.1 – Principy a technologie 667

19.3.2 – Testování a ladění Rails aplikací 669

19.3.3 – Rozšíření jádra 669

19.3.4 – Související nástroje a knihovny 670

19.4 – Vývoj webových aplikací s Nitro 671

19.4.1 – Vytvoření základní Nitro aplikace 671

19.4.2 – Nitro a vzor MVC 673

19.4.3 – Nitro a Og 677

19.4.4 – Běžné úkoly při vývoji webových aplikací v Nitro 678

19.4.5 – Několik důležitých detailů 681

19.5 – Úvod do Wee 683

19.5.1 – Jednoduchý příklad 684

19.5.2 – Asociování stavu s URL 685

19.6 – Vývoj webových aplikací s IOWA 686

19.6.1 – Základní koncepty IOWA 686

19.6.2 – Šablony v IOWA 689

19.6.3 – Předávání řízení mezi komponentami 690

19.7 – Ruby a webový server 691

19.7.1 – Používání mod_ruby 692

19.7.2 – Používání erb 693

19.7.3 – Používání serveru WEBrick 695

19.7.4 – Používání serveru Mongrel 697

19.8 – Závěr 699

Page 23: Ruby kompendium

22

Kapitola 20 Distribuované Ruby 701

20.1 – Přehled: použití drb 702

20.2 – Případová studie: simulace burzovního telegrafu 704

20.3 – Rinda: Ruby Tuplespace 708

20.4 – Service Discovery s distribuovaným Ruby 712

20.5 – Závěr 713

Kapitola 21 Vývojové nástroje pro Ruby 715

21.1 – Použití RubyGems 715

21.2 – Použití Rake 717

21.3 – Použití irb 721

21.4 – Utilita ri 726

21.5 – Podpora editorů 727

21.6 – Integrovaná vývojová prostředí 728

21.7 – Závěr 729

Kapitola 22 Komunita Ruby 731

22.1 – Zdroje na webu 731

22.2 – Diskusní skupiny a e-mailové konference 732

22.3 – Blogy a online magazíny 732

22.4 – Ruby Change Requests (požadavky na změnu) 733

22.5 – IRC kanály 733

22.6 – Konference o Ruby 734

22.8 – Závěr 734

Rejstřík 735

Page 24: Ruby kompendium

23

Předmluva

Předmluva k druhému vydáníLidé – zejména filozofové – ve starověké Číně si mysleli, že za světem a za každou existencí je ukry-to něco tajemného. Něco, co nemůže být nikdy vyřčeno, vysvětleno, a ani popsáno slovy. Číňané to nazývali Tao, Japonci zase Do. Pokud to přeložíte do angličtiny, znamená to Way (v češtině "Cesta" nebo "Způsob"). Je to Do v džudo, kendo, karatedo a aikido. Nejsou to jen bojová umění, ale zahr-nují jak filozofii, tak i způsob života.

Také programovací jazyk Ruby má svou filozofii a způsob myšlení. Naučí lidi myslet jinak. Pomáhá programátorům, aby měli při své práci více zábavy. Není to tím, že Ruby pochází z Japonska, ale tím, že programování je důležitou částí lidského bytí (dobře, přinejmenším některých lidských bytostí), přičemž Ruby bylo navrženo k tomu, aby lidem pomáhalo mít lepší život.

Tao je těžké popsat. Ačkoliv ho cítím, nikdy jsem se nepokusil jej vysvětlit pomocí slov. Je to pro mě příliš obtížné, dokonce i v japonštině, v mém rodném jazyce. Ale chlapík jménem Hal Fulton to zkusil a jeho první pokus, který spočíval v prvním vydání této knihy, byl dost dobrý. Tento jeho druhý pokus popsat Tao jazyka Ruby je díky pomoci mnoha lidí z komunity ještě lepší. Jak se Ruby stává stále více populárním (částečně i kvůli Ruby on Rails), je důležité porozumět tajemství pro-gramátorské produktivity. Věřím, že tato kniha vám pomůže se stát zdatným programátorem.

Veselé hackování.

Yukihiro "Matz" MatsumotoSrpen 2006, Japonsko

Předmluva k prvnímu vydáníKrátce poté, co jsem se počátkem 80. let poprvé setkal s počítači, začal jsem se zajímat o progra-movací jazyky. Od té doby jsem na ně byl zatížený. Myslím si, že důvodem pro tento zájem je skutečnost, že programovací jazyky jsou způsobem, jak vyjádřit lidské myšlenky. Jsou v zásadě orientovány na člověka.

Navzdory tomuto faktu měly programovací jazyky sklon být orientovány strojově. Mnoho jazyků bylo navrženo pro pohodlí samotných počítačů.

Ale vzhledem k tomu, že se počítače staly výkonnějšími a méně nákladnými, se tato situace po-stupně změnila. Podívejme se například na strukturované programování. Stroje se nestarají o to, zdali je program strukturován správně – pouze ho vykonají bit po bitu. Strukturované programo-vání není pro stroje, ale pro lidi. Totéž platí pro objektově orientované programování.

Přišel čas pro návrh jazyka zaměřeného na člověka.

Page 25: Ruby kompendium

24

V roce 1993 jsem mluvil s kolegou o skriptovacích jazycích, o jejich síle a budoucnosti. Skriptování jsem cítil jako cestu, kterou by se programování mělo v budoucnosti ubírat – orientací na člověka.

Ale s existujícími jazyky jako Perl a Python jsem nebyl spokojen. Chtěl jsem jazyk, který by byl silnější než Perl a více objektově orientovaný než Python. Protože jsem nemohl najít ideální jazyk, rozhodl jsem se vytvořit svůj vlastní.

Ruby sice není nejjednodušším jazykem, ale lidská duše ve svém přirozeném stavu také není jedno-duchá. Miluje jednoduchost a složitost zároveň. Nemůže ovládat příliš mnoho komplexních věcí, ale ani příliš mnoho věcí jednoduchých. Je to věc rovnováhy.

Při navrhování jazyka orientovaného na člověka, Ruby, jsem následoval princip nejmenšího pře-kvapení (Principle of Least Surprise). Všechno, co mě překvapilo, jsem považoval za méně správné. Následkem toho mám při programování v Ruby dobrý pocit – dokonce druh radosti. A již od prv-ního vydání Ruby v roce 1995 se mnou mnoho programátoru po celém světě o radosti z progra-mování v Ruby souhlasí.

Zde bych rád vyjádřil mé velké uznání lidem v komunitě Ruby. Oni jsou srdcem úspěchu Ruby.

Jsem také vděčný autorovi této knihy, kterým je Hal E. Fulton.

Tato kniha vysvětluje filozofií stojící za Ruby destilovanou z mého mozku a z komunity Ruby. Rád bych věděl, jak je možné, že Hal může číst mou mysl a odhalit tak tajemství Ruby. Nikdy jsem se s ním nesetkal tváří v tvář, ale doufám, že k tomu brzy dojde.

Věřím, že tato kniha a Ruby vám pomohou v tom, aby vaše programování bylo zábavou.

Yukihiro "Matz" MatsumotoZáří 2001, Japonsko

Page 26: Ruby kompendium

25

Poděkování

Poděkování pro druhé vydáníZdravý rozum říká, že druhé vydání bude vyžadovat dvakrát méně práce, než vyžadovalo vydání první. Zdravý rozum nemá pravdu.

Třebaže velká část této knihy vychází z prvního vydání, musela být část přepracována a doladěna. Každá jednotlivá věta v této knize musela projít skrz filtr, který se ptal: "Co bylo pravdou v roce 2001, je pravdou i v roce 2006?" A to je samozřejmě jen začátek.

Stručně řečeno – na toto druhé vydání jsem vynaložil nespočet stovek hodin práce; téměř tolik, kolik jsem vynaložil na první vydání. A to jsem "pouze autor".

Tato kniha mohla vzniknout pouze díky kolektivní práci mnoha lidí. Na straně vydavatele dlužím poděkováním těmto lidem za jejich tvrdou práci a nekonečnou trpělivost: Debra Williams Cauley, Songlin Qui a Mandie Frank. Další díky patří Geneil Breezeové za její neúnavné úpravy mé po-někud nedokonalé angličtiny. Chci zde poděkovat i všem spolupracovníkům, se kterými jsem se nikdy nesetkal, protože jejich práce probíhala v pozadí.

Technická redakce byla v tomto složení: Shashank Date a Francis Hwang. Odvedli skvělou práci a já si toho cením. Chyby, které se nakonec dostaly do tisku, samozřejmě padají na mou hlavu.

Mé další poděkování patří lidem, kteří dodávali vysvětlivky, psali vzorový kód a odpovídali na mé četné otázky. Mezi ně patří samotný Matz (Yukihiro Matsumoto), Dave Thomas, Christian Neu-kirchen, Chad Fowler, Curt Hibbs, Daniel Berger, Armin Roehrl, Stefan Schmiedl, Jim Weirich, Ryan Davis, Jenny W., Jim, Freeze, Lyle Johnson, Martin DeMello, Matt Lawrence, Ron Jeffries, Tim Hunter, Chet Hendrickson, Nathaniel Talbott a Bil Kleb.

Zvláštní dík patří největším přispěvatelům. Andrew Johnson podstatně zvýšil mou znalost regu-lárních výrazů. Paul Battley poskytl významné příspěvky do kapitoly o internacionalizaci. Masao Mutoh vypomohl se stejnou kapitolou a dále přispěl materiály o GTK. Austin Ziegler mě zasvětil do tajemství vytváření PDF souborů. Caleb Tennis přidal materiály o Qt. Eric Hodel přispěl mate-riálem o Rinda a Ring. James Britt vypomohl s kapitolou o webovém vývoji.

Znovu musím poděkovat a pochválit Matze – nejenom za jeho pomoc, ale v prvé řadě za vytvoření Ruby. Domo arigato gozaimasu! ("Mockrát děkuji!" – pozn. red.)

Opět děkuji svým rodičům, kteří mě bez přestání povzbuzovali a těšili se na tuto knihu. Možná ještě z nich udělám programátory.

A ještě jednou musím poděkovat všem lidem z komunity Ruby za jejich neúnavnou práci, produk-tivitu a za udržování ducha komunity. Obzvláště děkuji čtenářům této knihy (v obou vydáních) a doufám, že ji shledáte poučnou, užitečnou a možná dokonce i zábavnou.

Page 27: Ruby kompendium

26

Poděkování pro první vydáníNapsání knihy je týmové úsilí. Jedná se o skutečnost, kterou jsem nemohl plně docenit až do té doby, dokud jsem sám nějakou knihu nenapsal. Doporučuji to zažít, ačkoliv je to ponižující. Potvr-zuji tedy, že bez podpory mnoha lidí by tato kniha nemohla existovat.

Mé poděkování a ocenění musí nejprve směřovat k Matzovi (Yukihiro Matsumoto), který vytvořil jazyk Ruby. Domo arigato gozaimasu!

Další dík patří Conradu Scheikerovi za pomoc při vytváření celkové struktury knihy. Tento člověk mi navíc prokázal velkou službu, když mě v roce 1999 uvedl do jazyka Ruby.

Na vytváření obsahu této knihy se samozřejmě podílelo několik dalších lidí. Zde musím především zmínit Guye Hursta, který napsal nejenom značné části úvodních kapitol, ale také dva dodatky ke knize. Jeho pomoc musím označit za neocenitelnou.

Poděkování pochopitelně patří i ostatním přispěvatelům, které zde uvádím bez nějakého speciální-ho pořadí: Kevin Smith odvedl skvělou práci na sekci věnované GTK v kapitole 6, čímž mě zachrá-nil před horečným osvojováním si potřebných znalostí. Patrick Logan ve stejné kapitole osvětlil tajemství GUI FOX. Nesmím zapomenout na Chada Fowlera, který se v kapitole 9 věnoval XML, a jenž také přispěl nějakým materiálem do CGI sekce.

Velký dík také patří těm, kteří asistovali při provádění korektur, a již hodnotili nebo přispěli v dal-ších rozmanitých oblastech: Don Muchow, Mike Stok, Miho Ogishima a další. Také děkuji Davidu Eppsteinovi, profesoru matematiky, za zodpovězení mých otázek o teorii grafů.

Jednou z významných věcí na Ruby je podpora komunity. V e-mailových konferencích a diskus-ních skupinách se našlo mnoho lidí, kteří zodpověděli mé otázky a poskytli mi potřebnou pomoc. Opět bez nějakého speciálního pořadí se jednalo o tyto lidi: Dave Thomas, Andy Hunt, Hee-Sob Park, Mike Wilson, Avi Bryant, Yasushi Shoji ("Yashi"), Shugo Maeda, Jim Weirich, "Arton" a Ma-saki Suketa. Pokud jsem na někoho zapomněl, omlouvám se.

A samozřejmě – tato kniha by nikdy nevyšla bez ohromné podpory vydavatele. Na produkci této knihy v zákulisí pracovalo mnoho lidí – v první řadě musím poděkovat Williamu Brownovi, který pracoval blízko mě a byl stálým zdrojem podpory, a Scottu Meyerovi, jenž se důkladně probíral všemi podklady a dával jednotlivé materiály dohromady. Ostatní bohužel nemohu ani jmenovat, protože jsem o nich nikdy neslyšel. Nicméně oni sami vědí, o koho jde.

Také musím poděkovat svým rodičům, kteří z povzdálí tento projekt sledovali, po celou dobu mě povzbuzovali a dokonce se kvůli mně obtěžovali naučit kus počítačové vědy.

Jeden můj přítel, který pracuje jako spisovatel, mi jednou řekl: "Když napíšeš knihu a nikdo si ji nepřečte, pak jsi ve skutečnosti žádnou knihu nenapsal." Takže chci poděkovat i svým čtenářům. Tato kniha je určena vám. Doufám, že pro vás má nějakou hodnotu.

Page 28: Ruby kompendium

27

O autoroviHal Fulton má dva tituly v počítačové vědě na University of Mississippi. Předtím, než se kvůli řadě kontraktů (zejména pro IBM) přestěhoval do Austinu v Texasu, čtyři roky vyučoval počíta-čovou vědu na univerzitní úrovni. Více než 15 let pracoval s různými variantami Unixu, včetně AIX, Solaris a Linux. K Ruby se úplně poprvé dostal v roce 1999. V roce 2001 začal pracovat na prvním vydání této knihy, která byla tehdy celkově druhou knihou o Ruby v anglickém jazyce. Účastnil se celkem šesti konferencí o Ruby, přičemž na čtyřech z nich měl svou vlastní prezentaci (včetně první European Ruby Conference, která se konala v Karlsruhe v Německu). V současné době pracuje pro Broadwing Communications v Austinu v Texasu, kde pracuje na velkém datovém skladišti a souvisejících telekomunikačních aplikacích. Každý den pracuje s kódem C++, Oracle a samozřejmě i s Ruby.

Hal Fulton je aktivním členem e-mailové konference (a IRC kanálu) zaměřené na Ruby. Má rozpra-covaných několik projektů týkajících se Ruby. Je členem ACM a IEEE Computer Society. V osob-ním životě má rád hudbu, čtení, umění a fotografování. Je členem Mars Society a je vesmírný nad-šenec, který by se rád dostal do vesmíru předtím, než zemře. Žije v Austinu v Texasu.

Page 29: Ruby kompendium

28

ÚvodCesta, kterou lze pojmenovat, není tou pravou Cestou.

– Lao C', Kniha o Tao a ctnosti (Tao-te-ťing)

Originální anglický název této knihy je "The Ruby Way". Tento název si říká o vysvětlení. Mým cílem bylo napsat tuto knihu pokud možno ve shodě se samotnou filozofií Ruby. To bylo také cílem všech ostatních spolupracovníků. Ačkoliv s nimi sdílím ocenění za úspěch, odpovědnost za jakékoliv chyby, které se do této knihy dostaly, leží výhradně na mně.

Samozřejmě není v mých silách, abych vám precizním způsobem řekl úplně všechno o Ruby. To je v první řadě úkol pro samotného tvůrce jazyka, Matze, ale myslím si, že dokonce i on by měl potíž vyjádřit vše slovy. Stručně řečeno: "Ruby – kompendium znalostí pro začátečníky i profesionály" je pouze kniha, ale cesta Ruby (Ruby Way) je věcí tvůrce jazyka a komunity jako celku. Ačkoliv tohle lze jen velmi těžko popsat pomocí slov, v tomto úvodu se pokusím zachytit alespoň něco z toho nevyslovitelného o Ruby. Moudrý zájemce o Ruby to nicméně nebude brát až tak úplně vážně.

Vezměte, prosím, na vědomí, že toto je druhé vydání. Ačkoliv mnoho věcí zůstalo stejných, mnoho dalších věcí se změnilo. Většina tohoto úvodu je stejná jako v předchozím vydání, nicméně nahléd-něte do následující části "O druhém vydání", ve které jsou shrnuty změny a nový materiál.

O druhém vydáníVšechno se mění a Ruby není výjimkou. Když v srpnu 2006 píšu tento úvod, první vydání této knihy je téměř pět let staré. A to je určitě vhodný čas na aktualizaci.

V tomto vydání je mnoho změn a mnoho nových materiálů. Stará kapitola 4 o jednoduchých dato-vých úlohách je nyní rozdělena do šesti kapitol, dvě z nich (Symboly a rozsahy a Internacionalizace v Ruby) jsou úplně nové. Ve zbývajících čtyřech jsou přidány nové příklady a komentáře. Kapitola o regulárních výrazech byla rapidně rozšířena – nyní pokrývá nejenom běžné regulární výrazy, ale také novější engine Oniguruma.

Obsah kapitol 8 a 9 byl v prvním vydání této knihy původně obsažen v jediné kapitole. Ta byla rozdělena v okamžiku, kdy došlo k přidání dalších materiálů, protože se příliš rozrostla.

Současné kapitoly 18, 19 a 20 obdobným způsobem vyrostly z kapitoly 9. Abychom získali místo navíc pro tyto materiály, museli jsme bohužel z nového vydání odstranit všechny přílohy.

Zbývající nové kapitoly jsou následující:

� Kapitola 15 – Ruby a datové formáty. Zahrnuje XML, RSS, obrázkové soubory, tvorbu PDF souborů a další věci.

� Kapitola 16 – Testování a odstraňování chyb. Zabývá se unit testy, profilováním, laděním a dalšími podobnými tématy.

Page 30: Ruby kompendium

29

� Kapitola 17 – Balíčkování a distribuce kódu. Tato kapitola zahrnuje použití setup.rb, vy-tvoření RubyGems a další záležitosti.

� Kapitola 21 – Vývojové nástroje pro Ruby. Nabízí pohled na podporu Ruby v editoru a IDE, utilitu ri a RubyGems z pohledu uživatele.

� Kapitola 22 – Komunita Ruby. Tato kapitola shrnuje důležité webové stránky, e-mailové konference, diskusní skupiny, konference, IRC kanály a další.

V širším smyslu můžeme říci, že každá kapitola v této knize je nová, protože jsem upravil a aktuali-zoval každou z nich; udělal jsem stovky menších a tucty větších změn. Smazal jsem zastaralé nebo méně důležité věci a změnil materiály tak, aby odpovídaly všem aktuálním změnám v Ruby. Do každé kapitoly jsem samozřejmě přidal nové příklady a komentáře.

Pravděpodobně vás bude zajímat, co všechno bylo přidáno do starých kapitol. Jednou z nejdůle-žitějších změn je popis enginu Oniguruma, o kterém jsem se již zmínil dříve. Dále musím zmínit popis matematických knihoven a tříd jako BigDecimal, mathn a matrix. Nesmím zapomenout ani na nové třídy, jako například Set a DateTime.

K velkým změnám v obsahu došlo především v následujících kapitolach:

� Kapitoly 10 – I/O a uložení dat. Přidány materiály o readpartial a neblokujícím I/O. Ne-chybí materiály o třídě StringIO. Také jsem přidal materiály o CSV, YAML a KirbyBase. Do databázové části této kapitoly byly přidány informace o Oracle, SQLite, DBI a diskuse o ORM (Object-Relational Mappers).

� Kapitola 11 – OOP a dynamické rysy Ruby. Zahrnuje poslední dodatky k Ruby, například initialize_copy, const_get, const_missing a define_method. Do této kapitoly jsem také přidal informace o technice delegování.

� Kapitola 12 – Grafická prostředí pro Ruby. Téměř všechno bylo upraveno (zejména sekce o GTK a Fox). Část věnovaná QtRuby je úplně nová.

� Kapitola 14 – Skriptování a správa systému. Nově popisuje instalátor Ruby pro Windows a několik podobných balíčků. Došlo také k několika změnám v ukázkových kódech.

� Kapitola 18 – Síťové programování. Nově obsahuje část věnovanou e-mailovým přílohám. Interakce se serverem IMAP je rovněž novinkou. Obsahuje popis knihovny OpenURI.

� Kapitola 19 – Ruby a webové aplikace. Nyní stručně pokrývá Ruby on Rails, Nitro, Wee, IOWA a další webové nástroje. Také pokrývá knihovny WEBrick a Mongrel.

� Kapitola 20 – Distribuované Ruby. Obsahuje nové materiály popisující knihovnu Rinda, což je implementace konceptu tuplespace v Ruby. Také pokrývá těsně související Ring.

Jsou všechny tyto nové informace nezbytné? Ujišťuji vás, že ano.

Kniha The Ruby Way byla v pořadí druhou knihu o Ruby, která vyšla v anglickém jazyce. (Jako první vyšla vynikající kniha Programming Ruby od Davea Thomase a Andyho Hunta.) Záměrně jsem tuto knihu napsal tak, aby první knihu o Ruby spíše doplňovala, než překrývala. Podle ohlasů to vypadá, že se mi to povedlo.

Page 31: Ruby kompendium

30

Když jsem začal psát první vydání knihy o Ruby, neprobíhaly žádné mezinárodní konference o Ru-by. Neexistovalo RubyForge, ruby-doc.org nebo rubygarden.org. Stručně řečeno – kromě domov-ského webu Ruby bylo na internetu velmi málo informací o tomto programovacím jazyku. Archiv aplikací Ruby obsahoval pouze několik stovek položek.

V té době bylo k dispozici pouze několik málo publikací (ať už online nebo offline), které dávaly najevo, že ví o existenci Ruby. Vždy, když vyšel nějaký článek o Ruby, byl to pro nás důvod, aby-chom si ho všimli a diskutovali o něm na e-mailové konferenci.

Také neexistovaly nástroje a knihovny Ruby, které dnes považujete za zcela běžné. Neexistoval žád-ný RDoc. Nebyl žádný REXML pro analýzu XML. A matematická knihovna byla – v porovnání se současností – podstatně chudší. Ačkoliv existovala jistá podpora databází, ODBC podporováno nebylo. Tk bylo nejpoužívanějším GUI toolkitem a nejběžnější způsob vývoje webových aplikací spočíval na nízkoúrovňové CGI knihovně.

Pro platformu Windows nebyl k dispozici žádný instalátor na jedno kliknutí ("one-click" installer). Uživatelé této platformy obvykle používali Cygwin nebo kompilátor založený na mingw.

Systém RubyGems neexistoval ani v základní formě. Hledání a instalace knihoven a aplikací byl zcela manuální proces, který se neobešel bez zadávání příkazů tar a make. Nikdo neslyšel o Ruby on Rails, a pokud si dobře pamatuji, nikdo tehdy nepoužíval termín kachní typování (duck typing). Pro Ruby nebyl k dispozici ani YAML, ani Rake.

V té době jsme používali Ruby ve verzi 1.6.4 a mysleli si, jak je úžasná. Ale verzi 1.8.5, kterou používáme dnes, je ještě úžasnější. Ačkoliv došlo k několika změnám v syntaxi, nejedná se o nic důležitého, o čem bychom se měli rozepisovat. Většinou se jedná o okrajové záležitosti, které nyní dávají více smyslu než v minulosti.

Došlo ke změně sémantiky některých základních metod. Opět se většinou jedná o drobné změny. Například Dir#chdir dříve nepřijímal blok, v současně době to již umí. Některé základní metody byly zrušeny nebo přejmenovány. Metoda class přišla o svůj alias type. Metoda intern je nyní známa jako metoda to_sym. Array#indices je nyní Array#values_at atd.

Přibylo také několik nových základní metod, například Enumerable#inject, Enumerable#zip a IO#readpartial. Stará knihovna futils se nyní jmenuje fileutils a má svůj vlastní modul File Utils se jmenným prostorem namísto přidávání metod do třídy File.

Pochopitelně došlo i k jiným změnám, o kterých v této sekci nic nepíši. Nicméně je důležité uvědo-mit si, že všechny tyto změny byly provedeny s velkou pozorností a opatrností. Ruby je pořád Ruby. Mnoho krásy Ruby je odvozeno z faktu, že všechny změny byly provedeny pozvolna, s rozmyslem a moudrostí Matze a ostatních vývojářů.

A jak to vypadá dnes?

Dnes máme k dispozici více knih o Ruby a více publikovaných článků, než potřebujeme. Web pře-téká různými tutoriály, ukázkovými zdrojovými kódy a dokumentacemi. Objevily se nové nástroje a knihovny. Z těchto různých nástrojů se zdají být nejvíce používané webové frameworky, nástroje pro blogování, nástroje pro tvorbu značek a ORM (object-relational mappers). A samozřejmě je

Page 32: Ruby kompendium

31

zde k dispozici velké množství dalších – například nástroje a knihovny pro databáze, GUI, náročné výpočty, webové služby, práci s obrázky, správu zdrojů atd.

Podpora jazyka Ruby v editorech je rozšířenější a promyšlenější. IDE jsou velmi užitečné.

Je rovněž nesporné, že komunita Ruby se rozrostla a změnila. Ruby dnes rozhodně není podřadný jazyk – používá ho NASA, NOAA, Motorola a mnoho dalších velkých firem a institucí. Je používán nejenom pro práce s grafikou či databázemi, ale také pro různé výpočty, vývoj webových aplikací a další věci. Stručně řečeno – Ruby jde společně s hlavním proudem.

Aktualizaci této knihy dělám s láskou a věřím, že pro vás bude užitečná.

Jak pracovat s touto knihouPředpokládám, že Ruby se nebudete učit z této knihy, protože není určena pro úplné začátečníky. Tímto chci říci, že pokud je pro vás Ruby naprostou novinkou, možná bude vhodné začít s nějakou jinou knihou. Nicméně programátoři jsou houževnatá parta, takže pokud máte nějaké zkušenosti s programováním, lze využít tuto knihu pro získání potřebných znalostí o Ruby. V takovém pří-padě vám samozřejmě doporučuji začít kapitolou 1, která obsahuje stručný úvod do Ruby, věci týkající se syntaxe a samozřejmě i několik ukázkových příkladů.

Tato kniha je převážně určena k zodpovězení otázek typu "Jak mohu udělat...?", takže předpoklá-dám, že budete přeskakovat mezi jednotlivými tématy. Ačkoliv bych byl rozhodně poctěn, kdybyste si přečetli každou stránku od začátku až do konce, nevyžaduji to. Spíše očekávám, že budete brouz-dat obsahem a hledat techniky, které potřebujete nebo věci, jež jsou pro vás něčím zajímavé.

Od té doby, kdy vyšlo první vydání této knihy, jsem mluvil s mnoha lidmi, a jak se ukázalo, hodně z nich trpělivě četlo tuto knihu stránku po stránce. A co více – několik lidí mi sdělilo, že knihu použili pro učení, takže se ukazuje, že možnosti využití této knihy jsou opravdu velké.

Některé věci v této knize mohou vypadat docela jednoduše a možná se budete sami sebe ptát, proč o nich vlastně píšu. Je to kvůli tomu, že různí lidé mají různé schopnosti a zkušenosti. To, co je samozřejmostí pro jednoho uživatele, nemusí být pochopitelné pro jiného. Tuto knihu lze označit za kompromis, protože ačkoliv mým cílem bylo poskytnout vám komplexní a vyčerpávající infor-mace o Ruby, bylo nezbytné udržet rozsah této knihy na nějaké rozumné úrovni.

Při psaní této knihy jsem předpokládal, že věci, které vás zajímají, budete vyhledávat podle poža-dované funkcionality nebo vašeho záměru a nikoliv podle názvu metody nebo třídy. Například třída String obsahuje několik následujících metod – capitalize, upcase, casecmp, downcase a swapcase. V nějaké referenční příručce by tyto metody byly zařazeny pod odpovídající písmena abecedy, nicméně v této knize je naleznete pěkně pohromadě.

V honbě za úplností se občas odkazuji na nějaké jiné knihy, kde naleznete další informace. Díky tomu jsem mohl do knihy zařadit více různorodých příkladů a praktických ukázek.

Protože tato kniha je určena programátorům, snažil jsem se do ní dostat co nejvíce okomentované-ho kódu. Pokud pominu tento úvod, myslím si, že se mi to podařilo. Ačkoliv jako spisovatel mohu být někdy upovídaný, správný programátor chce vždy vidět kód. (A pokud ne, měl by.)

Page 33: Ruby kompendium

32

Některé příklady jsou kompletně vymyšlené, za což se musím omluvit. V některých případech může být problém (nebo zbytečně složité) ilustrovat požadovanou techniku či princip v kontextu reálného světa. Nicméně – pokud jsem chtěl demonstrovat nějaký komplexnější problém, snažil jsem se vytvořit řešení, které by bylo více založeno na potřebách reálného světa. Takže pokud v kni-ze naleznete téma popisující slučování řetězců, může se vám zdát část kódu, která obsahuje foo a bar jako nesmyslná. Pokud jsem se ovšem věnoval složitějšímu tématu, jakým je třeba analýza kódu XML, je ukázkový kód mnohem smysluplnější a realističtější.

Tato kniha obsahuje dva nebo tři osobní manýry, ke kterým se předem přiznávám. Jedním z nich je snaha vyhnout se "ošklivým" globálním proměnným ve stylu Perlu, jako například $_. Ačkoliv jsou v Ruby přítomny, pracují správně a každodenně jsou používány většinou programátorů v Ruby, téměř vždy se jim můžete vyhnout. A já jsem se rozhodl je vždy vynechat.

Dalším osobním manýrem je to, že se vyhýbám samostatným výrazům, pokud nemají nějaký ved-lejší efekt. Ruby je orientováno na výrazy, což je dobrá věc, kterou se v této knize snažím využívat. Ale v ukázkách kódu preferuji nepsat výrazy, které nevrací použitelnou hodnotu. Ačkoliv výraz "abc" + "def" může demonstrovat způsob, jakým se slučují řetězce, já místo toho raději napíšu něco jako str = "abc" + "def". Je možné, že toto se vám bude zdát jako zbytečně rozvláčné, nic-méně – pokud jste programátorem v jazyce C, který si opravdu všímá toho, zdali jsou funkce platné či nikoliv (nebo pokud jste programátor v jazyce Pascal, jenž přemýšlí v procedurách a funkcích), bude to pro vás mnohem přirozenější.

A poslední věc je ta, že nemám rád znak "mřížka" pro označení instančních metod. Mnoho ze skalních uživatelů Ruby si bude myslet, že jsem ukecaný, když řeknu "metoda instance crypt třídy String" místo prostého String#crypt. Nicméně tohle nikoho nepoplete. (Ve skutečnosti už po-malu začínám přecházet k používání znaku "mřížka", protože je zřejmé, že tato notace nevymizí.)

Vždy, když to bylo možné, snažil jsem se poskytnout odkazy na další zdroje, protože požadavek na rozumný rozsah této knihy mi nedovolit dát do knihy všechno, co jsem chtěl. Každopádně věřím, že vám tyto odkazy pomohou. V knize se například velmi často odkazuji na RAA (Ruby Applicati-on Archive), což je jeden z nejdůležitějších zdrojů na webu o Ruby, o kterém byste měli vědět.

Úvodní část většiny programátorských knih obvykle obsahuje naprosto zbytečnou ukázku typo-grafie, které je v knize použita pro výpisy zdrojových kódů (a případně pro další dodatečné infor-mace). V úvodu této knihy nic takového nenaleznete, protože nehodlám urážet vaši inteligenci.

Na závěr chci poukázat na skutečnost, že přibližně 10 procent této knihy bylo napsáno jinými lid-mi, čímž nemám na mysli pouze technické úpravy a opravu chyb. Ocením, když si přečtete podě-kování v této knize (a samozřejmě i v jakékoliv jiné knize). Mnoho čtenářů tuto část knihy bohužel přeskakuje. Běžte si poděkování přečíst teď hned. Prospěje vám to (stejně jako zelenina).

Zdrojové kódy ke staženíZdrojové kódy k této knize si můžete stáhnout z adresy zonerpress.cz/download/ruby-kom-pendium.zip (165 KB). Zdrojový archiv ve formátu .zip, obsahuje všechny významné fragmenty kódu. Pro jednotlivé soubory v tomto archivu se používá následující konvence. Výpisy kódu jsou

Page 34: Ruby kompendium

33

pojmenovány podle čísel jednotlivých kapitol, například soubor list11-1.rb obsahuje kód z vý-pisu 11.1. Kratší části kódu jsou pak pojmenovány na základě čísla stránky, kde se daný fragment kódu nachází, a nepovinného písmene – například soubory p240a.rb a p240b.rb se odkazují na dva fragmenty kódu ze strany 240. Části kódu, které jsou příliš krátké, nebo jež nemůžou být spuš-těny mimo kontext, se obvykle v archivu neobjevují.

Poznámka redakce k českému vydáníZdrojové kódy, které jsme pro vás stáhnuli z adresy www.rubyhacker.com, bohužel nejsou kom-pletní. Archiv obsahuje výpisy a kratší části kódu pouze do kapitoly 12. Ačkoliv Hal Fulton na své stránce slibuje přidání zdrojových kódů ze zbývajících kapitol do poloviny listopadu 2006, dopo-sud se tak nestalo. Za tuto situaci, kterou není v našich silách ovlivnit, se omlouváme.

Jak jsme napsali již výše, soubory s kratšími částmi kódu jsou v archivu pojmenovány podle čísel stránek, na kterých se nachází. Čísla stránek českého vydání nicméně neodpovídají číslům stránek originálního anglického vydání. Z tohoto důvodu jsme do archivu zahrnuli soubor ruby-puvod-ni-obsah.pdf, který obsahuje obsah z původního anglického vydání, a který můžete použít pro snadnější vyhledání zdrojových souborů s požadovanými fragmenty kódu.

Na závěr tohoto textu chci poděkovat panu Daliborovi Šrámkovi, který odvedl ohromné množství práce při odborných korekturách této knihy. Jeho web naleznete na www.insula.cz/dali/.

Sdělte nám svůj názorJako čtenáři této knihy se stáváte těmi nejdůležitějšími kritiky a komentátory. Vážíme si vašeho názoru a chtěli bychom vědět, co děláme správně, co bychom mohli dělat lépe, ve kterých oblastech bychom měli publikovat, a také vaše další podnětné myšlenky, o které jste ochotni se podělit.

Jako odborný redaktor Zoner Press vítám vaše názory. Můžete mi psát – poslat e-mail nebo dopis – a sdělit mi, co se vám v této knize lí bilo nebo nelíbilo, stejně tak, co bychom měli udělat, aby naše další knihy byly lepší. Pokud mi napíšete, nezapomeňte, prosím, připojit název knihy, ISBN, jméno autora, vaše jméno, telefon, fax nebo e-mail. Pozorně zhodnotím vaše názory a poskytnu je všem lidem, kteří pracovali na této knize.

Prosím, vězte, že nemohu pomoci s technickými problémy, které se týkají obsahu knihy, a že díky velkému množství e-mailů, jež dostávám, nemohu zaručit odpověď na každou zprávu.

E-mail: [email protected] nebo [email protected].

Adresa: ZonerPress,

ZONER software, s.r.o.,

Miroslav Kučera,

Nové sady 18,

602 00 Brno.

Page 35: Ruby kompendium

34

Co je "cesta Ruby"?Nechte nás připravit se na zápas s nepopsatelným, a uvidíte, možná se na to nakonec nevykašleme.

– Douglas Adams, Holistická detektivní kancelář Dirka Gentlyho

Jak si představit cestu Ruby (Ruby Way)? Věřím, že má dva aspekty. První je filozofie návrhu Ruby; druhý je filozofie jeho použití. Je přirozené, že návrh a použití spolu obvykle souvisí, ať už se jedná o software nebo hardware. Jestliže postavím nějaké zařízení a nasadím na něj kliku, je to proto, že očekávám, že ji někdo uchopí.

Ruby má jakousi nepojmenovanou kvalitu, která ho dělá tím, čím je. Vidíme tuto kvalitu v návrhu syntaxe a sémantiky jazyka, ale i v programech napsaných pro interpret jazyka. Jenže jakmile na-kreslíme tuto dělící čáru, hned se nám rozmaže.

Ruby zjevně není pouze nástroj pro tvorbu softwaru, ale sám o sobě je to také kus softwaru. Proč by programy v Ruby měly pracovat podle odlišných pravidel, než pracuje interpret? Koneckonců, Ruby je vysoce dynamický a rozšiřitelný jazyk. Mohou existovat důvody, proč by se tyto dvě úrov-ně měly tu a tam odlišovat; je to výhodné pro přizpůsobení se problémům skutečného světa. Ale z obecného pohledu mohou být (a měly by být) myšlenkové pochody stejné. Ruby může být imple-mentováno v Ruby, ve vskutku Hofstadterově stylu, i když v době psaní knihy tomu tak není.

Ačkoliv často nepřemýšlíme nad původem slova "cesta" (way), toto slovo má dva různé významy, ve kterých se používá. Na jedné straně to znamená metodu, způsob nebo techniku, na straně dru-hé pak může znamenat cestu nebo stezku. Je jasné, že oba tyto významy spolu vzájemně souvisejí a když říkám "cesta Ruby" (the Ruby Way), míním tím oba významy.

To, o čem tady mluvíme, je nejenom proces myšlení, ale také cesta, kterou následujete. Ani největší softwarový guru nemůže tvrdit, že dosáhl dokonalosti. Může pouze tvrdit, že následoval cestu. A ačkoliv zde může být více než jedna cesta, já zde mohu mluvit pouze o jedné.

Tradiční moudrost říká, že forma následuje funkci. A tradiční moudrost má obvykle pravdu. Ale Frank Lloyd Wright jednou řekl: "Forma následuje funkci – to je nepochopeno. Forma a funkce by měly být ve shodě, duchovně spojeny." Co tím Wright myslel? Myslím, že tuto pravdu se nelze naučit z knihy, ale pouze ze zkušenosti.

Nicméně, chtěl bych poukázat na to, jakým způsobem Wright vyjádřil tuto pravdu v trochu stra-vitelnější podobě. Byl velký zastánce jednoduchosti, jednou dokonce řekl, že "nejužitečnějšími ná-stroji architekta jsou guma na rýsovacím prkně a páčidlo na staveništi".

Jednou ze ctností Ruby je jednoduchost. Mám k tomuto tématu citovat další myslitele? Podle An-toine de St. Exuperyho: "Dokonalosti je dosaženo nikoliv tehdy, když není co přidat, ale když není co odebrat".

Ruby je ovšem složitý jazyk. Jak tedy mohu tvrdit, že je jednoduchý? Pokud lépe porozumíme ves-míru, možná nalezneme "zákon zachování složitosti" – fakt reality, který narušuje naše životy jako entropie, jíž se nemůžeme vyhnout, ale pouze ji přerozdělit. A tohle je klíč. Složitosti se nemůžeme

Page 36: Ruby kompendium

35

vyhnout, nicméně ji můžeme nechat okolo. Můžeme ji pohřbít mimo dohled. Tohle je starý princip "černé skříňky", která vykonává složité úkoly, nicméně zvenčí je ovládána jednoduchými příkazy.

Pokud jste ještě neztratili trpělivost s mými citacemi, pak je vhodné uvést citát Alberta Einsteina "Všechno by mělo být tak jednoduché, jak to jenom jde, ale ne jednodušší".

V Ruby z pohledu programátora vidíme jednoduchost (když ne z pohledu těch, kdo spravují sa-motný interpret). Vidíme také potenciál pro kompromis. Ve skutečném světě se musíte trochu ohýbat. Například každá entita v Ruby by měla být opravdovým objektem, nicméně určité hodnoty (jako například celá čísla) jsou uloženy jako přímé hodnoty. V obchodu, který je studentům počíta-čové vědy důvěrně znám po desetiletí, jsme vyměnili praktičnost implementace za eleganci návrhu. V důsledku jsme vyměnili jeden druh jednoduchosti za jiný.

To, co Larry Wall řekl o Perlu, je pravda: "Když něco řeknete v malém jazyce, zdá se to velké. Když něco řeknete ve velkém jazyce, zdá se to malé". Totéž platí pro angličtinu. Hlavní důvodem, proč biolog Ernst Haeckel mohl pouze třemi slovy sdělit, že "ontogeneze rekapituluje fylogenezi", bylo to, že měl k dispozici tato silná slova se specifickým významem. Povolujeme vnitřní složitost jazy-ka, protože nám umožňuje odsunout tuto složitost do pozadí v jednotlivých výrocích.

Řečeno jinými slovy – nepište 200 řádků kódu, když 10 řádků udělá stejnou práci. Stručnost je dob-rá věc. Krátká část programu zabere v mozku programátora méně místa a lze ji snadněji uchopit jako celek. A jako pozitivní vedlejší efekt se do kódu při psaní zanese méně chyb.

Samozřejmě je stále potřeba myslet na Einsteinovo varování o jednoduchosti. Pokud máte struč-nost ve svém žebříčku priorit hodně vysoko, skončíte s kódem, který bude beznadějně nečitelný. Informační teorie říká, že komprimovaná data mají podobné statistické vlastnosti jako náhodný šum. Pokud se díváte na C, APL nebo na notaci (špatně napsaného) regulárního výrazu – máte právě tento dojem. "Ano, jednoduše, ale ne příliš jednoduše." Tohle je klíč. Stručně, ale nikoliv na úkor čitelnosti.

Stručnost a čitelnost jsou dobré. Ale to má společnou příčinu, na kterou často zapomínáme. A sice, že počítače existují pro lidi, nikoliv lidi pro počítače. Dříve to bylo naopak. Počítače stály milio-ny dolarů a spotřebovaly velké množství energie. Lidé se chovali, jako by počítač byl ztělesněním boha; programátoři se považovali za prosebníky. Čas počítače byl cennější než čas člověka. Když se počítače staly menšími a levnějšími, staly se velmi populárními vysokoúrovňové jazyky. Ačkoliv něco takového bylo zcela zbytečné z hlediska počítačů, bylo to velmi efektivní z pohledu lidského. Ruby je pouze dalším rozvojem těchto myšlenek. Někdo občas použije zkratku VHLL (Very High--Level Language). Ačkoliv tento termín není přesně definován, myslím si, že jeho použití zde je opodstatněné.

Počítač je předurčen k tomu, aby byl náš sluha, nikoliv pán. A jak Matz s oblibou často říká: chytrý sluha by měl vykonat složité úkoly prostřednictvím několika krátkých příkazů. To je správná myš-lenka pro celou historii počítačové vědy. Na začátku byl strojový kód, přičemž postupně jsme pře-cházeli k nízkoúrovňovým a poté i k vysokoúrovňovým jazykům. Mluvím zde o posunu od formy zaměřené na stroj k formě zaměřené na člověka. Podle mého názoru je Ruby výborným příkladem programování orientovaného na člověka.

Page 37: Ruby kompendium

36

Přesuňme se do jiné doby. V roce 1980 vyšla nádherná malá kniha s názvem The Tao of Program-ming (napsal ji Geoffrey James). Téměř každý řádek této knihy stojí za zmínku, ale já zopakuji pouze jednu věc. Program by měl dodržovat "pravidlo nejmenšího údivu". Co to znamená? Nic víc než to, že program by měl vždy reagovat na uživatele takový způsobem, aby ho co nejméně překva-pil. (V případě interpretu jazyka je uživatelem samozřejmě programátor.) Ačkoliv si nejsem zcela jist, zdali lze Geoffreymu Jamesovi připsat autorství tohoto pravidla, v jeho knize jsem se s ním setkal poprvé. V komunitě Ruby se jedná o velmi známé a velmi často citované pravidlo. Obvykle se ovšem nazývá jako princip nejmenšího překvapení (principle of least surprise, POLS).

Každopádně, ať už toto pravidlo nazýváte jakkoliv, rozhodně platí a bylo dokonce základní di-rektivou v průběhu vývoje jazyka Ruby. Je také užitečné pro ty, co vyvíjejí různé knihovny nebo uživatelská rozhraní. Jediným problémem je samozřejmě to, že různí lidé mohou být překvapeni různými věcmi; neexistuje žádná univerzální dohoda, jak by se měl nějaký objekt nebo metoda chovat. Matz říká, že "nejmenší překvapení" by se především mělo vztahovat na něj, protože on je návrhář jazyka. Čím více budete myslet jako on, tím méně vás Ruby překvapí. A ujišťuji vás, že napodobování Matze není špatný nápad pro spoustu z nás.

Nezávisle na tom, jak logicky je systém vybudován, vaše intuice potřebuje trénink. Každý progra-movací jazyk je svět sám pro sebe, se svými vlastními předpoklady. Totéž platí pro lidské jazyky. Když jsem se učil němčinu, dozvěděl jsem se, že všechna podstatná jména se píší s prvním písme-nem velkým kromě slova deutsch. Postěžoval jsem si na to svému profesorovi, konec konců, vždyť je to název jazyka, nebo ne? Usmál se a řekl: "Nebojuj s tím."

To, co mě naučil, bylo, abych nechal němčinu němčinou. To je dobrá rada pro každého, kdo k Ru-by přichází od nějakého jiného jazyka. Nechte Ruby, aby mohlo být Ruby. Nečekejte, že je to Perl, protože není. Nečekejte, že je to LISP nebo Smalltalk, také není. Na druhé straně Ruby obsahuje společné prvky se všemi třemi zmíněnými. Postupujte tedy podle svých očekávání, ale nebojujte s tím, že někdy nebudou splněna. (Pokud Matz neodsouhlasí, že se jedná o potřebnou změnu.)

Každý programátor dnes zná princip ortogonality (orthogonality). Předpokládejme, že máme fik-tivní dvojici os se se sadou srovnatelných jazykových entit na jedné a s vlastnostmi nebo schop-nostmi na druhé. Když řekneme slovo ortogonalita, obvykle tím myslíme, že prostor definovaný těmito osami, je tak "plný", jak jen to lze logicky udělat.

Cesta Ruby (Ruby way) se částečně snaží o ortogonalitu. Pole je v některých věcech podobné jako haš, protože operace na každém z nich mohou být podobné. Limitu je ovšem dosaženo, jakmile vstoupíte do oblasti, kde se tyto dvě věci odlišují. Matz říká, že "přirozenost" je hodnocena více než ortogonalita. Ale pro porozumění toho, co je přirozené a co nikoliv, musíme přemýšlet a psát kód.

Ruby se snaží být k programátorovi maximálně přátelský. Například jsou k dispozici aliasy pro vel-ké množství metod – jak size, tak i length vrátí počet jednotek pole. Pravopisně odlišné indexes a indices se obě odkazují na stejnou metodu. Ačkoliv někdo tohle považuje za komplikaci nebo rovnou za špatnou vlastnost, mně se to docela líbí.

Ruby dále usiluje o konzistenci a pravidelnost. Není na tom nic tajemného. V každém aspektu života toužíme po tom, aby věci byly pravidelné a vzájemně podobné. Obtížnější na tom je nau-

Page 38: Ruby kompendium

37

čit se, kdy tyto principy porušit. Například Ruby má ve zvyku ke jménům predikátových metod připojovat otazník (?). To je správné a dobré, protože to dělá kód přehlednějším a jmenný prostor snadněji ovladatelným. Ale podstatně diskutabilnější se může zdát obdobné použití vykřičníku (!) pro označení metod, které jsou "destruktivní" nebo "nebezpečné" v tom smyslu, že modifikuji příjemce. Je to diskutabilní z toho důvodu, že tímto způsobem nejsou označeny úplně všechny destruktivní metody. Neměli bychom být konzistentní?

Ne, skutečně bychom neměli. Některé z metod svého příjemce mění přirozeně (jako například Ar-ray metody replace a concat). Některé metody umožňují přiřazení do vlastnosti třídy, takže by-chom neměli přidávat vykřičník k názvům vlastností nebo ke znaménku rovnosti. Některé metody pravděpodobně mění příjemce, jako třeba metoda read – to by zase znamenalo příliš časté použití tohoto značení. Kdyby název každé destruktivní metody končil vykřičníkem, naše programy by brzy vypadaly jako reklamní brožury pro multi-level marketing.

Povšimli jste si jistého napětí mezi protichůdnými silami? Tendence porušit někdy každé pravidlo? Dovolte mi tohle zformulovat jako druhý Fultonův zákon: "Každé pravidlo má výjimku, kromě druhého Fultonova zákona". (Ano, jedná se o žertík, ale jen malý.)

To, co vidíme v Ruby, není hloupé uplatňování konzistence. Není to ani přísné lpění na jednodu-chých pravidlech. A vskutku – částí cesty Ruby je to, že se nejedná o přísný a nepoddajný přístup. V návrhu jazyka, jak jednou řekl Matz, byste měli "následovat své srdce".

Dalším aspektem filozofie Ruby je toto – neobávejte se změn za běhu programu a nemějte strach z dynamických věcí. Celý svět kolem vás je dynamický; proč byste měli programovat staticky? Ruby lze označit za jeden z nejvíce dynamických jazyků v historii.

Chtěl bych také zmínit následující aspekt – nebuďte otrokem výkonu. Pokud je výkon aplikace nepřijatelný, problém musí být pochopitelně vyřešen, ale za normálních okolností by to neměla být první věc, nad kterou budete přemýšlet. Pokud výkonnost není kritická, preferujte eleganci nad výkonností. Avšak pokud píšete nějakou knihovnu, která může být používána nepředvídatelnými způsoby, výkon může být rozhodující od počátku.

Když se podívám na Ruby, všímám si rovnováhy mezi odlišnými cíli návrhu, složité interakce při-pomínají problém mnoha těles ve fyzice. Umím si docela dobře představit, že to může být formová-no jako díla Alexandra Caldera. Je to pravděpodobně tato interakce sama o sobě, harmonie, kterou ztělesňuje filozofie Ruby, spíše než její individuální části. Programátoři vědí, že jejich řemeslo není jen věda a technologie, ale i umění. Váhám, zdali říci, že v informatice existuje nějaký spirituální aspekt, ale jen tak mezi námi, pravděpodobně ano. (Pokud jste nečetli knihu Zen and the Art of Motorcycle Maintenance, kterou napsal Robert Pirsig, doporučuji vám to udělat.)

Programovací jazyk Ruby vyvstal z lidského nutkání vytvořit věc, která bude užitečná a krásná. Program napsaný v Ruby by měl pramenit z toho samého Bohem daného zdroje. To je pro mě podstata cesty Ruby.

Page 39: Ruby kompendium

38

Page 40: Ruby kompendium

KAPITOLA 3Práce s regulárními výrazy

Zvolil bych vést ho v bludišti po vyšlapaných cestách...

– Any Lowell, Patterns

Síla regulárních výrazů jako výpočetního nástroje byla často podceňována. Od jejich prvotních teoretických začátků ve čtyřicátých letech si v letech šedesátých našly cestu k počítačovým sys-témům a odtud k různým nástrojům v operačním systému Unix. V devadesátých letech došlo ke zdomácnění regulárních výrazů (hlavně díky popularitě Perlu), takže už to nebyly těžko srozumi-telné pomůcky, se kterými pracovali pouze ti největší zasvěcenci.

Krása regulárních výrazů spočívá v tom, že téměř veškerou naši zkušenost můžeme chápat jako vzory. Jakmile máme vzory, které můžeme popsat, dokážeme je nacházet; dokážeme nacházet střípky reality, které odpovídají těmto vzorům, a dokážeme je ovlivnit podle své volby.

V době psaní tohoto textu dochází ve vývoji Ruby k mnoha změnám. Stávající engine regulárních výrazů bude nahrazen novým, který se nazývá Oniguruma. Tomuto enginu je věnována pozdější sekce "3.13 – Ruby a Oniguruma" této kapitoly. V kapitole 4 jsou pak uvedena specifika regulárních výrazů v souvislosti s mezinárodním použitím.

3.1 – Syntaxe regulárních výrazůTypický regulární výraz je ohraničen dvojicí lomítek. Regulární výrazy rovněž mohou být zapsány ve formě %r. Tabulka 3.1 ukazuje některé jednoduché příklady:

Tabulka 3.1. Základní regulární výrazy.

Regex Vysvětlení

/Ruby/ Odpovídá samostatnému slovu Ruby.

Page 41: Ruby kompendium

130 Kapitola 3 – Práce s regulárními výrazy

Regex Vysvětlení

/[Rr]uby/ Odpovídá Ruby nebo ruby.

/^abc/ Odpovídá abc na začátku řádku.

%r(xyz$) Odpovídá xyz na konci řádku.

%r|[0-9]*| Odpovídá sekvenci (nula nebo více) čísel.

Dále je možné použít modifikátory, které se skládají z jednoho písmene, jež je umístěno ihned za samotným regulárním výrazem. Tabulka 3.2 ukazuje nejběžnější modifikátory:

Tabulka 3.2. Modifikátory v regulárních výrazech.

Modifikátor Význam

i Ignorovat velikost písmen v regulárním výrazu.

o Vykonat nahrazení výrazu pouze jednou.

m Víceřádkový režim (tečka odpovídá novému řádku).

x Rozšířený regulární výraz (povoluje prázdné znaky a komentáře).

Další modifikátory jsou popsány v kapitole 4. Na konec tohoto úvodu o regulárních výrazech si ještě v tabulce 3.3 vyjmenujme nejběžnější symboly a notace.

Tabulka 3.3. Běžné notace používané v regulárních výrazech.

Zápis Význam

^ Začátek řádku nebo řetězce.

$ Konec řádku nebo řetězce.

. Libovolný znak kromě nového řádku (s výjimkou víceřádkového režimu).

\w Alfanumerické znaky.

\W Jiné než alfanumerické znaky.

\s Prázdný znak ( mezera, tabulátor, nový řádek atd.).

\S Neprázdné znaky.

\d Číslice (totéž jako [0-9]).

\D Jiné znaky než číslice.

\A Začátek řetězce.

\Z Konec řetězce nebo před začátkem nového řádku.

Page 42: Ruby kompendium

131Ruby – kompendium znalostí pro začátečníky i profesionály

Zápis Význam

\z Konec řetězce.

\b Hranice slova (pouze vně []).

\B Jiné znaky než hranice slova.

\b Znak backspace (pouze uvnitř []).

[ ] Kterýkoliv znak množiny.

* Žádný nebo libovolný počet výskytů předchozího znaku.

*? Žádný nebo libovolný počet výskytů předchozího znaku (non-greedy).

+ Jeden nebo libovolný počet výskytů předchozího znaku.

+? Jeden nebo libovolný počet výskytů předchozího znaku (non-greedy).

{m,n} od m do n výskytů předcházejícího podvýrazu.

{m,n}? od m do n výskytů předcházejícího podvýrazu (non-greedy).

? Žádný nebo jeden výskyt předchozího znaku.

| Alternativy (X|Y znamená X nebo Y).

(?= ) Pozitivní vyhlížení.

(?! ) Negativní vyhlížení.

() Skupina výrazů.

(?> ) Vnořený výraz.

(?: ) Skupina nezačleněná do výsledku.

(?imx - imx) Nastavení volby na on/off, platné od tohoto okamžiku.

(?imx – imx:expr) Nastavení volby na on/off, platné pro tento výraz.

(?# ) Komentář.

Znalost regulárních výrazů přináší modernímu programátorovi velké množství výhod. Protože vyčerpávající popis tohoto tématu je mimo rámec této knihy, doporučujeme vám se podívat do knihy Mastering Regular Expressions, kterou napsal Jeffrey Friedl.

Pro další informace o tématu tohoto oddílu se podívejte na sekce "3.13 – Ruby a Oniguruma".

3.2 – Kompilování regulárních výrazůRegulární výrazy mohou být zkompilovány pomocí metody Regexp.compile (která je ve skuteč-nosti synonymem pro Regexp.new). První parametr je povinný a může to být řetězec nebo regex.

Page 43: Ruby kompendium

132 Kapitola 3 – Práce s regulárními výrazy

(Povšimněte si, že pokud je parametrem regulární výraz s nějakým modifikátorem, nebude tento modifikátor platný pro právě zkompilovaný regulární výraz.)

pat1 = Regexp.compile("^foo.*") # /^foo.*/

pat2 = Regexp.compile(/bar$/i) # /bar/ (i není propagováno)

Druhý parametr, pokud je uveden, je obvykle jednou z následujících konstant nebo bitovým OR více z nich – Regexp::EXTENDED, Regexp::IGNORECASE a Regexp::MULTILINE. Navíc libovolná hodnota parametru, která není nil, bude mít za následek vytvoření regulárního výrazu, jenž nebu-de citlivý na velikost písmen (case-insensitive). Toto vám však nedoporučujeme praktikovat.

options = Regexp::MULTILINE || Regexp::IGNORECASE

pat3 = Regexp.compile("^foo", options)

pat4 = Regexp.compile(/bar/, Regexp::IGNORECASE)

Třetí parametr, pokud je specifikován, je jazykový parametr, který umožňuje podporu vícebajto-vých znaků. Akceptuje jakoukoliv ze čtyř následujících řetězcových hodnot:

"N" or "n" means None

"E" or "e" means EUC

"S" or "s" means Shift-JIS

"U" or "u" means UTF-8

Literály regulárních výrazů mohou být samozřejmě specifikovány bez volání new nebo compile, stačí je uzavřít mezi lomítka.

pat1 = /^foo.*/

pat2 = /bar$/i

Pro více informací nahlédněte do kapitoly 4.

3.3 – Ošetření speciálních znakůMetoda třídy Regexp.escape zajišťuje ošetření všech speciálních znaků, které jsou použity v regu-lárních výrazech. Jsou to znaky jako hvězdička, otazník a hranaté závorky.

str1 = "[*?]"

str2 = Regexp.escape(str1) # "\[\*\?\]"

Metoda Regexp.quote je pouze alias.

Page 44: Ruby kompendium

133Ruby – kompendium znalostí pro začátečníky i profesionály

3.4 – Používání kotev (anchors)Kotva je speciální výraz, který odpovídá pozici v řetězci (nikoliv znaku nebo sekvenci znaků). Jak uvidíme později, jedná se o jednoduchý případ předpokladu s nulovou délkou (zero-width asserti-on). Jinak řečeno – je to vzor, který v případě shody nespotřebovává žádné znaky z řetězce.

Nejběžnější kotvy byly již uvedeny na začátku této kapitoly. Nejjednodušší jsou ^ a $, které odpo-vídají začátku a konci řetězce.

string = "abcXdefXghi"

/def/ =~ string # 4

/abc/ =~ string # 0

/ghi/ =~ string # 8

/^def/ =~ string # nil

/def$/ =~ string # nil

/^abc/ =~ string # 0

/ghi$/ =~ string # 8

Nicméně – právě jsem vám řekl malou lež. Tyto kotvy vlastně neodpovídají začátku a konci řetězce, ale řádku. Pouvažujte nad stejnými vzory, které jsou aplikovány na podobný řetězec, jenž ovšem obsahuje nové řádky:

string = "abc\ndef\nghi"

/def/ =~ string # 4

/abc/ =~ string # 0

/ghi/ =~ string # 8

/^def/ =~ string # 4

/def$/ =~ string # 4

/^abc/ =~ string # 0

/ghi$/ =~ string # 8

Máme ovšem k dispozici i speciální kotvy \A a \Z, které skutečně odpovídají začátku a konci ře-tězce.

string = "abc\ndef\nghi"

/\Adef/ =~ string # nil

/def\Z/ =~ string # nil

/\Aabc/ =~ string # 0

/ghi\Z/ =~ string # 8

\z je totéž jako \Z, ovšem s tím rozdílem, že \Z ignoruje případný ukončující znak nového řádku, kdežto v případě \z musí být shoda explicitní.

string = "abc\ndef\nghi"

str2 << "\n"

Page 45: Ruby kompendium

134 Kapitola 3 – Práce s regulárními výrazy

/ghi\Z/ =~ string # 8

/\Aabc/ =~ str2 # 8

/ghi\z/ =~ string # 8

/ghi\z/ =~ str2 # nil

Dále je možné pomocí \b porovnávat hranici slova, nebo pomocí \B místo, které není hranicí slo-va. Příklady s gsub vám pomohou pochopit, jak to pracuje:

str = "this is a test"

str.gsub(/\b/,"|") # "|this| |is| |a| |test|"

str.gsub(/\B/,"-") # "t-h-i-s i-s a t-e-s-t"

Neexistuje žádný způsob, jak rozlišit počáteční a koncovou hranici slova.

3.5 – Používání kvantifikátorůVelkou částí regulárních výrazů je práce s nepovinnými položkami a opakováním. Položka násle-dující za otazníkem je nepovinná – může být uvedena, nebo může chybět; porovnání je závislé na zbytku regulárního výrazu. (Nemá smysl tohle aplikovat na kotvy, ale pouze na část vzoru (subpat-tern) s nenulovou délkou.)

pattern = /ax?b/

pat2 = /a[xy]?b/

pattern =~ "ab" # 0

pattern =~ "acb" # nil

pattern =~ "axb" # 0

pat2 =~ "ayb" # 0

pat2 =~ "acb" # nil

Pro entity je obvyklé, aby se nekonečně opakovaly (což můžeme specifikovat kvantifikátorem +). Například tento vzor odpovídá jakémukoliv celému kladnému číslu:

pattern = /[0-9]+/

pattern =~ "1" # 0

pattern =~ "2345678" # 0

Další běžný případ je vzor, který nenastane ani jednou, nebo nastane vícekrát. To samozřejmě můžete udělat pomocí + a ?. V následujícím fragmentu kódu porovnáváme řetězec Huzzah násle-dovaný žádným, nebo více vykřičníky:

pattern = /Huzzah(!+)?/ # Závorky jsou zde nutné

pattern =~ "Huzzah" # 0

pattern =~ "Huzzah!!!!" # 0

Nicméně existuje i lepší způsob. Toto chování popisuje kvantifikátor *.

Page 46: Ruby kompendium

135Ruby – kompendium znalostí pro začátečníky i profesionály

pattern = /Huzzah!*/ # * se aplikuje pouze na !

pattern =~ "Huzzah" # 0

pattern =~ "Huzzah!!!!" # 0

Co když chceme porovnávat číslo sociálního pojištění? K tomu poslouží tento vzor:

ssn = "987-65-4320"

pattern = /\d\d\d-\d\d-\d\d\d\d/

pattern =~ ssn # 0

Ale tento způsob není příliš čistý. Pojďme jednoznačně říci, kolik číslic je v každé skupině. Číslo v závorkách je kvantifikátor:

pattern = /\d{3}-\d{2}-\d{4}/

Tohle sice není ten nejkratší možný vzor, který lze napsat, nicméně je jednoznačný a docela čitelný. Jako oddělovač může být použita i čárka. Představte si telefonní číslo obyvatele Elbonie, které se skládá z části se třemi až pěti čísly a z části se třemi až sedmi čísly. Tady je odpovídající vzor:

elbonian_phone = /\d{3,5}-\d{3,7}/

První a poslední číslice jsou nepovinné (ačkoliv musíme mít jednu, nebo druhou):

/x{5}/ # pro

/x{5,7}/ # pro 5-7

/x{,8}/ # pro až 8

/x{3,}/ # pro alespoň 3

Tímto způsobem mohou být samozřejmě přepsány kvantifikátory ?, + a *:

/x?/ # totéž jako /x{0,1}/

/x*/ # totéž jako /x{0,}

/x+/ # totéž jako /x{1,}

Terminologie regulárních výrazů je plná barvitých personifikujících termínů jako greedy (chamti-vý), reluctant (zdráhavý), lazy (líný) a possessive (majetnický). Rozdíl mezi greedy/non-greedy je jeden z nejdůležitějších. Zamysleme se nad touto částí kódu. Můžete očekávat, že tento regex bude odpovídat "Where the", ovšem místo toho odpovídá nejdelší části řetězce "Where the sea meets the":

str = "Where the sea meets the moon-blanch'd land,"

match = /.*the/.match(str)

p match[0] # Zobrazí celou shodu:

# "Where the sea meets the"

Důvodem je, že operátor * je greedy (chamtivý) – při porovnání zkonzumuje tolik řetězce, kolik je možné pro nejdelší možnou shodu. Pomocí otazníku z něj můžeme udělat non-greedy:

Page 47: Ruby kompendium

136 Kapitola 3 – Práce s regulárními výrazy

str = "Where the sea meets the moon-blanch'd land,"

match = /.*?the/.match(str)

p match[0] # Zobrazí celou shodu:

# "Where the"

Tyto ukázky nám předvádí, že operátor * je standartně chamtivý, greedy (bez připojeného ?). Totéž platí pro kvantifikátory + a {m,n}, a také pro kvantifikátor ?. Nebyl jsem ovšem schopen vymyslet dobré příklady pro situaci s {m,n}? a ??. Pokud nějaké znáte, podělte se.

Pro více informací o kvantifikátorech nahlédněte do sekce "3.13 – Ruby a Oniguruma".

3.6 – Pozitivní a negativní vyhlíženíRegulární výraz je porovnáván vůči řetězci přímočarým způsobem (se zpětnou kontrolou, back-tracking, v případě potřeby). Z tohoto důvodu existuje v řetězci koncept "aktuálního umístění" – v podstatě se jedná o souborový ukazatel nebo kurzor.

Termín vyhlížení (lookahead) se odkazuje na konstrukci, která odpovídá části řetězce za aktuální pozicí. Jedná se o vzor s nulovou délkou, protože dokonce i tehdy, když je srovnání úspěšné, nedo-jde ke zkonzumování žádné části řetězce (to znamená, že aktuální pozice se nemění).

V následujícím příkladě bude řetězec "New World" odpovídat pouze v případě, pokud bude násle-dován slovem "Symphony" nebo "Dictionary" (třetí slovo není součástí porovnání):

s1 = "New World Dictionary"

s2 = "New World Symphony"

s3 = "New World Order"

reg = /New World(?= Dictionary| Symphony)/

m1 = reg.match(s1)

m.to_a[0] # "New World"

m2 = reg.match(s2)

m.to_a[0] # "New World"

m3 = reg.match(s3) # nil

A zde je ukázka negativního vyhlížení:

reg2 = /New World(?! Symphony)/

m1 = reg.match(s1)

m.to_a[0] # "New World"

m2 = reg.match(s2)

m.to_a[0] # nil

m3 = reg.match(s3) # "New World"

V tomto příkladu bude řetězec "New World" odpovídat pouze tehdy, pokud nebude následován slovem "Symphony".

Page 48: Ruby kompendium

137Ruby – kompendium znalostí pro začátečníky i profesionály

3.7 – Přístup ke zpětným referencímKaždá část regulárního výrazu, která je umístěna do závorek, je samostatně přístupnou částí vý-sledku porovnání. Tyto části jsou očíslovány, takže prostřednictvím těchto čísel se na ně můžete odkazovat. Existuje několik způsobů, jak to provést. Pojďme nejprve prozkoumat tradičnější (tzn. ošklivější) způsoby. Speciální globální proměnné jako $1, $2 atd. mohou být použity jako reference na shody.

str = "a123b45c678"

if /(a\d+)(b\d+)(c\d+)/ =~ str

puts "Matches are: '#$1', '#$2', '#$3'"

# Vytiskne: Matches are: 'a123', 'b45', 'c768'

end

Uvnitř substituce (jako například sub nebo gsub) nemohou být tyto proměnné použity.

str = "a123b45c678"

str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=#$1, 2nd=#$2, 3rd=#$3")

# "1st=, 2nd=, 3rd="

Možná se ptáte, proč nemůže fungovat? Odpověď je jednoduchá – argumenty pro sub jsou vyhod-noceny ještě předtím, než dojde k zavolání sub. Následující kód je ekvivalentní:

str = "a123b45c678"

s2 = "1st=#$1, 2nd=#$2, 3rd=#$3"

reg = /(a\d+)(b\d+)(c\d+)/

str.sub(reg,s2)

# "1st=, 2nd=, 3rd="

Tento kód názorně demonstruje, že hodnoty $1 až $3 nesouvisí s porovnáním, které bylo provede-no uvnitř volání sub. V tomto případě mohou být použity speciální kódy \1, \2 atd.

str = "a123b45c678"

str.sub(/(a\d+)(b\d+)(c\d+)/, '1st=\1, 2nd=\2, 3rd=\3')

# "1st=a123, 2nd=b45, 3rd=c768"

Povšimněte si, že v předchozím fragmentu kódu jsme použili apostrofy. Pokud použijete klasické uvozovky, budou obrácená lomítka interpretována jako osmičkové (octal) únikové sekvence:

str = "a123b45c678"

str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=\1, 2nd=\2, 3rd=\3")

# "1st=\001, 2nd=\002, 3rd=\003"

Způsob, jak tohle obejít, spočívá v použití dvojitých únikových sekvencí:

str = "a123b45c678"

Page 49: Ruby kompendium

138 Kapitola 3 – Práce s regulárními výrazy

str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=\\1, 2nd=\\2, 3rd=\\3")

# "1st=a123, 2nd=b45, 3rd=c678"

Dále je možné použít blokovou formu nahrazování, ve které mohou být použity globální proměn-né, viz následující fragment kódu:

str = "a123b45c678"

str.sub(/(a\d+)(b\d+)(c\d+)/) { "1st=#$1, 2nd=#$2, 3rd=#$3" }

# "1st=a123, 2nd=b45, 3rd=c678"

Při použití bloku tímto způsobem není možné použít speciální čísla s obrácenými lomítky uvnitř řetězce obklopeného klasickými uvozovkami (nebo dokonce apostrofy).

Zde je vhodný okamžik, abych se zmínil o možnosti nezachytávajících (noncapturing) skupin. Někdy můžete chtít na znaky pohlížet jako na skupinu, například z důvodu vytvoření nějakého rafinovaného regulárního výrazu, ale nepotřebujete se odkazovat na odpovídající hodnoty při poz-dějším použití. V těchto případech můžete použít nezachytávající skupinu (noncapturing group), která je označena prostřednictvím syntaxe (?:...):

str = "a123b45c678"

str.sub(/(a\d+)(?:b\d+)(c\d+)/, "1st=\\1, 2nd=\\2, 3rd=\\3")

# "1st=a123, 2nd=c678, 3rd="

V tomto fragmentu kódu byla druhá skupina "zapomenuta", takže to, co bylo třetí částí výsledku porovnání, se stalo druhou.

Osobně nemám rád notaci \1 stejně jako notaci $1. Ano – je pravda, že někdy jsou tyto notace po-hodlné, nicméně vůbec není nutné je používat, protože to můžeme dělat "hezčím", více objektově orientovaným, způsobem.

Metoda třídy Regexp.last_match vrací objekt třídy MatchData (stejně jako to dělá metoda match instance). Tento objekt poskytuje metody instance, které umožňují programátorovi zpřístupňovat zpětné reference. S objektem MatchData se manipuluje prostřednictvím notace s hranatými závor-kami (jako by se jednalo o pole shod). Speciální prvek 0 obsahuje kompletní text odpovídajícího řetězce. Následně se prvek n odkazuje na n-tou shodu:

pat = /(.+[aiu])(.+[aiu])(.+[aiu])(.+[aiu])/i

# Čtyři identické skupiny v tomto vzoru

refs = pat.match("Fujiyama")

# refs je nyní: ["Fujiyama","Fu","ji","ya","ma"]

x = refs[1]

y = refs[2..3]

refs.to_a.each {|x| print "#{x}\n"}

Povšimněte si skutečnosti, že objekt ref není opravdovým polem. Takže – když s ním chceme za-cházet jako s polem za použití iterátoru each, musíme ho prostřednictvím to_a (jak je v případu výše ukázáno) převést na pole.

Page 50: Ruby kompendium

139Ruby – kompendium znalostí pro začátečníky i profesionály

K tomu, abyste lokalizovali odpovídající podřetězec uvnitř původního řetězce, můžete použít i dal-ší techniky. Metody begin a end vrací počáteční a koncový offset shod. (Je důležité si uvědomit, že koncový offset je ve skutečnosti indexem znaku následujícího po posledním znaku shody při porovnání.)

str = "alpha beta gamma delta epsilon"

# 0.....5....0.....5.....0....5....

# (pro naše konvence)

pat = /(b[^ ]+ )(g[^ ]+ )(d[^ ]+ )/

# Tří slova, pro každé jedna shoda

refs = pat.match(str)

# "beta "

p1 = refs.begin(1) # 6

p2 = refs.end(1) # 11

# "gamma "

p3 = refs.begin(2) # 11

p4 = refs.end(2) # 17

# "delta "

p5 = refs.begin(3) # 17

p6 = refs.end(3) # 23

# "beta gamma delta"

p7 = refs.begin(0) # 6

p8 = refs.end(0) # 23

Metoda offset podobným způsobem vrací pole se dvěma čísly, což je počáteční a koncový offset tohoto srovnání. Pokračování předchozího příkladu:

range0 = refs.offset(0) # [6,23]

range1 = refs.offset(1) # [6,11]

range2 = refs.offset(2) # [11,17]

range3 = refs.offset(3) # [17,23]

Části řetězce před a po srovnání podřetězce mohou být získány prostřednictvím metod pre_match a post_match. Pokračování předchozího příkladu:

before = refs.pre_match # "alpha "

after = refs.post_match # "epsilon"

Page 51: Ruby kompendium

KAPITOLA 8Pole, haš a ostatní výčty

Všechny části by do sebe měly zapadat bez použití hrubé síly. Mějte na paměti, že díly, které právě skládáte, jste předtím rozebrali. Pokud je nemůžete znovu složit dohromady, musí to mít nějaký dů-vod. V žádném případě nepoužívejte kladivo.

– IBM, návod k obsluze, 1925

Jednoduché proměnné rozhodně nepostačují pro skutečné programování. Každý moderní jazyk podporuje nejenom složitější formy strukturovaných dat, ale také mechanismy pro vytváření no-vých abstraktních datových typů. Historicky jsou nejstarší a nejrozšířenější datovou strukturou pole. Ve Fortranu se jim říkalo indexové proměnné, a ačkoliv později došlo k určitým změnám, jejich základní myšlenka zůstala stejná ve všech programovacích jazycích.

V současné době se extrémně populárním programovacím nástrojem stává haš. Podobně jako v případě pole je i haš indexovanou kolekcí datových položek, ovšem na rozdíl od něj může být in-dexován libovolným objektem. (V Ruby – jako ve většině ostatních programovacích jazyků – jsou prvky pole přístupné prostřednictvím číselného indexu.)

Později v této kapitole se obecněji podíváme na modul Enumerable a na to, jakým způsobem pracuje. Jak pole, tak i haš tento modul zahrnují jako mix-in. Stejně tak mohou modul využívat i všechny třídy, pro které má taková funkcionalita smysl. Ale nepředbíhejme. Začneme s poli.

8.1 – Práce s poliPole v Ruby jsou indexována celými čísly od nuly, stejně jako v případě polí v jazyce C. Nicmé-ně zde veškerá podobnost končí. Pole v Ruby jsou dynamická. Při jejich vytváření je možné (ale nikoliv nezbytné) specifikovat jejich velikost. Po svém vytvoření mohou růst podle potřeby, bez jakéhokoliv zásahu programátora.

Page 52: Ruby kompendium

252 Kapitola 8 – Pole, haš a ostatní výčty

Pole v Ruby jsou heterogenní v tom smyslu, že mohou uchovávat různé datové typy, nikoliv pouze jeden. Ve skutečnosti je to tak, že ukládají reference na objekty (nikoliv objekty samotné), s výjim-kou případů bezprostřední hodnoty jako u Fixnum.

Pole si uchovává svou velikost, takže se nemusíme zdržovat jejím výpočtem nebo ji ukládat v ex-terní proměnné, kterou bychom museli udržovat synchronizovanou s daným polem. V praxi jsou iterátory často definovány tak, abychom zřídkakdy potřebovali znát velikost pole.

A konečně – třída Array v Ruby poskytuje polím mnoho užitečných funkcí pro přístup, hledání, zřetězení a další manipulace s poli. Ve zbývajících částech této sekce tuto třídu podrobně prozkou-máme a rozšíříme její vestavěnou funkčnost.

8.1.1 – Vytvoření a inicializace pole Speciální metoda třídy [] je používána pro vytvoření pole. Datové položky, které jsou uvedeny v hranatých závorkách, jsou použity po naplnění pole. Následující řádky kódu nám ukazují tři způsoby volání této metody. (Pole a, b a c budou naplněna stejnými hodnotami).

a = Array.[](1,2,3,4)

b = Array[1,2,3,4]

c = [1,2,3,4]

V Ruby existuje metoda třídy nazvaná new, která může akceptovat žádný, jeden nebo dva parame-try. První parametr je počáteční velikost pole (počet prvků). Druhý parametr je počáteční hodnota pro každý prvek:

d = Array.new # Vytvoří prázdné pole

e = Array.new(3) # [nil, nil, nil]

f = Array.new(3, "blah") # ["blah", "blah", "blah"]

Pečlivě se podívejte na poslední řádek předcházejícího fragmentu kódu. Obvyklou začátečnickou chybou je myslet si, že objekty v poli jsou odlišné. Ve skutečnosti se jedná o tři reference (odkazy) na stejný objekt. Pokud tedy z nějakého důvodu změníte objekt (místo jeho nahrazení za jiný ob-jekt), změníte všechny prvky pole. Abyste se mohli vyhnout tomuto chování, použijte blok. Potom bude tento blok vyhodnocen pro každý prvek, takže každý prvek bude jiný objekt:

f[0].capitalize! # f je nyní: ["Blah", "Blah", "Blah"]

g = Array.new(3) { "blah" } # ["blah", "blah", "blah"]

g[0].capitalize! # g je nyní: ["Blah", "blah", "blah"]

8.1.2 – Zpřístupnění a přiřazení prvků pole Reference na prvky a přiřazování se provádí prostřednictvím metod třídy [] a []= (v tomto pořa-dí). Každá metoda třídy akceptuje celočíselný parametr, dvojici celých čísel (začátek a délku) nebo rozsah. Záporný index počítá od konce pole (začíná číslem -1).

Page 53: Ruby kompendium

253Ruby – kompendium znalostí pro začátečníky i profesionály

Speciální metoda instance at slouží jako jednoduchá reference na prvek. Protože může přijímat pouze jediný celočíselný parametr, je o něco málo rychlejší.

a = [1, 2, 3, 4, 5, 6]

b = a[0] # 1

c = a.at(0) # 1

d = a[-2] # 5

e = a.at(-2) # 5

f = a[9] # nil

g = a.at(9) # nil

h = a[3,3] # [4, 5, 6]

i = a[2..4] # [3, 4, 5]

j = a[2...4] # [3, 4]

a[1] = 8 # [1, 8, 3, 4, 5, 6]

a[1,3] = [10, 20, 30] # [1, 10, 20, 30, 5, 6]

a[0..3] = [2, 4, 6, 8] # [2, 4, 6, 8, 5, 6]

a[-1] = 12 # [2, 4, 6, 8, 5, 12]

V následujícím příkladě si povšimněte, jak reference směřující za konec pole způsobí změnu jeho velikosti. Také si povšimněte, že podpole (subarray) může být nahrazeno větším množstvím prvků, než v něm původně bylo, což také způsobí změnu jeho velikosti.

k = [2, 4, 6, 8, 10]

k[1..2] = [3, 3, 3] # [2, 3, 3, 3, 8, 10]

k[7] = 99 # [2, 3, 3, 3, 8, 10, nil, 99]

A nakonec bychom se měli zmínit o tom, že pole, které je přiřazeno jedinému prvku, se ve skuteč-nosti vloží jako vnořené pole (na rozdíl od přiřazení do rozsahu):

m = [1, 3, 5, 7, 9]

m[2] = [20, 30] # [1, 3, [20, 30], 7, 9]

# na druhou stranu...

m = [1, 3, 5, 7, 9]

m[2..2] = [20, 30] # [1, 3, 20, 30, 7, 9]

Metoda slice slouží jako alias pro metodu []:

x = [0, 2, 4, 6, 8, 10, 12]

a = x.slice(2) # 4

b = x.slice(2,4) # [4, 6, 8, 10]

c = x.slice(2..4) # [4, 6, 8]

Page 54: Ruby kompendium

254 Kapitola 8 – Pole, haš a ostatní výčty

Speciální metody first a last vrací první a poslední prvek pole. Pokud je pole prázdné, bude vráceno nil, viz následující fragment kódu:

x = %w[alpha beta gamma delta epsilon]

a = x.first # "alpha"

b = x.last # "epsilon"

Předvedli jsme vám, že některé techniky pro odkazování na prvky skutečně vrací celé podpole. Existuje pár dalších způsobů, jak hromadně přistupovat k prvkům, takže se na ně teď podíváme.

Metoda values_at akceptuje seznam indexů a vrací pole skládající se pouze z těchto prvků. Toto může být použito tam, kde nelze použít rozsah (tzn. v situaci, ve které spolu nesousedí všechny prvky). V předchozí verzi Ruby byla metoda values_at nazvána jako indices, přičemž aliasem byl indexes. Tyto názvy v současné verzi Ruby nefungují.

x = [10, 20, 30, 40, 50, 60]

y = x.values_at(0, 1, 4) # [10, 20, 50]

z = x.values_at(0..2,5) # [10, 20, 30, 60]

8.1.3 – Nalezení velikosti pole Metoda length (nebo její alias size) vrací počet prvků v poli. (Jako vždy je tato hodnota o jednič-ku větší než index posledního prvku).

x = ["a", "b", "c", "d"]

a = x.length # 4

b = x.size # 4

Metoda nitems je úplně stejná, až na to, že nepočítá prvky nil:

y = [1, 2, nil, nil, 3, 4]

c = y.size # 6

d = y.length # 6

e = y.nitems # 4

8.1.4 – Porovnávání políPorovnávání polí je docela choulostivé, takže pokud to potřebujete udělat, dělejte to opatrně. Meto-da instance <=> se používá pro porovnání polí. Pracuje stejně jako v jiných kontextech – vrací buď -1 (znamenající "menší než"), 0 (znamenající "rovno"), nebo 1 (znamenající "větší než"). Na této metodě jsou závislé metody == a !=.

Pole jsou porovnána pěkně prvek po prvku. První dva prvky, které se nerovnají, určí nerovnost ce-lého procesu porovnání. (To znamená, že přednost je dána prvku, který je nejvíce vlevo, stejně jako je tomu v případě, když porovnáváme dvě velká celá čísla "od oka", postupně po jedné číslici).

Page 55: Ruby kompendium

255Ruby – kompendium znalostí pro začátečníky i profesionály

a = [1, 2, 3, 9, 9]

b = [1, 2, 4, 1, 1]

c = a <=> b # -1 (znamená a < b)

Pokud se všechny prvky rovnají, pak se rovnají i pole. Pokud je jedno pole delší než druhé, při-čemž do délky kratšího pole jsou si rovny, pak je delší pole považováno za větší.

d = [1, 2, 3]

e = [1, 2, 3, 4]

f = [1, 2, 3]

if d < e # false

puts "d is less than e"

end

if d == f

puts "d equals f" # Vytiskne "d equals f"

end

Protože třída Array není šířena modulem Comparable, nejsou pro třídu definovány běžné operá-tory <, >, <= a >= . Ale pokud chcete, můžete je jednoduše nadefinovat sami:

class Array

def <(other)

(self <=> other) == -1

end

def <=(other)

(self < other) or (self == other)

end

def >(other)

(self <=> other) == 1

end

def >=(other)

(self > other) or (self == other)

end)

end

Pokud ovšem sami rozšíříte Array o modul Comparable, bude všechno jednodušší:

class Array

include Comparable

end

Page 56: Ruby kompendium

256 Kapitola 8 – Pole, haš a ostatní výčty

Jakmile máme definované tyto nové operátory, můžeme je použít tak, jak byste očekávali:

if a < b

print "a < b" # Vytiskne "a < b"

else

print "a >= b"

end

if d < e

puts "d < e" # Vytiskne "d < e"

end

Je možné, že výsledkem porovnání polí bude porovnání dvou prvků, pro které operátor <=> není definován nebo nemá význam. Následující kód způsobí chybu za běhu (TypeError), protože po-rovnání 3 <=> "x" je problematické:

g = [1, 2, 3]

h = [1, 2, "x"]

if g < h # Chyba!

puts "g < h" # Žádný výstup

end

Nicméně – v případě, že stále nejste zmateni, rovnost a nerovnost budou v tomto případě stále pracovat. To proto, že dva objekty různého typu jsou přirozeně považovány za nerovné, i když nemůžeme říct, který z nich je větší, nebo menší než druhý.

if g != h # Bez problémů.

puts "g != h" # Vytiskne "g != h"

end

A konečně – je možné, že dvě pole, která obsahují neodpovídající si datové typy, budou přeci jen porovnána pomocí operátorů < a >. V takovém příkladě dostaneme výsledek ještě předtím, než narazíme na neporovnatelné prvky:

i = [1, 2, 3]

j = [1, 2, 3, "x"]

if i < j # Bez problémů.

puts "i < j" # Vytiskne "i < j"

end

8.1.5 – Řazení políNejjednodušším způsobem, jak seřadit pole, je použít zabudovanou metodu sort:

words = %w(the quick brown fox)

list = words.sort # ["brown", "fox", "quick", "the"]

Page 57: Ruby kompendium

257Ruby – kompendium znalostí pro začátečníky i profesionály

# Nebo takto:

words.sort! # ["brown", "fox", "quick", "the"]

Tato metoda předpokládá, že všechny prvky v poli jsou porovnatelné s ostatními. Pomíchané pole, jako třeba [1, 2, "three", 4], normálně vrací chybu typu. V případě, jako je tento, můžete použít blokovou formu volání stejné metody. Následující příklad předpokládá, že existuje alespoň metoda to_s pro každý prvek (pro převedení na string):

a = [1, 2, "three", "four", 5, 6]

b = a.sort {|x,y| x.to_s <=> y.to_s}

# b is now [1, 2, 5, 6, "four", "three"]

Je samozřejmé, že takové řazení (v tomto případě závisející na ASCII) nemusí být smysluplné. Po-kud máte takové různorodé pole, zeptejte se prvně sami sebe, proč jej vlastně řadíte nebo proč vůbec ukládáte objekty různých typů.

Tato technika funguje, protože blok vrací celé číslo (-1, 0 nebo 1) při každém volání. Když je vráce-no číslo -1, znamená to, že x je menší než y; dva prvky jsou zaměněny. Takže pro řazení v sestup-ném pořadí můžeme jednoduše prohodit pořadí porovnání:

x = [1, 4, 3, 5, 2]

y = x.sort {|a,b| b <=> a} # [5, 4, 3, 2, 1]

Blok může být také použit pro komplexnější řazení. Předpokládejme, že chceme seřadit seznam knih a filmových titulů tímto způsobem – ignorovat velikost písmen, zcela ignorovat mezery a ig-norovat jistý druh vložené interpunkce. Zde prezentujeme jednoduchý příklad. (Věříme, že jak učitelé angličtiny, tak i programátoři budou zmateni z tohoto druhu řazení podle abecedy).

titles = ["Starship Troopers",

"A Star is Born",

"Star Wars",

"Star 69",

"The Starr Report"]

sorted = titles.sort do |x,y|

# Smaže členy (a, an, the)

a = x.sub(/^(a |an |the )/i, "")

b = y.sub(/^(a |an |the )/i, "")

# Smaže mezery a interpunkci

a.delete!(" .,-?!")

b.delete!(" .,-?!")

# Převede na velká písmena

a.upcase!

b.upcase!

# Porovná a a b

a <=> b

Page 58: Ruby kompendium

258 Kapitola 8 – Pole, haš a ostatní výčty

end

# Výsledek je nyní:

# [ "Star 69", "A Star is Born", "The Starr Report"

# "Starship Troopers", "Star Wars"]

Tento příklad není příliš použitelný a určitě by mohl být napsán kompaktněji. Pointa je v tom, že při porovnání dvou operandů může být na nich vykonán libovolně složitý soubor operací. (Nicmé-ně si povšimněte, že originální operandy jsme nechali nedotčeny; pracovali jsme s jejich kopiemi.) Tato metoda může být užitečná v mnoha situacích – například při řazení podle několika klíčů nebo podle klíčů, které jsou vypočítány až při běhu programu.

V novějších verzích Ruby obsahuje modul Enumerable metodu sort_by, která je samozřejmě součástí Array. Je velmi důležité ji pochopit. Metoda sort_by používá to, co lidé od Perlu nazývají Schwartzovou transformací (podle Randala Schwartze). Místo abychom řadili podle prvků samot-ných, aplikujeme na ně nějakou funkci nebo mapování a řadíme podle výsledku.

Představte si, že máte seznam souborů, který chcete seřadit podle velikosti. Přímočarý způsob by vypadal nějak takto:

files = files.sort {|x,y| File.size(x) <=> File.size(y) }

Nicméně jsou zde dva problémy. Zaprvé – vypadá to trochu ukecaně. Měli bychom být schopni tento fragment kódu trochu zestručnit. Zadruhé – dochází k vícenásobnému přístupu na disk, což je docela drahá operace (ve srovnání s jednoduchými operacemi v paměti). Čím více takových operací, tím hůře. Použitím metody sort_by ovšem vyřešíme oba tyto problémy najednou. Zde je správný způsob, jak to udělat:

files = files.sort_by {|x| File.size(x) }

V předchozím fragmentu kódu je každý klíč počítán pouze jednou. Výsledek je následně interně uložen jako dvojice klíč/data. Ačkoliv v případě menších polí může mít tento způsob efektivitu naopak nižší, může zvýšení čitelnosti kódu i tak stát za to.

Metoda sort_by! neexistuje. Nicméně si můžete vždy napsat svou vlastní.

A co řazení na základě více klíčů? Představte si, že máte pole objektů, která potřebujete seřadit na základě těchto tří atributů – jméno, věk a výška. Skutečnost, že pole jsou vzájemně porovnatelná, znamená, že následující technika bude funkční:

list = list.sort_by {|x| [x.name, x.age, x.height] }

Je samozřejmé, že nejste omezení pouze na jednoduché prvky pole, které byly použity ve výše uve-dených příkladech. Prvkem pole může být libovolný výraz.

Page 59: Ruby kompendium

259Ruby – kompendium znalostí pro začátečníky i profesionály

8.1.6 – Výběr z pole na základě kritériaNěkdy chceme lokalizovat prvek (nebo prvky) v poli podobným způsobem, jako se dotazujeme na tabulky v databázi. Existuje několik způsobů, jak to udělat. Všechny níže nastíněné způsoby pochází z modulu Enumerable.

Metoda detect nalezne nanejvýš jediný prvek. Akceptuje blok (ve kterém jsou prvky procházeny sekvenčně), přičemž vrací první prvek, pro nějž je výraz v bloku pravdivý.

x = [5, 8, 12, 9, 4, 30]

# Najde první násobek 6

x.detect {|e| e % 6 == 0 } # 12

# Najde první násobek 7

x.detect {|e| e % 7 == 0 } # nil

Objekty v poli mohou být samozřejmě libovolně složité, stejně jako test v bloku.

Metoda find je synonymem pro metodu detect. Metoda find_all je varianta, která vrací i více prvků. Metoda select je pak synonymem pro metodu find_all:

# Pokračování předchozího příkladu...

x.find {|e| e % 2 == 0} # 8

x.find_all {|e| e % 2 == 0} # [8, 12, 4, 30]

x.select {|e| e % 2 == 0} # [8, 12, 4, 30]

Metoda grep volá operátor rovnosti case pro porovnání každého prvku s daným vzorem. V nej-jednodušší formě vrací pole obsahující shodující se prvky. Protože je použit operátor rovnosti case (===), vzor nemusí být regulárním výrazem. ( Název grep samozřejmě pochází ze světa Unixu a historicky souvisí s příkazem g/re/p).

a = %w[January February March April May]

a.grep(/ary/) # ["January, "February"]

b = [1, 20, 5, 7, 13, 33, 15, 28]

b.grep(12..24) # [20, 13, 15]

Existuje bloková forma, která transformuje každý výsledek ještě předtím, než ho uloží do pole. Vý-sledné pole pak obsahuje návratové hodnoty bloku, nikoliv hodnoty, které byly do bloku předány:

# Pokračování předchozího příkladu...

# Pojďme uložit délky řetězců

a.grep(/ary/) {|m| m.length} # [7, 8]

# Pojďme umocnit každou hodnotu

b.grep(12..24) {|n| n*n} # {400, 169, 225}

Metoda reject je doplňková (komplementární) k metodě select. Vylučuje každý prvek, který blok vyhodnotí jako true. Je také definována metoda reject!:

Page 60: Ruby kompendium

KAPITOLA 12 Grafická rozhraní pro Ruby

Není nic horšího, než ostré zobrazení neurčitého konceptu.

– Ansel Adams

Nejsou žádné pochybnosti o tom, že se nacházíme ve věku grafického uživatelského rozhraní ( GUI). Je zřejmé, že v budoucnu bude preferovaným způsobem interakce s počítačem nějaká forma gra-fického rozhraní. Nemyslím si ovšem, že by v dalším desetiletí měl vymizet příkazový řádek – ten má jisté své místo ve světě. Ale dokonce i hackeři ze staré školy (kteří by raději používali příkaz cp –R než rozhraní drag-and-drop) někdy používají GUI, když je to vhodné.

S programováním grafiky se neodmyslitelně pojí různé významné problémy. První problém po-chopitelně spočívá v návrhu smysluplného a použitelného prostředí programu. V návrhu uživatel-ského rozhraní nemá obrázek vždy cenu tisíce slov. Tato kniha rozhodně nemůže pojmout celou problematiku tvorby GUI. Naším cílem zde není řešit ergonomii, estetiku či psychologii.

Druhým obvyklým problémem je to, že programování grafiky je složitější. Musíme si totiž dělat starosti o velikost, tvary, umístění a chování všech ovládacích prvků, které mohou být zobrazeny na obrazovce, jež mohou být ovládány myší a/nebo klávesnicí.

Třetí potíž spočívá v tom, že různé počítačové kultury mají odlišné představy o tom, co je systém oken a jak by měl být implementován. Nesourodost mezi těmito systémy musí být prvně vyzkou-šena, aby mohla být následně plně pochopena. Nejeden programátor se pokoušel vytvořit nějaký multiplatformní nástroj, aby nakonec zjistil, že tou nejtěžší částí je přizpůsobení GUI.

Tato kapitola vám s těmito problémy příliš pomoci nemůže. Maximum, co pro vás mohu udělat, je poskytnout úvod k několika populárním GUI systémům, a nabídnout několik cenných rad a po-střehů. Převážná část této kapitoly je věnována systémům Tk, GTK+, FOX a Qt. Ačkoliv se jedná o nejvíce rozšířené systémy, je docela slušná šance, že se zeptáte: "Proč v této kapitole nebyl popsán (sem vložte název vašeho oblíbeného GUI)?"

Důvodů může být několik. Jedním důvodem je omezený prostor, protože tato kniha primárně není o grafickém rozhraní. Dalším důvodem může být to, že váš oblíbený systém nemá natolik vyspělé

Page 61: Ruby kompendium

446 Kapitola 12 – Grafická rozhraní pro Ruby

Ruby rozhraní, aby se dal použít. A posledním důvodem může být fakt, že ne všechny systémy uživatelského rozhraní jsou si rovny. Tato kapitola se tudíž snaží pokrýt pouze ty, které jsou nejdů-ležitější a nejvíce vyspělé. O zbytku se zmíníme pouze krátce.

12.1 – Ruby/TkKořeny Tk sahají až do roku 1988 (pokud počítáme různé předběžně uvolněné verze). Dlouho bylo považováno za společníka k programovacímu jazyku Tcl, nicméně Tk se začalo používat i s něko-lika dalšími jazyky, včetně Perlu a Ruby. Pokud by Ruby někdy mělo nějaké nativní GUI, pravdě-podobně by se jednalo o Tk. V době psaní této knihy je velmi široce používáno, přičemž některé verze Ruby lze stáhnout včetně Tk.

Předchozí zmínka o Perlu není úplně bezdůvodná. Tk pro Ruby a Perl je dost podobné na to, aby materiál pro Perl/Tk mohl být z velké části použitelný i pro Ruby/Tk. Doporučujeme si sehnat zajímavou knihu Learning Perl/Tk, ISBN 1565923146 od Nancy Walsh.

12.1.1 – PřehledV roce 2001 bylo Tk pravděpodobně nejvíce používaným GUI pro Ruby. Bylo první, které bylo dostupné a dlouho bylo i součástí standardní instalace Ruby. I když v současnosti už není tak po-pulární jako předtím, je stále široce používáno. Někteří vývojáři říkají, že na Tk je patrný jeho věk – pro ty, co mají rádi čisté, objektově orientované, rozhraní, může být rozhraní Tk trochu zklamání. Ale má výhodu v popularitě, přenositelnosti a stabilitě.

Jakákoliv aplikace Ruby/Tk musí použít příkaz require pro načtení rozšíření tk. Poté je aplikační rozhraní sestavováno postupně, počínaje nějakým druhem kontejneru a ovládacích prvků, kterými je tento kontejner naplněn. Nakonec je provedeno volání Tk.mainloop (tato metoda zachytává všechny události – jako pohyb myši a stisky tlačítek – a zpracovává je).

require "tk"

# Sestavení aplikace...

Tk.mainloop

Stejně jako ve většině (nebo rovnou ve všech) okenních systémech jsou i zde ovládací prvky Tk nazývány widgety. Tyto widgety jsou obvykle dohromady seskupeny v kontejnerech. Kontejner na nejvyšší úrovni je nazýván kořen. Ačkoliv tento kořen není nutné specifikovat explicitně, rozhodně je to vhodné.

Každá třída widgetu je pojmenována na základě svého názvu ve světě Tk (připojením slova Tk na začátek). Proto widget Frame odpovídá třídě TkFrame.

Widgety jsou instanciovány prostřednictvím metody new. První parametr specifikuje kontejner, do kterého je widget umístěn. Pokud je vynechán, předpokládá se kořen.

Page 62: Ruby kompendium

447Ruby – kompendium znalostí pro začátečníky i profesionály

Nastavení, které je použito pro instanciaci widgetu, může být specifikováno dvěma způsoby. Prv-ní způsob (jako v Perlu) spočívá v předání haše s vlastnostmi a hodnotami. (Připomínáme, že se jedná o trik syntaxe Ruby – haš, který je vložen jako poslední nebo jako jediný parametr, může mít závorky vynechány.)

my_widget = TkSomewidget.new( "borderwidth" => 2, "height" => 40 ,

"justify" => "center" )

Další způsob spočívá v předání bloku do konstruktoru, který bude vyhodnocen pomocí instan-ce_eval. Uvnitř tohoto bloku můžeme volat metody pro nastavení vlastností widgetu (prostřed-nictvím metod, které mají stejné názvy jako vlastnosti). Mějte na paměti, že blok kódu je vyhod-nocován v kontextu objektu, nikoliv volajícího. To například znamená to, že proměnné instance volajícího nemohou být odkazovány uvnitř tohoto bloku.

my_widget = TkSomewidget.new do

borderwidth 2

height 40

justify "center"

end

V Tk jsou dostupní celkem tři správci rozložení – všichni slouží pro účely řízení relativní velikos-ti a umístění widgetu na obrazovce. První (a nejčastěji používaný) je pack. Další dva jsou grid a place: správce grid je sofistikovaný, ale poněkud náchylný k chybám; správce place je ze všech nejjednodušší, protože vyžaduje absolutní hodnoty pro umístění widgetu. Ve všech příkladech této kapitoly budeme používat pouze správce pack.

12.1.2 – Jednoduchá aplikace s oknyV této sekci si předvedeme nejjednodušší možnou aplikaci – jednoduchou aplikaci kalendáře, kte-rá zobrazí aktuální datum. Abychom se dostali do programovací nálady, začneme explicitním vy-tvořením root a umístěním widgetu Label dovnitř něj.

require "tk"

root = TkRoot.new() { title "Today's Date" }

str = Time.now.strftime("Today is \n%B %d, %Y")

lab = TkLabel.new(root) do

text str

pack("padx" => 15, "pady" => 10, "side" => "top")

end

Tk.mainloop

V předchozím kódu vytváříme kořen, nastavujeme řetězec s datem a vytváříme popisek (label). Při vytváření popisku nastavujeme text, který je hodnotou str a voláme pack, který tohle všechno

Page 63: Ruby kompendium

448 Kapitola 12 – Grafická rozhraní pro Ruby

úhledně uspořádá. Správci pack sdělujeme, aby nastavil výplň 15 pixelů horizontálně a 10 pixelů vertikálně, a žádáme, aby text byl centrován na střed. Obrázek 12.1 ukazuje vzhled aplikace.

Obrázek 12.1. Jednoduchá Tk aplikace.

Jak jsme už zmínili výše, vytvoření popisu (label) může vypadat takto:

lab = TkLabel.new(root) do

text str

pack("padx" => 15, "pady" => 10, "side" => "top")

end

Použitými jednotkami, které jsou v tomto příkladě specifikovány pro padx a pady, jsou standardně pixely. Pochopitelně, můžeme pracovat i v jiných jednotkách – k hodnotě stačí připojit požadova-nou jednotku. Taková hodnota se sice nyní stává řetězcem, ale protože to Ruby/Tk vůbec nevadí, nevadí to ani nám. Dostupné jednotky jsou centimetry (c), milimetry (m), palce (i) a body (p). Podívejte se na následující příklad:

pack("padx" => "80m")

pack("padx" => "8c")

pack("padx" => "3i")

pack("padx" => "12p")

Atribut side v tomto případě vůbec nic neovlivňuje, protože jsme ji nastavili na výchozí hodnotu. Pokud změníte velikost okna aplikace, povšimnete si, že text se "přilepí" do horní části oblasti, ve které je umístěn. Jak asi tušíte, další možné hodnoty jsou right, left a bottom.

Metoda pack poskytuje několik dalších voleb, které řídí umístění widgetu na obrazovce:

� Volba fill specifikuje, zdali widget vyplňuje svůj přidělený obdélník (v horizontálním a/nebo vertikálním směru). Možné hodnoty jsou x, y, both a none (výchozí je none).

� Volba anchor bude ukotvovat widget uvnitř přiděleného obdélníku na základě "kompasové" notace. Výchozí je center; další možné hodnoty jsou n, s, e, w, ne, nw, se a sw.

� Volba in zabaluje widget s ohledem na nějaký jiný kontejner, než je rodičovský. Výchozí je samozřejmě rodič.

� Volby before a after mohou být použity pro změnu pořadí widgetu. To je občas užitečné, protože widgety nemusí být vytvořeny (na rozdíl od jejich umístění na obrazovce) v nějakém konkrétním pořadí.

Celkově můžeme říci, že Tk je docela flexibilní z hlediska umístění widgetů na obrazovce. Prostu-dujte si dokumentaci pro další informace.

Page 64: Ruby kompendium

449Ruby – kompendium znalostí pro začátečníky i profesionály

12.1.3 – Práce s tlačítkyJedním z nejběžnějších widgetů v jakémkoliv GUI je stisknutelné tlačítko (nebo jednoduše tlačít-ko). Jak asi očekáváte, v aplikacích Ruby/Tk umožňuje použití tlačítek třída TkButton. V případě složitějších aplikací obvykle vytváříme rámce, které obsahují různé widgety, jež poté umisťujeme na obrazovku. V těchto kontejnerech mohou být umístěny i widgety ve formě tlačítka.

Pro tlačítko musíme specifikovat nejméně tři následující vlastnosti:

� Text tlačítka.

� Příkaz spojený s tlačítkem. Tento příkaz bude proveden, když je tlačítko stisknuto.

� Pozice tlačítka uvnitř jeho kontejneru.

Tady je malá ukázka:

btn_OK = TkButton.new do

text "OK"

command (proc { puts "The user says OK." })

pack("side" => "left")

end

V tomto fragmentu kódu vytváříme nové tlačítko a přiřazujeme nový objekt do proměnné btn_OK. Do konstruktoru předáváme blok, ačkoliv bychom mohli použít i haš. V tomto případě používáme víceřádkovou formu (kterou osobně preferuji, ačkoliv v praxi můžete do jednoho řádku nacpat tolik kódu, kolik jenom chcete. Zapamatujte si, že blok je prováděn pomocí instance_eval, což znamená, že je vyhodnocen v kontextu objektu (v tomto případě nového objektu TkButton).

Text, který je specifikován ve formě parametru v metodě text, bude umístěn na samotné tlačítko. Může to být několik slov nebo dokonce i několik řádků. Použití metody pack už jsme viděli. Není zajímavá, ačkoliv je nepostradatelná, pokud má být widget vůbec viditelný. Zajímavou částí kódu je metoda command, která přijímá objekt Proc a připojuje ho k tlačítku. V této kapitole budeme často používat metodu lambdaproc z modulu Kernel, která zkonvertuje blok na objekt Proc.

Akce, kterou zde provádíme, je ovšem dosti hloupá. Když uživatel stiskne tlačítko, bude provedeno negrafické puts. To znamená, že výstup půjde do okna příkazového řádku, ze kterého byl spuštěn program (nebo do pomocného okna konzoly).

Nyní vám nabídneme lepší příklad. Výpis 12.1 ukazuje aplikaci termostatu, která bude zvyšovat nebo snižovat zobrazenou hodnotu (čímž nám poskytne iluzi toho, že můžeme ovládat teplotu v místnosti). Vysvětlení následuje, jako vždy, až za výpisem.

Výpis 12.1. Simulovaný termostat.

require 'tk'

# Běžné nastavení...

Top = { 'side' => 'top', 'padx'=>5, 'pady'=>5 }

Page 65: Ruby kompendium

450 Kapitola 12 – Grafická rozhraní pro Ruby

Left = { 'side' => 'left', 'padx'=>5, 'pady'=>5 }

Bottom = { 'side' => 'bottom', 'padx'=>5, 'pady'=>5 }

temp = 74 # Počáteční teplota...

root = TkRoot.new { title "Thermostat" }

top = TkFrame.new(root) { background "#606060" }

bottom = TkFrame.new(root)

tlab = TkLabel.new(top) do

text temp.to_s

font "{Arial} 54 {bold}"

foreground "green"

background "#606060"

pack Left

end

TkLabel.new(top) do # symbol"stupně"

text "o"

font "{Arial} 14 {bold}"

foreground "green"

background "#606060"

# Přidat anchor-north do hash

pack Left.update({ 'anchor' => 'n' })

end

TkButton.new(bottom) do

text " Up "

command proc { tlab.configure("text"=>(temp+=1).to_s) }

pack Left

end

TkButton.new(bottom) do

text "Down"

command proc { tlab.configure("text"=>(temp-=1).to_s) }

pack Left

end

top.pack Top

bottom.pack Bottom

Tk.mainloop

Page 66: Ruby kompendium

451Ruby – kompendium znalostí pro začátečníky i profesionály

Pomocí toho kódu vytváříme dva rámce. Horní rámec ovládá pouze zobrazení. Teplotu ve Fahren-heitech zobrazujeme prostřednictvím velkého písma (pro znak stupně používáme malé, strategicky umístěné, písmenko "o"). Spodní rámec je pak použit pro tlačítka "nahoru" a "dolů".

Povšimněte si, že používáme několik nových vlastností pro objekt TkLabel. Metoda font speci-fikuje písmo a velikost textu. Hodnota řetězce je závislá na platformě. Ta, která je ukázána zde, je platná pro systém Windows. V systému Unix by se obvykle jednalo o dlouhý a těžkopádný název fontu ve stylu -Adobe-Helvetica-Bold-R-Normal–*-120-*-*-*-*-*-*.

Metoda foreground nastavuje barvu textu. V našem příkladu vkládáme řetězec "green", který má v útrobách Tk předem definovaný význam. (Pokud chcete zjistit, zdali je v Tk předdefinována nějaká barva, nejsnazší cestou je to vyzkoušet). Barva pozadí se nastavuje pomocí metody back-ground. V tomto případě specifikujeme barvu odlišně, protože ji chceme v typickém hexadecimál-ním formátu (řetězec "#606060" reprezentuje pěknou šedou barvu).

Abychom nekomplikovali pěkný jednoduchý design, nepřidali jsme do něj žádné tlačítko "Konec". Aplikaci můžete jako obvykle zavřít kliknutím na ikonu Close v pravém horním rohu okna.

V příkazech pro tlačítka je použita metoda configure, kterou se mění obsah horního popisku při zvyšování nebo snižování aktuální teploty. Jak už jsem zmínil dříve, tímto způsobem může být za běhu změněna jakákoliv vlastnost (přičemž její efekt se okamžitě projeví na monitoru).

Nyní zmíníme dva další triky, které můžete s textovými tlačítky provést. Metoda justify přijímá parametr (left, right nebo center), kterým můžete specifikovat umístění textu na tlačítku (vý-chozí hodnota je center). Tlačítko může obsahovat i několik řádků textu – metoda wraplength specifikuje sloupec, ve kterém dojde k zalomení textu.

Styl tlačítka může být změněn prostřednictvím metody relief, která mu dá 3D vzhled. Parame-trem této metody musí být jeden z těchto řetězců: flat, groove, raised, ridge (výchozí), sunken nebo solid. Metody width a height explicitně nastavují velikost tlačítka. Je také dostupná meto-da borderwidth. Pro další volby – kterých je opravdu mnoho – se podívejte do dokumentace.

Pojďme se nyní podívat na některé další příklady. Naše nová tlačítka na sobě nebudou mít text, ale pouze obrázek. Pro tyto účely jsem předem vytvořil dvojici obrázků ve formátu GIF, které obsa-hují šipky nahoru a dolů. (Tyto grafické soubory jsou pojmenovány vskutku intuitivně – up.gif a down.gif.) Pro získání reference na každý z nich můžeme použít třídu TkPhotoImage. Poté mů-žeme tyto reference použít při instanciaci tlačítek.

up_img = TkPhotoImage.new("file"=>"up.gif")

down_img = TkPhotoImage.new("file"=>"down.gif")

TkButton.new(bottom) do

image up_img

command proc { tlab.configure("text"=>(temp+=1).to_s) }

pack Left

end

Page 67: Ruby kompendium

452 Kapitola 12 – Grafická rozhraní pro Ruby

TkButton.new(bottom) do

image down_img

command proc { tlab.configure("text"=>(temp-=1).to_s) }

pack Left

end

Tímto kódem jednoduše nahraďte odpovídající řádky v našem prvním příkladu s termostatem. S výjimkou změněných tlačítek je všechno stejné. Obrázek 12.2 ukazuje aplikaci termostatu.

Obrázek 12.2. Simulace termostatu (s grafickými tlačítky).

12.1.4 – Práce s textovými poliVstupní textové pole může být zobrazeno a spravováno použitím widgetu TkEntry. Jak asi očeká-váte, pro ovládání velikosti, barvy a chování tohoto widgetu je dostupných mnoho voleb; my vám nabídneme jeden rozsáhlý příklad, který ilustruje několik z nich. Vstupní pole je užitečné pouze v případě, kdy z něj dokážeme získat hodnotu, která byla do něj vložena. Pole bude vázáno na pro-měnnou TkVariable (jak uvidíme později sami), ačkoliv může být použita i metoda get.

Předpokládejme, že chceme vytvořit telnet klienta, který bude přijímat čtyři informace – hosta, číslo portu (standardně 23), uživatelské ID a heslo. Přidáme také dvě tlačítka pro operaci "přihláše-ní" a"zrušení". Následující kus kódu provádí nějaké triky s rámci pro seřazení věcí a pro jejich lepší vzhled. Kód není napsán přenositelným způsobem a skutečný Tk guru by tento přístup odsoudil. Ale protože se jedná pouze o ilustrační ukázku, tato skutečnost nás vůbec netrápí. Na obrázku 12.3 je zobrazena již dokončená aplikace. Její kód je pak uveden ve výpisu 12.2.

Obrázek 12.3. Simulovaný telnet klient.

Page 68: Ruby kompendium

KAPITOLA 19 Ruby a webové aplikace

Ó, jak zamotanou síť jsme to upředli...!

– Sir Walter Scott, Píseň posledního skotského barda

Ruby je univerzální jazyk; nemůže být nazýván "jazykem webu". Webové aplikace (a webové ná-stroje obecně) ovšem patří mezi nejběžnější využití Ruby. Existuje mnoho způsobů, jak provádět vývoj webových aplikací v Ruby – od knihoven, které jsou malé a nízkoúrovňové, až po rozsáhlé frameworky (pracovní rámce), jež určují váš styl myšlení a kódování.

A začněme na spodním konci – podíváme se na knihovnu cgi.rb, která je standardem v Ruby.

19.1 – CGI programování v RubyLibovolný člověk obeznámený s programováním webových aplikací nepochybně již slyšel o termí-nu CGI (Common Gateway Interface). CGI bylo vytvořeno brzy po vzniku samotného webu, aby mohly existovat programově implementované stránky, a pro dosažení větší interakce mezi konco-vým uživatelem a webovým serverem. Ačkoliv od doby jeho vzniku bylo představeno nespočetné množství náhradních technologií, CGI ve světě webu stále žije a daří se mu dobře. Velká část úspě-chu a dlouhověkosti CGI může být přisuzována jeho jednoduchosti. Kvůli této jednoduchosti je například snadné implementovat CGI programy v libovolném jazyce. Standard CGI specifikuje, jakým způsobem bude proces webového serveru předávat data mezi sebou a jeho potomky. Většina této interakce se děje skrze standardní proměnné prostředí a proudy v operačním systému.

CGI programování (a pro úplnost – i samotné HTTP) je založeno na mechanismu bezstavového požadavku a odpovědi. Obecně lze říci, že jakmile je ustanoveno jediné TCP spojení, klient (což je obvykle webový prohlížeč) zahájí konverzaci prostřednictvím jednoduchého HTTP příkazu. Dva nejběžněji používané příkazy v tomto protokolu jsou GET a POST (k jejich významu se dostaneme za chvíli). Po vydání příkazu webový server odpovídá a zavírá výstupní proud.

Page 69: Ruby kompendium

660 Kapitola 19 – Ruby a webové aplikace

Následující ukázka kódu, která je o něco málo pokročilejší než standardní "Hello, World!", názorně ukazuje, jak provést vstup a výstup přes CGI.

def parse_query_string

inputs = Hash.new

raw = ENV['QUERY_STRING']

raw.split("&").each do |pair|

name,value = pair.split("=")

inputs[name] = value

end

inputs

end

inputs = parse_query_string

print "Content-type: text/html\n\n"

print "<HTML><BODY>"

print "<B><I>Hello</I>, #{inputs['name']}!</B>"

print "</BODY></HTML>"

Použití URL http://mywebserver/cgi-bin/hello.cgi?name=Dali vyprodukuje výstup "Hello, Dali!" ve vašem prohlížeči.

Jak už jsem zmínil dříve, existují dva hlavní způsoby, jak přistoupit k URL – HTTP metody GET a POST. Z nedostatku místa bohužel musím dát přednost jejich jednoduchému popisu před pečli-vou definicí. Metoda GET je obvykle volána při kliknutí na odkaz nebo při přímém použití URL (jako tomu bylo v předchozím příkladu). Parametry jsou předávány prostřednictvím dotazovacího řetězce URL, který je CGI programům přístupný přes proměnnou prostředí QUERY_STRING. Me-toda POST se nejčastěji používá v HTML formulářích. Parametry, které jsou odeslány prostřednic-tvím metody POST, jsou zahrnuty v těle zprávy a nejsou viditelné v URL. CGI programům jsou doručovány přes standardní vstupní proud.

Ačkoliv předchozí příklad byl velmi jednoduchý, cokoliv méně banálního by mohlo vést ke zbyteč-ným zmatkům. Programy, které jsou potřebné pro spolupráci s několika HTTP metodami, nahrá-váním souborů, cookies, relacemi a dalšími složitostmi, je nejlepší řešit prostřednictvím univerzál-ní knihovny pro práci s CGI prostředím. Ruby naštěstí poskytuje plnohodnotnou sadu tříd, které automatizují velkou část práce, již byste jinak museli dělat ručně.

Spousta dalších nástrojů a knihoven se pokouší vývoj CGI zjednodušit. Mezi nejlepší z nich patří ruby-web od Patricka Maye (dříve Narf). Pokud chcete nízkoúrovňové řízení, ale standardní CGI knihovna není vaší zálibou, vyzkoušejte tuto knihovnu (k nalezení na http://ruby-web.org).

Pokud chcete řešení postavené na šablonách, Amrita (http://amrita.sourceforge.jp) může být pro vás dobrým řešením. Také se podívejte na Cerise, což je webový aplikační server založený na Amritě (http://cerise.rubyforge.org). Pravděpodobně existují i další knihovny, takže po-kud jste nenašli to, co jste hledali, zkuste pohledat na webu nebo se někoho zeptejte.

Page 70: Ruby kompendium

661Ruby – kompendium znalostí pro začátečníky i profesionály

19.1.1 – Představení knihovny cgi.rb Knihovna CGI je umístěna v souboru cgi.rb ve standardní distribuci Ruby. Většina její funkcio-nality je implementována kolem centrální třídy vhodně pojmenované jako CGI. Jednou z prvních věcí, kterou budete chtít udělat při používání této knihovny, je vytvoření instance CGI.

require "cgi"

cgi = CGI.new("html4")

Inicializátor pro třídu CGI přijímá jeden parametr, který specifikuje úroveň HTML, jež by měla být podporována metodami pro generování HTML kódu v CGI balíčku. Tyto metody zajišťují, aby programátor nemusel řešit vkládání množství HTML kódu do jinak čistého Ruby kódu:

cgi.out do

cgi.html do

cgi.body do

cgi.h1 { "Hello Again, "} +

cgi.b { cgi['name']}

end

end

end

Zde jsme použili CGI knihovny k téměř přesné reprodukci funkcionality předchozího programu. Jak můžete sami vidět, třída CGI se stará o analýzu jakéhokoliv vstupu, přičemž výsledné hodnoty interně ukládá ve struktuře podobné haši. Takže – pokud jste specifikovali URL jako some.pro-gram.cgi?age=4, hodnota může být být zpřístupněna přes cgi['age’].

V předchozím kódu si povšimněte, že je ve skutečnosti použita pouze návratová hodnota bloku. HTML je vybudováno a uloženo postupně (tj. není okamžitě vypsáno). Jinak řečeno – spojování řetězců, které vidíme zde, je absolutně nezbytné. Bez toho by se objevil pouze poslední vyhodno-cený řetězec.

Třída CGI dále poskytuje užitečné mechanismy pro práci s URL-kódovanými řetězci a pro citace HTML nebo XML kódu. URL kódování je proces, který spočívá v konverzi řetězců s nebezpečnými znaky do formátu, jenž je zobrazitelný v řetězci URL. Výsledkem jsou podivně vypadající "%" řetěz-ce, jež vidíte v některých URL, zatímco prohlížíte web. Tyto řetězce jsou ve skutečnosti numerické ASCII kódy, které jsou reprezentovány hexadecimálně s prefixem "%".

require "cgi"

s = "This| is^(aT$test"

s2 = CGI.escape(s) # "This%7C+is%5E%28aT%24test"

puts CGI.unescape(s2) # Vytiskne "This| is^(aT$test"

Třída CGI může být dále použita k ošetření HTML nebo XML kódu, který by měl být zobrazen (tj. nikoliv vykonán) v prohlížeči. Například řetězec "<some_stuff>" nebude v prohlížeči zobrazen požadovaným způsobem. Pokud tedy máte potřebu doslovně zobrazovat HTML nebo XML kód

Page 71: Ruby kompendium

662 Kapitola 19 – Ruby a webové aplikace

v prohlížeči (například při výuce HTML), třída CGI vám nabízí podporu pro konverzi speciálních znaků do příslušných entit, viz následující ukázka:

require "cgi"

some_text = "<B>This is how you make text bold</B>"

translated = CGI.escapeHTML(some_text)

# "<B>This is how you make text bold</B>"

puts CGI.unescapeHTML(translated)

# Vytiskne "<B>This is how you make text bold</B>"

19.1.2 – Zobrazení a zpracování formulářůNejběžnější způsob interakce s CGI programy je skrze HTML formuláře. HTML formuláře jsou vytvořeny pomocí specifických značek, které jsou následně přeloženy do vstupních widgetů v pro-hlížeči. Podrobnější diskuse k tomuto tématu je bohužel mimo rozsah této kapitoly, nicméně na webu (a také v různých knihách) lze nalézt dostatek potřebných informací.

Třída CGI nabízí metody pro generování všech prvků souvisejících s HTML formuláři. Následující fragment kódu ukazuje, jak zobrazit a zpracovat HTML formulář.

require "cgi"

def reverse_ramblings(ramblings)

if ramblings[0] == nil then return " " end

chunks = ramblings[0].split(/\s+/)

chunks.reverse.join(" ")

end

cgi = CGI.new("html4")

cgi.out do

cgi.html do

cgi.body do

cgi.h1 { "sdrawkcaB txeT" } +

cgi.b { reverse_ramblings(cgi['ramblings'])} +

cgi.form("action" => "/cgi-bin/rb/form.cgi") do

cgi.textarea("ramblings") { cgi['ramblings'] } + cgi.submit

end

end

end

end

Tento příklad zobrazuje textovou oblast (text area) a obsah, který bude rozdělen do slov a obrácen. Například – pokud do formuláře napíšete větu "This is test", po zpracování se objeví text "test is

Page 72: Ruby kompendium

663Ruby – kompendium znalostí pro začátečníky i profesionály

This". Metoda form třídy CGI může přijímat parametr method, který určuje HTTP metodu (GET, POST atd.) použitou daným formulářem. Výchozí metoda je metoda POST.

Tento příklad předvedl pouze několik málo prvků, které lze použít v HTML stránce. Pro jejich kompletní seznam nahlédněte do libovolné referenční příručky o HTML.

19.1.3 – Práce s cookiesHTTP je bezstavový protokol. To znamená, že poté, co prohlížeč dokončí požadavek na webovou stránku, webový server nemá žádnou možnost, jak rozlišit jeho další požadavky od jakéhokoliv jiného prohlížeče na webu. A to je okamžik, kdy na scénu přichází HTTP cookies. Cookies totiž nabízí způsob (ačkoliv poněkud neohrabaný) pro udržování stavu mezi jednotlivými požadavky ze stejného prohlížeče.

Mechanismus fungování cookie je snadný. Webový server prostřednictvím HTTP záhlaví odpo-vědi posílá prohlížeči příkaz, aby někde uložil dvojici klíč/hodnota. Tato data mohou být uložena v paměti nebo na disku. Pro každý následující požadavek, který směřuje k doméně specifikované v této cookie, bude prohlížeč posílat data cookie v HTTP hlavičce požadavku.

Ačkoliv všechna tato cookies můžete vytvářet a číst ručně, určitě je vám jasné, že něco takového není potřeba. CGI knihovny Ruby totiž poskytují třídu Cookie, která umí pracovat s cookies.

require "cgi"

lastacc = CGI::Cookie.new("kabhi", "lastaccess=#{Time.now.to_s}")

cgi = CGI.new("html3")

if cgi.cookies.size < 1

cgi.out("cookie" => lastacc) do

"Hit refresh for a lovely cookie"

end

else

cgi.out("cookie" => lastacc) do

cgi.html do

"Hi, you were last here at: "+

"#{cgi.cookies['kabhi'].join.split('=')[1]}"

end

end

end

Prostřednictvím tohoto fragmentu kódu je vytvořena cookie s názvem kabhi, která obsahuje klíč lastacces nastavený na aktuální čas. Pokud má prohlížeč předchozí uloženou hodnotu pro tuto cookie, bude zobrazena. Cookies jsou přístupné jako proměnná instance na třídě CGI a uloženy jako Hash. Každá cookie může ukládat více dvojic klíč/hodnota, takže když přistoupíte k cookie jejím jménem, získáte pole.

Page 73: Ruby kompendium

664 Kapitola 19 – Ruby a webové aplikace

19.1.4 – Práce s relacemi uživateleCookies jsou fajn, pokud chcete ukládat jednoduchá data a nevadí vám, že za jejich uchování je od-povědný prohlížeč. Ale v mnoha případech máte poněkud vyšší nároky na perzistenci dat. Napří-klad v případě, kdy máte k dispozici větší množství dat, která chcete trvale udržovat a jež nechcete posílat sem a tam s každým požadavkem. Co dělat v případě, kdy se jedná o nějaká citlivá data, která potřebujete asociovat s příslušnou relací a kdy nevěříte webovému prohlížeči?

Pro pokročilejší perzistenci ve webových aplikacích použijte třídu CGI::Sessions. Práce s touto třídou se velmi podobá práci s třídou CGI::Cookie v tom, že hodnoty jsou uloženy a získávány přes strukturu podobnou haši.

require "cgi"

require "cgi/session"

cgi = CGI.new("html4")

sess = CGI::Session.new( cgi, "session_key" => "a_test",

"prefix" => "rubysess.")

lastaccess = sess["lastaccess"].to_s

sess["lastaccess"] = Time.now

if cgi['bgcolor'][0] =~ /[a-z]/

sess["bgcolor"] = cgi['bgcolor']

end

cgi.out do

cgi.html do

cgi.body ("bgcolor" => sess["bgcolor"]) do

"The background of this page" +

"changes based on the 'bgcolor'" +

"each user has in session." +

"Last access time: #{lastaccess}"

end

end

end

Přístup k "/thatscript.cgi?bgcolor=red" změní uživateli stránku na červenou pro každé ná-sledující načtení až do té doby, než bude v URL specifikována jiná barva pro bgcolor. Třída CGI::Session je instanciována s objektem CGI a sadou nastavení v Hash. Nepovinný parametr ses-sion_key specifikuje klíč, který bude prohlížečem použit při každém požadavku pro identifikaci sama sebe. Data relace jsou uložena v dočasných souborech pro každou relaci, přičemž parametr prefix specifikuje řetězec, kterým bude začínat název souboru, což učiní vaši relaci snadno iden-tifikovatelnou v souborovém systému serveru.

Page 74: Ruby kompendium

665Ruby – kompendium znalostí pro začátečníky i profesionály

Třída CGI::Session v současnosti stále postrádá spoustu užitečných rysů – například schopnost ukládat objekty (nikoliv pouze řetězce), ukládat relaci napříč několika servery atd. Dnes již ovšem existuje plugin database_manager, který může zjednodušit implementaci některých těchto rysů. Pokud děláte cokoliv vzrušujícího s CGI::Session, rozhodně se o to podělte s ostatními.

19.2 – Používání FastCGINejčastěji kritizovaný nedostatek CGI je ten, že pro každé nové volání vyžaduje vytvoření nového procesu. To má významný vliv na výkon. Neschopnost ponechat objekty v paměti mezi jednotli-vými požadavky může mít negativní dopad i na návrh. Kombinace těchto potíží vedla k vytvoření FastCGI.

FastCGI v podstatě není nic víc než definice protokolu a softwarová implementace tohoto protoko-lu. FastCGI je obvykle implementováno ve formě pluginu webového serveru (například ve formě modul pro Apache), přičemž v rámci běžících procesů umožňuje zachytávat HTTP požadavků, které jsou přes socket přesměrovávány k trvale běžícímu procesu na pozadí. Tento přístup má pozitivní vliv na rychlost, zejména ve srovnání s tradičním spouštěním nového procesu pro ob-sloužení požadavku. Dále poskytuje programátorovi možnost ukládat věci do paměti (a při dalším požadavku je tam samozřejmě najít).

Servery pro FastCGI byly implementovány v mnoha programovacích jazycích včetně Ruby. Eli Green vytvořil modul, který je kompletně vytvořen v Ruby, jenž implementuje protokol FastCGI a který rapidně zjednodušuje vývoj FastCGI programů.

Aniž bychom zacházeli do podrobnějších detailů, ve výpisu 19.1 prezentujeme vzorovou aplikaci. Jak sami vidíte, tento kousek kódu poskytuje funkcionalitu shodnou s předchozím příkladem.

Výpis 19.1. Vzorová aplikace ve FastCGI.

require "fastcgi"

require "cgi"

last_time = ""

def get_ramblings(instream)

# Nepěkně získá hodnotu prvního páru jmého/hodnota

# CGI to pro nás může udělat.

data = ""

if instream != nil

data = instream.split("&")[0].split("=")[1] || ""

end

return CGI.unescape(data)

end

def reverse_ramblings(ramblings)

Page 75: Ruby kompendium

666 Kapitola 19 – Ruby a webové aplikace

if ramblings == nil then return "" end

chunks = ramblings.split(/\s+/)

chunks.reverse.join(" ")

end

server = FastCGI::TCP.new('localhost', 9000)

begin

server.each_request do |request|

stuff = request.in.read

out = request.out

out << "Content-type: text/html\r\n\r\n"

out << <<-EOF

<html>

<head><title>Text Backwardizer</title></head>

<h1>sdrawkcaB txeT</h1>

<i>You previously said: #{last_time}</i><BR>

<b>#{reverse_ramblings(get_ramblings(stuff))}</b>

<form method="POST" action="/fast/serv.rb">

<textarea name="ramblings">

</textarea>

<input type="submit" name="submit"

</form>

</body></html>

EOF

last_time = get_ramblings(stuff)

request.finish

end

ensure

server.close

end

První věci, které si na tomto kódu nepochybně povšimnete (pokud jste četli předchozí sekci), je několik detailů, které musíte ve FastCGI udělat ručně, a které byste tato nemuseli dělat při použití CGI. (Jedním takovým detailem je například zadrátovaný HTML kód.) Druhou věcí je metoda get_ramblings, která ručně analyzuje vstup a vrací pouze relevantní hodnoty. Tento kód mimo-chodem pracuje pouze s HTTP metodou POST, což je jedna z dalších nevýhod, na kterou narazíte při nepoužívání knihovny CGI.

Použití FastGCi samozřejmě s sebou nese i nějaké výhody. Ačkoliv v tomto příkladu nespouštíme žádné srovnávací testy, FastCGI je rychlejší než normální CGI. Například režii pro vytvoření no-vého procesu jsme ušetřili ve prospěch vytvoření spojení v místní síti na port 9000 (FastCGI::TCP.new('localhost', 9000)). A dále – proměnná last_time byla v tomto příkladu použita k uchování stavu v paměti mezi jednotlivými požadavky. Toto je v tradičním CGI nemožné.

Page 76: Ruby kompendium

667Ruby – kompendium znalostí pro začátečníky i profesionály

Dále zde chci poukázat na skutečnost, že je možné tyto knihovny využívat pouze částečně. Pomoc-né funkce z cgi.rb mohou být použity samostatně (tj. bez použití této knihovny pro řízení aplika-ce). Například funkce CGI.escapeHTML může být použita izolovaně od zbytku knihovny. Toto by udělalo předchozí příklad o něco čitelnější.

19.3 – Ruby on RailsJedním z nejznámějších webových frameworků ve světě Ruby je nepochybně Ruby on Rails (nebo jednoduše Rails). Tento framework je dílem Davida Heinemeiera Hansona.

Rails využívá dynamických rysů Ruby. Má svou vlastní filozofii návrhu a umožňuje rychlý vývoj webových aplikací. Rails je nejenom velmi dobře známý, ale také dobře zdokumentovaný. V této knize se mu věnujeme pouze zběžně.

19.3.1 – Principy a technologieRails je vybudován na paradigmatu návrhového vzoru MVC (Model-View-Controller). Každá we-bová aplikace, která je postavena na Rails, je přirozeně rozdělena do modelů (jež modelují doménu problému), pohledů (které prezentují informaci uživateli a umožňují interakci) a ovladačů (jež za-jišťují spojení mezi pohledy a modely).

Chování Rails jako frameworku je založeno na několika principech. Jeden princip je méně softwa-ru – nepište kód k tomu, aby navázal jednu věc na jinou, pokud tyto věci mohou být dohromady navázány automaticky. Další příbuzný princip je přednost konvence před konfigurací. Následová-ním určitých předdefinovaných stylů programování a pojmenovávání se konfigurace stává méně důležitou, čímž se tak přiblížíte k ideálnímu prostředí s "nulovou konfigurací".

Rails je dobrý v automatizaci úkolů, které vyžadují omezenou úroveň inteligence. Generuje kód vždy, když je to praktické, takže programátor není nucen vytvářet tyto kousky kódu ručně.

Protože webové aplikace se mnohdy neobejdou bez nějaké databáze v pozadí, Rails nabízí hlad-kou integraci databází. Existuje rovněž silná tendence, aby webové frameworky měly svůj oblíbený ORM (object-relational mapper) a Rails v tomto ohledu není výjimkou. Výchozí ORM pro Rails je Active Record, což jsme si popsali již v kapitole 10.

Databáze jsou popsány v souboru config/database.yaml, což je jeden z mála konfiguračních souborů, které budete potřebovat. Tento soubor, který je samozřejmě uložen ve formátu YAML, specifikuje tři různé databáze – jednu pro vývoj, jednu pro testování a jednu pro ostrý provoz. Ač-koliv tyto tři vyhrazené databáze mohou na první pohled vypadat nesmyslně, toto schéma je tím, co dává Rails jeho sílu.

Rails pro vás generuje prázdné modely a ovladače (controllers). Když tyto modely editujete, de-finujete vztah mezi databázovými tabulkami metodami, jako například has_many a belongs_to (abych uvedl aspoň dvě). Protože modely a tabulky vzájemně korespondují, tento kód rovněž de-finuje vztah mezi samotnými modely. Platnost dat může být bezproblémová s metodami jako va-