Grails beginners workshop

68
GRAILS - BEGINNERS WORKSHOP Jacob Aae Mikkelsen

Transcript of Grails beginners workshop

Page 1: Grails beginners workshop

GRAILS - BEGINNERS WORKSHOPJacob Aae Mikkelsen

Page 2: Grails beginners workshop

AGENDAInstalling Grails

Grails Intro

Application Scenario

Creating the application

Exercises

Page 3: Grails beginners workshop

JACOB AAE MIKKELSENSenior Engineer at Lego

Microservice based architechture on JVM

Previously 4 years at Gennemtænkt IT

Consultant on Groovy and Grails

External Associate Professor - University of SouthernDenmark

@JacobAae

Blogs The Grails Diary

Page 4: Grails beginners workshop

INSTALLING GRAILS

Page 5: Grails beginners workshop

USB WITH MATERIALOn the USB Sick passed around, the following materials are

available

These slides

Git repository with Grails application

Grails

IntelliJ editor

Bootstrap files

Page 6: Grails beginners workshop

MAC & LINUXUse Gvm tool

curl ­s get.gvmtool.net | bash

Restart terminal

gvm install grails 3.0.1

Page 7: Grails beginners workshop

WINDOWSOption 1: Use GVM Tool in Cygwin

Option 2: Use Posh-GVM in Powershell( )https://github.com/flofreud/posh-gvm

Option 3 Download Grails, and setup GRAILS_HOME(

)http://grails.asia/grails-tutorial-for-beginners-setup-your-

windows-development-environment/

Page 8: Grails beginners workshop

GRAILS INTROGrails is a full stack framework

-

Embrace the Don’t Repeat Yourself (DRY) principle

-

Encourages proper testing

-

Use the Groovy language and extensive use of DomainSpecific Languages (DSLs)

Page 9: Grails beginners workshop

INCLUDED WITH GRAILSObject Relational Mapping (ORM) layer → Hibernate

Expressive view technology → Groovy Server Pages (GSP)

Controller layer → Spring MVC

Interactive command line env and build system → Gradle

Embedded Tomcat container with on the fly reloading

Dependency injection → Spring container

i18n → Spring’s core MessageSource concept

Transactional service layer → Spring’s transactionabstraction

Page 10: Grails beginners workshop

LATEST VERSION: 3.0.1Groovy 2.4

Spring 4.1 and Spring Boot 1.2

Gradle Build System

Application Profiles

Redesigned API based on Traits

Filters → Interceptors

Main class → Easy to run from any IDE

Page 11: Grails beginners workshop

APPLICATION SCENARIOLets make a small conference application that can

keep track at what talks and workshops you haveattended

let attendees rate and supply feedback

Page 12: Grails beginners workshop

MODELWe need 3 domain classes in this small application

Attendee

Talk

Rating

Page 13: Grails beginners workshop

ATTENDEEName

Email

Which talks attended

Page 14: Grails beginners workshop

TALKSpeaker name

Starting time

List of ratings and comments

Page 15: Grails beginners workshop

RATINGAttendee who supplied the rating

The talk it rates

The rating (1-5)

Comment

Page 16: Grails beginners workshop

FUNCTIONALITYAttendees must be able to register which talks heattended

An attendee must be able to rate a talk from 1-5 andsubmit a comment too

A list of comments and an average grade must beavailable when vieving a talk

Page 17: Grails beginners workshop

STEP 1: CREATING THE APPLICATIONTo create a new Grails Application, run the grails

create-app command in a terminal

grails create­app eu­gr8conf­grailsdemo

Lets go through the tree view of whats generated

You don’t have to do the above step, if you clone thisrepo - then it is all done :)

git clone https://github.com/JacobAae/eu­gr8conf­grailsdemo.git

Page 18: Grails beginners workshop

WHATS GENERATED (1). build.gradle gradle wrapper gradle­wrapper.jar gradle­wrapper.properties gradle.properties gradlew gradlew.bat

Page 19: Grails beginners workshop

WHATS GENERATED (2) grails­app controllers UrlMappings.groovy domain services taglib utils views error.gsp index.gsp layouts main.gsp notFound.gsp

Page 20: Grails beginners workshop

WHATS GENERATED (3) grails­app assets images javascripts stylesheets conf application.yml logback.groovy spring resources.groovy i18n

Page 21: Grails beginners workshop

WHATS GENERATED (4) grails­app init BootStrap.groovy eu gr8conf grailsdemo Application.groovy

Page 22: Grails beginners workshop

WHATS GENERATED (5) src integration­test groovy main groovy webapp test groovy

Page 23: Grails beginners workshop

RUNNING OUR APPTo get to the interactive console:

grails

In interactive mode

run­app

Page 24: Grails beginners workshop

RESULTGo to: http://localhost:8080/

Page 25: Grails beginners workshop

STEP 2: FIRST DOMAIN CLASSLets create the first domin class with controller and views:

Attendee

Page 26: Grails beginners workshop

CREATING THE DOMAIN CLASSIn interactive mode

create­domain­class eu.gr8conf.grailsdemo.Attendee

Resulting in| Created grails-app/domain/eu/gr8conf/grailsdemo/Attendee.groovy| Created src/test/groovy/eu/gr8conf/grailsdemo/AttendeeSpec.groovy

Page 27: Grails beginners workshop

RUNNING TESTSIn interactive mode

test­app

Which fails since we have not yet implemented any test

Page 28: Grails beginners workshop

PROPERTIES AND CONSTRAINTSEdit the Attendee class to contain

String nameString emailString nationality

Date dateCreatedDate lastUpdated

static constraints = name blank: false email blank: false, unique: true, email: true nationality nullable: true

Page 29: Grails beginners workshop

GORMAdds lots of convenience

validate()

save()

get(id)

delete()

list()

Dynamic finders: Attendee.findByName('Jacob')

Page 30: Grails beginners workshop

TESTINGHere is an example of a test for constraints that are violated

@Unrollvoid "Test invalid properties for attendee: #comment"() when: Attendee attendee = new Attendee(name: name, email: email, nationality: nationality)

then: !attendee.validate()

where: name | email | nationality | comment '' | '[email protected]' | 'Danish' | 'Blank name'

INFO: Lets add positive and more negative tests for theconstraints.

Page 31: Grails beginners workshop

CONTROLLER AND VIEWSIn interactive mode

generate­all eu.gr8conf.grailsdemo.Attendee

And run the app again - and click the controller

Page 32: Grails beginners workshop

EXERCISEOpen AttendeeControllerSpec.groovy and update

it, so the tests passes

Page 33: Grails beginners workshop

STEP 3: MORE DOMAIN CLASSESLets make the other two domain classes, and see how they

can interact

Page 34: Grails beginners workshop

HINTS create­domain­class eu.gr8conf.grailsdemo.Talk create­domain­class eu.gr8conf.grailsdemo.Rating generate­all eu.gr8conf.grailsdemo.Talk generate­all eu.gr8conf.grailsdemo.Rating

Page 35: Grails beginners workshop

RELATIONSAttendee.groovy

static hasMany = [talks: Talk]

Talk.groovy

static belongsTo = Attendee // Where the addTo method will work from i.e. cascading savestatic hasMany = [attendees: Attendee,ratings: Rating]

Rating.groovy

static belongsTo = [talk:Talk]

Page 36: Grails beginners workshop

CONTROLLERSA controller handles requests and creates or prepares theresponse

Business logic placed elsewhere (services)

Placed in controller folder and ends in Controller

Methods → actions

Databinding

Page 37: Grails beginners workshop

VIEWS AND TEMPLATESMade with Groovy Server Pages (GSP)

Extensive suite of tags

formatting

looping

input fields

Page 38: Grails beginners workshop

CONTROLLERS AND VIEWSProblem: Lets make a new Display Attendee page, where we

can see talks attended better.

What to do:

Make a display action in the AttendeeController

Add a display.gsp view

add link to the new display or replace the show action

We could also rewrite the show action :)

Page 39: Grails beginners workshop

THE ACTIONdef display(Long id) Attendee attendee = Attendee.get(id) respond attendee

Page 40: Grails beginners workshop

THE VIEWCopy and paste the show.gsp view, and replace

<f:display bean="attendee" />

with

Page 41: Grails beginners workshop

THE VIEW (2)<ol class="property­list attendee"> <li class="fieldcontain"> <span id="name­label" class="property­label">Name</span> <span class="property­value" aria­labelledby="name­label"> $attendee.name</span></li> <!­­ email and nationalityleft out ­­> <li class="fieldcontain"> <span id="talks­label" class="property­label">Talks</span> <span class="property­value" aria­labelledby="talks­label"> <ul> <g:each in="$attendee.talks" var="talk"> <li>$talk.title</li> </g:each> </ul> </span> </li></ol>

Page 42: Grails beginners workshop

EXERCISESMake a link for each talk to go to an add rating page

Implement the add rating functionality

Page 43: Grails beginners workshop

STEP 4: SERVICESThe default code leaves too much logic in the controller, thisshould be in a service, that also should handle transactions

etc.

Lets try to cleanup the attendee controller and place someof the logic in a service

Page 44: Grails beginners workshop

SERVICESAre transactional by default

Easy to autoinject

Great place for logic and functionality

Page 45: Grails beginners workshop

HINTScreate­service eu.gr8conf.grailsdemo.AttendeeService

Page 46: Grails beginners workshop

USING SERVICES - AUTOINJECTIONIn the AttendeeController, where we will use the service, all

we need to do, is add this line

AttendeeService attendeeService

Then it will be autoinjected because Grails recognices thename

Page 47: Grails beginners workshop

CLEANING UP CONTROLLERSThe default generated controllers does everything

Lets cleanup the save method.

Page 48: Grails beginners workshop

HELPER CLASSESIn src/main/groovy you can place helper classes, or

non-Grails-artefacts

class Result def item Status status

enum Status OK, NOT_FOUND, HAS_ERRORS

Page 49: Grails beginners workshop

SERVICE METHODThe service method could look like

Result saveAttendee(Attendee attendee) if (attendee == null) transactionStatus.setRollbackOnly() return new Result(status: Status.NOT_FOUND) if (attendee.hasErrors()) transactionStatus.setRollbackOnly() return new Result(status: Status.HAS_ERRORS, item: attendee) attendee.save flush:true return new Result(status: Status.OK, item: attendee)

Page 50: Grails beginners workshop

CONTROLLERdef save(Attendee attendee) Result result = attendeeService.saveAttendee(attendee) switch( result.status) case Status.NOT_FOUND: notFound() break case Status.HAS_ERRORS: respond result.item.errors, view:'create' break case Status.OK: request.withFormat form multipartForm flash.message = message(code: 'default.created.message println attendee.dump() redirect attendee '*' respond attendee, [status: CREATED]

Page 51: Grails beginners workshop

TASKClean up the rest of the controller, removig everything that

needs the @Transactional annotation

Page 52: Grails beginners workshop

STEP 5: TAGLIBSTaglibs can help make the views more clean and DRY

Page 53: Grails beginners workshop

TAGLIBPlaced in grails-app/taglib

must end with TagLib.groovy

Closure taking parameters attrs and body

create­taglib eu.gr8conf.grailsdemo.RatingTagLib

Page 54: Grails beginners workshop

SERVICE TO CALCULATE AVERAGERATING

class TalkService

BigDecimal calculateAverageRating(Talk talk) if( !talk || !talk.ratings ) return null talk.ratings*.value.sum() / talk.ratings.size()

Page 55: Grails beginners workshop

THE TAGLIBTalkService talkService

def showRating = attrs ­> Talk talk = attrs.talk BigDecimal avg = talkService.calculateAverageRating(talk)

if( avg ) out << """<span class='average­rating'> $roundAverage(avg)</span>""" else out << "N/A" private roundAverage(BigDecimal average) "$Math.round(1.0 * average * 100) / 100 "

Page 56: Grails beginners workshop

TESTING TAGLIBSvoid "test output from showRating"() when: String output = tagLib.showRating(talk: new Talk(ratings: ratings), a ­> a )

then: output == expected

where: ratings | expected null | 'N/A' [new Rating(value: 5)] | "<span class='average­rating'>5</span>

Page 57: Grails beginners workshop

STEP 6: ASSETS, STYLING, PLUGINSAND LAYOUTS

Page 58: Grails beginners workshop

ASSETSJavascript, images and css files are handled by asset-

pipeline plugins

build.gradle

runtime "org.grails.plugins:asset­pipeline"

Compiles Less

Minifies JS

Combines files

Page 59: Grails beginners workshop

BOOTSTRAPLets include the (Twitter) Bootstrap project in our

application.

Page 60: Grails beginners workshop

BOOTSTRAPIn the grails-app/assets folder, place the Bootstrap

files

. fonts glyphicons­halflings­regular.eot ... + rest of fonts files images ... javascripts bootstrap.css.map bootstrap.js stylesheets bootstrap.css bootstrap­theme.css combined­bootstrap.css ...

Page 61: Grails beginners workshop

ASSET FILELets make a Bootstrap css asset package

combined-bootstrap.css

/**= require bootstrap*= require bootstrap­theme*= require_self*/

Page 62: Grails beginners workshop

LAYOUTSIn grails-app/views/layouts Layonts are placed

that can be reused across pages.

Page 63: Grails beginners workshop

LAYOUTSviews/layouts/bootstrap.gsp

<!doctype html><html lang="en" class="no­js"> <head> <meta http­equiv="Content­Type" content="text/html; charset=UTF­8 <meta http­equiv="X­UA­Compatible" content="IE=edge"> <title><g:layoutTitle default="Grails"/></title> <meta name="viewport" content="width=device­width, initial­scale=1 <asset:stylesheet src="combined­bootstrap.css"/> <asset:javascript src="application.js"/> <g:layoutHead/> </head> <body class="container"> <g:layoutBody/> <div class="footer" role="contentinfo"></div> <div id="spinner" class="spinner" style="display:none;"><g:message </body></html>

Page 64: Grails beginners workshop

USING LAYOUTSviews/attendee/display.gsp

<head> <meta name="layout" content="bootstrap">...

views/attendee/display.gsp

<g:link class="btn btn­primary" action="edit" resource="$attendee" <span class="glyphicon glyphicon­edit" aria­hidden="true"></span> <g:message code="default.button.edit.label" default="Edit" /></g:link>

Page 65: Grails beginners workshop

FIXING ICONSIn bootstrap.css, update the path to Glyphicons font

(line 267++)

views/attendee/display.gsp

@font­face font­family: 'Glyphicons Halflings'; src: url('../assets/fonts/glyphicons­halflings­regular.eot'); src: url('../assets/fonts/glyphicons­halflings­regular.eot?#iefix') format('embedded­opentype'), url('../fonts/glyphicons­halflings­regular.woff2') format('woff2'), url('../fonts/glyphicons­halflings­regular.woff') format('woff'), url('../fonts/glyphicons­halflings­regular.ttf') format('truetype'), url('../fonts/glyphicons­halflings­regular.svg#glyphicons_halflingsregular') format('svg');

Page 66: Grails beginners workshop

TASKSInclude Bootstrap

Restyle a page using a bootstrap layout

Page 67: Grails beginners workshop

EXERCISESShow the distribution of grades in the talk page (2*3, 2*4,4*5)

Page 68: Grails beginners workshop

LITERATUREhttp://gvmtool.net/

https://github.com/flofreud/posh-gvm

https://grails.org/single-page-documentation.html

http://getbootstrap.com/