Monthly Archives: April 2009

TDD na przykładzie Grails – klasy domenowe i kontrolery

Zaczynam się sypać. Poważnie. Znów mnie coś w kręgosłupie strzyka, nic fajnego, siedzenie jeszcze jakoś mi wychodzi, ale chodzenie to już o wiele gorzej. Choć jedyna rzecz pozytywna z tego wynika – porządki na dysku i kolejny wpis na blogu.

W poprzednim wpisie przedstawiłem specyfikę TDD na przykładzie ograniczeń dla klas domenowych. Przed rozpoczęciem dalszej zabawy przytoczę dodatkowe asercje, które oferuje nam klasa GroovyTestCase, która jest używana zarówno do testów jednostkowych jak i integracyjnych w Grails (oczywiście skopiowane z dokumentacji).

  • assertArrayEquals(Object[] expected, Object[] value)
  • assertLength(int length, char[] array)
  • assertLength(int length, int[] array)
  • assertLength(int length, Object[] array)
  • assertContains(char expected, char[] array)
  • assertContains(int expected, int[] array)
  • assertToString(Object value, String expected)
  • assertInspect(Object value, String expected)
  • assertScript(final String script) // assert that a script runs without exceptions
  • shouldFail(Closure code) // assert that an exception was thrown in that closure
  • shouldFail(Class clazz, Closure code) // the same but for a class

Trochę ułatwiają życie, bez konieczności pisania własnych asercji na szybko. Przetestowaliśmy już ograniczenia dla kasy domenowej, teraz zajmijmy się jej metodami. Założenie, że wypadałoby testować takie metody jak save() czy metody wyszukujące jest trochę kontrowersyjne. Z jednej strony dobrze byłoby testować także i je, ale z drugiej strony to tak, jakbyśmy wątpili w umiejętności programistyczne twórców frameworka. Dlatego też nie jest to zalecana praktyka. Skupimy się zatem na przetestowaniu dopisanej przez programistę metody – a najprostszą metodą tego typu będzie oczywiście toString().

Nasz HtjTopic będzie oczywiście służył do ładnego wylistowania jego zawartości na stronie w formie tabeli, czy ładnej listy, ale zaprezentowana metoda toString() ma tylko charakter podglądowy (dodałem własności pominięte w poprzednim wpisie).

String toString() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd")
"${question} zadane: " + formatter.format(adddate) + " przez ${author.nick}"
}

Test napisany dla tej metody wygląda tak:

void testToString() {
DateFormat df = DateFormat.getDateInstance()
String adddateString = "2009-04-28"
Date adddate = df.parse(adddateString)
def topic = new HtjTopic( question: "Czy potrzebuję komputera?", author: new Htj_Users( nick: "Chlebik" ), adddate: adddate )
assertEquals "Czy potrzebuję komputera? zadane: 2009-04-28 przez Chlebik", topic.toString()
}

I tyle. Zasadniczo wywołanie metod napisanych przez nas samych w klasie domenowej jest podręcznikowym wręcz przykładem testu jednostkowego. Nie trzeba nawet odwoływać się do innych metod, dziedziczenia, etc. Sprawa jest zatem dość prosta i nie trzeba więcej komentarza.

Zajmijmy się teraz kontrolerami. Tutaj sprawa jest o wiele trudniejsza, gdyż kontroler (no chyba, że służy tylko jako ‘zaplecze’ dla wyświetlenia widoku) może zwracać często różne wyniki w zależności od przekazanych parametrów, rodzaju odebranego żądania i tak dalej. To kontrolery z definicji są najbardziej pokomplikowanymi elementami aplikacji zgodnej z wzorcem MVC. Mimo to da się je oczywiście testować, co zresztą zaraz zrobimy.

Testowanie kontrolerów to już nie są testy jednostkowe, choć typologia w tej dziedzinie jest momentami kulawa. Grailsy domyślnie przy tworzeniu klas domenowych tworzą klasę w katalogu z testami jednostkowymi, zaś przy tworzeniu kontrolerów odpowiedni test zostaje stworzony w ktalogu z tzw. testami integracyjnymi. Jak można się domyślić, testy integracyjne służą przetestowaniu relacji i przepływu w danym artefakcie (tutaj akurat kontrolerze). Nie odchodząc za bardzo znów do tworzenia kodu do testów, spróbujmy odnieść się do kodu klasy UsersController, który powstał przy okazji pisania kodu autoryzującego.

Grailsy (jak podaje dokumentacja) domyślnie wrappują tego typu testy (integracyjne) w obiekty Spring Mock Library, co pozwala na odwoływanie się do fałszywych obiektów żądania czy odpowiedzi (sesji też). Dzięki temu możemy stworzyć dla każdego testu warunki identyczne z tymi, w których najczęściej dana akcja będzie pracowała (i z tymi skrajnymi też, testowanie warunków brzegowych to istotna część procesu testowania). By nie przedłużać i komplikować napiszemy szybki test dla akcji login. Sprawdza ona czy użytkownik jest zalogowany, jeśli nie jest to wyświetla formularz, albo dobiera się do tablicy POST. Jednakże kiedy użytkownik nie jest zalogowany (session.user jest puste), wówczas redirectuje na stronę główną oraz wyświetla komunikat o tym, iż użytkownik jest już zalogowany. Oto jak wygląda test:

void testLogin() {
def c = new UsersController()
c.session.user = "jakisobiekt"
c.login()
assertEquals "/", c.response.redirectedUrl
}

I oczywiście test przechodzi nasz kod poprawnie. Należy zwrócić uwagę na ciekawą metodę redirectedUrl, która umozliwia zbadanie docelowego adresu, pod który zostanie skierowane nasze żądanie. Więcej tego typu metod można znaleźć w dokumentacji Spring Mock.

Oczywiście takie same założenia co do testowania odnoszą się do usług, czy command objectów. Testować można również własne znaczniki GSP. Zasadniczo testować można wszystko do czego zresztą gorąco zachęcam.

Advertisements

TDD na przykładzie Grails – klasy domenowe i ich reguły

Nie tak dawno temu zawędrowałem na spotkanie WJUGa, na którym poruszono temat DDD (Domain Driven Development). Okazało się, że Grailsy są pięknym przykładem tego podejścia w praktyce. Jednakże na razie skupimy się na tym, co do tej pory omijałem w moich postach dotyczących frameworka – testach. Pisałem o nich wcześniej w kontekście JUnit, teraz zobaczmy jak zostało to zrobione w Grails.

Framework rozróżnia dwa typy testów – testy jednostkowe oraz testy integracyjne. Póki co zajmiemy się tymi pierwszymi. Zacznijmy od klas domenowych, gdyż to na ich przykładzie najlepiej pokazać jak powinien przebiegać proces budowania aplikacji w oparciu o testy. Jako podstawa posłuży nam kolejna funkcjonalność w aplikacji HowToJava – zadawanie pytań. Stworzymy zatem dwie klasy dziedzinowe – jedna będzie reprezentowała założony temat (wątek pytania, tak samo działający jak post na forum), a także konkretny wpis w ramach tego pytania/tematu.

Tworzymy zatem klasę dziedzinową HtjTopic. Wykonanie polecenia:

grails create domain-class

Powoduje utworzenie nie tylko klasy domenowej, ale także stosownych dla niej testów. Utworzona klasa testowa wygląda tak:

import grails.test.*

class HtjTopicTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}

protected void tearDown() {
super.tearDown()
}

void testSomething() {

}
}

Mamy jak widać metodę wykonywaną na rozpoczęciu testu, a także jedną wywoływaną na koniec. Oczywiście w środku mamy też metodę będącą sednem testu. Przed jego wykonaniem zastanówmy się jednakże czego potrzebujemy w naszej klasie domenowej. Z całą pewnością takie pytanie zadane w serwisie musi mieć autora, tytuł z całą pewnością również by się przydał, data dodania myślę też by się przydała. Ostatecznie po szybkim procesie myślowym powstała taka oto lista:

  • tytuł będący łańcuchem o określonej długości
  • data utworzenia wpisu
  • autor (będzie to zalogowany użytkownik)

Dodamy też później kategorię, do której przynależy nasze pytanie, jednakże by nie komplikować kodu póki co, pozostaniemy przy naszej wersji. Generalna zasada TDD głosi, aby nie pisać nowego kodu, jeżeli testy nie kończą się sukcesem. Na razie nie mamy ani testów, ani kodu, zatem problem jest czysto akademicki. Zasadniczo jedyną rzeczą, którą można przetestować w wyraźny sposób póki co są ograniczenia i to, czy nasza klasa ich przestrzega. Stworzymy sobie obiekt imitujący zachowanie prawdziwego obiektu (tzw. mock) i na nim będziemy testować. Można by zastanowić się po co testować ograniczenia wpisywane do klasy, które mają na celu właśnie kontrolę i testowanie, czy poprawne wartości zostały wpisane? Autorzy frameworka odpowiadają na to pytanie prosto – bo tak :), ale też by uniknąć literówek, które (rzekomo) są plagą w tym przypadku. Grailsy z definicji ułatwiają życie i w związku z czym oferują gotowy szkielet do testowania. Nasza metoda testująca powinna wyglądać na razie tak:

void testConstraints() {
def existingTopic = new HtjTopic()
mockForConstraintsTests(HtjTopic, [ existingTopic ])

// Testujemy constraint notNull
def topic = new HtjTopic()
assertFalse topic.validate()
assertEquals "nullable", topic.errors["question"]

// Testujemy constraint blank
topic = new HtjTopic( question: "" )
assertFalse topic.validate()
assertEquals "blank", topic.errors["question"]

// Testujemy constraint minSize
topic = new HtjTopic( question: "test")
assertFalse topic.validate()
assertEquals "size", topic.errors["question"]

// Testujemy constraint maxSize
topic = new HtjTopic( question: "test test test")
assertFalse topic.validate()
assertEquals "size", topic.errors["question"]

// Na koniec dla pewnosci, ze poprawne dane powoduja przejscie testow
topic = new HtjTopic( question: "Jak pisać" )
assertTrue topic.validate()
}

Jej zrozumienie nie powinno nastręczać problemów. Dziwić mogą tylko dwie pierwsze linijki. Wywołanie metody mockForConstraintsTests w takim kształcie ‘uzbraja’ egzemplarze klasy domenowej w metodę validate(), co pozwala na sprawdzenie poprawności constraints (Uwaga: skróciłem długość tematu by ładniej to wyglądało w kodzie na blogu. Normalnie oczywiście powinien być dłuższy). Testy (uruchamiane w NetBeans kombinacją klawiszy Alt-F6) pokażą po pierwszym wywołaniu błędy bo i pewnie nie ma jeszcze klasy, której to obiekty chcemy utworzyć. Oto kod klasy HtjTopic:

class HtjTopic {

String question

static constraints = {
question( blank: false, size: 5..10, nullable: false )
}
}

To oczywiście na razie tyle, aby łatwiej było pokazać ideę testów. Wyniki testów możemy ładnie sobie obejrzeć w przeglądarce (katalog /tests w folderze projektu) – każdy błąd lub niepowodzenie testu zostanie odnotowane. Filozofia pisania testów jest taka, aby nie napychać jednej metody chamską wręcz ilością linii kodu. Należy to rozbić, np. ograniczając metody do konkretych pól, albo też do walidacji metodami poszczególnych ograniczneń (np. tylko wartości NULL).

Dodam jeszcze tylko, że testowanie odbywa się w spejcalnym środowisku testowym, które posiada oddzielny wpis konfiguracyjny w pliku DataSource.groovy. Oto jak powinna wyglądać konfiguracja, aby opierała się na HSQLu.

test {
dataSource {
pooled = true
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
dbCreate = "create-drop"
url = "jdbc:hsqldb:mem:testDb"
}
}

To tyle na dziś. W następnym wpisie zajmiemy się dodatkowymi metodami klas dziedzinowych oraz zerkniemy na inne artefakty, które można podpiąć pod testy.

Helion się poprawia

Naprawdę! Wreszcie po okresie względnej posuchy jeśli chodzi o książki o wiadomej tematyce zaczęło się cos w tym temacie zmieniać. Wpierw kolejne wydanie Head First o JSP, potem 8 wydanie z serii Core, drugi tom książki o JSF, nowe wydanie Blocha, a tutaj jeszcze…

.. bardzo ciekawa zapowiedź. Wreszcie wszystko w jednym miejscu, pewnikiem nie będzie to aż tak bardzo rozbudowane jak mogłoby być (choć 888 stron robi wrażenie), ale z pewnością jest to pierwsza na poważnie tego typu pozycja na rynku. Kiedyś w bibliotece UW mignęło mi coś o Ancie, ale to jakaś prehistoria była. Miejmy nadzieję, że Helion nie zawiedzie i pojawi się kolejna ciekawa pozycja na rynku.

Chlebik walks alone

Ah taki przewrotny tytuł tegoż wpisu 🙂 Konkretnie rzecz w tym, iż z czasem usługa na mor.ph przestała mi wystarczać. Jednak co własna przestrzeń to własna przestrzeń. Jednakże jakoś do tej pory nie za bardzo miałem okazję, aby zapoznać się z kwestią administracyjną Javy. Dlatego zrobiłem to co każdy mądry człowiek by uczynił – zatrudniłem do tego ludzi.

A konkretnie wstępnie ukończyłem rozmowy w sprawie przestrzeni serwerowej.  Na razie nie będzie to nic wielkiego, ale umożliwi pokazanie wszystkich moich “dzieł” 🙂 szerokiej publiczności bez oglądania się na oferowane rozwiązania. Płacę i mam pewną usługę. Wstępnie na serwerze będzie też działał serwer WWW z PHP zatem może uda się postawić jakąś małą platformę WWW dla Grailsów, gdyż póki co w polskim internecie posucha jeśli chodzi o strony o tej technologii.

Póki co zatem HowToJava znika z netu, wróci niebawem. Dział również czynię niewidocznym, mam nadzieję, że powróci za mniej więcej dwa tygodnie wraz z szeregiem innych rzeczy.

Uzyszkodnicy i aplikacja, cz. II

Użytkowników w bazie powiedzmy, że już mam. Teraz wypadałoby zrobić coś, aby dać im możliwość zarejestrowania się w serwisie.

Proces ten jest prosty jak konstrukcja gwoździa. Wpisujemy login, wpisujemy hasło (dwa razy), wpisujemy napisik z CAPTCHA i po walidacji formularza ( np. nick unikalny i hasło nie za krótkie ) tworzone jest konto w serwisie. Do tego można się na nie nawet zalogować! Zasadniczo skoro domenę już mamy (przynajmniej na początek), widok sobie zaraz stworzymy, kontroler też ogarniemy. Jedyną rzeczą, o której póki co nie za bardzo mam pojęcie to wdrożenie CAPTCHY. Sensowność jej wdrożenia polega na tym, iż jest to plugin do frameworka, a takowymi jeszcze się nie bawiłem (no chyba że skryptami/pluginami do wrzucenia aplikacji na serwer mor.ph).

Dla procesu rejestracji oraz późniejszego logowania stworzyłem w poprzednim wpisie kontroler Users. Teraz należy usunąć z niego wpis do scaffoldingu i zabrać się do samodzielnej pracy. Tworzymy akcję register, a także stosowny widok. Kod kontrolera jest oczywisty,  widok (registry.gsp) to też prosty formularz. Wkleiłbym kod (używam tam znaczników GSP), ale niestety WordPress wygrał ze mną i za cholery nie dało się mimo wpisania encji, etc. umieścić tego kodu tutaj w ładnej formie. Chyba czas przesiąść się na Joggera czy coś.

Pluginy do Grailsów możemy znaleźć na stronie domowej projektu. Ja postanowiłem użyć JCAPTCHA, polecanego w książce “Beginning Groovy and Grails”. Co prawda w treści wyżej wymienionej pozycji używa się prostszego rozwiązania, nie ma sensu przepisywać kodu, lepiej czegoś nowego się nauczyć. JCAPTCHA jest oficjalnym rozszerzeniem frameworka zatem wystarczy w konsoli wpisać (w katalogu naszej aplikacji):

grails install-plugin jcaptcha

By po chwili mielenia cieszyć się zaimportowanym rozszerzeniem. Tutaj dopiero zaczyna się przygoda. JCAPTCHA to nie w kij dmuchał i z nim trzeba już się trochę nagimnastykować. Wpierw trzeba wyedytować plik konfiguracyjny:

jcaptchas

{

image = new GenericManageableCaptchaService(

new GenericCaptchaEngine(

new GimpyFactory(

new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz1234567890"),

new ComposedWordToImage(new RandomFontGenerator(

20, 30, [new Font("Arial", 0, 10)] as Font[]),

new GradientBackgroundGenerator(140, 35,

new SingleColorGenerator(Color.white),

new SingleColorGenerator(new Color(152, 245, 255))),

new NonLinearTextPaster(6, 6, new Color(108, 123, 139))

)

)

),

180,

180000)

}

Nie pytajcie co i z czym. Gotowca ściągnąłem z Disco Bloga. Dla mnie wystarczy, gdybym zaczął kombinować, aby tło było różowe, a literki czarne i do tego robiło BLING BLING co 2 sekundy to może bym się wgłębiał w meandry tego zapisu. Nie muszę to i nie zawracam sobie głowy. Na samym początku pliku należy dodać importy odpowiednich plików – lista pod ww. adresem. Dodać też trzeba linijkę w kodzie widoku, aby wrzucił nasz anty-spam do formularza:

<input type="text" id="captcha"  name="captcha" value=""/>

<jcaptcha:jpeg name="image"/>

Oto zdjęcie z efektem mojej pracy:

register_captcha1

Pozostaje tylko napisać kod kontrolera i logikę. Logika jest prosta – walidacja przekazanych danych i lecimy do przodu. Najistotniejszy wydaje się być kontroler, a także mechanizm do pokazywania błędów walidacyjnych (wrzucę to do layoutu by działał uniwersalnie wszędzie). Wymienione powyżej funkcjonalności głupio byłoby powielać w co drugim kontrolerze – choć i to jest metoda, przynajmniej w przypadku walidacji. Jednakże tutaj będę mądry i stworzymy takie coś jak service, czyli usługę. Jest to po prostu oddzielna klasa, która oferuje pewną funkcjonalność, a którą możemy łatwo wywołać z poziomu kontrolera. Co więcej – usługisingletonami (domyślnie) z całym dobrodziejstwem inwentarza.

Usługę utworzyć prosto – prawyklik na katalogu Services w NetBeans, wpisujemy nazwę klasy (u mnie formValidation) i mamy gotową usługę. Mocą konwencji do nazwy klasy jest dopisywany suffix ‘Service’. Oto wygenerowany kod:

class FormValidateService {boolean transactional = truedef serviceMethod() {}

}

W miejsce serviceMethod rzecz jasna trzeba wcisnąć nazwę metody, a także jej ciało. Kiedy tak się stanie mamy gotową na każde wezwanie usługę. Ja postanowiłem stworzyć dwie takowe – jedną uniwersalną do walidacji pól formularza, a także jeszcze jedną do translacji nazw pól (pole ‘name’ w elementach formularza) na polskie odpowiedniki celem ich ładnego wyświetlenia przy ew. błędzie walidacji. Oto kod usługi formValidate:

class FormValidateService {static int FIELD_INCORRECT = 0;

static int FIELD_COMPARE_INCORRECT = 1;

String fieldNamePl;

String fieldConfirmNamePl;

String defaultMessage;

boolean transactional = true

def translateFieldServiceHashMap validateForm( ArrayList Data ) {

HashMap dataValidated = new HashMap();

Boolean isValidated = true;

for( HashMap mapParams : Data ) {

if( mapParams.getAt('paramGiven') == mapParams.getAt('paramExpected') ) {

// dataValidated.putAt( mapParams.get('index'), true );

} else {

fieldNamePl = translateFieldService.translateName( mapParams.get('index') );

fieldConfirmNamePl = translateFieldService.translateName( mapParams.get('index_confirm') );

int compareStatus = mapParams.getAt('compareStatus');

if( compareStatus == FIELD_INCORRECT ) {

defaultMessage = 'Pole [' + fieldNamePl + '] ma niepoprawną wartość!';

}

else if( compareStatus == FIELD_COMPARE_INCORRECT ) {

defaultMessage = 'Pole [' + fieldNamePl + '] ma wartość inną niż pole [' + fieldConfirmNamePl + ']';

}

else {

defaultMessage = 'Pole [' + fieldNamePl + '] ma niepoprawną wartość!';

}

dataValidated.putAt( mapParams.get('index'), new org.springframework.validation.FieldError( 'FormValidateService', fieldNamePl, fieldNamePl, true, new String[1], new Object[1], defaultMessage ) );

isValidated = false;

}

}

return [ validated: isValidated , data: dataValidated ];

}

}

Jak już wspomniałem usługi to singletony (jest to zachowanie domyślne, ale jak wszystko w Grails daje się to zmienić), a odwoływanie się do nich jest banalne. Wystarczy w kontrolerze Users wpisać taki kod:

def formValidateService

I od tej pory do każdej z metod tejże usługi możemy odwoływać się z dowolnego miejsca w kontrolerze. Jak widać na powyższym przykładzie z poziomu kodu usługi możemy odwoływać się do funkcjonalności innej usługi. Oto jeszcze kod małej usługi tłumaczącej – można to oczywiście oprzeć o domyślne ustawienia i plikach z domyślnymi komunikatami. Póki co działa to w takiej formie, gdyż wówczas mamy dowolność – trzymania komunikatów np. w bazie danych, niekoniecznie zaś na plikach (co ułatwia często pracę). Kod:

class TranslateFieldService {boolean transactional = trueString translateName( String toTranslate ) {

String returnString = '';

HashMap translations = new HashMap();

translations.put( 'login', 'login' );

translations.put( 'passwd', 'hasło' );

translations.put( 'passwd_confirm', 'potwierdzenie hasła' );

returnString = ( translations.containsKey( toTranslate ) ) ? translations.getAt( toTranslate ) : toTranslate ;

return returnString;

}

}

I tyle nasze usługi. Powiedzmy, że dają one pewną funkcjonalność, choć z całą pewnością można by je ulepszyć. Dla naszej aplikacji ważne będzie też miejsce, gdzie możnaby wyświetlić informacje przeznaczone dla uzytkownika (błędy walidacji, informacje o charakterze neutralnym). By to osiągnąć dodałem trochę magii w pliku layoutu main.gsp, ale nie jest to nic wykraczającego poza zwykły IF. No i teraz nadszedł czas na kontroler:

class UsersController {def defaultAction = "index"

def formValidateService

def JcaptchaService

def index = { }

def register = {

if( session.user == null ) {

HashMap validationResults;

if( request.method == "POST" ) {

validationResults = formValidateService.validateForm(

[ [index: 'passwd', index_confirm: 'passwd_confirm',paramGiven: params.passwd.toLowerCase(),paramExpected: params.passwd_confirm.toLowerCase(), compareStatus: formValidateService.FIELD_COMPARE_INCORRECT ],

[index: 'captcha', paramGiven: JcaptchaService.validateResponse( 'image', session.id, params.captcha), paramExpected: true ] ] )

if( validationResults.getAt('validated') == true ) {

Htj_Users user = new Htj_Users( nick: params.nick, passwd: params.passwd.encodeAsMD5(), role: Htj_User_Role.get(1) );

if( user.save() ) {

session.user = user

flash.message = 'Rejestracja zakończona pomyślnie'

redirect(uri: '/')

} else {

flash.errors = user.errors.getAllErrors()

}

} else {

flash.errors = ((HashMap) validationResults.getAt('data')).values()

}

[ params: params ]

}

} else {

flash.message = 'Użytkownik już jest zalogowany!'

redirect( uri: '/' )

}

}

def login = {

if( session.user == null ) {

if( request.method == 'POST' ) {

Htj_Users user =

Htj_Users.findByNick(

params.login )

if( user != null ) {

session.user = user;

redirect( uri: '/' );

} else {

HashSet errors = new HashSet();

errors.add(new org.springframework.validation.FieldError( 'FormValidateService', 'login', 'login', true, new String[1], new Object[1], 'Podano złe dane!' ));

flash.errors = errors

}

}

} else {

flash.message = 'Już jesteś zalogowany!'

redirect( uri: '/' )

}

}

def logout = {

session.user = null;

flash.message = 'Zostałeś pomyślnie wylogowany!'

redirect( uri: '/' );

}

}

Jak przekonuję się po raz kolejny WordPress ma strasznie kijowe formatowanie kodu. Dlatego też sugeruję skopiowanie powyższego do IDE i przejrzenie kodu w bardziej przyjaznej formie. Myślę, że nie powinien on sprawić trudności. Poniżej kilka screenów z efektów działania programu:

Błąd rejestracji:

bledyrejestracji

Pomyślna rejestracja i zalogowanie:

zarejestrowanopomyslnie

To tyle. Oczywiście w realnie działającej aplikacji tak przeprowadzony proces rejestracji wołałby o pomstę do nieba – najlepiej byłoby dodać dla użytkowników pole e-mail, aby można było przesłać na wskazany adres mejl aktywujący (no i później w aplikacji wygodniej byłoby posiadać adresy swoich użytkowników). Również usability pewnikiem kuleje, podobnie jak sam kod 🙂 Jednakże tak jakoś głupio wyszło, że napisanie tego posta zajęło mi dwa tygodnie więc podaruję sobie te małe błędy.

Gdyby takie książki pisali dla PHP

Podczas moich studiów politologicznych próbowano mnie nauczyć wielu rzeczy. Mniej lub bardziej przydatnych, czasami przeraźliwie nudnych, a czasem szalenie ciekawych. Była też jeszcze jedna kategoria – przedmiotów połączonych z wykładowcą.

Do takowych należał przedmiot System polityczny RP i miał za zadanie wkuć nam do głowy kolejne artykuły z konstytucji, podstawowych ustaw i inszych tego typu pozycji. Niektórzy lubili, a inni nie. Jednakże na zajęcia z tegoż przedmiotu chodzili często ludzie spoza wydziału, notoryczni imprezowicze i lenie, a także (o dziwo 🙂 ja sam. Dlaczego? Bo zajęcia prowadził dr Ryszard Chruściak (pozdrawiam). Człowiek ten brał udział w pracach nad naszą obecną konstytucją, a opowieści z ówczesnych posiedzeń szanownych gremiów wywoływały nie raz salwy śmiechu. Naprawdę cudna rzecz.

Skąd ten przydługi wstęp? Ano dlatego, iż podobne wrażenie odniosłem podczas lektury niedawno wydanej przez Helion książki – Java. Efektywne programowanie. Jej autorem jest Joshua Bloch, którego najlepiej chyba przedstawi wpis na Wiki. Jest on zatem człowiekiem, który zna się na rzeczy, a swoje doświadczenie po raz kolejny przedstawił szerokiemu gremium publikując drugie wydanie swej znakomitej książki.

Podczas lektury swoiste deja vu, którego podstawy przedstawiłem we wstępie. Z każdego rozdziału, z każdego napisanego zdania płynie szerokim strumieniem wiedza autora, której nie zdobywał na jałowych dyskusjach. W kontekście, iż być może niedługo wezmę się za SCJP, nagle zobaczyłem jak wiele błędów można popełnić nadpisując choćby standardowe metody każdego obiektu w Javie! I to nie tylko błędów, często również nieścisłości (hierarchia klas, ograniczanie dostępu), albo też wskazówek wydajnościowych (typy proste, a klasy opakowujące).

I po tych ochach i achach zadam jedno krótkie pytanie – dlaczego coś takiego nie powstało póki co dla programistów PHP? Choć może to i dobrze – wtedy z pewnością nie kupowaliby książki Blocha kiedy w końcu z tego PHP uciekną 🙂 Lekturę jednakże z czystym sumieniem polecam każdemu. Nie jest to na pewno ‘jednowieczorny’ temat – przeczytać można i całą a jednym podejściem. Potem jednakże jak z innymi mądrymi książkami – należy do niej wracać, wszak udoskonalanie warsztatu programistycznego jest procesem nigdy nie mającym końca.

Wesołych nie świąt

Trochę ostatnio cicho na blogu – kilka przypadków losowych sprawiło, że kolejnego posta piszę już drugi tydzień. No ale mam wymówkę – święta.

Wiadomo jak to bywa. Trzeba posprzątać w końcu, umyć okna i przekonać się, że to ich brud zasłania świat, a nie blok naprzeciwko. Coś i ugotować by mieć na wkupne na rodzinne wizyty. W końcu odpocząć i poświęcić choć chwile rodzinie, a w moim przypadku mojej drugiej połówce. I tego też życzę wszystkim czytelnikom bloga. Aby nie poddawali się ‘easteringowi’ ( parafrazująć ‘christmasing’ ). Jedzcie, pijcie, spotykajcie się ze znajomymi, rodziną,wyjdźcie gdzieś, cokolwiek, byle było to fajne i z dala od komputera.

A jak wrócicie to będę czekał z kolejnym wpisem. Miłego.

PS. Pyszną białą kiełbasę kupiłem.