GRAILS
N00B TO NINJA IN 3 HOURS
[INTRODUCTION WORKSHOP]
/BrianJohnsen @brianjohnsendk
WHOAMI
(whocares)
Brian Johnsen, GennemtænktITA/S
12+ years Java
Groovy&Grails since 2008
Your FriendlyNeighborhood GR8Conf Organizer
READY?
1. Copygrails installto disk and unzip
2. Add to path
3. Voilá
$grails-version
Grailsversion:2.3.8
FULL STACK FRAMEWORK
ORM (GORM)
Powerfulview technology(GSP) and easyTagLib creation
DependencyInjection and on-the-flyreloading(Spring)
Internationalization support(i18n)
Runtime Container (Tomcat)
TestingFramework (Spock)
Plugin System
Command line tools
Build Tool(Gant/Gradle)
and abunch of other stuff…
ARCHITECTURE
PHILOSOPHY
LETS GET STARTED!
DEMO TIME!
CLI and create app demo
YOUR TURN!
Itjustgoes on...
CLI
Tryitout
$grailshelp
CREATE THE APP
$grailscreate-appgrailsdemo
$cdgrailsdemo
$grailsrun-app
|Serverrunning.Browsetohttp://localhost:8080/grailsdemo
Openyourfavoritebrowserandcheckitout!
RUNNING APPLICATION!
ANATOMY
ANATOMY
grails-app-TopleveldirectoryforGroovysources
conf-Configurationsources.
controllers-Webcontrollers-TheCinMVC.
domain-Theapplicationdomain-TheMinMVC.
i18n-Supportforinternationalization(i18n).
migrations-Databasemigration.
services-Theservicelayer.
taglib-Taglibraries.
utils-Grailsspecificutilities.
views-GroovyServerPages-TheVinMVC.
scripts-Gantscripts.
src-Supportingsources
groovy-OtherGroovysources
java-OtherJavasources
test-Unitandintegrationtests.
web-app-JavaScriptandCSS.
wrapper-Wrapper.
DON'T LET FRIENDS FORK
Open:
/grails-app/conf/BuildConfig.groovy
Change:
grails.project.fork=[
//configuresettingsforcompilationJVM,notethatifyoualtertheGroovyversion
...
...
//configuresettingsfortheConsoleUIJVM
console:[maxMemory:768,minMemory:64,debug:false,maxPerm:256]
]
To:
grails.project.fork=false
DOMAIN
/grails-app/domain/
The M in MVC
Gets mapped to the database
Dynamic finders FTW -> ForgetSQL
constraintsenforce database consistency
Defines scaffolded views
DOMAIN DEMO!
MY FIRST DOMAIN CLASS
$grailscreate-domain-classPerson
//grails-app/domain/Person.groovy
classPerson{
Stringname
Integerage
staticconstraints={
nameblank:false
agemin: 0
}
}
SAVE AND VALIDATE
constraintsare enforced on save()and validate()
defperson=newPerson(age:42,name:'DouglasAdams')
person.validate()//onlychecksconstraints
person.save()//savestheinstancetothedatabase
WHOLE LOTTA DYNAMIC GOIN’ ON
Finders are dynamicallycreated for allproperties
defeverybody=Person.list()
deftheOne=Person.findByNameAndAge('Brian',41)
defcaseInsensitive=Person.findAllByNameIlike('graeme')
defunderAge=Person.findAllByAgeLessThan(18)
defseniors=Person.findAllByAgeGreaterThanEquals(65)
deforderedByName=Person.listOrderByName()
TESTING
/test/unit/
Spock Specifications
Deeplyintegrated in Grails
When creatinganyartifactacorrespondingtestis created
CreatingPersonalso creates aPersonSpec
TEST DEMO!
SPOCK TEST DEFAULT
//test/unit/grailsdemo/PersonSpec.groovy
@TestFor(Person)
classPersonSpecextendsSpecification{
defsetup(){
}
defcleanup(){
}
void"testsomething"(){
}
}
WRITE A TEST!
"age should be more than 0"
COULD LOOK LIKE THIS
//test/unit/grailsdemo/PersonSpec.groovy
@TestFor(Person)
classPersonSpecextendsSpecification{
void"ageshouldbemorethan0"(){
when:
defperson= newPerson(age:age,name:'joe')
then:
person.validate()==valid
where:
age |valid
0 | true
41 | true
-1 | false
}
}
TESTING
- THE EASIEST WAY TO EXECUTE CODE!
Playwith dynamic finders
TAKING CONTROL
/grails-app/controllers/
The C in MVC
Databinding
Redirectingrequests
Delegatingto business logic
HIM AGAIN!
Scaffolded UI Demo
SCAFFOLDED CONTROLLER
//grails-app/controllers/grailsdemo/PersonController.groovy
classPersonController{
staticscaffold=true
}
Fire itup and give itaspin
$grailsrun-app
STOP THE PRESSES
IT'S BACKED BY A REAL DATABASE!
Browse to http://localhost:8080/demo/dbconsole
WANT SOME REST?
AUTOMATIC CONTENT NEGOTIATION
http://localhost:8080/demo/person/show/1.json
{
class:"grailsdemo.Person",
id:1,
age:41,
name:"Brian"
}
http://localhost:8080/demo/person/show/1.xml
<personid="1">
<age>41</age>
<name>Brian</name>
</person>
BACK ON TRACK
GENERATE CONTROLLER
$grailsgenerate-controllergrailsdemo.Person
ALL THAT CODE
Open:
grails-app/controllers/grailsdemo/PersonController.groovy
SOMETHING LIKE THIS?
//grails-app/controllers/grailsdemo/PersonController.groovy
@Transactional(readOnly=true)
classPersonController{
staticallowedMethods=[save:"POST",update:"PUT",delete:"DELETE"]
defindex(Integermax){
params.max=Math.min(max?:10,100)
respondPerson.list(params),model:[personInstanceCount:Person.count()]
}
defshow(PersonpersonInstance){
respondpersonInstance
}
...
...
}
TESTING CONTROLLERS
Run:
$grailstest-appunit:PersonController
Whathappens?
Open:
/test/unit/grailsdemo/PersonControllerSpec.groovy
TESTING CONTROLLERS
Change:
defpopulateValidParams(params){
assertparams!=null
//TODO:Populatevalidpropertieslike...
//params["name"]='someValidName'
}
To:
defpopulateValidParams(params){
assertparams!=null
params["name"]='joe'//orwhatever
params["age"]=42//orwhatever
}
MORE TESTING
Do we have the time?
VIEWS
/grails-app/views/
The Vin MVC
GSP
GENERATE VIEWS
$grailsgenerate-allgrailsdemo.Person
THE INDEX VIEW
Open:
grails-app/views/person/index.gsp
THE SHOW VIEW
Open:
grails-app/views/person/show.gsp
CREATE AND EDIT VIEWS - TEMPLATES
Open:
grails-app/views/person/create.gsp
grails-app/views/person/edit.gsp
grails-app/views/person/_form.gsp
SERVICES
/grails-app/services/
Should contain allbusiness logic
Transactional
Injected into anyartefact
CREATE THE SERVICE
$grailscreate-servicePerson
Open:
grails-app/services/demo/PersonService.groovy
UPDATE THE SERVICE
@Transactional
classPersonService{
defgetAll(){
Person.list()
}
}
UPDATE THE CONTROLLER
classPersonController{
PersonServicepersonService//injectedbygrails
defindex(Integermax){
respondpersonService.getAll()
}
...
}
LETS ADD A NEW FEATURE
Person should have country
//grails-app/domain/Person.groovy
classPerson{
Stringname
Integerage
Stringcountry
staticconstraints={
nameblank:false
age min:0
countrynullable:true
}
}
UPDATE THE TESTS
Open:
test/unit/grailsdemo/PersonSpec.groovy
UPDATE THE VIEW
Open:
grails-app/views/person/index.gsp
and:
grails-app/views/person/_form.gsp
ANOTHER NEW FEATURE
Creatinganew person should send you to the listview (index)
Open:
grails-app/controllers/grailsdemo/PersonController.groovy
PLUGINS
https://grails.org/plugins/
Build TestData
Searchable
Quartz
SpringSecurity
MongoDB
and itgoes on and on...

The Grails introduction workshop