The Grails introduction workshop

55
GRAILS N00B TO NINJA IN 3 HOURS [INTRODUCTION WORKSHOP] / Brian Johnsen @brianjohnsendk

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 Interface

Transcript of The Grails introduction workshop

Page 1: The Grails introduction workshop

GRAILSN00B TO NINJA IN 3 HOURS

[INTRODUCTION WORKSHOP]

/ Brian Johnsen @brianjohnsendk

Page 2: The Grails introduction workshop

WHOAMI(whocares)

Brian Johnsen, Gennemtænkt IT A/S12+ years JavaGroovy & Grails since 2008Your Friendly Neighborhood GR8Conf Organizer

Page 3: The Grails introduction workshop

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

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

ARCHITECTURE

Page 6: The Grails introduction workshop

PHILOSOPHY

Page 7: The Grails introduction workshop

LETS GET STARTED!

Page 8: The Grails introduction workshop

DEMO TIME!

CLI and create app demo

Page 9: The Grails introduction workshop

YOUR TURN!

Page 10: The Grails introduction workshop

It just goes on...

CLITry it out

$ grails help

Page 11: The Grails introduction workshop

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

RUNNING APPLICATION!

Page 13: The Grails introduction workshop

ANATOMY

Page 14: The Grails introduction workshop

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
Page 16: The Grails introduction workshop
Page 17: The Grails introduction workshop

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

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

DOMAIN DEMO!

Page 20: The Grails introduction workshop

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

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

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

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

TEST DEMO!

Page 25: The Grails introduction workshop

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

WRITE A TEST!

"age should be more than 0"

Page 27: The Grails introduction workshop

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

TESTING- THE EASIEST WAY TO EXECUTE CODE!

Play with dynamic finders

Page 29: The Grails introduction workshop

TAKING CONTROL/grails-app/controllers/

The C in MVCData bindingRedirecting requestsDelegating to business logic

Page 30: The Grails introduction workshop

HIM AGAIN!

Scaffolded UI Demo

Page 31: The Grails introduction workshop

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

STOP THE PRESSESIT'S BACKED BY A REAL DATABASE!

Browse to http://localhost:8080/demo/dbconsole

Page 33: The Grails introduction workshop

WANT SOME REST?

Page 34: The Grails introduction workshop

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

BACK ON TRACK

Page 36: The Grails introduction workshop

GENERATE CONTROLLER

$ grails generate-controller grailsdemo.Person

Page 37: The Grails introduction workshop

ALL THAT CODEOpen:

grails-app/controllers/grailsdemo/PersonController.groovy

Page 38: The Grails introduction workshop

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

TESTING CONTROLLERSRun:

$ grails test-app unit: PersonController

What happens?

Open:/test/unit/grailsdemo/PersonControllerSpec.groovy

Page 40: The Grails introduction workshop

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

MORE TESTING

Do we have the time?

Page 42: The Grails introduction workshop

VIEWS/grails-app/views/

The V in MVCGSP

Page 43: The Grails introduction workshop

GENERATE VIEWS

$ grails generate-all grailsdemo.Person

Page 44: The Grails introduction workshop

THE INDEX VIEWOpen:

grails-app/views/person/index.gsp

Page 45: The Grails introduction workshop

THE SHOW VIEWOpen:

grails-app/views/person/show.gsp

Page 46: The Grails introduction workshop

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

SERVICES/grails-app/services/

Should contain all business logicTransactionalInjected into any artefact

Page 48: The Grails introduction workshop

CREATE THE SERVICE

$ grails create-service Person

Open:grails-app/services/demo/PersonService.groovy

Page 49: The Grails introduction workshop

UPDATE THE SERVICE@Transactionalclass PersonService { def getAll() { Person.list() }}

Page 50: The Grails introduction workshop

UPDATE THE CONTROLLERclass PersonController {

PersonService personService //injected by grails

def index(Integer max) { respond personService.getAll() }...}

Page 51: The Grails introduction workshop

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

UPDATE THE TESTS

Open:test/unit/grailsdemo/PersonSpec.groovy

Page 53: The Grails introduction workshop

UPDATE THE VIEWOpen:

grails-app/views/person/index.gsp

and:grails-app/views/person/_form.gsp

Page 54: The Grails introduction workshop

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

PLUGINShttps://grails.org/plugins/

Build Test DataSearchableQuartzSpring SecurityMongoDBand it goes on and on...