Ruby kompendium

Page 1

E N C Y K L O P E D I E

Z O N E R

P R E S S

RUBY

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

© Foto: Jiří Heller

H a l

F u l t o n


„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 dostáváte na horskou dráhu mezi „Jak to můžu použít“ a „To je tak skvělé!“. Jakmile na tuto dráhu jednou 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ě komplexně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“



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

Hal Fulton


THE RUBY WAY, SECOND EDITION Hal 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 reproduced 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 TECHNIQUES 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ály Autor: Hal Fulton Copyright © ZONER software, s.r.o. Vydání první v roce 2009. Všechna práva vyhrazena. Zoner Press Katalogové číslo: ZR724 ZONER software, s.r.o. Nové sady 18, 602 00 Brno Překlad: Jiří Koutný Odpovědný redaktor: Miroslav Kučera Odborná korektura: Miroslav Kučera a Dalibor Šrámek Šéfredaktor: Ing. Pavel Kristián DTP: 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 distribuová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: knihy@zoner.cz http://www.zonerpress.cz

ISBN 978-80-7413-018-2


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


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

1.1 – Úvod do objektové orientace

39 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


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


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


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

Kapitola 4

156

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


10 Kapitola 5

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

5.1 – Reprezentace čísel v Ruby

183 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

6.1 – Symboly

217 217

6.1.1 – Symboly jako výčty

219

6.1.2 – Symboly jako metahodnoty

219


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

Kapitola 7

231

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


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

8.1 – Práce s poli

251 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


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

Kapitola 9

295

Pokročilejší datové struktury

9.1 – Práce s množinami

297 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 9.3 – Práce se stromy

305 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


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

Kapitola 10

319

I/O a uložení dat

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

321 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


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

Kapitola 11

374

OOP a dynamické rysy Ruby

11.1 – Každodenní OOP úlohy

375 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


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 11.2.11 – Pokročilejší programovací disciplíny 11.3 – Práce s dynamickými rysy

420 421 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


17 Kapitola 12

Grafická rozhraní pro Ruby

12.1 – Ruby/Tk

445 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 12.5.1 – Ruby a X

497 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


18 Kapitola 13

Vlákna v Ruby

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

501 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í 13.3 – Závěr

Kapitola 14

529 530

Skriptování a správa systému

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

531 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í 14.4.1 – Získávání a nastavování proměnných prostředí

543 543


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 14.9 – Různé skriptovací úlohy

556 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

Kapitola 15

562

Ruby a datové formáty

15.1 – Analýza XML pomocí REXML

563 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 15.4.1 – Základní koncepty a techniky

582 582


20 15.4.2 – Vzorový dokument 15.5 – Závěr

Kapitola 16

585 592

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

17.1 – Používání RDoc

613 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í

18.1 – Síťové servery 18.1.1 – Jednoduchý server: aktuální čas

625 626 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 18.2.1 – Získávání opravdu náhodných čísel z webu

638 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


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

Kapitola 19

657

Ruby a webové aplikace

19.1 – CGI programování v Ruby

659 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


22 Kapitola 20

Distribuované Ruby

20.1 – Přehled: použití drb

701 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


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 ukryto 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 zahrnují 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í programátorské produktivity. Věřím, že tato kniha vám pomůže se stát zdatným programátorem. Veselé hackování. Yukihiro "Matz" Matsumoto Srpen 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 programovací 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 postupně 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é programová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.


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í jednoduchá. 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řekvapení (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 prvního vydání Ruby v roce 1995 se mnou mnoho programátoru po celém světě o radosti z programová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" Matsumoto Září 2001, Japonsko


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é poně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 Neukirchen, 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 regulá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 materiá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, produktivitu 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.


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í. Potvrzuji 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 diskusní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 Masaki 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.


27

O autorovi Hal 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á rozpracovaných několik projektů týkajících se Ruby. Je členem ACM a IEEE Computer Society. V osobní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.


28

Úvod Cesta, 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édně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 datový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.


29

Kapitola 17 – Balíčkování a distribuce kódu. Tato kapitola zahrnuje použití setup.rb, vytvoř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 aktualizoval 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. Nechybí 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.


30 Když jsem začal psát první vydání knihy o Ruby, neprobíhaly žádné mezinárodní konference o Ruby. Neexistovalo RubyForge, ruby-doc.org nebo rubygarden.org. Stručně řečeno – kromě domovské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, abychom 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 žádný 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 FileUtils 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ědomit 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řeté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


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 knihou Př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 brouzdat 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í informace 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žadované 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.)


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 knize 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ý vedlejší 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é, nicmé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ž pomalu 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 Application 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 typografie, které je v knize použita pro výpisy zdrojových kódů (a případně pro další dodatečné informace). 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 lidmi, čí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-kompendium.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


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 kompletní. 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, doposud 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-puvodni-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ázor Jako č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: miroslav.kucera@zoner.cz nebo knihy@zoner.cz. Adresa: ZonerPress, ZONER software, s.r.o., Miroslav Kučera, Nové sady 18, 602 00 Brno.


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 nakreslí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ě úrovně 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 implementová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ě druhé 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 stravitelně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 Antoine 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 vesmí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


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í samotný 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 jazyka, 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 dobrá 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 miliony 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řechá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.


36 Přesuňme se do jiné doby. V roce 1980 vyšla nádherná malá kniha s názvem The Tao of Programming (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řekvapil. (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í direktivou 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ý programovací 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ísmenem 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 Ruby 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 fiktivní dvojici os se se sadou srovnatelných jazykových entit na jedné a s vlastnostmi nebo schopnostmi 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 velké 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-


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 Array metody replace a concat). Některé metody umožňují přiřazení do vlastnosti třídy, takže bychom 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 jednoduchý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řipomí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.


38


KAPITOLA 3 Prá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 systé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 srozumitelné 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.


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.


Ruby – 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í.

()

131

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.


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ž nebude 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ícebajtový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 regulá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.

# "\[\*\?\]"


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

133

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 assertion). 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é odpoví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 řetě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"


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í slova. 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ásledují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 (subpattern) 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ásledovaný žá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 *.


Ruby – kompendium znalostí pro začátečníky i profesionály pattern = /Huzzah!*/

# * se aplikuje pouze na !

pattern =~ "Huzzah"

# 0

pattern =~ "Huzzah!!!!"

# 0

135

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 (chamtivý), 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:


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, backtracking, 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é, nedojde 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ásledová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".


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

137

3.7 – Přístup ke zpětným referencím Kaž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 vyhodnoceny 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 provedeno 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"


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ěnné, 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 pozdě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 pohodlné, 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ávorkami (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 zachá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.


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

139

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"


KAPITOLA 8 Pole, 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í nový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 indexová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 poli Pole 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.


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ýjimkou 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 externí 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ě prozkoumá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 parametry. 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ý objekt), 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řadí). 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).


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

253

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]


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ě. Metoda 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 celé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).


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

255

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


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 porovnání 3 <=> "x" je problematické: g = [1, 2, 3] h = [1, 2, "x"] if g < h puts "g < h"

# Chyba! # Žá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 puts "g != h"

# Bez problémů. # 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 puts "i < j"

# Bez problémů. # 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"]


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

257

# 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é. Pokud 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áceno číslo -1, znamená to, že x je menší než y; dva prvky jsou zaměněny. Takže pro řazení v sestupné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 ignorovat 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


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ů samotný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 uvedených příkladech. Prvkem pole může být libovolný výraz.


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

259

8.1.6 – Výběr z pole na základě kritéria Ně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 nejjednodušší 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!:


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 grafické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 pochopitelně spočívá v návrhu smysluplného a použitelného prostředí programu. V návrhu uživatelské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 postř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é


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/Tk Koř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ěkolika 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řehled V 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 populá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.


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

447

Nastavení, které je použito pro instanciaci widgetu, může být specifikováno dvěma způsoby. První 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í instance_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 vyhodnocová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í velikosti 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 okny V této sekci si předvedeme nejjednodušší možnou aplikaci – jednoduchou aplikaci kalendáře, která zobrazí aktuální datum. Abychom se dostali do programovací nálady, začneme explicitním vytvoř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


Kapitola 12 – Grafická rozhraní pro Ruby

448

ú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žadovanou 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. Prostudujte si dokumentaci pro další informace.


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

449

12.1.3 – Práce s tlačítky Jedním z nejběžnějších widgetů v jakémkoliv GUI je stisknutelné tlačítko (nebo jednoduše tlačítko). 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 }


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


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

451

Pomocí toho kódu vytváříme dva rámce. Horní rámec ovládá pouze zobrazení. Teplotu ve Fahrenheitech 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 specifikuje 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 background. V tomto případě specifikujeme barvu odlišně, protože ji chceme v typickém hexadecimální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. Parametrem 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á metoda 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é obsahují š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


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 poli Vstupní 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 promě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ášení" 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.


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 Ruby Libovolný č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 koncový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.


Kapitola 19 – Ruby a webové aplikace

660

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člivou 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. Metoda POST se nejčastěji používá v HTML formulářích. Parametry, které jsou odeslány prostřednictví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ální 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 pokud jste nenašli to, co jste hledali, zkuste pohledat na webu nebo se někoho zeptejte.


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

661

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í funkcionality 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.program.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í vyhodnocený ř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ězce, 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


Kapitola 19 – Ruby a webové aplikace

662

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 prohlíž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


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

663

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 cookies HTTP 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í odpově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.


Kapitola 19 – Ruby a webové aplikace

664

19.1.4 – Práce s relacemi uživatele Cookies jsou fajn, pokud chcete ukládat jednoduchá data a nevadí vám, že za jejich uchování je odpově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 session_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 identifikovatelnou v souborovém systému serveru.


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

665

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í FastCGI Nejč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 jednotlivý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 protokolu. 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 obslouž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)


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 mimochodem 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í nové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é.


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

667

Dále zde chci poukázat na skutečnost, že je možné tyto knihovny využívat pouze částečně. Pomocné funkce z cgi.rb mohou být použity samostatně (tj. bez použití této knihovny pro řízení aplikace). 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 Rails Jední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 technologie Rails je vybudován na paradigmatu návrhového vzoru MVC (Model-View-Controller). Každá webová 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ž zajišťují spojení mezi pohledy a modely). Chování Rails jako frameworku je založeno na několika principech. Jeden princip je méně softwaru – 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í hladkou 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 ActiveRecord, 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, definujete 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ěž definuje vztah mezi samotnými modely. Platnost dat může být bezproblémová s metodami jako va-


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.