29 stycznia 2009

Web 2.0 i Grails - rozdział 8 z "Beginning Groovy and Grails"

Kolejny rozdział 8. "Web 2.0 - Ajax and Friends" z Beginning Groovy and Grails: From Novice to Professional za mną. I kolejny raz sprowadziło się do przedstawienia kilku wtyczek grailsowych, które umożliwiają skorzystanie z funkcjonalności ogólnie dostępnych projektów.

Dotychczasowy rozwój aplikacji webowej w Grails wykorzystywał jedynie bardzo podstawowe elementy HTML. Według autorów książki aplikacja webowa powinna mieć to coś, co spowoduje zainteresowanie nią. Nie musi być specjalnie funkcjonalna. Musi mieć to coś. Oczywiście, jeśli będzie funkcjonalna to tym dla niej lepiej, ale nie jest to kryterium przesądzające, który serwis webowy jest dobry. Dobre aplikacje są jedynie dobre (obym mógł napisać chociaż to o swoich!) i "will not generate revenue". W kolejnych rozdziałach przedstawione zostaną techniki, które znacząco uatrakcyjnią aplikację. Ważne, aby aplikacja dostarczała funkcjonalność w interesujący sposób, który sprawi zadowolenie u użytkowników i sprawi, że wrócą do niej (bez konieczności odgórnych poleceń).

Rozdział rozpoczyna się wdrożeniem funkcjonalności pełnotekstowych. Chodzi o to, aby opis zadania składał się z wytłuszczeń, italik, itp. Interpretowanie HTMLa wprowadzonego przez użytkowników jest niezalecane - zbyt wiele zabawy z tym, a i ryzyko zniszczenia strony duże. Instalacja edytora tekstowego, jakkolwiek sprowadza edycję tekstu do pracy podobnej z MS Word czy OpenOffice (kolejność przypadkowa) jest wartościowe, ale często zbyt zaawansowane. Taką funkcjonalność mamy dzięki wtyczce FCK editor. Ostatecznie wybrano udostępnienie (meta)języka ala Wiki z pomocą wtyczki radeox, która opakowuje projekt Radeox z SnipSnap jako Wiki. Instalacja z grails install-plugin radeox i od tej pory możemy korzystać ze znacznika g:radeoxRender do opakowania tekstu, który ma podlegać obróbce przez Radeox, np. (Listing 8-2):
 <g:radeoxRender>${todo?.note}</g:radeoxRender>
Kolejna wartościowa funkcjonalność w aplikacjach webowych to możliwość wyszukiwania treści. W Grails realizujemy to przez wtyczkę Searchable, która oparta jest na OpenSymphony Search Engine, który z kolei korzysta z Apache Lucene. Instalacja to typowe grails install-plugin searchable. Deklarujemy, które klasy domenowe mają być przeszukiwane przy pomocy statycznego bloku static searchable = true. Domyślna strona wyszukiwania to http://<server>:<port>/<kontekst>/searchable.html. Jakby inaczej mogła nazywać się domyślna strona wtyczki searchable?! Niesamowite użycie "konwencji nad konfiguracją". Jeśli nawet Grails nie stanie się Twoim szkieletem webowym, to warto wykorzystać jego cechy w swoich projektach. Połączenie wtyczki radeox z searchable to edycja pliku grails-app/searchable/index.gsp i dodanie g:radeoxRender w odpowiednich miejscach.

Następną funkcjonalnością jest zapis plików na serwerze, która jest realizowana przez nikogo innego, jak sam Spring Framework. Wystarczy zadeklarować w klasie dziedzinowej dodatkowe pola typu byte[] na treść pliku oraz dwa pola typu String dla nazwy pliku i typ, wykorzystać <input type="file" name="<nazwa_pola_typu_byte[]_w_klasie_dziedziny>" /> oraz w formularzu ustawić enctype="multipart/form-data". Następnie w springowym pliku konfiguracyjnym w Grails - conf/spring/resources.xml - zadeklarować właściwe ziarno springowe na bazie CommonsMultipartResolver (zainteresowanych szczegółami zachęcam do lektury książki). Ostatnim krokiem jest obsługa zapisu, tj. obsługa akcji w kontrolerze - zabawa z MultipartHttpServletRequest oraz CommonsMultipartFile. Nic szczególnie interesującego dla zainteresowanych Grails - zabawa konfiguracją springową.

I ostatnia funkcjonalność - wysyłanie emaili. Ponownie na scenie pojawia się Spring Framework ze swoim Spring Mail - org.springframework.mail.MailSender. Dużo konfiguracji springowej w resources.xml, aby ostatecznie zapoznać się z akcją sendAcknowledgment, gdzie korzysta się z grailsowego SimpleTemplateEngine. Fajne tricki, które sprowadzają temat szablonów do kilku wywołań - zawsze miałem przekonanie, że koniecznych jest więcej.

Biblioteki znaczników (ang. tag libraries) dostarczają zbiór własnych akcji, które wykorzystujemy na stronach GSP. Tworzymy biblioteki, aby zawrzeć powtarzające się kawałki kodu w mniej obszernej strukturze, łatwiejszej do użycia w wielu miejscach (co pozwala nam na przestrzeganie zasady DRY - ang. Don't Repeat Yourself). Jak zapewniają autorzy "creating a tag library with Grails is simpler than that" (gdzie "that" to "tworzenie znaczników w szkieletach javowych" - pewnie "piją" do JSP czy JSF, w których tworzenie znaczników to faktycznie wyzwanie).
Biblioteki znaczników w Grails umieszczamy w grails-app/taglib. Są to zwykłe klasy grailsowe POGO (ang. Plain Old Groovy Object), w którym nazwy metod to nazwy znaczników. Do metod przekazywane są dwa parametry attrs oraz body, które reprezentują, odpowiednio, atrybuty oraz ciało znacznika, np. dla poniższej metody
 def wyswietl = { attrs, body ->
def parametrTekst = attrs['tekst']
out << """Skoro prosisz, to wyświetlam tekst "${parametrTekst}"."""
}
odpowiadający znacznik jest <g:wyswietl tekst="do wyswietlenia" />, gdyż wszystkie znaczniki należą do domyślnej przestrzeni znaczników w Grails g. Jeśli chcielibyśmy zmienić przestrzeń dla naszych znaczników dodajemy static namespace = 'naszIdentyfikator' do klasy znaczników. Warto również zwrócić uwagę na użycie potrójnego cudzysłowu, w którym możemy używać dowolnego znaku, nawet cudzysłowu, bez groźby wpadki. Poza tym parametr out wskazuje na standardowe wyjście - na stronę.

AJAX (ang. Asynchronous JavaScript and XML) to sztandarowa technika wspierająca Web 2.0. Nie jest to żadna technologia, a zbiór technologii, jak, wspomniane w nazwie, JavaScript i XML, ale nie tylko. W rozwiązaniach AJAXowych stosuje się również technologie typu JSON. AJAX pozwala na wysłanie danych do serwera asynchronicznie (synchronicznie również, ale w tym nie ma nic szczególnego) oraz wczytanie odpowiedzi bez konieczności przeładowania całej strony. Największym wyzwaniem przy wdrożeniu technologii ajaxowej bezpośrednio jest skorzystanie z przesłanych z serwera danych. To jest miejsce dla szkieletów ajaxowych, które ukrywają przed nami zawiłości AJAX oraz obsługi różnych typów przeglądarek. Popularne szkielety ajaksowe to Prototype, Dojo Toolkit, script.aculo.us oraz Yahoo! User Interface (YUI), a nawet zaklasyfikowano Google Web Toolkit (GWT). Ruby on Rails korzysta z script.aculo.us, Tapestry Tacos z Dojo, a Grails...wszystkie z wymienionych (!) I jak tu nie polubić Grails?! Wybór tego jednego szkieletu ajaxowego to połączenie znacznika <g:javascript library="<nazwa_szkieletu_ajaxowego>">, gdzie nazwa_szkieletu_ajaxowego to prototype, yahoo, scriptaculous oraz dojo. Ten ostatni, ze względu na swój rozmiar, nie jest domyślnie dostarczany z Grails i wymaga instalacji poleceniem grails install-dojo.

Domyślnie dostarczany zestaw znaczników ajaxowych GSP to remoteField, remoteFunction, remoteLink, formRemote, javascript oraz submitToRemote. Dostarczenie własnych ajaxowych komponentów graficznych to zabawa już na poziomie wybranego szkieletu ajaxowego. W książce postawiono na script.aculo.us, jako że krewniak Ruby on Rails właśnie jego używa i w ten sposób można porównać pracę obu.

Wdrożenie AJAXa sprowadza się do zmiany zachowania sposobu odświeżania części stron, tzw. częściowe odświeżenie, czyli w Grails jest to przede wszystkim stworzenie nowego szablonu zwanego częściową stroną (wycinkiem?). Wymaganiem Grails (i Ruby on Rails) jest, aby wywołania ajaxowe były kierowane do innej strony, która z kolei jest dołączona do rozkładu (ang. layout). Wyświetlenie strony z innej to użycie znacznika GSP - g:render. W ruch idzie g:formRemote.

Następny przykład w rozdziale to edycja pola "w miejscu" - po kliknięciu pojawia się ekran edycyjny w miejscu wybranego pola. Tworzony jest nowy znacznik g:editInPlace oparty na script.aculo.us. Jeśli własny znacznik, to nowa klasa AjaxTagLib w katalogu grails-app/taglib z metodą editInPlace (dokładnie taka, jak nazwa znacznika).

Przedstawienie obsługi AJAX w Grails kończy stworzenie funkcjonalności autouzupełniania, które polega na dynamicznym wyświetlaniu listy rozwijalnej z pasującymi do wprowadzonych już znaków ciągami. W zasadzie nic nowego w kontekście nauki Grails poza kolejnymi kontrolerami i akcjami, które wykorzystują zapytania kryteriowe z Hibernate, które ubrane w grailsowy DSL (a w zasadzie GORMowy DSL) przedstawiają się następująco (Listing 8-30):
 def findUsers = {
def users = User.createCriteria().list {
or {
like("userName", "%${params.userId}%")
like("firstName", "%${params.userId}%")
like("lastName", "%${params.userId}%")
}
order("lastName")
order("firstName")
}
}
Niesamowite, jak łatwo czyta się taki kod. Do utworzenia nieuporządkowanej listy użytkowników do wyświetlenia na stronie autorzy skorzystali z grailsowego groovy.xml.MarkupBuilder. Utworzenie listy nieuporządkowanej wygląda podobnie do powyższego zapytania kryteriowego, tj. (Listing 8-30):
 def writer = new StringBuffer()

new groovy.xml.MarkupBuilder(writer).ul {
for (u in users) {
li(id: u.id, "$(u.lastName), $u.firstName")
}
}
render writer.toString()
Na zakończenie ostatnia ciekawa funkcjonalność do rozważenia w naszych aplikacjach webowych celem ich uatrakcyjnienia - strumień RSS (ang. RSS feed). Funkcjonalność dostarczana w Grails przez wtyczkę Feeds, którą instalujemy przez grails install-plugin feeds. Później dedykowany kontroler z akcją, która zwraca strumień w odpowiednim formacie, np. Atom. Ponownie ilość kodu zdumiewa - kilka linijek i temat obsłużony. Cudo! Jeśli tylko będzie okazja na wdrożenie Grails, zrobię to bez wahania. Na pewno niezapomniane wrażenia (oby nie tylko początkowe).