středa 30. května 2012

Postřehy z integračního testování v Grails

Včera a dnes jsem začal s integračním testováním. Nevím, zda správně aplikuji filosofii integračních testů, ale hlavní je, že testuje to, co chci :) Napíšu sem pár postřehů, které jsem za 2 dny shromáždil a které mě překvapili:

  1. pro vytvoření mockup controlleru použijte normálně Controller controller = new Controller(). Problémem je však v tom, že pokud jednou vytvořený controller přiřadíte parametry či zavoláte akci, zůstanou parametry i odpovědi z akce v controlleru. A to i přesto, že vytvoříte novou instanci znova pomocí new Controller(). Tento problém jsem eliminoval tak, že jsem controller vlastnoručně vyčistil od "bordelu".
    /**
    * vyčistí controller pro další použití
    * musí se použít pokaždé, když chceme
    * vytvořit nový request
    * @param controller
    */
    private void clear(def controller) {
        controller.response?.reset()
        controller.params?.clear()
        controller.modelAndView?.clear()
    }
    Problém by šel líp vyřešit pomocí setUp() a tearDown(), ale protože testuji celý proces, tak jsem tyto metody nepoužil.

  2. pokud chcete otestovat vrácený model z controlleru, lze použít konstrukci controller.modelAndView.model.nazevModelu. Avšak konstrukce 
    [voucherInstance: voucherInstance, diagnosis: diagnosis, insuranceCompanyList: insuranceCompanyList]  
    nevrací žádný model (nevím, co přesně to vrací...). Stejně je to s konstrukcí 
    render(template: "detail", model: [voucherInstance: voucherInstance], status: 200)
    Zde to vrací vygenerovaný HTML snippet. Model vrací tato konstrukce: 
    render view: "edit", model: [voucherInstance: voucherInstance, diagnosis: diagnosis, insuranceCompanyList: insuranceCompanyList]

  3. používám IntelliJ jako IDE pro vývoj v Grails. Při testování jsem narazil na problém při debugování v controlleru, který používá metodu s closure.
    // akce v controlleru
    def edit() {
        checkVoucherById(params.id) {voucherInstance ->         
            // tady nemůžu vůbec stopnout zpracování
            render view: "edit", model: [voucherInstance: voucherInstance]
        }
    }


    // tohle je metoda, která bere jako parametr closure
    private checkVoucherById(id, closure) {
        Voucher voucherInstance = null
        try {
            id = id as long
            voucherInstance = voucherService.get(id)
            closure(voucherInstance)
        } catch (Throwable t) {
            // nějaká jiná akce
        }
    }
    Uvnitř closure, který předávám v metodě edit(), se zpracování nezastaví, i když tam hodím breakpoint.
  4. při testování controlleru lze přidat parametry do requestu podobně jako je to při skutečném provozu. V dokumentaci je popsán tento způsob:
    def controller = new AuthenticationController()
    controller.params.login = "marcpalmer"
    controller.params.password = "secret"
    controller.params.passwordConfirm = "secret"
    controller.signup()
    To však lze provést i pomocí Map parametru.
    def controller = new AuthenticationController()
    controller.request.parameters = [login: "marcpalmer", password: "secret", passwordConfirm: "secret"]
    controller.signup()

  5. Odlišný počet parametrů při použití controller.request.parameters a  controller.params. Mám-li jako parametr něco takového:
    [

    "visitListSize": 1.toString(), "visitList[0].date_day": 1.toString(), "visitList[0].date_month": 1.toString(),


    "visitList[0].date": Date.parse("dd-MM-yyyy", "01-01-2012").toString(), "visitList[0].date_year": 2012.toString()

    ]
    a použiji controller.request.parameters jako pro přiřazení parametrů do controlleru, v controlleru pak mám o 1 parametr víc. Tím je mapa vytvořená z visitList[0], což je stejné chování jako controller za běžného provozu. Použiji-li však pro přiřazení parametrů konstrukci controller.params jako v manuálu, tak mapa chybí.

neděle 27. května 2012

Grails - jak jsou naprogramovány mapping v doménových třídách

Tento článek je o jedné zajímavosti z knihy Programming Grails (koupil jsem si Early Release). V Grails existuje jednoduchý způsob definování mapování pro doménové třídy pomocí closure static mapping = {}. Doteď jsem neměl ani páru, jak je to udělané, ani jsem se o to nezajímal. Teď už chápu o trochu více, i když ne úplně vše.

Closure (česky uzávěry) umožňují uvnitř nich volat metody. Příklad:

class SomeClass {
   void callClosure() {
      def dog = {
         woof() // volání metody void woof() z closure
      }
      dog() // zavolá closure
   }
   void woof() {
      println "Woof!"
   }
}

Každý closure má obsažené 3 různé atributy: Owner, Delegate a this. this odkazuje na instanci třídy, kde je closure definován. Owner obsahuje odkaz na instanci, kde je obsažen closure. Ve většině případů je tedy this stejný jako owner. Výjimkou je třeba tento případ (z knihy Programming Groovy):

examiningClosure() {
    println "In First Closure:"
    println "class is " + getClass().name
    println "this is " + this + ", super:" + this.getClass().superclass.name // this = owner
    println "owner is " + owner + ", super:" + owner.getClass().superclass.name
    println "delegate is " + delegate +
    ", super:" + delegate.getClass().superclass.name
    examiningClosure() {  // closure uvnitř closure
        println "In Closure within the First Closure:"
        println "class is " + getClass().name
        println "this is " + this + ", super:" + this.getClass().superclass.name // this != owner
        println "owner is " + owner + ", super:" + owner.getClass().superclass.name
        println "delegate is " + delegate +
        ", super:" + delegate.getClass().superclass.name
    }
}
Výstup:

In First Closure:
class is ThisOwnerDelegate$_run_closure1
this is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
owner is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
delegate is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
In Closure within the First Closure:
class is ThisOwnerDelegate$_run_closure1_closure2
this is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
owner is ThisOwnerDelegate$_run_closure1@15c330aa, super:groovy.lang.Closure
delegate is ThisOwnerDelegate$_run_closure1@15c330aa, super:groovy.lang.Closure

 Atribut Delegate v closure je ten, který právě umožňuje dělat kouzla jako v static mapping = {}. Closure totiž umožňuje nastavit atribut Delegate na jiný objekt pomocí setDelegate(Object delegate) a pak volat jejich metody. V praxi to znamená toto:


Doménová třída s defaultním řazením entit podle dne:
class Attendance {
    Date day
    static mapping = {
        sort("day")
    }
}
V doménové třídě však žádná metoda sort defaultně neexistuje. Avšak třída je platná a funkční. Pokud použijete debugger a zastavíte zpracování v closure mappings, uvidíte následující:
this = class wellness.Attendance
owner = class wellness.Attendance
delegate = org.codehaus.groovy.grails.orm.hibernate.cfg.HibernateMappingBuilder@3321875 // ha, to je on
Když si otevřete třídu HibernateMappingBuilder, nacházejí se tam metody, které se používají v mapping, jako je výše zmíněný sort, cache, table, version apod. To je celé tajemství.

P.S. Další otázkou je, kde je nastaven setDelegate() v doménových třídách? To jsem se ještě nedočetl (ale myslím, že je to udělané pomocí MOP). O tom napíšu, až budu vědět na stopro nebo si to někde přečtu.

Grails - URL mapování pro celý package

Dlouho jsem hledal způsob, jak namapovat všechny controllery ve stejném package do stejného vzoru. Hodilo by se to např. v případě, že potřebuji změnit všechny controllery pod package admin do URL "/admin/${controller}/${action}". Jednak se lépe zabezpečují a jednak mají tyto URL "hezčí" formát. V oficiální dokumentaci Grails není popsaný takový způsob mapování. Na StackOverflow mi poradili, ať udělám admin sekci jako zvláštní plugin. To je ale podle mě dost složitý na malý projekt.

Nakonec jsem objevil článek, který popisuje triky s URL mapováním v Grailsu. Trošku jsem to změnil a řešení je na světě:
for (controller in AppCtx.grailsApplication.controllerClasses) {  // AppCtx - viz článek
        def cName = controller.logicalPropertyName
        def packageName = controller.packageName
        if (packageName.contains(".admin") || packageName.contains(".springsecurity")) {
            "/admin/${cName}/$action?/$id?"(controller: cName) {
                constraints {
                }
            }
        } else {
            "/${cName}/$action?/$id?"(controller: cName) {
                constraints {
                    // apply constraints here
                }
            }
        }
}
Odteď všechny controllery, které jsou v balíku "admin", budou mít URL namapovány jako "/admin/${controller}/${action}", a zbytek klasicky jako "/${controller}/${action}.

středa 16. května 2012

getTotalCount() metoda

Tak jsem se dnes naučil trik, jak získat celkový počet entit (aneb objektů, instancí, řádků) v databázi, které vyhovují kritériím. Jde o to, že pomocí metody createCriteria().list(max: params.max, offset: params.offset) získám omezený počet entit. Také však potřebuji získat počet, kolik těchto entit se skutečně nachází v databázi.
A k tomu slouží právě metoda getTotalCount(). Metoda  createCriteria().list(max: params.max, offset: params.offset) totiž vrací instanci třídy PagedResultList, který toto umožňuje. Jsem si toho ani nevšim a už jsem se chtěl zeptat na StackOverflow, ale byl jsem včas upozorněn a v dokumentaci je to hned na začátku popisu metody createCritera(). RTFM.

úterý 15. května 2012

Metoda inject() v Groovy

Včera jsem po přečtení článku narazil znovu na fci inject(). Tuto fci jsem předtím moc nechápal, tak jsem se rozhodl, že ho zkusím použít v aplikaci, kterou zrovna dělám.

Příklad:
Chci v jqGrid použít dropwdown menu pro filtrování položek. Abych udělal toto dropdown menu, potřebuji dostat řetězec vypadající asi takto:
:Vše;NEW:Nová;SENT:Odeslaná;CLOSED:Uzavřená
kde mám vždy dvojici hodnotu option value a text, který se zobrazuje. Obě tyto hodnoty můžu získat z enumerační třídy.

enum Status {
        NEW("Nová"),
        SENT("Odeslaná"),
        CLOSED("Uzavřená")
        String czech
        Status(String czech) {
            this.czech = czech
        }
    }
Jeden způsob, jakým mohu získat předchozí řetězec, je tento:
var statusEnumsStr = "${protetika.Invoice.Status.values().collect { it.name() + ":" + it.czech}.join(";")}";
statusEnumsStr = ":Vše;"+ statusEnumsStr; 
Metoda values() vrací Collection<Status> se všemi enum hodnoty, který pak přeměním na řetězec složený z jména hodnoty (metoda name()) a českého názvu hodnoty, a "posbírám" tyto řetězce (metoda collect()) do nové kolekce. Hodnoty z této kolekce pak spojím do jednoho řetězce pomocí ";". Teď už mám řetězec:
NEW:Nová;SENT:Odeslaná;CLOSED:Uzavřená
Zbytek je jasné.

Teď to samé udělám pomocí metody inject():
var statusEnumsStr = "${protetika.Invoice.Status.values().inject(":Vše") {initVal, colVal -> initVal + ";" + colVal.name() + ":" + colVal.czech }}";
Metoda inject bere 2 parametry: inicializační parametr a closure. Funguje tak, že pro první prvek v kolekci provede to, co je v closure, a výsledek se pak stane inicializační hodnotou pro další prvek. Lepší znázornění:

  1. naše kolekce [name: "NEW", czech:"Nová"; name: "SENT", czech: "Odeslaná"; name: "CLOSED", czech: "Uzavřená"]
  2. inicializační hodnota je ":Vše" a první prvek v kolekci je name: "NEW", czech:"Nová"
  3. V closure se provede ":Vše" + ";" + "NEW" + ":" + "Nová" a tato hodnota se vrací jako další inicializační hodnota
  4. Pro druhý prvek kolekce name: "SENT", czech: "Odeslaná" je pak k dispozici inic. hodnota :Vše;NEW:Nová;

Kroky se pak opakují.

sobota 12. května 2012

Vyřazení duplikátních výsledků z dtb

Dnes jsem hledal možnost, jak vyřadit duplikátní záznamy ve výsledcích vrácené z databáze pomocí GORM. Protože jsem už používal dotazy pomocí třídy Criteria v Hibernate, tak jsem nakonec našel tuto konstrukci:
NejakaEntita.createCriteria().list() {
    resultTransformer Criteria.DISTINCT_ROOT_ENTITY // this is it
}

Good to know. Význam té konstrukce jsem zatím nezkoumal, na to snad bude čas někdy jindy. Hlavní je, že funguje.

čtvrtek 10. května 2012

Moje první MySQL procedura

Včera jsem napsal svoji první MySQL proceduru. Měl za úkol vygenerovat náhodné číslo, které má 10 číslic a je jedinečné v celé databázi.

create procedure generaterc()
begin
declare _rc varchar(255);  --tady jsem měl chybu
declare _id int;
set _id = 1;
while _id < ((select count(*) from patient) + 1) do
    begin
    set _rc = cast(FLOOR(1000000000 + (RAND() * 8999999999)) AS char);  
    if not exists(select * from patient where patient.rc = _rc) then
        update patient set rc=_rc where id=_id;
        set _id=_id+1;          
    end if;
    end;
end while;  
end
Poznámka pod čarou:
Proměnné, které chceme pak dávat jako hodnotu do sloupců databáze, by měly mít stejný datový typ jako ten sloupec. Měl jsem v tabulce sloupec rc, který byl typu varchar(255). Proměnnou jsem však deklaroval jako declare _rc char; a to způsobilo nekompatibilní dat. typ. http://stackoverflow.com/questions/10524573/mysql-procedure-random-number-generating

Zjištění:
V databázi s 82 618 řádky funkce vůbec nedoběhne do konce, a to jsem ji nechal běžet přes 30 min. Nakonec jsem ty čísla náhodně negeneroval :)

středa 9. května 2012

PDF renderování a čeština

Včera jsem měl za úkol vyřešit problém s kódováním češtiny v PDF dokumentu. Kolegové, kteří na tom dělali, si nevěděli rady (nebo se s tím nechtěli párat), tak to přesunuli na mě...
V projektu používáme plugin Grails Rendering pro generování PDF souboru. Používání bylo jednoduché, jenže zase české znaky se nezobrazovaly. Bylo potřeba přidat správný font.

@font-face {
        src: url(file:c:/windows/fonts/arialuni.ttf);
        -fs-pdf-font-embed: embed;
        -fs-pdf-font-encoding: Identity-H;
}
Takhle se vše zobrazovalo správně. Problém byl ale v tom, že odkaz na font byl funkční jen pro Windows. OK, tak prostě se ten soubor s fontem zkopíroval do web-app/font složky (složka s HTML a CSS a obrázky, která se nekompiluje). Komplikací poté ale bylo, že nešlo na ten soubor odkazovat:

@font-face {
        src: url(file:${resource(dir:'font', file: 'ARIALUNI.TTF')});
        -fs-pdf-font-embed: embed;
        -fs-pdf-font-encoding: Identity-H;
}
A hned NullPointerException. Naštěstí jsem pak našel na StackOverlow, jak to někdo také řešil. A také jsem přehlídl důležitou větu v manuálu (RTFM): All links to resources (e.g. images, css) must be accessible by the application . This is due to the linked resources being accessed by application and not a browser. Takže řešením bylo nějak takhle (na soubor se muselo odkazovat jako na soubor z PC, ne přes URL):

@font-face {
        src: url(file:${czechFontPath});
        -fs-pdf-font-embed: embed;
        -fs-pdf-font-encoding: Identity-H;
}



úterý 1. května 2012

DBF soubor s českými znaky

České čárky a háčky,to je občas velké zlo v kódování. A ještě větší zlo je,když toto kódování nevíte a ani neumíte zjistit. V současném projektu je 1 z úkolů migrace databáze. Jenže databáze starého systému,který je starý a k tomu ještě desktopový, že jsem tu databázi viděl poprvé (slyšel podruhé). Jednalo se o databázi foxpro a hromada souborů s koncovkou .dbf.
Myslel jsem, že vše proběhne bez problému. Jako první jsem stáhl z netu nějaké programy pro zobrazování,ale bohužel žádná neuměla české znaky přečíst (např. č). Pak jsem koukal i přímo po java knihovnách. Nejlepší byl tento: http://www.hxtt.com/orderdbf.html, akorát stál prachy,takže nic pro studenty. Trial verze umožňovala zobrazit jen 1000 prvních řádků... Ale plusem byl ten,že jsem konečně zjistil kódování dbf souboru (Cp1250).
Po delším hledání jsem narazil na program od českých autorů: http://www.pspad.com/cz/dbfview.htm. Ten funguje skvěle,doporučuji. Migrace samotné provedeme konverzí na csv a pak načtení csv do databáze.