martedì 29 aprile 2008

Getting Started with Grails

Dopo essermi ben ben infarinato con Groovy, è ora di passare a Grails, e quindi, dopo aver proceduto all'installazione e alla configurazione del framework, parto con lo "studiare" qualche libro si questo framework. Speriamo che alla fine di queste letture mi convinca (anzi, ci convinciamo) che sia proprio il prodotto che fa per noi.

Parto con la lettura di un libro trovato online, scaricabile anche gratuitamente sul sito dedicato al minibook: http://www.infoq.com/minibooks/grails oppure acquistabile online sul famoso servizio di POD (Printing On Demand): lulu.com (http://www.lulu.com/content/618462)

Come sempre, userò questo post per prendere appunti, ove necessario, su questo libro.

Sebbene Grails abbia un database embedded, validissimo per i primi esempi "al volo", il libro consiglia, per un qualcosa di più duraturo, un database come mysql, sebbene grails sia compatibile con molti altri, tra cui, per esempio, oracle.
Procedo quindi all'installazione di MySql 5.0 (per rendermi le cose semplici scarico l'autoinstallante, scegliendo l'installazione completa, lasciando le opzioni standard, compreso l'installare il servzio di windows per il server mysql, sulla porta 3306).

  • All'interno della definizione del dominio (cioè, in pratica, la definizione delle classi che definiscono di fatto il progetto) del progetto di esempio Racetrack, si definisce la classe Race. All'interno della stessa, nell'elenco dei fields, si ha questa
    static hasMany = [registrations:Registration]
    che permette di specificare la relazione uno a molti che intercorre tra Race e Registration. Quella riga di codice, infatti, dice a Grails che la classe Race avrà la proprietà registrations che memorizzerà una collezione di oggetti Registration. Non serve poi specificare di nuovo l'esistenza del field registrations, perchè è implicita nella riga sopracitata (Grails segue la logica del DRY - Don't Repeat Yourself).
    La definizione delle relazioni ha diverse configurazioni, specificate dal GORM (Grails Object Relational Mapping), tutte reperibili qui: http://grails.org/GORM+-+Defining+relationships
  • Grails considera tutti le propietà (fields) di una classe come obbligatori, cioè per ogni classe devono essere espressi. Se si vuole specificare che uno dei campi non è obbligatorio, va aggiunta nelle constrains l'opzione nullable=true per ogni campo che si vuole specificare come facoltativo
  • Sempre seguendo l'esempio di prima, se nella relazione tra due classi, come definita prima, una a molti tra Race e Registration (cioè una corsa a zero o più registrazioni ad essa associata) possiamo aggiungere il concetto che una Registration non esiste se non correlata ad una Race. Quindi nella classe Registration aggiungiamo questa riga di codice
    static belongsTo = Race
    Cioè creiamo la proprietà belongsTo che specifica appunto che l'istanza della classe Registration esiste solo se associata ad un'istanza della classe Race, e se cancelliamo la Race, si cancellano tutte le Registration ad essa associate.
  • Secondo la logica del Convention Over Configuration, non serve specificare che le due classi specificate prima sono classi da "memorizzare", perché per convenzione le classi del dominio, come quelle di cui sopra, realizzate nella directory Domain, hanno bisogno della persistenza, e Grails quindi la gestirà in maniera automatica (di default, tramite Hibernate).
  • Il concetto del flash message è stato introdotto per permettere il "passaggio di parametri" tra le pagine. Infatti, per memorizzare messaggi di errore/avviso, e per passarseli tra le pagine, non possiamo usare variabili normali di una classe, perchè scomparirebbero all'aggiornamento della pagina (a meno di memorizzarle fisicamente da qualche parte). Usando invece questa variabile automaticamente creata da grails, possiamo passare tra le pagine questi messaggi: flash.message = "bla bla".
    Ciò che viene memorizzato nella variabile flash è disponibile durante la richiesta nella quale viene memorizzata e la successiva, quindi anche, per esempio, dopo un redirect della pagina.

Cambiamenti da fare al codice del tutorial dovuti ad errori di stampa del libro o necessari per le nuove vesioni di Grails (il libro è stato scritto per grails antecedente lo 0.4 e io attualmente sto usando grails 1.0.x)
  • Pagina 13 (25 sul pdf)
    La proprietà optional non esiste più da grails 1.0, ora, per dichiarare opzionale un campo, va aggiunta la propietà nullable=true nelle constraints per quel dato campo.
    Quindi la riga
    static optionals = ["postalAddress", "gender"]
    non va scritta, e si aggiunge invece una constraints nella classe
    static constraints = {
    postalAddress(nullable:true)
    }

    ricordandosi ovviamente di mantenere questa constraints anche successivamente
  • Pagina 21 (33 sul pdf)
    Il file DevelopmentDataSource non esiste più
    Le configurazioni vanno tutte fatte nel file DataSource.groovy
    Oltretutto io ho deciso di non usare, almeno per ora, mysql, ma di usare HSQLDB, già integrato con grails... l'unica modifica che faccio al file è quella di non memorizzare in memoria principale il file, ma di memorizzarlo a disco.
    Quindi le configurazioni per il development che si vogliono introdurre vanno comunque fatte nel file DataSource.groovy all'interno di queste righe di codice
    [...]
    environments {
    development {
    dataSource {
    //dbCreate = "create-drop" // one of 'create', 'create-drop','update'
    dbCreate = "update"
    url = "jdbc:hsqldb:file:devDB"
    }
    }
    [...]
  • Pagina 25 (37 sul pdf), pagina 27 (39 sul pdf)
    La proprietà maxLength non esiste più, va sostituita da maxSize, così, per esempio la riga
    city(maxLength:30,blank:false)
    diventa
    city(maxSize:30,blank:false)
  • Pagina 35 (47 sul pdf)
    Il codice della pagina gjp è proposta è diverso, in realtà qui non c'è nulla da modificare poichè la pagina gsp è stata creata automaticamente dal framework, quindi già aggiornata al codice necessario per il funzionamento.
  • Pagina 36 (48 sul pdf)
    Il codice del controller a cui si fa riferimento è diverso, in realtà qui non c'è nulla da modificare poichè il controller è stata creato automaticamente dal framework, quindi già aggiornata al codice necessario per il funzionamento, con il comando "generate-all".
  • Pagina 40 (52 sul pdf)
    Il messaggio a cui si fa riferimento, default.invalid.max.length.message, in realtà è default.invalid.max.size.message, dato che, come spiegato prima, il
    maxLength e minLength sono sostituiti da maxSize e minSize.
  • Pagina 41 (53 sul pdf)
    Non serve più riavviare il server ad ogni cambiamento, grails si accorge da solo e basta salvare le modifiche, e se si ritorna sulla pagina web, riaggiornandola, le si ritrovano subito.
  • Pagina 41 (53 sul pdf)
    In realtà non servirebbe aggiungere questo segnale di avvertimento, perchè le nuove versioni di grails lo aggiungono in maniera automatica, sempre tramite un javascript onclick
  • Pagine 43-45 (55-57 sul pdf)
    In realtà anche qui pare non sia necessario aggiungere il flash.massage ad ogni azione, perchè già create in automatico dalla nuove versioni di Grails
  • Pagina 46 (58 sul pdf)
    Come indicato prima la pagina gsp è diversa, ma il codice da rimuovere è facilmente identificabile. Però, oltre rimuovere la colonna id effettivamente inutile, va conservato il link per leggere i dettagli della race. Questo link è proprio conservato nell'id, quindi cancelliamo pure la riga dell'id, ma modifichiamo anche quella del nome in modo da conservare questo link
    Cancelliamo quindi:
    ${race.id?.encodeAsHTML()}
    E modifichiamo la riga sotto da
    ${race.name?.encodeAsHTML()}
    a
    ${race.name?.encodeAsHTML()}
  • pagina 102 -103 (114-115 sul pdf)
    Il file di configurazione di log4j, log4j.production.properties, non si trova più in racetrack/web-app/WEB-INF. Tutte le configurazioni di log4j vanno inserite nel file Config.groovy che si trova in /grails-app/conf. Inoltre le modifiche da apportare sono diverse da quelle indicate nel libro.
    La parte dedicata a log4j del mio file Config.groovy è così diventata:

    log4j {
    appender.stdout = "org.apache.log4j.ConsoleAppender"
    appender.'stdout.layout'="org.apache.log4j.PatternLayout"
    appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'
    appender.errors = "org.apache.log4j.FileAppender"
    appender.'errors.layout'="org.apache.log4j.PatternLayout"
    appender.'errors.layout.ConversionPattern'='[%r] %c{2} %m%n'
    appender.'errors.File'="stacktrace.log"
    rootLogger="error,stdout"

    appender.access="org.apache.log4j.FileAppender"
    appender.'access.file'="access.log"
    appender.'access.layout'="org.apache.log4j.PatternLayout"
    appender.'access.layout.ConversionPattern'='%d %p %x [%c] %m%n'


    logger {
    //grails="error"
    StackTrace="error,errors"
    grails.'app.controller.UserController'="warn,access"
    org {
    codehaus.groovy.grails.web.servlet="error" // controllers
    codehaus.groovy.grails.web.pages="error" // GSP
    codehaus.groovy.grails.web.sitemesh="error" // layouts
    codehaus.groovy.grails."web.mapping.filter"="error" // URL mapping
    codehaus.groovy.grails."web.mapping"="error" // URL mapping
    codehaus.groovy.grails.commons="info" // core / classloading
    codehaus.groovy.grails.plugins="error" // plugins
    codehaus.groovy.grails.orm.hibernate="error" // hibernate integration
    springframework="off"
    hibernate="off"
    }
    }
    additivity.StackTrace=false
    additivity.grails.app.controller.UserController=false
    }

3 commenti:

StefanoP ha detto...

molto utile

grazie
Stefano

Giorgio ha detto...

perchè non riesco a fare la ricerca a pagina 69 del pdf?
in pratica ho seguito tutte le istruzioni, ma ogni volta che cerco qualcosa torna alla pagina della ricerca vuota e non mi mosta i risultati.

Gabriele ha detto...

non saprei dirti come mai.
forse hai scaricato un pdf corrotto?
vedo che anche tu stai facendo una tesi con grails... spero sia più veloce della mia :-P