giovedì 12 novembre 2009

Grails validation, un incongruenza nella constraint size

Dopo mesi che non mi faccio più sentire (l'elezione a sindaco ha decisamente influito) ecco che mi ripropongo a voi.
Sembra difficile crederlo, ma siete davvero in molti quelli che seguite il blog... anche se poco aggiornato.
D'altronde in Italia non sono molti quelli che programmano in GWT e Grails, e in particolare con una combinazione di entrambi.

Oggi forse abbiamo trovato un incongruenza, o almeno qualcosa di poco intuitivo, nella validazione di grails.
Era un qualcosa che avevamo già intuito in passato, oggi abbiamo fatto alcuni test e possiamo essere sicuri di questo comportamento. Stiamo usando grails 1.2M4, ma credo il comportamento sia simili almeno per grails 1.0 in avanti.

In effetti la documentazione è poco chiara sulle constraint, in particolare proprio sull'interazione della constraint size con nullable e blank, verso i quali ha una frase piuttosto criptica:
Currently this constraint cannot be used in addition to blank or nullable, a custom validator may be added to perform this kind of constraints.

Il che apparentemente significherebbe, se il mio inglese non mi tradisce, che la constraint size non si può usare in aggiunta a nullable e blank... ma la cosa risulta falsa, infatti, prendiamo ad esempio questa classe

class Prova {
static constraints = {
prova1(size:0..16)
prova2(size:1..16)
}
String prova1
String prova2
}


e questo codice in un controller:

Prova provaA = new Prova()
provaA.validate()
println "Validazione ProvaA"
provaA.errors.each {println it}

Prova provaB = new Prova()
provaB.prova1 = ""
provaB.prova2 = ""
provaB.validate()
println "Validazione ProvaB"
provaB.errors.each {println it}


si ha questo output
Validazione ProvaA
org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'Prova' on field 'prova2': rejected value [null]; codes [Prova.prova2.nullable.error.Prova.prova2,Prova.prova2.nullable.error.prova2,Prova.prova2.nullable.error.java.lang.String,Prova.prova2.nullable.error,prova.prova2.nullable.error.Prova.prova2,prova.prova2.nullable.error.prova2,prova.prova2.nullable.error.java.lang.String,prova.prova2.nullable.error,Prova.prova2.nullable.Prova.prova2,Prova.prova2.nullable.prova2,Prova.prova2.nullable.java.lang.String,Prova.prova2.nullable,prova.prova2.nullable.Prova.prova2,prova.prova2.nullable.prova2,prova.prova2.nullable.java.lang.String,prova.prova2.nullable,nullable.Prova.prova2,nullable.prova2,nullable.java.lang.String,nullable]; arguments [prova2,class Prova]; default message [La proprietà [{0}] della classe [{1}] non può essere null]
Field error in object 'Prova' on field 'prova1': rejected value [null]; codes [Prova.prova1.nullable.error.Prova.prova1,Prova.prova1.nullable.error.prova1,Prova.prova1.nullable.error.java.lang.String,Prova.prova1.nullable.error,prova.prova1.nullable.error.Prova.prova1,prova.prova1.nullable.error.prova1,prova.prova1.nullable.error.java.lang.String,prova.prova1.nullable.error,Prova.prova1.nullable.Prova.prova1,Prova.prova1.nullable.prova1,Prova.prova1.nullable.java.lang.String,Prova.prova1.nullable,prova.prova1.nullable.Prova.prova1,prova.prova1.nullable.prova1,prova.prova1.nullable.java.lang.String,prova.prova1.nullable,nullable.Prova.prova1,nullable.prova1,nullable.java.lang.String,nullable]; arguments [prova1,class Prova]; default message [La proprietà [{0}] della classe [{1}] non può essere null]
Validazione ProvaB
org.springframework.validation.BeanPropertyBindingResult: 0 errors

Non avendo dichiarato nessun'altra contraint oltre a size, come per ogni proprietà della classe, si dovrebbe sottointendere "nullable: true"
E infatti sembrerebbe così, nel tentativo su ProvaA abbiamo i due errori sul valore nullo

Il size sulla stringa è inteso come un controllo sulla lenght() della stringa stessa. quindi può trarre in inganno il size(0..16) che potrebbe far pensare di aver concesso alla stringa un lenght 0... quindi come non avergli dato valore, quindi come aver sottinteso nullable:true
Ma non è così

Altra cosa strana è il size(1..0)... concedere ad una stringa lunghezza minima di 1, potrebbe far pensare di costringere la stringa ad avere un valore diverso dal valore vuoto, in parole povere di aver sottointeso "blank: false", ma non è così
come si vede dall'output precedente, un assegnazione prova2="" è accettata... anche se palesemente un lenght su una stringa = "" sarebbe 0.

Comportamento strano, non credete?

venerdì 22 maggio 2009

Accedere al DataSource di grails da dovunque

Mi è riservito dover riaccedere al datasource da un qualcosa che non fosse un controller o un service.
Infatti nel nostro progetto abbiamo una classe utility che deve avere accesso al database, per poter fare una query con HQL...

Per poterlo fare bisogna:

import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
def ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
def dataSource= ctx.dataSource

e ora nel dataSource si ha una classe di tipo: http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/jdbc/datasource/DriverManagerDataSource.html
e ora si può accedere alla connessione così:

def sql = new Sql((Connection)dataSource.getConnection())

Con grails 1.1 si può fare più semplicemente

import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def static ctx = AH.application.mainContext
def dataSource= ctx.dataSource

Per poi continuare come scritto prima

giovedì 30 aprile 2009

Database Naming convention

Alcune convenzioni largamente utilizzate (in gran parte usate anche da grails e hibernate) per i nomi nei database.
Ho trovato questo libro, SQL Programming Style by Joe Celko
Che sembrerebbe essere interessante e si basa sullo standard ISO/IEC 11179-5:2005, che da quanto letto è lo standard per la "naming convention" in senso lato.

Lo standard iso, riassumento, dice tra le altre cose

1. Be unique (within any data dictionary in which it appears).
2. Be stated in the singular.
3. State what the concept is, not only what it is not.
4. Be stated as a descriptive phrase or sentence(s).
5. Contain only commonly understood abbreviations.
6. Be expressed without embedding definitions of other data elements
or underlying concepts.
7. Tables, sets, and other collections shall be named with a collective,
class, or plural name.
8. Procedures shall have a verb in their name.
9. A copy (alias) of a table shall include the base table name as
well as the role it is playing at that time.

Elenco qui, invece, alcune delle convenzioni trovate nel libro
  • Lunghezza dei nomi (tutti): dovrebbe essere inferiore ai 30 caratteri. Molti dbms sql accettano sino a 128 caratteri (come MS SQL) ma considerando il limite di oracle di 30 caratteri, conviene che tutti i nomi usati siano inferiori a questo limite. Comunque sembra una cifra più che sufficiente.
  • Evitare i caratteri speciali, come ad esempio $, #, @, alcuni dmbs li supportano, ma non tutti.
  • I nomi delle tabella è buona norma scriverli "capitalizzati" (prima lettera maiuscala, il resto minuscolo). Tutto il resto (nomi colonne, ecc...) tutto minuscolo. Questa però pare essere una convenzione piuttosto controversa.
  • Per nomi composti (sia tabella sia colonna) usare l'undescore come separatore, e non le "camel words" (cioè come in java, le parole attaccate capitalizzate, come NomeTabella). Per i nomi di tabella usare parole capitalizzate.
  • Essere coerenti nella scelta se usare nomi di tabella al singolare o al plurale. Ci sono alcune correnti di pensiero, alcuni pensano sia meglio usare nomi al plurale, dopotutto una tabella contiene in se solitamente più di un entità. Però nella modellazione una tabella rappresenta un oggetto, quindi espresso al sigolare. Inoltre scrivere tutto al singolare evita i problemi derivati proprio dalle particolare regole nel rendere plurarle una parola (person diventa people). Il libro consiglia nomi al plurale, anche se molti altri (e io mi aggiungo a questi) consigliano nomi al singolare.
  • Evitare i prefissi, soprattutto nei nomi di tabelle. Alcuni usano anteporre table_ o tbl_ ai nomi di colonne, ma è inutile, anzi scomodo. Sconsigliato anche anteporre il nome della tabella a tutte le colonne della tabella stessa.
  • Le tabelle di relazione dovrebbero avere nomi che riassumano al relazione stessa. Per esempio un'ipotetica relazione di "matrimonio" tra la tabella "uomo" e la tabella "donna" dovrebbe chiamarsi "matrimonio", e non "uomo_donna". Se non possibile, si usano i nomi della tabelle unite dall'underscore.
  • Le chiavi primarie, dove possibile, dovrebbero essere degli standard industriali, come per esempio l'ISBN, o simili.

giovedì 19 febbraio 2009

Grails Are Not Configured

Non è la prima volta che mi capita questo errore, e non è la prima volta che perdo decine di minuti per risolverlo... mi dimentico sempre la soluzione.
In questi giorni ho importato ed esportato svariati progetti... e quindi spesso ci sono incompatibilità con le configurazioni degli stessi (librerie, jdk, facet bla bla).

A volte capita che, nel tentare di far partire/compilare un progetto Intellij Idea segnali "Grails are not configured"
Per risolvere il problema bisogna verificare che:
  • Ci sia il Grails JDK configurato correttamente, per fare questo bisogna accedere alla struttura del progetto (ctr+alt+shift+s, oppure F4 se si clicca sulla root del progetto nel project explorer) , andare su moduli-> grails e verificare che sia richiamato un jdk di grails
  • sia settata, sia in IDEA che in windows la variabile GRAILS_HOME
e tutto dovrebbe risolversi :-D

venerdì 13 febbraio 2009

Novità in ambito GWT: Smart GWT, aggiornamenti a GWT-EXT e GWTMosaic

Negli ultimi mesi sono venute alla luce importanti novità nel modno di gwt.

Alcuni aggiornamenti alle librerie GWT-EXT: siamo ora alla 2.0.6, anche se nella home è dichiarata ancora la 2.0.5
Questa versione la trovate da un link posto in questo post: http://gwt-ext.com/forum/viewtopic.php?f=12&t=3624
Attenti però, le prime versioni di questo pacchetto avevano una struttura directory che richiamava la versione precedente.
Purtroppo questa release, anzi, in realtà anche quella precedente, è stata accompagnata da una notizia che ci ha un po' scioccati: GWT-EXT non verrà più aggiornato... o meglio, verranno corretti comunque eventuali bachi e, forse, mantenuta la compatibilità con futuri rilasci di GWT e di nuovi browser, ma di fatto lo sviluppo è fermo... questo perchè gli sviluppatori hanno deciso di propendere per lo sviluppo di una nuova tecnologia non più basata sulle librerie ext-JS.
Infatti viene caldamente consigliato al conversione a SmartGWThttp://www.jroller.com/sjivan/entry/smartgwt_1_0_released
Queste nuove librerie grafiche si basano sul framework SmartClient, che non ha, almeno per ora, i problemi delle licenze di EXT-js.

Molti più widget, un buon supporto al pattern MVC e una nuova e interessante struttura dati (molto simile sotto certi aspetti a quella che abbiamo sviluppato per la mia tesi).

Per ora non intendiamo migrare, ma in un futuro probabilmente lo faremo.
Comunque, di fatto, uno dei problemi di gwt-ext rimane: il codice che il sorgente deve scaricare è enorme, poichè tutto richiama librerie javascript che vanno comunque totalmente scaricate dal browser (e parliamo di 700K minimo, esclusa l'applicazione in sè).

Pochi giorni fa è stata rilasciato un nuovo aggiornamento di GWT-Mosaic, che ora si fa davvero interessante, si è arrivati alla versione 0.1.8 (http://groups.google.com/group/gwt-mosaic/browse_thread/thread/a114236cb7faaa37#)
Questo piccolosissima libreria gwt, piccolissima se paragonata a GWT-Ext o SmartGwt, è molto interessante perchè si appoggia nativamente e interamente su GWT... cioè non richiama codice javascript che il browser deve scaricare, ma lascia tutto in pasto al compilatore java-to-javascript di GWT... questo permettere di mettere in codice javascript (e quindi tutto peso che il browser deve scaricare) solo quello che in realtà si programma... e questo è un notevole vantaggio per applicazioni piccole o medio piccole
La differenza è notevolissima: per un mini progetto (giusto qualche pannello e qualche pulsante) con gwt mosaic, il codice (tra pagina html con js embedde e css) che il browser deve scaricare è di 92K, tra l'altro probabilmente molto riducibili, perchè si hanno due css da 28 k e molto probabilmente sono io l'imbranato che non ha capito come usarne solo uno, datyo che sono quasi identici, quindi possiamo dire che si arriverebbe a meno di 70 KB
Con gwt ext invece si tratterebbe di almeno 2MB (tra pagina html con js embedde, altri js e css) , poichè comunque bisogna scaricare il core  in js (che sono le librerie ext) di 770KB, più i js dell'applicazione, circa 10 KB, più almeno 100KB di css (in questo caso obbligatori)
insomma... si va dai 80-90 KB con gwtmosaic agli 800-900 di gwt-ext.

certo, per grandi applicazioni le dimensioni di un progetto gwt-ext non cambiamo moltissimo in proporzione (il nostro helpdesk, che è abbastanza corposo, arriva a 900-1000KB totali), però la differenza è enorme.

martedì 20 gennaio 2009

Latex

Ho iniziato ad impaginare al tesi con latex... in realtà non ho ancora terminato di scriverla, mi manca uno dei due capitoli centrali...
ma almeno inizio a scrivere seriamente il tutto.

Prendo appunti su alcuni settaggi:

Voglio usare una doppia bibliografia, così da costrurire la classica bibliografia, e successivamente, una sitografia
Uso ovviamente bibtex, decisamente comodo
lo affianco al pacchetto multibib

quindi nella classe caricata all'inizio del documento, scrivo questi due comandi

\usepackage{multibib}
\newcites{sito}{Sitografia}

dove il newcites introduce proprio la seconda bibliografia e una serie di comandi (simili a quelli di bibtex, ma con l'aggiunta ad onuno del suffisso "sito") appositi

all'interno del documento principale, subito dopo al creazione della bibliografia, creo la sitografia in questo modo

\bibliography{bibliografia} % inserisce la bibliografia e la prende in questo caso d bibliografia.bib

%sitografia, prendendo i dati da sitografia.bib, dentro il quale uso "misc"%
\bibliographystylesito{alpha}
\bibliographysito{sitografia} 

All'interno del documento, usando il normale comando \cite citerò i documenti della bibliografia normale, usando il comando \citosito, utilizzo al sitografia


Per poter avere anche la sitografia, però, non posso usare il comodo comando preimpostato all'interno di TeXnicCenter "Build and View" (che compila tutto, crea il pdf, e lo visualizza), ma devo andare di riga di comando

latex tesi
bibtex tesi
bibtex sito
latex tesi
latex tesi

e qui si produce il file dvi

con il comando

dvips primo.dvi -o

si produce il file PostScript ps