Darmowy fragment publikacji:
Ruby. Receptury
Autorzy: Lucas Carlson, Leonard Richardson
T³umaczenie: Andrzej Gra¿yñski, Rados³aw Meryk
ISBN: 83-246-0768-4
Tytu³ orygina³u: Ruby Cookbook
Format: B5, stron: 888
Zbiór gotowych rozwi¹zañ dla programistów u¿ywaj¹cych jêzyka Ruby
(cid:129) Jak przetwarzaæ pliki XML i HTML?
(cid:129) Jak wykorzystywaæ œrodowisko Ruby on Rails?
(cid:129) W jaki sposób ³¹czyæ Ruby z technologi¹ AJAX?
Korzystasz w pracy z jêzyka Ruby i zastanawiasz siê, czy niektóre zadania
programistyczne mo¿na wykonaæ szybciej? Chcesz poznaæ zasady programowania
obiektowego w Ruby? A mo¿e interesuje Ciê framework Ruby on Rails? Jêzyk Ruby
zdobywa coraz wiêksz¹ popularnoœæ, jest wykorzystywany do tworzenia aplikacji
sieciowych i sta³ siê podstaw¹ œrodowiska Ruby on Rails. Jednak nawet najlepszy
jêzyk programowania nie uwalnia programistów od ¿mudnego realizowania zadañ,
które nie maj¹ zbyt wiele wspólnego z tworzeniem aplikacji, czyli usuwania b³êdów,
implementowania typowych algorytmów, poszukiwania rozwi¹zañ mniej lub bardziej
typowych problemów i wielu innych.
Ksi¹¿ka „Ruby. Receptury” znacznie przyspieszy Twoj¹ pracê. Znajdziesz tu kilkaset
praktycznych rozwi¹zañ problemów wraz z przejrzystym komentarzem oraz tysi¹ce
wierszy proponowanego kodu, który bêdziesz móg³ wykorzystaæ w swoich projektach.
Przeczytasz o strukturach danych, algorytmach, przetwarzaniu plików XML i HTML,
tworzeniu interfejsów u¿ytkownika dla aplikacji i po³¹czeniach z bazami danych.
Nauczysz siê generowaæ i obrabiaæ pliki graficzne, korzystaæ z us³ug sieciowych,
wyszukiwaæ i usuwaæ b³êdy w aplikacjach, a tak¿e pisaæ skrypty niezwykle pomocne
w administrowaniu systemem operacyjnym Linux.
(cid:129) Przetwarzanie danych tekstowych i liczbowych
(cid:129) Operacje na tablicach
(cid:129) Praca z systemem plików
(cid:129) Programowanie obiektowe
(cid:129) Przetwarzanie dokumentów XML i HTML oraz plików graficznych
(cid:129) Generowanie plików PDF
(cid:129) Po³¹czenie z bazami danych
(cid:129) Korzystanie z poczty elektronicznej, protoko³u telnet i po³¹czeñ Torrent
(cid:129) Projektowanie aplikacji internetowych za pomoc¹ Ruby on Rails
(cid:129) Stosowanie us³ug sieciowych
(cid:129) Optymalizacja aplikacji
(cid:129) Tworzenie wersji dystrybucyjnych
(cid:129) Automatyzacja zadañ z wykorzystaniem jêzyka Rake
(cid:129) Budowanie interfejsów u¿ytkownika
Jeœli chcesz rozwi¹zaæ problem, skorzystaj z gotowej receptury — ko³o ju¿ wynaleziono
Wydawnictwo Helion
ul. Koœciuszki 1c
44-100 Gliwice
tel. 032 230 98 63
e-mail: helion@helion.pl
Spis treści
Wprowadzenie .............................................................................................................. 17
1. Łańcuchy ........................................................................................................................29
32
34
35
37
39
41
42
43
45
47
48
49
51
52
53
55
58
60
61
64
1.1. Budowanie łańcucha z części
1.2. Zastępowanie zmiennych w tworzonym łańcuchu
1.3. Zastępowanie zmiennych w istniejącym łańcuchu
1.4. Odwracanie kolejności słów lub znaków w łańcuchu
1.5. Reprezentowanie znaków niedrukowalnych
1.6. Konwersja między znakami a kodami
1.7. Konwersja między łańcuchami a symbolami
1.8. Przetwarzanie kolejnych znaków łańcucha
1.9. Przetwarzanie poszczególnych słów łańcucha
1.10. Zmiana wielkości liter w łańcuchu
1.11. Zarządzanie białymi znakami
1.12. Czy można potraktować dany obiekt jak łańcuch?
1.13. Wyodrębnianie części łańcucha
1.14. Obsługa międzynarodowego kodowania
1.15. Zawijanie wierszy tekstu
1.16. Generowanie następnika łańcucha
1.17. Dopasowywanie łańcuchów za pomocą wyrażeń regularnych
1.18. Zastępowanie wielu wzorców w pojedynczym przebiegu
1.19. Weryfikacja poprawności adresów e-mailowych
1.20. Klasyfikacja tekstu za pomocą analizatora bayesowskiego
2. Liczby .............................................................................................................................67
68
70
73
2.1. Przekształcanie łańcucha w liczbę
2.2. Porównywanie liczb zmiennopozycyjnych
2.3. Reprezentowanie liczb z dowolną dokładnością
5
2.4. Reprezentowanie liczb wymiernych
2.5. Generowanie liczb pseudolosowych
2.6. Konwersje między różnymi podstawami liczenia
2.7. Logarytmy
2.8. Średnia, mediana i moda
2.9. Konwersja stopni na radiany i odwrotnie
2.10. Mnożenie macierzy
2.11. Rozwiązywanie układu równań liniowych
2.12. Liczby zespolone
2.13. Symulowanie subklasingu klasy Fixnum
2.14. Arytmetyka liczb w zapisie rzymskim
2.15. Generowanie sekwencji liczb
2.16. Generowanie liczb pierwszych
2.17. Weryfikacja sumy kontrolnej w numerze karty kredytowej
76
77
79
80
83
85
87
91
94
96
100
105
107
111
3. Data i czas .....................................................................................................................113
115
119
122
126
127
129
131
134
135
138
139
140
142
145
3.1. Odczyt dzisiejszej daty
3.2. Dekodowanie daty, dokładne i przybliżone
3.3. Drukowanie dat
3.4. Iterowanie po datach
3.5. Arytmetyka dat
3.6. Obliczanie dystansu między datami
3.7. Konwersja czasu między strefami czasowymi
3.8. Czas letni
3.9. Konwersje między obiektami Time i DateTime
3.10. Jaki to dzień tygodnia?
3.11. Obsługa dat biznesowych
3.12. Periodyczne wykonywanie bloku kodu
3.13. Oczekiwanie przez zadany odcinek czasu
3.14. Przeterminowanie wykonania
4. Tablice .......................................................................................................................... 147
149
152
154
155
156
158
159
4.1. Iterowanie po elementach tablicy
4.2. Wymiana zawartości bez używania zmiennych pomocniczych
4.3. Eliminowanie zdublowanych wartości
4.4. Odwracanie kolejności elementów w tablicy
4.5. Sortowanie tablicy
4.6. Sortowanie łańcuchów bez rozróżniania wielkości liter
4.7. Zabezpieczanie tablic przed utratą posortowania
6
|
Spis treści
4.8. Sumowanie elementów tablicy
4.9. Sortowanie elementów tablicy według częstości występowania
4.10. Tasowanie tablicy
4.11. Znajdowanie N najmniejszych elementów tablicy
4.12. Tworzenie hasza za pomocą iteratora inject
4.13. Ekstrahowanie wybranych elementów z tablicy
4.14. Operacje teoriomnogościowe na tablicach
4.15. Partycjonowanie i klasyfikacja elementów zbioru
164
165
167
168
170
172
175
177
5. Hasze ...........................................................................................................................183
186
187
189
191
193
195
196
200
201
203
204
207
209
210
211
5.1. Wykorzystywanie symboli jako kluczy
5.2. Wartości domyślne w haszach
5.3. Dodawanie elementów do hasza
5.4. Usuwanie elementów z hasza
5.5. Tablice i inne modyfikowalne obiekty w roli kluczy
5.6. Kojarzenie wielu wartości z tym samym kluczem
5.7. Iterowanie po zawartości hasza
5.8. Iterowanie po elementach hasza w kolejności ich wstawiania
5.9. Drukowanie hasza
5.10. Inwersja elementów hasza
5.11. Losowy wybór z listy zdarzeń o różnych prawdopodobieństwach
5.12. Tworzenie histogramu
5.13. Odwzorowanie zawartości dwóch haszów
5.14. Ekstrakcja fragmentów zawartości haszów
5.15. Przeszukiwanie hasza przy użyciu wyrażeń regularnych
6. Pliki i katalogi .............................................................................................................. 213
216
218
220
223
224
227
230
232
233
234
6.1. Czy taki plik istnieje?
6.2. Sprawdzanie uprawnień dostępu do plików
6.3. Zmiana uprawnień dostępu do plików
6.4. Sprawdzanie, kiedy plik był ostatnio używany
6.5. Przetwarzanie zawartości katalogu
6.6. Odczytywanie zawartości pliku
6.7. Zapis do pliku
6.8. Zapis do pliku tymczasowego
6.9. Losowy wybór wiersza z pliku
6.10. Porównywanie dwóch plików
6.11. Swobodne nawigowanie po „jednokrotnie odczytywalnych”
strumieniach wejściowych
238
Spis treści
|
7
6.12. Wędrówka po drzewie katalogów
6.13. Szeregowanie dostępu do pliku
6.14. Tworzenie wersjonowanych kopii pliku
6.15. Łańcuchy udające pliki
6.16. Przekierowywanie standardowego wejścia i standardowego wyjścia
6.17. Przetwarzanie plików binarnych
6.18. Usuwanie pliku
6.19. Obcinanie pliku
6.20. Znajdowanie plików o określonej własności
6.21. Odczytywanie i zmiana bieżącego katalogu roboczego
240
242
245
248
250
252
255
257
258
260
7. Bloki kodowe i iteracje ...............................................................................................263
265
267
269
7.1. Tworzenie i wywoływanie bloku kodowego
7.2. Tworzenie metod wykorzystujących bloki kodowe
7.3. Przypisywanie bloku kodowego do zmiennej
7.4. Bloki kodowe jako domknięcia: odwołania do zmiennych zewnętrznych
w treści bloku kodowego
7.5. Definiowanie iteratora dla struktury danych
7.6. Zmiana sposobu iterowania po strukturze danych
7.7. Nietypowe metody klasyfikujące i kolekcjonujące
7.8. Zatrzymywanie iteracji
7.9. Iterowanie równoległe
7.10. Kod inicjujący i kończący dla bloku kodowego
7.11. Tworzenie systemów luźno powiązanych przy użyciu odwołań zwrotnych
272
273
276
278
279
281
285
287
8. Obiekty i klasy ............................................................................................................. 291
294
296
299
301
303
305
307
308
311
315
317
319
321
8.1. Zarządzanie danymi instancyjnymi
8.2. Zarządzanie danymi klasowymi
8.3. Weryfikacja funkcjonalności obiektu
8.4. Tworzenie klasy pochodnej
8.5. Przeciążanie metod
8.6. Weryfikacja i modyfikowanie wartości atrybutów
8.7. Definiowanie wirtualnych atrybutów
8.8. Delegowanie wywołań metod do innego obiektu
8.9. Konwersja i koercja typów obiektów
8.10. Prezentowanie obiektu w postaci czytelnej dla człowieka
8.11. Metody wywoływane ze zmienną liczbą argumentów
8.12. Symulowanie argumentów zawierających słowa kluczowe
8.13. Wywoływanie metod superklasy
8
|
Spis treści
8.14. Definiowanie metod abstrakcyjnych
8.15. Zamrażanie obiektów w celu ich ochrony przed modyfikacją
8.16. Tworzenie kopii obiektu
8.17. Deklarowanie stałych
8.18. Implementowanie metod klasowych i metod-singletonów
8.19. Kontrolowanie dostępu — metody prywatne, publiczne i chronione
323
325
327
330
332
334
9. Moduły i przestrzenie nazw .......................................................................................339
339
343
345
346
348
350
352
353
354
9.1. Symulowanie wielokrotnego dziedziczenia za pomocą modułów-domieszek
9.2. Rozszerzanie wybranych obiektów za pomocą modułów
9.3. Rozszerzanie repertuaru metod klasowych za pomocą modułów
9.4. Moduł Enumerable — zaimplementuj jedną metodę, dostaniesz 22 za darmo
9.5. Unikanie kolizji nazw dzięki ich kwalifikowaniu
9.6. Automatyczne ładowanie bibliotek na żądanie
9.7. Importowanie przestrzeni nazw
9.8. Inicjowanie zmiennych instancyjnych dołączanego modułu
9.9. Automatyczne inicjowanie modułów-domieszek
10. Odzwierciedlenia i metaprogramowanie .................................................................357
358
359
363
364
366
368
370
372
375
377
380
382
383
386
389
391
10.1. Identyfikacja klasy obiektu i jej superklasy
10.2. Zestaw metod obiektu
10.3. Lista metod unikalnych dla obiektu
10.4. Uzyskiwanie referencji do metody
10.5. Poprawianie błędów w „obcych” klasach
10.6. Śledzenie zmian dokonywanych w danej klasie
10.7. Weryfikacja atrybutów obiektu
10.8. Reagowanie na wywołania niezdefiniowanych metod
10.9. Automatyczne inicjowanie zmiennych instancyjnych
10.10. Oszczędne kodowanie dzięki metaprogramowaniu
10.11. Metaprogramowanie z użyciem ewaluacji łańcuchów
10.12. Ewaluacja kodu we wcześniejszym kontekście
10.13. Anulowanie definicji metody
10.14. Aliasowanie metod
10.15. Programowanie zorientowane aspektowo
10.16. Wywołania kontraktowane
11. XML i HTML .................................................................................................................395
396
398
11.1. Sprawdzanie poprawności dokumentu XML
11.2. Ekstrakcja informacji z drzewa dokumentu
Spis treści
|
9
11.3. Ekstrakcja informacji w trakcie analizy dokumentu XML
11.4. Nawigowanie po dokumencie za pomocą XPath
11.5. Parsowanie błędnych dokumentów
11.6. Konwertowanie dokumentu XML na hasz
11.7. Walidacja dokumentu XML
11.8. Zastępowanie encji XML
11.9. Tworzenie i modyfikowanie dokumentów XML
11.10. Kompresowanie białych znaków w dokumencie XML
11.11. Autodetekcja standardu kodowania znaków w dokumencie
11.12. Konwersja dokumentu między różnymi standardami kodowania
11.13. Ekstrakcja wszystkich adresów URL z dokumentu HTML
11.14. Transformacja tekstu otwartego na format HTML
11.15. Konwertowanie ściągniętego z internetu dokumentu HTML na tekst
11.16. Prosty czytnik kanałów
400
401
404
406
409
411
414
417
418
419
420
423
425
428
12. Formaty plików graficznych i innych ........................................................................ 433
433
436
439
441
444
447
449
451
453
455
458
460
461
465
12.1. Tworzenie miniaturek
12.2. Dodawanie tekstu do grafiki
12.3. Konwersja formatów plików graficznych
12.4. Tworzenie wykresów
12.5. Wprowadzanie graficznego kontekstu za pomocą wykresów typu Sparkline
12.6. Silne algorytmy szyfrowania danych
12.7. Przetwarzanie danych rozdzielonych przecinkami
12.8. Przetwarzanie plików tekstowych nie w pełni zgodnych z formatem CSV
12.9. Generowanie i przetwarzanie arkuszy Excela
12.10. Kompresowanie i archiwizowanie plików za pomocą narzędzi Gzip i Tar
12.11. Czytanie i zapisywanie plików ZIP
12.12. Czytanie i zapisywanie plików konfiguracyjnych
12.13. Generowanie plików PDF
12.14. Reprezentowanie danych za pomocą plików muzycznych MIDI
13. Bazy danych i trwałość obiektów ............................................................................. 469
472
475
476
13.1. Serializacja danych za pomocą biblioteki YAML
13.2. Serializacja danych z wykorzystaniem modułu Marshal
13.3. Utrwalanie obiektów z wykorzystaniem biblioteki Madeleine
13.4. Indeksowanie niestrukturalnego tekstu z wykorzystaniem
biblioteki SimpleSearch
13.5. Indeksowanie tekstu o określonej strukturze z wykorzystaniem
biblioteki Ferret
10
|
Spis treści
479
481
13.6. Wykorzystywanie baz danych Berkeley DB
13.7. Zarządzanie bazą danych MySQL w systemie Unix
13.8. Zliczanie wierszy zwracanych przez zapytanie
13.9. Bezpośrednia komunikacja z bazą danych MySQL
13.10. Bezpośrednia komunikacja z bazą danych PostgreSQL
13.11. Mapowanie obiektowo-relacyjne z wykorzystaniem
biblioteki ActiveRecord
13.12. Mapowanie obiektowo-relacyjne z wykorzystaniem
biblioteki Og
13.13. Programowe tworzenie zapytań
13.14. Sprawdzanie poprawności danych z wykorzystaniem
biblioteki ActiveRecord
13.15. Zapobieganie atakom typu SQL Injection
13.16. Obsługa transakcji z wykorzystaniem biblioteki ActiveRecord
13.17. Definiowanie haków dotyczących zdarzeń związanych z tabelami
13.18. Oznaczanie tabel bazy danych z wykorzystaniem modułów-domieszek
484
486
487
489
491
493
497
501
504
507
510
511
514
14. Usługi internetowe .....................................................................................................519
520
522
524
526
528
531
535
538
540
543
546
547
549
550
552
555
557
559
562
567
14.1. Pobieranie zawartości strony WWW
14.2. Obsługa żądań HTTPS
14.3. Dostosowywanie nagłówków żądań HTTP
14.4. Wykonywanie zapytań DNS
14.5. Wysyłanie poczty elektronicznej
14.6. Czytanie poczty z serwera IMAP
14.7. Czytanie poczty z wykorzystaniem protokołu POP3
14.8. Implementacja klienta FTP
14.9. Implementacja klienta telnet
14.10. Implementacja klienta SSH
14.11. Kopiowanie plików do innego komputera
14.12. Implementacja klienta BitTorrent
14.13. Wysyłanie sygnału ping do zdalnego komputera
14.14. Implementacja własnego serwera internetowego
14.15. Przetwarzanie adresów URL
14.16. Pisanie skryptów CGI
14.17. Ustawianie plików cookie i innych nagłówków odpowiedzi HTTP
14.18. Obsługa przesyłania plików na serwer z wykorzystaniem CGI
14.19. Uruchamianie serwletów WEBrick
14.20. Własny klient HTTP
Spis treści
|
11
15. Projektowanie aplikacji internetowych: Ruby on Rails ............................................ 571
573
576
578
581
582
585
588
590
594
595
596
599
601
602
15.1. Prosta aplikacja Rails wyświetlająca informacje o systemie
15.2. Przekazywanie danych ze sterownika do widoku
15.3. Tworzenie układu nagłówka i stopki
15.4. Przekierowania do innych lokalizacji
15.5. Wyświetlanie szablonów za pomocą metody render
15.6. Integracja baz danych z aplikacjami Rails
15.7. Reguły pluralizacji
15.8. Tworzenie systemu logowania
15.9. Zapisywanie haseł użytkowników w bazie danych w postaci skrótów
15.10. Unieszkodliwianie kodu HTML i JavaScript przed wyświetlaniem
15.11. Ustawianie i odczytywanie informacji o sesji
15.12. Ustawianie i odczytywanie plików cookie
15.13. Wyodrębnianie kodu do modułów pomocniczych
15.14. Rozdzielenie widoku na kilka części
15.15. Dodawanie efektów DHTML z wykorzystaniem
biblioteki script.aculo.us
15.16. Generowanie formularzy do modyfikowania obiektów modelu
15.17. Tworzenie formularzy Ajax
15.18. Udostępnianie usług sieciowych w witrynie WWW
15.19. Przesyłanie wiadomości pocztowych za pomocą aplikacji Rails
15.20. Automatyczne wysyłanie komunikatów o błędach pocztą elektroniczną
15.21. Tworzenie dokumentacji witryny WWW
15.22. Testy modułowe witryny WWW
15.23. Wykorzystywanie pułapek w aplikacjach internetowych
605
607
611
614
616
618
620
621
624
638
640
642
644
645
649
16. Usługi sieciowe i programowanie rozproszone .......................................................627
628
631
634
636
637
16.1. Wyszukiwanie książek w serwisie Amazon
16.2. Wyszukiwanie zdjęć w serwisie Flickr
16.3. Jak napisać klienta XML-RPC?
16.4. Jak napisać klienta SOAP?
16.5. Jak napisać serwer SOAP?
16.6. Wyszukiwanie w internecie z wykorzystaniem usługi sieciowej
serwisu Google
16.7. Wykorzystanie pliku WSDL w celu ułatwienia wywołań SOAP
16.8. Płatności kartami kredytowymi
16.9. Odczytywanie kosztów przesyłki w serwisie UPS lub FedEx
16.10. Współdzielenie haszów przez dowolną liczbę komputerów
16.11. Implementacja rozproszonej kolejki
12
|
Spis treści
16.12. Tworzenie współdzielonej „tablicy ogłoszeń”
16.13. Zabezpieczanie usług DRb za pomocą list kontroli dostępu
16.14. Automatyczne wykrywanie usług DRb z wykorzystaniem
biblioteki Rinda
16.15. Wykorzystanie obiektów pośredniczących
16.16. Zapisywanie danych w rozproszonej pamięci RAM z wykorzystaniem
systemu MemCached
16.17. Buforowanie kosztownych obliczeniowo wyników za pomocą
systemu MemCached
16.18. Zdalnie sterowana „szafa grająca”
650
653
654
656
659
661
664
17. Testowanie, debugowanie, optymalizacja i tworzenie dokumentacji ...................669
670
672
673
676
677
679
681
684
686
690
692
696
699
701
702
17.1. Uruchamianie kodu wyłącznie w trybie debugowania
17.2. Generowanie wyjątków
17.3. Obsługa wyjątków
17.4. Ponawianie próby wykonania kodu po wystąpieniu wyjątku
17.5. Mechanizmy rejestrowania zdarzeń w aplikacji
17.6. Tworzenie i interpretowanie stosu wywołań
17.7. Jak pisać testy modułowe?
17.8. Uruchamianie testów modułowych
17.9. Testowanie kodu korzystającego z zewnętrznych zasobów
17.10. Wykorzystanie pułapek do kontroli i modyfikacji stanu aplikacji
17.11. Tworzenie dokumentacji aplikacji
17.12. Profilowanie aplikacji
17.13. Pomiar wydajności alternatywnych rozwiązań
17.14. Wykorzystywanie wielu narzędzi analitycznych jednocześnie
17.15. Co wywołuje tę metodę? Graficzny analizator wywołań
18. Tworzenie pakietów oprogramowania i ich dystrybucja ........................................705
18.1. Wyszukiwanie bibliotek poprzez kierowanie zapytań
do repozytoriów gemów
18.2. Instalacja i korzystanie z gemów
18.3. Wymaganie określonej wersji gemu
18.4. Odinstalowywanie gemów
18.5. Czytanie dokumentacji zainstalowanych gemów
18.6. Tworzenie pakietów kodu w formacie gemów
18.7. Dystrybucja gemów
18.8. Instalacja i tworzenie samodzielnych pakietów z wykorzystaniem
skryptu setup.rb
706
709
711
714
715
717
719
722
Spis treści
|
13
19. Automatyzacja zadań z wykorzystaniem języka Rake ............................................725
727
729
731
733
734
737
738
740
19.1. Automatyczne uruchamianie testów modułowych
19.2. Automatyczne generowanie dokumentacji
19.3. Porządkowanie wygenerowanych plików
19.4. Automatyczne tworzenie gemów
19.5. Pobieranie informacji statystycznych dotyczących kodu
19.6. Publikowanie dokumentacji
19.7. Równoległe uruchamianie wielu zadań
19.8. Uniwersalny plik Rakefile
20. Wielozadaniowość i wielowątkowość ......................................................................747
748
751
754
756
758
760
763
766
20.1. Uruchamianie procesu-demona w systemie Unix
20.2. Tworzenie usług systemu Windows
20.3. Wykonywanie dwóch operacji jednocześnie z wykorzystaniem wątków
20.4. Synchronizacja dostępu do obiektu
20.5. Niszczenie wątków
20.6. Równoległe uruchamianie bloku kodu dla wielu obiektów
20.7. Ograniczanie liczby wątków z wykorzystaniem ich puli
20.8. Sterowanie zewnętrznym procesem za pomocą metody popen
20.9. Przechwytywanie strumienia wyjściowego i informacji o błędach
z polecenia powłoki w systemie Unix
20.10. Zarządzanie procesami w innym komputerze
20.11. Unikanie zakleszczeń
21.
Interfejs użytkownika .................................................................................................773
774
21.1. Pobieranie danych wejściowych wiersz po wierszu
21.2. Pobieranie danych wejściowych znak po znaku
776
778
21.3. Przetwarzanie argumentów wiersza polecenia
21.4. Sprawdzenie, czy program działa w trybie interaktywnym
781
21.5. Konfiguracja i porządkowanie po programie wykorzystującym
767
768
770
782
784
785
787
790
791
792
795
798
bibliotekę Curses
21.6. Czyszczenie ekranu
21.7. Określenie rozmiaru terminala
21.8. Zmiana koloru tekstu
21.9. Odczytywanie haseł
21.10. Edycja danych wejściowych z wykorzystaniem biblioteki Readline
21.11. Sterowanie migotaniem diod na klawiaturze
21.12. Tworzenie aplikacji GUI z wykorzystaniem biblioteki Tk
21.13. Tworzenie aplikacji GUI z wykorzystaniem biblioteki wxRuby
14
|
Spis treści
21.14. Tworzenie aplikacji GUI z wykorzystaniem biblioteki Ruby/GTK
21.15. Tworzenie aplikacji Mac OS X z wykorzystaniem biblioteki RubyCocoa
21.16. Wykorzystanie AppleScript do pobierania danych wejściowych
od użytkownika
802
805
812
22. Rozszerzenia języka Ruby z wykorzystaniem innych języków ...............................815
816
819
822
825
827
22.1. Pisanie rozszerzeń w języku C dla języka Ruby
22.2. Korzystanie z bibliotek języka C z poziomu kodu Ruby
22.3. Wywoływanie bibliotek języka C za pomocą narzędzia SWIG
22.4. Kod w języku C wstawiany w kodzie Ruby
22.5. Korzystanie z bibliotek Javy za pośrednictwem interpretera JRuby
23. Administrowanie systemem ......................................................................................831
832
833
835
836
23.1. Pisanie skryptów zarządzających zewnętrznymi programami
23.2. Zarządzanie usługami systemu Windows
23.3. Uruchamianie kodu w imieniu innego użytkownika
23.4. Okresowe uruchamianie zadań bez używania mechanizmu cron lub at
23.5. Usuwanie plików, których nazwy spełniają kryteria określone
przez wyrażenie regularne
23.6. Zmiana nazw grupy plików
23.7. Wyszukiwanie plików zdublowanych
23.8. Automatyczne wykonywanie kopii zapasowych
23.9. Ujednolicanie własności i uprawnień w katalogach użytkowników
23.10. Niszczenie wszystkich procesów wybranego użytkownika
838
840
842
845
846
849
Skorowidz ................................................................................................................... 853
Spis treści
|
15
ROZDZIAŁ 1.
Łańcuchy
Ruby jest językiem przyjaznym programiście. Przed programistami hołdującymi filozofii pro-
gramowania zorientowanego obiektowo odkryje on drugą jego naturę; programiści stroniący
od obiektów nie powinni mieć natomiast większych trudności, bowiem — w odróżnieniu od
wielu innych języków — w języku Ruby stosuje się zwięzłe i konsekwentne nazewnictwo me-
tod, które generalnie zachowują się tak, jak (intuicyjnie) można by tego oczekiwać.
Łańcuchy znakomicie nadają się na obszar „pierwszego kontaktu” z językiem Ruby: są uży-
teczne, łatwo się je tworzy i wykorzystuje, występują w większości języków, a więc służyć
mogą zarówno jako materiał porównawczy, jak i okazja do przedstawienia koncepcyjnych
nowości języka Ruby w rodzaju duck typing (receptura 1.12), otwartych klas (receptura 1.10),
symboli (receptura 1.7), a nawet gemów (receptura 1.20).
Omawiane koncepcje ilustrujemy konsekwentnie interaktywnymi sesjami języka Ruby. W śro-
dowisku Uniksa i Mac OS X służy do tego program irb, uruchamiany z wiersza poleceń. Użyt-
kownicy Windows mogą także wykorzystywać w tym celu program fxri, dostępny za pomo-
cą menu Start (po zainstalowaniu środowiska Ruby za pomocą pakietu „one-click installer”,
który pobrać można spod adresu http:/rubyforge.org/projects/rubyinstaller). Program irb jest rów-
nież dostępny w Windows. Wspomniane programy tworzą po uruchomieniu interaktywną po-
włokę języka Ruby, pod kontrolą której wykonywać można fragmenty przykładowego kodu.
Łańcuchy języka Ruby podobne są do łańcuchów w innych „dynamicznych” językach — Perlu,
Pythonie czy PHP. Nie różnią się zbytnio od łańcuchów znanych z języków C i Java. Są dyna-
miczne, elastyczne i modyfikowalne.
Rozpocznijmy więc naszą sesję, wpisując do wiersza poleceń powłoki następujący tekst:
string = To jest napis
Spowoduje to wyświetlenie rezultatu wykonania polecenia:
= To jest napis
Polecenie to powoduje utworzenie łańcucha To jest napis i przypisanie go zmiennej o na-
zwie string. Łańcuch ten staje się więc wartością zmiennej i jednocześnie wartością całego wy-
rażenia, co uwidocznione zostaje w postaci wyniku wypisywanego (po strzałce = ) w ramach
interaktywnej sesji. W treści książki zapisywać będziemy ten rodzaj interakcji w postaci
string To jest napis = To jest napis
W języku Ruby wszystko, co można przypisać zmiennej, jest obiektem. W powyższym przy-
kładzie zmienna string wskazuje na obiekt klasy String. Klasa ta definiuje ponad sto metod
29
— nazwanych fragmentów kodu służących do wykonywania rozmaitych operacji na łańcu-
chach. Wiele z tych metod wykorzystywać będziemy w naszej książce, także w niniejszym
rozdziale. Jedna z tych metod — String#length — zwraca rozmiar łańcucha, czyli liczbę skła-
dających się na niego bajtów:
string.length = 13
W wielu językach programowania wymagana (lub dopuszczalna) jest para nawiasów po na-
zwie wywoływanej metody:
string.length() = 13
W języku Ruby nawiasy te są niemal zawsze nieobowiązkowe, szczególnie w sytuacji, gdy
do wywoływanej metody nie są przekazywane żadne parametry (jak w powyższym przykła-
dzie). Gdy parametry takie są przekazywane, użycie nawiasów może uczynić całą konstruk-
cję bardziej czytelną:
string.count s = 2 # s występuje dwukrotnie
string.count( s ) = 2
Wartość zwracana przez metodę sama z siebie jest obiektem. W przypadku metody String#
length, wywoływanej w powyższym przykładzie, obiekt ten jest liczbą 20, czyli egzempla-
rzem (instancją) klasy Fixnum. Ponieważ jest obiektem, można na jego rzecz także wywoły-
wać metody:
string.length.next = 14
Weźmy teraz pod uwagę bardziej ciekawy przypadek — łańcuch zawierający znaki spoza
kodu ASCII. Poniższy łańcuch reprezentuje francuskie zdanie „il était une fois” zakodowane
według UTF-81:
french_string = il xc3xa9tait une fois # = il 303251tait une fois
Wiele języków programowania, między innymi Java, traktuje łańcuchy jako ciągi znaków.
W języku Ruby łańcuch postrzegany jest jako ciąg bajtów. Ponieważ powyższy łańcuch za-
wiera 14 liter i 3 spacje, można by domniemywać, że jego długość wynosi 17; ponieważ jedna
z liter jest znakiem dwubajtowym, łańcuch składa się z 18, nie 17 bajtów:
french_string.length # = 18
Do różnorodnego kodowania znaków powrócimy w recepturach 1.14 i 11.12; specyfiką łań-
cuchów zawierających znaki wielobajtowe zajmiemy się także w recepturze 1.8.
Znaki specjalne (tak jak binarne dane w łańcuchu french_string) mogą być reprezentowane
za pomocą tzw. sekwencji unikowych (escaping). Ruby udostępnia kilka rodzajów takich se-
kwencji w zależności od tego, w jaki sposób tworzony jest dany łańcuch. Jeżeli mianowicie
łańcuch ujęty jest w cudzysłów ( ... ), można w nim kodować zarówno znaki binarne (jak
w przykładowym łańcuchu francuskim), jak i znak nowego wiersza
znany z wielu innych
języków:
puts Ten łańcuch
zawiera znak nowego wiersza
# Ten łańcuch
# zawiera znak nowego wiersza
W łańcuchu zamkniętym znakami apostrofu ( ... ) jedynym dopuszczalnym znakiem
specjalnym jest odwrotny ukośnik (backslash), umożliwiający reprezentowanie pojedyncze-
go znaku specjalnego; para \ reprezentuje pojedynczy backslash.
1 xc3xa9 jest zapisem w języku Ruby unikodowego znaku é w reprezentacji UTF-8.
30
|
Rozdział 1. Łańcuchy
puts Ten łańcuch wbrew pozorom
niezawiera znaku nowego wiersza
# Ten łańcuch wbrew pozorom
niezawiera znaku nowego wiersza
puts To jest odwrotny ukośnik: \
# To jest odwrotny ukośnik:
Do kwestii tej powrócimy w recepturze 1.5, a w recepturach 1.2 i 1.3 zajmiemy się bardziej
spektakularnymi możliwościami łańcuchów ograniczonych apostrofami.
Oto inna użyteczna możliwość inicjowania łańcucha, nazywana w języku Ruby here documents:
long_string = EOF
To jest długi łańcuch
Składający się z kilku akapitów
EOF
# = To jest długi łańcuch
Składający się z kilku akapitów
puts long_string
# To jest długi łańcuch
# Składający się z kilku akapitów
Podobnie jak w przypadku większości wbudowanych klas języka Ruby, także w przypadku
łańcuchów można kodować tę samą funkcjonalność na wiele różnych sposobów („idiomów”)
i programista może dokonać wyboru tego, który odpowiada mu najbardziej. Weźmy jako przy-
kład ekstrakcję podłańcucha z długiego łańcucha: programiści preferujący podejście obiekto-
we zapewne użyliby do tego celu metody String#slice:
string # To jest napis
string.slice(3,4) # jest
Programiści wywodzący swe nawyki z języka C skłonni są jednak do traktowania łańcuchów
jako tablic bajtów — im także Ruby wychodzi naprzeciw, umożliwiając ekstrakcję poszczegól-
nych bajtów łańcucha:
string[3].chr + string[4].chr + string[5].chr + string[6].chr
# = jest
Podobnie proste zadanie mają programiści przywykli do Pythona:
string[3, 4] # = jest
W przeciwieństwie do wielu innych języków programowania, łańcuchy Ruby są modyfiko-
walne (mutable) — można je zmieniać już po zadeklarowaniu. Oto wynik działania dwóch me-
tod: String#upcase i String#upcase! — zwróć uwagę na istotną różnicę między nimi:
string.upcase # = TO JEST NAPIS
string # = To jest napis
string.upcase! # = TO JEST NAPIS
string # = TO JEST NAPIS
Przy okazji widoczna staje się jedna z konwencji składniowych języka Ruby: metody „nie-
bezpieczne” — czyli głównie te modyfikujące obiekty „w miejscu” — opatrywane są nazwami
kończącymi się wykrzyknikiem. Inna konwencja syntaktyczna związana jest z predykatami,
czyli metodami zwracającymi wartość true albo false — ich nazwy kończą się znakiem za-
pytania.
string.empty? # = false
string.include? To # = true
Użycie znaków przestankowych charakterystycznych dla języka potocznego, w celu uczynie-
nia kodu bardziej czytelnym dla programisty, jest odzwierciedleniem filozofii twórcy języka
Ruby, Yukihiro „Matza” Matsumoto, zgodnie z którą to filozofią Ruby powinien być czytelny
1.1. Budowanie łańcucha z części
|
31
przede wszystkim dla ludzi, zaś możliwość wykonywania zapisanych w nim programów przez
interpreter jest kwestią wtórną.
Interaktywne sesje języka Ruby są niezastąpionym narzędziem umożliwiającym poznawanie
metod języka i praktyczne eksperymentowanie z nimi. Ponownie zachęcamy do osobistego
sprawdzania prezentowanego kodu w ramach sesji programu irb lub fxri, a z biegiem cza-
su tworzenia i testowania także własnych przykładów.
Dodatkowe informacje na temat łańcuchów Ruby można uzyskać z następujących źródeł:
• Informację o dowolnej wbudowanej metodzie języka można otrzymać wprost w oknie
programu fxri, wybierając odnośną pozycję w lewym panelu. W programie irb można
użyć w tym celu polecenia ri — na przykład informację o metodzie String#upcase! uzy-
skamy za pomocą polecenia
ri String#upcase!
• why the lucky stiff napisał wspaniałe wprowadzenie do instalacji języka Ruby oraz wyko-
rzystywania poleceń ir i irb. Jest ono dostępne pod adresem http://poignantguide.net/ruby/
expansion-pak-1.html.
• Filozofię projektową języka Ruby przedstawia jego autor, Yukihiro „Matz” Matsumoto,
w wywiadzie dostępnym pod adresem http://www.artima.com/intv/ruby.html.
1.1. Budowanie łańcucha z części
Problem
Iterując po strukturze danych, należy zbudować łańcuch reprezentujący kolejne kroki tej
iteracji.
Rozwiązanie
Istnieją dwa efektywne rozwiązania tego problemu. W najprostszym przypadku rozpoczy-
namy od łańcucha pustego, sukcesywnie dołączając do niego podłańcuchy za pomocą ope-
ratora :
hash = { key1 = val1 , key2 = val2 }
string =
hash.each { |k,v| string #{k} is #{v}
}
puts string
# key1 is val1
# key2 is val2
Poniższa odmiana tego prostego rozwiązania jest nieco efektywniejsza, chociaż mniej czytelna:
string =
hash.each { |k,v| string k is v
}
Jeśli wspomnianą strukturą danych jest tablica, bądź też struktura ta daje się łatwo przetrans-
formować na tablicę, zwykle bardziej efektywne rozwiązanie można uzyskać za pomocą me-
tody Array#join:
puts hash.keys.join(
) +
# key1
# key2
32
|
Rozdział 1. Łańcuchy
Dyskusja
W językach takich jak Pyton czy Java sukcesywne dołączanie podłańcuchów do pustego po-
czątkowo łańcucha jest rozwiązaniem bardzo nieefektywnym. Łańcuchy w tych językach są
niemodyfikowalne (immutable), a więc każdorazowe dołączenie podłańcucha wiąże się ze
stworzeniem nowego obiektu. Dołączanie serii podłańcuchów oznacza więc tworzenie dużej
liczby obiektów pośrednich, z których każdy stanowi jedynie „pomost” do następnego etapu.
W praktyce przekłada się to na marnotrawstwo czasu i pamięci.
W tych warunkach rozwiązaniem najbardziej efektywnym byłoby zapisanie poszczególnych
podłańcuchów w tablicy (lub innej modyfikowalnej strukturze) zdolnej do dynamicznego roz-
szerzania się. Gdy wszystkie podłańcuchy zostaną już zmagazynowane we wspomnianej ta-
blicy, można połączyć je w pojedynczy łańcuch za pomocą operatora stanowiącego odpowied-
nik metody Array#join języka Ruby. W języku Java zadanie to spełnia klasa StringBuffer.
Unikamy w ten sposób tworzenia wspomnianych obiektów pośrednich.
W języku Ruby sprawa ma się zgoła inaczej, bo łańcuchy są tu modyfikowalne, podobnie jak
tablice. Mogą więc być rozszerzane w miarę potrzeby, bez zbytniego obciążania pamięci lub
procesora. W najszybszym wariancie rozwiązania możemy więc w ogóle zapomnieć o tabli-
cy pośredniczącej i umieszczać poszczególne podłańcuchy bezpośrednio wewnątrz łańcucha
docelowego. Niekiedy skorzystanie z Array#join okazuje się szybsze, lecz zwykle niewiele
szybsze, ponadto konstrukcja oparta na jest generalnie łatwiejsza do zrozumienia.
W sytuacji, gdy efektywność jest czynnikiem krytycznym, nie należy tworzyć nowych łańcu-
chów, jeżeli możliwe jest dołączanie podłańcuchów do łańcucha istniejącego. Konstrukcje
w rodzaju
str a + b
czy
str #{var1} #{var2}
powodują tworzenie nowych łańcuchów, które natychmiast „podłączane” są do większego
łańcucha — a tego właśnie chcielibyśmy unikać. Umożliwia nam to konstrukcja
str var1 var2
Z drugiej jednak strony, nie powinno się modyfikować łańcuchów nietworzonych przez sie-
bie i względy bezpieczeństwa przemawiają za tworzeniem nowego łańcucha. Gdy definiujesz
metodę otrzymującą łańcuch jako parametr, metoda ta nie powinna modyfikować owego łań-
cucha przez dołączanie podłańcuchów na jego końcu — chyba że właśnie to jest celem meto-
dy (której nazwa powinna tym samym kończyć się wykrzyknikiem, dla zwrócenia szczegól-
nej uwagi osoby studiującej kod programu).
Przy okazji ważna uwaga: działanie metody Array#join nie jest dokładnie równoważne do-
łączaniu kolejnych podłańcuchów do łańcucha. Array#join akceptuje separator, który wsta-
wiany jest między każde dwa sąsiednie elementy tablicy; w przeciwieństwie do sukcesywne-
go dołączania podłańcuchów, separator ten nie jest umieszczany po ostatnim elemencie. Różnicę
tę ilustruje poniższy przykład:
data = [ 1 , 2 , 3 ]
s =
data.each { |x| s x oraz }
s # = 1 oraz 2 oraz 3 oraz
data.join( oraz ) # = 1 oraz 2 oraz 3
1.1. Budowanie łańcucha z części
|
33
Aby zasymulować działanie Array#join za pomocą iteracji, można wykorzystać metodę
Enumerable#each_with_index, opuszczając separator dla ostatniego indeksu. Da się to jed-
nak zrobić tylko wówczas, gdy liczba elementów objętych enumeracją znana jest a priori:
s =
data.each_with_index { |x, i| s x; s | if i data.length-1 }
s # = 1|2|3
1.2. Zastępowanie zmiennych w tworzonym łańcuchu
Problem
Należy stworzyć łańcuch zawierający reprezentację zmiennej lub wyrażenia języka Ruby.
Rozwiązanie
Należy wewnątrz łańcucha zamknąć zmienną lub wyrażenie w nawiasy klamrowe i poprze-
dzić tę konstrukcję znakiem # (hash).
liczba = 5
Liczba jest równa #{liczba}. # = Liczba jest równa 5.
Liczba jest równa #{5}. # = Liczba jest równa 5.
Liczba następna po #{liczba} równa jest #{liczba.next}.
# = Liczba następna po 5 równa jest 6.
Liczba poprzedzająca #{liczba} równa jest #{liczba-1}.
# = Liczba poprzedzająca 5 równa jest 4.
To jest ##{number}! # = To jest #5!
Dyskusja
Łańcuch ujęty w cudzysłów ( ... ) jest przez interpreter skanowany pod kątem obecności
specjalnych kodów substytucyjnych. Jednym z najbardziej elementarnych i najczęściej używa-
nych kodów tego typu jest znak
, zastępowany znakiem nowego wiersza.
Oczywiście istnieją bardziej skomplikowane kody substytucyjne. W szczególności dowolny
tekst zamknięty w nawiasy klamrowe poprzedzone znakiem # (czyli konstrukcja #{tekst})
interpretowany jest jako wyrażenie języka Ruby, a cała konstrukcja zastępowana jest w łań-
cuchu wartością tego wyrażenia. Jeżeli wartość ta nie jest łańcuchem, Ruby dokonuje jej kon-
wersji na łańcuch za pomocą metody to_s. Proces ten nosi nazwę interpolacji.
Tak powstały łańcuch staje się nieodróżnialny od łańcucha, w którym interpolacji nie zasto-
sowano:
#{liczba} == 5 # = true
Za pomocą interpolacji można umieszczać w łańcuchu nawet spore porcje tekstu. Przypad-
kiem ekstremalnym jest definiowanie klasy wewnątrz łańcucha i wykorzystanie podłańcucha
stanowiącego wynik wykonania określonej metody tej klasy. Mimo ograniczonej raczej uży-
teczności tego mechanizmu, warto go zapamiętać jako dowód wspaniałych możliwości języ-
ka Ruby.
{Tutaj jest #{class InstantClass
def bar
pewien tekst
end
end
34
|
Rozdział 1. Łańcuchy
InstantClass.new.bar
}.}
# = Tutaj jest pewien tekst.
Kod wykonywany w ramach interpolacji funkcjonuje dokładnie tak samo, jak każdy inny kod
Ruby w tej samej lokalizacji. Definiowana w powyższym przykładzie klasa InstantClass nie
różni się od innych klas i może być używana także na zewnątrz łańcucha.
Gdy w ramach interpolacji wywoływana jest metoda powodująca efekty uboczne, efekty te
widoczne są na zewnątrz łańcucha. W szczególności, jeżeli efektem ubocznym jest nadanie war-
tości jakiejś zmiennej, zmienna ta zachowuje tę wartość na zewnątrz łańcucha. Mimo iż nie po-
lecamy celowego polegania na tej własności, należy koniecznie mieć świadomość jej istnienia.
Zmiennej x nadano wartość #{x = 5; x += 1}. # = Zmiennej x nadano wartość 6.
x # = 6
Jeżeli chcielibyśmy potraktować występującą w łańcuchu sekwencję #{tekst} w sposób lite-
ralny, nie jako polecenie interpolacji, wystarczy poprzedzić ją znakiem odwrotnego ukośnika
() albo zamknąć łańcuch znakami apostrofu zamiast cudzysłowu:
#{foo} # = #{foo}
#{foo} # = #{foo}
Alternatywnym dla {} kodem substytucyjnym jest konstrukcja here document. Pozwala ona
na zdefiniowanie wielowierszowego łańcucha, którego ogranicznikiem jest wiersz o wyróż-
nionej postaci.
name = Mr. Lorum
email = END
Szanowny #{name},
Niestety, nie możemy pozytywnie rozpatrzyć Pańskiej reklamacji w związku
z oszacowaniem szkody, gdyż jesteśmy piekarnią, nie firmą ubezpieczeniową.
Podpisano,
Buła, Rogal i Precel
Piekarze Jej Królewskiej Wysokości
END
Język Ruby pozostawia programiście dużą swobodę w zakresie wyboru postaci wiersza ogra-
niczającego:
koniec_wiersza
Pewien poeta z Afryki
Pisał dwuwierszowe limeryki
koniec_wiersza
# = Pewien poeta z Afryki
Pisał dwuwierszowe limeryki
Patrz także
• Za pomocą techniki opisanej w recepturze 1.3 można definiować łańcuchy i obiekty sza-
blonowe, umożliwiające „odroczoną” interpolację.
1.3. Zastępowanie zmiennych w istniejącym łańcuchu
Problem
Należy utworzyć łańcuch umożliwiający interpolację wyrażenia języka Ruby, jednakże bez
wykonywania tej interpolacji — ta wykonana zostanie później, prawdopodobnie wtedy, gdy
będą znane wartości zastępowanych wyrażeń.
1.3. Zastępowanie zmiennych w istniejącym łańcuchu
|
35
Rozwiązanie
Problem można rozwiązać za pomocą dwojakiego rodzaju środków: łańcuchów typu printf
oraz szablonów ERB.
Ruby zapewnia wsparcie dla znanych z C i Pythona łańcuchów formatujących typu printf.
Kody substytucyjne w ramach tych łańcuchów mają postać dyrektyw rozpoczynających się
od znaku (modulo):
template = Oceania zawsze była w stanie wojny z s.
template Eurazją
# = Oceania zawsze była w stanie wojny z Eurazją.
template Antarktydą
# = Oceania zawsze była w stanie wojny z Antarktydą.
Z dwoma miejscami dziesiętnymi: .2f Math::PI
# = Z dwoma miejscami dziesiętnymi: 3.14
Dopełnione zerami: .5d Math::PI # = Dopełnione zerami: 00003
Szablony ERB przypominają swą postacią kod w języku JSP lub PHP. Zasadniczo szablon
ERB traktowany jest jako „normalny” łańcuch, jednak pewne sekwencje sterujące traktowane
są jako kod w języku Ruby lub aktualne wartości wyrażeń:
template = ERB.new q{Pyszne = food !}
food = kiełbaski
template.result(binding) # = Pyszne kiełbaski!
food = masło orzechowe
template.result(binding) # = Pyszne masło orzechowe!
Poza sesją irb można pominąć wywołania metody Kernel#binding:
puts template.result
# Pyszne masło orzechowe!
Szablony ERB wykorzystywane są wewnętrznie przez widoki Rails i łatwo można rozpoznać
je w plikach .rhtml.
Dyskusja
W szablonach ERB można odwoływać się do zmiennych (jak food w powyższym przykła-
dzie), zanim zmienne te zostaną zdefiniowane. Wskutek wywołania metody ERB#result lub
ERB#run szablon jest wartościowany zgodnie z bieżącymi wartościami tych zmiennych.
Podobnie jak kod w języku JSP i PHP, szablony ERB mogą zawierać pętle i rozgałęzienia wa-
runkowe. Oto przykład rozbudowanego szablonu ERB:
template = q{
if problems.empty?
Wygląda na to, że w kodzie nie ma błędów!
else
W kodzie kryją się nastepujące potencjalne problemy:
problems.each do |problem, line|
* = problem w wierszu = line
end
end }.gsub(/^s+/, )
template = ERB.new(template, nil, )
problems = [[ Użyj is_a? zamiast duck typing , 23],
[ eval() jest potencjalnie niebezpieczne , 44]]
template.run(binding)
36
|
Rozdział 1. Łańcuchy
# W kodzie kryją się nastepujące potencjalne problemy:
# * Użyj is_a? zamiast duck typing w wierszu 23
# * eval() jest potencjalnie niebezpieczne w wierszu 44
problems = []
template.run(binding)
# Wygląda na to, że w kodzie nie ma błędów!
ERB jest wyrafinowanym mechanizmem, jednak ani szablony ERB, ani łańcuchy typu printf
nie przypominają w niczym prostych podstawień prezentowanych w recepturze 1.2. Podsta-
wienia te nie są aktywowane, jeśli łańcuch ujęty jest w apostrofy ( ... ) zamiast w cudzy-
słów ( ... ). Można wykorzystać ten fakt do zbudowania szablonu zawierającego meto-
dę eval:
class String
def substitute(binding=TOPLEVEL_BINDING)
eval( { #{self} }, binding)
end
end
template = q{Pyszne #{food}!} # = Pyszne #{food}!
food = kiełbaski
template.substitute(binding) # = Pyszne kiełbaski!
food = masło orzechowe
template.substitute(binding) # = Pyszne masło orzechowe!
Należy zachować szczególną ostrożność, używając metody eval, bowiem potencjalnie stwarza
ona możliwość wykonania dowolnego kodu, z czego skwapliwie skorzystać może ewentual-
ny włamywacz. Nie zdarzy się to jednak w poniższym przykładzie, jako że dowolna wartość
zmiennej food wstawiona zostaje do łańcucha jeszcze przed jego interpolacją:
food = #{system( dir )}
puts template.substitute(binding)
# Pyszne #{system( dir )}!
Patrz także
• Powyżej prezentowaliśmy proste przykłady szablonów ERB; przykłady bardziej skom-
plikowane znaleźć można w dokumentacji klas ERB pod adresem http://www.ruby-doc.org/
stdlib/libdoc/erb/rdoc/classes/ERB.html.
• Receptura 1.2, „Zastępowanie zmiennych w tworzonym łańcuchu”.
• Receptura 10.12, „Ewaluacja kodu we wcześniejszym kontekście”, zawiera informacje na
temat obiektów Binding.
1.4. Odwracanie kolejności słów lub znaków
w łańcuchu
Problem
Znaki lub słowa występują w łańcuchu w niewłaściwej kolejności.
1.4. Odwracanie kolejności słów lub znaków w łańcuchu
|
37
Rozwiązanie
Do stworzenia nowego łańcucha, zawierającego znaki łańcucha oryginalnego w odwrotnej
kolejności, można posłużyć się metodą reverse:
s = .kapo an sipan tsej oT
s.reverse # = To jest napis na opak.
s # = .kapo an sipan tsej oT
s.reverse! # = To jest napis na opak.
s # = To jest napis na opak.
W celu odwrócenia kolejności słów w łańcuchu, należy podzielić go najpierw na podłańcuchy
oddzielone „białymi” znakami2 (czyli poszczególne słowa), po czym włączyć listę tych słów
z powrotem do łańcucha, w odwrotnej kolejności.
s = kolei. po nie Wyrazy
s.split(/(s+)/).reverse!.join( ) # = Wyrazy nie po kolei.
s.split(//).reverse!.join( ) # = Wyrazy nie po. kolei
Dyskusja
Metoda String#split wykorzystuje wyrażenie regularne w roli separatora. Każdorazowo
gdy element łańcucha udaje się dopasować do tego wyrażenia, poprzedzająca go część łańcu-
cha włączona zostaje do listy, a metoda split przechodzi do skanowania dalszej części łań-
cucha. Efektem przeskalowania całego łańcucha jest lista podłańcuchów znajdujących się mię-
dzy wystąpieniami separatora. Użyte w naszym przykładzie wyrażenie regularne /(s+)/
reprezentuje dowolny ciąg „białych” znaków, zatem metoda split dokonuje podziału łańcu-
cha na poszczególne słowa (w potocznym rozumieniu).
Wyrażenie regularne /b reprezentuje granicę słowa; to nie to samo co „biały” znak, bowiem
granicę słowa może także wyznaczać znak interpunkcyjny. Zwróć uwagę na konsekwencje
tej różnicy w powyższym przykładzie.
Ponieważ wyrażenie regularne /(s+)/ zawiera parę nawiasów, separatory także są włącza-
ne do listy wynikowej. Jeżeli zatem zestawimy elementy tej listy w kolejności odwrotnej, se-
paratory oddzielające słowa będą już obecne na swych miejscach. Poniższy przykład ilustruje
różnicę między zachowywaniem a ignorowaniem separatorów:
Trzy banalne wyrazy .split(/s+/) # = [ Trzy , banalne , wyrazy ]
Trzy banalne wyrazy .split(/(s+)/)
# = [ Trzy , , banalne , , wyrazy ]
Patrz także
• Receptura 1.9, „Przetwarzanie poszczególnych słów łańcucha”, ilustruje kilka wyrażeń
regularnych wyznaczających alternatywną definicję „słowa”.
• Receptura 1.11, „Zarządzanie białymi znakami”.
• Receptura 1.17, „Dopasowywanie łańcuchów za pomocą wyrażeń regularnych”.
2 Pojęcie „białego znaku” wyjaśnione jest w recepturze 1.11 — przyp. tłum.
38
|
Rozdział 1. Łańcuchy
1.5. Reprezentowanie znaków niedrukowalnych
Problem
Należy stworzyć łańcuch zawierający znaki sterujące, znaki w kodzie UTF-8 lub dowolne znaki
niedostępne z klawiatury.
Rozwiązanie
Ruby udostępnia kilka mechanizmów unikowych (escape) w celu reprezentowania znaków
niedrukowalnych. W łańcuchach ujętych w cudzysłowy mechanizmy te umożliwiają repre-
zentowanie dowolnych znaków.
Dowolny znak można zakodować w łańcuchu, podając jego kod ósemkowy (octal) w formie
ooo lub kod szesnastkowy (hexadecimal) w formie xhh.
octal = 00 01 10 20
octal.each_byte { |x| puts x }
# 0
# 1
# 8
# 16
hexadecimal = x00x01x10x20
hexadecimal.each_byte { |x| puts x }
# 0
# 1
# 16
# 32
W ten sposób umieszczać można w łańcuchach znaki, których nie można wprowadzić bez-
pośrednio z klawiatury czy nawet wyświetlić na ekranie terminala. Uruchom poniższy pro-
gram, po czym otwórz wygenerowany plik smiley.html w przeglądarce WWW.
open( smiley.html , wb ) do |f|
f meta http-equiv= Content-Type content= text/html;charset=UTF-8
f xe2x98xBA
end
Niektóre z niedrukowalnych znaków — te wykorzystywane najczęściej — posiadają specjal-
ne, skrócone kody unikowe:
a == x07 # = true #ASCII 0x07 = BEL (Dźwięk systemowy)
== x08 # = true #ASCII 0x08 = BS (Cofanie)
e == x1b # = true #ASCII 0x1B = ESC (Escape)
f == x0c # = true #ASCII 0x0C = FF (Nowa strona)
== x0a # = true #ASCII 0x0A = LF (Nowy wiersz)
== x0d # = true #ASCII 0x0D = CR (Początek wiersza)
== x09 # = true #ASCII 0x09 = HT (Tabulacja pozioma)
v == x0b # = true #ASCII 0x0B = VT (Tabulacja pionowa)
Dyskusja
W języku Ruby łańcuchy są ciągami bajtów. Nie ma znaczenia, czy bajty te są drukowalny-
mi znakami ASCII, niedrukowalnymi znakami binarnymi, czy też mieszanką obydwu tych
kategorii.
1.5. Reprezentowanie znaków niedrukowalnych
|
39
Znaki niedrukowalne wyświetlane są w języku Ruby w czytelnej dla człowieka reprezentacji
ooo, gdzie ooo jest kodem znaku w reprezentacji ósemkowej; znaki posiadające reprezenta-
cję mnemoniczną w postaci znak wyświetlane są jednak w tej właśnie postaci. Znaki dru-
kowalne wyświetlane są zawsze w swej naturalnej postaci, nawet jeżeli w tworzonym łańcu-
chu zakodowane zostały w inny sposób.
x10x11xfexff # = 20 21376377
x48145x6cx6c157x0a # = Hello
Znak odwrotnego ukośnika () reprezentowany jest przez parę takich ukośników (\) — jest
to konieczne dla odróżnienia literalnego użycia znaku od mnemonicznej sekwencji uniko-
wej rozpoczynającej się od takiego znaku. Przykładowo, łańcuch \n składa się z dwóch
znaków: odwrotnego ukośnika i litery n.
\ .size # = 1
\ == x5c # = true
\n [0] == ?\ # = true
\n [1] == ?n # = true
\n =~ /
/ # = nil
Ruby udostępnia także kilka wygodnych skrótów dla reprezentowania kombinacji klawiszy
w rodzaju Ctrl+C. Sekwencja C- znak oznacza rezultat naciśnięcia klawisza znak z jed-
noczesnym przytrzymaniem klawisza Ctrl; analogicznie sekwencja M- znak oznacza rezul-
tat naciśnięcia klawisza znak z jednoczesnym przytrzymaniem klawisza Alt (lub Meta):
C-aC-bC-c # = 01 02 03 # Ctrl+A Ctrl+B Ctrl+C
M-aM-bM-c # = 341342343 # Alt+A Alt+B Alt+C
Dowolna z opisywanych sekwencji może pojawić się wszędzie tam, gdzie Ruby spodziewa
się znaku. W szczególności możliwe jest wyświetlenie kodu znaku w postaci dziesiętnej — na-
leży w tym celu poprzedzić ów znak znakiem zapytania (?).
?C-a # = 1
?M-z # = 250
W podobny sposób można używać rozmaitych reprezentacji znaków do specyfikowania za-
kresu znaków w wyrażeniach regularnych:
contains_control_chars = /[C-a-C-^]/
Foobar =~ contains_control_chars # = nil
FooC-zbar =~ contains_control_chars # = 3
contains_upper_chars = /[x80-xff]/
Foobar =~ contains_upper_chars # = nil
Foo212bar =~ contains_upper_chars # = 3
Poniższa aplikacja śledzi („szpieguje”) naciśnięcia klawiszy, reagując na niektóre kombinacje
specjalne:
def snoop_on_keylog(input)
input.each_byte do |b|
case b
when ?C-c; puts Ctrl+C: zatrzymać proces?
when ?C-z; puts Ctrl+Z: zawiesić proces?
when ?
; puts Nowy wiersz.
when ?M-x; puts Alt+X: uruchomić Emacs?
end
end
end
snoop_on_keylog( ls -ltR 03emacsHello 12370rot13-other-window 12 32 )
# Ctrl+C: zatrzymać proces?
40
|
Rozdział 1. Łańcuchy
# Nowy wiersz.
# Alt+X: uruchomić Emacs?
# Nowy wiersz.
# Ctrl+Z: zawiesić proces?
Sekwencje reprezentujące znaki specjalne interpretowane są tylko w łańcuchach ujętych
w cudzysłów oraz łańcuchach tworzonych za pomocą konstrukcji {} lub Q{}. Nie są one
interpretowane w łańcuchach zamkniętych znakami apostrofu oraz łańcuchach tworzonych
za pomocą konstrukcji q{}. Fakt ten można wykorzystać w przypadku potrzeby literalnego
wyświetlenia sekwencji reprezentujących znaki specjalne oraz w przypadku tworzenia łańcu-
chów zawierających dużą liczbę odwrotnych ukośników.
puts foo bar
# foo bar
puts {foo bar}
# foo bar
puts Q{foo bar}
# foo bar
puts foo bar
# foo bar
puts q{foo bar}
# foo bar
Nieinterpretowanie sekwencji reprezentujących znaki specjalne w łańcuchach zamkniętych
znakami apostrofu może wydać się dziwne — i niekiedy nieco kłopotliwe — programistom
przyzwyczajonym do języka Python. Jeżeli łańcuch ujęty w cudzysłów sam zawiera znaki
cudzysłowu ( ), jedynym sposobem reprezentowania tychże jest użycie sekwencji unikowej
, 42 lub x22. W przypadku łańcuchów obfitujących w znaki cudzysłowu może się to
wydać kłopotliwe i najwygodniejszym rozwiązaniem jest zamknięcie łańcucha apostrofami
— znaków cudzysłowu można wówczas używać literalnie, tracimy jednak możliwość inter-
pretowania znaków specjalnych. Na szczęście istnieje złoty środek pozwalający na pogodze-
nie tych sprzecznych racji: jeśli chcesz zachować możliwość interpretowania znaków specjal-
nych w łańcuchach najeżonych znakami cudzysłowu, użyj konstrukcji {}.
1.6. Konwersja między znakami a kodami
Problem
Chcemy otrzymać kod ASCII danego znaku lub przetransformować kod ASCII znaku w sam
znak.
Rozwiązanie
Kod ASCII znaku możemy poznać za pomocą operatora ?:
?a # = 97
?! # = 33
?
# = 10
W podobny sposób możemy poznać kod ASCII znaku wchodzącego w skład łańcucha — na-
leży wówczas wyłuskać ów znak z łańcucha za pomocą indeksu:
a [0] # = 97
kakofonia [1] # = 97
1.6. Konwersja między znakami a kodami
|
41
Konwersję odwrotną — kodu ASCII na znak o tym kodzie — realizuje metoda chr, zwracają-
ca jednoznakowy łańcuch:
97.chr # = a
33.chr # = !
10.chr # =
0.chr # = 00
256.chr # RangeError: 256 out of char range
Dyskusja
Mimo iż łańcuch jako taki nie jest tablicą, może być utożsamiany z tablicą obiektów Fixnum
— po jednym obiekcie dla każdego bajta. Za pomocą odpowiedniego indeksu można wyłu-
skać obiekt Fixnum reprezentujący konkretny bajt łańcucha, czyli kod ASCII tego bajta. Za po-
mocą metody String#each_byte można iterować po wszystkich obiektach Fixnum tworzących
dany łańcuch.
Patrz także
• Receptura 1.8, „Przetwarzanie kolejnych znaków łańcucha”.
1.7. Konwersja między łańcuchami a symbolami
Problem
Mając symbol języka Ruby, należy uzyskać reprezentujący go łańcuch, lub vice versa — ziden-
tyfikować symbol odpowiadający danemu łańcuchowi.
Rozwiązanie
Konwersję symbolu na odpowiadający mu łańcuch realizuje metoda Symbol#to_s lub metoda
Symbol#id2name, dla której to_s jest aliasem.
:a_symbol.to_s # = a_symbol
:InnySymbol.id2name # = InnySymbol
: Jeszcze jeden symbol! .to_s # = Jeszcze jeden symbol!
Odwołanie do symbolu następuje zwykle przez jego nazwę. Aby uzyskać symbol reprezento-
wany przez łańcuch w kodzie programu, należy posłużyć się metodą String.intern:
:dodecahedron.object_id # = 4565262
symbol_name = dodecahedron
symbol_name.intern # = :dodecahedron
symbol_name.intern.object_id # = 4565262
Dyskusja
Symbol jest najbardziej podstawowym obiektem języka Ruby. Każdy symbol posiada nazwę
i wewnętrzny identyfikator (internal ID). Użyteczność symboli wynika z faktu, że wielokrot-
ne wystąpienie tej samej nazwy w kodzie programu oznacza każdorazowo odwołanie do te-
go samego symbolu.
42
|
Rozdział 1. Łańcuchy
Symbole są często bardziej użyteczne niż łańcuchy. Dwa łańcuchy o tej samej zawartości
są dwoma różnymi obiektami — można jeden z nich zmodyfikować bez wpływu na drugi.
Dwie identyczne nazwy odnoszą się do tego samego symbolu, co oczywiście przekłada się na
oszczędność czasu i pamięci.
string .object_id # = 1503030
string .object_id # = 1500330
:symbol.object_id # = 4569358
:symbol.object_id # = 4569358
Tak więc n wystąpień tej samej nazwy odnosi się do tego samego symbolu, przechowywanego
w pamięci w jednym egzemplarzu. n identycznych łańcuchów to n różnych obiektów o iden-
tycznej zawartości. Także porównywanie symboli jest szybsze niż porównywanie łańcuchów,
bowiem sprowadza się jedynie do porównywania identyfikatorów.
string1 == string2 # = false
:symbol1 == :symbol2 # = false
Na koniec zacytujmy hakera od języka Ruby, Jima Wericha:
• Użyj łańcucha, jeśli istotna jest zawartość obiektu (sekwencja tworzących go znaków).
• Użyj symbolu, jeśli istotna jest tożsamość obiektu.
Patrz także
• Receptura 5.1, „Wykorzystywanie symboli jako kluczy”.
• Receptura 8.12, „Symulowanie argumentów zawierających słowa kluczowe”.
• Rozdział 10., a szczególnie receptura 10.4, „Uzyskiwanie referencji do metody”, i receptu-
ra 10.10, „Oszczędne kodowanie dzięki metaprogramowaniu”.
• http://glu.ttono.us/articles/2005/08/19/understanding-ruby-symbols — interesujący artykuł o sym-
bolach języka Ruby.
1.8. Przetwarzanie kolejnych znaków łańcucha
Problem
Należy wykonać pewną czynność w stosunku do każdego znaku łańcucha z osobna.
Rozwiązanie
W dokumencie złożonym wyłącznie ze znaków ASCII każdy bajt łańcucha odpowiada jedne-
mu znakowi. Za pomocą metody String#each_byte można wyodrębnić poszczególne bajty
jako liczby, które następnie mogą być skonwertowane na znaki.
foobar .each_byte { |x| puts #{x} = #{x.chr} }
# 102 = f
# 111 = o
# 111 = o
# 98 = b
# 97 = a
# 114 = r
1.8. Przetwarzanie kolejnych znaków łańcucha
|
43
Za pomocą metody String#scan można wyodrębnić poszczególne znaki łańcucha jako jedno-
znakowe łańcuchy:
foobar .scan( /./ ) { |c| puts c }
# f
# o
# o
# b
# a
# r
Dyskusja
Ponieważ łańcuch jest sekwencją bajtów, można by oczekiwać, że metoda String#each umoż-
liwia iterowanie po tej sekwencji, podobnie jak metoda Array#each. Jest jednak inaczej — me-
toda String#each dokonuje podziału łańcucha na podłańcuchy względem pewnego separa-
tora (którym domyślnie jest znak nowego wiersza):
foo
bar .each { |x| puts x }
# foo
# bar
Odpowiednikiem metody Array#each w odniesieniu do łańcuchów jest metoda each_byte.
Każdy element łańcucha może być traktowany jako obiekt Fixnum, a metoda each_byte umoż-
liwia iterowanie po sekwencji tych obiektów.
Metoda String#each_byte jest szybsza niż String#scan i jako taka zalecana jest w przypad-
ku przetwarzania plików ASCII — każdy wyodrębniony obiekt Fixnum może być łatwo prze-
kształcony w znak (jak pokazano w Rozwiązaniu).
Metoda String#scan dokonuje sukcesywnego dopasowywania podanego wyrażenia regu-
larnego do kolejnych porcji łańcucha i wyodrębnia każdą z tych opcji. Jeżeli wyrażeniem tym
jest /./, wyodrębniane są poszczególne znaki łańcucha.
Jeśli zmienna $KCODE jest odpowiednio ustawiona, metoda scan może być stosowana także
do łańcuchów zawierających znaki w kodzie UTF-8. Jest to najprostsza metoda przeniesienia
koncepcji „znaku” na grunt łańcuchów języka Ruby, które z definicji są ciągami bajtów, nie
znaków.
Poniższy łańcuch zawiera zakodowaną w UTF-8 francuską frazę „ça va”:
french = xc3xa7a va
Nawet jeżeli znaku ç nie sposób poprawnie wyświetlić na terminalu, poniższy przykład ilu-
struje zmianę zachowania metody String#scan w sytuacji, gdy określi się wyrażenie regu-
larne stosownie do standardów Unicode lub ustawi zmienną $KCODE tak, by Ruby traktował
wszystkie łańcuchy jako kodowane według UTF-8:
french.scan(/./) { |c| puts c }
# Ă
# §
# a
#
# v
# a
french.scan(/./u) { |c| puts c }
# ç
# a
#
44
|
Rozdział 1. Łańcuchy
# v
# a
$KCODE = u
french.scan(/./) { |c| puts c }
# ç
# a
#
# v
# a
Gdy Ruby traktuje łańcuchy jako sekwencje znaków UTF-8, a nie ASCII, dwa bajty reprezen-
tujące znak ç traktowane są łącznie, jako pojedynczy znak. Nawet jeśli niektórych znaków
UTF-8 nie można wyświetlić na ekranie terminala, można stworzyć programy, które zajmą
się ich obsługą.
Patrz także
• Receptura 11.12, „Konwersja dokumentu między różnymi standardami kodowania”.
1.9. Przetwarzanie poszczególnych słów łańcucha
Problem
Należy wydzielić z łańcucha jego kolejne słowa i dla każdego z tych słów wykonać pewną
czynność.
Rozwiązanie
Najpierw należy zastanowić się nad tym, co rozumiemy pod pojęciem „słowa” w łańcuchu.
Co oddziela od siebie sąsiednie słowa? Tylko białe znaki, czy może także znaki interpunkcyj-
ne? Czy „taki-to-a-taki” to pojedyncze słowo, czy może cztery słowa? Te i inne kwestie roz-
strzyga się jednoznacznie, definiując wyrażenie regularne reprezentujące pojedyncze słowo
(kilka przykładów takich wyrażeń podajemy poniżej w Dyskusji).
Wspomniane wyrażenie regularne należy przekazać jako parametr metody String#scan, któ-
ra tym samym dokona podzielenia łańcucha na poszczególne słowa. Prezentowana poniżej
metoda word_count zlicza wystąpienia poszczególnych słów w analizowanym tekście; zgod-
nie z użytym wyrażeniem regularnym „słowo” ma składnię identyczną z identyfikatorem ję-
zyka Ruby, jest więc ciągiem liter, cyfr i znaków podkreślenia:
class String
def word_count
frequencies = Hash.new(0)
downcase.scan(/w+/) { |word| frequencies[word] += 1 }
return frequencies
end
end
{Dogs dogs dog dog dogs.}.word_count
# = { dogs = 3, dog = 2}
{ I have no shame, I said.}.word_count
Pobierz darmowy fragment (pdf)