O slideshow foi denunciado.

Master Thesis - Comparative analysis of programming Environments based on Ruby and JavaScript.

1

Compartilhar

Carregando em…3
×
1 de 110
1 de 110

Master Thesis - Comparative analysis of programming Environments based on Ruby and JavaScript.

1

Compartilhar

Baixar para ler offline

In this thesis, we analyzed technologies for creating web applications, using Ruby and JavaScript. Were chosen two tools for creating frontend and three responsible for the backend. The main emphasis has been on a comparison of selected tools. The preface provides background information to the problem, presents the purpose and division of work. This also explains the motivation to take the topic of work and arrangement of chapters. The theoretical introduction describes the essential issues of web application architecture, the understanding of which is crucial for the realization of the theme. Description of the technology is an important chapter, which describes the selected libraries. These chapters lead to the multivariate analysis. This chapter is a comparison of selected tools, aimed to identify the best in each category. At the end, there is a summary of the work carried out, the conclusions of the study, as well as a subjective assessment of examined technologies. It also takes a polemic about the future direction of web applications.

In this thesis, we analyzed technologies for creating web applications, using Ruby and JavaScript. Were chosen two tools for creating frontend and three responsible for the backend. The main emphasis has been on a comparison of selected tools. The preface provides background information to the problem, presents the purpose and division of work. This also explains the motivation to take the topic of work and arrangement of chapters. The theoretical introduction describes the essential issues of web application architecture, the understanding of which is crucial for the realization of the theme. Description of the technology is an important chapter, which describes the selected libraries. These chapters lead to the multivariate analysis. This chapter is a comparison of selected tools, aimed to identify the best in each category. At the end, there is a summary of the work carried out, the conclusions of the study, as well as a subjective assessment of examined technologies. It also takes a polemic about the future direction of web applications.

Mais Conteúdo rRelacionado

Livros relacionados

Gratuito durante 14 dias do Scribd

Ver tudo

Audiolivros relacionados

Gratuito durante 14 dias do Scribd

Ver tudo

Master Thesis - Comparative analysis of programming Environments based on Ruby and JavaScript.

  1. 1. POLITECHNIKA POZNAŃSKA WYDZIAŁ ELEKTRYCZNY PRACA DYPLOMOWA MAGISTERSKA ANALIZA PORÓWNAWCZA ŚRODOWISK PROGRAMISTYCZNYCH OPARTYCH NA JĘZYKACH RUBY I JAVASCRIPT Adam SKOŁUDA Damian ROMANÓW Promotor: dr inż. Anna Grocholewska-Czuryło Poznań, 2015
  2. 2. Streszczenie Analiza porównawcza środowisk programistycznych opartych na językach Ruby i Java- Script. W niniejszej pracy poddano analizie technologie do tworzenia aplikacji internetowych wykorzystujące Ruby i JavaScript. Dwa narzędzia dotyczące tworzenia części klienckich oraz trzy odpowiedzialne za logikę serwerową. Główny nacisk położony został na porów- nanie wybranych narzędzi. Wstęp dostarcza informacji wprowadzających do zagadnienia, a także przedstawia cel i podział pracy. Tłumaczy również motywację podjęcia tematu pracy oraz układ rozdziałów. Wprowadzanie teoretyczne opisuje niezbędne kwestie archi- tektury aplikacji internetowych, których zrozumienie jest kluczowe dla realizacji tematu. Opis technologii to obszerny i ważny rozdział przybliżający wybrane biblioteki. Powyższe rozdziały prowadzą do wielowymiarowej analizy. Rozdział ten jest porównaniem wybra- nych narzędzi, mającym na celu wskazanie faworytów w poszczególnych kategoriach. W zakończeniu znajduje się podsumowanie przeprowadzonych prac, wnioski płynące z ba- dań, a także subiektywna ocena badanych technologii. Podejmuje również polemikę na temat przyszłego kierunku rozwoju aplikacji internetowych.
  3. 3. Abstract Comparative analysis of development environments based on Ruby and JavaScript. In this thesis, we analyzed technologies for creating web applications, using Ruby and JavaScript. Were chosen two tools for creating frontend and three responsible for the backend. The main emphasis has been on comparison of selected tools. Preface provides background information to the problem, presents the purpose and division of work. This also explains the motivation to take the topic of work and arrangement of chapters. Theoretical introduction describes the essential issues of web application architecture, the understanding of which is crucial for the realization of the theme. Description of the technology is a important chapter, which describes the selected libraries. These chapters lead to the multivariate analysis. This chapter is a comparison of selected tools, aimed to identify the best in each category. At the end, there is a summary of the work carried out, the conclusions of the study, as well as a subjective assessment of examined technologies. It also takes a polemic about the future direction of web applications.
  4. 4. Spis treści Spis treści 7 Spis rysunków 10 Spis listingów 12 1 Wstęp 15 1.1 Motywacje podjęcia tematu . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.2 Podział prac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.3 Cel pracy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.4 Układ pracy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2 Wprowadzenie teorytyczne 19 2.1 REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.2 AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3 SPA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.4 Model - View - Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.5 Języki . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.5.1 Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.5.2 JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.6 Środowiska programistyczne . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.6.1 Ekosystem Rubiego . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.6.2 Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 2.7 Bezpieczeństwo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 2.7.1 Uwierzytelnianie . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2.7.2 Autoryzacja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3 Opis technologii 45 3.1 ReactJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.1.1 Deklaratywny charakter widoków . . . . . . . . . . . . . . . . . . . 46 7
  5. 5. SPIS TREŚCI 8 3.1.2 Props . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.1.3 State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 3.1.4 Flux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 3.1.5 Virtual DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.1.6 Diff Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.1.7 JSX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.1.8 Backbone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.2 AngularJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 3.2.1 Kompilator HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.2.2 Dwustronne wiązanie danych . . . . . . . . . . . . . . . . . . . . . 55 3.2.3 Obiekt $scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.2.4 Dziedziczenie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 3.2.5 Metody $digest(), $apply() i $watch() . . . . . . . . . . . . . . . . . 59 3.2.6 Modularność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 3.2.7 Wstrzykiwanie zależności . . . . . . . . . . . . . . . . . . . . . . . . 63 3.2.8 Dyrektywy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 3.2.9 Komunikacja z serwerem . . . . . . . . . . . . . . . . . . . . . . . . 65 3.3 Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 3.3.1 MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.3.2 DRY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 3.3.3 Generatory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 3.3.4 Struktura projektu . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 3.3.5 Konwencja ponad konfiguracją . . . . . . . . . . . . . . . . . . . . . 75 3.3.6 TDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 3.3.7 Aktywna społeczność . . . . . . . . . . . . . . . . . . . . . . . . . . 78 3.4 Sinatra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 3.4.1 Elastyczność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.4.2 Wydajność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.4.3 Prostota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 3.5 Grape . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 3.5.1 Elastyczność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 3.5.2 Wydajność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 3.5.3 Prostota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 4 Analiza 87 4.1 AngularJS - ReactJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
  6. 6. SPIS TREŚCI 9 4.1.1 Próg wejścia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.1.2 Komplementarność . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.1.3 Poziom ekspresji kodu . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.1.4 Wydajność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.1.5 Debuggowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.1.6 Przepływ danych . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.1.7 Szablony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 4.1.8 Społeczność i popularność . . . . . . . . . . . . . . . . . . . . . . . 96 4.1.9 Podsumowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 4.2 Rails - Sinatra - Grape . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.2.1 Wydajność . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 4.2.2 Społeczność i popularność . . . . . . . . . . . . . . . . . . . . . . . 100 4.2.3 Podsumowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 5 Zakończenie 103 Bibliografia 107 Wykaz skrótów 108
  7. 7. Spis rysunków 2.1 Porównanie ilości ofert pracy dla popularnych języków serwerowych, [źró- dło: http://www.indeed.com/jobtrends]. . . . . . . . . . . . . . . . . . . 29 2.2 Wykres pokazujący ilość ofert pracy dla języków JavaScript, C++, C#, PHP oraz Python, [źródło: http://www.indeed.com/jobtrends]. . . . . . 30 2.3 Przykład nieblokującej operacji wejścia-wyjścia wykonywanej przez Node, [źródło: [3]]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 2.4 Porównanie ilości ofert pracy dla popularnych platform serwerowych, [źró- dło: http://www.indeed.com/jobtrends]. . . . . . . . . . . . . . . . . . . 40 2.5 Dostęp do Internetu stacjonarnego w Polsce, [źródło: http://pclab.pl/ news63802.html]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.1 Diagram obrazujący przepływ sterowania w architekturze Flux, [źródło: https://github.com/facebook/flux]. . . . . . . . . . . . . . . . . . . . . 49 3.2 Porównanie ilości ofert pracy dla popularnych platform klienckich typu MVC, [źródło: http://www.indeed.com/jobtrends]. . . . . . . . . . . . . 55 3.3 Jednostronne wiązanie danych w AngularJS, [źródło: https://docs.angularjs. org/guide/databinding]. . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.4 Dwustronne wiązanie danych w AngularJS, [źródło: https://docs.angularjs. org/guide/databinding]. . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.5 Wykres pokazujący dominację platformy Rails, pod względem ilości ofert pracy, [źródło: http://www.indeed.com/jobtrends]. . . . . . . . . . . . . 68 3.6 Komunikat informujący o wynikach testów Guard. . . . . . . . . . . . . . . 77 3.7 Statystyki pobrań najpopularniejszych gemów ze strony https://rubygems. org/. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 3.8 Statystyki kontrybucji do repozytorium rails z serwisu GitHub, [źródło: https://github.com/rails/rails/graphs/contributors]. . . . . . . . . 80 3.9 Liczba kontrybucji, gałęzi, wydań oraz wkładców w repozytorium rails z serwisu GitHub, [źródło: https://github.com/rails/rails/]. . . . . . . 80 10
  8. 8. SPIS RYSUNKÓW 11 3.10 Liczba obserwatorów, gwiazdek oraz rozgałęzień repozytorium Rails z ser- wisu GitHub, [źródło: https://github.com/rails/rails/]. . . . . . . . . 80 4.1 Testy renderowania obiektów z wykorzystaniem: AngularJS, ReactJS - ska- la liniowa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 4.2 Testy renderowania obiektów z wykorzystaniem: AngularJS, ReactJS - ska- la logarytmiczna. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 4.3 Porównanie czasu operacji na liście 100 zadań do zrobienia w ms, [źródło: http://evancz.github.io/todomvc-perf-comparison/]. . . . . . . . . . 94 4.4 Problem z dwustronnym wiązaniem danych w AngularJS, [źródło: http:// techblog.constantcontact.com/software-development/reactive-component- based-uis/]. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.5 Rozwiązanie problemu z wiązaniem danych w ReactJS, [źródło: http:// survivejs.com/webpack_react/react_and_flux/]. . . . . . . . . . . . . 95 4.6 Statystyki dotyczące AngularJS ze strony https://github.com/angular/ angular.js/. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 4.7 Statystyki dotyczące ReactJS ze strony https://github.com/facebook/ react. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 4.8 Porównanie ilości wyszukiwań w Google. . . . . . . . . . . . . . . . . . . . 97 4.9 Porównanie wydajności Rails, Grape oraz Sinatra pod względem ilości ob- służonych żądań na minutę. . . . . . . . . . . . . . . . . . . . . . . . . . . 99 4.10 Porównanie wydajności Rails, Grape oraz Sinatra pod względem średniego czasu dla jednego zapytania. . . . . . . . . . . . . . . . . . . . . . . . . . . 99 4.11 Porównanie ilości pobrań ze strony https://rubygems.org/. . . . . . . . . 101
  9. 9. Spis listingow: 2.1 Przykład kodu jQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.2 Przykład kodu czysty Javascript . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3 Elastyczność języka Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.4 Obiektowość języka Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.5 Funkcyjność języka Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.6 Typy danych w języku JavaScript . . . . . . . . . . . . . . . . . . . . . . . 30 2.7 Komentarz blokowy w języku JavaScript . . . . . . . . . . . . . . . . . . . 30 2.8 Komentarz liniowy w języku JavaScript . . . . . . . . . . . . . . . . . . . . 31 2.9 Instrukcja if w języku JavaScript . . . . . . . . . . . . . . . . . . . . . . . 31 2.10 Pęta while w języku JavaScript . . . . . . . . . . . . . . . . . . . . . . . . 31 2.11 Pętla do...while w języku JavaScript . . . . . . . . . . . . . . . . . . . . . . 31 2.12 Pętla for w języku JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.13 Pętla for...in oraz for...of w języku JavaScript . . . . . . . . . . . . . . . . 31 2.14 Instrukcja switch w języku JavaScript . . . . . . . . . . . . . . . . . . . . . 31 2.15 Notacja kropkowa wywołania metody w języku JavaScript . . . . . . . . . 32 2.16 Notacja z nawiasami kwadratowymi wywołania metody w języku JavaScript 32 2.17 Funkcja konstruktora w języku JavaScript . . . . . . . . . . . . . . . . . . 32 2.18 Tworzenie instancji klasy Obiekt w języku JavaScript . . . . . . . . . . . . 33 2.19 Definicja funkcji w języku JavaScript . . . . . . . . . . . . . . . . . . . . . 33 2.20 Dziedziczenie w języku JavaScript . . . . . . . . . . . . . . . . . . . . . . . 33 2.21 Przkład instalacji gema ”Devise” . . . . . . . . . . . . . . . . . . . . . . . 34 2.22 Zmiana wersji Rubiego na 2.2.3 . . . . . . . . . . . . . . . . . . . . . . . . 35 2.23 Przykładowy Gemfile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.24 Przykładowy Gemfile.lock . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.25 ”Hello World” w irb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.26 Zmiana hasła klienta o id 1 . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.27 Przykład modelu ”Post” i zabezpieczonego kontrolera . . . . . . . . . . . . 43 2.28 Przykład zabezpieczenia metody ”update” kontrolera . . . . . . . . . . . . 43 12
  10. 10. SPIS LISTINGOW: 13 2.29 Przykład definicji uprawnień w bibliotece ”cancancan” . . . . . . . . . . . 44 3.1 Przykład funkcji ”render” dla elementu listy rzeczy do zrobienia . . . . . . 46 3.2 Przekazanie funkcji do subkomponentu . . . . . . . . . . . . . . . . . . . . 47 3.3 Ustawienie początkowego stanu komponentu w ReactJS . . . . . . . . . . . 48 3.4 Zmiana stanu komponentu w ReactJS . . . . . . . . . . . . . . . . . . . . . 48 3.5 Część kodu obiektu Dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.6 Operacje na DOM przykład . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.7 React transformacja węzłów różnych typów . . . . . . . . . . . . . . . . . . 52 3.8 React transformacja węzłów o takich samych typach . . . . . . . . . . . . . 52 3.9 Porównanie budowy szablonów z użyciem JSX i bez . . . . . . . . . . . . . 53 3.10 Przypisywanie atrybutów i funkcji w AngularJS . . . . . . . . . . . . . . . 58 3.11 Dziedziczenie realizowane w AngularJS . . . . . . . . . . . . . . . . . . . . 59 3.12 Kontroler ParentController . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.13 Kontroler ChildController . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.14 Przykład wykorzystania metody $watch . . . . . . . . . . . . . . . . . . . 60 3.15 Przykład wykorzystania metoty $apply . . . . . . . . . . . . . . . . . . . . 61 3.16 Przykład deklaracji modułu w AngularJS . . . . . . . . . . . . . . . . . . . 62 3.17 Plik controller.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 3.18 Szkielet aplikacji w AngularJS . . . . . . . . . . . . . . . . . . . . . . . . . 62 3.19 Przykłady wstrzykiwania zależności w AngularJS . . . . . . . . . . . . . . 63 3.20 Przykłady własnej dyrektywy w AngularJS . . . . . . . . . . . . . . . . . . 64 3.21 Pole konfiguracji dyrektywy restrict E w AngularJS . . . . . . . . . . . . . 65 3.22 Pole konfiguracji dyrektywy restrict A w AngularJS . . . . . . . . . . . . . 65 3.23 Pole konfiguracji dyrektywy restrict C w AngularJS . . . . . . . . . . . . . 65 3.24 Pole konfiguracji dyrektywy restrict M w AngularJS . . . . . . . . . . . . . 65 3.25 Przykładowe zapytanie GET z wykorzystaniem metody $http w AngularJS 65 3.26 Przykładowe zapytanie POST z wykorzystaniem metody $http w AngularJS 66 3.27 Przykład użycia ”scope” w modelu Rails. . . . . . . . . . . . . . . . . . . . 69 3.28 Przykład walidacji formatu adresu email. . . . . . . . . . . . . . . . . . . . 69 3.29 Przykład helpera zamieniającego liczbę na walutę. . . . . . . . . . . . . . . 69 3.30 Przykład użycia ”before action” do uwierzytelnienia użytkownika. . . . . . 70 3.31 Model wpisu oraz polubienia . . . . . . . . . . . . . . . . . . . . . . . . . . 71 3.32 Modele wpisu, wydarzenia i polubienia wraz z wyekstrahowanym modułem 71 3.33 Przykład wykorzystania generatora w Rails . . . . . . . . . . . . . . . . . . 72 3.34 Przykład definicji ścieżek dla nowego obiektu . . . . . . . . . . . . . . . . . 75 3.35 Przykład utworzonych przez Rails ścieżek API dla operacji CRUD . . . . . 75
  11. 11. SPIS LISTINGOW: 14 3.36 Przykład testu modelu w bibliotece RSpec . . . . . . . . . . . . . . . . . . 76 3.37 Przykład ”fabryki” klientów w bibliotece Factory Girl . . . . . . . . . . . . 77 3.38 Przykład użycia biblioteki ”faker” . . . . . . . . . . . . . . . . . . . . . . . 78 3.39 Aplikacja ”Hello World” w Sinatrze . . . . . . . . . . . . . . . . . . . . . . 80 3.40 Obsługa żądania POST pod adresem ”/todos” . . . . . . . . . . . . . . . . 81 3.41 Aplikacja ”Hello World” w Grape . . . . . . . . . . . . . . . . . . . . . . . 84 3.42 Operacje CRUD w Grape . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.1 Przykład ilustrujący ”boilerplate code” w AngularJS - HTML . . . . . . . 88 4.2 Przykład ilustrujący ”boilerplate code” w ReactJS - HTML . . . . . . . . 89 4.3 Przykład ilustrujący ”boilerplate code” w AngularJS - JavaScript . . . . . 89 4.4 Przykład ilustrujący ”boilerplate code” w ReactJS - JavaScript . . . . . . . 91 4.5 Przykład szablonu listy w ReactJS . . . . . . . . . . . . . . . . . . . . . . 94 4.6 Przykład szablonu listy w AngularJS . . . . . . . . . . . . . . . . . . . . . 96
  12. 12. Rozdział 1 Wstęp Internet jako wynalazek bez wątpienia stawiany jest w jednym szeregu z powstaniem takich innowacji jak wynalezienie elektryczności, druku czy też maszyny parowej. Każdy z tych patentów usprawniał w znaczący sposób proces wykonywania pracy. Nie inaczej jest w przypadku Internetu. W dobie powszechnej informatyzacji bardzo mocno rozwija się rynek aplikacji internetowych, których w większości głównym zadaniem jest wspo- maganie procesu wymiany i przetwarzania danych pomiędzy użytkownikami. W związ- ku z tym powstało wiele różnych metod, architektur, języków programowania, a także sposobów tworzenia interfejsów użytkownika. Wszystko po to, aby proces korzystania z aplikacji internetowej był jak najbardziej intuicyjny, wygodny, szybki dla użytkownika. Współcześni klienci firm tworzących oprogramowanie stawiają duże wymagania. Aplika- cja w większości przypadków musi być intuicyjna, musi podpowiadać użytkownikowi co ma zrobić, gdy ten się zgubi, powinna przechowywać dane o każdej czynności użytkow- nika, powinna mieć dynamiczny charakter, powinna przetwarzać dane w locie, dostęp do danych musi być zapewniony w każdym momencie a na dodatek aplikacja powinna działać tak samo dobrze na każdym urządzeniu i platformie. Te i inne wymagania wymusiły na programistach i firmach tworzących oprogramowanie, podejście do tematu aplikacji inter- netowych w różny sposób. Dzięki temu, aby ułatwić proces wytwarzania oprogramowania, które spełniałoby wygórowane wymagania klientów, powstało wiele różnych koncepcji, z których przetrwały tylko te które miały zasadność istnienia. Natomiast każdy sposób re- alizacji oprogramowania wiąże się także z odpowiednim doborem technologii do wymagań dla oprogramowania. Przykładem takiej koncepcji tworzenia oprogramowania mogą być bardzo popularne ostatnio aplikacje typu Single Page Application, które mają wyraźny podział na warstwę kliencką i serwerową. Posiadają one bardzo mocno rozbudowaną war- stwę prezentacji i odpytują tylko serwer celem pobrania danych do wyświetlenia. W tego typu aplikacjach deweloperzy mają do wyboru wiele różnych technologi do implementacji 15
  13. 13. Rozdział 1. Wstęp 16 zarówno części serwerowych np. Ruby on Rails, Python i Django lub PHP i Laravel, a także części klienckich np. Angular, Ember, React czy też Backbone. 1.1 Motywacje podjęcia tematu Aktualne trendy na rynku aplikacji internetowych skłoniły nas do zainteresowania się najczęściej wykorzystywanymi technologiami do ich realizacji. W dzisiejszym świecie, aby być konkurencyjnym, wiele firm skazanych jest na posiadanie własnych systemów infor- matycznych. Przekłada się to na stosunkowo dużą liczbę ofert pracy dla programistów w różnych technologiach oraz na różnych poziomach zaawansowania. Dlatego też posiadanie wiedzy i umiejętności pozwalających realizować tego typu systemy zwiększa atrakcyj- ność programisty na rynku pracy. Kolejnym powodem, który skłonił nas do wybrania realizowanego tematu, był fakt, iż chcieliśmy rozpoznać najpopularniejsze narzędzia do implementacji części serwerowych i klienckich aplikacji sieciowych, poznać ich zalety i wady, wyrobić sobie opinię na temat każdego z nich i wybrać najlepszy naszym zdaniem zestaw bibliotek, który mógłby w przyszłości być przez nas wykorzystany do realizacji komercyjnego systemu informatycznego. W związku z tym, że podczas przebiegu studiów mieliśmy okazję poznać wystarczające spektrum języków programowania, podjęliśmy de- cyzję o wyborze języka Ruby i wokół tego języka zostały dobrane biblioteki w przypadku części serwerowych. Natomiast jeżeli chodzi o wybór języka do implementacji warstwy prezentacji aplikacji, to wyboru w zasadzie nie ma i musi być to język JavaScript i to dla niego wybrane zostały najpopularniejsze biblioteki wspomagające tworzenie bogatych aplikacji internetowych. 1.2 Podział prac Damian Romanów Adam Skołuda 1 Wstęp 2 Wprowadzenie teorytyczne 2.1 REST 2.2 AJAX 2.3 SPA 2.4 Model - View - Controller 2.5 Języki 2.5.1 Ruby
  14. 14. Rozdział 1. Wstęp 17 2.5.2 JavaScript 2.6 Środowiska programistyczne 2.6.1 Ekosystem Rubiego 2.6.2 Node 2.7 Bezpieczeństwo 2.7.1 Uwierzytelnianie 2.7.2 Autoryzacja 3 Opis technologii 3.1 ReactJS 3.2 AngularJS 3.3 Rails 3.3.1 MVC 3.3.2 DRY 3.3.3 Generatory 3.3.4 Struktura projektu 3.3.5 Konwencja ponad konfiguracją 3.3.6 TDD 3.3.7 Aktywna społeczność 3.4 Sinatra 3.5 Grape 4 Analiza 4.1 AngularJS - ReactJS 4.1.1 Próg wejścia 4.1.2 Komplementarność 4.1.3 Poziom ekspresji kodu 4.1.4 Wydajność 4.1.5 Debuggowanie 4.1.6 Przepływ danych 4.1.7 Szablony 4.1.8 Społeczność i popularność 4.1.9 Podsumowanie 4.2 Rails - Sinatra - Grap 4.2.2 Wydajność 4.2.3 Społeczność i popularność 4.2.4 Podsumowanie 4.2.4 Podsumowanie
  15. 15. Rozdział 1. Wstęp 18 5 Zakończenie 1.3 Cel pracy Głównym celem niniejszej pracy była analiza bibliotek do tworzenia aplikacji interne- towych w środowiskach Ruby i JavaScript. Brane były pod uwagę narzędzia do tworzenia zarówno części klienckiej, jak i serwerowej. Podejmowany temat wymagał odpowiedniego zaznajomienia się z wybranymi technologiami. Analiza została oparta przede wszystkim o badanie zaimplementowanych aplikacji do tworzenia listy zadań. Dodatkowo, spostrze- żenia i konkluzje autorów pracy zostały skonfrontowane z zewnętrznymi źródłami, co pozwoliło szerzej spojrzeć na przedstawiane zagadnienie. 1.4 Układ pracy Praca została podzielona na rozdziały. Każdy z nich opisuje część rozważań dotyczą- cych analizowanych technologii. Struktura rozdziałów została zdefiniowana w taki sposób, aby praca miała logiczny i chronologiczny układ. Wprowadzenie teoretyczne przedstawia podstawowe informację na temat architektury aplikacji internetowych. Opis technologii ma na celu szczegółowe przedstawienie wybranych narzędzi i rozwiązań. Rozdział ”Anali- za” zawiera rozważania dotyczące każdej z wykorzystanych technologii. Rozważania te do- tyczą samej implementacji, a także porównania pod kątem architektury i bezpieczeństwa. Zawarte są w nim również wyniki przeprowadzonych prób wydajnościowych poszczegól- nych bibliotek. Rozdział ”Zakończenie” jest bardzo subiektywnym spojrzeniem na każde z analizowanych rozwiązań technologicznych oraz wybór naszym zdaniem najlepszej z punktu widzenia programisty. Zakończenie jest podsumowaniem całej pracy, zawiera spis zrealizowanych zadań, napotkane trudności oraz próbę prognozowania dalszego rozwoju badanych technologii.
  16. 16. Rozdział 2 Wprowadzenie teorytyczne Niniejsza praca ma na celu analizę technologii tworzenia aplikacji internetowych (web application). W poprzednim rozdziale zostało wspomniane, że teraźniejszy użytkownik oczekuje od nich wiele. Należałoby zatem zdefiniować pojęcie ”aplikacja internetowa”, czym różni się od strony internetowej i czy w ogóle się od niej różni. Do zdefiniowania powyższych wymagane będzie przedstawienie kilku innych technologii. Internet nie był- by tym, czym jest obecnie bez HTTP (Hypertext Transfer Protocol) - protokołu typu żądanie-odpowiedź działającego w architekturze klient-serwer. Rolę serwera odgrywają programy osadzone na publicznie dostępnym węźle sieciowym. Ogromną zaletą jest tutaj dowolność w wyborze języka implementacyjnego. Może to być C, C++, C# czy Java. Programista może wybrać jeden z języków predestynowanych do tego typu zastosowań (ze względu na samą składnię czy też zestawy bibliotek ułatwiających tworzenie serwe- rów) np. PHP, Python, Scala czy też omawiany szerzej w niniejszej pracy Ruby. Bar- dziej niekonwencjonalne, badawcze podejście doprowadziło nawet do powstania serwerów w Prologu [27] czy Assemblerze [26]. Jedynym wymogiem jest zgodność ze standardem HTTP [25]. Specyfikacja HTTP obejmuje kilka metod, z czego najważniejsze z nich to GET, POST, PUT, DELETE. Architektura usług sieciowych bardzo często, a w wręcz powinna opierać się na REST (Representational State Transfer). Kolejnym elementem układanki jest język do opisu struktury dokumentów (stron) w Internecie - HTML (Hy- pertext Markup Language) [24]. Znaczniki HTML są nośnikami dla tekstów, obrazków czy hiperłączy. Dokument HTML jest odpowiedzią serwera na zapytanie klienta. Klientem jest zazwyczaj przeglądarka internetowa, parsująca odpowiedź z serwera i wyświetlająca ją użytkownikowi. Jak się okazało, te dwie proste technologie stworzyły duet, który prze- trwał próbę czasu i położył podwaliny pod dzisiejszy kształt aplikacji internetowych. Czy zatem każda strona internetowa jest aplikacją internetową? Odpowiedź na to pytanie nie jest oczywista i jednoznaczna. Pomocne może okazać się odwołane do klasycznych aplika- 19
  17. 17. Rozdział 2. Wprowadzenie teorytyczne 20 cji - np. okienkowych programów na komputery PC. Aplikacja służy do realizacji celu, jest narzędziem, dzięki któremu użytkownik może zrealizować swoje potrzeby. Np. program graficzny pozwala nam na obróbkę czy retusz zdjęć. Czy sam folder ze zdjęciami nazwa- libyśmy narzędziem, który realizuje jakiś cel? Oczywiście pozwala na ich wyświetlanie. Nie jest to jednak twórcze działanie, a jedynie sama prezentacja treści. Można powie- dzieć, że wspólnym mianownikiem wszystkich aplikacji jest możliwość kreacji. Zatem czy strona internetowa prezentująca zawsze taką samą zawartość jest aplikacją? W świetle powyższej argumentacji z pewnością nie. Przykładami aplikacji internetowych mogą być Facebook, dokumenty Google czy Twitter - narzędzia umożliwiające użytkownikowi twór- cze działanie. Ze względu na swoją budowę aplikację internetową można nazwać również stroną internetową - jednak w obecnych czasach jest to spore uproszczenie i spłycenie jej funkcjonalności. 2.1 REST Protokół HTTP jest wystarczający do tworzenia serwerów dostarczających treści (za- sobów). Przy małych aplikacjach ścieżki dostępu do zasobów (URI) można definiować w zasadzie dowolnie. Złożone systemy wymuszają jednak stosowanie pewnych konwencji. Brak wzorców i konwencji jest jedną z przyczyn regresji. W świecie usług sieciowych liczą się dwa wzorce czy też style tworzenia systemów - SOAP (Simple Object Access Protocol) oraz REST (Representional State Transfer). SOAP opiera się na RPC, jest raczej wy- korzystywany do komunikacji komputer-komputer, zachodzi tutaj silne wiązanie serwera z klientem. Jest to właściwie protokół. REST, to natomiast to wzorzec architektural- ny do projektowania złożonych, skalowalnych i wydajnych usług sieciowych. Jest bardzo elastyczny, o ile jest właściwie zaimplementowany. Opiera się na zasobach, standaryzuje sposób dostępu do nich. Żeby można było mówić o usłudze sieciowej RESTful konieczne jest spełnienie wszystkich czterech głównych reguł: • Model klient-serwer Polega na podziale odpowiedzialności między tymi dwoma bytami. Klient nie po- siada informacji o sposobie przechowywania zasobów na serwerze. Nie jest dla nie- go ważne czy jest to baza danych, zwykły plik czy też inna usługa sieciowa. Nie jest ważny język programowania, użyte biblioteki czy sam sposób implementacji. Z drugiej strony serwer nie jest obciążony przechowywaniem stanu klienta, nie mu- si wiedzieć nic o interfejsie użytkownika. Elementem spajającym jest API. Można dowolnie zamieniać oba elementy, pod warunkiem utrzymywania jednolitego inter- fejsu. Cecha ta została wykorzystana w niniejszej pracy. Zaimplementowane zostały
  18. 18. Rozdział 2. Wprowadzenie teorytyczne 21 3 serwery HTTP i 2 klienty utrzymując niezmienne API. Pozwoliło to osiągnąć dużą niezależność poszczególnych elementów • Bezstanowość Jak zostało wspomniane wyżej, serwer nie przechowuje stanu czy sesji klienta. Zatem każde żądanie musi zawierać wszystkie informacje potrzebne do jego obsłużenia. Korzystając z Internetu, bardzo często użytkownik ulega złudzeniu, że jest inaczej. Wiele aplikacji oferuje możliwość logowania, a nawet zapamiętywania tego logowania przez dłuższy okres. Powyższe funkcjonalności nie naruszają jednak architektury REST. W momencie logowania serwer generuje token, który jest przesyłany do klienta i zapisywany w tzw. ”ciasteczku” (HTTP cookie). W kolejnych żądaniach legitymujący się nim klient jest uważany za zalogowanego. • Cacheability Cecha, która nie ma dobrego odpowiednika w języku polskim. Chodzi to tu o składo- wanie wyników poprzednich zapytań, aby umożliwić szybki do nich dostęp w przy- szłości. Z jednej strony umożliwia dostęp do niektórych zasobów niemal w czasie rzeczywistym, a z drugiej zmniejsza obciążenie samej sieci oraz serwera. • Wielowarstwowość Architektura REST nie wymaga, aby końcowym serwerem była jedna maszyna. Możliwe jest zastosowanie kilku warstw pośredniczących i sterowanie obciążeniem. Klient końcowy postrzega taki system w ten sam sposób jak jeden serwer. 2.2 AJAX Przez długi czas zachowanie stron internetowych było dość sekwencyjne. Użytkownik klikał w hiperłącze, żądanie było wysyłane do serwera. Po otrzymaniu odpowiedzi cała strona, a właściwie DOM (Document Object Model) były renderowane. Nie zawsze takie zachowanie jest jednak potrzebne i pożądane. Zajmuje ono dość sporo czasu oraz powodu- je specyficzne ”mignięcie” zawartości. Czasem zmianom ulegają bardzo niewielkie części całego dokumentu. Rozwiązaniem tego problemu jest technika AJAX (Asynchronous Ja- vaScript and XML) (w większości przypadków używa się jednak formatu JSON zamiast XML - mówi się wtedy o AJAJ). Dzięki niej komunikacja użytkownika z treścią w Inter- necie stała się bardziej dynamiczna, poprzez swoją asynchroniczność. Tym samym strony internetowe bardziej zbliżyły się do standardowych programów komputerowych. Warto w tym miejscu wspomnieć o bibliotece jQuery, która bardzo uprościła używanie AJAX.
  19. 19. Rozdział 2. Wprowadzenie teorytyczne 22 Przykład asynchronicznego zapytania GET w czystym JavaScripcie na listingu 2.1 oraz przy pomocy jQuery na 2.2. Listing 2.1: Przykład kodu jQuery 1 $.getJSON(’/my/url’, function(data) { 2 }); Listing 2.2: Przykład kodu czysty Javascript 1 var request = new XMLHttpRequest (); 2 request.open(’GET’, ’/my/url’, true); 3 4 request.onload = function () { 5 if (request.status >= 200 && request.status < 400) { 6 // Success! 7 var data = JSON.parse(request.responseText); 8 } else { 9 // We reached our target server , but it returned an error 10 11 } 12 }; 13 14 request.onerror = function () { 15 // There was a connection error of some sort 16 }; 17 request.send (); Wszystkie żadania asynchroniczne wykonywane w języku JavaScript należą do żądań XHR (XMLHttpRequest). Są nośnikiem obiektów o tej samej nazwie. AJAX posiada jednak pew- ne ograniczenia. Jego funkcjonowanie opiera się na języku JavaScript. Użytkownik może wyłączyć jego obsługę w przeglądarce, w takim przypadku AJAX będzie bezużyteczny. 2.3 SPA AJAX przyczynił się do sporej zmiany Internetu. Uatrakcyjnił interakcję użytkownika z aplikacją internetową, upodobnił ją do swoich ”desktopowych” pierwowzorów. Jeśli moż- na część danych pobrać z serwera w tle, na żądanie użytkownika lub wtedy kiedy będzie to konieczne, to dlaczego nie sprawić, aby cała aplikacja działała w ten sposób? SPA (Single- page application) to podzbiór aplikacji internetowych charakteryzujący się pojedynczym przeładowaniem strony, przy jej początkowym wyświetleniu. Dalsza komunikacja odbywa się za pomocą JavaScripti i żądań XHR.
  20. 20. Rozdział 2. Wprowadzenie teorytyczne 23 Zalety: • Rozdzielenie logiki serwera i klienta Standardowe serwery HTTP są odpowiedzialne również za tworzenie dokumentu HTML wraz z osadzonymi w nim zmiennymi, czy tworzenie formularzy. W przy- padku SPA zadania te zostają w całości przerzucone na język JavaScript. Serwer udostępnia tylko API, czyli szynę wymiany danych. Podejście takie przynosi wiele korzyści. Jako że są to tak naprawdę dwie osobne aplikacje - mogą być rozwija- ne przez niezależne zespoły. Logika biznesowa czy szczegóły działania procesów na serwerze są niewidoczne dla klienta. • Elastyczność i atrakcyjność interfejsu użytkownika HTML generowany serwerowo ma pewne ograniczenia. Interakcja z użytkownikiem opiera się na formularzach, a te przy skomplikowanych interakcjach stają się często wąskim gardłem. Problematyczna staje się implementacja funkcjonalności działają- cych ”na żywo”. Formularze mają swoje prawa, muszą zostać wysłane na serwer, obsłużone, wtedy klient dostaje odpowiedź. Wykorzystanie JavaScriptu daje możli- wość niemal natychmiastowego komunikowania o wprowadzonych zmianach, również wprowadzenia stosownych efektów wizualnych. Przykładem może być załadowanie zdjęcia profilowego na serwer. W standardowym formularzu HTML możemy wybrać zdjęcie, po czym widzimy ewentualnie jego nazwę. Dopiero po wysłaniu i przetwo- rzeniu, serwer jest w stanie pokazać nam wynik tego działania. Przy wykorzystaniu JS, widzimy natychmiastowo wybrany obrazek. Wiele aplikacji oferuje też możliwość jego przycięcia. Jest to funkcjonalność nieosiągalna dla zwykłych formularzy. • Wiele klientów Jako że serwer nie posiada żadnej wiedzy o kliencie, wprowadzanie kolejnych apli- kacji klienckich, w dowolnych technologiach nie pociąga za sobą żadnych zmian w kodzie serwera. W dzisiejszym świecie bardzo często się mówi o tzw. IoT (Internet of Things). Tworzenie aplikacji z wyraźnym rozdziałem serwera i klienta stwarza idealne warunki do rozszerzenia systemu o aplikacji mobilne. Jest to bardzo ważny aspekt z punktu widzenia świata biznesu. Obecnie bardzo często tworzy się całe eko- systemy powiązanych ze sobą aplikacji - włączając w to klienty www, czy mobilne np. Android, czy iOS. Wady: • Niedojrzałość JS Technologia tworzenia warstw klienckich aplikacji internetowych całkowicie opar-
  21. 21. Rozdział 2. Wprowadzenie teorytyczne 24 tych o język JavaScript jest stosunkowo nowym podejściem. Same frameworki JS- owe rozwijają się bardzo szybko. Z jednej strony jest to pozytywne zjawisko, rozwój zawsze jest dobry. Obserwując jednak ewolucję (czy raczej rewolucję) takich rozwią- zań jak AngularJS czy ReactJS, trudno pozbyć się wrażenia, że programista jest w pewnym sensie królikiem doświadczalnym, a jego produkt poligonem ćwiczebnym. Jak wiadomo w świecie technologii rok to bardzo dużo czasu, wiele rzeczy może się zmienić. W świecie JavaScriptu standardy mogą zmienić się w tydzień. I mowa tutaj o fundamentalnych kwestiach architekturalnych, a nie kosmetycznych poprawkach. Brakuje nienaruszalnych zasad i standardów, na których można oprzeć budowanie aplikacji. Te wszystkie względy czynią użycie wspomnianych rozwiązań w poważ- nych, komercyjnych aplikacjach co najmniej ryzykownym. Technologie nastawione raczej na back-end (np. Ruby on Rails, PHP Symphony) istnieją o wiele dłużej. Ich wybór jest bezpieczniejszym wyjściem. • Niezawodność Całkowite rozdzielenie warstw klienta i serwera ma niewątpliwie swoje plusy, nie jest jednak pozbawione wad. Dwie osobne aplikacje to z pewnością więcej kodu niż jedna. Dodatkowo warstwa front-endowa cechuje się dużą asynchronicznością, jest sterowana zdarzeniami (kliknięcia, interakcje). Nie ma tutaj tak jasnego przepływu danych, jak po stronie serwera (zapytanie - odpowiedź). Wszystko to sprawia, że całość jest dużo trudniejsza do automatycznego testowania, co za tym idzie bardziej zawodna. Należy też dodać, że pojedynczy błąd w klienckim kodzie JavaScript, skutkuje bardzo często zaprzestaniem dalszego wykonywania programu. • Synchronizacja stanu Aplikacja kliencka jest w całości odpowiedzialna za generowanie i zmienianie doku- mentu HTML widocznego dla użytkownika. Szablony do jego tworzenia są ustalone wcześniej i znane. Zmienne, które trzeba w nim osadzić - nie. Zostają one pobrane z serwera przy pomocy XHR podczas działania aplikacji. Stan aplikacji po stro- nie klienta musi być synchronizowany z tym, co rezyduje w bazie danych serwera. Wprowadza to konieczność częściowej duplikacji logiki serwera, a co za tym idzie jeszcze bardziej zwiększa objętość kodu. • Indeksowanie W projektowaniu aplikacji internetowych bardzo ważnym zagadnieniem jest opty- malizacja ich zawartości pod kątem algorytmów wyszukiwarek internetowych (SEO). Właściciel serwisu chce, aby jego strona była atrakcyjna, ale co ważniejsze, aby była łatwo dostępna dla potencjalnych klientów. Polega to m.in. na odpowiednim osa-
  22. 22. Rozdział 2. Wprowadzenie teorytyczne 25 dzeniu słów kluczowych czy meta-opisów. Jeśli chodzi o SPA - zadanie komplikuje się nieco. Cała zawartość (w tym słowa kluczowe) pobierana jest poprzez AJAX już po pierwszym załadowaniu strony. Dla użytkownika nie jest to problem, ale web crawlery nie obsługują tego typu treści. Rozwiązaniem problemu jest dodat- kowe generowanie dokumentu HTML po stronie serwera, specjalnie dla silników wyszukiwarek. Zagadnienie to jest jednak dość złożone, nie wszystkie frameworki dostarczają tego typu rozwiązań. Przy braku powyższych dodatkowych czynności, strona internetowa widziana przez wyszukiwarkę np. Google jest pusta. Taki stan rzeczy ma fatalny wpływ na indeksowanie strony i jest nie do przyjęcia. • Początkowe ładowanie Kolejną niedogodnością jest tzw. ”slow start”. Aplikacja tego typu może ładować się znacznie dłużej niż standardowa strona. Oczywiście dzięki temu eliminuje się późniejsze przeładowania, a sama strona działa dużo płynniej. Co jednak w przy- padku gdy aplikacja potrzebuje kilku (kilkanastu) sekund do załadowania? (co nie jest odosobnionym przypadkiem, zwłaszcza przy dużych aplikacjach, a także przy wolnym połączeniu internetowym) Dzisiejszy użytkownik jest bardzo niecierpliwy i nieprzyzwyczajony do czekania na treść. Jego pierwsze wrażenie kształtuje się już w przeciągu ułamków sekundy, a utwierdza się w tym przekonaniu w czasie do kilku sekund. Co, jeśli przez cały ten czas będzie wpatrywał się w pasek ładowania, albo co gorsza, pustą stronę? • Wyłączony JS Wydaje się, że temat braku obsługi języka JavaScript przez przeglądarki interne- towe w roku 2015 nie powinien być podniesiony. Wciaż jednak istnieje grupa użyt- kowników (czasem całych korporacji) korzystająych z przestarzałych przeglądarek (wczesne wersje IE) z wyłączonym JavaScriptem. Nie jest to duży odsetek, ale pro- gramista wybierający zestaw technologii powinien się z nim liczyć. W przypadku gdy głównym odbiorcą aplikacji ma być powyższa grupa, implementacja systemu w technice SPA byłaby błędem. 2.4 Model - View - Controller W informatyce bardzo powszechnym podejściem jest dekompozycja problemu. Ła- twiejsze staje się rozwiązanie trzech małych problemów niż jednego dużego. Na podobną myśl w odniesieniu do aplikacji z interfejsem użytkownika wpadł już w 1970 roku Try- gve Reenskaug. Współczesne programy komputerowe potrafią być bardzo złożone, ofe-
  23. 23. Rozdział 2. Wprowadzenie teorytyczne 26 rując użytkownikowi cały wachlarz funkcjonalności. Gdyby jednak rozłożyć każdą z nich na czynniki pierwsze, okazałoby się, że złożony obiekt składa się z małych niezależnych bloczków - modelu, widoku i kontrolera. Wydawać się może, że tylko 3 byty to za mało dużych aplikacji. Jak pokazuje jednak historia informatyki, najprostsze rozwiązania są najlepsze (REST), a utrzymanie prostej i spójnej architektury kluczem do sukcesu. Echo MVC pobrzmiewa bardzo silnie w ekosystemie WWW. Jak się okazało, forma aplikacji internetowych, jest bardzo spójna z tym, co proponuje MVC. Współcześnie wszystkie liczące się technologie do zastosowań webowych korzystają z MVC. Framework ”Rails” bardzo silnie wypromował wzorzec MVC - do tego stopnia, że programiści zaczęli tworzyć frameworki w innych językach, inspirowane właśnie Railsami [18]. Kolejnym dowodem słuszności MVC, może być fakt jego stosowania również po stronie klienta JavaScript. Początkowo nie było to potrzebne, jednak obecnie ilość potrzebnych informacji i stopień skomplikowania logiki stał się na tyle duży, że wzorzec MVC (lub jego odmiany) stały się koniecznością. Brak jego zastosowania bardzo często skutkuje regresją kodu, wzrostem zawodności oraz spadkiem czytelności (spagetti code). Wzorzec MVC opisuje przeznacze- nie i sposób interakcji między trzema tytularnymi typami obiektów. Krótki opis każdego z nich: • Model Odpowiedzialny za przechowywanie informacji związanych z obiektem, jedyny no- śnik logiki biznesowej. W większości przypadków powiązany z bazą danych (ORM - mapowanie obiektowo-relacyjne). Niezależny od widoku. • Widok Sposób prezentacji danych (np. z modelu) oraz interfejs użytkownika. • Kontroler Obiekt spinający dwa powyższe - pozwala przechwytać interakcje użytkownika zapo- czątkowane na komponentach widoku i przekazywać je do modelu. Z drugiej strony dostarcza danych z modelu do widoku, odpowiedzialny również za dobór właściwego modelu (np. wpis o odpowiednim id) 2.5 Języki Języki programowania są podobnie jak języki mowy naturalnej zbiorem pewnych reguł syntaktycznych oraz semantycznych dzięki którym człowiek w tym przypadku programista może wydawać zrozumiałe polecenia do wykonania przez komputer. Prawo relatywizmu językowego zaproponowane ok. 1930r przez Sapira i Whorfa mówi o tym, że używany
  24. 24. Rozdział 2. Wprowadzenie teorytyczne 27 język wpływa w większym lub mniejszym stopniu na myślenie i sposób postrzegania ota- czającego nas świata. Pomimo tego, iż hipoteza ta dotyczy języka mowy, jej założenia znajdują także odzwierciedlenie w posługiwaniu się językami programowania. Dlatego też wybór danego języka także w informatyce jest bardzo istotny. Natomiast warto pamiętać o tym, iż w większości przypadków nowe języki programowania postrzegane są błędnie jako cudowne narzędzia rozwiązujące wszystkie problemy. Nie istnieje jedno uniwersalne narzędzie do wszystkiego. Wynika to z tego, iż występuje bardzo duża liczba zbiorów pro- blemów a każdy z nich ma zbyt dużą ilość ograniczeń. Skutkiem tego jest fakt, iż z reguły języki programowania specjalizują się w rozwiązywaniu problemów zwykle z wąskiego zakresu danej dziedziny. Jednakże wśród programistów już zawsze będą trwały swojego rodzaju ”wojny na języki” podyktowane zazwyczaj indywidualnymi preferencjami, ale także szerokim wachlarzem dostępnych narzędzi. 2.5.1 Ruby Ruby jest popularnym, dynamicznym językiem skryptowym, który ma na celu dać programiście duże możliwości, poczucie swobody oraz ma sprawić, aby programowanie w tym języku było tak naturalne i przyjemne jak to tylko możliwe. Według jego twórcy Yukihiro “Matz” Matsumoto Ruby jest językiem starannie dobranej równowagi. Aby to osiągnąć, jego autor połączył wybrane elementy każdego z języków Perla, Smalltalka, Eif- fel, Ady, i Lispa, by stworzyć język, który balansuje programowanie funkcjonalne wraz z programowaniem imperatywnym. Pierwsze publiczne ukazanie języka Ruby nastąpiło w 1995 roku. Natomiast dopiero w 2006 język ten zyskał większą popularność oraz przychyl- ność środowiska programistów. Stało się tak głównie za sprawą powstania i popularyzacji frameworka do tworzenia aplikacji internetowych Rails, o którym więcej w rozdziale 3.3. Kolejnym powodem, dla którego omawiany język stał się tak popularny, co obrazuje Rys. 2.1, jest fakt, iż Ruby jest językiem całkowicie darmowym także w rozumieniu kopiowania, modyfikowania i rozprowadzania tego języka. Oto najważniejsze cechy języka: • Ruby jest językiem zwinnym. A to dlatego, że udostępnia programiście, możliwość dowolnego modyfikowania jego części czego skutkiem jest zachęta społeczności do ciągłego udoskonalania wykorzystywanego przez nich języka. Ruby stara się nie na- rzucać żadnych ograniczeń programiście co pozwala w zależności od intencji usunąć lub przedefiniować podstawowe elementy języka. Przykładowo operację dodawania wykonuje się za pomocą operatora ”+”. Natomiast istnieje możliwość wykonania tego działania za pomocą słowa ”plus”. W tym celu należałoby dodać odpowiednią metodę do klasy ”Numeric”.
  25. 25. Rozdział 2. Wprowadzenie teorytyczne 28 Listing 2.3: Elastyczność języka Ruby 1 class Numeric 2 def plus(x) 3 self .+(x) 4 end 5 end 6 7 y = 1. plus 3 Wynikiem działania jest oczywiście 4. Operatory w języku Ruby są tak zwanym ”lukrem” składniowym dla metod, które również można w dowolny sposób przede- finiować. • Ruby jest językiem bardzo wysokiego poziomu. Twórcy języka kierowali się zasadą, że to komputer powinien pracować dla programisty, a nie odwrotnie. Dzięki temu nawet najbardziej skomplikowane operacje możemy wykonywać za pomocą stosun- kowo nie dużej ilości kodu w porównaniu z językami niższego poziomu. • Ruby jest językiem silnie obiektowym. Dzięki temu każda część kodu może mieć własne atrybuty w postaci zmiennych instancji oraz własne metody. Dzięki temu możemy wywołać metodę na liczbie: Listing 2.4: Obiektowość języka Ruby 1 10. times { print "Everything is an object!" } Istnieje wiele języków programowania, w których np. liczby i inne bazowe typy nie zachowują się jak obiekty. Inaczej jest w przypadku Rubiego, który tę cechę odziedziczył po Smalltalku, co znacznie ułatwia korzystanie z języka, dlatego że zasady dotyczące obiektów mają również zastosowanie dla całego języka. • Ruby posiada cechy języków funkcyjnych. Dzięki temu, możemy dołączyć do do- wolnej metody domknięcie, które opisuje sposób działania danej metody. Tego typu domknięcie nazywa się blokiem i zostało zaczerpnięte przez twórców z języka funk- cyjnego Lisp. Listing 2.5: Funkcyjność języka Ruby 1 ruby_traits = 2 %w[Agile Object -oriented High -level ].map do |trait| 3 "Ruby is " + trait.downcase + " language!" 4 end
  26. 26. Rozdział 2. Wprowadzenie teorytyczne 29 Rysunek 2.1: Porównanie ilości ofert pracy dla popularnych języków serwerowych, [źródło: http://www.indeed.com/jobtrends]. Powyższy przykład pokazuje blok, który znajduje się między słowami kluczowymi ”do” i ”end”. Bloki mogą być definiowane w dowolny sposób i realizować złożone operacje. • Ruby realizuje paradygmat dziedziczenia. Natomiast robi to inaczej niż w większo- ści języków programowania. Ruby realizuje celowo tylko dziedziczenie jednokrotne, natomiast dla programistów tego języka wprowadzono możliwość korzystania z mo- dułów, które są zbiorami metod. Do każdej klasy może zostać dołączony tego typu moduł, który rozszerza klasę o implementacje metod z danego modułu. Tego typu rozwiązanie uznawane jest przez programistów Rubiego za prostsze i wygodniejsze względem wielokrotnego dziedziczenia, które nakłada wiele ograniczeń i może być stosunkowo skomplikowane. • Ruby posiada mechanizm odśmiecania pamięci. Tak jak w wielu nowoczesnych ję- zykach programowania, tak i w Rubim występuje garbage collector z prawdziwego zdarzenia typu mark-and-sweep, który wykorzystywany jest dla wszystkich obiektów żyjących w pamięci obiektowej. Według twórców języka nie istnieje potrzeba prze- trzymywania informacji na temat liczby odniesień do obiektu tak jak w metodach odśmiecania typu reference counting.
  27. 27. Rozdział 2. Wprowadzenie teorytyczne 30 Rysunek 2.2: Wykres pokazujący ilość ofert pracy dla języków JavaScript, C++, C#, PHP oraz Python, [źródło: http://www.indeed.com/jobtrends]. 2.5.2 JavaScript JavaScript jest skryptowym językiem programowania, który pojawił się w już w 1995 za sprawą firmy Netscape. Wbrew pozorom geneza jego nazwy ma nie wiele wspólnego z językiem Java. Powstała ona po prostu w wyniku kontraktów biznesowych pomiędzy firma Netscape i Sun Microsystems. W 1997 roku ECMA stworzyła standard dla języka JavaScript zwany ECMAScript. JavaScript zyskał dużą popularność za sprawą rozwoju dynamicznych aplikacji internetowych, co można zauważyć na wykresie 2.2 trendów od- nośnie zatrudnienia. Dzisiaj już nikt nie wyobraża sobie funkcjonowania aplikacji www bez korzystania z JavaScriptu. Na bazie tego języka powstało wiele rozbudowanych fra- meworków frontendowych o których więcej w rodziale 3 a nawet backendowych opisanych w rozdziale 2.6.2 co także miało duże znaczenie na zwiększenie popularności tego Java- Scriptu a co za tym idzie także programistów posługujących się tym językiem. Elementy języka JavaScript zgodne ze standardem ECMAScript: • Niektóre podstawowe typy danych i obiekty wykorzystywane w JavaScripcie. Listing 2.6: Typy danych w języku JavaScript 1 String , Boolean , Number , Object , Math , Array • JavaScript odziedziczył podstawowe instrukcje sterujące po językach C++ i Java. Listing 2.7: Komentarz blokowy w języku JavaScript
  28. 28. Rozdział 2. Wprowadzenie teorytyczne 31 1 /* To jest komentarz 2 blokowy zajmujacy 3 kilka linii */ Listing 2.8: Komentarz liniowy w języku JavaScript 1 // To jest komentarz liniowy Listing 2.9: Instrukcja if w języku JavaScript 1 if (warunki) { 2 instrukcje; 3 } 4 else { 5 instrukcje; 6 } Listing 2.10: Pęta while w języku JavaScript 1 while (warunki) { 2 instrukcje; 3 } Listing 2.11: Pętla do...while w języku JavaScript 1 do { 2 instrukcje 3 } while (warunki); Listing 2.12: Pętla for w języku JavaScript 1 for ([ poczatkowe ]; [warunki ]; [krokowe ]) { 2 instrukcje; 3 } Listing 2.13: Pętla for...in oraz for...of w języku JavaScript 1 for (wlasnosc in obiekt) { 2 instrukcje; 3 } Listing 2.14: Instrukcja switch w języku JavaScript 1 switch (wyrazenie) {
  29. 29. Rozdział 2. Wprowadzenie teorytyczne 32 2 case wartosc1: 3 instrukcje; 4 break; 5 case wartosc2: 6 instrukcje; 7 break; 8 default: 9 instrukcje; 10 break; 11 } • W JavaScripcie występują obiekty i typy prymitywne. Według standardu ECMA- Script obiekty są tablicami asocjacyjnymi. Ze względu na to iż w JavaScripcie me- toda danego obiektu jest także jego polem istnieją dwa sposoby odwołania: Listing 2.15: Notacja kropkowa wywołania metody w języku JavaScript 1 m.metoda1 (); Listing 2.16: Notacja z nawiasami kwadratowymi wywołania metody w języku JavaScript 1 m["metoda1"](); • Aby utworzyć w JavaScripcie własny obiekt trzeba stworzyć funkcję konstruktora. Listing 2.17: Funkcja konstruktora w języku JavaScript 1 function Obiekt(pole1 , pole2) { 2 this.pole1 = pole1; 3 this.pole2 = pole2; 4 5 function metoda1 () { 6 alert("Obiekt :: metoda1 ()"); 7 } 8 this.metoda1 = metoda1; 9 10 function metoda2 () { 11 alert("Obiekt :: metoda2 ()"); 12 } 13 this.metoda2 = metoda2; 14 }
  30. 30. Rozdział 2. Wprowadzenie teorytyczne 33 • W większości współczesnych języków obiektowych występują ”klasy” które pozwala- ją tworzyć własne niestandardowe typy. Inaczej jest w przypadku języka JavaScript, ponieważ według ECMA pojęcie ”klasy” nie istnieje w sensie formalnym. Mówiąc o ”klasach” w JavaScripcie mamy na myśli obiekty stworzone z wykorzystaniem tego samego konstruktora. Listing 2.18: Tworzenie instancji klasy Obiekt w języku JavaScript 1 var m = new Obiekt (1, 2); • Definiowanie funkcji w JavaScripcie odbywa się za pomocją słowa kluczowego func- tion. Zgodnie z ECMAScript funkcje są jednocześnie obiektami klasy Function. Listing 2.19: Definicja funkcji w języku JavaScript 1 function dodajLiczby(a, b) { 2 return a+b; 3 } • JavaScript implementuje także paradygmat dziedziczenia, ale w uproszczony sposób wykorzystując prototypy. Listing 2.20: Dziedziczenie w języku JavaScript 1 function KlasaBazowa () { 2 this.metoda1 = function () { 3 alert("KlasaBazowa ::1()"); 4 } 5 this.metoda2 = function () { 6 alert("KlasaBazowa ::2()"); 7 } 8 } 9 10 function KlasaPochodna () { 11 // metoda2 przeciaza odpowiednia metode z klasy KlasaBazowa: 12 this.metoda2 = function () { 13 alert("KlasaPochodna ::2()"); 14 } 15 } 16 KlasaPochodna.prototype = new KlasaBazowa (); 17 18 x = new KlasaBazowa (); 19 y = new KlasaPochodna ();
  31. 31. Rozdział 2. Wprowadzenie teorytyczne 34 Rozdział opracowany na podstawie [15] 2.6 Środowiska programistyczne Poprzez pojęcie środowiska programistyczne rozumiemy tu technologie, narzędzia oraz biblioteki zorientowane wokół danego języka programowania wraz z tymi językami, które zostały bliżej przedstawione w rozdziale 2.5. Należy pamiętać o tym, iż wybór danego języka determinuje całe środowisko pracy danego programisty. Dlatego też w tym rozdziale przedstawione zostaną bliżej konkretne zestawy narzędzi i bibliotek, ich geneza oraz baza teoretyczna dla wybranych w poprzednim rozdziale języków programowania. 2.6.1 Ekosystem Rubiego Język Ruby sam w sobie jest świetnym narzędziem, nastawionym na tworzenie czy- stego, zrozumiałego kodu. Jest to język powszechnego zastosowania, w praktyce jednak używany w większości przypadków w zestawieniu z Railsem. Czy popularność powyższych można tłumaczyć tylko wysoką estetyką kodu? Ma to z pewnością wielkie znaczenie, jed- nak nie mniej ważny jest aspekt open-source. Zarówno Ruby, jak i Rails to środowiska otwarte. Każdy ma dostęp do kodu źródłowego, każdy może go modyfikować na własne potrzeby. Może również tymi modyfikacjami dzielić się z twórcami, aby każdy mógł korzy- stać z nowych funkcjonalności czy poprawek. Podobnie jest ze wszystkimi gemami (”gem” to nazwa dodatkowej biblioteki w języku Ruby. Nawiązuje do nazewnictwa języka, ruby - rubin. Jest to nazwa własna i dość charakterystyczna, dlatego w dalszej części pracy, pojęcia te będą używane zamiennie). I to właśnie otwartość środowiska jest motorem napędzającym cały ekosystem. RubyGems Ruby od wersji 1.9 jest dostarczany z wbudowanym menadżerem pakietów - Ruby- Gems. Moduł ten jest domyślnie połączony z repozytorium gemów - rubygems.org. Jeśli wskazana biblioteka znajduje się w repozytorium, zostanie automatycznie pobrana. Ist- nieje możliwość wskazywania również innych źródeł. Instalacja nowego gema ogranicza się do wywołania jednej komendy, np: Listing 2.21: Przkład instalacji gema ”Devise” 1 gem install devise Narzędzie RubyGems zostało szerzej opisane w [17] w rozdziale 21.1.
  32. 32. Rozdział 2. Wprowadzenie teorytyczne 35 RVM Bardzo często zdarza się, że programista pracuje nad kilkoma projektami. Nie wszyst- kie z nich musza korzystać z tej samej wersji języka Ruby. W standardowych okoliczno- ściach każda zmiana projektu wiązałaby się z reainstalacją Rubiego wraz z potrzebnymi gemami. Takie działanie byłoby ogromną stratą czasu. Z pomocą przychodzi RVM, czyli menadżer wersji języka Ruby. Dzięki niemu w systemie rezydują obok siebie różne wersje wraz z zainstalowanymy gemami. Zmiana wersji odbywa się za pomocą prostej komendy: Listing 2.22: Zmiana wersji Rubiego na 2.2.3 1 rvm use 2.2.3 2 ruby -v 3 ruby 2.2.3 p173 (2015 -08 -18 revision 51636) [x86_64 -darwin14] Bundler Instalacja pakietów jest prosta dzięki RubyGems. Bardzo często zdarza się jednak, że gemy bazują na innych gemach (dependency). Ręczne sprawdzanie zależności i instalacja odpowiednich wersji bibliotek, byłoby bardzo czasochłonnym zajęciem. W dodaktu cały proces należałoby powtarzać przy każdej aktualizacji gemów. Powyższe zadanie jest trudne dla człowieka, jednak komputery radzą sobie z nim bez problemu. Gem ”bundler” auto- matycznie negocjuje wersje pakietów, znajduje takie, które spełniają oczekiwania każdego gema (o ile to możliwe). Programista tworzy jedynie plik Gemfile, w którym wylistowane są potrzebne gemy. Bundler tworzy na jego podstawie plik Gemfile.lock wskazujący na ich konkretne wersje. Poniżej przykłady wymienionych plików: Listing 2.23: Przykładowy Gemfile 1 source ’https :// rubygems.org’ 2 3 gem ’guard ’ 4 gem ’guard -shell ’ 5 gem ’terminal -notifier -guard ’ Listing 2.24: Przykładowy Gemfile.lock 1 GEM 2 remote: https :// rubygems.org/ 3 specs: 4 coderay (1.1.0) 5 ffi (1.9.10)
  33. 33. Rozdział 2. Wprowadzenie teorytyczne 36 6 formatador (0.2.5) 7 guard (2.13.0) 8 formatador (>= 0.2.4) 9 listen (>= 2.7, <= 4.0) 10 lumberjack (~> 1.0) 11 nenv (~> 0.1) 12 notiffany (~> 0.0) 13 pry (>= 0.9.12) 14 shellany (~> 0.0) 15 thor (>= 0.18.1) 16 guard -compat (1.2.1) 17 guard -shell (0.7.1) 18 guard (>= 2.0.0) 19 guard -compat (~> 1.0) 20 listen (3.0.3) 21 rb -fsevent (>= 0.9.3) 22 rb -inotify (>= 0.9) 23 lumberjack (1.0.9) 24 method_source (0.8.2) 25 nenv (0.2.0) 26 notiffany (0.0.7) 27 nenv (~> 0.1) 28 shellany (~> 0.0) 29 pry (0.9.12.6) 30 coderay (~> 1.0) 31 method_source (~> 0.8) 32 slop (~> 3.4) 33 rb -fsevent (0.9.5) 34 rb -inotify (0.9.5) 35 ffi (>= 0.5.0) 36 shellany (0.0.1) 37 slop (3.6.0) 38 terminal -notifier -guard (1.6.4) 39 thor (0.19.1) 40 41 PLATFORMS 42 ruby 43 44 DEPENDENCIES 45 guard 46 guard -shell 47 terminal -notifier -guard
  34. 34. Rozdział 2. Wprowadzenie teorytyczne 37 irb Bardzo wygodnym narzędziem jest interaktywna konsola języka Ruby (irb - interactive Ruby). Jako że Ruby jest językiem interpretowalnym, można dynamicznie wykonywać jego instrukcje i prezentować ich wyniki. Irb jest przydatne w przypadku chęci przetestowania działania napisanego kodu. Poniżej przykład użycia irb: Listing 2.25: ”Hello World” w irb 1 $ irb 2 :001 > puts ’Hello World ’ 3 Hello World 4 => nil Jeszcze ciekawszym narzędziem jest rails console. Jest bardzo podobne do konsoli irb, używane jest jednak tylko w obrębie aplikacji Rails. Dzięki niemu możemy korzystać ze wszystkich klas i metod napisanych w obrębie tej aplikacji. Np. możliwe staje się uzyskanie dostępu do bazy danych poprzez ORM. Możemy wprowadzić zmiany, operując na modelach, a nie tabelach bazodanowych. Przykład użycia rails console: Listing 2.26: Zmiana hasła klienta o id 1 1 $ rails console 2 przykladowa_aplikacja >> Client.find_by(id: 1). update_attributes password: ’nowe haslo ’ 3 Client Load (19.4 ms) SELECT "clients".* FROM "clients" WHERE " clients"."id" = $1 LIMIT 1 [["id", 1]] 4 (2.2 ms) BEGIN 5 SQL (0.6 ms) UPDATE "clients" SET " encrypted_password " = $1 , " updated_at" = $2 WHERE "clients"."id" = $3 [[" encrypted_password ", " $2a$10$fOjZciThhpJ9flUV0B5f1 .. qbsZXgEBfYb /5 PJkQMGWxww0XKbbj ."], ["updated_at", "2015 -08 -29 15:41:19.559307 "], ["id", 1]] 6 (48.7 ms) COMMIT 7 => true Rake Odpowiednik narzędzia ”make” z systemów UNIX. Pozwala na konstruowanie zadań, reguł. Umożliwia zarządzane zależnoścami. Zadania definiuje się w języku Ruby, dzię- ki zastosowaniu specjalnego DSL. Co ciekawe umożliwia budowanie także programów w innych językach, np. C.
  35. 35. Rozdział 2. Wprowadzenie teorytyczne 38 2.6.2 Node Node.js jest platformą stworzoną na bazie środowiska uruchomieniowego JavaScript. Projekt ten jest stosunkowo młody, gdyż pojawił się w 2009 roku i bardzo szybko zyskał dużą popularność, co widać na Rys. 2.4. Społeczność wokół Node.js rośnie tak szybko, że obecnie projekt ten jest drugi co do liczby obserwatorów w portalu GitHub i aktualnie istnieje ok. 70 000 modułów stworzonych na licencji ”Open Source” dostępnych z poziomu menadżera pakietów dla Node.js npm. Główne cechy platformy Node.js: • Podstawą działania Node.js jest język JavaScript, o którym więcej w rozdziale 2.5.2. Do wykonywania kodu po stronie serwera Node stosuje wirtualną maszynę Java- Script V8, z której korzysta przeglądarka Google Chrome. Dzięki temu aplikacje tworzone w Node zyskują dużą wydajność z tego względu, iż zamiast uruchamiania kodu bajtowego z wykorzystaniem interpretera Node kompiluje program do kodu maszynowego. Wykorzystanie języka JavaScript po stronie serwera przynosi kilka istotnych korzyści. Po pierwsze w wyniku tego, iż aplikacje internetowe mogą być tworzone z wykorzystaniem tylko jednego języka programowania zarówno po stronie klienckiej, jak i serwerowej minimalizowany jest proces przełączania kontekstu. Po drugie Node wykorzystuje JSON jako format wymiany danych zyskujący dużą popu- larność w dziedzinie tworzenia aplikacji internetowych będący natywnym formatem dla języka JavaScript. Dzięki temu, że Node korzysta z dokładnie jednej maszyny wirtualnej V8, zgodnej ze standardem ECMAScript, korzystanie z nowych funk- cji języka JavaScript nie jest ograniczone przez spóźnione aktualizacje przeglądarek internetowych różnych producentów. • Obsługa operacji wejścia-wyjścia na serwerze. Zgodnie z rysunkiem 2.3 obrazują- cym wykonywanie nieblokującej operacji Node jest serwerem asynchronicznym. W standardowym podejściu komunikacji z serwerem mamy model typu zapytanie - od- powiedź kóre realizowane jest w głównym wątku aplikacji i kolejne zapytania do serwera są zwyczajnie kolejkowane. Natomiast w przypadku Node.js istnieje tzw. pętla zdarzeń, która dopiero po otrzymaniu odpowiedzi wykonuje operacje zdefinio- wane w funkcji zwrotnej. • Tworzenie aplikacji typu DIRT. W związku z tym, że Node dzięki nieblokującemu przetwarzaniu akcji wejścia-wyjścia powoduje małe obciążenie podczas przetwarza- nia operacji, często jest wykorzystywany jako pośrednik w przekazywaniu różnych strumieni danych z różnych źródeł. Ta cecha powoduję, iż Node staję się popularny w aplikacjach przetwarzających dużą ilość danych w czasie rzeczywistym. Przykła-
  36. 36. Rozdział 2. Wprowadzenie teorytyczne 39 Rysunek 2.3: Przykład nieblokującej operacji wejścia-wyjścia wykonywanej przez Node, [źródło: [3]]. dem aplikacji typu DIRT napisanej w Node jest [16] która służy do testowania stron internetowych na różnych platformach w czasie rzeczywistym. W związku z dużym rozwojem frameworków frontendowych rosnącym ich skompli- kowaniem oraz tendencją do upodabniania się do aplikacji backendowych pojawiły się zapotrzebowania na dodatkowe narzędzia i biblioteki do odpowiedniego zarządzania tymi frameworkami. I tutaj do gry wchodzi Node.js, który wprowadza między innymi mena- dżer zarządzania pakietami npm, które można porównać do gemów w środowisku Ruby oraz nvm, który służy do zarządzania wersjami Node.js. Nvm jest odpowiednikiem rvm, o którym więcej w rozdziale 2.6.1. W wyniku tego, iż technologie aplikacji klienckich nie podlegają w zasadzie żadnej standaryzacji tego, typu rozwiązań open source jest bardzo dużo i nie sposób je wszystkie opisać a co dopiero używać. 2.7 Bezpieczeństwo W dzisiejszym świecie Internet jest medium, w którym dochodzi do olbrzymiej wy- miany informacji. Dane przesyłane w Internecie dotyczą każdego aspektu życia, dlatego też kluczowym pojęciem stało się bezpieczeństwo. Według jednej z definicji komputer jest
  37. 37. Rozdział 2. Wprowadzenie teorytyczne 40 Rysunek 2.4: Porównanie ilości ofert pracy dla popularnych platform serwerowych, [źródło: http://www.indeed.com/jobtrends]. uznawany za bezpieczny wówczas, gdy można stwierdzić, że sprzęt oraz oprogramowanie na nim znajdujące się działają zgodnie z oczekiwaniami użytkownika. Natomiast w obliczu bardzo szybkiego rozwoju globalnej sieci, jaką jest Internet Rys. 2.5 oraz sposobu i skali publikacji informacji, powstała potrzeba bezpieczeństwa ukierunkowana na WWW. Defi- niuje się je jako zbiór technologii, procedur i metod wykorzystywanych do zabezpieczania serwerów WWW, użytkowników, a także organizacje stające za nimi. Wprowadzane środki bezpieczeństwa mają za zadanie ustrzec użytkowników przed nieoczekiwanym zachowa- niem komputerów. Istnieje wiele znaczących czynników, które wpływają na odmienne i bardziej ukierunkowane podejście do bezpieczeństwa WWW: • Internet z założenia jest siecią dwukierunkową. Publikowane za jej pomocą informa- cje znajdują się na serwerach WWW, do których dostęp mogą mieć miliony osób na całym świecie. Tego typu zagrożenia nie występują w przypadku innych mediów np. gazeta, faks, telefon. • Sieć WWW wykorzystywana jest przez duże organizacje komercyjne, ale także i rzą- dowe do przechowywania i dostępu do wielu różnych danych także tych wrażliwych. Skutkiem tego jest wymagane zwiększone bezpieczeństwo dostępu do tych danych oraz większa kontrola nad tym, kto posiada dostęp do nich. • Pomimo tego, że sieć internetowa tworzona była i standaryzowana od początku swo- jego istnienia przez wiele lat to poziom skomplikowania zarówno z punktu widzenia sprzętowego, jak i oprogramowania jest na tyle duży, że liczba występujących błę-
  38. 38. Rozdział 2. Wprowadzenie teorytyczne 41 dów bardzo mocno podnosi ryzyko zagrożenia bezpieczeństwa. Przykładem takiego oprogramowania mogą być przeglądarki internetowe, którym cały czas zdarzają się często poważne luki w zabezpieczeniach. • Na poziom istotności zabezpieczeń serwerów WWW w znaczący sposób wpływa fakt, iż większość użytkowników Internetu nie posiada wystarczającej wiedzy, do- świadczenia ani świadomości zagrożeń, jakie mogą się pojawić. • W większości przypadków naprawa skutków naruszeń bezpieczeństwa wymaga wię- cej zasobów czasu i pieniędzy niż wprowadzenie odpowiednich zabezpieczeń. • W dzisiejszym świecie za pośrednictwem sieci WWW codziennie dochodzi do nie- zliczonej ilości transakcji finansowych wraz z wymianą informacji poufnych typu numery kart kredytowych czy też dane personalne obu kontrahentów. • Wiele firm wykorzystuje sieć WWW do komunikacji z zewnętrznymi odbiorcami, którymi mogą być partnerskie firmy bądź kontrahenci zlokalizowani na całym świe- cie. Zastrzeżone dane wymieniane pomiędzy tymi firmami mogą się stać celem przy- kładowo dla wrogów danej firmy. Problem zapewnienia bezpieczeństwa WWW należy podzielić na trzy główne elementy, z których każdy musi spełniać warunki bezpieczeństwa, aby całość przetwarzania danych w obrębie WWW można było nazwać bezpiecznym: • Odpowiednie zabezpieczenie serwera WWW oraz informacji na nim przechowywa- nych w taki sposób, aby mieć pewność, że serwer będzie działał ciągle i poprawnie. Przechowywane dane na serwerze WWW nie mogą być modyfikowane przez nieupo- ważnione do tej operacji osoby oraz mogą być udostępniane tylko i wyłącznie dla osób autoryzowanych, czyli takich, które posiadają prawa dostępu do tych informa- cji. • Jak w każdym systemie informatycznym tak i w sieci WWW największe zagrożenie, jeżeli chodzi o bezpieczeństwo, występuje na styku dwóch różnych systemów. Klu- czowym elementem jest zabezpieczenie przekazywanych informacji w szczególności tych poufnych od klienta do serwera. Głównym zagrożeniem bezpieczeństwa komu- nikacji na linii klient-serwer są wszelkiego rodzaju podsłuchy np. atak ”man in the middle”. • Zabezpieczenie komputera klienta, czyli użytkownika końcowego zwykle bywa naj- trudniejszym elementem dla zapewnienia bezpieczeństwa. Klient powinien być pew-
  39. 39. Rozdział 2. Wprowadzenie teorytyczne 42 Rysunek 2.5: Dostęp do Internetu stacjonarnego w Polsce, [źródło: http://pclab.pl/ news63802.html]. ny tego, że dane przesyłane na jego komputer są zgodne z oczekiwaniami i nie spowodują uszkodzenia innych danych bądź urządzeń. 2.7.1 Uwierzytelnianie Uwierzytelnianie jest procesem mającym na celu potwierdzenie tożsamości danego użytkownika. Metoda ta jest podstawowym mechanizmem bezpieczeństwa danych. W przypadku poprawnego uwierzytelnienia istnieje pewność, że dany użytkownik jest osobą, za którą się podaje, czego konsekwencją z kolei jest przydzielenie mu dostępu do zasobów, czyli uwierzytelnienie co jest opisane dokładniej w rozdziale 2.7.2. 2.7.2 Autoryzacja Samo uwierzytelnienie klienta wobec serwera nie jest wystarczające, aby zapewnić bezpieczeństwo. Możliwe jest w ten sposób ograniczenie dostępu do pewnych rejonów aplikacji, aż do momentu zalogowania. Złożone aplikacje internetowe dają użytkowni- kom dostęp do ogromu treści. Czasem aplikacja przechowuje jednak dane, które nie po- winny być oglądane przez osoby niebędące z nimi powiązane. Np. aplikacja bankowa umożliwia przeglądanie historii swoich transakcji pod adresem http://przykladowy- bank/user/transactions. Użytkownik może wyświetlić szczegóły pojedynczej transak- cji pod adresem http://przykladowy-bank/user/transactions/:id, gdzie :id to nu- mer transakcji. Nie byłoby jednak właściwe, gdyby inna osoba mogła uzyskać dostęp
  40. 40. Rozdział 2. Wprowadzenie teorytyczne 43 do tych danych, po prostu przeszukując wszystkie możliwe numery transakcji. Kolejnym przykładem może być blog gdzie pod adresem http://blog.com/recent posts możemy wszystkie posty utworzone w ciągu ostatnich 5 dni. Co, jednak jeśli chcemy, aby tylko użytkownicy ze specjalnymi uprawnieniami mogli wyświetlać posty oznaczone jak taj- ne? Problemem do rozwiązania jest w tym przypadku filtrowanie treści w zależności od uprawnień użytkownika. Wracając do przykładu z najnowszymi postami. Przekazując do widoku kolekcję wszystkich postów, należy wziąć pod uwagę uprawnienia (np. czy mamy do czynienia z administratorem) użytkownika (w tym przypadku zmienna current user). Listing 2.27: Przykład modelu ”Post” i zabezpieczonego kontrolera 1 class Post < ActiveRecord :: Base 2 scope :recent , -> { where(’created_at BETWEEN ? AND ?’, 5. days.ago , Time.now) } 3 scope :visible_by , -> (client) { client.admin ? all : where( supersecret: false) } 4 end 5 6 class PostsController < BaseController 7 def index 8 render Post.visible_by(current_user).recent 9 end 10 end Kolejnym problemem, który trzeba rozważyć w powyższym przykładzie, jest możliwość edycji posta. Funkcjonalność edycji utworzonych przez siebie bytów jest konieczna w więk- szości przypadków. Nie chcemy jednak aby ktoś inny mógł edytować nasze posty. Zabez- pieczenie kontrolera na powyższy przypadek wygląda następująco: Listing 2.28: Przykład zabezpieczenia metody ”update” kontrolera 1 class PostsController < BaseController 2 before_action : check_ownership !, only: :update 3 expose (: post) 4 5 def update 6 post. update_attributes (body: params [: body ]) 7 redirect_to post_path(@post) 8 end 9 10 private 11 12 def check_ownership ! 13 fail UnauthorizedAccess unless post.author == current_user
  41. 41. Rozdział 2. Wprowadzenie teorytyczne 44 14 end 15 end Przed wykonaniem akcji update serwer sprawdza, czy użytkownik domagający się takiej akcji jest autorem posta. Jeśli nie jest, wzbudza wyjątek UnauthorizedAccess, a następ- nie może wyświetlić użytkownikowi informację, że może edytować tylko swoje posty. Jeśli test na autorstwo przejdzie pozytywnie, pozwala wykonać żądaną akcję. Powyższe za- bezpieczenia wydają się trywialne, ale są bardzo ważne do zachowania bezpieczeństwa całej aplikacji. Edycja czyjegoś wpisu nie jest może dużą stratą, ale wszędzie tam gdzie system obraca jakimikolwiek środkami pieniężnymi, stawka jest dużo większa. Mimo tego programiści bardzo często niedostatecznie zabezpieczają dostęp do usług. Biblioteką, która podchodzi do powyższego zagadnienia kompleksowo, jest cancan- can[22]. Jej twórcy zaproponowali deklaratywne podejście do uprawnień użytkowników. Zamiast w każdym miejscu sprawdzać, czy dane konto ma rzeczywiście dostęp do konkret- nego zasobu, definiuje się jego uprawnienia w jednym pliku. Podczas dostępu do zasobów biblioteka sprawdza, czy żądany obiekt znajduje się na liście uprawnień. Przykład definicji uprawnień: Listing 2.29: Przykład definicji uprawnień w bibliotece ”cancancan” 1 class Ability 2 include CanCan :: Ability 3 4 def initialize(user) 5 user ||= User.new # guest user (not logged in) 6 if user.admin? 7 can :read , Post 8 else 9 can :read , supersecret: false 10 end 11 end 12 end Klasa Ability definiuje zdolności użytkownika. Powyższy definicja jest intuicyjna i zbli- żona do pseudokodu. Może być zrozumiana również przez kadrę menadżerską, co jest dużą zaletą i ułatwia komunikację w zespołach. Jest to jeden z atutów języka Ruby.
  42. 42. Rozdział 3 Opis technologii W niniejszym rozdziale zostaną wprowadzone analizowane technologie do tworzenia aplikacji klienckich typu SPA: ReactJS oraz AngularJS (rozdziały 3.1 i 3.2). W dalszej częsci przedstawione zostaną technologie serwerowe, mianowicie: Rails, Sinatra oraz Gra- pe (3.3, 3.4, 3.5). Wprowadzenie zawierać będzie informacje ogólne, krótki rys historyczny, a także czynniki motywujące autorów powyższych technologii do użycia takich czy innych rozwiązań. Autorzy pracy będą starali się wskazać najbardziej charakterystyczne cechy analizowanych bibliotek. Duży nacisk zostanie położony również na architekturę przesta- wianych rozwiązań, z uwzględnieniem wzorców projektowych i modelowania przepływu informacji. Tam gdzie to konieczne, wprowadzone zostaną szczegóły użytych algorytmów (Diff Algorithm 3.1.6). Konkretne elementy bibliotek, zostaną podparte przykładami ko- du, czy to z zaimplementowanych aplikacji, czy dokumentacji. 3.1 ReactJS Biblioteka ReactJS napisana w języku JavaScript, przez programistę Facebooka Jorda- na Walke, jest z całą pewnością najciekawszą pozycją w niniejszej pracy. Celem przyświe- cającym jej twórcom było usprawnienie projektowania interfejsów użytkownika, poprzez wprowadzenie deklaratywnych, modularnych i dajacych się ponownie używać komponen- tów. Jeśli rozważalibyśmy jej umiejscowienie w modelu MVC - byłaby to litera V - widok. Wydaje się, że odpowiedzialność widoku w powyższym wzorcu jest dość mała. React jed- nak prezentuje rewolucyjne, a także dość kontrowersyjne podeście, wymuszając zmiany na całym przepływie danych. Sama biblioteka jest dość minimalistyczna. Nie wymusza na programiście konkretnej architektury projektu. Nie jest wskazany preferowany spo- sób komunikacji z serwerem, struktura klas czy folderów. Twórcy Reacta proponują co prawda wzorzec o nazwie ”Flux” (o którym będzie też mowa w tym rozdziale), ale jest 45
  43. 43. Rozdział 3. Opis technologii 46 to opcjonalne. Takie podejście daje dużą elastyczność, jest jednak sporym wyzwaniem. Samodzielne dobieranie komponentów i projektowanie architektury wymaga pewnego do- świadczenia. Jako materiał do analizy w niniejszej pracy posłużyło połączenie Reacta, Fluxa i lekkiej biblioteki BackboneJS (tylko elementy do komunikacji z API). Więcej o tych technologiach można znaleźć w [9] i [13]. 3.1.1 Deklaratywny charakter widoków Opisywany w rozdziale 3.2 AngularJS korzysta z rozwiązania o nazwie ”two-way data binding”. Jest to funkcjonalność wbudowana w tę bilbiotekę (podobne rozwiązanie zasto- sowano w EmberJS - więcej informacji o nim w [10]). Przez długi czas był to standard wiązania danych z szablonem. Zarządzanie stanem obiektów po stronie klienta i ich syn- chronizacja z serwerem jest zadaniem trudnym. ”Two-way data binding” zdaje się cudow- nym rozwiązaniem. Programista nie musi ręcznie kontrolować wartości przechowywanych w formularzach i zapisywać ich w specjalnych obiektach. Po powiązaniu elementu inter- fejsu użytkownika z konkretnym modelem każda zmiana jednego z nich będzie skutkowała też zmianą drugiego. Twórcy Reacta przekonują, że takie wiązanie danych, jest źródłem wielu problemów. Ciężko stwierdzić jak głęboko może sięgać łańcuch takich zmian. Za- miast tego proponują prostsze rozwiązanie - ”unidirectional data flow”. Każdy komponent posiada funkcję ”render”. W niej za pomocą biblioteki JSX definiowany jest wygląd tego elementu, poniżej przykład z zaimplementowanej aplikacji: Listing 3.1: Przykład funkcji ”render” dla elementu listy rzeczy do zrobienia 1 render: function () { 2 var todo = this.props.todo; 3 var iClassName = ’fa fa -2x action check ’ 4 var trClassName = ’row ’ 5 if (todo.get(’completed ’)) { 6 iClassName += ’fa -check -square ’; 7 trClassName += ’bg -success ’; 8 } else { 9 iClassName += ’fa -square -o’; 10 }; 11 if (this.props.showCompleted === false && todo.get(’completed ’) === true) { 12 return null 13 }; 14 15 return ( 16 <tr className ={ trClassName}>
  44. 44. Rozdział 3. Opis technologii 47 17 <td > 18 <i className ={ iClassName} onClick ={ this. _onToggleComplete }/> 19 </td > 20 <td >{ todo.get(’name ’)}</td > 21 <td className=’inline ’> 22 <i className=’fa fa -times -circle fa -2x delete pull -right action close ’ onClick ={ this. _onDestroyClick }/> 23 </td > 24 </tr > 25 ); 26 } Programista definiuje wygląd komponentu tylko raz. Jeśli zajdą jakieś zmiany w przeka- zywanych parametrach lub jego stanie, cały DOM zostanie przerenderowany. 3.1.2 Props W powyższym przykładzie pokazane zostało użycie funkcji Reacta o nazwie ”props”, pochodzącej od słowa ”properties”. Służy ona do przekazywania danych z widoku nad- rzędnego do osadzenia w szablonie JSX (hierarchia widoków może być złożona, zgodnie ze wzorcem projektowym o nazwie kompozyt, opisanym szerzej w [11]). Co ciekawe można przekazywać w ten sposób nie tylko prymitywy, ale też złożone obiekty, np. JSON czy też funkcje. Przekazanie funkcji w taki sposób jest również dobrą praktyką - jest to zgodne ze wzorcem projektowym obserwator [11]. Zapobiega to zbędnemu i szkodliwemu wiązaniu obiektów. Obserwowany obiekt nie ma dostępu do rodzica, potrafi wywyłać tylko metodę, którą otrzymał. Poniżej przykład takiego działania: Listing 3.2: Przekazanie funkcji do subkomponentu 1 // Przekazanie funkcji do subkomponentu 2 return ( 3 <div id=’main ’> 4 <TodoTextInput onSave ={ this.createTodo }/> 5 ... 6 </div > 7 ); 8 9 // Wywolanie funkcji przekazanej z komponentu glownego 10 _save: function () { 11 this.props.onSave(this.state.value); 12 this.setState ({ 13 value: ’’
  45. 45. Rozdział 3. Opis technologii 48 14 }); 15 } Należy zaznaczyć, że wszystkie dane przekazane przez ”props” są niezmienne (immutable) dla komponentu, który je otrzymał. 3.1.3 State Bardzo często zdarza się, że elementy interfejsu użytkownika muszą reagować na inte- rakcje. Przykładowo przycisk ”Lubię to” na Facebooku, zmienia swój wygląd po kliknię- ciu, komunikując użytkownikowi wykonanie akcji. Jak zostało wspomniane w poprzednim podrozdziale dane pochodzące z ”props” są niezmienne. ”State” jest miejscem przecho- wywania danych dynamicznie zmieniających się podczas cyklu życia komponentu. Do ustawienia początkowego stanu służy funkcja ”getInitialState”. Poniżej przykład użycia z aplikacji ”Todo”: Listing 3.3: Ustawienie początkowego stanu komponentu w ReactJS 1 getInitialState : function () { 2 return { 3 value: this.props.value || ’’ 4 }; 5 } W trakcie działania aplikacji, komponent może zmienić swój stan, dzięki funkcji ”setSta- te”, poniżej przykład użycia: Listing 3.4: Zmiana stanu komponentu w ReactJS 1 // Element UI ktory jest zmieniany 2 <input className="form -control fw" autoFocus ={ true} value ={ this.state .value} onChange ={ this._onChange} placeholder="Task to be done ..." type="name"/> 3 4 // Funkcja wywolywana po zmianie 5 _onChange: function(/* object */ event) { 6 this.setState ({ 7 value: event.target.value 8 }); 9 },
  46. 46. Rozdział 3. Opis technologii 49 Rysunek 3.1: Diagram obrazujący przepływ sterowania w architekturze Flux, [źródło: https://github.com/facebook/flux]. 3.1.4 Flux Przepływ danych narzucony przez Reacta jest bardzo przyjazny dla programisty. Lu- dzie nie są przystosowani do pracy nad kilkoma zadaniami jednocześnie. W dwustronnym przepływie danych analiza zachowania aplikacji często wymagania śledzenia długiego łan- cucha zmian. W takim przypadku łatwo o pomyłkę. React proponuje zastosowanie wzorca SSoT - jedynego źródła prawdy (ang. Single Source of Truth). Pomysł na architekturę całej aplikacji klienckiej dostarcza właśnie Flux (szerzej opisany w [1]). Przepływ danych we Fluxie został przedstawiony na rysunku 3.1: Flux składa się z kilku głównych elemen- tów (rysunek 3.1). Przepływ sterowania odbywa się w pętli po każdym z nich. Poniżej ich opis (ułożone są chronologicznie, od punktu rozpoczęcia interakcji przez użytkownika, aż do pełnego przerysowania dokumentu HTML): • Widok Komponent Reacta. Wchodzi w interakcje z użytkownikiem, renderuje HTML. Opi- sany szerzej w 3.1.1 • Action Creator Dodatkowa warstwa abstrakcji służąca do tworzenia akcji. Została utworzona po to, aby programista inicjował zmiany danych tylko w jednym miejscu. Jego funk- cje mogą przyjmować argumenty, wynikiem ich działania jest przekazanie akcji do Dispatchera
  47. 47. Rozdział 3. Opis technologii 50 • Dispatcher Kolejna warstwa abstrakcji. W tym miejscu podejmowane są decyzje, co należy zrobić z akcją. Zazwyczaj przekazywane są dalej, jednak możliwe są inne możliwości. Poniżej wycinek kodu z Dispatchera zaimplementowanej aplikacji. Akcja o typie ”TODO CREATE” jest wywoływana tylko wtedy, gdy jej atrybut ”text” nie jest pusty: Listing 3.5: Część kodu obiektu Dispatcher 1 switch(action.actionType) { 2 case TodoConstants. TODO_LOAD_ALL_COMPLETE : 3 TodoStore.emitChange (); 4 break; 5 6 case TodoConstants.TODO_CREATE: 7 text = action.text.trim (); 8 if (text !== ’’) { 9 create(text); 10 } 11 break; 12 // ... 13 } • Store Centralny punkt składowania danych, również miejsce komunikacji z API. W zaim- plementowanej aplikacji do tego celu została użytka biblioteka BackboneJS (opisana szerzej w 3.1.8). Po wykonaniu działań na danych, obiekt Store emituje globalne zda- rzenie, na które reagują wszystkie komponenty. Cały interfejs, wszystkie formularze, przyciski, pola tekstowe zostają wyrenderowane ponownie. Wydaje się, że jest to nie- efektywne działanie i przy standardowym pełnym odświeżeniu tak by było. Jednak jedna bardzo ważna właściwość Reacta sprawia, że takie działanie ma sens. 3.1.5 Virtual DOM DOM został stworzony do reprezentacji dokumentów HTML niezależnie od platfor- my i języka. Ma strukturę drzewa. Przez długi czas był stosowany tylko do wyświetlenia informacji. Nie został stworzony z myślą o złożonych operacjach dodawania, usuwania i zmieniania węzłów. Te uwarunkowania czynią działania na nim wąskim gardłem (co po- kazały testy przeprowadzone przez autorów pracy 4, a także przeprowadzone przez inne osoby). React został zaprojektowany z myślą o przejrzystej architekturze i łatwo roz-
  48. 48. Rozdział 3. Opis technologii 51 wijalnym, modularnym kodzie. Rozpoznawalny jest jednak chyba głównie dzięki swojej wydajności. Z każdą zmianą stanu, React przerenderowuje całą aplikację. Jednak robi to w specyficzny sposób. Virtual DOM to dodatkowa warstwa abstrakcji, lekka kopia prawdziwego DOM. Wszystkie zmiany stanu są odzwierciedlane najpierw w Virtual DOM. Operacje na nim są szybsze, gdyż obiekt ten jest tylko wirtualny - nie musi zostać przerysowywany po każdej opera- cji. W przypadku zwyczajnego DOM bardzo często zdarza się, że przeplatając operacje odczytu i zapisu, wymagany jest tzw. ”reflow” i to nawet kilkukrotny. Ilustruje to listing 3.6: Listing 3.6: Operacje na DOM przykład 1 // Wejscie 2 <div> 3 <div>a</div> 4 <div>b</div> 5 </div> 6 7 // Wyjscie 8 <div> 9 <div>c</div> 10 <div>d</div> 11 </div> W standardowej manipulacji na DOM, przeglądarka wykonałaby ”reflow” 4 razy. Przy operowaniu na znaczącej liczbie węzłów liczonej w tysiącach, byłoby to już zauważalne dla użytkownika. React oblicza minimalną liczbę zmian między drzewami (diff algorithm), a dopiero później aplikuje je do DOM. Dlatego fakt, że cała aplikacja przerysowuje się z każdą zmianą danych, nie niesie za sobą spadku wydajności, a jedynie wzrost przejrzystości architektury. 3.1.6 Diff Algorithm Operacja obliczania minimalnej liczby transformacji jednego drzewa w drugie ma zlo- żoność rzędu O(n3 ). W przypadku manipulacji tysiącem węzłów (co nie jest rzadkim przypadkiem), koniecznie jest wykonanie jednego miliarda operacji. Taka złożoność jest jednak nie do przyjęcia. Autorzy Reacta oparli swój algorytm na dwóch dodatkowych założeniach: • Dwa komponenty tej samej klasy wygenerują podobne drzewa, a dwa komponenty różnych klas wygenerują różne drzewa.
  49. 49. Rozdział 3. Opis technologii 52 • Możliwym jest dostarczenie unikalnych kluczy, dla elementów niezmiennych przy ponownym rysowaniu. Algorytm porównywania drzew ma rekurencyjny charakter. Zatem chcąc operować na całym drzewie, należy najpierw zdefiniować operację na jednym węźle. React rozróżnia 3 typy takiej operacji: • Różne typy węzła Listing 3.7: React transformacja węzłów różnych typów 1 renderA: <div /> 2 renderB: <span /> 3 => [removeNode <div />], [insertNode <span />] W takim przypadku React nie podejmuje próby negoncjacji atrybutów, czy węzłów potomnych. Cała gałąź drzewa zostaje usunięta i zastąpiona nową. • Zgodne typy węzła Listing 3.8: React transformacja węzłów o takich samych typach 1 renderA: <div style ={{ color: ’red’}} /> 2 renderB: <div style ={{ fontWeight: ’bold ’}} /> 3 => [removeStyle color], [addStyle font -weight ’bold ’] Tutaj bardziej opłacalnym działaniem staje się zmiana atrybutu. Należy dodać, że style elementu (które w HTML są po prostu zmienną typu String), traktowane są jako tablica asocjacyjna. • Inteligentne operowanie na liście Najbardziej problematyczne jest obliczanie różnic w liście węzłów. Przypadek ze stałą liczbą elementów jest prosty - React porównuje po prostu obie listy, element po elemencie. Złożoność operacji to O(n). Dodanie elementu na końcu listy, również nie jest skomplikowane (złożoność nie wzrasta). Co jednak w przypadku dodawania elementów na początku lub w środku listy? Rozwiązaniem tego problemu mogłoby być obliczenie odległośi Levenshteina (złożoność O(n2 )). Jednak ten algorytm mimo wyższej złożoności, nie pozwala na wykrycie zmiany kolejności elementów. Algoryt- my, które na to pozwalają, mają jeszcze większą złożoność.
  50. 50. Rozdział 3. Opis technologii 53 Twórcy Reacta zaproponowali prostsze rozwiązanie. Zamiast skomplikowaych ob- liczeń na elementach listy, nadali każdemu z nich unikalny (w lokalnym zakresie) klucz, niezmienialny przy kolejnych przerysowaniach. Dzięki temu iterowanie po li- ście sprowadza się do iteracji po wszystkich kluczach tablicy asocjacyjnej (złożoność O(n)). 3.1.7 JSX Kolejną rzeczą wyróżniającą Reacta jest biblioteka JSX do pisania szablonów. Należy zaznaczyć, że jest całkiem opcjonalna. Dzięki niej programista może osadzać elementy o bardzo podobnej składni do HTMLa, w kodzie JavaScript. Różnice obu podejść na listingu 3.9 Listing 3.9: Porównanie budowy szablonów z użyciem JSX i bez 1 // Komponent HelloMessage zbudowany z pomoca JSX 2 var HelloMessage = React.createClass ({ 3 render: function () { 4 return <div >Hello {this.props.name}</div >; 5 } 6 }); 7 React.render(<HelloMessage name="John" />, mountNode); 8 9 // Komponent HelloMessage zbudowany w czystym JS 10 var HelloMessage = React.createClass ({ displayName: "HelloMessage", 11 render: function () { 12 return React.createElement("div", null , "Hello ", this.props.name ); 13 } 14 }); 15 React.render(React.createElement(HelloMessage , {name: "John"}), mountNode); 3.1.8 Backbone React, czy też architektura Flux, nie dostarcza gotowych narzędzi do komunikacji z API. Programista ma więc wolny wybór. Istnieje możliwość używania czystego JavaScrip- tu, jQuery lub dodatkowej biblioteki. Bardzo często spotyka się implementacje Fluxa wraz z minimalistyczną biblioteką BackboneJS, która dostarcza dodatkową warstwę abstrakcji, w postaci modeli i kolekcji. Dzięki nim nie ma potrzeby konstruowania skomplikowanych
  51. 51. Rozdział 3. Opis technologii 54 zapytań XHR. Wszystko to zostało opakowane w wygodne funkcje, np. fetch() - do po- bierania listy obiektów, save() - zapisywania stanu obiektu na serwerze, czy destroy() - do usuwania obiektu. 3.2 AngularJS Angular z języka angielskiego znaczy w tłumaczeniu dosłownym kanciasty, kątowy, narożny. Czy nazwa tego frameworka mówi coś więcej na temat jego funkcjonalności, ten rozdział postara się na to i inne pytania odpowiedzieć. AngularJS jest to zestaw narzę- dzi JavaScript do tworzenia aplikacji frontendowych typu SPA. Koncepcja SPA została omówiona w rozdziale 2.3. Historia Angulara sięga roku 2009. W początkowym etapie projekt ten był zaledwie małym prywatnym pomysłem realizowanym przez pracowników firmy Google Adama Abronsa i Misko Hevery’ego. Zarząd koncernu Google uznał projekt AngularJS na tyle ciekawy, że otrzymał oficjalne wsparcie wraz z zespołem programi- stów który miał za zadanie go rozwijać. W 2012 roku po raz pierwszy framework został upubliczniony. Wiele zalet Angulara zaczerpnięto ze sprawdzonych metod, co pozwoliło na stworzenie wydajnego i efektywnego frameworka, który zapewnia nieskomplikowaną strukturę, szerokie spektrum możliwości oraz wygodne metody testowania. Bardzo duży udział w rozwoju Angulara ma także społeczność internetowa. Dzięki ciągłej wymianie do- świadczeń pomiędzy programistami AngularJS framework jest poddawany ciągłym ulep- szeniom. Programiści chcący poznać wady i zalety Angulara w praktyce powinni odwiedzić stronę [8] na której znajdują się dokumentacje, kursy, poradniki, opisy API i inne rzeczy przydatne dla deweloperów. Przykłady aplikacji napisanych w Angularze znajdziemy na stronie https://builtwith.angularjs.org/. Bardzo istotnym aspektem dotyczącym Angula- ra jest fakt, iż jest on publikowany na licencji MIT, co oznacza, że jest w pełni darmowy. AngularJS pozwala w teorii w szybki i łatwy sposób budować warstwę kliencką aplikacji internetowych. Koncepcja omawianego frameworka zakłada tzw. MVW, czyli organizację aplikacji w obrębie model-widok-cokolwiek dzięki, czemu można pogodzić idee JavaScript z modelem MVC. W ostatnim czasie omawiany framwework stał się bardzo popularny, co zaobserwować możemy na wykresie 3.2 oraz zaczął być wykorzystywany w aplikacjach typu enterprise. Za pomocą Angulara stworzone zostały między innymi YouTube na Play Station 3 oraz platforma muzyczna VEVO. AngularJS posiada kilka interesujących rozwiązań, których próżno szukać w innych frameworkach. Oto niektóre z nich:
  52. 52. Rozdział 3. Opis technologii 55 Rysunek 3.2: Porównanie ilości ofert pracy dla popularnych platform klienckich typu MVC, [źródło: http://www.indeed.com/jobtrends]. 3.2.1 Kompilator HTML Główną cechą odróżniającą Angulara od reszty tego typu narzędzi to fakt posiadania własnego kompilatora HTML. Dzięki temu można w prosty sposób rozszerzyć HTML o nowe zdeiniowne wcześniej tagi, które mogą realizować nowe funkcje. 3.2.2 Dwustronne wiązanie danych W przeszłości, zanim technologia AJAX opisana w rozdziale 2.2, była szerzej wykorzy- stywana, do konstruowania interfejsu użytkownika wykorzystywane były narzędzia typu PHP, Rails, ASP.NET lub inne. Serwer odpowiadał za generowanie widoku HTML przed prezentacją go dla użytkownika. Dzięki wykorzystaniu biblioteki jQuery dla języka Ja- vaScript można odświerzać wybrane elementy DOM bez konieczności przeładowywania całej strony. W HTML wstrzykiwane są dane, a następnie wynik jest dodawany do do- wolnej części DOM z wykorzystaniem atrybutu ”inneHtml” dla właściwego elementu. W koncepcji one way binding informacje są pobierane z modelu, który służy za swego ro- dzaju kontener do przechowywania danych i wysyłane do widoku, który ma za zadanie je wyświetlić. Natomiast nie istnieje możliwość wpływania na model z poziomu widoku przy tym podejściu. Zgodnie z diagramem 3.3 dane synchronizowane są tylko z widokiem, to znaczy, że programista musi zaimplementować mechanizm synchronizacji widoku z mo- delem, gdy przykładowo użytkownik wprowadzi dane do aplikacji. Przykładem takiego frameworka jest BackboneJS. Problem ten został rozwiązany w Angularze. Omawiany
  53. 53. Rozdział 3. Opis technologii 56 Rysunek 3.3: Jednostronne wiązanie danych w AngularJS, [źródło: https://docs. angularjs.org/guide/databinding]. framework posiada tak zwane dwustronne wiązanie danych, które pozwala na synchroni- zację stanu widoku i modelu po stronie JavaScript. Wystarczy dodać prostą deklarację, która zdefiniuje, jakie obiekty po stronie kontrolera lub widoku będą ze sobą powiązane. W tej deklaracji wykorzystujemy obiekt $scope oraz dyrektywę ”ng-model”. Na rysun- ku 3.4 możemy zobaczyć, że gdy dajmy na to, zostanie wprowadzona zmiana w widoku, przykładowo wprowadzimy tekst w polu formularza, to dane te automatycznie zostaną zsynchronizowane z modelem. Podobnie działa to w drugą stronę. To znaczy, gdy przy- kładowo dane w modelu zmienią się w wyniku odpowiedzi na zapytanie do API serwera, to mechanizm podwójnego wiązania uaktualni te dane w widoku, czyli w warstwie pre- zentacji dla użytkownika. Dzieki temu rozwiązaniu programista jest odciążony z dbania o aktualny stan danych w każdym miejscu w aplikacji. 3.2.3 Obiekt $scope Charakterystyczną cechą Angulara jest obiekt $scope. Służy on do transportowania modelu pomiędzy widokiem a kontrolerem. Odpowiada też za nasłuchiwanie zdarzeń lub zmian zachodzących w modelu, a także za propagację tych zmian. Pomimo że $scope jest traktowany przez twórców omawianego frameworka, w sposób wyjątkowy to wpsomniany obiekt jest tak naprawdę zwykłym obiektem typu POJO, którego atrybutami możemy
  54. 54. Rozdział 3. Opis technologii 57 Rysunek 3.4: Dwustronne wiązanie danych w AngularJS, [źródło: https://docs. angularjs.org/guide/databinding]. dowolnie manipulować. Warty uwagi jest fakt, iż generalnie $scope jest tworzony i wstrzy- kiwany w sposób automatyczny bez udziału programisty. AngularJS w początkowej fazie ładowania aplikacji tworzy powiązanie między tagiem zawierającym dyrektywę ”ng-app” a wszystkimi elementami znajdującymi się poniżej obiektu $scope. Najwyżej w hierarchii obiektów znajduje się $rootScope, który jest rodzicem wszystkich obiektów $scope. Każ- da aplikacja posiada tylko jeden obiekt typu $rootScope, po którym dziedziczą wszystkie inne obiekty $scope. W fazie początkowej ładowania aplikacji tworzona jest nadrzędna instancja $rootScope. Dobrą praktyką jest relatywnie mała liczba atrybutów przypiasna do niego, gdyż pełni on rolę obiektu golobalnego, który powinien zawierać tylko rzeczy najistotniejsze. Przy korzystaniu z dużej ilości bibliotek zewnętrznych pojawia się ryzyko, iż wystąpi zbieżność nazw medod lub atrybutów przypisanych do obiektów typu $rootSco- pe dlatego też unikanie tego rodzaju sytuacji może zaoczędzić programiście wiele czasu i nerwów. W związku z tym, że zmienna $scope jest inicjalizowana w procesie począt- kowego ładowania aplikacji, czyli tzw. bootstrap elementy przypisane tej zmiennej są od początku dostępne w widoku. Na listingu 3.10 zaczerpniętego z książki [7] mamy przy- kład przypisywania funkcji i atrybutów do modelu po stronie kontrolera. W omawianym przykładzie został zdefiniowany atrybut dateOriginal w globalnym obiekcie $rootScope a

×