Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert...

60
! ∀ # ∃%%&

description

Praca magisterska zrealizowana na Wydziale Matematyki i Informatyki Uniwersytetu im. Adama Mickiewicza w Poznaniu.Wstęp1. Rozwój platformy1.1 Historia Ruby2. Koncepcje i techniki programistyczne3. Podstawy języka Ruby3.1. Elementy składnii3.2. Programowanie obiektowe4. Wielowarstwowa architektura systemu4.1 Czym jest Ruby on Rails4.2 Przepływ sterowania i model MVC4.3 Narzędzia4.4 Komponenty5. Budowa aplikacji5.1 Kontroler5.2 Model5.3 Widok6. Testowanie aplikacji6.1 Wprowadzenie6.2 Testy jednostkowe6.3 Testy funkcjonalne6.4 Inne rodzaje testów6.5 Narzędzia alternatywne oraz dodatkowe7. Wdrożenie7.1 Dostępne opcje7.2 Platforma wdrożeniowa8. WnioskiBibliografia

Transcript of Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert...

Page 1: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

������������ ���������������

��������������������������

���������������

�����������������

������������������

� ����������������������

��������������������

����������������� ����� ������!�����

�������∀���#�������������

������∃%%&

Page 2: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

�������������� ����������������������������������������� �����������������������������������������������������������

Page 3: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

3

Spis treściWstęp ................................................................................................................ 41. Rozwój platformy Ruby On Rails .................................................................. 6

1.1. Historia Ruby ...................................................................................... 61.2. Ruby on Rails ...................................................................................... 7

2. Koncepcje i techniki programistyczne ........................................................... 92.1. Koncepcje ............................................................................................ 92.2. Techniki ............................................................................................. 10

3. Podstawy języka Ruby ................................................................................ 153.1. Elementy składni .............................................................................. 153.2. Programowanie obiektowe ................................................................ 16

4. Wielowarstwowa architektura systemu ....................................................... 194.1. Czym jest Ruby on Rails ................................................................... 194.2. Przypływ sterowania i model MVC .................................................... 214.3. Narzędzia .......................................................................................... 224.4. Komponenty ...................................................................................... 23

5. Budowa aplikacji ......................................................................................... 255.1. Kontroler ........................................................................................... 255.2. Model ................................................................................................ 285.3. Widok ................................................................................................ 34

6. Testowanie aplikacji .................................................................................... 366.1. Wprowadzenie ................................................................................. 366.2. Testy jednostkowe ........................................................................... 406.3. Testy funkcjonalne ........................................................................... 416.4. Inne rodzaje testów ......................................................................... 456.5. Narzędzie alternatywne oraz dodatkowe ......................................... 46

7. Wdrożenie ................................................................................................... 487.1. Dostępne opcje ................................................................................ 487.2. Platforma wdrożeniowa .................................................................... 51

8. Wnioski ...................................................................................................... 57Bibliografia ...................................................................................................... 59

Page 4: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

4

WstępRuby on Rails jest znaną w świecie i dojrzałą technologią, która od lat rozwijasię w zawrotnym tempie, zdobywając sobie coraz więcej zwolenników. Jej sukcesw  sporej mierze przyczynił się do popularyzacji języka Ruby, który w 2006bardzo szybko osiągnął 10 miejsce w rankingu Tiobe, a mimo to znajomośćtych rozwiązań w środowisku akademickim wydaje się być bardzo niewielka.W  niniejszej pracy postaramy się w ogólnym zarysie zaprezentować całyframework, posiłkując się przy tym fragmentami kodu zrealizowanej przez nasaplikacji.

Praca zaczyna się przedstawieniem krótkiej historii Rails oraz języka, w którymzostał on napisany, co pozwala lepiej zrozumieć kontekst jego powstawania orazporównać na tle innych technologi tego okresu.

Sam framework został opublikowany w 2004 roku, trzy lata po tym jak pojawił sięManifest Zwinnego Wytwarzania Oprogramowania (Agile Manifesto) i od początkuwspiera pracę z użyciem technik, wykorzystywanych głównie w metodykachzwinnych. Z tego względu drugi rozdział poświęcony został na ich opisanie orazwspomnienie kluczowych koncepcji, jakie kryją się za jego architekturą.

Ruby posiada bardzo duże możliwości, które okazują się być szczególnieprzydatne w metaprogramowaniu - technice mającej bardzo duże znaczeniew  Rails. Jednak już nawet na poziomie składniowym odróżnia się on niecood najpopularniejszych obecnie języków, dlatego trzeci rozdział stanowi krótkieomówienie samego języka, dzięki czemu mamy nadzieję, że zrozumienieprzykładów zawartych w pracy nie będzie nastręczało problemów.

Czwarty rozdział to omówienie architektury systemu. Daje on pogląd na to, jakogólnie działa aplikacja i jak poszczególne warstwy współpracują ze sobą, podczasinterakcji użytkownika ze stroną internetową. Każda z warstw modelu MVC jestscharakteryzowana osobno w części piątej.

Istotnym elementem procesu wytwarzania każdego rodzaju oprogramowaniajest testowanie. Rails dostarcza szereg narzędzi wspierających automatycznetestowanie, które dodatkowo mogą zostać uzupełnione wieloma bibliotekamistworzonymi przez społeczność programistów. Jedne jak i drugie zostałyprzedstawione w rozdziale szóstym.

Siódmy rozdział skupia się na wadach i zaletach dostępnych rozwiązań, z którychmożna skorzystać podczas uruchamiania gotowego systemu. Zostały w nimtakże szczegółowo opisane nasze doświadczenia oraz technologie użyte podczaswdrażania projektu w domenie co-do-grosza.pl w kwietniu 2009 roku.

W ostatnim rozdziale znalazły się wnioski oraz rozważania odnośnie przyszłościplatformy.

Zrealizowana w ramach pracy magisterskiej aplikacja internetowa Manage MyMoney ma za zadanie wspierać użytkowników w procesie zarządzania finansamiosobistymi. Pozwala ona wprowadzać oraz importować informacje o dokonanychprzez użytkownika transakcjach i ich przeznaczeniu, w celu ich późniejszegoprzetwarzania. Stworzony system posiada moduły odpowiedzialne za zarządzanie

Page 5: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wstęp

5

dłużnikami i wierzycielami (wysyłanie przypomnień), planowanie (realizacjakrótko i długoterminowych celów finansowych), raportowanie (różnorodnewykresy) oraz obsługuje wiele rodzajów jednostek (akcje, fundusze, waluty).

Page 6: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

6

Rozdział 1Rozwój platformy Ruby On Rails

We współczesnym świecie używamy stron internetowych pełnych treści orazserwisów, które pozwalają robić, co tylko wyobraźnia nam podpowie. Jednakżedroga do stanu jaki obserwujemy teraz była długa i wyboista - kilkanaścielat minęło od powstania pierwszej strony internetowej której treść zostaławygenerowana dynamicznie. Ruby on Rails dopiero pojawił się na tej ścieżcei jest wielce prawdopodobne że innowacyjność rozwiązań tego frameworku staniesię kamieniem milowym w dalszym rozwoju technologii internetowych. Poniższyrozdział stanowi zwięzły opis historii języka Ruby oraz frameworku Ruby on Rails.

1.1. Historia RubyProjekt Ruby został zainicjowany w 1993 roku przez Yukihiro Matsumoto,japońskiego programistę. Celem Matsumoto było stworzenie „językaskryptowego, który jest potężniejszy niż Perl i bardziej zorientowany obiektowo niżPython”[3]. Pierwsza publiczna wersja o numerze 0.95 została zaprezentowanana Japońskich grupach dyskusyjnych w 1995 r. Już w tym stadium rozwoju językposiadał duże możliwości: pełną obiektowość, mixiny, iteratory, domknięcia,przechwytywanie wyjątków czy odśmiecacz pamięci (garbage collection). Wersja1.0 została opublikowana rok później, a w 1999 wraz z wersją 1.3 i powstaniemanglojęzycznej listy dyskusyjnej języka zaczęła się ekspansja Ruby'ego pozaJaponię.

Język Ruby sam w sobie nie zyskał popularności przy tworzeniu stroninternetowych, choć było to naturalnie możliwe, np. przy użyciu mechanizmuCommon Gateway Interface[1]. CGI w prosty sposób pozwala serwerowiinternetowemu na uruchomienie skryptu (przykładowo w języku Ruby)w odpowiedzi na przychodzące żądanie i wysłanie wyników jego działania jakoodpowiedź. Na długie lata Ruby pozostał jednak niszowym narzędziem używanymnp. przez administratorów do pisania skryptów powłoki w systemach uniksowych.Dopiero użycie go przez framework RoR zwróciło uwagę programistycznegoświata na ten egzotyczny język.

Obecnie Ruby cały czas intensywnie się rozwija, aktualna w czasie pisania tejpracy wersja stabilna języka (1.8.7) została wydana w czerwcu 2008 roku, tejwersji zaleca się do pracy z frameworkiem Ruby On Rails. Dzięki RoR nastąpiłolawinowe zainteresowanie tym językiem, wg. indeksu TIOBE (www.tiobe.com)Ruby jest obecnie 10. najpopularniejszym językiem programowania.

Page 7: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Rozwój platformy Ruby On Rails

7

Rysunek 1.1. Wykres zainteresowania językiem w ostatnich latach

Aktualne prace nad językiem mają na celu głównie zwiększenie wydajnościinterpretera oraz ustandaryzowanie składni języka w ISO. Równolegle powstajekilka interpreterów języka Ruby[2]:

• MRI - standardowa implementacja, rozwijana i utrzymywana przez YukihiroMatsumoto, aktualna wersja 1.8.

• Yarv - implementacja oparta o maszynę wirtualną, pozwala na nawet 10-krotne zwiększenie wydajności kodu, Yarv stał się oficjalnym interpreteremRuby w  wersji 1.9. Istnieje możliwośc uruchomienia Ruby on Rails na tejimplementacji.

• Ruby Enterprise - poprawiona wersja MRI, o szybszym działaniu interpreterai mniejszym zużyciu pamięci - zalecana do środowisk produkcyjnych.

• jRuby - będąca implementacją działającą na maszynie wirtualnej Java.Dystrybucja ta jest bardziej przenośna oraz umożliwia dostęp do wszystkichbibliotek napisanych w języku Java z poziomu aplikacji Ruby. Ma to dużeznaczenie dla popularyzacji języka w dużych komercyjnych projektach, którychdomeną jest w tej chwili Java i technologie J2EE.

• IronRuby - implementacja ma platformę .NET (trwają prace).

• MacRuby - implementacja w języku ObjectivC dla systemu Mac OS X (trwająprace).

1.2. Ruby on RailsW ciągu wielu lat od powstania pierwszego skryptu CGI powstały różnorodnenarzędzia do tworzenia stron w liczących się językach programowania.W  małych i  średnich zastosowaniach dużą popularność zyskał PHP, pozwalałszybko tworzyć strony przeplatając kod HTML instrukcjami dostępu do bazydanych. W rozwiązaniach korporacyjnych dużą popularność zyskała Java,

Page 8: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Rozwój platformy Ruby On Rails

8

najpierw wprowadzając specyfikację Java Servlet, a potem Java Server Pages.Szybka ekspansja internetu sprawiła że początkowe technologie stały sięniewystarczające, powstawały coraz to nowe rozwiązania. Programista WWWchcący stworzyć rozbudowaną aplikację musiał za każdym razem napisaćdużo kodu własnoręcznie (w przypadku PHP) lub zintegrować wiele technologii(w przypadku języka Java) obejmujących język programowania, mapowanieobiektowo relacyjne (Hibernate), szkielet MVC (Struts), narzędzie do budowaniaaplikacji (Ant) oraz nauczyć się konfigurować wszystkie komponenty przy pomocyjęzyka XML. Można powiedzieć że Ruby on Rails powstał w odpowiedzi nazaistniały stan rzeczy, z niechęci do powtarzania ciągle tych samych czynnościprzy tworzeniu aplikacji oraz z nieuzasadnionego wydłużania się czasu uczeniaw przypadku popularnych technologii.

Zaistnienie RoR jest związane z duńskim programistą Davidem HeinemeieremHanssonem. Podczas swojej pracy nad aplikacją do zarządzania projektami(BaseCamp) w firmie 37Signals stworzył rozbudowany framework ułatwiającytworzenie aplikacji, zawierający wspomniane mapowanie obiektowo relacyjneoraz wsparcie dla MVC. DHH opublikował swój framework na zasadach opensource w 2004 pod nazwą Ruby on Rails. Dzięki pracy społeczności Open SourceRuby on Rails jest dziś dojrzałą technologią tworzenia stron internetowych,posiadającą wsparcie w postaci wielu rozszerzeń, dokumentacji oraz książek.Ponadto rozwiązania wprowadzone w Ruby on Rails okazały się na tyle ciekawe,że zostały zaadoptowane we frameworkach takich jak CakePHP i Django (Python).

Page 9: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

9

Rozdział 2Koncepcje i techniki

programistyczneRuby on Rails został zbudowany w myśl prostych koncepcji mających na celuzwiększenie wydajności programisty. Jego twórcy mają też określony pogląd nasam proces tworzenia aplikacji, w związku z czym już od samego początkuzapewnia on szerokie wsparcie dla pewnych technik programistycznych, którychna co dzień używają korzystający z niego programiści. W niniejszym rozdzialezostaną zaprezentowane zarówno jedne jak i drugie.

2.1. KoncepcjeDwie kluczowe koncepcje miały swój wpływ na to, jak jest zbudowane i jak pracujesię z Rails. Często na względzie mają je również twórcy dodatków, którzy starająsię, by ich komponenty były napisane w tym samym duchu.

Don't repeat yourself

Don't Repeat Yourself - zasada ta nakazuje umieszczanie każdej informacjiw systemie tylko raz w celu uniknięcia duplikacji [1], która zwiększa nakład pracyniezbędny do dokonania zmian i stopień trudności ich wprowadzania oraz możezmniejszyć przejrzystość kodu i prowadzić do niespójności (w sytuacji, gdy zmianazostanie odzwierciedlona tylko w jednym miejscu a pominięta w innym). W samymśrodowisku jest ona doskonale zrealizowana, ponadto miejsce, w którym należyzapisać taką informację, jest zwykle wyznaczone przez wzorzec MVC orazwygenerowany szkielet aplikacji. Jest to bardzo duży plus dla programistów,którzy przychodzą do RoR z innych środowisk, gdzie nierzadko pojawia się sporanadmiarowość.

Jednym z kontrprzykładów stosowania się do tej zasady jest NHibernate-silnik do mapowania obiektowo relacyjnego dedykowany dla platformy .Net,w którym zapisanie informacji o relacji jeden do wiele pomiędzy dwomamapowanymi tabelami, wiąże się z koniecznością edytowania czterech plików.Tymczasem RoR korzystając z silnika ORM jakim jest ActiveRecord wymagaw takiej sytuacji napisania jedynie dwóch linii kodu (zob. przykład w następnympodrozdziale). Wszelkie informacje o mapowaniach zapisuje się korzystając całyczas z tego samego języka programowania, bez konieczności zagłębiania się wpliki konfiguracyjne (takie jak XML) i ich strukturę, co zdecydowanie zwiększaczytelność informacji oraz zmniejsza nakład pracy jaki jest wymagany np. wprocesie refaktoryzacji.

Convention over configuration

Konwencje (w programowaniu) to zbiór zasad, przyjętych przez społecznośćkorzystającą z danej technologi. Ich przestrzeganie nie jest konieczne dopoprawnego działania aplikacji, jednak zwykle przynosi szereg korzyści(zwłaszcza podczas kolaboracji większej liczby osób), które przyczyniają się doich tak szerokiego stosowania, że możemy je nazwać właśnie tym określeniem.

Page 10: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Koncepcje i technikiprogramistyczne

10

Niektóre konwencje wyłaniają się spontanicznie, inne zostają z góry narzuconeprzez twórców.

Druga zasada jaka przyświeca twórcom Ruby on Rails to Convention OverConfiguration (konwencja ponad konfigurację) i objawia się ona we wszystkichaspektach jego używania [1]. Oznacza ona, że wszelkie metody oraz notacjeużywane przez nas przy pisaniu aplikacji mają bardzo sensowne wartościdomyślne, które mogą być pomijane zostawiając miejsce tylko tym istotnym. Jeśliw swojej pracy stosujemy się do tych konwencji, struktura aplikacji oraz kod stająsię czytelniejsze oraz łatwiejsze do zrozumienia dla nowych osób pojawiającychsię w projekcie. Nierzadko jedna linia w RoR oddaje to, co w innych frameworkachmusi być oddane, z użyciem dziesiątek linii kodu. Spójrzmy na poniższy przykład:

class User < ActiveRecord::Base has_many :categoriesend

class Category < ActiveRecord::Base belongs_to :userend

Dzięki dobrze dobranym wartościom domyślnym dla metod has_many orazbelongs_to przekazanie jednego parametru podczas ich wywołania informujeaplikację o (odpowiedzi zapisane w nawiasie dotyczą wywołania pierwszej zmetod):

• nazwie klasy, na którą mają być mapowane powiązane w bazie obiekty,(Category )

• nazwie tabeli, z której mają być pobrane odpowiednie rekordy, (categories )

• kolumnie będącej kluczem głównym tabeli (kolumna id w tabeli users ),

• kolumnie będącej kluczem obcym (kolumna user_id w tabeli categories ),

• nazwie metody służącej do pobierania takich powiązanych obiektów (metodacategories w klasie User ).

Te dane mogą być jednak zmienione poprzez przekazanie parametrów takich jak:class_name , foreign_key , primary_key .

CoC pozwala w prosty sposób wyrazić złożone fakty. Dobra realizacja tejzasady nie odbiera programiście możliwości dokładnej konfiguracji ani niezmniejsza elastyczności używanych narzędzi czy funkcji, lecz jedynie minimalizujenarzut pracy niezbędny na zdefiniowanie zachowań w sytuacjach typowych. Wnietypowych zaś, kiedy konwencja jest złamana, programista może po prostunadpisać parametry domyślne, które w przeciwnym wypadku pozostawiłbyniezmienione. Rails spopularyzował to podejście, które możemy już spotkać wwielu innych frameworkach.

2.2. TechnikiO ile konwencje stosowane w RoR są raczej ogólnie przyjęte i niemal przezwszystkich stosowane oraz uznawane, o tyle jeśli chodzi o zalecane techniki

Page 11: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Koncepcje i technikiprogramistyczne

11

programowania ich realizacja nie zawsze przebiega bezproblemowo oraz płynnie.Techniki te nie są nierozerwalnie związane tylko z tą jedną platformą czy językiem,ani też w żaden sposób narzucane programistom Rails, jednak doświadczenideweloperzy często się nimi posługują oraz opisują w swoich pracach zyskijakie przynoszą. Wybór ten podyktowany został zatem ich udokumentowanąobecnością w świecie RoR oraz istniejącymi i szeroko opisywanymi narzędziamiwspierającymi ich realizację.

Test Driven Development

Test Driven Development jest techniką tworzenia oprogramowania, w myśl którejnajpierw tworzy się testy opisujące sposób działania nowej funkcjonalności orazweryfikujące poprawność implementacji a dopiero następnie tworzy się kodprogramu niezbędny by testy przeszły pomyślnie, by na końcu przejść do procesurefaktoryzacji systemu.

Kiedy przychodzi nam do zaimplementowania nowej funkcjonalności, powinniśmysię najpierw zastanowić, czy aktualna architektura aplikacji jest najlepsząumożliwiającą nam uczynienie tego. Jeśli tak, możemy zacząć naszą iterację, jeślinie, musimy najpierw zmienić tą część architektury, która ma związek z naszymzadaniem, by osiągnąć stan, w którym jego realizacja będzie najłatwiejszaz możliwych. Dzięki posiadaniu już testów, które obejmują dotychczasowefunkcjonalności, nie musimy się bać wprowadzanych zmian i możemy ichdokonywać małymi krokami, sprawdzając jednocześnie jaki jest ich wpływ nawynik testów. Można zatem w łatwy sposób dostrzec skutki wprowadzanychusprawnień oraz inne miejsca w kodzie wymagające poprawek, by aplikacja wciążdziałała prawidłowo. Takie podejście sprawia, że architektura systemu podlegaciągłemu doskonaleniu, czyniąc naszą pracę łatwiejszą w przyszłości.

Podejście TDD rządzi się dwiema żelaznymi zasadami [4]:

• napisz nowy kod tylko wtedy, kiedy nie przechodzi jeden z testów,

• unikaj powtórzeń.

Te dwie proste reguły mają swoje implikacje, zarówno w sposobie pracy grupyprogramistów, jak i w budowie naszej aplikacji [5]:

• należy pisać własne testy, ponieważ nie możemy czekać kilkanaście razyw ciągu dnia, aż napisze je ktoś inny,

• nasze środowisko musi nam dostarczać natychmiastowych informacjint. skutków naszych nawet najmniejszych zmian,

• aplikacja powinna składać się z wysoce spójnych i luźno powiązanychkomponentów, by ich testowanie było szybsze oraz łatwiejsze.

Ponadto wyznaczają one porządek wg. którego pracujemy [4]:

1. Dodaj test.

W TDD implementację każdej nowej funkcjonalności zaczyna się od napisaniatestów. Oczywiście z początku ich sprawdzenie zakończy się porażką. Żebyjednak napisać test programista musi poznać zarówno specyfikację jaki wymagania funkcjonalności. Sprawdzenie, czy z początku test się nie powodzi

Page 12: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Koncepcje i technikiprogramistyczne

12

jest niezbędne i stanowi warunek konieczny poprawności testu. Gwarantujenam, że nie będzie on zdawany zawsze, a tym samym stanie się bezużyteczny.W tradycyjnym modelu testy powstają zwykle po napisaniu kodu. Podejścieodwrotne sprawia, że programista skupia się na wymaganiach jeszcze zanimnapisze kod, co wymaga od niego wyobrażenia sobie sposobu, w jaki będziechciał go używać w przyszłości.

2. Napisz kod.

Następny krok stanowi napisanie kodu, który przechodzi test. Jego jakość możejednak wciąż pozostawiać wiele do życzenia. Jest to akceptowalne, gdyż ostatnietap ma celu jego poprawę. Ważnym jest, by pisać tylko kod ukierunkowanyna zdanie napisanego wcześniej testu. Zasadniczo, nie powinno się w tymmomencie implementować funkcjonalności na przyszłość, a szczególnie takiej,której nie pokrywa żaden z testów.

3. Uruchom testy i zobacz jak są poprawnie zdane.

Jeśli wszystkie testy są zdane, programista może być pewny, że jego kod spełniastawiane przed nim wymagania (zakładając, że napisał on rozsądne testy). Takasytuacja stanowi dobry punkt wyjścia do następnej, końcowej fazy.

4. Zrefaktoryzuj kod.

Jeśli istnieje taka potrzeba kod powinien zostać poddany refaktoryzacji,szczególnie takiej, która usuwa powtórzenia. W tym etapie procesu programistamoże obserwować czy wprowadzone przez niego zmiany nie zaburzą działaniasystemu i są przeprowadzane właściwie. Nieustanna obecność testów podnosipoziom zaufania do tworzonego przez siebie kodu, a także ułatwia rozeznaniesię, gdzie ewentualnie został popełniony błąd.

Jest to szczególny moment, którego nie należy pomijać, gdyż prowadzi ondo uniknięcia duplikacji (zgodnie z zasadą DRY) oraz łatwiejszego utrzymaniasystemu w przyszłości, ze względu na lepszą architekturę.

Korzyści [6] [7] [8] [9]:

• Możliwe jest też zmniejszenie liczby błędów pojawiających się w aplikacji,do tego stopnia, że gotowe do wdrożenia oprogramowanie zawierające nowefunkcjonalności, dostępne jest każdego dnia, co może prowadzić do nowegotypu relacji z klientami oraz partnerami biznesowymi.

• Programiści, używający tego podejścia w zupełnie nowych projektach, dużorzadziej odczuwają konieczność korzystania z debuggera. W połączeniuz używaniem systemu kontroli wersji może się okazać, że przywrócenieostatniej wersji przechodzącej testy jest bardziej produktywne niż korzystaniez debuggera w celu znalezienia błędu.

• Pomimo faktu, że pisanie testów wymaga większej ilości kodu, całościowy kosztimplementacji jest zwykle niższy.

• Duża liczba testów przyczynia się do zmniejsza liczby błędów oraz ichwychwytywania we wczesnych fazach pracy, zapobiegając tym samympowstawaniu droższych problemów w późniejszym czasie, których poprawieniewymagałoby debugowania aplikacji.

Page 13: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Koncepcje i technikiprogramistyczne

13

• TDD może prowadzić do bardziej zmodularyzowanego, elastycznegoi  rozszerzalnego oprogramowania. Dzieje się tak, ponieważ wymaga sięod dewelopera myślenia o powstającej aplikacji przez pryzmat małychjednostek, które mogą być napisane i przetestowane niezależnie, a następniezintegrowane ze sobą.

Wskazuje się na kilka faz oswajania z tą techniką [10]:

1. Programista zaczyna pisać testy jednostkowe dla swojego kodu, korzystającz jednego z wielu istniejących na rynku narzędzi wspomagających ten proces(zwykle framework z rodziny xUnit).

2. W miarę jak liczba testów wzrasta, programista zaczyna cieszyć sięwzrastającym poczuciem zaufania do wykonanej pracy.

3. Pojawia się odczucie, że pisanie testów przed napisaniem kodu, pomagaskoncentrować się na napisaniu tylko tego, co jest niezbędne.

4. Programista zauważa, że gdy powraca do kodu, którego nie widział przezpewien czas, testy służą mu jako dokumentacja jego działania.

5. Punkt zwrotny ma miejsce, gdy zauważa się, że pisanie testów w tensposób, pomaga odkryć API do własnego kodu, czyli wyobrazić sobie jakchcielibyśmy z niego korzystać. Od tego momentu TDD staje się częściąprocesu projektowania.

6. Uświadomienie sobie, że TDD w większej mierze dotyczy definiowaniapożądanego zachowania (klasy, modułu, aplikacji), niż samego testowania.

7. Zachowanie to inaczej określenie sposobu interakcji komponentów systemu.Podstawę zaawansowanego TDD stanowi zatem korzystanie z obiektówimitujących pewne zachowania (mocking) w celu skrócenia i przyspieszeniatestów oraz zmniejszenia zależności między nimi.

Behaviour Driven Development

Skupienie się na zachowaniu systemu i jego poszczególnych elementów jestcechą charakterystyczną naprawdę doświadczonych praktyków TDD. Jak sięjednak okazuje osiągnięcie punktu piątego z wymienionej w poprzednim rozdzialelisty faz okazuje się dla wielu programistów niezmiernie trudne [10]. Jednymz powodów dla którego ma się tak dziać jest nazewnictwo stosowane w TDD,które niemal zawsze zawiera w sobie słowo test: przypadki testowe, zestawytestów, metody testujące, testy akceptacyjne itd., co zdaje się sprawiać wrażenie,że docelowym przeznaczeniem całej techniki jest właśnie testowanie aplikacji.Tymczasem celem procesu jaki narzuca TDD jest tak naprawdę wyspecyfikowanieco powinien robić tworzony kod nim zacznie się go pisać oraz dokumentowaniedziałania aplikacji poprzez testy.

Początki Behaviour Driven Development są związane z powstaniem dwóchframeworków (RSpec dla języka Ruby oraz JBehave dla Javy) opartych napodobnych założeniach jak te z rodziny xUnit, jednak używających zupełnie innejnomenklatury mającej zwrócić naszą uwagę, czy aby na pewno dana klasa, moduł,bądź cała aplikacja powinna mieć taką funkcjonalność lub odpowiedzialność [11].Słowa test oraz assert nie mają ponoć takiej mocy jak używane w BDD słowo

Page 14: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Koncepcje i technikiprogramistyczne

14

should i nie pobudzają programisty do poddawania w wątpliwość, tego w jakisposób zaprojektował system oraz rozdzielił powinności.

Poniżej możemy zobaczyć przykładowy kod opisujący klasę Bowling korzystającz frameworka RSpec. Specyfikuje on, że w przypadku bardzo kiepskiej gryzawodnika, gdy dwudziestokrotnie nie udało mu się strącić żadnego kręgla, jegokońcowy wynik powinien wynosić 0.

describe Bowling do it "should score 0 for gutter game" do @bowling = Bowling.new 20.times { @bowling.hit(0) } @bowling.score.should == 0 endend

Ten sposób definiowania wymagań dużo lepiej nadaje się jednak do opisywaniaklas czy poszczególnych modułów systemu i sprawdza się jako zamiennikklasycznych frameworków testujących, niż jako sposób określenia zachowańcałego tworzonego systemu z punktu widzenia użytkownika końcowego. Jest onzrozumiały dla programisty, ale nie jest to dokument, który można przedstawićklientowi.

BDD kładzie duży nacisk na ścisłą współpracę z udziałowcami projektu, którajest możliwa dzięki temu, że deweloperzy w procesie komunikacji posługują sięmieszanką języka naturalnego oraz języka specyficznego dla dziedziny w jakiejrozwijany jest projekt. W szczególności ma to miejsce w scenariuszach czylidokumentach, gdzie wyraża się oczekiwane działanie systemu w konkretnychsytuacjach przykładowych.

Poszczególnym krokom takiego scenariusza odpowiadają zaimplementowanew języku programowania akcje wykonywane z użyciem scenariuszowegoframeworka testującego, narzędzia typowego dla BDD. Takim narzędziem dlaRails jest Cucumber. Zapobiega to regresjom w trakcie tworzenia aplikacji orazautomatyzuje proces weryfikacji zgodności działania systemu z oczekiwaniamiwzględem niego (scenariusze jako testy akceptacyjne).

Idea ścisłego przenikania języka dziedzinowego do języka aplikacji zostałazaczerpnięta z podejścia do projektowania aplikacji znanego pod nazwąDomain-driven design.

Przykładowy scenariusz, który może zostać przedstawiony interesariuszowi,w celu weryfikacji poprawnego zrozumienia wymagań, a następnie pozaimplementowaniu automatycznie wykonywany przez framework testujący:

Story: Logging in and out Scenario: Logged in user can log out. Given an activated user logged in as 'reggie' When she goes to /logout Then she should be redirected to the home page When she follows that redirect! Then she should see a notice message 'You have been logged out' And she should not be logged in And her session store should not have user_id

Page 15: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

15

Rozdział 3Podstawy języka Ruby

Pomimo prostej składni, język Ruby jest dość skomplikowany i opisaniewszystkich jego elementów jest materiałem na grubą książkę. W rozdziale tymzostaną opisane podstawy języka, które pozwolą lepiej zrozumieć przykładyzawarte w pracy, oraz elementy, które wpłynęły na tak dużą popularność tegoskryptowego języka.

3.1. Elementy składniJęzyk Ruby czerpie pełnymi garściami z innych języków skryptowych, takich jakPerl czy Python, a to co go wyróżnia to łatwiejsza, bardziej przypominająca języknaturalny składnia.

Budowa kodu źródłowego

Programy w języku Ruby zapisywane są w plikach tekstowych (standardowozakończonych rozszerzeniem .rb ), do edycji których wystarczy zwykły edytor.Ruby w przeciwieństwie do np., Javy nie posiada ograniczeń dotyczących nazwyi położenia plików w hierarchii katalogów, aczkolwiek przyjęło się, że jeśli w plikuznajduje się definicja klasy np. MyClass to plik będzie nazwany w konwencjipodkreślnikowej: my_class [3].

Język Ruby jest całkowicie obiektowy, w tym sensie, że zawsze operujemy w nimna obiektach i na metodach obiektów. Jednakże interpreter języka jest dośćelastyczny i nie narzuca nam pisania kodu w sposób obiektowy, dlatego program,wypisujący prosty tekst, może przyjąć taką postać:

puts 'hello world'

Już tak prosty przykład pozwala nam poznać jedno z założeń języka - prostotę:

• Nie została zadeklarowana klasa, ani metoda w ramach której wykonywanyjest powyższy kod, jednakże interpreter języka dodał te elementy dynamicznieprzed uruchomieniem samego kodu.

• W powyższym przykładzie puts jest wywołaniem metody z modułu Kernel ,który jest automatycznie ładowany do każdego obiektu stworzonego w Ruby –dzięki temu można wykonywać zadeklarowane w nim metody.

• Przekazanie parametru do metody może odbywać się bez używania nawiasów.Wiele innych języków wymaga wpisania parametrów metod w nawiasy, w Rubysą one opcjonalne.

• Brak separatorów instrukcji: standardowo znak nowej linii traktowany jest jakoseparator instrukcji, ale można wpisać kilka instrukcji w jednej linii przy pomocyznaku średnika.

Struktury kontrolne

Ruby posiada standardowe struktury kontrolne znane z innych języków, takichjak C, czy Java. Podstawową różnicą między tymi strukturami, jest brak

Page 16: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Podstawy języka Ruby

16

nawiasów klamrowych oznaczających bloki kodu, zamiast których używa się słowakluczowego end jako oznaczenie końca bloku instrukcji.

If x > 0 puts x*2elsif x < 0 puts x*4else puts xend

Ciekawostką jest także to, że Ruby posiada specjalne słowo kluczowe pozwalającesprawdzić czy warunek jest fałszywy:

unless record.save flash[:notice] = 'Cannot save'end

Dodatkowo instrukcje warunkowe mogą wystąpić po instrukcji której dotyczą:

errors.add_to_base(:wrong_order) if wrong_currencies_order?

Ciekawą właściwością języka jest też to, że każda struktura kontrolna zwracawartość (będąca wartością ostatniej wykonanej instrukcji), dzięki czemu możnajej użyć w wyrażeniu przypisania, np.:

begin_of_period = case symbol when :THIS_DAY then Date.today when :THIS_WEEK then Date.today.beginning_of_week when :THIS_MONTH then Date.today.beginning_of_month when :THIS_QUARTER then Date.today.beginning_of_quarter when :THIS_YEAR then Date.today.beginning_of_yearend

W zmiennej begin_of_period znajdzie się obliczona data w zależności odparametru symbol .

3.2. Programowanie obiektoweObiektowość Rubiego jest jedną z jego najczęściej wymienianych zalet, dlategopoza zasadami budowy składni języka warto też poznać możliwości definiowaniaobiektów, sposoby ich przechowywania oraz stosowania metaprogramowania.

Klasy, metody, obiekty i zmienne

Definiowanie zmiennych w Rubym polega na podaniu nazwy zmienneji przypisaniu do niej wartości. Zmienne nie mają typu[3], dlatego niepotrzebnajest dodatkowa deklaracja znana z innych języków:

x = 5

Typ mają natomiast obiekty na które zmienne wskazują, x w powyższymprzykładzie ma typ Fixnum – jest to standardowa klasa do przechowywania liczbcałkowitych.

Page 17: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Podstawy języka Ruby

17

Programista może w Rubym definiować własne typy, służy do tego słowo kluczoweclass . Klasy mogą posiadać metody – je definiuje się przy pomocy słowa def .

Uproszczony przykład klasy Money ze zrealizowanego projektu:

class Money def initialize(*args) @hash = {} end

def is_empty? return @hash.keys.size == 0 end

end

Zmienne zaczynające swoją nazwę od znaku @ są traktowane przez Ruby jakozmienne instancji obiektu, natomiast zmienne z dwoma znakami @ są traktowanejako zmienne klasy (statyczne)[3].

Kontenery, iteratory

Ruby posiada dwie podstawowe klasy służące jako kontenery obiektów: Arrayi Hash.

Posiadają one podobną funkcjonalność, ale różnią się sposobem indeksowaniaelementów i literałami do tworzenia kolekcji:

• obiekty w Array przypisane są do liczb całkowitych, a samą tablicę tworzy sięprzy pomocy kwadratowych nawiasów:

days = ['Mon', 'Tue', 'Wed']puts days[0] # kod zwróci 'Mon'

• obiekty w Hash mogą być indeksowane przez dowolne inne obiekty, a kolekcjętworzy się poprzez nawiasy klamrowe

values = {'category' => 'Expenses','value' => 5.3, 'currency' => 'PLN'}puts values['value'] # kod zwróci 5.3

Kolekcje są dobrym przykładem do zaprezentowania iteratorów – jednejz największych zalet tego języka, niezwykle wpływających na produktywność.

Iteratory są metodami zaimplementowanymi w kolekcjach, które pozwalają naprzejście przez wszystkie elementy danej kolekcji i wykonanie na nich operacji.Podstawowym iteratorem jest each :

days.each |day| do puts dayend

Powyższy kod będzie iterował po kolekcji dni, przypisywał aktualny element dozmiennej day i wypisywał na ekranie za pomocą metody puts . Należy zauważyć,że kod między do a end tworzy blok, który zostaje przekazany do metody each .

Page 18: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Podstawy języka Ruby

18

Ciekawym iteratorem jest też partition , który pozwala podzielić kolekcję na dwieczęści, w zależności od tego czy blok kodu wykonany na danym elemencie zwróciłprawdę czy fałsz. Poniższy kod dzieli tablicę liczb na liczby parzyste i nieparzyste:

even, odd = [1,2,5,66,78,99,142].partition do |number| number % 2 == 0end

W standardowych kolekcjach zdefiniowanych jest dużo więcej iteratorów różnegoprzeznaczenia[3]. Można też tworzyć własne iteratory i metody które przyjmująblok kodu, podobnie jak each .

Metaprogramowanie

Istotną cechą Rubiego, jest możliwość dynamicznego definiowania metod i klasw czasie działania programu, w tym zmiana lub rozszerzenie działania klaswbudowanych w język takich jak Array czy String , bez używania dziedziczenia.Ruby pozwala nawet przechwycić próbę wywołania nieistniejącej metodyi wygenerować ją w razie potrzeby.

class Date def next_quarter self.at_end_of_quarter.next endend

Powyższy kod nie definiuje klasy Date , jak mogłoby się wydawać, ale przedefiniujestandardowo istniejącą klasę dodając do niej nową metodę obliczającą począteknastępnego kwartału.

Metaprogramowanie używane jest szczególnie w aplikacjach opartych na Rails,np. przy definiowaniu relacji między klasami:

class User < ActiveRecord::Base has_many :transfers has_many :currencies, :dependent => :destroy has_many :goalsend

Powyższy fragment implementacji klasy User , zawiera definicje relacjiużytkownika z innymi obiektami w modelu. Metoda has_many przyjmuje jakoparametr nazwę relacji a sama dynamicznie tworzy metody dostępowe dotych kolekcji obiektów, takie jak: User.transfers , User.transfers.find ,User.transfers.empty? czy User.transfers.create .

Page 19: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

19

Rozdział 4Wielowarstwowa

architektura systemuW rozdziale tym opisane zostaną ogólne aspekty architektury Ruby on Railsa  także informacje o sposobie współpracy poszczególnych komponentów,działaniu aplikacji, o tym jak rozłożone są pliki w standardowym projekcie, orazo tym jakich narzędzi i skryptów można używać podczas pracy nad projektem.

4.1. Czym jest Ruby on RailsRuby on Rails jest frameworkiem do szybkiego tworzenia aplikacji internetowych.Innymi słowy Rails jest strukturą wspomagającą tworzenie, rozwój i testowaniepowstającej aplikacji, jest szkieletem zapewniającym podstawowe mechanizmy,który może być wypełniany właściwą treścią programu [2].

Framework ten zyskał sobie wielu zwolenników dzięki zastosowaniu czytelnejstruktury kodu oraz ogromnym możliwościom połączonym z łatwością ichużycia. Ponadto tworzenie aplikacji w Ruby On Rails nie wymaga używaniawyspecjalizowanych edytorów IDE (Integrated Development Environment), gdyżwszystkie czynności można wykonać w konsoli systemowej a pliki edytowaćw  najprostszym edytorze tekstu. W istocie powstały takie zintegrowaneśrodowiska deweloperskie dedykowane pracy w Ruby on Rails, jednak ichdziałanie polega głównie na kolorowaniu składni oraz wykonywaniu skryptówi generatorów dostępnych w samym środowisku, dlatego też wszystkie przykładyumieszczone w tej pracy ograniczają się wyłącznie do interakcji ze zwykłą konsolątekstową.

Pracę z Ruby on Rails zaczyna się od wygenerowania nowego projektu przypomocy polecenia rails , podając jako parametr nazwę tworzonej aplikacji [2]:

rails manage_my_money create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments create config/initializers create config/locales (...)

Instrukcja ta tworzy szereg plików i katalogów, z których każdy ma określonezastosowanie:

• app – Znajduje się tu główny kod aplikacji.

• app/controllers – Katalog przeznaczony jest na kod warstwy kontrolera:klasy poszczególnych kontrolerów, oraz klasa głównego kontrolera(ApplicationController ) z którego dziedziczą pozostałe.

Page 20: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wielowarstwowaarchitektura systemu

20

• app/models – Znajdują się tutaj klasy składające się na warstwę modelu– w większości klasy uczestniczące w mapowaniu obiektowo relacyjnym.

• app/views – Miejsce składowania szablonów dla warstwy widoku.

• app/views/layouts – W tym katalogu znajdują się specjalne szablony,zawierające elementy dostępne na wielu ekranach aplikacji, takie jak np. stopkaczy menu.

• app/helpers – Umieszczone tutaj zostają tzw. helpery, czyli moduływspomagające działanie szablonów widoku. Są one przydatne jeśli np.dane otrzymane od warstwy kontrolera trzeba wstępnie przetworzyć przedwyświetleniem. Poniższy helper definiuje metodę, która zamienia nazwękodową pewnej opcji w systemie na jej nazwę zrozumiałą dla użytkownika:

module GoalsHelper def description_for_completion_condition(code) case code when :at_least then 'Co najmniej' when :at_most then 'Co najwyzej' end endend

Zdefiniowana w ten sposób metoda description_for_completion_conditionmoże być następnie użyta w implementacji warstwy widoku.

• config – Katalog przechowujący pliki konfiguracyjne, najważniejsze z nich todatabase.yml definiujący połączenia z bazą danych, routes.rb zawierającyścieżki routingu oraz enviroment.rb – zawierający różnorodne ustawienia dlacałego frameworku.

• db – Zawiera skrypt schema.rb definiujący aktualny schemat bazy danych naktórym działa aplikacja, oraz pliki migracji generujące go w sposób przyrostowy.

• doc – Przeznaczony na dokumentajcę projektu, która zwykle jest generowanana podstawie komentarzy w kodzie.

• lib – W tym miejscu umieszcza się dodatkowe moduły i biblioteki, którenie należą do modelu, widoku ani kontrolera. Biblioteki takie ładowane sąautomatycznie na starcie aplikacji.

• public – Zawiera tzw. pliki statyczne aplikacji. Są to elementy, które niewymagają przetwarzania po stronie serwera aplikacji, takie jak obrazki czykaskadowe arkusze stylów oraz pliki JavaScript.

• script – Zawiera skrypty pomocnicze, np. generatory kodu czy skryptyumożliwiające uruchomienie serwera.

• test – W tym katalogu znajdują się pliki testów automatycznych.

• vendor/plugins – Zawiera zewnętrzne moduły niezbędne do działania aplikacjiczyli tzw. pluginy.

• vendor/rails – W tym folderze można umieścić wszystkie biblioteki składającesię na framework, przydatne jeśli w systemie zainstalowana jest inna wersja niżwymagana przez projekt.

Page 21: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wielowarstwowaarchitektura systemu

21

4.2. Przypływ sterowania i model MVCIstotną cechą frameworku Ruby on Rails jest oparcie go na solidnejpodstawie jaką jest wzorzec projektowy MVC (Model View Conroller). Wzorzecprojektowy to inaczej zbiór zasad poprawnego tworzenia i projektowaniaoprogramowania. Rozumienie i stosowanie wzorców pozwala nie tylko tworzyćlepsze oprogramowanie, ale też przyśpiesza komunikację między programistami,którzy nie muszą tłumaczyć sobie nawzajem zawiłych czasem pomysłówarchitektonicznych – porozumiewają się używając ich nazw.

Istnieje wiele takich wzorców projektowych i wiele z nich zostało użytych w Rails,jednakże MVC jest nadrzędny wobec każdego innego – ustala wysokopoziomowąstrukturę projektu, każdy element systemu podlega MVC.

Model-Widok-Kontroler został zaproponowany w 1979 roku przez TrygveReenskaug[1] – wymyślił on nowe podejście do budowania aplikacji z graficznyminterfejsem użytkownika, polegające na wyodrębnieniu z projektu częściodpowiedzialnych za:

• dostęp i operacje na danych (model),

• prezentację interfejsu (widok),

• odbieranie poleceń użytkownika i zarządzanie przetwarzaniem (kontroler).

Model ten był przez lata z powodzeniem używany przy projektowaniu aplikacjidesktopowych i z tych właśnie doświadczeń czerpali też twórcy frameworkuRuby on Rails. Zasadę działania MVC i jednocześnie Rails, najłatwiej zobrazowaćna ogólnym schemacie prezentującym pojedyncze zapytanie HTTP do serweraaplikacji: [1]

Rysunek 4.1. Wykres przepływu w modelu MVC.

1. Na początku użytkownik, poprzez przeglądarkę internetową, wysyła do serweraaplikacji formularz HTML, który wraz z innymi parametrami jest przesyłanyprotokołem HTTP do serwera aplikacji. Następnie serwer dobiera odpowiednikontroler i przekazuje do niego żądanie.

Page 22: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wielowarstwowaarchitektura systemu

22

2. Kontroler kontaktuje się z modelem, celem odczytania lub zapisania rekordóww  bazie danych, oprócz tego model może wykonać specyficzną logikębiznesową związaną z danymi lub przeprowadzić ich walidację.

3. Po wykonaniu operacji na modelu, kontroler przekazuje sterowanie doodpowiedniego szablonu widoku dostarczając do niego dane otrzymanez warstwy modelu.

4. Warstwa widoku generuje z szablonu i dostarczonych danych kod HTML, któryjest następnie wysyłany do przeglądarki. Przeglądarka wyświetla stronę WWWi czeka na dalsze polecenia użytkownika.

Tak, w ogólnej perspektywie przedstawia się działanie serwera w przypadkujednego żądania. Cykl życia aplikacji polega natomiast na nieustannymodpowiadaniu na takie żądania od wielu użytkowników.

4.3. NarzędziaRuby on Rails to nie tylko zbiór komponentów i bibliotek współpracujących ze sobąpodczas obsługi żądania. Na framework składa się też szereg narzędzi i skryptów,które w różnym stopniu wspomagają pracę programisty, należą do nich m.in. [2]:

• ./script/server – Jeden z ważniejszych skryptów, powoduje uruchomienieaplikacji na domyślnym serwerze aplikacji, którym w tym przypadku jestWEBrick:

./script/server=> Booting WEBrick=> Rails 2.3.2 application starting on http://0.0.0.0:3000=> Call with -d to detach=> Ctrl-C to shutdown server[2009-05-22 23:14:41] INFO WEBrick 1.3.1[2009-05-22 23:14:41] INFO ruby 1.8.7 (2008-08-11) [i486-linux][2009-05-22 23:14:46] INFO WEBrick::HTTPServer#start: port=3000

Powyższe wywołanie umożliwia interakcję z uruchomioną aplikacją na lokalnymadresie i porcie 3000.

• ./script/console – Skrypt otwiera tzw. konsolę deweloperską – narzędzieumożliwiające interaktywną pracę z frameworkiem, bez faktycznegouruchamiania serwera aplikacji. Daje dostęp przede wszystkim do całegomodelu i umożliwia np. znalezienie pierwszego użytkownika z bazy danychi szybką zmianę jego hasła, wynik podawany jest natychmiast po zatwierdzeniupolecenia:

./script/console Loading development environment (Rails 2.3.2)>> u = User.find :first=> #<User id: 2, login: "robert", name: "", email: "[email protected]", created_at: "2009-03-27 22:52:14", updated_at: "2009-03-31 11:59:19">>> u.password = "123456"=> "123456">> u.save!

Page 23: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wielowarstwowaarchitektura systemu

23

• ./script/generate – Skrypt pozwalający generować elementy różnegoprzeznaczenia: modele, kontrolery, migracje bazy danych. Zajmuje się onstworzeniem odpowiednich plików i wypełnieniem ich wymaganą treścią,pozwalając programiście skupić się na najważniejszym - implementowaniulogiki biznesowej. Ponadto można rozbudowywać funkcjonalnośc tegonarzędzia tworząc własne szablony dla generatora lub instalując je w postacipluginów.

• rake – Jest to program wspomagający budowanie aplikacji, wzorowanyna znanym narzędziu make. Umożliwia proste uruchamianie wcześniejzdefiniowanych zadań. Rails dostarcza spory zestaw takich zadań,pozwalających m.in. na utworzenie bazy danych, zainstalowanie komponentuzewnętrznego czy uruchomienie testów aplikacji. Istnieje możliwośćdefiniowania nowych, dodatkowych zadań dla rake .

4.4. KomponentyRails nie jest monolityczną strukturą, w rzeczywistości poza rdzeniemframeworku, tworzą go następujące moduły [2]:

• Active Support – zestaw bibliotek i dodatków do języka Ruby, ułatwiającyoperacje na m.in. ciągach znaków, datach i liczbach,

• Active Record – biblioteka do obsługi relacyjnej bazy danych,

• Action Pack – obsługa warstwy kontrolera, widoku i routingu,

• Action Mailer – umożliwia wysyłanie i odbieranie poczty elektronicznej,

• Active Resource – biblioteka do komunikacji z zewnętrznymi systemami,opartymi na interfejsie REST (Representational state transfer).

Poza standardowymi modułami, programista ma możliwość rozszerzaniafunkcjonalności frameworku o zewnętrzne komponenty, czyli tzw pluginy. Łatwośćtworzenia i używania pluginów sprawiła, że powstały ich setki, dzięki czemu Railsmoże spełniać najróżniejsze wymagania projektowe.

Przykładowo, w projekcie Manage My Money, używanych jest 10 zewnętrznychkomponentów, co przystępnie obrazuje poniższy diagram:

Page 24: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wielowarstwowaarchitektura systemu

24

Rysunek 4.2. Używane w projekcie rozszerzenia

Pokrótce funkcjonalność tych rozszerzeń przedstawia się następująco:

• SSL Requirement - Wymusza pracę aplikacji w bezpiecznym trybie SSL (SecureSocket Layer),

• Exception Notification - Umożliwia automatyczne przesyłania wiadomości emaildo zdefiniowanych odbiorców z treścią wyjątku jaki spowodował użytkownikw czasie pracy z aplikacją,

• Open Flash Chart - Umożliwia generowanie wykresów w technologii Flash,

• Autocomplete - Służy do implementacji funkcjonalności dopełnianiawprowadzanych danych,

• Validates Equality - Napisany w ramach projektu plugin, który umożliwia łatwesprawdzanie, czy pewne atrybuty obiektu są takie same dla niego jak i dlakolekcji innych elementów, które zawiera,

• FasterCVS - Parser plików CSV (Comma Separated Values),

• Thinking Sphinx – Dostarcza API do komunikacji z silnikiem wyszukiwaniapełnotekstowego Sphinx. Umożliwia jego konfigurację na poziomie klas modeluzamiast w zewnętrznych plikach. Wspiera tym samym realizację koncpecji DRYoraz pozwala używać tego samego języka programowania do wyrażenia różnychfaktów.

• Nokogiri - Biblioteka do parsowania XML/HTML,

• Restful authentication - umożliwia generowanie klas odpowiedzialnych zazarządzanie użytkownikami,

• Awesome nested set - Umożliwia proste dokonywanie operacji na elementachskładających się na struktury drzewiaste. Dostarcza bardziej optymalnychzapytań do wyszukiwania potomków oraz przodków w drzewach niżstandardowe rozwiązania.

Page 25: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

25

Rozdział 5Budowa aplikacji

Ruby on Rails jest frameworkiem warstwowym, a jego główna ideaarchitektoniczna oparta jest na wzorcu MVC. Znakomita większość kodu projektutrafia więc do jednej z trzech warstw: modelu, obsługującego dostęp do bazydanych; widoku, generującego interfejs użytkownika oraz kontrolera, którywszystko nadzoruje.

Rozdział ten opisuje budowę i działanie warstw frameworku posiłkując się przytym przykładami kodu z projektu Manage My Money.

5.1. KontrolerWarstwa kontrolera jest odpowiedzialna za odbieranie żądań od użytkownika,delegowanie zadań związanych z logiką biznesową do warstwy modelui  dobieranie odpowiedniego widoku celem wysłania w odpowiedzi na daneżądanie.

Tworzenie kontrolera

Każdy kontroler w Rails jest klasą, która dziedziczy z klasy kontrolera aplikacjii jest umieszczona w pliku w folderze /app/controllers . Plik ten można utworzyćsamodzielnie, jednak znacznie łatwiej jest skorzystać z dostępnych skryptów dogenerowania kodu.

app> script/generate controller users

exists app/controllers/ exists app/helpers/ create app/views/usersexists test/functional/ exists test/unit/helpers/ create app/controllers/users_controller.rbcreate test/functional/users_controller_test.rbcreate app/helpers/users_helper.rb create test/unit/helpers/users_helper_test.rb

Powyższe polecenie, oprócz klasy kontrolera, utworzy też m.in. folder, w którymumieszczone zostaną szablony widoku, a także klasę do jego testowania.

Utworzony w ten sposób kontroler przyjmie taką postać:

class UsersController < ApplicationControllerend

Zgodnie z konwencją Rails nazwa kontrolera została rozszerzona o sufiksController a sam kontroler dziedziczy z klasy ApplicationController . Pozatym nie on ma żadnej funkcjonalności, gdyż nie zostały w nim definiowane żadnemetody.

Page 26: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

26

Budowa kontrolera

Publiczne metody instancji kontrolera w Rails nazywane są akcjami. Akcje te sądostępne z zewnątrz, np. z poziomu formularza na stronie widoku.

Programista ma dużą dowolność w tworzeniu akcji kontrolera, jednak jeśli danykontroler jest odpowiedzialny za operacje CRUD (create, read, update, delete)na modelu (a jest tak w większości przypadków) to powinien zastosować zestawmetod zgodny z konwencją [1]:

• new, create - para metod służąca do tworzenia nowych krotek w bazie danych,metoda new jest odpowiedzialna za wysłanie do przeglądarki użytkownikaformularza pozwalającego na określenie atrybutów nowego obiektu, podczasgdy metoda create interpretuje te atrybuty i zapisuje nową krotkę w bazie,

• edit , update - para metod, o analogicznym działaniu jak new, create , alepozwalająca edytować istniejące obiekty,

• destroy – metoda mająca za zadanie usuwanie obiektów z bazy danych,

• index – wyświetlanie listy wszystkich elementów tabeli,

• show – wyświetlenie konkretnego elementu, na podstawie jego identyfikatora.

Niezależnie od tego, jaka konwencja zostanie przyjęta, do akcji zawszeprzekazywane są obiekty pozwalające uzależnić przetwarzanie od czynnikówzewnętrznych, należą do nich [2]:

• params - hash zawierający parametry żądania, np. w przypadku zapytania POSTwysłanego ze strony HTML, params będzie zawierał pary postaci: nazwa_pola=> wartość_pola_formularza,

• request - zawiera szczegółowe informacje o żądaniu, takie jak adres pod któryskierowano zapytanie, czy nazwa przeglądarki użytkownika,

• session - hash przechowujący między żądaniami dowolne zapisane w nimdane, tzw. sesja użytkownika.

Generowanie odpowiedzi

Akcja kontrolera, poza odbieraniem żądania, wywołaniem logiki biznesowej, mateż za zadanie dostarczenie odpowiedniego widoku do przeglądarki użytkownika.Standardowym zachowaniem, jeśli w akcji nie zostało zdefiniowane inaczej, jestodszukanie w odpowiednim folderze szablonu o takiej samej nazwie jak akcja.

Dla przykładu:

class CurrenciesController < ApplicationController def index @currencies = Currency.for_user(@current_user).find(:all, :order => 'name') endend

Page 27: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

27

Wywołaniu akcji index towarzyszy wygenerowanie kodu HTML na podstawieszablonu widoku zawartego w pliku app/views/currencies/index.html.erb .

Standardowe zachowanie można zmieniać na wiele sposobów, dla przykładuweźmy metodę create , z kontrolera zarządzającego walutami:

def create @currency = Currency.new(params[:currency]) @currency.user = @current_user if @currency.save flash[:notice] = 'Created new unit.' redirect_to :action => :index else render :action => 'new' endend

Akcja create jest wywoływana przy wysyłaniu formularza HTML podczastworzenia nowej waluty. Metoda ta stara się utworzyć nową walutę na podstawieotrzymanych parametrów formularza, oraz zapisać ją przy pomocy metody save .

Powodzenie w zapisywaniu skutkuje wywołaniem metody redirect_to , która tozwraca do klienta żądanie przekierowania na podany adres (w tym przypadkubędzie to adres odpowiadający akcji wyświetlającej wszystkie waluty).

Niepowodzenie natomiast, powoduje użycie metody render i wyświetlenieszablonu new używanego do prezentacji formularza. Takie zachowanie umożliwiaużytkownikowi poprawienie wprowadzonych danych. W tym przypadku nienastępuje przekierowanie.

Filtry

Filtry są mechanizmem działającym na poziomie kontrolera, pozwalającym nawykonywanie dodatkowych czynności w ramach danej akcji, lub grupy akcji.Używanie filtrów pozwala oczyścić plik z kodu, który normalnie należało bypowtarzać w prawie każdej akcji.

Dostępne są trzy rodzaje filtrów [1]:

• before_filter – umożliwia wykonanie dodatkowych metod, jeszcze przedwykonaniem samej akcji.

Umieszczenie następującej linijki w kodzie klasy kontrolera powodujewykonanie metody login_required przed każdą jego akcją.

before_filter :login_required

Taki zapis daje możliwość zablokowania dostępu do akcji dla niezalogowanychużytkowników, jeszcze przed wykonaniem samej metody kontrolera. Działaniefiltrów można ograniczać do zadanej grupy akcji:

before_filter :check_perm_edit, :only => [:edit, :destroy]

lub podawać wyjątki nie podlegające działaniu filtrów:

before_filter :login_required, :except => :login

Page 28: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

28

• after_filter – działa tak samo jak before_filter , z tą różnicą, że wywołujemetody po wykonaniu bieżącej akcji, lecz jeszcze przed wysłaniem jakiejkolwiekodpowiedzi do klienta, pozwala to np. na dodanie kompresji danych.

• around_filter – łączy w sobie cechy obu poprzednich filtrów – umożliwiazdefiniowanie w jednej metodzie kodu do wykonania przed i po akcji, np.dodanie logowania:

around_filter :logger

def logger logger.log "Staring processing something at #{Time.now}" yield logger.log "Finished processing something at #{Time.now}"end

Kluczowe jest tutaj użycie słowa yield , które powoduje wykonanie samej akcji.

Routing

W poprzednich podrozdziałach zostało zaznaczone, że publiczne metodykontrolera są dostępne jako akcje w aplikacji. Interfejsem pozwalającymna wywoływanie tych akcji jest odpowiednio zdefiniowany adres URL.Przypisywaniem adresów URL do akcji zajmuje się w Rails moduł routingu.

Ustawienia związane z routingiem znajdują się w pliku app/config/routes.rb ,jego standardowa zawartość:

ActionController::Routing::Routes.draw do |map| map.connect ':controller/:action/:id'end

powoduje, że adres postaci localhost/currencies/edit/1 potraktowanyzostanie jako wywołanie metody edit z kontrolera currencies i przekazanieparametru 'id' o wartości 1.

Konfigurację routingu można rozszerzać o własne reguły, np. zdefiniowaćspecjalną składnię adresu dla części administracyjnej serwisu

map.connect 'admin/:action/:id' :controler => :user, :admin_action => true

w ten sposób odwołania postaci admin/edit/2 będą przekazywane do kontroleraUser , z dodatkowym parametrem admin_action .

5.2. ModelNajważniejszą warstwą aplikacji Rails jest warstwa modelu. Odpowiada ona zaobsługę modelu biznesowego, czyli przechowywanie i walidację danych, ponadtocała logika biznesowa powinna być umieszczona w tej warstwie.

Active record

Active Record jest modułem zajmującym się mapowaniem obiektowo relacyjnym.Jego użycie sprawia, że korzystanie z bazy danych nie wiąże się z używaniem

Page 29: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

29

języka SQL – programista operuje na obiektach reprezentujących rekordy w baziea framework sam generuje odpowiednie zapytania. AR upraszcza sposób pracyz bazą danych – tabele z bazy stają się klasami, rekordy stają się obiektami,a kolumny – atrybutami tych obiektów [2].

Przykładowo, tabela currencies , zdefiniowana w SQL następująco:

CREATE TABLE currencies ( id integer NOT NULL, symbol character varying(255) NOT NULL, long_symbol character varying(255) NOT NULL, name character varying(255) NOT NULL, long_name character varying(255) NOT NULL, user_id integer);

zostaje zmapowana na klasę Currency .

class Currency < ActiveRecord::Baseend

Obiekty klasy zaimplementowanej w ten sposób, będą posiadały metody służącedo odczytywania i zapisywania wartości krotek w bazie, m.in. long_symbol() ,long_symbol=(a_long_symbol) , name() , name=(a_name) , czy też user_id()oraz user_id=(a_user_id) .

Konfiguracja połączenia

Używając AR należy upewnić się, że konfiguracja połączenia z  bazą danychumieszczona w pliku config/database.yml jest poprawna. Znajdują się tamosobne ustawienia dla trzech domyślnych środowisk: testowego, produkcyjnegoi developerskiego. Każde z nich wymaga podania następujących parametrów [1]:

• adapter : określa rodzaj używanej bazy, podanie tego parametru jest konieczne,gdyż producenci systemów bazodanowych stosują w swoich bazach różnedialekty języka SQL. Rails wspiera wiele takich dialektów (między innymiMySQL, PostrgreSQL, MSSQL, DB2, Oracle) i dzięki temu znosi z programistyobowiązek implementowania tych różnic samemu.

• database : nazwa bazy danych,

• host : adres bazy danych,

• username : nazwa użytkownika bazy danych,

• password : hasło dla podanego użytkownika.

Przykładowa konfiguracja środowiska testowego może wyglądać następująco:

test: adapter: postgresql database: money_test host: localhost username: postgres password: postgres

Page 30: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

30

Migracje

Migracje są mechanizmem pozwalającym zarządzać strukturą bazy danychpodobnie jak systemy kontroli wersji zarządzają kodem aplikacji. Dodatkowostanowią pewien poziom abstrakcji nad instrukcjami DDL (Data DefinitionLanguage) systemów baz danych. Dzięki temu, że kod migracji jest zapisanyw języku Ruby, ewentualna zmiana systemu bazodanowego nie stanowi dużegoproblemu.

Migracje umożliwiają [1]:

• tworzenie tabel, zdefiniowanie kolumn, ich typów i atrybutów:

create_table :goals do |t| t.string :description t.boolean :include_subcategories t.integer :period_type_int t.integer :goal_type_int (...)end

• usuwanie tabel:

drop_table :goals

• zmianę kolumn w już istniejących tabelach:

add_column :users, :transaction_amount, :integer, :null => trueremove_column :users, :include_transactions_from_subcategories

change_table(:goals) do |t| t.date :period_start t.date :period_endend

• zakładanie indeksów:

add_index :system_categories, :id, :unique => true

Migracje przechowywane są w plikach, z których każdy odpowiada za pewnązmianę struktury bazy danych. Ich wykonanie wiąże się z wprowadzeniemtych zmian oraz aktualizacją numeru wersji struktury bazy, co pozwalaprzy uruchamianiu następnych migracji wykonać tylko te, które nie zostałyzaaplikowane wcześniej. Dzięki swej specyficznej budowie migracje pozwalająrównież na wycofanie wcześniejszych zmian.

Relacje

Poza odwzorowywaniem pól tabeli na pola obiektów ActiveRecord potrafi równieżmapować relacje między tabelami. Tworzy się je umieszczając jedno z dostępnychmakr: has_one , has_many , belongs_to oraz has_and_belongs_to_manyw klasach których relacja dotyczy. AR obsługuje następujące typy relacji:

• Relacja jeden do wielu - ta relacja zakłada, że jedna z tabel ma kolumnę, którawskazuje na klucz główny drugiej tabeli,

Page 31: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

31

create_table :categories do |t| t.integer :user_id (...)end

create_table :users do |t|(...)end

Tak zdefiniowanym tabelom i kluczom odpowiadają następujące definicjew klasach:

class Category < ActiveRecord::Base belongs_to :userend

class User < ActiveRecord::Base has_many :usersend

Dzięki tym zapisom możliwe są m.in. następujące wywołania:

@user.categories – zwraca listę wszystkich obiektów użytkownika,

@category.user – zwraca użytkownika danej kategorii.

• Relacja wiele do wielu – w tym przypadku klucze obce nie są definiowanew samych tabelach których relacja dotyczy, ale w dodatkowej zawierającejtylko pary kluczy. Tabela reprezentująca relację Category i SystemCategory mapostać:

create_table :categories_system_categories, :id => false do |t| t.integer :category_id, :null => false t.integer :system_category_id, :null => falseend

Nie jest wymagane by tabela relacyjna categories_system_categories byłamapowana obiektowo, należy natomiast dodać zapisy

has_and_belongs_to_many :categories

oraz

has_and_belongs_to_many :system_categories

odpowiednio do klas SystemCategory i Category .

• Relacja jeden do jednego – jest to szczególny przypadek relacji jeden do wielu,gdyż klucze obce w tabelach zdefiniowane są identycznie, jedyna różnica leżyw zapisie has_many , który zmienia się na has_one .

Operacje na rekordach

AR realizuje wszystkie podstawowe operacje na rekordach, należą do nich [2]:

Page 32: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

32

• Tworzenie rekordów. Najłatwiej nowy rekord tworzy się przy pomocykonstruktora klasy zmapowanej na daną tabelę i przypisaniu wartościodpowiednim polom, a następnie wykonanie metody save na takzainicjalizowanym obiekcie. Dokładnie tak zaimplementowane to zostałow następującej metodzie tworzącej dane do testów:

def create_share_report(user) new_share_report = ShareReport.new new_share_report.user = user new_share_report.name = "Testowy raport" new_share_report.save! (...)end

Tym sposobem w bazie danych zostanie zapisany nowy wiersz w tabeli raportów.

Inną możliwością utworzenia rekordu jest przekazanie do konstruktora hashaz nazwami i wartościami pól. Z tego rozwiązania korzysta się zazwyczaj w kodziekontrolerów, by sprawnie utworzyć nowy obiekt z parametrów formularza:

def create @goal = Goal.new(params[:goal]) @goal.save!end

• Kasowanie rekordów. Istnieje możliwość bezpośredniego skasowania wierszatabeli, używając metody delete(id) , gdzie id jest wartością klucza głównego.Natomiast do kasowania rekordów, które zostały zainstancjonowane w postaciobiektów służy metoda destroy :

@goal = @current_user.goals.find(params[:id])@goal.destroy

• Aktualizacja rekordów. Zmianę wartości rekordów najłatwiej wprowadzićzmieniając wartości pól obiektu reprezentującego dany wiersz i, podobnie jakprzy tworzeniu obiektu, wykonać metodę save :

@goal = @current_user.goals.find(params[:id])@goal.user = [email protected]

Można również użyć metody update_attributes! , która przyjmuje hashwartości zmienionych pól

@goal.update_attributes!({:name => 'Changed name', :user => nil})

• Odczyt rekordów. Odczyt i wyszukiwanie danych jest jedną z najczęstszychczynności podczas pracy z bazą danych, dlatego też AR definiuje całą gamęmożliwości dotarcia do danych.

Podstawowym narzędziem wyszukiwania danych jest metoda klasowa find ,wywołanie jej z jednym parametrem spowoduje wyszukanie rekordu z danejtabeli o danym kluczu głównym:

User.find 1

Page 33: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

33

Do metody find można przekazać opcje zawężające wynikowy zbiór danych:w postaci parametryzowanych ciągu znaków:

Goal.find :conditions => ['period_start = ? AND period_end = ?, new_goal.period_start, new_goal.period_end]

lub w postaci hasha:

Goal.find :conditions => {:period_start => Date.today, :period_end => Date.tomorrow }

Dostępne są również metody wyszukujące specyficzne dla danego modelu, np.model Report , który posiada pola name oraz depth , można przeszukiwać przypomocy wywołania:

Report.find_by_name_and_depth('Report1', 5)

Czasem istnieje potrzeba odrzucenia zalet standardowych metodi  bezpośredniego wykonania na bazie danych zapytania SQL, służy do tegometoda find_by_sql

User.find_by_sql "SELECT * FROM users"

Walidacje

Active Record wprowadza mechanizm utrzymania spójności danych biznesowych,w znacznym stopniu eliminujący potrzebę używania specyficznych dla systemubaz danych wyzwalaczy, czy tez procedur składowanych. Walidacje są metodamiwykonywanymi na poziomie klasy modelu, mającymi za zadanie sprawdzanieprzed zapisem rekordu do bazy czy jest on zgodny z wytycznymi, określonymiw parametrach walidacji [2].

Walidacje operują na polach obiektów, lub grupach pól. Do podstawowychwalidacji należą:

• validates_presence_of :period_start, :period_end

Zapewnia, że podane pola nie są puste,

• validates_numericality_of :max_categories_values_count, :only_integer => true, :greater_than_or_equal_to => 1

Zapewnia, że pole podane jako pierwszy parametr będzie liczbą, dodatkoweparametry zawężają zbiór dopuszczalnych wartości do liczb całkowitychwiększych lub równych 1,

• validates_length_of :login, :within => 3..40

Zapewnia, że długość ciągu znaków zawiera się między 3 a 40,

• validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i

Walidacja pola względem wyrażenia regularnego.

Page 34: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

34

Gdy podstawowe walidacje nie są wystarczające, można zadeklarować własnąmetodę walidującą:

class MultipleCategoryReport < Report validate :has_at_least_one_category?

def has_at_least_one_category? if category_report_options.empty? errors.add_to_base(:should_have_at_least_one_category) end endend

W tym przypadku metoda validate przyjmuje jako parametr nazwę metody,której zadaniem jest zwalidowanie danego modelu pod względem posiadania conajmniej jednego obiektu category w relacji jeden do wielu.

5.3. WidokOstatnim, lecz nie mniej ważnym elementem każdej aplikacji Rails, jest warstwawidoku – jej zadaniem jest generowanie na żądanie kontrolera (oraz przy pomocyzmiennych przez niego przekazanych) kodu w formacie rozumianym przez klientaaplikacji. W zależności od potrzeb będzie to HTML, Java Script lub nawet XML.

Szablony widoku

Kod widoku zlokalizowany jest w plikach zwanych szablonami, w folderze app/views . Nazwa pliku szablonu odpowiada zazwyczaj nazwie odpowiadającej akcjiw kontrolerze, natomiast rozszerzenie uzależnione jest od formatu wynikowegooraz wybranego mechanizmu przetwarzania [2]. Domyślnym mechanizmem jestERB (Embeded Ruby – zagnieżdżony Ruby), dlatego plik szablonu generującegoodpowiedź do przeglądarki użytkownika, na zapytanie o  listę obiektów typuCategory , będzie miał nazwę categories/index.html.erb

Szablony składają się głównie z kodu w formacie wynikowym, przeplatanymkodem Rubiego, umieszczonym w odpowiednich znacznikach. Prosty szablon,generujący stronę logowania, prezentuje się następująco:

<h1>Logowanie</h1>

<% form_tag session_path, :class=>'style2' do -%> <p> <%= label_tag 'login', 'Login' %> <%= text_field_tag 'login', @login %> <%= help_tag 'system_elements.session.login.login' %> </p> <p> <%= label_tag 'password', 'Haslo' %> <%= password_field_tag 'password', nil %> <%= help_tag 'system_elements.session.login.password' %> </p> <p><%= submit_tag 'Zaloguj' %></p><% end -%>

Page 35: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Budowa aplikacji

35

W szablonach możliwe jest użycie znaczników

<%= kod_ruby %>

oraz

<% kod ruby %>

Różnica między nimi jest subtelna i polega na umieszczeniu w kodziewynikowym wartości wyrażenia pierwszego, natomiast zignorowanie wynikudrugiego wyrażenia. W znacznikach oprócz zwykłego kodu rubiego, możnaumieszczać wywołania metod z Rails, wspomagających generowanie wynikowegokodu, takimi metodami w powyższym przykładzie są np. form_tag , label_tag czyteż text_field_tag generujące odpowiednio znaczniki HTML <form/> , <label/>, <input/> .

Szablony operują na danych przekazywanych przez kontroler: wszystkie zmienneinstancji (poprzedzone przedrostkiem „@”) kontrolera są dostępne też dla widoku.Mają one również dostęp do stałych kontrolera i zmiennych globalnych.

Szablon główny i szablony częściowe

Szablony zaprezentowane w tym rozdziale same w sobie nie generowałykompletnego kodu HMTL, brak tam takich wymaganych elementów, jak <html/>, <body/> , czy też definicji stylu wyglądu strony. Elementy te zazwyczaj niezmieniają się w ramach aplikacji, stanowią one tzw. layout[1] strony, dlategow Rails umieszczono mechanizm pozwalający zdefiniować te elementy tylko razw jednym miejscu, a elementy zmienne strony dołączać w ramach potrzeb.

Taki wspólny dla całej aplikacji szablon znajduje się w pliku views/layouts/application.html.erb , dotyczą go te same zasady co omawianych wcześniejszablonów, z wyjątkiem instrukcji: <%= yield %> . Odpowiada ona za połączeniekodu wygenerowanego z szablonu przetwarzanej akcji z kodem szablonu aplikacji.

Innym rodzajem szablonów są szablony częściowe (partials [1]), które stanowiąpewną analogię do metod w obiektach – pozwalają zamknąć część kodu szablonuw osobnym pliku, by następnie powoływać się na nie z innych miejsc. Szablonytakie, zapisywane w plikach z przedrostkiem _, mogą być wywoływane z poziomuinnego szablonu lub nawet kontrolera. Podobnie jak metody, partiale mogąprzyjmować parametry, które wpływają na ich działanie.

Podstawowym sposobem wywoływania szablonów częściowych jest użyciemetody render z parametrem :partial , np.:

<%= render :partial => 'kind_of_transfer', :locals => { :transfer => @transfer } %>

Umieszczenie takiego kodu w szablonie powoduje włączenie wyniku działaniaszablonu kind_of_transfer do wygenerowanej treści. Użycie w tej metodzieparametru :locals , powoduje przekazanie do wnętrza szablonu częściowegozmiennej @transfer .

Dodatkowo istnieje możliwość przekazania parametru :collection , któregoużycie powoduje iteracyjne wykonanie szablonu częściowego dla każdegoelementu podanej jako parametr kolekcji.

Page 36: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

36

Rozdział 6Testowanie aplikacji

Społeczność programistów języka Ruby znana jest jako najbardziej przesiąkniętaideą testowania. Standardowe biblioteki języka zawierają moduł Test::Unita programiści zachęcani są do jak najszerszego testowania swoich programówzarówno przez najbardziej znane osoby w społeczności jak i niemal w każdejksiążce.

Chociaż sam język Ruby nie jest jeszcze ustandaryzowany (pojawiły się jużpropozycje dokonania tego w oparciu o ISO) to istnieje projekt RubySpec, którystawia sobie za cel utworzenie wykonywalnej specyfikacji dla języka oraz jegostandardowych bibliotek. RubySpec jest głównie rozwijany i wykorzystywanyprzez deweloperów różnych implementacji języka (zwłaszcza Rubinius oraz JRuby)dla zapewnienia ich kompatybilności. Twórcy nowych interpreterów mogą dziękibogatemu zestawowi testów sprawdzić, czy zachowują się one w  pożądanysposób w najróżniejszych sytuacjach oraz mierzyć postępy w pracach.

Nic zatem dziwnego, że to zorientowanie na testowanie głęboko przeniknęło doRails i jest uznawane, za jedną z najlepszych praktyk. W tym rozdziale zostanązaprezentowane standardowe sposoby testowania aplikacji Ruby on Rails orazkrótko przedstawione alternatywne rozwiązania.

6.1. WprowadzenieTestowanie odbywa się w oparciu o standardową bibliotekę Test::Unitdostarczaną razem z interpreterem języka. Oznacza to, że programiści językaRuby, którzy są z nią zaznajomieni, mogą niemal od razu zacząć pisać testy dlaRails.

Proces testowania polega na napisaniu metod sprawdzających prawdziwośćpewnych stwierdzeń, bazując przy tym na gotowych zestawach danych. Takiemetody zgrupowane razem w klasie Test::Unit::TestCase tworzą razemzestaw testów, który może być uruchomiony w każdej chwili. Uruchomienietestów dostarcza informacji zwrotnej, o powodzeniu lub niepowodzeniu procesuweryfikacji poszczególnych założeń, które miały być spełnione.

Asercje

Serce całego frameworku testującego stanowią asercje, które weryfikująprawdziwość oczekiwań programisty względem jakiegoś obiektu lub kodu. Asercjeto po prostu metody wywoływane w testach, przyjmujące jeden lub więcejparametrów a czasem dodatkowo blok kodu. Jeśli założenie wyrażone przezasercję nie jest spełnione, następuje przerwanie wykonywania aktualnego testui uznaje się, że został on zakończony niepowodzeniem.

Niektóre, najczęściej używane asercje (ostatni opcjonalny parametr oznaczającykomunikat, który ma się pojawić, jeśli nie są one spełnione, został zawszepominięty) [12]:

• assert(object);

Page 37: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

37

Nie zaliczona, gdy parametr object to false lub nil . Przykład:

assert Money.new.empty?

Metoda empty? utworzonego obiektu klasy Money powinna zwrócić true abyasercja była spełniona.

• assert_nil(object);assert_not_nil(object);

Przechodzi (nie przechodzi), gdy parametr object ma wartość nil . Przykład:

assert_not_nil Money.new(@zloty => 10).currency

Metoda currency obiektu klasy Money, który przechowuje informację o 10złotych nie powinna zwrócić nil .

• assert_equal(expected, actual);assert_not_equal(expected, actual);

Test jest kontynuowany, jeśli parametry expected oraz actual są równe (różne).Przykład:

assert_equal Money.new(@zloty => 10), Money.new(@zloty => 10)

Dwa obiekty klasy Money przechowujące informację o takiej samej ilości środkóww tej samej walucie powinny być uznane za równe sobie.

• assert_match(pattern, string);assert_not_match(pattern, string);

Przechodzi, jeśli parametr string zostanie (nie zostanie) dopasowany dowyrażenia regularnego pattern. Przykład:

assert_match /pust[y|a|e]/, empty_user.errors.on(:login)

Opis błędu pola login , w sytuacji gdy nie jest ono wypełnione powinien zawieraćjedno ze słów: pusty, pusta, puste.

• assert_raise(Exception, ... , &block);assert_nothing_raised(Exception, ... , &block);

Asercja spełniona, jeśli wykonywany blok kodu rzuci (nie rzuci) jedenz wymienionych rodzajów wyjątków. Przykład:

assert_nothing_raised do User.SALDO_CALCULATING_ALGORITHMS.keys.each do |algorithm| Category.compute(algorithm, @john.categories) endend

Obliczenie wszystkimi możliwymi algorytmami salda dla kategorii użytkownika@john nigdy nie powinno spowodować rzucenia jakiegokolwiek wyjątku. Takitest nie przejdzie, jeśli programista dodałby nowy algorytm obliczania salda, alezapomniałby go zaimplementować.

Page 38: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

38

Budowa zestawu

Zestaw testów to zbiór metod o nazwach zaczynających się od słowa testzdefiniowanych w klasie dziedziczącej z Test::Unit::TestCase .

W ciele metody następuje zwykle doprowadzenie obiektu testowanej klasy doodpowiedniego stanu, a następnie upewnienie się co do jego poprawności,poprzez wykonanie asercji.

Często wykonanie każdego testu byłoby poprzedzone dokładnie taką samąprocedurą inicjalizacyjną np. tworzone są te same obiekty, których współpracajest później testowana. Taki kod umieszcza się wewnątrz metody setupuruchamianej przed wywołaniem każdego z testów lub metody teardownwykonywanej po ich zakończeniu, zwykle w celu finalizacji działania pewnychobiektów np. zamknięcia deskryptorów plików [1].

Przykładowy zestaw dwóch testów dla klasy Money przechowującej informacjeo środkach pieniężnych w różnych walutach:

class MoneyTest < Test::Unit::TestCase

def setup save_common_currencies end def test_initialize assert_nothing_raised do Money.new(@zloty => 10, @dolar => 15) Money.new(@zloty, 10) Money.new() end

assert_raise ArgumentError do Money.new(1) end end

def test_emptyness money = Money.new() assert money.is_empty?

money.add!(10, @zloty) assert !money.is_empty?

money.add!(-10, @zloty) assert money.is_empty? end end

W zestawie tym przed uruchomieniem obu metod testujących wywoływanajest metoda setup , a w niej save_common_currencies tworząca standardowewaluty przypisane do zmiennych @zloty oraz @dolar . Metoda test_initialize

Page 39: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

39

sprawdza czy tworzenie nowych obiektów klasy Money z użyciem poprawnychkonstruktorów nie spowoduje powstania wyjątku. Użycie niepoprawnych powinnospowodować rzucenie wyjątku typu ArgumentError . Wewnątrz test_emptynesssprawdzane jest, czy wywołania metody empty? zwracają odpowiednio truei  false w zależności od tego czy obiekt klasy Money jest pusty, czy teżprzechowuje informacje o środkach w walucie @zloty .

Uruchamianie

Wykonywanie testów możliwe jest zarówno na najniższym poziomie (pojedynczametoda z jednego zestawu np. takiego, jak opisany w poprzednim podrozdziale):

app> ruby -I test test/unit/money_test.rb --name "test_emptyness"

jak i coraz wyższych:

• Jeden zestaw dla konkretnej jednostki, zwykle klasy (tutaj cały zestaw testówklasy Money):

app> ruby -I test test/unit/money_test.rb

• Wszystkie zestawy z konkretnej grupy testów (jednostkowe, funkcjonalne,integracyjne):

app> rake test:unitsapp> rake test:functionalsapp> rake test:integration

• Wszystkie testy dostępne dla aplikacji:

app> rake test

W wyniku wykonania jednej z takich komend, zostaną uruchomione testy a ichrezultat jest prezentowany na ekranie:

• Wynik negatywny jednego z testów. Pojawia się informacja o nazwie testuzakończonego niepowodzeniem oraz numer linii kodu, gdzie znajdowała sięniezgodna z prawdą asercja. Niepomyślne wykonanie oznaczane jest literą "F"na pasku postępu.

Started................... F..... itd.Finished in 97.429342 seconds.

1) Failure:test_memcached_write_and_read(MemcachedTest)[/test/unit/memcached_test.rb: 7]:<true> expected but was <false>.

190 tests, 1296 assertions, 1 failures, 0 errorsCommand failed with status (1)

• Wszystkie testy zakończone prawidłowo. Pomyślne wykonanie oznaczane jestkropką.

Page 40: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

40

Started......................... itd.Finished in 96.124058 seconds.

190 tests, 1297 assertions, 0 failures, 0 errors

Tworzenie szkieletu testów

Ilekroć korzystamy z dostępnych generatorów kodu, w odpowiednich katalogachtworzone są także pliki zawierające szkielet testów dla tworzonych obiektów:

app> script/generate model user && script/generate controller session . . . . . .create app/ models/user.rbcreate test/unit/user_test.rb . . . . . .create app/ controllers/session_controller.rbcreate test/functional/session_controller_test.rb

Możliwe jest również dodatkowe wygenerowanie potrzebnych plików:

app> script/generate integration_test save_loan_and_send_remindexists test/integration/create test/integration/save_loan_and_send_remind_test.rb

6.2. Testy jednostkoweTesty jednostkowe stanowią narzędzie weryfikacji logiki biznesowej [1]. W aplikacjiManage My Money najczęściej przy ich użyciu kontroli podlegają takie obszary jak:

• reguły walidacji obiektów biznesowych,

• prawidłowość dokonywanych obliczeń,

• zachowanie obiektów wobec nieprawidłowych danych.

Zgodnie z konwencją każdej klasie z dziedziny modelu odpowiada jeden zestawtestów sprawdzających jej poprawność oraz (w mniejszym stopniu) stanowiącychspecyfikację jej zachowania.

Testy jednostkowe zawarte są w klasie ActiveSupport::TestCase , któraudostępnia kilka dodatkowych funkcjonalności w porównania z jej nadklasąTest:Unit:TestCase takich jak:

• Użycie deklaratywnej składni:

test "Saldo is empty before creating transactions" do @john.categories.each{|category| assert category.saldo.empty?}end

Page 41: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

41

zamiast definiowania metod:

def test_saldo_is_empty @john.categories.each{|category| assert category.saldo.empty?}end

• Korzystanie z dodatkowych asercji:

• assert_difference(expressions, difference = 1, &block);assert_no_difference(expressions, &block);

Przechodzi gdy wartość wyrażenia expression zmieni się o wartośćparametru difference (nie zmieni się), w wyniku wykonania podanego blokukodu. Przykład:

assert_difference("@john.categories.count", -1) do @john.categories.first.destroyend

Wartość wyrażenia @john.categories.count oznaczającego liczbę kategoriinależących do użytkownika @john powinna być o 1 mniejsza po wykonaniukodu @john.categories.first.destroy usuwającego pierwszą kategoriętego użytkownika.

• assert_valid(activerecord_object);

Zaliczona, jeśli parametr activerecord_object jest obiektem, w którymspełnione są wszystkie reguły walidacyjne.

• Dodawanie callbacków dla testów (wykonywanych przed uruchomieniem,lub po zakończeniu każdego z nich) poprzez wywołanie metody setup lubteardown na poziomie klasy i podanie symbolu oznaczającego nazwę metodydo wykonania. Zaletą korzystania z tej implementacji jest automatycznewykonywanie callbacków z nadklasy we właściwej kolejności:

class CategoryTest < ActiveSupport::TestCase setup :save_currencies setup :save_usersend

zamiast

class CategoryTest < ActiveSupport::TestCase def setup super save_currencies save_users endend

6.3. Testy funkcjonalneKontrolery nadzorują pracę całej aplikacji poprzez przyjmowanie żądań,współpracę z modelem a następnie wysyłanie odpowiedzi. Testowanie

Page 42: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

42

kontrolerów oznacza zatem upewnienie się, że odpowiednie żądania zostanąobsłużone w odpowiedni sposób (np. pod względem dostarczanej treści, którazwykle jest dynamicznie generowana).

Podstawy

Wewnątrz testu dziedziczącego po klasie ActionController::TestCase możemyużywać kilku metod (get , post , put , delete , head ) o nazwach odpowiadającymżądaniom HTTP, symulując w ten sposób różne działania użytkownika w serwisie.Argumenty tych metod w prosty sposób pozwalają na imitowanie sytuacji, w jakiejbył użytkownik, przed wykonaniem testowanej akcji w przeglądarce:

get(action, parameters = nil, session = nil, flash = nil);

• action - testowana metoda kontrolera,

• parameters - opcjonalny hash z parametrami żądania HTTP np. dane wysłaneprzez użytkownika w formularzu,

• session - stan sesji,

• flash - hash z obiektami (np. wiadomościami) przekazywanymi tylko donastępnego żądania.

Przykładowo:

get :indexpost :create, :user => { :name => "dave" , :age => "24" }

Do dyspozycji jest też metoda xhr , która służy do testowania żądań typuXmlHttpRequest wysłanych np. z użyciem kodu JavaScript osadzonego na stronie.W serwisie Manage My Money ma to miejsce np. kiedy wysyłany jest tekstwprowadzonego opisu transakcji a serwer ma zwrócić dostępne podpowiedzido wpisywanego tekstu. Pierwszy parametr tej metody oznacza rodzaj żądania(:get, :post, :put, :delete, :head) a następne są takie same jak opisane wcześniejdla poprzednich metod.

Do sprawdzania statusu odpowiedzi używa się metody assert_response , któramoże przyjąć zarówno oczekiwany numer, jak i opisowy symbol np.:

assert_response 200assert_response :redirectassert_response :unauthorized

W przypadku gdy odpowiedzią jest przekierowanie dobrze wiedzieć, czy kierujeono użytkownika we właściwe miejsce:

assert_redirected_to :controller => :session , :action => :newassert_redirected_to "http://example.org/login"

Możemy się też upewnić co do szablonu, jaki został użyty do wygenerowania kodustrony:

assert_template 'index'assert_template 'empty_list'

Page 43: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

43

Korzystając z metod session , flash oraz cookies możemy sprawdzić stansesji, obiektu flash oraz zawartość jaką miałyby pliki cookies. Może to byćprzydatne np. gdy chcemy sprawdzić, czy sesja zawiera identyfikator użytkownikapo zalogowaniu się przez niego:

post :create, :login => @john.name, :password => 'valid password'assert_equal @john.id, session[:user_id]

Ostatnia ważna i często używana metoda to assigns , która pozwala odczytaćzmienną instancji kontrolera o podanej nazwie. W poniższym przypadkuupewniamy się, że przy próbie pobrania listy walut, instancja kontroleraCurrenciesController przypisała wartość do zmiennej @currencies , z którejkorzysta widok podczas generowania strony HTML.

class CurrenciesControllerTest < ActionController::TestCase def test_index get :index assert_response :success assert_template 'index' assert_not_nil assigns(:currencies) endend

Do poprawnego działania wspomnianych metod, w czasie wykonywania testów,muszą być odpowiednio ustawione zmienne @controller , @request oraz@response . W przeszłości trzeba było je zdefiniować w metodzie setupuruchamianej przed każdym z nich np.:

class CurrenciesControllerTest < ActionController::TestCase def setup @controller = CurrenciesController.new @request = TestRequest.new @response = TestResponse.new endend

Aktualnie jednak nie jest to już konieczne, gdyż klasaActionController::TestCase definiuje callback

setup :setup_controller_request_and_response

ustawiający te zmienne. W szczególności tworzy nową instancję kontrolera napodstawie nazwy klasy agregującej testy (CurrenciesControllerTest ). Jeślijednak nazwa odbiega od konwencji można wyraźnie wskazać, jaki kontroler mabyć testowany poprzez użycie metody tests :

class SecurityCurrenciesControllerTest < ActionController::TestCase tests CurrenciesControllerend

Tak rozbudowany arsenał dostępnych środków pozwala pisać zarówno proste jaki bardziej rozbudowane testy o bardzo dużym stopniu czytelności:

class CurrenciesControllerTest < ActionController::TestCase

Page 44: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

44

def test_create_currency_without_errors log_john

assert_difference("@ john.currencies.count", +1) do post : create, : currency => { :long_symbol => 'CHF' :symbol => 'CHF', :long_name => 'frank szwajcarski', :name => 'frank' }

assert_response :redirect assert_ redirected_to :action => : index assert_match / Utworzono/, flash[:notice] end end

end

Ten test sprawdza czy po wysłaniu przez zalogowanego użytkownika @johnżądania HTTP typu POST z danymi formularza nt. nowo tworzonej waluty zostanieon przekierowany na listę dostępnych walut wraz z komunikatem, że utworzononowy obiekt. Zapewnia on także, że liczba walut użytkownika wzrosła o 1.

Testy bezpieczeństwa

Testy bezpieczeństwa dla kontrolerów mają na celu zweryfikowanie, czyprawidłowo działają ograniczenia w dostępności do elementów systemuutworzonych przez innych użytkowników.

Jednym z przykładowych sposobów, w jaki użytkownik mógłby chcieć spróbowaćobejrzeć lub edytować dane innych osób, jest samodzielne przejście pod innyadres w przeglądarce lub wysłanie odpowiednio spreparowanego żądania HTTPnp. korzystając z uniksowego programu curl.

Listing testu sprawdzającego, czy kontroler odpowiedzialny za zarządzaniekategoriami, nie jest podatny na tego typu atak:

class CategoriesControllerTest < ActionController::TestCase

def setup save_users(:john, :kate) log_john end

def test_invisible_to_others [[:show,:get], [:search, :post], [:destroy, :delete], [:edit, :get], [:update, :put]].each do |action, method| send(method, action, :id => @kate.asset.id) assert_redirected_to :action => :index assert_match("Brak uprawnien", flash[:notice])

Page 45: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

45

end end

end

Werfyikuje on czy zalogowany użytkownik @john podejmując jakąkolwiekakcję (podgląd, edycja, zapis zmian, usunięcie, wyszukiwanie) wobec kategoriiużytkowniczki @kate, zostanie przekierowany z powrotem do listy swoich kategoriiz komunikatem, że brak mu uprawnień do wykonania niedozwolonej akcji.

6.4. Inne rodzaje testówMożliwe jest tworzenie także kilku innych rodzajów testów. Są one jednak rzadziejspotykane lub często implementowane przy użyciu alternatywnych zamienników,dlatego zostaną omówione tylko pokrótce.

Testy integracyjne

Testy integracyjne obejmują wiele kontrolerów oraz akcji w celu zapewnienia,że współpracują one razem w oczekiwany sposób [1]. Najbardziej przekrojowotestują one całą aplikację od wysyłania żądań po bazę danych. Mogą onezostać użyte do testowania scenariuszy, w których użytkownik osiąga pewienrezultat poprzez wykonanie ciągu wielu różnych akcji np. przejście przez całyproces dokonywania zakupów od logowania, poprzez dodawanie przedmiotówdo koszyka, aż po dokonanie płatności. Składniowo testy integracyjne sąpodobne do funkcjonalnych. W aplikacji Manage My Money skorzystaliśmy z nichw bardzo niewielkim stopniu, gdyż wysokopoziomowe testowanie aplikacji zostałozrealizowane z użyciem Selenium.

Testy routingu

Istnieje możliwość napisania testów jednostkowych, które będą koncentrowaćsię na sprawdzaniu prawidłowości mapowania adresów stron na wywołania akcjiw kontrolerach. Używa się w tym celu odpowiednich asercji: assert_generates ,assert_recognizes oraz assert_routing [1]. Jednak budowanie aplikacjiw oparciu o konwencję REST niemal całkowicie eliminuje konieczność testowaniaroutingu. Wydaje się to być sensowne tylko w zaawansowanych przypadkach.

Testy wydajnościowe

Przy użyciu klas Benchmark oraz ActionView::Helpers::BenchmarkHelper badasię czas w jakim wykonywane są poszczególne zadania w testach [1]. Railsdostarcza także skrypty służące za profiler i benchmark, którymi można ad hocsprawdzać dowolny kod.

Ponieważ jednak testowanie w odizolowanym środowisku potrafi wskazywaćzupełnie inne rezultaty niż w środowisku produkcyjnym, nie jest to bardzozalecana technika i należy używać jej ostrożnie. Trzeba też pamiętać, że maszynyprogramistów mogą dalece różnić się wydajnością i z tego powodu ustalenieodgórnych ograniczeń na czas wykonania kodu może być trudne oraz powodowaćnieuzasadnione błędy, podczas uruchamiania testów na wolniejszej maszynie.

Z tego powodu raczej korzysta się z adapterów na bieżąco zbierającychdane o wydajności w środowisku produkcyjnym lub stara się je wydobyć

Page 46: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

46

z  logów. Wdrożenie w domenie co-do-grosza.pl jest nadzorowane przy użyciuoprogramowania New Relic RPM, które umożliwia bieżące przeglądanie statystykz poziomu przeglądarki.

6.5. Narzędzie alternatywne oraz dodatkoweZe względu na łatwą rozszerzalność środowiska Ruby on Rails poprzez gemyoraz pluginy a także odmienność technik programistycznych używanych przyrealizacji projektów istnieje wiele narzędzi mających wspomagać testowanieaplikacji. Niektóre z nich zdobyły już bardzo silną pozycję [13] a ich znajomośćjest wymagana lub bardzo pożądana przez pracodawców:

• RSpec - alternatywny framework do testowania oraz specyfikowania zachowańaplikacji powstały jako narzędzie do realizacji projektów zgodnie z BDD.Zamiennik dla Test::Unit .

• Cucumber - narzędzie do wykonywania testów funkcjonalnych, opisującychdziałanie aplikacji w postaci plików tekstowych (ich składnia oparta jest o językGherkin ) [14]. Pliki te zawierają scenariusze, składające się z opisanych wjęzyku naturalnym kroków, którym odpowiadają napisane w języku Ruby akcjedo wykonania.

• Selenium - rozbudowany zestaw narzędzi (Core, IDE, RC, Grid) do testowaniaaplikacji webowych w przeglądarkach internetowych. Może się ono odbywaćw różnych systemach operacyjnych oraz przeglądarkach (pod warunkiem, żewspierają one JavaScript) na trzy sposoby [15]:

1. Do aplikacji testującej (Selenium Core), działającej w środowisku przeglądarkidostarczany jest plik określający poszczególne kroki, jakie mają byćwykonane np. przejście pod odpowiedni adres, wciśnięcie przycisku, wpisanietekstu w polu formularza. Poszczególne kroki i asercje są przetwarzane,a w przypadku niepowodzenia test jest przerywany. Istotnym ograniczeniemtego narzędzia jest fakt, że zbiór wszystkich komend do wykonania musibyć załadowany od razu, co nie pozwala np. na dynamiczne generowanienastępnego polecenia na podstawie identyfikatora obiektu zapisanegow bazie, w czasie wykonywania poprzednich instrukcji.

Wspomaganiem testowania aplikacji Rails z użyciem selenium zajmuje sięplugin selenium-on-rails , który dostarcza kilka wygodnych usprawnieńm.in. przyjemniejszą składnię (formaty .sel oraz .rsel ).

2. Przy użyciu dodatkowego komponentu jakim jest Selenium Remote Control(RC). Zbudowany jest on z:

• serwera działającego jako proxy dla żądań HTTP oraz zarządzającegouruchamianiem i wyłączaniem przeglądarki, w której odbywają się testy.

• bibliotek dla języka programowania używanego do sterowania testami(możliwe jest używanie innego niż ten, w którym piszemy aplikację).Wspierane języki to: Java, Ruby, Python, Perl, PHP oraz platforma .Net .

Odmienny sposób działania RC powoduje, że testy nie są całkowiciewykonywane tylko w przeglądarce, lecz mogą korzystać z modeluaplikacji oraz wszystkich dobrodziejstw frameworku testującego, który je

Page 47: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Testowanie aplikacji

47

aktualnie wykonuje. Poszczególne asercje do sprawdzenia są przesyłane doprzeglądarki na bieżąco a pomiędzy nimi może być wykonywany dowolnykod aplikacji oraz inne asercje weryfikujące np. stan obiektów biznesowychzamiast tylko widok strony WWW, jak to ma miejsce w pierwszym, opisanymwariancie.

Te liczne możliwości wiążą się jednak z dużo większą liczbą komponentówużywanych do testowania oraz bardziej rozbudowaną i skomplikowanąkonfiguracją. Czas wykonywania takich testów również ulega wydłużeniu(zakończenie kilku napisanych testów aplikacji Manage My Money z użyciemRC zajmuje około 5 minut). Z pomocą przy korzystaniu z Selenium RCprzychodzi gem selenium-client .

3. Korzystając z Selenium Grid, które pozwala na wielu maszynach(w szczególności jednej) uruchamiać zarówno kilka instancji SeleniumRC jak i przeglądarek, przez co możliwe jest szybsze zakończenietestów oraz dokładne sprawdzenie poprawności działania aplikacjiw różnych środowiskach (systemach operacyjnych, przeglądarkach, wersjachoprogramowania).

• Webrat - narzędzie do pisania i wykonywania testów akceptacyjnych. Przyużyciu jednolitego API, umożliwia testowanie poprzez symulowanie działaniaprzeglądarki (szybkość) lub rzeczywiste jej uruchomienie z pomocą Selenium(konieczność testowania kodu JavaScript lub wywołań Ajax).

• Shoulda - gem zawierający dodatkowe makra oraz asercje do testowanianajczęściej pojawiających się konstrukcji w modelach i kontrolerach. Umożliwiatakże pisanie i wykonywanie testów przy użyciu czytelniejszej składni.

• Mocha oraz RR - wspomagają korzystanie z takich technik jak mocking orazstubbing.

• Factory girl - zamiennik dla standardowo dostępnego w Rails mechanizmuładowania obiektów do bazy danych, w celu ich wykorzystania w wykonywanychtestach.

Page 48: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

48

Rozdział 7Wdrożenie

Celem twórców aplikacji internetowych jest oczywiście udostępnienie ich dziełajak najszerszemu gronu odbiorców. Wymaga to od nich uruchomienia aplikacjina publicznie dostępnym serwerze w środowisku spełniającym wszystkie warunkiwymagane przez aplikację odnośnie dostępnych komponentów, z którymi ma onawspółpracować. Każde wdrożenie ze względu na unikatowy charakter programujest procesem niepowtarzalnym i nie dającym się bardzo łatwo przenieść nagrunt innej aplikacji. W tym rozdziale opiszemy nasze doświadczenia z wdrażaniastworzonego przez nas systemu Manage My Money, użyte narzędzia orazzastosowane konfiguracje. Wspomniane zostaną ograniczenia jakim podlegaliśmyw całym procesie i skutki jakie one wywarły.

7.1. Dostępne opcjeIstnieje kilka możliwości uruchamiania dynamicznych aplikacji zbudowanychw oparciu o Ruby on Rails. Różnią się one między sobą wieloma aspektami, jednakto co będzie nas najbardziej interesować to prostota konfiguracji, stabilność orazwydajność. Przedstawione w tym rozdziale rozwiązania pozwolą lepiej zrozumieć,opisaną w dalszej części architekturę stworzonego systemu.

Common Gateway Interface

Common Gateway Interface (CGI) jest opracowanym w 1993 roku interfejsemumożliwiającym wymianę informacji pomiędzy serwerem WWW oraz innymiprogramami znajdującymi się na serwerze. Wysłane przez użytkownika danesą przetwarzane przez serwer WWW, a następnie dostarczane do programu(zwykle będącego skryptem jakiegoś interpretowanego języka np. Perl) poprzezstandardowe wejście oraz specjalne zmienne środowiskowe. Po zakończeniuswojego działania taki program wysyła wynik na standardowe wyjście, skąd jeston pobierany przez serwer i poddawany dalszym operacjom [16].

Ten sposób działania aplikacji jest bardzo niezalecany ze względu na jego kiepskąwydajności. Bardzo bogate środowisko Ruby on Rails rozrasta się jeszcze bardziejwraz z wykorzystywanymi przez aplikację pluginami oraz gemami. Specyfikadziałania programów w trybie CGI powoduje, że z każdym przychodzącymdo serwera żądaniem należałoby utworzyć nowy proces, który następnieinterpretowałby od nowa kod frameworka oraz aplikacji. Całość trwa na tyle długo,że możemy uznać tą przestarzałą metodę, która ma swoja korzenie w początkachdynamicznego internetu, za zupełnie bezużyteczną dla rozważanych przez naszastosowań [2].

Lighttpd/Apache+         

FastCGI        

FastCGI, w przeciwieństwie do CGI, nie tworzy nowego procesu dla każdegożądania, lecz dysponuję ich pulą, z której wybierany jest jeden do jego obsługi.Proces ten po obsłużeniu żądania trwa dalej i oczekuje na kolejne. Oznacza to,że nie jest konieczne nieustanne tworzenie i usuwanie procesów przez system

Page 49: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

49

operacyjny, co na bardziej obciążonych maszynach zajmowało istotną część czasupracy procesora. Ponadto, trwanie procesu umożliwia powtórne wykorzystanie jużnawiązanych połączeń z bazą danych oraz cachowanie obiektów w jego pamięci.

W początkowym okresie istnienia Rails promowano używanie serwera Lighttpdpracującego w trybie FastCGI, jednak okazało się, że to rozwiązanie generujeróżne problemy oraz narzekano na obsługę load balancingu. FastCGI nie doczekałsię też dobrej implementacji w Apache'u [2].

mod_ruby

Mod_ruby jest opcjonalnym modułem dla serwera Apache, który rozszerza jegofunkcjonalność, o możliwość interpretowania kodu napisanego w języku Ruby.Skrypty działające pod kontrolą takiego modułu uruchamiają się dużo szybciej,ponieważ nie ma konieczności każdorazowo od nowa uruchamiać interpreterajęzyka. Moduł ten jednak nie osiągnął jakości, jakiej od niego oczekiwano[2] oraz posiada bardzo dużą wadę, jaką jest brak bezpieczeństwa, podczasjednoczesnego działania wielu różnych aplikacji Ruby on Rails, gdyż mogą onezacząć współdzielić między sobą klasy [17]. Może on zatem stanowić jedynierozwiązanie, dla osób mogących sobie pozwolić na komfort korzystania z osobnejinstancji serwera Apache dla każdej rozwijanej aplikacji, jednak dla większości jestto rozwiązanie nie do przyjęcia.

Apache / Nginx+       

mongrel_cluster / Thin / Ebb      

Apache - to otwarty serwer HTTP dostępny dla wielu systemów operacyjnych,aktualnie rozwijany przez Apache Software Foundation. Najszerzej spotykanyserwer HTTP w Internecie (46% udziału w rynku - maj 2009) [18], który przezlata dowiódł swoje stabilności i niezawodności. Jego bogate opcje konfiguracyjneoraz bardzo liczna gama dodatkowych modułów rozwiązujących wiele problemówsprawiają, iż jest on bardzo często używany przez firmy hostingowe, dla swoichklientów. Nginx to zdobywający sobie coraz większą popularność serwer HTTP(wg. badań z maja 2009 roku serwował on 2.8 miliona stron)[18] napisany przezRosjanina Igora Sysoeva, który rywalizuje z Apachem szybkością, mniejszymzużyciem pamięci oraz łatwiejsza składnią konfiguracji.

Mongrel, Thin oraz Ebb to serwery przetwarzające kod Ruby'ego. W tej rolispisują się bardzo dobrze, jednak nie ma sensu ich obciążać żądaniami doplików statycznych (obrazki, style, pliki binarne), których nigdy nie będą w stanieobsłużyć tak szybko jak napisane w języku C, specjalizowane serwery klasyApache czy Nginx.

Dlatego, w celu osiągnięcia jak największej wydajności, stosuje się rozwiązaniehybrydowe. Postawiony z przodu serwer HTTP (Apache, Nginx) obsługuje żądaniado plików statycznych, natomiast gdy przychodzi prośba wymagająca użyciaRuby'ego, jest ona przekierowywana do jednego z kilku serwerów, które topotrafią (wykorzystywany jest w tym celu load balancing, czyli mechanizmrównomiernego rozkładania obciążenia pomiędzy wiele uruchomionych instancjiaplikacji).

Mongrel jest zarówno biblioteką jak i serwerem HTTP zaprojektowanym jakoalternatywa dla FastCGI. Większość jego kodu została napisana w Ruby, pozostała

Page 50: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

50

część w języku C. Do komunikacji z innymi serwerami, które mogą stanowićwarstwę poprzedzającą używa protokołu HTTP zamiast FastCGI czy SCGI. PozaRails wspiera także kilka innych frameworków.

Thin jest serwerem, który został oparty o trzy biblioteki:

• Mongrel parser - składowa mongrela, odpowiedzialna za jego wysoką stabilnośći szybkość pracy.

• Event Machine - wysoce skalowalna i stabilna, asynchroniczna biblioteka I/O dlaaplikacji sieciowych.

• Rack - minimalny interfejs do pracy między serwerami webowymia frameworkami używającymi Ruby, powstały na bazie inspiracji pythonowymmodułem WSGI.

Najmłodszy z nich czyli Ebb, to mały i szybki serwer HTTP, napisany całkowiciew języku C. Każdy z nich ma możliwość pracy jako klaster kilku procesów.

Dedykowane serwery do przetwarzania Ruby'ego uczyniły wdrażanie aplikacjiopartych o Rails zdecydowanie prostszym. Dużo mniejsza problematyczność tegorozwiązania oraz możliwość prostego skalowania aplikacji w poziomie (poprzezdołożenie kolejnego serwera) spowodowały wyraźne odejście od korzystaniaz wcześniej polecanego FastCGI na rzecz opisanych powyżej rozwiązań.

Phusion Passenger(mod_rails)        

Wyznacznikiem łatwości instalacji aplikacji webowej są z pewnością systemyoparte o PHP. Do ich zainstalowania wystarczy wykupienie hostingu u dostawcyoraz przegranie plików do odpowiedniego folderu a następnie skonfigurowaniebazy danych na stronie internetowej, pod którą dostępna jest uruchamianaaplikacja.

Twórcy modułu Phusion Passenger (rozszerzenia dostępnego dla Apache oraz odniedawna także Nginx) postawili sobie za cel osiągnięcie takiej samej prostoty wewdrażaniu aplikacji opartych o Rails, tak by nie było konieczności konfigurowania,nadzorowania i restartowania kilku różnych usług dla każdego wdrożenia. Ichpraca została bardzo pozytywnie odebrana w świecie Rubiego. Wiele osób uważa,że ten sposób szybko zdobędzie sobie pierwszeństwo, natomiast korzystaniez serwerów takich jak mongrel, thin, czy ebb będzie zarezerwowane raczej dlasytuacji wymagających jak największej wydajności kosztem mniejszej prostoty.

Wdrażanie aplikacji na własne serwery (rozumiane jako takie, gdzie posiadamydostęp do pełnych uprawń administratora, czyli praktycznie wszystkierozwiązania poza hostingiem dzielonym) było łatwe od momentu pojawieniasię mongrela i jego następców. Jednak zaoferowanie hostingu dla Ruby onRails przez firmy było bardzo trudne, aż do chwili zaistnienia mod_rails, gdyżwymagało udostępniania klientom możliwości logowania się przez połączeniaSSH, uruchamiania własnych procesów oraz co się z tym wiązało dużego nakładupracy, położonego na bezpieczeństwo tych rozwiązań, które dawały klientomsporą swobodę działania na serwerach usługodawcy. Z tego powodu wdrażaniemałej aplikacji, start-upu wiązało się z koniecznością posiadania własnego

Page 51: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

51

serwera, który mogliśmy dowolnie konfigurować (jednak musieliśmy też umiećgo odpowiednio zabezpieczyć) lub poszukiwania specjalnych rozwiązań wśródnielicznych firm oferujących takowe. W obu przypadkach problemem zwyklepozostawała cena. Phusion Passenger daję nadzieję, że w najbliższej przyszłościcała procedura znacznie się uprości a cena zmaleje, gdyż wielu dostawców będziemogło w prosty sposób zaoferować hosting dla Ruby on Rails oparty o moduł,bardzo wygodny dla nich oraz dla klientów. Byłby to moment, kiedy zostałabyusunięta spora bariera ograniczająca rozwój Rails, podobnie jak miało to miejscew przypadku ASP .NET, co może mieć pozytywny wpływ na popularyzację tegorozwiązania.

Phussion Passenger ma kilka zalet, które sprawiają, iż wdrażanie z jego użyciemjest dużo łatwiejsze [17][19]:

• Nie trzeba nadzorować serwerów odpowiedzialnych za przetwarzanie koduRuby. Do takich zadań używa się programów takich jak monit lub god.Passenger potrafi samodzielnie stwierdzić, że jakiś proces przestał odpowiadaći utworzyć nowy.

• Brak sztywno określonej liczby instancji aplikacji. Są one dodawane bądźodejmowane dynamicznie w zależności od zapotrzebowania na nie, co oznaczalepsze zarządzanie pamięcią na serwerze.

• Zapytania są wysyłane do procesów, które mają najmniej klientów w kolejce(można też skorzystać z globalnego kolejkowania, przydatnego gdy aplikacjaczęsto obsługuje długie żądania).

• Współpraca z interpreterem Ruby Enterprise napisanym przez tych samychautorów, który pozwala zaoszczędzić około 33% pamięci.

W standardowym scenariuszu każdy z procesów odpowiedzialnych zaprzetwarzanie kodu Ruby jest tworzony osobno i nie współdzielą one żadnychdanych. Passenger kiedy korzysta z interpretera Ruby Enterprise używa innejtechniki: najpierw uruchamiany jest jeden proces, który ładuje środowiskoa  następnie wykonywany jest jego fork. System operacyjny używa wtedytechniki Copy on Write, która sprawia, że tak długo jak dane są tylko czytane,tylko jedna ich kopia pozostaje w pamięci i jest używana przez oba procesy.Dzięki takiemu podejściu instancje aplikacji mogą współdzielić zwłaszcza kodframeworka i bibliotek z których korzystają [19][17].

Aby osiągnąć zamierzony rezultat, należało uczynić garbage collector językaprzyjaznym dla techniki COW, poprzez używanie zbioru pól bitowych (setof marking bitfields) zamiast flagi wewnątrz obiektu, oznaczającej czy jeston osiągalny, czy też powinien zostać usunięty z pamięci. Ustawianie przezgarbage collector flagi powodowało, że dane były kopiowane i  przestawałybyć współdzielone przez procesy, co nie ma miejsca w wersji Enterprise, gdzieinformacja ta jest odizolowana od samego obiektu [19][20].

7.2. Platforma wdrożeniowaTworząc aplikację Manage My Money nie mieliśmy przekonania, że jest onaw stanie w polskich warunkach wygenerować sensowny dochód. Wobec liczneji  często dużo lepiej zorganizowanej konkurencji (pod względem strukturalnym

Page 52: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

52

oraz posiadanych zasobów) chcieliśmy tylko znaleźć odpowiednią niszę, którajednak naszym zdaniem i tak nie jest jeszcze w stanie być dochodowa.Pragneliśmy zatem, by zainwestowane przez nas środki finansowe, poświęcone narealizację tego projektu, utrzymały się na jak najniższym poziomie. Ograniczenieto było głównym kryterium zawężającym wybór sposobu i miejsca wdrożeniasystemu.

Skoro nie mogliśmy sobie pozwolić na komfort posiadania własnego serwera,którego konfiguracja i parametry byłyby całkowicie pod naszą kontrolą, coznacznie ułatwiłoby wdrożenie dając nam możliwość łatwego i szybkiegodoinstalowywania potrzebnych komponentów a także ich pełnej konfiguracji,pozostało nam jedynie skorzystanie z płatnego hostingu. Przedział cenowyoferowanych rozwiązań był szeroki, jednak w większości były one dedykowanedla firm i przerastały nasze skromne możliwości oraz oferowały wygórowaneparametry. Zdecydowaliśmy się skorzystać z najtańszej wtedy oferty dla kontshellowych prezentowanej na rootnode.net. Usługę mieliśmy zakupioną jeszczez czasów, gdy oferowana była ona przez stowarzyszenie, a nie firmę.

Apache

W procesie przetwarzania żądania od użytkownika, odwiedzającego witrynę co-do-grosza.pl, biorą udział dwie maszyny. Na początku dochodzi ono do komputerao nazwie Venema, na którym działa serwer Apache. Jest on przystosowanydo obsługi aplikację Ruby on Rails, z użyciem opisanego wcześniej modułuPassenger, jednak nie udało nam się skorzystać z tego rozwiązania, ze względuna rozbieżność zainstalowanych na nim wersji oprogramowania w stosunku donaszych wymagań. Zdecydowaliśmy się więc na inne podejście, dlatego rola tegoserwera sprowadza się tylko do przekazania żądania do drugiej maszyny o nazwieStallman. Służy nam do tego zdefiniowany w katalogu htdocs (katalog głównydla dokumentów witryny) plik .htaccess o następującej treści:

RewriteEngine onRewriteRule ^(.*)$ http://s.co-do-grosza.pl:3030/$1 [P,L]

Takie ustawienia powoduje, że każde zapytanie postaci http://co-do-grosza.pl/X/Y/Z/... zostanie przekształcone na http://s.co-do-grosza.pl:3030/X/Y/Z/... przyużyciu modułu mod_rewrite, który służy do zamiany adresów internetowychpo stronie serwera na podstawie reguł i wyrażeń regularnych. Litera Poznacza, że mamy do czynienia z  proxy, natomiast L, że należy skończyćprzetwarzanie po zastosowaniu tej reguły. Ponieważ subdomena s.co-do-grosza.pljest skonfigurowana tak, by wskazywała na maszynę Stallman, w wynikuzastosowania tej reguły każde żądanie zostaje przekierowane właśnie tam,konkretnie na port 3030, gdzie nasłuchuje aplikacja Pound.

Page 53: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

53

Rysunek 7.1. Architektura systemu

Pound

Pound to bardzo mały program, który służy nam jako load-balancer i rozkładaobciążenie do czterech procesów mongrel, z których każdy działa na innym porcie.Konfiguracja takiej funkcjonalności wygląda następująco:

User "deploy"Group "users"ListenHTTP Address 0.0.0.0 Port 3030 Service BackEnd Address 127.0.0.1 Port 3040 End BackEnd Address 127.0.0.1 Port 3039 End # Analogicznie dla portów 3038 oraz 3037 EndEnd

Pound potrafi przekierować ruch do innej maszyny/procesu niż pierwotniewylosowana dla danego żądania w przypadku, gdyby ona nie odpowiadała(np. w czasie awarii). Posiada sporo opcji umożliwiających wybór docelowegoadresu na podstawie różnych kryteriów, jednak nam wystarczyły najprostszeustawienia. Gdybyśmy dysponowali własny serwerem moglibyśmy balansowanieruchu ustawić na poziomie konfiguracji hosta dla serwera Apache, jednak naserwerarch Venema oraz Stallman nie mieliśmy praw dostępu do odpowiednichplików, co zmusiło nas do korzystania z Pounda.

Page 54: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

54

Pierwotnie chcieliśmy aby aplikacja była dostępna przy użyciu HTTPS, cowydawało się być nietrudnym zadaniem zważywszy na fakt, że Pound potrafidziałać jako front-end tego bezpiecznego protokołu. W trybie tym rozszyfrowywujeon przesłane do niego żądanie a następnie przesyła je do właściwego serweraWWW, który albo nie potrafi obsługiwać szyfrowania, albo nie chcemy go obciążaćtym zadaniem. Po wygenerowaniu testowego certyfikatu dla domeny i drobnychzmianach w pliku:

User "deploy"Group "users"ListenHTTPS Address 0.0.0.0 Port 3030 Cert "/home/rupert/.ssh/server.pem" Service BackEnd Address 127.0.0.1 Port 3040 End #Analogicznie dla portów 3037-2039 EndEnd

okazało się jednak, że rodzi to pewne problemy. Ponieważ połączenie pierwotnienawiązywane jest z serwerem Apache działającym na maszynie Venema(pamiętajmy, że Pound chodzi na Stallmanie), do którego konfiguracji nie mamydostępu, nie wie on nic o wystawionym dla witryny certyfikacie. Z tego powoduprzeglądarka użytkownika pokazuje bardzo groźny komunikat, że certyfikat dlaodwiedzanej strony (co-do-grosza.pl), jest nieprawidłowy, gdyż został wystawionydla innej domeny (na serwerze jest już skonfigurowany certyfikat dla rootnode.pl)i radzi jej opuszczenie. Użytkownik może w takiej sytuacji mimo wszystkozaakceptować certyfikat i dodać go do listy wyjątków, a następnie korzystać zestrony z użyciem szyfrowanego połączenia, jednak w praktyce nikt by się na takiepotencjalne niebezpieczeństwo nie zdecydował. Sprawa byłaby bardzo prosta,gdybyśmy dysponowali własnym serwerem i mogli na nim ustawić ścieżkę docertyfikatu. Ostatecznie nie zdecydowaliśmy się na korzystanie z HTTPS.

Mongrel

Z trzech możliwych do wyboru serwerów (mongrel, thin, ebb) wybraliśmy tenpierwszy, ponieważ jest on najdłużej na rynku i został przez ten czas gruntownieprzetestowany na wielu środowiskach produkcyjnych. Klaster 4 procesów możnauruchomić korzystając z wbudowanego w Rails skryptu spawner do tego:

app> script/process/spawner \ mongrel \ --environment=production \ --instances=4 \ --port=3037

Po uruchomieniu procesów ich numery zostaną zapisane w plikach wewnątrzkatalogu tmp aplikacji. Dzięki temu w późniejszym czasie, istnieje możliość bykorzystając ze skryptu reaper, wykonać jedną z czterech akcji:

Page 55: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

55

• restart - Powoduje ponowne uruchomienie aplikacji razem ze środowiskiemRuby on Rails.

• reload - Przeładowywuje na nowo aplikację, jednak bez całego środowiska RoR.

• graceful - Procesy zostaną zakończone po obsłużeniu aktualnie przetwarzanychżądań.

• kill - Kończy procesy nawet jeśli są w czasie przetwarzania żądania.

Przykładowo:

app> script/process/reaper -a graceful

Ten sposób uruchamiania i kończenia działania aplikacji w trybie produkcyjnymnie jest jednak wspierany przez Rails w wersji 2.3. W takim wypadku używasię polecenia mongrel_rails z odpowiednimi parametrami. Aby z jego pomocązarządzać klastrem procesów należy zainstalować odpowiednie gemy w systemie:mongrel oraz mongrel_cluster .

Capistrano

Capistrano zostało pierwotnie zaprojektowane do zautomatyzowania procesuwdrażania aplikacji railsowych. Sposobem działania przypomina programy makeoraz rake, w których definiujemy zadania i zależności między nimi a następniewykonujemy je w zależności od potrzeb, z tą różnicą, że Capistrano potrafiuruchamiać zadania na zdalnych komputerach korzystając z protokołu SSH. Tona jakim komputerze zadanie zostanie rozpoczęte, zależy od roli, do której jestprzypisane. Dzięki temu w sytuacji, gdy potrzeby stają się większe i przestajesię używać jednego serwera do wszystkich zadań (np. w sytuacji postawieniabazy danych na dedykowanym do tego serwerze), trzeba zmienić tylko plikkonfiguracyjny, by przypisać w nim role dla nowych maszyn.

Po pierwszym uruchomieniu aplikacji na serwerze produkcyjnym, wdrożenie jejkażdej następnej wersji jest bardzo proste i sprowadza się do wydania jednejkomendy (cap deploy:migrations ), która [21]:

• pobiera z repozytorium uaktualnioną wersję kodu na serwer aplikacji,

• wykonuje dostępne migracje na bazie danych,

• uaktualnia link wskazujący na katalog z wersją kodu do uruchomienia,

• zatrzymuje i uruchamia serwer aplikacji by zaczął używać najnowszej kopiioprogramowania.

Capistrano umożliwia podpinanie dodatkowych zadań do wykonania na każdymz tych etapów (zarówno przed jak i po). Korzystając z tej możliwości rozszerzyliśmyopisany proces automatycznego wdrażania nowej wersji aplikacji Manage MyMoney o:

• pobieranie z innego repozytorium (niedostępnego publicznie) plikówkonfiguracyjnych,

• pobieranie dodatkowych rozszerzeń, które pozwalają na monitoring wdrożonejaplikacji,

Page 56: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wdrożenie

56

• zarządzanie dodatkowymi usługami potrzebnymi do prawidłowego działaniasystemu takimi jak silnik wyszukiwania pełnotekstowego oraz scheduler zadań,

• uruchamianie testów na serwerze aplikacji przed zastosowaniem najnowszegokodu,

• tworzenie kopi zapasowej bazy danych, indeksów oraz źródeł,

• zamianę niektórych elementów graficznych witryny, w celu wyeksponowaniajej adresu internetowego co-do-grosza.pl .

W przypadku, gdyby uruchomiona wersja była w jakiś sposób wadliwa, istniejemożliwość szybkiego powrotu do poprzedniej.

Page 57: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

57

Rozdział 8Wnioski

Ruby on Rails to narzędzie pracy, które już od dawna jest w stanie konkurowaćz innymi obecnymi na rynku technologiami takimi jak Php, Java, czy .Neti  dostępnymi na te języki platformami do tworzenia stron internetowych. Niestanowi oczywiście rozwiązania wszystkich problemów i w wielu sytuacjachlepszym wyjściem jest skorzystanie z innego oprogramowania (np. lepiejwpisującego się w istniejący ekosystem aplikacji klienta).

Przez długi czas słabym punktem Rails był skomplikowany system wdrażania,jednak od samego początku następuje w tej dziedzinie postęp. Zalecany aktualniemoduł Phusion Passenger cechuje się wyjątkową prostotą instalacji, konfiguracjioraz działania, dzięki czemu bariera wejścia obniżyła się jeszcze bardziej.

Początki pracy z Rails mogą jednak nie być łatwe dla niektórych osób, szczególnietakich, które wcześniej tworzyły oprogramowanie korzystając głównie z systemuWindows. Większość dokumentacji oraz książek od razu zakłada pewną znajomośćśrodowiska *nix a przykłady podawane są zwykle tylko dla Mac OS X oraz Linux.Osoba, która chce się zapoznać z RoR zmuszona jest nie tylko nauczyć się nowegojęzyka i frameworku ale także kompletnie innego zestawu narzędzi, cechującychsię diametralnie różną filozofią działania niż te znane z Windows. Powoli jednaknastępuje coraz większe otwarcie na tą grupę programistów, co naszym zdaniemjest niezbędne, aby technologia ta zdobyła większe uznanie i popularność biorącpod uwagę jak ogromna przepaść dzieli liczbę użytkowników wspomnianychsystemów operacyjnych.

Trudno wyrokować jaka będzie przyszłość Rails i czy zdobędzie sobie na tyleduże uznanie, by w większej mierze przeniknąć do środowiska korporacyjnego.Marketingowy sukces jaki osiągnął nie jest jeszcze jednoznaczny z powszechnymjego używaniem na całym świecie (w Europie przyjmuje się raczej dość wolno,w Polsce także daleko mu do pozycji lidera, chociaż z Rails skorzystano np.przy budowie serwisu blip.pl realizowanego przez spółkę GG Network, właścicielakomunikatora Gadu Gadu). Ostatnimi czasy sporo wspomina się w sieci nt. takichjęzyków jak Erlang, Groovy czy Scala (ma już nawet swój, bardzo przychylnierecenzowany framework webowy o nazwie Lift), które mają zdetronizować Javęi  zająć miejsce tego nieco przestarzałego języka [22]. Cały świat ponownieodkrywa możliwości jakie daje JavaScript i to nie tylko w głównej domenie, jakąjest osadzanie go na stronach internetowych, ale także do realizowania aplikacjidesktopowych. Nieustannie pojawiają się też języki zupełnie nowe. Jest więcw czym wybierać.

Skoro dostępnych jest coraz więcej języków o podobnych lub różnych założeniach,tym co może przesądzić o sukcesie jest możliwość ich integracji z istniejącymijuż na rynku rozwiązaniami. Nie od dziś bowiem wiadomo, że duże systemy(zatem takie, które najrzadziej są przepisywane z użyciem nowych technologii)rzadko kiedy działają samodzielnie, raczej w celu realizacji swoich zadańwspółpracują z  innymi systemami komputerowymi. Z tego powodu przewidujesię, że największe szanse będą miały języki wykonywane na platformie JVM,która uważana jest za bardzo stabilne i dopracowane rozwiązanie i coraz częściej

Page 58: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Wnioski

58

wykorzystuje się ją do uruchamiania kodu, nie napisanego wcale w Javie [22].Sporą szansą dla Rails jest tutaj projekt JRuby - interpreter języka Ruby napisanycałkowicie w Javie, który umożliwia współpracę obu tych języków i wzajemnekorzystanie z bibliotek. Cały czas rozwija się IronRuby, który miejmy nadzieję jużniedługo, pozwoli korzystać z Ruby'ego, a co za tym idzie z Rails, na platformie.Net.

Niniejsza praca ogólnie opisuje framework Ruby on Rails oraz w niewielkim stopniusam język Ruby. Mamy nadzieję, że już niedługo pojawią się opracowania, którychautorzy podejmą się bardziej szczegółowo opisać wybrane zagadnienia z tejdziedziny takie jak np. metaprogramowanie oraz optymalizacja.

Jako programiści na co dzień korzystający z ogromnej ilości oprogramowaniaopen-source, które daje nam pracę i radość, postanowiliśmy udostępnić kodzrealizowanej przez nas aplikacji na bardzo liberalnej licencji BSD. Osoby chętnedo współpracy lub samodzielnego zainstalowania tego systemu zapraszamy nastronę: http://github.com/paneq/manage-my-money/ .

Page 59: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

59

Bibliografia

Książki[1] Sam Ruby, Dave Thomas, i David Heinemeier Hansson. Copyright © 2009 The

Pragmatic Programmers, LLC. 978-1-9343561-6-6. Agile Web Developmentwith Rails. Third Edition.

[2] Jarosław Zabiełło. Copyright © 2009 Helion. 978-83-246-0631-3. Ruby on Rails2.1. Tworzenie nowoczesnych aplikacji internetowych.

[3] Dave Thomas, Chad Fowler, i Andy Hunt. Copyright © 2005 The PragmaticProgrammers, LLC. Copyright © 2007 Helion (Polish Edition). 978-83-246-0522-4. Programowanie w języku Ruby. Wydanie II.

[4] Kent Beck. Copyright © 2002 Addison-Wesley Professional. 978-0321146533. Addison-Wesley Professional. Test Driven Development: By Example.

Internet[5] Scott Ambler. Introduction to Test Driven Design (TDD) [http://www.agiledata.org/

essays/tdd.html].

[6] Robert C. Martin. The Three Laws of TDD [http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd].

[7] Dror Helper. The Cost of Test Driven Development [http://blog.typemock.com/2009/03/cost-of-test-driven-development.html].

[8] Mike Clark. Test-Driven Development with JUnit Workshop [http://clarkware.com/courses/TDDWithJUnit.html].

[9] Noel Llopis. Test-Driven Game Development (Part 1) [http://www.gamesfromwithin.com/articles/0502/000073.html].

[10] Padraic Brady. Designing Klingon Warships Using Behaviour Driven Development[http://devzone.zend.com/article/3082].

[11] Dan North. Introducing BDD [http://dannorth.net/introducing-bdd].

[12] Test::Unit - Ruby Unit Testing Framework [http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html].

[13] Christoph Olszowka. The Ruby Toolbox: Know your options [http://ruby-toolbox.com/].

[14] Aslak Hellesøy. Cucumber wiki [http://wiki.github.com/aslakhellesoy/cucumber/ruby-on-rails].

[15] SeleniumHQ [http://seleniumhq.org/].

Page 60: Tworzenie i wdrażanie aplikacji internetowych na platformie Ruby on Rails / Autorzy: Robert Pankowecki oraz Jarosław Plebański

Bibliografia

60

[16] D. Robinson i K. Coar. The Common Gateway Interface (CGI) Version 1.1 [http://www.ietf.org/rfc/rfc3875.txt].

[17] Phusion. Phusion Passenger users guide, Nginx version [http://www.modrails.com/documentation/Users%20guide%20Nginx.html].

[18] Netcraft. May 2009 Web Server Survey [http://news.netcraft.com/archives/2009/05/27/may_2009_web_server_survey.html].

[19] Hongli Lai. Passenger and other Ruby frameworks [http://izumi.plan99.net/blog/index.php/2008/03/27/passenger-and-other-ruby-frameworks/].

[20] Werner Schuster. Phusion Passenger/mod_rails makes Rails deployment easy[http://www.infoq.com/news/2008/04/phusion-passenger-mod-rails-gc].

[21] Laurie Young. Capistrano Task Graph [http://wildfalcon.com/archives/2008/09/22/].

[22] Jarosław Zabiełło. Scala - język przyszłości [http://blog.zabiello.com/2009/03/28/scala-lang].