Parę słów o grails

Do zakończenia książki Programming Groovy: Dynamic Productivity for the Java Developer zostało mi kilka rozdziałów, które nadrobię w jak ta wiedza mi będzie potrzebna. Ale, że groovy to dopiero pierwszy krok, więc teraz pora na grails. Ten wpis będzie oparty na książce Beginning Groovy and Grails: From Novice to Professional a kolejne na The Definitive Guide to Grails z powodu tego, że ta druga korzysta z wersji 1.1. A między wersjami są pewne różnice.

Do bardzo szybkiego startu można skorzystać z tego tutorialu, dzięki zostanie stworzona bardzo ale to bardzo prosta, funkcjonalna aplikacja CRUD.

W grails mamy do czynienia z konwencja ponad konfigurację (convention over configuration) i wzorcem mvc (model to klasy domenowe, view – gsp).
Grails zapewnia z miejsca serwer, bazę danych, system budowania i testy.
scaffolding – tworzenie CRUD bardzo małym nakładem kodu, tak jak w powyższym tutorialu.
GORM, pluginy, agile są bardzo mocno powiązane z grails.

Grails nie wymyśla koła na nowo, tylko integruje w sobie wiele rozwiązań:
groovy
spring
hibernate
siteMesh
ajax(script.aculo.us, rico, prototype)
jetty
hsqldb
junit
gant

Tworzenie aplikacji używając scaffoldingu można sprowadzić się do następujących kroków:
1.Utworzenie aplikacji
2.Uruchomienie
3.Stworzenie klas domenowych
4.Implementacja testów
5.Uruchomienie testów i poprawka klas domenowych
6.Stworzenie kontrolera
7.Powtarzanie kroków 3-6 do momentu aż domeny i kontrolery będą kompletne a testy będą przechodzić.

Stworzenie aplikacji:

create-app projectName

W netbeansie 6.7.1 file/new project/groovy/grails, w następnym oknie podając nazwę i lokalizację. Wcześiej należy skonfigurować ścieżkę do grails.
Tworzy się nowy katalog, jeśli operacje są wykonywane z terminala to polecenia należy wykonywać z poziomu tego katalogu (root aplikacji). Struktura katalogów, jest standardowa, i każdy ma swoją rolę.

Uruchomienie aplikacji (serwera):

run-app

(netbeans prawy na nazwie projektu i run)

Tworzenie klasy domenowej

create-domain-class clasName

(prawym na domainClasses, new i Grails domain class). Zostaje utworzona klasa oraz klasa testowa.
Uruchomienie testów

test-app

(prawy na projekcie i test). Raport znajduje się w test\reports\html\.

Usuwanie wszystkiego to

Obiekt.list()*.delete()

Uwaga list ładuje wszystkie obiekty z bazy, więc mogą pojawić się problemy z pamięcią.
Istnieją rozszerzenia assertów np assertToString().
Id i version nie są widoczne w klasie domenowej. Są tworzone domyślnie.

Constrainty dla pól w klasie domenowej tworzy się poprzez:
static constraints = {
name(blank:false)
createdDate()
priority()
status()
note(maxSize:1000, nullable:true)
completedDate(nullable:true)
dueDate(nullable:true)
Jednocześnie ograniczenia mogą reprezentować elementy w html (maxSize to textarea a sam String to input) i są pokazywane na stronie w takiej kolejności jak są wpisane w ograniczenia. Walidacja i komunikaty pojawiają się jeśli ograniczenie jest złamane.

Tworzenie kontrolera

create-controller name

(prawy na kontrolerze).

Użycie scaffoldingu to

def scaffold = Klasa domenowa

Klasa nie może się nazywać Category. Powoduje to błąd

ERROR plugins.DefaultGrailsPlugin  - Cannot generate controller logic for scaffolded class interface groovy.lang.Category. It is not a domain class!

Ewentualnie można użyć tej klasy razem z pakietem. W wersji grails 1.1 doszła klasa Category w pakiecie groovy.lang, a ten pakiet jest domyślnie zaimportowany. Należy wyczyścić zawartość docelowego katalogu (tam gdzie jest to kopiowane u mnie: C:\Documents and Settings\User\.grails\1.1.1\projects\collab-todo [polecenie clean]

static belongsTo = [User, Category]

belongsTo oznacza, że dany obiekt zostanie usunięty jeśli powiązane obiekty zostaną usunięte (np User lub Category).
W oknie edycji/tworzenia nowego obiektu te pola są reprezentowane jako selecty a wyświetlone są za pomocą metody toString().

static hasMany = [todos:Todo]

hasMany – relacja jeden do wielu, dostęp do właściwości poprzez klucz (tutaj todos)

Uzupełnienie o scaffolding w grails.

Dynamiczne typowanie w groovy

Venkat Subramaniam w czwartym rozdziale swojej książki Programming Groovy: Dynamic Productivity for the Java Developer przedstawia dynamiczne typowanie.

Zalety dynamicznego typowania:
-Pozwala osiągnąć większy stopień polimorfizmu niż inne języki statycznie typowane
-Nie trzeba rzutować na inne obiekty

Dynamiczne typowanie jest czymś innym niż słabe typowanie.

Domyślnie każdy obiekt jest typu Object.

Projektowanie przez zachowanie/możliwości – design by capability.
W statycznym typowaniu należy tworzyć klasy nadrzędne/interfejsy i przy dodaniu nowej rzeczy więcej problemów z rozbudową, przy dynamicznym jeśli obiekt posiada daną metodę to znaczy ze jest „czymś”(zachowuje się jak „coś”).

„Jeśli coś chodzi jak kaczka i kwacze jak kaczka to jest kaczką – duck typing.

Dynamiczne typowanie wymaga dużej dyscypliny, wręcz wskazane są testy jednostkowe (junit) oraz dobra konwencja nazewnicza.

if (obiekt.metaClass.respondsTo(obiekt, 'methodName' ))

można sprawdzić czy obiekt posiada daną metodę, metoda bezpieczna zawsze zwraca wartość.

Groovy jednocześnie jest opcjonalnie typowanay, zapewnia to integrację z innymi bibliotekami, frameworkami i narzędziami.
Jeśli przed metodą jest def, to zwraca ona Object.
Nie ma typów prymitywnych, wszystko to obiekt.

Działania matematyczne http://groovy.codehaus.org/Groovy+Math,
2**3 –> 2^3 (2 do 3 potęgi), Liczby z przecinkiem są traktowane jako java.math.BigDecimal, znika problem z resztami na ostatnich bitach. Można podać typy zmiennoprzecinkowe znane z Javy (Float, Double).

MultiMetody:

public class Employee {
  public void raise(Number amount){
    System.out.println("Employee got raise" );
  }
}

public class Executive extends Employee{
  public void raise(Number amount){
     System.out.println("Executive got raise" );
  }
  public void raise(java.math.BigDecimal amount){
    System.out.println("Executive got outlandish raise" );
  }
}

import java.math.BigDecimal;
public class GiveRaiseJava{
  public static void giveRaise(Employee employee){
    employee.raise(new BigDecimal(10000.00));
  }
  public static void main(String[] args){
    giveRaise(new Employee());
    giveRaise(new Executive());
  }
}

wynikiem w javie jest:
Employee got raise
Executive got raise
W javie w wywołaniu metody klasy podrzędnej pod uwagę brane są takie parametry jak w klasie nadrzędnej – metoda dziedziczona.

void giveRaise(Employee employee){
  employee.raise(new BigDecimal())
// same as
//employee.raise(10000.00)
}
giveRaise new Employee()
giveRaise new Executive()

wynik dla tych samych klas Employee i Executive jak wyżej:
Employee got raise
Executive got outlandish raise

W groovy pod uwagę brany jest typ parametru, dzięki czemu można wywołać metodę przeciążoną (ciekawe zastosowaniu przy kolekcjach i listach).

Warto nadawać typy przy testach i przy mapowaniu(GORM) a także jeśli jest tworzone api dla kogoś kto używa statycznych języków.