Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00405 008346 10495107 na godz. na dobę w sumie
Rails. Zaawansowane programowanie - książka
Rails. Zaawansowane programowanie - książka
Autor: Liczba stron: 336
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-1724-1 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> rails - programowanie
Porównaj ceny (książka, ebook, audiobook).

Twórz zaawansowane projekty w Rails!

Ruby on Rails przebojem wdarł się na rynek szkieletów aplikacji internetowych. Stworzony w architekturze MVC z wykorzystaniem popularnego języka Ruby, został entuzjastycznie przyjęty przez społeczność programistów. Główne założenia autora tego projektu, Davida Heinemeiera Hanssona, to szybkość, łatwość i przyjemność tworzenia kodu. Ruby on Rails jest dojrzałym rozwiązaniem, wykorzystywanym przez wiele firm w aplikacjach internetowych, tworzonych pod kątem ich specyficznych potrzeb. Liczba aplikacji, które powstały z wykorzystaniem tego szkieletu, świadczy o jego wysokiej jakości oraz niewątpliwie ma wpływ na wzrost popularności samego języka Ruby.

'Rails. Zaawansowane programowanie' porusza te tematy, które Wy, programiści, lubicie najbardziej! Dzięki tej książce dowiesz się, w jaki sposób wykorzystać gotowe wtyczki oraz jak stworzyć nowe. Nauczysz się stosować zaawansowane funkcje bazy danych oraz podłączać się jednocześnie do wielu baz. Po lekturze tego podręcznika bez problemu zapewnisz swojej aplikacji najwyższy poziom bezpieczeństwa, optymalną wydajność i skalowalność. Autor wskazuje tutaj również niezwykle interesujące kwestie, dotyczące projektowania dużych aplikacji, wykorzystania systemów kontroli wersji oraz utrzymywania właściwej struktury projektu.

Poznaj wszystkie funkcje Ruby on Rails!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Rails. Zaawansowane programowanie Autor: Brad Ediger T³umaczenie: Pawe³ Gonera ISBN: 978-83-246-1724-1 Tytu³ orygina³u: Advanced Rails Format: 168x237 , stron: 336 Twórz zaawansowane projekty w Rails! • Jak zadbaæ o bezpieczeñstwo? • Jak zapewniæ wydajnoœæ Twojej aplikacji? • Jak stworzyæ i utrzymaæ du¿y projekt w Rails? Ruby on Rails przebojem wdar³ siê na rynek szkieletów aplikacji internetowych. Stworzony w architekturze MVC z wykorzystaniem popularnego jêzyka Ruby, zosta³ entuzjastycznie przyjêty przez spo³ecznoœæ programistów. G³ówne za³o¿enia autora tego projektu, Davida Heinemeiera Hanssona, to szybkoœæ, ³atwoœæ i przyjemnoœæ tworzenia kodu. Ruby on Rails jest dojrza³ym rozwi¹zaniem, wykorzystywanym przez wiele firm w aplikacjach internetowych, tworzonych pod k¹tem ich specyficznych potrzeb. Liczba aplikacji, które powsta³y z wykorzystaniem tego szkieletu, œwiadczy o jego wysokiej jakoœci oraz niew¹tpliwie ma wp³yw na wzrost popularnoœci samego jêzyka Ruby. „Rails. Zaawansowane programowanie” porusza te tematy, które Wy, programiœci, lubicie najbardziej! Dziêki tej ksi¹¿ce dowiesz siê, w jaki sposób wykorzystaæ gotowe wtyczki oraz jak stworzyæ nowe. Nauczysz siê stosowaæ zaawansowane funkcje bazy danych oraz pod³¹czaæ siê jednoczeœnie do wielu baz. Po lekturze tego podrêcznika bez problemu zapewnisz swojej aplikacji najwy¿szy poziom bezpieczeñstwa, optymaln¹ wydajnoœæ i skalowalnoœæ. Autor wskazuje tutaj równie¿ niezwykle interesuj¹ce kwestie, dotycz¹ce projektowania du¿ych aplikacji, wykorzystania systemów kontroli wersji oraz utrzymywania w³aœciwej struktury projektu. • Przypomnienie i omówienie podstawowych elementów Ruby i Rails • Stosowanie ActiveSupport oraz RailTies • Zastosowanie i projektowanie wtyczek • Zaawansowane wykorzystanie baz danych • Uwierzytelnianie za pomoc¹ LDAP • Bezpieczne szyfrowanie hase³ • Bezpieczne przetwarzanie formularzy i danych u¿ytkownika • Zapewnienie wydajnoœci • Skalowanie architektury • Wykorzystywanie us³ug Web • Tworzenie wielojêzycznych aplikacji • Zarz¹dzanie du¿ymi projektami • U¿ywanie systemów kontroli wersji Poznaj wszystkie funkcje Ruby on Rails! Spis treļci Wstýp ........................................................................................................................................5 1. Techniki podstawowe ...................................................................................................9 9 12 30 41 46 49 Czym jest metaprogramowanie? Podstawy Ruby Techniki metaprogramowania Programowanie funkcyjne Przykäady Propozycje dalszych lektur 2. ActiveSupport oraz RailTies ........................................................................................ 51 51 54 61 65 79 81 Ruby, jakiego nie znamy Jak czytaè kod? ActiveSupport Core Extensions RailTies Propozycje dalszych lektur 3. Wtyczki Rails ................................................................................................................83 83 87 89 94 97 Wtyczki Tworzenie wtyczek Przykäad wtyczki Testowanie wtyczek Propozycje dalszych lektur 4. Bazy danych .................................................................................................................99 99 104 112 118 120 121 126 127 Systemy zarzñdzania bazñ danych DuĔe obiekty (binarne) Zaawansowane funkcje baz danych Podäñczanie do wielu baz danych Buforowanie Wyrównywanie obciñĔenia i wysoka dostöpnoĈè LDAP Propozycje dalszych lektur 3 Problemy w aplikacji Problemy w sieci WWW Wstrzykiwanie SQL ćrodowisko Ruby Propozycje dalszych lektur 5. Bezpieczeħstwo ......................................................................................................... 129 129 138 145 146 147 6. Wydajnoļë .................................................................................................................. 149 150 156 165 174 181 183 7. REST, zasoby oraz usĥugi Web .................................................................................. 185 185 203 207 226 230 Narzödzia pomiarowe Przykäad optymalizacji Rails WydajnoĈè ActiveRecord Skalowanie architektury Inne systemy Propozycje dalszych lektur Czym jest REST? Zalety architektury REST REST w Rails Analiza przypadku — Amazon S3 Propozycje dalszych lektur 8. i18n oraz L10n ............................................................................................................ 231 231 Lokalizacje Kodowanie znaków 232 233 Unicode 235 Rails i Unicode 243 Rails L10n 262 Propozycje dalszych lektur 9. Wykorzystanie i rozszerzanie Rails ..........................................................................263 263 274 279 285 10. DuŜe projekty .............................................................................................................287 287 298 299 305 311 Skorowidz ............................................................................................................................. 313 Wymiana komponentów Rails Wykorzystanie komponentów Rails Udziaä w tworzeniu Rails Propozycje dalszych lektur Kontrola wersji ćledzenie bäödów Struktura projektu Instalacja Rails Propozycje dalszych lektur 4 _ Spis treļci ROZDZIAĤ 1. Techniki podstawowe Do osiñgniöcia niezawodnoĈci jest wymagana prostota. Edsger W. Dijkstra Od pierwszego wydania w lipcu 2004 roku Ĉrodowisko Ruby on Rails stale zdobywa popu- larnoĈè. Rails przyciñga programistów PHP, Java i .NET swojñ prostotñ — architekturñ model-widok-kontroler (MVC), rozsñdnymi wartoĈciami domyĈlnymi („konwencja nad kon- figuracjñ”) oraz zaawansowanym jözykiem programowania Ruby. ćrodowisko Rails miaäo przez pierwszy rok lub dwa säabñ reputacjö z uwagi na braki w do- kumentacji. Luka ta zostaäa wypeäniona przez tysiñce programistów, którzy korzystali ze Ĉrodowiska Ruby on Rails, wspóätworzyli je i pisali na jego temat, jak równieĔ dziöki pro- jektowi Rails Documentation (http://railsdocumentation.org). Dostöpne sñ tysiñce blogów, które zawierajñ samouczki oraz porady na temat programowania w Rails. Celem tej ksiñĔki jest zebranie najlepszych praktyk oraz wiedzy zgromadzonej przez Ĉrodo- wisko programistów Rails i zaprezentowanie ich w äatwej do przyswojenia, zwartej formie. Poszukiwaäem ponadto tych aspektów programowania dla WWW, które sñ czösto niedoce- niane lub pomijane przez Ĉrodowisko Rails. Czym jest metaprogramowanie? Rails udostöpnia metaprogramowanie dla mas. Choè nie byäo to pierwsze zastosowanie za- awansowanych funkcji Ruby, to jednak jest ono chyba najbardziej popularne. Aby zrozumieè dziaäanie Rails, konieczne jest wczeĈniejsze zapoznanie siö z tymi mechanizmami Ruby, które zostaäy wykorzystane w tym Ĉrodowisku. W tym rozdziale przedstawione zostanñ podsta- wowe mechanizmy zapewniajñce dziaäanie technik przedstawianych w pozostaäych rozdzia- äach ksiñĔki. Metaprogramowanie to technika programowania, w której kod jest wykorzystywany do tworzenia innego kodu, bñdĒ dokonania introspekcji samego siebie. Przedrostek meta (z gre- ki) wskazuje na abstrakcjö; kod wykorzystujñcy techniki metaprogramowania dziaäa jedno- czeĈnie na dwóch poziomach abstrakcji. Metaprogramowanie jest wykorzystywane w wielu jözykach, ale jest najbardziej popularne w jözykach dynamicznych, poniewaĔ majñ one zwykle wiöcej funkcji pozwalajñcych na ma- nipulowanie kodem jako danymi. Pomimo tego, Ĕe w jözykach statycznych, takich jak C# lub 9 Java, dostöpny jest mechanizm refleksji, to nie jest on nawet w czöĈci tak przezroczysty, jak w jözykach dynamicznych, takich jak Ruby, poniewaĔ kod i dane znajdujñ siö w czasie dzia- äania aplikacji na dwóch osobnych warstwach. Introspekcja jest zwykle wykonywana na jednym z tych poziomów. Introspekcja syntak- tyczna jest najniĔszym poziomem introspekcji — pozwala na bezpoĈredniñ analizö tekstu programu lub strumienia tokenów. Metaprogramowanie bazujñce na szablonach lub ma- krach zwykle dziaäa na poziomie syntaktycznym. Ten typ metaprogramowania jest wykorzystany w jözyku Lisp poprzez stosowanie S-wyraĔeþ (bezpoĈredniego täumaczenia drzewa abstrakcji skäadni programu) zarówno w przypadku kodu, jak i danych. Metaprogramowanie w jözyku Lisp wymaga intensywnego korzystania z makr, które sñ tak naprawdö szablonami kodu. Daje to moĔliwoĈè pracy na jednym poziomie; kod i dane sñ reprezentowane w ten sam sposób, a jedynym, co odróĔnia kod od danych, jest to, Ĕe jest on wartoĈciowany. Jednak metaprogramowanie na poziomie syntaktycznym ma swoje wady. Przechwytywanie zmiennych oraz przypadkowe wielokrotne wartoĈciowanie jest bez- poĈredniñ konsekwencjñ umieszczenia kodu na dwóch poziomach abstrakcji dla tej samej prze- strzeni nazw. Choè dostöpne sñ standardowe idiomy jözyka Lisp pozwalajñce na uporanie siö z tymi problemami, to jednak sñ one kolejnymi elementami, których programista Lisp musi siö nauczyè i pamiötaè o nich. Introspekcja syntaktyczna w Ruby jest dostöpna za poĈrednictwem biblioteki ParseTree, która pozwala na täumaczenie kodu Ēródäowego Ruby na S-wyraĔenia1. Interesujñcym zastosowa- niem tej biblioteki jest Heckle2, biblioteka uäatwiajñca testowanie, która analizuje kod Ēródäowy Ruby i zmienia go poprzez modyfikowanie ciñgów oraz zmianö wartoĈci true na false i odwrotnie. W zaäoĔeniach, jeĔeli nasz kod jest odpowiednio pokryty testami, kaĔda mody- fikacja kodu powinna zostaè wykryta przez testy jednostkowe. Alternatywñ dla introspekcji syntaktycznej jest dziaäajñca na wyĔszym poziomie introspekcja semantyczna, czyli analiza programu z wykorzystaniem struktur danych wyĔszego pozio- mu. Sposób realizacji tej techniki róĔni siö w roĔnych jözykach programowania, ale w Ruby zwykle oznacza to operowanie na poziomie klas i metod — tworzenie, modyfikowanie i alia- sowanie metod; przechwytywanie wywoäaþ metod; manipulowanie äaþcuchem dziedzicze- nia. Techniki te sñ zwykle bardziej zwiñzane z istniejñcym kodem niĔ metody syntaktyczne, poniewaĔ najczöĈciej istniejñce metody sñ traktowane jako czarne skrzynki i ich implementa- cja nie jest swobodnie zmieniana. Nie powtarzaj siý Na wysokim poziomie metaprogramowanie jest przydatne do wprowadzania zasady DRY (ang. Don’t Repeat Yourself — „nie powtarzaj siö”). Zgodnie z tñ technikñ, nazywanñ równieĔ „Raz i tylko raz”, kaĔdy element informacji musi byè zdefiniowany w systemie tylko raz. Powielanie jest zwykle niepotrzebne, szczególnie w jözykach dynamicznych, takich jak Ruby. Podobnie jak abstrakcja funkcjonalna pozwala nam na unikniöcie powielania kodu, który jest taki sam lub niemal taki sam, metaprogramowanie pozwala nam uniknñè podobnych kon- cepcji, wykorzystywanych w aplikacji. 1 http://www.zenspider.com/ZSS/Products/ParseTree. 2 http://rubyforge.org/projects/seattlerb. 10 _ Rozdziaĥ 1. Techniki podstawowe Metaprogramowanie ma na celu zachowanie prostoty. Jednym z najäatwiejszych sposobów na zapoznanie siö z metaprogramowaniem jest analizowanie kodu i jego refaktoryzacja. Nadmiarowy kod moĔe byè wydzielany do funkcji; nadmiarowe funkcje lub wzorce mogñ byè czösto wydzielone z uĔyciem metaprogramowania. Wzorc e projektowe definiujñ nakäadajñce siö obszary; wzorce zostaäy zaprojektowane w celu zminimalizowania liczby sytuacji, w których musimy rozwiñzywaè ten sam problem. W spoäecznoĈci Ruby wzorce projektowe majñ dosyè zäñ reputacjö. Dla czöĈci programistów wzorce sñ wspólnym säownikiem do opisu rozwiñzaþ powtarzajñcych siö problemów. Dla innych sñ one „przeprojektowane”. Aby byè pewnym tego, Ĕe zastosowane zostanñ wszystkie dostöpne wzorce, muszñ byè one naduĔywane. JeĔeli jednak bödñ uĔywane rozsñdnie, nie musi tak byè. Wzorce projektowe sñ uĔyteczne jedynie w przypadku, gdy pozwalajñ zmniejszaè zäoĔonoĈè kognitywnñ. W Ruby czöĈè najbardziej szczegóäowych wzorców jest tak przezroczy- sta, Ĕe nazywanie ich „wzorcami” moĔe byè nieintuicyjne; sñ one w rzeczywistoĈci idiomami i wiökszoĈè programistów, którzy „myĈlñ w Ruby”, korzysta z nich bezwiednie. Wzorce powinny byè uwaĔane za säownik wykorzystywany przy opisie architektury, a nie za bibliotekö wstöpnie przygotowanych rozwiñzaþ implementacji. Dobre wzorce projektowe dla Ruby znacznie róĔniñ siö w tym wzglödzie od dobrych wzorców projektowych dla C++. Uogólniajñc, metaprogramowanie nie powinno byè wykorzystywane tylko do powtarzania kodu. Zawsze powinno siö przeanalizowaè wszystkie opcje, aby sprawdziè, czy inna techni- ka, na przykäad abstrakcja funkcjonalna, nie nadaje siö lepiej do rozwiñzania problemu. Jed- nak w kilku przypadkach powtarzanie kodu poprzez metaprogramowanie jest najlepszym sposobem na rozwiñzanie problemu. JeĔeli na przykäad w obiekcie musi byè zdefiniowanych kilka podobnych metod, tak jak w metodach pomocniczych ActiveRecord, moĔna w takim przypadku skorzystaè z metaprogramowania. Puĥapki Kod, który siö sam modyfikuje, moĔe byè bardzo trudny do tworzenia i utrzymania. Wybra- ne przez nas konstrukcje programowe powinny zawsze speäniaè nasze potrzeby — powinny one upraszczaè Ĕycie, a nie komplikowaè je. Przedstawione poniĔej techniki powinny uzu- peäniaè zestaw narzödzi w naszej skrzynce, a nie byè jedynymi narzödziami. Programowanie wstýpujéce Programowanie wstöpujñce jest koncepcjñ zapoĔyczonñ z Ĉwiata Lisp. Podstawowñ koncep- cjñ w tym sposobie programowania jest tworzenie abstrakcji od najniĔszego poziomu. Przez utworzenie na poczñtku konstrukcji najniĔszego poziomu budujemy w rzeczywistoĈci pro- gram na bazie tych abstrakcji. W pewnym sensie piszemy jözyk specyficzny dla domeny, za pomocñ którego tworzymy programy. Koncepcja ta jest niezmiernie uĔyteczna w przypadku ActiveRecord. Po utworzeniu podsta- wowych schematów i modelu obiektowego moĔna rozpoczñè budowanie abstrakcji przy wy- korzystaniu tych obiektów. Wiele projektów Rails zaczyna siö od tworzenia podobnych do zamieszczonej poniĔej abstrakcji modelu, zanim powstanie pierwszy wiersz kodu kontrolera lub nawet projekt interfejsu WWW: Czym jest metaprogramowanie? _ 11 class Order ActiveRecord::Base has_many :line_items def total subtotal + shipping + tax end def subtotal line_items.sum(:price) end def shipping shipping_base_price + line_items.sum(:shipping) end def tax subtotal * TAX_RATE end end Podstawy Ruby Zakäadamy, Ĕe Czytelnik dobrze zna Ruby. W podrozdziale tym przedstawimy niektóre z aspektów jözyka, które sñ czösto mylñce lub Ēle rozumiane. Niektóre z nich mogñ byè Czytelnikowi znane, ale sñ to najwaĔniejsze koncepcje tworzñce podstawy technik metaprogramowania przedstawianych w dalszej czöĈci rozdziaäu. Klasy i moduĥy Klasy i moduäy sñ podstawñ programowania obiektowego w Ruby. Klasy zapewniajñ mecha- nizmy hermetyzacji i separacji. Moduäy mogñ byè wykorzystywane jako tzw. mixin — zbiór funkcji umieszczonych w klasie, stanowiñcych namiastkö mechanizmu dziedziczenia wielo- bazowego. Moduäy sñ równieĔ wykorzystywane do podziaäu klas na przestrzenie nazw. W Ruby kaĔda nazwa klasy jest staäñ. Dlatego wäaĈnie Ruby wymaga, aby nazwy klas rozpoczy- naäy siö od wielkiej litery. Staäa ta jest wartoĈciowana na obiekt klasowy, który jest obiektem klasy Class. RóĔni siö od obiektu Class, który reprezentuje faktycznñ klasö Class3. Gdy mówi- my o „obiekcie klasowym”, mamy na myĈli obiekt reprezentujñcy klasö (wraz z samñ klasñ Class). Gdy mówimy o „obiekcie Class”, mamy na myĈli klasö o nazwie Class, bödñcñ klasñ bazowñ dla wszystkich obiektów klasowych. Klasa Class dziedziczy po Module; kaĔda klasa jest równieĔ moduäem. Istnieje tu jednak nie- zwykle waĔna róĔnica. Klasy nie mogñ byè mieszane z innymi klasami, a klasy nie mogñ dziedziczyè po obiektach; jest to moĔliwe tylko w przypadku moduäów. Wyszukiwanie metod Wyszukiwanie metod w Ruby moĔe byè dosyè mylñce, a w rzeczywistoĈci jest dosyè regularne. Najprostszym sposobem na zrozumienie skomplikowanych przypadków jest przedstawienie struktur danych, jakie Ruby wewnötrznie tworzy. 3 JeĔeli nie jest to wystarczajñco skomplikowane, trzeba pamiötaè, Ĕe obiekt Class posiada równieĔ klasö Class. 12 _ Rozdziaĥ 1. Techniki podstawowe KaĔdy obiekt Ruby4 posiada zbiór pól w pamiöci: klass WskaĒnik do obiektu klasy danego obiektu (zostaäa uĔyta nazwa klass zamiast class, poniewaĔ ta druga jest säowem kluczowym w C++ i Ruby; jeĔeli nazwalibyĈmy jñ class, Ruby kompilowaäby siö za pomocñ kompilatora C, ale nie moĔna byäoby uĔyè kompila- tora C++. Ta wprowadzona umyĈlnie literówka jest uĔywana wszödzie w Ruby). iv_tbl „Tablica zmiennych instancyjnych” to tablica mieszajñca zawierajñca zmienne instancyjne naleĔñce do tego obiektu. flags Pole bitowe znaczników Boolean zawierajñce informacje statusu, takie jak stan Ĉladu obiektu, znacznik zbierania nieuĔytków oraz to, czy obiekt jest zamroĔony. KaĔda klasa Ruby posiada te same pola, jak równieĔ dwa dodatkowe: m_tbl „Tablica metod” — tabela mieszajñca metod instancyjnych danej klasy lub moduäu. super WskaĒnik klasy lub moduäu bazowego. Pola te peäniñ waĔnñ rolö w wyszukiwaniu metod i sñ waĔne w zrozumieniu tego mechani- zmu. W szczególnoĈci moĔna zwróciè uwagö na róĔnicö pomiödzy wskaĒnikami obiektu kla- sy: klass i super. Zasady Zasady wyszukiwania metod sñ bardzo proste, ale zaleĔñ od zrozumienia sposobu dziaäania struktur danych Ruby. Gdy do obiektu jest wysyäany komunikat5, wykonywane sñ nastöpujñce operacje: 1. Ruby korzysta z wskaĒnika klass i przeszukuje m_tbl z obiektu danej klasy, szukajñc odpowiedniej metody (wskaĒnik klass zawsze wskazuje na obiekt klasowy). 2. JeĔeli nie zostanie znaleziona metoda, Ruby korzysta z wskaĒnika super obiektu klaso- wego i kontynuuje wyszukiwanie w m_tbl klasy bazowej. 3. Ruby wykonuje wyszukiwanie w ten sposób aĔ do momentu znalezienia metody bñdĒ teĔ do osiñgniöcia koþca äaþcucha wskaĒników super. 4. JeĔeli w Ĕadnym obiekcie äaþcucha nie zostanie znaleziona metoda, Ruby wywoäuje me- todö method_missing z obiektu odbiorcy metody. Powoduje to ponowne rozpoczöcie te- go procesu, ale tym razem wyszukiwana jest metoda method_missing zamiast poczñt- kowej metody. 4 Poza obiektami natychmiastowymi (Fixnums, symbols, true, false oraz nil), które przedstawimy póĒniej. 5 W Ruby czösto stosowana jest terminologia przekazywania komunikatów pochodzñca z jözyka Smalltalk — gdy jest wywoäywana metoda, mówi siö, Ĕe jest przesyäany komunikat. Obiekt, do którego jest wysyäany ko- munikat, jest nazywany odbiorcñ. Podstawy Ruby _ 13 Zasady te sñ stosowane w sposób uniwersalny. Wszystkie interesujñce mechanizmy wykorzy- stujñce wyszukiwanie metod (mixin, metody klasowe i klasy singleton) wykorzystujñ struktu- rö wskaĒników klass oraz super. Przedstawimy teraz ten proces nieco bardziej szczegóäowo. Dziedziczenie klas Proces wyszukiwania metod moĔe byè mylñcy, wiöc zacznijmy od prostego przykäadu. Poni- Ĕej przedstawiona jest najprostsza moĔliwa definicja klasy w Ruby: class A end Kod ten powoduje wygenerowanie w pamiöci nastöpujñcych struktur (patrz rysunek 1.1). Rysunek 1.1. Struktury danych dla pojedynczej klasy Prostokñty z podwójnymi ramkami reprezentujñ obiekty klas — obiekty, których wskaĒnik klass wskazuje na obiekt Class. WskaĒnik super wskazuje na obiekt klasy Object, co oznacza, Ĕe A dziedziczy po Object. Od tego momentu bödziemy pomijaè wskaĒniki klass dla Class, Module oraz Object, jeĔeli nie bödzie to powodowaäo niejasnoĈci. Nastöpnym przypadkiem w kolejnoĈci stopnia skomplikowania jest dziedziczenie po jednej klasie. Dziedziczenie klas wykorzystuje wskaĒniki super. Utwórzmy na przykäad klasö B dzie- dziczñcñ po A: class B A end Wynikowe struktury danych sñ przedstawione na rysunku 1.2. Säowo kluczowe super pozwala na przechodzenie wzdäuĔ äaþcucha dziedziczenia, tak jak w poniĔszym przykäadzie: class B def initialize logger.info Tworzenie obiektu B super end end Wywoäanie super w initialize pozwala na przejĈcie standardowej metody wyszukiwania metod, zaczynajñc od A#initialize. 14 _ Rozdziaĥ 1. Techniki podstawowe Rysunek 1.2. Jeden poziom dziedziczenia Konkretyzacja klas Teraz moĔemy przedstawiè sposób wyszukiwania metod. Na poczñtek utworzymy instancjö klasy B: obj = B.new Powoduje to utworzenie nowego obiektu i ustawienie wskaĒnika klass na obiekt klasowy B (patrz rysunek 1.3). Rysunek 1.3. Konkretyzacja klas Podstawy Ruby _ 15 Pojedyncza ramka wokóä obj reprezentuje zwykäy obiekt. Trzeba pamiötaè, Ĕe kaĔdy prosto- kñt na tym diagramie reprezentuje instancje obiektu. Jednak prostokñty o podwójnej ramce, reprezentujñce obiekty, sñ obiektami klasy Class (których wskaĒnik klass wskazuje na obiekt Class). Gdy wysyäamy komunikat do obj: obj.to_s realizowany jest nastöpujñcy äaþcuch operacji: 1. WskaĒnik klass obiektu obj jest przesuwany do B; w metodach klasy B (w m_tbl) wy- szukiwana jest odpowiednia metoda. 2. W klasie B nie zostaje znaleziona odpowiednia metoda. Wykorzystywany jest wskaĒnik super z obiektu klasy B i metoda jest poszukiwana w klasie A. 3. W klasie A nie zostaje znaleziona odpowiednia metoda. Wykorzystywany jest wskaĒnik super z obiektu klasy A i metoda jest poszukiwana w klasie Object. 4. Klasa Object zawiera metodö to_s w kodzie natywnym (rb_any_to_s). Metoda ta jest wywoäywana z parametrem takim jak # B:0x1cd3c0 . Metoda rb_any_to_s analizuje wskaĒnik klass odbiorcy w celu okreĈlenia nazwy klasy wyĈwietlenia; dlatego pokazy- wana jest nazwa B, pomimo tego, Ĕe wywoäywana metoda znajduje siö w Object. Doĥéczanie moduĥów Gdy zaczniemy korzystaè z moduäów, sprawa stanie siö bardziej skomplikowana. Ruby ob- säuguje doäñczanie moduäów zawierajñcych ICLASS6, które sñ poĈrednikami moduäów. Gdy doäñczamy moduä do klasy, Ruby wstawia ICLASS reprezentujñcy doäñczony moduä do äaþ- cucha super doäñczajñcej klasy. W naszym przykäadzie doäñczania moduäu uproĈcimy nieco sytuacjö przez zignorowanie kla- sy B. Zdefiniujemy moduä i dodamy go do A, co spowoduje powstanie struktur danych przed- stawionych na rysunku 1.4: module Mixin def mixed_method puts Witamy w mixin end end class A include Mixin end Tutaj wäaĈnie do gry wkracza ICLASS. WskaĒnik super wskazujñcy z A na Object jest prze- chwytywany przez nowy ICLASS (reprezentowany przez kwadrat narysowany przerywanñ liniñ). ICLASS jest poĈrednikiem dla moduäu Mixin. Zawiera on wskaĒniki do tablic iv_tbl z Mixin (zmienne instancyjne) oraz m_tbl (metody). 6 ICLASS jest nazwñ dla klas poĈredniczñcych, wprowadzonñ przez Mauricia Fernándeza. Nie majñ one oficjalnej nazwy, ale w kodzie Ēródäowym Ruby noszñ nazwö T_ICLASS. 16 _ Rozdziaĥ 1. Techniki podstawowe Rysunek 1.4. Wäñczenie moduäu w äaþcuch wyszukiwania Na podstawie tego diagramu moĔna äatwo wywnioskowaè, do czego säuĔñ nam klasy poĈred- niczñce — ten sam moduä moĔe zostaè doäñczony do wielu róĔnych klas; klasy mogñ dziedzi- czyè po róĔnych klasach (i przez to mieè inne wskaĒniki super). Nie moĔemy bezpoĈrednio wäñczyè klasy Mixin do äaþcucha wyszukiwania, poniewaĔ jego wskaĒnik super bödzie wskazy- waä na dwa róĔne obiekty, jeĔeli zostanie doäñczony do klas majñcych róĔnych rodziców. Gdy utworzymy obiekt klasy A, struktury bödñ wyglñdaäy jak na rysunku 1.5. objA = A.new Rysunek 1.5. Wyszukiwanie metod dla klasy z doäñczonym moduäem Wywoäujemy tu metodö mixed_method z obiektu mixin, z objA jako odbiorcñ: objA.mixed_method # Witamy w mixin Podstawy Ruby _ 17 Wykonywany jest nastöpujñcy proces wyszukiwania metody: 1. W klasie obiektu objA, czyli A, wyszukiwana jest pasujñca metoda. ēadna nie zostaje znale- ziona. 2. WskaĒnik super klasy A prowadzi do ICLASS, który jest poĈrednikiem dla Mixin. Pasujñca metoda jest wyszukiwana w obiekcie poĈrednika. PoniewaĔ tablica m_tbl poĈrednika jest taka sama jak tablica m_tbl klasy Mixin, metoda mixed_method zostaje odnaleziona i wy- woäana. W wielu jözykach majñcych moĔliwoĈè dziedziczenia wielobazowego wystöpuje problem diamentu, polegajñcy na braku moĔliwoĈci jednoznacznego identyfikowania metod obiektów, których klasy majñ schemat dziedziczenia o ksztaäcie diamentu, jak jest to pokazane na ry- sunku 1.6. Rysunek 1.6. Problem diamentu przy dziedziczeniu wielobazowym Biorñc jako przykäad diagram przedstawiony na tym rysunku, jeĔeli obiekt klasy D wywoäuje metodö zdefiniowanñ w klasie A, która zostaäa przesäoniöta zarówno w B, jak i C, nie moĔna jasno okreĈliè, która metoda zostanie wywoäana. W Ruby problem ten zostaä rozwiñzany przez szeregowanie kolejnoĈci doäñczania. W czasie wywoäywania metody äaþcuch dziedziczenia jest przeszukiwany liniowo, doäñczajñc wszystkie ICLASS dodane do äaþcucha. Trzeba przypomnieè, Ĕe Ruby nie obsäuguje dziedziczenia wielobazowego; jednak wiele mo- duäów moĔe byè doäñczonych do klas i innych moduäów. Z tego powodu A, B oraz C muszñ byè moduäami. Jak widaè, nie wystöpuje tu niejednoznacznoĈè; wybrana zostanie metoda doäñczona jako ostatnia do äaþcucha wywoäania: module A def hello Witamy w A end end module B include A def hello Witamy w B end 18 _ Rozdziaĥ 1. Techniki podstawowe end module C include A def hello Witamy w C end end class D include B include C end D.new.hello # = Witamy w C JeĔeli zmienimy kolejnoĈè doäñczania, odpowiednio zmieni siö wynik: class D include C include B end D.new.hello # = Witamy w B W przypadku ostatniego przykäadu, gdzie B zostaä doäñczony jako ostatni, diagram obiektów jest przedstawiony na rysunku 1.7 (dla uproszczenia wskaĒniki od Object i Class zostaäy usuniöte). Rysunek 1.7. Rozwiñzanie problemu diamentu w Ruby — szeregowanie Podstawy Ruby _ 19 Klasa singleton Klasy singleton (równieĔ metaklasy lub eigenklasy; patrz nastöpna ramka, „Terminologia klas singleton”) pozwalajñ na zróĔnicowanie dziaäania obiektu w stosunku do innych obiek- tów danej klasy. Czytelnik prawdopodobnie spotkaä siö wczeĈniej z notacjñ pozwalajñcñ na otwarcie klasy singleton: class A end objA = A.new objB = A.new objA.to_s # = # A:0x1cd0a0 objB.to_s # = # A:0x1c4e28 class objA # Otwarcie klasy singleton dla objA def to_s; Obiekt A ; end end objA.to_s # = Obiekt A objB.to_s # = # A:0x1c4e28 Zapis class objA otwiera klasö singleton dla objA. Metody instancyjne dodane do klasy singleton funkcjonujñ jako metody instancyjne w äaþcuchu wyszukiwania. Wynikowe struk- tury danych sñ przedstawione na rysunku 1.8. Rysunek 1.8. Klasa singleton dla obiektu Jak zwykle, obiekt objB jest klasy A. JeĔeli poprosimy Ruby o podanie typu objA, okaĔe siö, Ĕe jest to równieĔ obiekt klasy A: objA.class # = A Jednak wewnötrznie obiekt ten dziaäa nieco inaczej. Do äaþcucha wyszukiwania zostaje do- dana inna klasa. Jest to obiekt klasy singleton dla objA. W tej dokumentacji bödziemy go na- zywaè Class:objA. Ruby nadaje mu podobnñ nazwö: # Class:# A:0x1cd0a0 . Podobnie jak inne klasy, wskaĒnik klass klasy singleton (niepokazany) wskazuje na obiekt Class. 20 _ Rozdziaĥ 1. Techniki podstawowe Terminologia klas singleton Termin metaklasa nie jest szczególnie precyzyjny w okreĈlaniu klas singleton. Nazwanie klasy „meta” wskazuje, Ĕe jest ona nieco bardziej abstrakcyjna niĔ zwykäa klasa. W tym przypadku nie ma to miejsca; klasy singleton sñ po prostu klasami naleĔñcymi do okreĈlonej instancji. Prawdziwe metaklasy sñ dostöpne w takich jözykach, jak Smalltalk, gdzie mamy bogaty proto- kóä metaobiektów. Metaklasy w Smalltalku to klasy, których instancjami sñ klasy. W przypad- ku Ruby jedynñ metaklasñ jest Class, poniewaĔ wszystkie klasy sñ obiektami Class. Dosyè popularnym alternatywnym terminem dla klasy singleton jest eigenklasa, od niemiec- kiego säowa eigen („wäasny”). Klasa singleton obiektu jest jego eigenklasñ (wäasnñ klasñ). Klasa singleton zostaje oznaczona jako klasa wirtualna (jeden ze znaczników flags wskazuje, Ĕe klasa jest wirtualna). Klasy wirtualne nie mogñ byè konkretyzowane i zwykle nie sñ wy- korzystywane w Ruby, o ile nie zadamy sobie trudu, aby ich uĔyè. Gdy chcieliĈmy okreĈliè klasö obiektu objA, Ruby wykorzystywaä wskaĒniki klass i super w hierarchii, aĔ do mo- mentu znalezienia pierwszej klasy niewirtualnej. Z tego powodu uzyskaliĈmy odpowiedĒ, Ĕe klasñ objA jest A. WaĔne jest, aby to zapamiötaè — klasa obiektu (z perspektywy Ruby) moĔe nie odpowiadaè obiektowi, na który wskazuje klass. Klasy singleton sñ tak nazwane nie bez powodu — w obiekcie moĔe byè zdefiniowana tylko jedna taka klasa. Dziöki temu moĔemy bez niejednoznacznoĈci odwoäywaè siö do klasy sin- gleton objA lub Class:objA. W naszym kodzie moĔemy zaäoĔyè, Ĕe klasa singleton istnieje; w rzeczywistoĈci Ruby tworzy jñ w momencie pierwszego wywoäania. Ruby pozwala na definiowanie klas singleton w dowolnych obiektach poza Fixnum oraz sym- bolach. Symbole oraz Fixnum sñ wartoĈciami natychmiastowymi (dla zapewnienia odpowied- niej wydajnoĈci sñ przechowywane w pamiöci bezpoĈrednio, a nie jako wskaĒniki do struktur danych). PoniewaĔ sñ one przechowywane w caäoĈci, nie posiadajñ wskaĒników klass, wiöc nie ma moĔliwoĈci zmiany äaþcucha wyszukiwania metod. MoĔna równieĔ otworzyè klasö singleton dla true, false oraz nil, ale zwracana bödzie ta sama klasa singleton co klasa obiektu. WartoĈciami sñ obiekty singleton (jedyne instancje), odpo- wiednio TrueClass, FalseClass oraz NilClass. Gdy odwoäamy siö do klasy singleton dla true, otrzymamy TrueClass, poniewaĔ jest to jedyna moĔliwa instancja tej klasy. W Ruby: true.class # = TrueClass class true; self; end # = TrueClass true.class == (class true; self; end) # = true Klasy singleton i obiekty klas Teraz sprawy siö komplikujñ. NaleĔy pamiötaè o podstawowej zasadzie wyszukiwania me- tod — na poczñtku Ruby przechodzi po wskaĒnikach klass i wyszukuje metody; nastöpnie korzysta z wskaĒników super do przeglñdania äaþcucha, aĔ do znalezienia odpowiedniej metody lub osiñgniöcia koþca äaþcucha. WaĔne jest, aby pamiötaè, Ĕe klasy sñ równieĔ obiektami. Tak jak zwykäe obiekty mogñ mieè klasö singleton, tak samo obiekty klas mogñ równieĔ posiadaè klasy singleton. Te klasy singleton, podobnie jak inne klasy, mogñ posiadaè metody. PoniewaĔ klasy singleton sñ dostöpne za Podstawy Ruby _ 21 pomocñ wskaĒnika klass z obiektu klasy, metody instancji klasy singleton sñ metodami klasy wäaĈciciela singletonu. Peäny zbiór struktur danych poniĔszego kodu jest pokazany na rysunku 1.9. class A end Rysunek 1.9. Peäny zbiór struktur danych jednej klasy Klasa A dziedziczy po Object. Obiekt klasy A jest typu Class. Class dziedziczy po Module, który z kolei dziedziczy po Object. Metody zapisane w tablicy m_tbl klasy A sñ metodami instancyjnymi A. Co siö wiöc stanie, gdy wywoäamy metodö klasowñ z A? A.to_s # = A Stosowane sñ te same zasady wyszukiwania, przy uĔyciu A jako odbiorcy (naleĔy pamiötaè, Ĕe A jest staäñ wartoĈciowanñ jako obiekt klasy A). Na poczñtek Ruby korzysta ze wskaĒnika klass pomiödzy A a Class. W tablicy m_tbl Class wyszukiwana jest funkcja o nazwie to_s. PoniewaĔ nic nie zostaäo znalezione, Ruby przechodzi za pomocñ wskaĒnika super z Class do Module, gdzie zostaje odnaleziona funkcja to_s (w kodzie natywnym, rb_mod_to_s). Nie powinno byè to niespodziankñ. Nie ma tu Ĕadnej magii. Metody klasowe sñ wyszukiwane w ten sam sposób co metody instancyjne — jedynñ róĔnicñ jest to, Ĕe odbiorcñ jest klasa, a nie instancja klasy. Teraz, gdy wiemy, w jaki sposób sñ wyszukiwane metody, moĔemy wnioskowaè, Ĕe moĔe- my zdefiniowaè metodö klasowñ dla dowolnej klasy przez zdefiniowanie metody instancyjnej obiektu Class (aby wstawiè go do m_tbl Class). Faktycznie — to dziaäa: class A; end # z Module#to_s A.to_s # = A class Class def to_s; Class#to_s ; end end A.to_s # = Class#to_s 22 _ Rozdziaĥ 1. Techniki podstawowe Jest to interesujñca sztuczka, ale o ograniczonej uĔytecznoĈci. Zwykle chcemy zdefiniowaè osobne metody klasowe dla kaĔdej z klas. W takim przypadku moĔna wykorzystaè klasy singleton dla obiektów klasowych. Aby otworzyè klasö singleton dla klasy, naleĔy po prostu uĔyè nazwy klasy w notacji klasy singleton: class A; end class B; end class A def to_s; Klasa A ; end end A.to_s # = Klasa A B.to_s # = B Wynikowe struktury danych sñ przedstawione na rysunku 1.10. Dla uproszczenia klasa B jest pominiöta. Rysunek 1.10. Klasa singleton dla klasy Metoda to_s zostaäa dodana do klasy singleton dla A lub Class:A. Teraz, gdy zostanie wywo- äana metoda A.to_s, Ruby skorzysta z wskaĒnika klass do Class:A i wywoäa z niej odpo- wiedniñ metodö. W definicji metody znajduje siö jeszcze jeden problem. W definicji klasy lub moduäu self zawsze wskazuje na obiekt klasy lub moduäu: class A self # = A end Tak wiöc class A wewnñtrz definicji klasy A moĔe byè zapisane jako class self, poniewaĔ self wewnñtrz definicji A wskazuje na ten sam obiekt. Ten idiom jest uĔywany wszödzie w Rails do definiowania metod klasowych. PoniĔszy przykäad przedstawia wszystkie sposoby defi- niowania metod klasowych. class A def A.class_method_one; Metoda klasowa ; end def self.class_method_two; Równieš metoda klasowa ; end class A def class_method_three; Nadal metoda klasowa ; end end Podstawy Ruby _ 23 class self def class_method_four; Kolejna metoda klasowa ; end end end def A.class_method_five To dziaĪa poza definicjî klasy end class A def A.class_method_six MetaklasĂ mošna otworzyð poza definicjî klasy end end # Drukuje po kolei wyniki wywoáania kaĪdej metody. w(one two three four five six).each do |number| puts A.send(: class_method_#{number} ) end # Metoda klasowa # RównieĪ metoda klasowa # Nadal metoda klasowa # Kolejna metoda klasowa # To dziaáa poza definicją klasy # MetaklasĊ moĪna otworzyü poza definicją klasy Oznacza to równieĔ, Ĕe wewnñtrz definicji klasy singleton — podobnie jak w kaĔdej innej defi- nicji klasy — self nadal wskazuje na obiekt definiowanej klasy. Gdy pamiötamy, Ĕe ta wartoĈè w definicji bloku lub klasy jest wartoĈciñ ostatniej wykonanej instrukcji, to wiemy, Ĕe warto- Ĉciñ class objA; self; end jest obiekt klasa singleton objA. Konstrukcja class objA otwiera klasö singleton, a self (klasa singleton) jest zwracany z definicji klasy. ãñczñc to wszystko, moĔemy otworzyè klasö Object i dodaè metodö instancyjnñ do kaĔdego obiektu, który zwraca klasö singleton obiektu: class Object def metaclass class self self end end end Metoda ta tworzy podstawy metaid, o czym wkrótce. Brakujéce metody Po caäym tym zamieszaniu method_missing jest dosyè prosta. Istnieje tylko jedna reguäa — jeĔeli caäa procedura wyszukiwania metod zawiedzie, wyszukiwanie metody jest wykony- wane ponownie; szukana jest tym razem metoda method_missing zamiast poczñtkowej metody. JeĔeli metoda zostanie znaleziona, wywoäywana jest z argumentami oryginalnej metody, z do- äñczonñ nazwñ metody. Przekazywany jest równieĔ kaĔdy blok. DomyĈlna metoda method_missing z Object (rb_method_missing) zgäasza wyjñtek. 24 _ Rozdziaĥ 1. Techniki podstawowe Metaid Autorem niewielkiej biblioteki o nazwie metaid.rb, wspomagajñcej metaprogramowanie w Ruby, jest why the lucky stiff. Jest ona na tyle uĔyteczna, aby doäñczaè jñ do kaĔdego projektu, w którym potrzebne jest metaprogramowanie7: class Object # Ukryty singleton Ğledzi kaĪdego. def metaclass; class self; self; end; end def meta_eval blk; metaclass.instance_eval blk; end # Dodanie metody do metaklasy. def meta_def name, blk meta_eval { define_method name, blk } end # Definiowanie metody instancyjnej wewnątrz klasy. def class_def name, blk class_eval { define_method name, blk } end end W kaĔdym obiekcie biblioteka ta definiuje cztery metody: metaclass Odwoäuje siö do klasy singletonu odbiorcy (self). meta_eval Odpowiednik class_eval dla klas singletonów. WartoĈciuje dany blok w kontekĈcie klasy singletonu odbiorcy. meta_def Definiuje metodö w klasie singleton odbiorcy. JeĔeli odbiorca jest klasñ lub moduäem, spo- woduje to utworzenie metody klasowej (metody instancyjnej klasy singleton odbiorcy). class_def Definiuje metodö instancyjnñ odbiorcy (który musi byè klasñ lub moduäem). Korzystanie z metaid jest tak proste, poniewaĔ zastosowano w niej znaczne uproszczenia. Przez wykorzystanie skrótu do odwoäywania siö i rozszerzania metaklas nasz kod staje siö bardziej czytelny, poniewaĔ nie jest zatäoczony konstrukcjami takimi jak class self; self; end. Im krótszy i czytelny jest kod realizujñcy danñ technikö, tym bardziej prawdopodobne jest, Ĕe uĔyjemy go we wäaĈciwy sposób w naszym kodzie. PoniĔszy przykäad pokazuje zastosowanie metaid do uproszczenia naszej klasy singleton: class Person def name; Bob ; end def self.species; Homo sapiens ; end end Metody klasowe sñ dodawane jako metody instancyjne klasy singleton: Person.instance_methods(false) # = [ name ] Person.metaclass.instance_methods - Object.metaclass.instance_methods # = [ species ] 7 Seeing Metaclasses Clearly: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html. Podstawy Ruby _ 25 Przy uĔyciu metod z metaid moĔemy napisaè nasze definicje metod w nastöpujñcy sposób: Person.class_def(:name) { Bob } Person.meta_def(:species) { Homo sapiens } Wyszukiwanie zmiennych W Ruby wystöpujñ cztery rodzaje zmiennych — zmienne globalne, zmienne klasowe, zmienne instancyjne oraz zmienne lokalne8. Zmienne globalne sñ przechowywane globalnie, a zmienne lokalne sñ przechowywane leksykalnie, wiöc nie bödñ one przedmiotem naszej dyskusji, ponie- waĔ nie wykorzystujñ systemu klas Ruby. Zmienne instancyjne sñ specyficzne dla okreĈlonego obiektu. Sñ one prefiksowane za pomocñ symbolu @: @price jest zmiennñ instancyjnñ. PoniewaĔ kaĔdy obiekt Ruby ma strukturö iv_tbl, kaĔdy obiekt moĔe posiadaè zmienne instancyjne. PoniewaĔ kaĔda klasa jest równieĔ obiektem, klasy równieĔ mogñ posiadaè zmienne instan- cyjne. W poniĔszym przykäadzie kodu przedstawiony jest sposób odwoäania do zmiennej in- stancyjnej klasy: class A @ivar = Zmienna instancyjna klasy A end A.instance_variable_get(:@ivar) # = Zmienna instancyjna klasy A Zmienne instancyjne sñ zawsze wyszukiwane na podstawie obiektu wskazywanego przez self. PoniewaĔ self jest obiektem klasowym A w definicji klasy A ... end, @ivar naleĔy do obiektu klasowego A. Zmienne klasowe sñ inne. Do zmiennych klasowych (które zaczynajñ siö od @@) moĔe odwoäy- waè siö kaĔda zmienna klasowa. Zmienne klasowe mogñ byè równieĔ wykorzystywane w samej definicji klasy. Choè zmienne klasowe i instancyjne sñ podobne, nie sñ one tym samym: class A @var = Zmienna instancyjna klasy A @@var = Zmienna klasowa klasy A def A.ivar @var end def A.cvar @@var end end A.ivar # = Zmienna instancyjna klasy A A.cvar # = Zmienna klasowa klasy A W tym przykäadzie @var oraz @@var sñ przechowywane w tym samym miejscu — w tablicy iv_tbl klasy A. Sñ to jednak inne zmienne, poniewaĔ majñ one inne nazwy (symbole @ sñ doäñczane do nazwy zmiennej przy przechowywaniu). Funkcje Ruby do odwoäywania siö do zmiennych instancyjnych i klasowych sprawdzajñ, czy przekazywane nazwy sñ we wäaĈci- wym formacie: 8 Istniejñ równieĔ staäe, ale nie ma to w tej chwili wiökszego znaczenia. 26 _ Rozdziaĥ 1. Techniki podstawowe A.instance_variable_get(:@@var) # ~ -:17:in instance_variable_get : @@var is not allowed as an instance variable name (NameError) Zmienne klasowe sñ nieco mylñce w uĔyciu. Sñ one wspóädzielone w caäej hierarchii dziedzicze- nia, wiöc klasa pochodna modyfikujñca zmiennñ klasowñ modyfikuje równieĔ zmiennñ klasowñ rodzica. class A; @@x = 3 end = 3 class B A; @@x = 4 end = 4 class A; @@x end = 4 MoĔe to byè zarówno przydatne, jak i mylñce. Generalnie potrzebujemy albo zmiennych in- stancyjnych — które sñ niezaleĔne od hierarchii dziedziczenia — albo dziedziczonych atry- butów klasy zapewnianych przez ActiveSupport, które propagujñ wartoĈci w kontrolowany, dobrze zdefiniowany sposób. Bloki, metody i procedury Jednñ z zaawansowanych funkcji Ruby jest moĔliwoĈè wykorzystywania fragmentów kodu jako obiektów. Do tego celu wykorzystuje siö trzy klasy: Proc Klasa Proc reprezentuje blok kodu — fragment kodu, który moĔe byè wywoäywany z ar- gumentami i moĔe zwracaè wartoĈè. UnboundMethod Jest ona podobna do Proc; reprezentuje metodö instancyjnñ okreĈlonej klasy (naleĔy pamiötaè, Ĕe metoda klasowa jest równieĔ metodñ instancyjnñ obiektu klasowego, wiöc UnboundMethods moĔe reprezentowaè równieĔ metody klasowe). UnboundMethod musi byè zwiñzana z klasñ przed wywoäaniem. Method Obiekty Method sñ obiektami UnboundMethod, które zostaäy zwiñzane z obiektem za po- mocñ UnboundMethod#bind. MoĔna je równieĔ uzyskaè za pomocñ Object#method. Przeanalizujemy teraz kilka sposobów na uzyskanie obiektów Proc oraz Method. Jako przy- käadu uĔyjemy metody Fixnum#+. Zwykle wywoäujemy jñ przy pomocy uproszczonej skäadni: 3 + 5 # = 8 MoĔna jednak uĔyè wywoäania metody instancyjnej z obiektu Fixnum, tak samo jak innych metod instancyjnych: 3.+(5) # = 8 Do uzyskania obiektu reprezentujñcego metodö instancyjnñ moĔna wykorzystaè metodö Object# ´method. Metoda ta bödzie zwiñzana z obiektem, na którym zostaäa wywoäana, czyli 3. add_3 = 3.method(:+) add_3 # = # Method: Fixnum#+ Metoda ta moĔe byè skonwertowana do Proc lub wywoäana bezpoĈrednio z argumentami: add_3.to_proc # = # Proc:0x00024b08@-:6 add_3.call(5) # = 8 # Metoda#[] jest wygodnym synonimem dla Metoda#call. add_3[5] # = 8 Podstawy Ruby _ 27 Istniejñ dwa sposoby na uzyskanie metody niezwiñzanej. MoĔemy wywoäaè instance_method na obiekcie klasy: add_unbound = Fixnum.instance_method(:+) add_unbound # = # UnboundMethod: Fixnum#+ MoĔna równieĔ odäñczyè metodö, która zostaäa wczeĈniej zwiñzana z obiektem: add_unbound == 3.method(:+).unbind # = true add_unbound.bind(3).call(5) # = 8 MoĔemy zwiñzaè UnboundMethod z dowolnym obiektem tej samej klasy: add_unbound.bind(15)[4] # = 19 Jednak doäñczany obiekt musi byè instancjñ tej samej klasy, poniewaĔ w przeciwnym razie otrzymamy TypeError: add_unbound.bind(1.5)[4] # = # ~ -:16:in bind : bind argument must be an instance of Fixnum (TypeError) # ~ from -:16 OtrzymaliĈmy ten bäñd, poniewaĔ + jest zdefiniowany w Fixnum; dlatego obiekt UnboundMethod, jaki otrzymujemy, musi byè zwiñzany z obiektem, który jest kind_of?(Fixnum). Gdyby me- toda + byäa zdefiniowana w Numeric (z którego dziedziczñ Fixnum oraz Float), wczeĈniejszy kod zwróciäby 5.5. Bloki na procedury i procedury na bloki BieĔñca implementacja Ruby ma wyraĒnñ wadö — bloki nie zawsze sñ obiektami Proc i odwrot- nie. Zwykäe bloki (tworzone za pomocñ do...end oraz {}) muszñ byè doäñczane do wywoäania metody i nie sñ automatycznie obiektami. Nie moĔna na przykäad zapisaè code_ block ={puts abc }. Przydajñ siö tu funkcje Kernel#lambda i Proc.new, które konwertujñ bloki na obiekty Proc9. block_1 = lambda { puts abc } # = # Proc:0x00024914@-:20 block_2 = Proc.new { puts abc } # = # Proc:0x000246a8@-:21 Pomiödzy Kernel#lambda i Proc.new wystöpuje niewielka róĔnica. Powrót z obiektu Proc utworzonego za pomocñ Kernel#lambda powoduje zwrócenie wyniku do funkcji wywoäujñcej; powrót z obiektu Proc utworzonego za pomocñ Proc.new powoduje próbö wykonania po- wrotu z funkcji wywoäujñcej, a jeĔeli nie jest to moĔliwe, zgäaszany jest LocalJumpError. Poni- Ĕej pokazany jest przykäad: def block_test lambda_proc = lambda { return 3 } proc_new_proc = Proc.new { return 4 } lambda_proc.call # = 3 proc_new_proc.call # = puts Nigdy nie zostanie wywoĪane end block_test # = 4 Instrukcja powrotu w lambda_proc zwraca wartoĈè 3. W przypadku proc_new_proc instrukcja powrotu powoduje wyjĈcie z funkcji wywoäujñcej block_test — dlatego wartoĈè 4 jest zwra- 9 Kernel#proc jest innñ nazwñ dla Kernel#lambda, ale jest ona przestarzaäa. 28 _ Rozdziaĥ 1. Techniki podstawowe cana przez block_test. Instrukcja puts nie zostanie nigdy wykonana, poniewaĔ instrukcja proc_new_proc.call spowoduje wczeĈniejsze zakoþczenie block_test. Bloki mogñ byè konwertowane do obiektów Proc przez przekazanie ich do funkcji przy wy- korzystaniu w parametrach formalnych funkcji: def some_function( b) puts Blokiem jest #{b}, który zwraca #{b.call} end some_function { 6 + 3 } # Blokiem jest # Proc:0x00025774@-:7 , który zwraca 9 MoĔna równieĔ zastñpiè Proc za pomocñ , jeĔeli funkcja oczekuje bloku: add_3 = lambda {|x| x+3} (1..5).map( add_3) # = [4, 5, 6, 7, 8] Zamkniýcia Zamkniöcia (ang. closure) sñ tworzone w przypadku, gdy blok lub obiekt Proc odwoäuje siö do zmiennej zdefiniowanej poza ich zakresem. Pomimo tego, Ĕe blok zawierajñcy moĔe wyjĈè z zakresu, zmienne sñ utrzymywane do momentu wyjĈcia z zakresu przez odwoäujñcy siö do nich blok lub obiekt Proc. Uproszczony przykäad, pomimo Ĕe niezbyt praktyczny, demonstruje tö zasadö: def get_closure data = [1, 2, 3] lambda { data } end block = get_closure block.call # = [1, 2, 3] Funkcja anonimowa (lambda) zwracana przez get_closure odwoäuje siö do danych ze zmien- nej lokalnej, która jest zdefiniowana poza jej zakresem. Dopóki zmienna block znajduje siö w zakresie, bödzie przechowywaäa wäasnñ referencjö do data, wiöc instancja data nie zostanie usuniöta (pomimo tego, Ĕe funkcja get_closure zakoþczyäa siö). NaleĔy zwróciè uwagö, Ĕe przy kaĔdym wywoäaniu get_closure, data odwoäuje siö do innej zmiennej (poniewaĔ jest lo- kalna dla funkcji): block = get_closure block2 = get_closure block.call.object_id # = 76200 block2.call.object_id # = 76170 Klasycznym przykäadem zamkniöcia jest funkcja make_counter, która zwraca funkcjö licznika (Proc), która po uruchomieniu zwiöksza i zwraca ten licznik. W Ruby funkcja make_counter moĔe byè zaimplementowana w nastöpujñcy sposób: def make_counter(i=0) lambda { i += 1 } end x = make_counter x.call # = 1 x.call # = 2 y = make_counter y.call # = 1 y.call # = 2 Podstawy Ruby _ 29 Funkcja lambda tworzy zamkniöcie obejmujñce bieĔñcñ wartoĈè zmiennej lokalnej i. Nie tylko moĔna odwoäywaè siö do zmiennych, ale moĔna równieĔ modyfikowaè jej wartoĈci. KaĔde zamkniöcie uzyskuje osobnñ instancjö zmiennej (poniewaĔ jest to zmienna lokalna dla kaĔdej z instancji make_counter). PoniewaĔ x oraz y zawierajñ referencje do innych instancji zmiennej lokalnej i, majñ one inny stan. Techniki metaprogramowania Po omówieniu podstaw Ruby przedstawimy kilka powszechnie stosowanych technik metapro- gramowania wykorzystywanych w tym jözyku. Choè przykäady sñ napisane w Ruby, wiökszoĈè z technik moĔna wykorzystaè w dowolnym dynamicznym jözyku programowania. W rzeczywistoĈci wiele z idiomów metaprogramowa- nia stosowanych w Ruby jest bezwstydnie skradzionych z jözyków Lisp, Smalltalk lub Perl. OpóŚnienie wyszukiwania metod do czasu wykonania Czasami chcemy utworzyè interfejs, którego metody sñ zaleĔne od danych dostöpnych w czasie wykonywania programu. NajwaĔniejszym przykäadem takiej konstrukcji sñ metody akcesorów atrybutów w ActiveRecord dostöpne w Rails. Wywoäania metod obiektu ActiveRecord (tak jak person.name) sñ modyfikowane w czasie dziaäania na odwoäania do atrybutów. Na pozio- mie metod klasy ActiveRecord oferuje niezwykäñ elastycznoĈè — wyraĔenie Person.find_ ´all_by_user_id_and_active(42, true) jest zamieniane na odpowiednie zapytanie SQL, a dodatkowo, jeĔeli rekord nie zostanie znaleziony, zgäaszany jest wyjñtek NoMethodError. UmoĔliwia to metoda method_missing dostöpna w Ruby. Gdy na obiekcie zostanie wywoäana nieistniejñca metoda, Ruby przed zgäoszeniem wyjñtku NoMethodError wyszukuje w klasie obiektu metodö method_missing. Pierwszym argumentem method_missing jest nazwa wy- woäywanej metody; pozostaäe argumenty odpowiadajñ argumentom przekazanym do metody. KaĔdy blok przekazany do metody jest równieĔ przekazywany do method_missing. Tak wiöc kompletna sygnatura tej metody jest nastöpujñca: def method_missing(method_id, *args, block) ... end Istnieje kilka wad wykorzystywania method_missing: x Jest wolniejsza niĔ konwencjonalne wyszukiwanie metod. Proste testy pokazujñ, Ĕe wyszu- kiwanie metod za pomocñ method_missing jest co najmniej dwa do trzech razy bardziej czasochäonne niĔ konwencjonalne wyszukiwanie. x PoniewaĔ wywoäywana metoda nigdy faktycznie nie istnieje — jest po prostu przechwy- tywana w ostatnim kroku procesu wyszukiwania metod — nie moĔe byè dokumentowana lub poddawana introspekcji, jak konwencjonalne metody. x PoniewaĔ wszystkie metody dynamiczne muszñ przechodziè przez metodö method_missing, moĔe ona znacznie urosnñè, jeĔeli w kodzie znajduje siö wiele miejsc wymagajñcych dy- namicznego dodawania metod. x Zastosowanie method_missing ogranicza zgodnoĈè z przyszäymi wersjami API. Gdy bö- dziemy polegaè na metodzie method_missing przy obsäudze niezdefiniowanych metod, wprowadzenie nowych metod w przyszäych wersjach API moĔe zmieniè oczekiwania naszych uĔytkowników. 30 _ Rozdziaĥ 1. Techniki podstawowe Dobrñ alternatywñ jest podejĈcie zastosowane w funkcji generate_read_methods z Active ´Record. Zamiast czekaè na przechwycenie wywoäania przez method_missing, ActiveRecord generuje implementacje dla metod odczytu i modyfikacji atrybutu, dziöki czemu mogñ byè one wywoäywane przez konwencjonalny mechanizm wyszukiwania metod. Jest to bardzo wydajna metoda, a dynamiczna natura Ruby pozwala na napisanie metod, któ- re przy pierwszym wywoäaniu wymieniajñ siö na swoje zoptymalizowane wersje. Jest to uĔy- wane w routingu Ruby, który musi byè bardzo szybki; zastosowanie tej metody jest przed- stawione w dalszej czöĈci rozdziaäu. Programowanie generacyjne — tworzenie kodu na bieŜéco Jednñ z efektywnych technik, która skäada siö z kilku kolejnych, jest programowanie genera- cyjne — pisanie kodu tworzñcego kod. Technika ta moĔe byè zastosowana do bardzo prostych zadaþ, takich jak pisanie skryptu automatyzujñcego niektóre nudne czöĈci programowania. MoĔna na przykäad wypeäniè przypadki testowe dla kaĔdego z uĔytkowników: brad_project: id: 1 owner_id: 1 billing_status_id: 12 john_project: id: 2 owner_id: 2 billing_status_id: 4 ... JeĔeli byäby to jözyk bez moĔliwoĈci zastosowania skryptów do definiowania przypadków testowych, konieczne byäoby napisanie ich röcznie. MoĔe to zaczñè sprawiaè problemy, gdy dane przekroczñ masö krytycznñ, a jest niemal niemoĔliwe, gdy przypadki testowe majñ dziwne za- leĔnoĈci w danych Ēródäowych. Programowanie generacyjne pozwala napisaè skrypt do gene- rowania tych przypadków uĔycia na podstawie danych Ēródäowych. Choè nie jest to idealne rozwiñzanie, jest to znaczne usprawnienie w stosunku do pisania przypadków uĔycia röcznie. Wystöpuje tu jednak problem z utrzymaniem — konieczne jest wäñczenie skryptu w proces tworzenia oraz zapewnienie, Ĕe przypadki testowe sñ regenerowane w momencie zmiany danych Ēródäowych. Jest to (na szczöĈcie) rzadko, o ile w ogóle potrzebne w Ruby on Rails. Niemal w kaĔdym aspekcie konfiguracji aplikacji Rails moĔna stosowaè skrypty, co jest spowodowane w wiökszoĈci przez zastosowanie wewnötrznych jözyków specyficznych dla domeny (DSL — ang. Domain Specific Language). W wewnötrznym DSL moĔna mieè do dyspozycji wszystkie moĔliwoĈci jö- zyka Ruby, nie tylko okreĈlony interfejs biblioteki, jaki autor zdecydowaä siö udostöpniè. Wracajñc do poprzedniego przykäadu, ERb (ang. Embedded Ruby) znacznie uäatwia pracö. MoĔ- na wstrzyknñè dowolny kod Ruby na poczñtek pliku YAML10 z uĔyciem znaczników ERb oraz = , doäñczajñc tam dowolnñ logikö: 10 Uniwersalny jözyk formalny przeznaczony do reprezentowania róĔnych danych w ustrukturyzowany sposób. Säowo YAML to akronim rekursywny od säów YAML Ain’t Markup Language. Pierwotnie interpretowano ten skrót jako Yet Another Markup Language. Pierwsza wersja zaproponowana zostaäa w 2001 roku przez Clarka Evansa we wspóäpracy z Ingy döt Net oraz Oren Ben-Kiki — przyp. red. Techniki metaprogramowania _ 31 User.find_all_by_active(true).each_with_index do |user, i| = user.login _project: id: = i owner_id: = user.id billing_status_id: = user.billing_status.id end Implementacja tego wygodnego mechanizmu w ActiveRecord nie moĔe byè prostsza: yaml = YAML::load(erb_render(yaml_string)) przy wykorzystaniu metody pomocniczej erb_render: def erb_render(fixture_content) ERB.new(fixture_content).result end W programowaniu generacyjnym czösto wykorzystuje siö Module#define_method lub class_eval oraz def do tworzenia metod na bieĔñco. Technika ta jest wykorzystywana do akcesorów atry- butów; funkcja generate_read_methods definiuje metody do modyfikacji i odczytu jako metody instancyjne klasy ActiveRecord w celu zmniejszenia liczby wywoäaþ metody method_missing (która jest dosyè kosztownñ technikñ). Kontynuacje Kontynuacje sñ bardzo efektywnñ technikñ kontroli przepäywu sterowania. Kontynuacja repre- zentuje okreĈlony stan stosu wywoäaþ oraz zmiennych leksykalnych. Jest to migawka wykonana w momencie, gdy Ruby wykonuje dany fragment kodu. Niestety, w implementacji Ruby 1.8 implementacja kontynuacji jest tak powolna, Ĕe technika ta jest bezuĔyteczna w wielu aplikacjach. W kolejnych wersjach maszyn wirtualnych Ruby 1.9 sytuacja moĔe siö poprawiè, ale nie moĔna siö spodziewaè dobrej wydajnoĈci dziaäania kontynuacji w Ruby 1.8. Jest to jednak bardzo uĔyteczna konstrukcja, a biblioteki WWW bazujñce na kontynuacjach sñ interesujñcñ alter- natywñ dla bibliotek takich jak Rails, wiöc przedstawimy tu ich zastosowanie. Kontynuacje sñ tak efektywne z kilku powodów: x Kontynuacje sñ po prostu obiektami; mogñ byè one przekazywane z funkcji do funkcji. x Kontynuacje mogñ byè wywoäywane z dowolnego miejsca. JeĔeli mamy referencjö kon- tynuacji, moĔemy jñ wywoäaè. x Kontynuacje mogñ byè uĔywane wielokrotnie. MoĔna je wielokrotnie wykorzystywaè do powrotu z funkcji. Kontynuacje sñ czösto przedstawiane jako „strukturalne GOTO”. Z tego powodu powinny byè traktowane z takñ samñ uwagñ jak kaĔda inna konstrukcja GOTO. Kontynuacje majñ niewielkie lub Ĕadne zastosowanie w kodzie aplikacji; powinny byè ukryte w bibliotece. Nie uwaĔam, Ĕe naleĔy chroniè programistów przed nimi samymi. Chodzi o to, Ĕe kontynuacje majñ wiökszy sens przy tworzeniu abstrakcji niĔ przy bezpoĈrednim wykorzystaniu. Gdy programista buduje aplikacjö, powinien myĈleè o „zewnötrznym iteratorze” lub „koprocedurze” (obie te abstrakcje sñ zbudowane za pomocñ kontynuacji), a nie o „kontynuacji”. 32 _ Rozdziaĥ 1. Techniki podstawowe Seaside11 jest bibliotekñ WWW dla jözyka Smalltalk, która bazuje na kontynuacjach. Sñ one wykorzystywane w Seaside do zarzñdzania stanem sesji. KaĔdy z uĔytkowników odpowiada kontynuacji na serwerze. Gdy zostanie odebrane Ĕñdanie, wywoäywana jest kontynuacja i wy- konywany jest dalszy kod. Dziöki temu caäa transakcja moĔe byè zapisana jako jeden strumieþ kodu pomimo tego, Ĕe moĔe ona skäadaè siö z wielu Ĕñdaþ HTTP. Biblioteka ta korzysta z tego, Ĕe kontynuacje w Smalltalku sñ serializowalne; mogñ byè one zapisane do bazy danych lub w systemie plików, a nastöpnie po odebraniu Ĕñdania pobierane i ponownie wywoäywane. Kontynuacje w Ruby nie sñ serializowalne. W Ruby kontynuacje sñ tylko obiektami pamiöcio- wymi i nie mogñ byè transformowane do strumienia bajtów. Borges (http://borges.rubyforge.org) jest prostym przeniesieniem Seaside 2 do Ruby. Gäównñ róĔ- nicñ pomiödzy Seaside a Borges jest to, Ĕe biblioteka Borges musi przechowywaè wszystkie bieĔñce kontynuacje w pamiöci, poniewaĔ nie sñ one serializowalne. To znaczne ograniczenie uniemoĔliwia stosowanie biblioteki Borges do aplikacji WWW o jakimkolwiek obciñĔeniu. JeĔeli w jednej z implementacji Ruby powstanie mechanizm serializowania kontynuacji, ograniczenie to zostanie usuniöte. EfektywnoĈè kontynuacji przedstawia poniĔszy kod przykäadowej aplikacji Borges, która ge- neruje listö elementów z magazynu dostöpnego online: class SushiNet::StoreItemList Borges::Component def choose(item) call SushiNet::StoreItemView.new(item) end def initialize(items) @batcher = Borges::BatchedList.new items, 8 end def render_content_on(r) r.list_do @batcher.batch do |item| r.anchor item.title do choose item end end r.render @batcher end end # class SushiNet::StoreItemList WiökszoĈè akcji jest wykonywana w metodzie render_content_on, która korzysta z BatchedList (do stronicowania) w celu wygenerowania stronicowanej listy äñczy do produktów. Jednak caäa zabawa zaczyna siö od wywoäania anchor, w którym jest przechowywane wywoäanie do wykonania po klikniöciu odpowiedniego äñcza. Nie ma jednak zgody, w jaki sposób wykorzystywaè kontynuacje do programowania WWW. HTTP zostaä zaprojektowany jako protokóä bezstanowy, a kontynuacje dla transakcji WWW sñ caäkowitym przeciwieþstwem bezstanowoĈci. Wszystkie kontynuacje muszñ byè przecho- wywane na serwerze, co zajmuje pamiöè i przestrzeþ na dysku. Wymagane sñ równieĔ doäñ- czajñce sesje do kierowania wywoäaþ uĔytkownika na ten sam serwer. W wyniku tego, jeĔeli jeden z serwerów zostanie wyäñczony, wszystkie jego sesje zostajñ utracone. Najbardziej po- pularna aplikacja Seaside, DabbleDB (http://dabbledb.com) wykorzystuje kontynuacje w bardzo maäym stopniu. 11 http://seaside.st. Techniki metaprogramowania _ 33 Doĥéczenia Doäñczenia zapewniajñ kontekst dla wartoĈciowania w kodzie Ruby. Doäñczenia to zbiór zmien- nych i metod, które sñ dostöpne w okreĈlonym (leksykalnym) punkcie kodu. KaĔde miejsce w kodzie Ruby, w którym sñ wartoĈciowane instrukcje, posiada doäñczenia i mogñ byè one po- brane za pomocñ Kernel#binding. Doäñczenia sñ po prostu obiektami klasy Binding i mogñ byè one przesyäane tak jak zwykäe obiekty: class C binding # = # Binding:0x2533c def a_method binding end end binding # = # Binding:0x252b0 C.new.a_method # = # Binding:0x25238 Generator rusztowania Rails zapewnia dobry przykäad zastosowania doäñczeþ: class ScaffoldingSandbox include ActionView::Helpers::ActiveRecordHelper attr_accessor :form_action, :singular_name, :suffix, :model_instance def sandbox_binding binding end # ... end ScaffoldingSandbox to klasa zapewniajñca czyste Ĉrodowisko, na podstawie którego generu- jemy szablon. ERb moĔe generowaè szablon na podstawie kontekstu doäñczeþ, wiöc API jest dostöpne z poziomu szablonów ERb. part_binding = template_options[:sandbox].call.sandbox_binding # ... ERB.new(File.readlines(part_path).join,nil, - ).result(part_binding) WczeĈniej wspomniaäem, Ĕe bloki sñ zamkniöciami. Doäñczenie zamkniöcia reprezentuje jego stan — zbiór zmiennych i metod, do których posiada dostöp. Doäñczenie zamkniöcia moĔna uzyskaè za pomocñ metody Proc#binding: def var_from_binding( b) eval( var , b.binding) end var = 123 var_from_binding {} # = 123 var = 456 var_from_binding {} # = 456 UĔyliĈmy tutaj tylko obiektu Proc jako obiektu, dla którego pobieraliĈmy doäñczenie. Poprzez dostöp do doäñczeþ (kontekstu) tych bloków moĔna odwoäaè siö do zmiennej lokalnej var przy pomocy zwykäego eval na doäñczeniu. 34 _ Rozdziaĥ 1. Techniki podstawowe Introspekcja i ObjectSpace — analiza danych i metod w czasie dziaĥania Ruby udostöpnia wiele metod pozwalajñcych na zaglñdanie do obiektów w czasie dziaäania programu. Dostöpne sñ metody dostöpu do zmiennych instancyjnych, ale poniewaĔ äamiñ one zasadö hermetyzacji, naleĔy ich uĔywaè z rozwagñ. class C def initialize @ivar = 1 end end c = C.new c.instance_variables # = [ @ivar ] c.instance_variable_get(:@ivar) # = 1 c.instance_variable_set(:@ivar, 3) # = 3 c.instance_variable_get(:@ivar) # = 3 Metoda Object#methods zwraca tablicö metod instancyjnych wraz z metodami typu singleton zdefiniowanymi w obiekcie odbiorcy. JeĔeli pierwszym parametrem methods jest false, zwra- cane sñ wyäñcznie metody typu singleton. class C def inst_method end def self.cls_method end end c = C.new class c def singleton_method end end c.methods - Object.methods # = [ inst_method , singleton_method ] c.methods(false) # = [ singleton_method ] Z kolei metoda Module#instance_methods zwraca tablicö metod instancyjnych klasy lub moduäu. NaleĔy zwróciè uwagö, Ĕe instance_methods jest wywoäywana w kontekĈcie klasy, na- tomiast methods — w kontekĈcie instancji. Przekazanie wartoĈci false do instance_methods powoduje, Ĕe zostanñ pominiöte metody klas nadrzödnych: C.instance_methods(false) # = [ inst_method ] Do analizy metod klasy C moĔemy wykorzystaè metodö metaclass z metaid: C.metaclass.instance_methods(false) # = [ new , allocate , cls_method , superclass ] Z mojego doĈwiadczenia wynika, Ĕe metody te sñ zwykle wykorzystywane do zaspokojenia ciekawoĈci. Z wyjñtkiem bardzo niewielu dobrze zdefiniowanych idiomów rzadko zdarza siö, Ĕe w kodzie produkcyjnym wykorzystywana jest refleksja metod obiektu. Znacznie czöĈciej techniki te sñ wykorzystywane we wierszu poleceþ konsoli do wyszukiwania dostöpnych metod obiektu — zwykle jest to szybsze od siögniöcia do podröcznika. Array.instance_methods.grep /sort/ # = [ sort! , sort , sort_by ] Techniki metaprogramowania _ 35 ObjectSpace ObjectSpace to moduä wykorzystywany do interakcji z systemem obiektowym Ruby. Posia- da on kilka przydatnych metod moduäu, które uäatwiajñ operacje niskopoziomowe. x Metody usuwania nieuĔytków: define_finalizer (konfiguruje metodö wywoäania zwrot- nego wywoäywanñ bezpoĈrednio przed zniszczeniem obiektu), undefine_finalizer (usuwa to wywoäanie zwrotne) oraz garbage_collect (uruchamia usuwanie nieuĔytków). x _id2ref konwertuje ID obiektu na referencjö do tego obiektu Ruby. x each_object pozwala na iteracjö po wszystkich obiektach (lub wszystkich obiektach okreĈlonej klasy) i przekazuje je do bloku. Jak zawsze, duĔe moĔliwoĈci wiñĔñ siö ze znacznñ odpowiedzialno
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Rails. Zaawansowane programowanie
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: