the grails introduction workshop
DESCRIPTION
Grails is a complete web application framework that runs on the Java JVM. It is a full-stack framework, and handles all layers from the user interface to the persistence layer. Grails is based on known and proven technologies, such as the Spring Framework and Hibernate. It has been around since 2006, and has made considerable progress around the globe in the past few years. This workshop aims to show how to get from 0 to running application with Grails in three hours - so hold on to your hats! We will touch the following points: Grails application structure Domain model and persistence Controllers Services Testing User InterfaceTRANSCRIPT
![Page 1: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/1.jpg)
GRAILSN00B TO NINJA IN 3 HOURS
[INTRODUCTION WORKSHOP]
/ Brian Johnsen @brianjohnsendk
![Page 2: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/2.jpg)
WHOAMI(whocares)
Brian Johnsen, Gennemtænkt IT A/S12+ years JavaGroovy & Grails since 2008Your Friendly Neighborhood GR8Conf Organizer
![Page 3: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/3.jpg)
READY?1. Copy grails install to disk and unzip2. Add to path3. Voilá
$ grails -versionGrails version: 2.3.8
![Page 4: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/4.jpg)
FULL STACK FRAMEWORKORM (GORM)Powerful view technology (GSP) and easy TagLib creationDependency Injection and on-the-fly reloading (Spring)Internationalization support (i18n)Runtime Container (Tomcat)Testing Framework (Spock)Plugin SystemCommand line toolsBuild Tool (Gant/Gradle)and a bunch of other stuff…
![Page 5: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/5.jpg)
ARCHITECTURE
![Page 6: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/6.jpg)
PHILOSOPHY
![Page 7: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/7.jpg)
LETS GET STARTED!
![Page 8: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/8.jpg)
DEMO TIME!
CLI and create app demo
![Page 9: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/9.jpg)
YOUR TURN!
![Page 10: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/10.jpg)
It just goes on...
CLITry it out
$ grails help
![Page 11: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/11.jpg)
CREATE THE APP$ grails create-app grailsdemo
$ cd grailsdemo
$ grails run-app
| Server running. Browse to http://localhost:8080/grailsdemo
Open your favorite browser and check it out!
![Page 12: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/12.jpg)
RUNNING APPLICATION!
![Page 13: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/13.jpg)
ANATOMY
![Page 14: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/14.jpg)
ANATOMYgrails-app - Top level directory for Groovy sourcesconf - Configuration sources.controllers - Web controllers - The C in MVC.domain - The application domain - The M in MVC.i18n - Support for internationalization (i18n).migrations - Database migration.services - The service layer.taglib - Tag libraries.utils - Grails specific utilities.views - Groovy Server Pages - The V in MVC.
scripts - Gant scripts.src - Supporting sourcesgroovy - Other Groovy sourcesjava - Other Java sources
test - Unit and integration tests.web-app - JavaScript and CSS.wrapper - Wrapper.
![Page 15: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/15.jpg)
![Page 16: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/16.jpg)
![Page 17: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/17.jpg)
DON'T LET FRIENDS FORKOpen:
/grails-app/conf/BuildConfig.groovy
Change:grails.project.fork = [ // configure settings for compilation JVM, note that if you alter the Groovy version forked compilation is required ... ... // configure settings for the Console UI JVM console: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256]]
To:grails.project.fork = false
![Page 18: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/18.jpg)
DOMAIN/grails-app/domain/
The M in MVCGets mapped to the databaseDynamic finders FTW -> Forget SQLconstraints enforce database consistencyDefines scaffolded views
![Page 19: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/19.jpg)
DOMAIN DEMO!
![Page 20: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/20.jpg)
MY FIRST DOMAIN CLASS$ grails create-domain-class Person
//grails-app/domain/Person.groovyclass Person { String name Integer age static constraints = { name blank: false age min: 0 }}
![Page 21: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/21.jpg)
SAVE AND VALIDATEconstraints are enforced on save() and validate()def person = new Person(age: 42, name: 'Douglas Adams')
person.validate() //only checks constraintsperson.save() //saves the instance to the database
![Page 22: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/22.jpg)
WHOLE LOTTA DYNAMIC GOIN’ ONFinders are dynamically created for all properties
def everybody = Person.list()def theOne = Person.findByNameAndAge('Brian', 41)def caseInsensitive = Person.findAllByNameIlike('graeme')def underAge = Person.findAllByAgeLessThan(18)def seniors = Person.findAllByAgeGreaterThanEquals(65)def orderedByName = Person.listOrderByName()
![Page 23: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/23.jpg)
TESTING/test/unit/
Spock SpecificationsDeeply integrated in GrailsWhen creating any artifact a corresponding test is created
Creating Person also creates a PersonSpec
![Page 24: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/24.jpg)
TEST DEMO!
![Page 25: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/25.jpg)
SPOCK TEST DEFAULT//test/unit/grailsdemo/PersonSpec.groovy@TestFor(Person)class PersonSpec extends Specification { def setup() { }
def cleanup() { }
void "test something"() { }}
![Page 26: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/26.jpg)
WRITE A TEST!
"age should be more than 0"
![Page 27: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/27.jpg)
COULD LOOK LIKE THIS//test/unit/grailsdemo/PersonSpec.groovy@TestFor(Person)class PersonSpec extends Specification { void "age should be more than 0"() { when: def person = new Person(age: age, name: 'joe')
then: person.validate() == valid
where: age | valid 0 | true 41 | true -1 | false }}
![Page 28: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/28.jpg)
TESTING- THE EASIEST WAY TO EXECUTE CODE!
Play with dynamic finders
![Page 29: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/29.jpg)
TAKING CONTROL/grails-app/controllers/
The C in MVCData bindingRedirecting requestsDelegating to business logic
![Page 30: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/30.jpg)
HIM AGAIN!
Scaffolded UI Demo
![Page 31: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/31.jpg)
SCAFFOLDED CONTROLLER//grails-app/controllers/grailsdemo/PersonController.groovyclass PersonController { static scaffold = true}
Fire it up and give it a spin$ grails run-app
![Page 32: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/32.jpg)
STOP THE PRESSESIT'S BACKED BY A REAL DATABASE!
Browse to http://localhost:8080/demo/dbconsole
![Page 33: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/33.jpg)
WANT SOME REST?
![Page 34: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/34.jpg)
AUTOMATIC CONTENT NEGOTIATIONhttp://localhost:8080/demo/person/show/1.json
{ class: "grailsdemo.Person", id: 1, age: 41, name: "Brian"}
http://localhost:8080/demo/person/show/1.xml<person id="1"> <age>41</age> <name>Brian</name></person>
![Page 35: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/35.jpg)
BACK ON TRACK
![Page 36: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/36.jpg)
GENERATE CONTROLLER
$ grails generate-controller grailsdemo.Person
![Page 37: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/37.jpg)
ALL THAT CODEOpen:
grails-app/controllers/grailsdemo/PersonController.groovy
![Page 38: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/38.jpg)
SOMETHING LIKE THIS?//grails-app/controllers/grailsdemo/PersonController.groovy@Transactional(readOnly = true)class PersonController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) { params.max = Math.min(max ?: 10, 100) respond Person.list(params), model: [personInstanceCount: Person.count()] }
def show(Person personInstance) { respond personInstance } ... ...}
![Page 39: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/39.jpg)
TESTING CONTROLLERSRun:
$ grails test-app unit: PersonController
What happens?
Open:/test/unit/grailsdemo/PersonControllerSpec.groovy
![Page 40: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/40.jpg)
TESTING CONTROLLERSChange:
def populateValidParams(params) { assert params != null // TODO: Populate valid properties like... //params["name"] = 'someValidName' }
To:def populateValidParams(params) { assert params != null params["name"] = 'joe' //or whatever params["age"] = 42 //or whatever }
![Page 41: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/41.jpg)
MORE TESTING
Do we have the time?
![Page 42: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/42.jpg)
VIEWS/grails-app/views/
The V in MVCGSP
![Page 43: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/43.jpg)
GENERATE VIEWS
$ grails generate-all grailsdemo.Person
![Page 44: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/44.jpg)
THE INDEX VIEWOpen:
grails-app/views/person/index.gsp
![Page 45: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/45.jpg)
THE SHOW VIEWOpen:
grails-app/views/person/show.gsp
![Page 46: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/46.jpg)
CREATE AND EDIT VIEWS - TEMPLATESOpen:
grails-app/views/person/create.gsp
grails-app/views/person/edit.gsp
grails-app/views/person/_form.gsp
![Page 47: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/47.jpg)
SERVICES/grails-app/services/
Should contain all business logicTransactionalInjected into any artefact
![Page 48: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/48.jpg)
CREATE THE SERVICE
$ grails create-service Person
Open:grails-app/services/demo/PersonService.groovy
![Page 49: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/49.jpg)
UPDATE THE SERVICE@Transactionalclass PersonService { def getAll() { Person.list() }}
![Page 50: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/50.jpg)
UPDATE THE CONTROLLERclass PersonController {
PersonService personService //injected by grails
def index(Integer max) { respond personService.getAll() }...}
![Page 51: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/51.jpg)
LETS ADD A NEW FEATUREPerson should have country
//grails-app/domain/Person.groovyclass Person { String name Integer age String country static constraints = { name blank: false age min: 0 country nullable: true }}
![Page 52: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/52.jpg)
UPDATE THE TESTS
Open:test/unit/grailsdemo/PersonSpec.groovy
![Page 53: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/53.jpg)
UPDATE THE VIEWOpen:
grails-app/views/person/index.gsp
and:grails-app/views/person/_form.gsp
![Page 54: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/54.jpg)
ANOTHER NEW FEATURECreating a new person should send you to the list view (index)
Open:grails-app/controllers/grailsdemo/PersonController.groovy
![Page 55: The Grails introduction workshop](https://reader036.vdocument.in/reader036/viewer/2022081400/554f5881b4c905b9508b5230/html5/thumbnails/55.jpg)
PLUGINShttps://grails.org/plugins/
Build Test DataSearchableQuartzSpring SecurityMongoDBand it goes on and on...