+ All Categories

Grails

Date post: 23-Nov-2014
Category:
Upload: joshegl
View: 482 times
Download: 1 times
Share this document with a friend
Popular Tags:
173
1/173 - 1% Grails Introduction
Transcript
Page 1: Grails

1/173 - 1%

Grails Introduction

Page 2: Grails

2/173 - 2%

Introduction

Page 3: Grails

3/173 - 2%

Grailsfull stack framework + plugins

Groovy language

Convention over configuration

Object Relational Mapping (GORM) built on top Hibernate

View technology (GSPs)

Controller layer built on Spring MVC

Embedded Tomcat

Dependency Injection with Spring

i18n support

Page 4: Grails

4/173 - 3%

Installationhttp://grails.org/Download (grails-1.3.5.zip)

Add GRAILS_HOME environment variable

Add %GRAILS_HOME%\bin to PATH

Page 5: Grails

5/173 - 3%

Test Grails installation$ grailsWelcome to Grails 1.3.5 - http://grails.org/Licensed under Apache Standard License 2.0Grails home is set to: /home/miguel/.grails

No script name specified. Use 'grails help' for more info or 'grails interactive' to enter interactive mode

Page 6: Grails

6/173 - 4%

First Grails application

Page 7: Grails

7/173 - 5%

Create application$ grails create-app tutorial$ cd tutorial

Page 8: Grails

8/173 - 5%

Create controller$ grails create-controller hello

Page 9: Grails

9/173 - 6%

grails-app/controllers/tutorial/HelloController.groovy

package tutorial

class HelloController { def world = { render "Hello World!" }}

Page 10: Grails

10/173 - 6%

Run application$ grails run-app

Page 11: Grails

11/173 - 7%

browse http://localhost:8080/tutorial

Page 12: Grails

12/173 - 7%

IDE- IntelliJ IDEA grails integrate-with --intellij- NetBeans (grails supported)- Eclipse (SpringSource Tool Suite, STS)- TextMate grails integrate-with --textmate

Page 13: Grails

13/173 - 8%

Grails directory structuregrails-app - top level app dir conf - Configuration files controllers - web controllers domain - application domain models i18n - i18n resource files services - services layer taglib - custom tag libraries views - Groovy Server Pagesscripts - scripts for grailssrc - Supporting sourcestest - unit, integration and functional tests

Page 14: Grails

14/173 - 9%

Grails commands

Page 15: Grails

15/173 - 9%

Running a Grails application$ grails run-app

Page 16: Grails

16/173 - 10%

Testing a Grails application$ grails test-app

Page 17: Grails

17/173 - 10%

Deploying a Grails application$ grails war

Page 18: Grails

18/173 - 11%

Scaffolding$ grails generate-all tutorial.Hello

- generates skeleton code - controller - views- it SHOULD always be customized- it is only a starting point

Page 19: Grails

19/173 - 11%

Creating artifacts$ grails create-controller$ grails create-domain-class$ grails create-unit-test$ grails create-tag-lib

Page 20: Grails

20/173 - 12%

Configuration

Page 21: Grails

21/173 - 13%

Basic configurationgrails/conf/Config.groovy

Page 22: Grails

22/173 - 13%

custom configurationset: my.app.value = "some value"

read (controller/taglibs): grailsApplication.config.my.app.value

import org.codehaus.groovy.grails.commons.*

CodeHolder.config.my.app.hello

Page 23: Grails

23/173 - 14%

logginglog4j = { error 'package1', 'package2' warn 'package3'}

Page 24: Grails

24/173 - 14%

logging packagesorg.codejaus.groovy.grails.commons - class loadingorg.codejaus.groovy.grails.web - web request processingorg.codejaus.groovy.grails.web.mapping - url mappingorg.codejaus.groovy.grails.plugins - plugin activityorg.springframework - spring activityorg.hibernate - hibernate activity

Page 25: Grails

25/173 - 15%

GORMgrails.gorm.failOnError

save() method throws Exception on validation failure

grails.gorm.autoFlush=true

save(),delete(),merge() to flush session

Page 26: Grails

26/173 - 16%

Environmentsper environment configuration

- Config.groovy- DataSource.groovy

preset- dev- test- prod

Page 27: Grails

27/173 - 16%

Environments in command linegrails [environment] [command name]grails test war // creates war for the test //environment

Page 28: Grails

28/173 - 17%

Programmatic environmentdetection

import grails.util.Environment

switch(Environment.current) { case Environment.DEVELOPMENT: someConfigForDev() break case Environment.PRODUCTION: someConfigForProd() break}

Page 29: Grails

29/173 - 17%

Per environment bootstrapdef init = { ServletContext ctx -> environments { production { ctx.setAttribute("env", "prod") } development { someConfigForDev() } } someConfigForAllEnvironments()}

Page 30: Grails

30/173 - 18%

Environments in application codeimport grails.util.EnvironmentEnvironment.executeForCurrentEnvironment { production { someConfig() } development { someOtherConfig() }}

Page 31: Grails

31/173 - 18%

DataSourceJDBC

- put jar in grails project lib/ directory- environment aware- use a runtime dependency

dependencies { // mysql runtime 'mysql:mysql-connector-java:5.1.5' // sqlserver runtime 'net.sourceforge.jtds:jtds:1.2.4'}

Page 32: Grails

32/173 - 19%

JDBC configuration- driverClassName- username- password- url- dbCreate- pooled- logSql- dialect- properties - jndiName

Page 33: Grails

33/173 - 20%

JDBC configuration exampledataSource { pooled = true dbCreate = "update" url = "jdbc:mysql://localhost/yourDB" driverClassName = "com.mysql.jdbc.Driver" dialect = org.hibernate.dialect.MySQL5InnoDBDialect username = "yourUser" password = "yourPassword" properties { maxActive = 50 maxIdle = 25 minIdle = 5 initialSize = 5 minEvictableIdleTimeMillis = 60000 timeBetweenEvictionRunsMillis = 60000 maxWait = 10000 validationQuery = "/* ping */" }}

Page 34: Grails

34/173 - 20%

Externalized configurationconfiguration

grails.config.locations = [ "classpath:${appName}-config.properties", "classpath:${appName}-config.groovy", "file:${userHome}/.grails/${appName}-config.properties", "file:${userHome}/.grails/${appName}-config.groovy" ]

read: grailsApplication

Page 35: Grails

35/173 - 21%

Versioning// set$ grails set-version 0.99application.properties

// read in controllersdef version = grailsApplication.metadata['app.version']def grailsVer = grailsApplication.metadata['app.grails.version']

def version = grails.util.GrailsUtil.grailsVersion

Page 36: Grails

36/173 - 21%

Documentation- Textile variation- src/doc/guide 1. first chapter.gdoc 2. this will be the second chapter.gdoc

- $ grails doc # generate documentation

Page 37: Grails

37/173 - 22%

Dependency resolution- Repositories - maven - directory

- Scope - build - compile - runtime - test - provided

Page 38: Grails

38/173 - 22%

Configuration// group:name:versionruntime "com.mysql:mysql-connector-java:5.1.5"runtime(group: 'com.mysql', name: 'mysql-connector-java', version: '5.1.5')

// plugin dependenciesplugins { test ':spock:0.5-groovy' runtime ':jquery:1.4.2.7'}

Page 39: Grails

39/173 - 23%

Command line

Page 40: Grails

40/173 - 24%

Gant- Groovy wrapper around Apache Ant- Search locations: - USER_HOME/.grails/scripts - PROJECT_HOME/scripts - PROJECT_HOME/plugins/*/scripts - GRAILS_HOME/scripts

- example $ grails run-app

- searches:- USER_HOME/.grails/scripts/RunApp.groovy - PROJECT_HOME/scripts/RunApp.groovy - PLUGINS_HOME/*/scripts/RunApp.groovy - GLOBAL_PLUGINS_HOME/*/scripts/RunApp.groovy - GRAILS_HOME/scripts/RunApp.groovy

Page 41: Grails

41/173 - 24%

Ant- $ grails integrate-with --ant - build.xml - ivy.xml - ivisettings.xml

- CuiseControl/Hudson

Page 42: Grails

42/173 - 25%

GORM

Page 43: Grails

43/173 - 25%

Domain classes- hold state about business processes- implement behavior- relationships between domain classes - one-to-one - one-to-many

Page 44: Grails

44/173 - 26%

GORM- Grails' Object Relational Mapping (ORM)- Hibernate 3

Page 45: Grails

45/173 - 27%

Create DB MySQLmysql -uroot -pcreate database tutorial;create user tutorial@localhost identified by 'tutorial';grant all on tutorial.* to tutorial@localhost;

Page 46: Grails

46/173 - 27%

Config database connectiongrails-app/conf/DataSource.groovy:environments { development { dataSource { dbCreate = "create-drop" // one of 'create', 'create-drop','update' //loggingSql = true url = "jdbc:mysql://localhost/tutorial" driverClassName = "com.mysql.jdbc.Driver" dialect = org.hibernate.dialect.MySQL5InnoDBDialect username = "tutorial" password = "tutorial" } }}

Page 47: Grails

47/173 - 28%

Enable Maven remotegrails-app/conf/BuildConfig.groovy:mavenCentral()

Page 48: Grails

48/173 - 28%

Add dependencygrails-app/conf/BuildConfig.groovy:dependencies { runtime 'mysql:mysql-connector-java:5.1.5'}

Page 49: Grails

49/173 - 29%

Demo$ grails create-app tutorial // default package: tutorial

$ grails create-domain-class Person

Page 50: Grails

50/173 - 29%

grails-app/domain/tutorial/Person.groovy

package tutorial

class Person {

static constraints = { }}

Page 51: Grails

51/173 - 30%

grails-app/domain/tutorial/Person.groovy

package tutorial

class Person { String name Integer age Date lastVisit

static constraints = { }}

Page 52: Grails

52/173 - 31%

Example// grails consoleimport tutorial.*

// savedef p = new Person(name: 'Miguel', age: 31, lastVisit: new Date())p.save()

// readp = Person.get(1)println p.name

// updatep = Person.get(1)p.name = "Bob"p.save()

// deletep = Person.get(1)p.delete()

// listdef l = Person.list()l.each { println

Page 53: Grails

53/173 - 31%

Relationships- relationship - define how domain classes interact with each other - unless specified in both ends, exists only in the direction it is defined - cardinality one-to-one one-to-many many-to-many

- direction - unidirectional - bidirectional

Page 54: Grails

54/173 - 32%

Example domain classes $ grails create-domain-class Face $ grails create-domain-class Nose $ grails create-domain-class Book $ grails create-domain-class Author

Page 55: Grails

55/173 - 32%

one-to-one 1class Face { Nose nose // property}class Nose {}

// defined using _a property_ of the type of another // domain class// unidirectional (Face -> nose)// many-to-one (Many faces have can have a given nose)

Page 56: Grails

56/173 - 33%

mysql> describe face;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || nose_id | bigint(20) | NO | MUL | NULL | |+---------+------------+------+-----+---------+----------------+

mysql> describe nose;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | |+---------+------------+------+-----+---------+----------------+

Page 57: Grails

57/173 - 33%

one-to-one 2class Face { Nose nose static constraints = { nose unique: true }}class Nose {}

// unidirectional (Face -> Nose)// one-to-one (A nose can only be in one face)

Page 58: Grails

58/173 - 34%

mysql> describe face;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || nose_id | bigint(20) | NO | UNI | NULL | |+---------+------------+------+-----+---------+----------------+

mysql> describe nose;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | |+---------+------------+------+-----+---------+----------------+

Page 59: Grails

59/173 - 35%

one-to-one 3class Face { Nose nose}class Nose { static belongsTo = [ face:Face ]}

// bidirectional (Face <-> Nose)// many-to-one (Many faces have can have a given nose)

Page 60: Grails

60/173 - 35%

mysql> describe face;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || nose_id | bigint(20) | NO | MUL | NULL | |+---------+------------+------+-----+---------+----------------+

mysql> describe nose;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | |+---------+------------+------+-----+---------+----------------+

Page 61: Grails

61/173 - 36%

behavior- insert/updates cascade from Face to Nose // Nose is saved automatically new Face(nose: new Nose()).save()

- the inverse ins't true // Won't work. Face is transient new Nose(face: new Face()).save()

- deletes are cascaded too! def f = new Face(1) f.delete() // Face and Nose are deleted

- foreign key stored in _parent_ (Face) as nose_id

Page 62: Grails

62/173 - 36%

one-to-one 3class Face { static hasOne = [ nose:Nose ]}class Nose { Face face}

// bidirectional (Face <-> Nose)// one-to-one

Page 63: Grails

63/173 - 37%

mysql> describe face;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | |+---------+------------+------+-----+---------+----------------+

mysql> describe nose;+---------+------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || face_id | bigint(20) | NO | UNI | NULL | |+---------+------------+------+-----+---------+----------------+

Page 64: Grails

64/173 - 37%

one-to-manyclass Author { static hasMany = [ books:Book ]

String name}class Book { String title}

// unidirectional (Author -> Book)// one-to-many (An author can have many books)// mapped with a join table by default

Page 65: Grails

65/173 - 38%

mysql> describe author;+---------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+--------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || name | varchar(255) | NO | | NULL | |+---------+--------------+------+-----+---------+----------------+

mysql> describe book;+---------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+--------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || title | varchar(255) | NO | | NULL | |+---------+--------------+------+-----+---------+----------------+

mysql> describe author_book;+-----------------+------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-----------------+------------+------+-----+---------+-------+| author_books_id | bigint(20) | YES | MUL | NULL | || book_id | bigint(20) | YES | MUL | NULL | |+-----------------+------------+------+-----+---------+-------+

Page 66: Grails

66/173 - 39%

Exampleimport tutorial.*

def a = new Author(name: 'Tolkien')

a.addToBooks(title: 'The Hobbit')a.addToBooks(title: 'The Lord of the Rings')a.save()

a.books.each { println it.title}

Page 67: Grails

67/173 - 39%

mysql> select * from author;+----+---------+---------+| id | version | name |+----+---------+---------+| 1 | 0 | Tolkien |+----+---------+---------+

mysql> select * from book;+----+---------+-----------------------+| id | version | title |+----+---------+-----------------------+| 1 | 0 | The Lord of the Rings || 2 | 0 | The Hobbit |+----+---------+-----------------------+

mysql> select * from author_book;+-----------------+---------+| author_books_id | book_id |+-----------------+---------+| 1 | 1 || 1 | 2 |+-----------------+---------+

Page 68: Grails

68/173 - 40%

behavior// save/update are cascaded// deletes are not cascadedimport tutorial.*

def a = Author.get(1)

a.delete()

Author.list().size() // 0Book.list().size() // 2

Page 69: Grails

69/173 - 40%

mysql> select * from author;Empty set (0.00 sec)

mysql> select * from book;+----+---------+-----------------------+| id | version | title |+----+---------+-----------------------+| 1 | 0 | The Lord of the Rings || 2 | 0 | The Hobbit |+----+---------+-----------------------+2 rows in set (0.00 sec)

mysql> select * from author_book;Empty set (0.00 sec)

Page 70: Grails

70/173 - 41%

one-to-many 2class Author { static hasMany = [ books:Book ]

String name}class Book { static belongsTo = [ author:Author ]

String title}

// bidirectional (Author <-> Book)// one-to-many (An author can have many books, // a book has only an author)

Page 71: Grails

71/173 - 42%

mysql> describe author;+---------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+---------+--------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || name | varchar(255) | NO | | NULL | |+---------+--------------+------+-----+---------+----------------+

mysql> describe book;+-----------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+-----------+--------------+------+-----+---------+----------------+| id | bigint(20) | NO | PRI | NULL | auto_increment || version | bigint(20) | NO | | NULL | || author_id | bigint(20) | NO | MUL | NULL | || title | varchar(255) | NO | | NULL | |+-----------+--------------+------+-----+---------+----------------+

Page 72: Grails

72/173 - 42%

Exampleimport tutorial.*

def a = new Author(name: 'Tolkien')def b = new Book(title: 'The Hobbit')def b2 = new Book(title: 'The Lord of the Rings')

a.addToBooks(b)a.addToBooks(b2)a.save()

println(a.books.size()) // 2a.books.each { println it.title}// The Hobbit// The Lord of the Ringsprintln b.author.name // Tolkienprintln b2.author.name // Tolkien

Page 73: Grails

73/173 - 43%

mysql> select * from author;+----+---------+---------+| id | version | name |+----+---------+---------+| 1 | 0 | Tolkien |+----+---------+---------+

mysql> select * from book;+----+---------+-----------+-----------------------+| id | version | author_id | title |+----+---------+-----------+-----------------------+| 1 | 0 | 1 | The Hobbit || 2 | 0 | 1 | The Lord of the Rings |+----+---------+-----------+-----------------------+

Page 74: Grails

74/173 - 43%

many-to-many// hasMany on both sides// belongsTo on the owned (subordinated) side of the relationship// owning side takes responsibility for persisting relationship// owning side cascade saves// use a join table

class Book { static belongsTo = Author static hasMany = [ authors:Author ]

String title}class Author { static hasMany = [ books:Book ]

String name}

Page 75: Grails

75/173 - 44%

Exampleimport tutorial.*

new Author(name: 'Tolkien') .addToBooks(new Book(title: 'The Hobbit')) .addToBooks(new Book(title: 'The Lord of the Rings')) .save()

println Author.list().size() // 1 println Book.list().size() // 2

Page 76: Grails

76/173 - 44%

mysql> select * from author;+----+---------+---------+| id | version | name |+----+---------+---------+| 1 | 0 | Tolkien |+----+---------+---------+

mysql> select * from book;+----+---------+-----------------------+| id | version | title |+----+---------+-----------------------+| 1 | 0 | The Hobbit || 2 | 0 | The Lord of the Rings |+----+---------+-----------------------+

mysql> select * from author_books;+-----------+---------+| author_id | book_id |+-----------+---------+| 1 | 1 || 1 | 2 |+-----------+---------+

Page 77: Grails

77/173 - 45%

Exampleimport tutorial.*

new Book(title: 'The C programming language') .addToAuthors(name: 'Kernighan') .addToAuthors(name: 'Ritchie') .save()

println Author.list().size() // 1 println Book.list().size() // 3

Page 78: Grails

78/173 - 46%

mysql> select * from author;+----+---------+---------+| id | version | name |+----+---------+---------+| 1 | 0 | Tolkien |+----+---------+---------+

mysql> select * from book;+----+---------+----------------------------+| id | version | title |+----+---------+----------------------------+| 1 | 0 | The Hobbit || 2 | 0 | The Lord of the Rings || 3 | 0 | The C programming language |+----+---------+----------------------------+

mysql> select * from author_books;+-----------+---------+| author_id | book_id |+-----------+---------+| 1 | 1 || 1 | 2 |+-----------+---------+

Page 79: Grails

79/173 - 46%

Save/updatedef p = Person.get(1)p.name = "Bob"p.save()

// no SQL update guaranteed in that point// Hibernate batches SQL statements

def p = Person.get(1)p.name = "Bob"p.save(flush: true) // Forces a synchronization with DB

// Handling exceptions on savingdef p = Person.get(1)try { p.save(flush:true)}catch(Exception e) { // deal with exception}

Page 80: Grails

80/173 - 47%

Deletedef p = Person.get(1)p.delete()

// same as save/update

def p = Person.get(1)p.delete(flush:true) // Forces synchronization with DB

// Handling exceptionsdef p = Person.get(1)try { p.delete(flush:true) } catch(org.springframework.dao.DataIntegrityViolationException e) { // deal with exception}

Page 81: Grails

81/173 - 47%

Tipshttp://blog.springsource.com/2010/06/23/gorm-gotchas-part-1/

Page 82: Grails

82/173 - 48%

Eager and lazy fetching// by default lazy

class Location { String city}class Author { String name Location location}Author.list().each { author -> println author.location.city}

// If there are N = 4 authors// 1 query to fetch all authors// 1 query per author to get the location (because we're // printing the city)// Total = 4 + 1 = N + 1 querys

Page 83: Grails

83/173 - 48%

Eager loadingclass Author { String name Location location

static mapping = { location fetch: 'join' }}

Author.list(fetch: [location: 'join']).each { a -> println a.location.city}

Page 84: Grails

84/173 - 49%

Eager loading// Dynamic finders #Author.findAllByNameLike("John%", [ sort: 'name', order: 'asc', fetch: [location: 'join'] ]).each { a -> // ...}

// Criteria queriesdef authors = Author.withCriteria { like("name", "John%") join "location"}

Page 85: Grails

85/173 - 50%

Querying// listdef books = Book.list()def books = Book.list(offset:10, max:20)def books = Book.list(sort:"title", order: "desc")

// retrievaldef b = Book.get(23)def list = Book.getAll(1,3,24)

Page 86: Grails

86/173 - 50%

Dynamic findersclass Book { String title Date releaseDate Author author}class Author { String name}

def bb = Book.findByTitle("The Hobbit")b = Book.findByTitleLike("%Hobb%")b = Book.findByReleaseDateBetween(firstDate, secondDate)b = Book.findByReleaseDateGreaterThan(someDate)b = Book.findByTitleLikeOrReleaseDateLessThan("%obbi%", someDate)b = Book.findByReleaseDateIsNull()b = Book.findByReleaseDateIsNotNull()b = Book.findAllByTitleLike("The %", [max:3, offset:10, sort: "title", order: "desc"])

Page 87: Grails

87/173 - 51%

Criteriadef c = Book.createCriteria()def results = c { eq("releaseDate", someDate) or { like("title", "%programming%") like("title", "%Ring%") } maxResults(100) order("title", "desc")}

Page 88: Grails

88/173 - 51%

HQLdef list = Book.findAll( "from Book as b where b.title like 'Lord of the%'")def list = Book.findAll( "from Book as b where b.author = ?", [author])def list = Book.findAll( "from Book as b where b.author = :author", [author:someAuthor])

Page 89: Grails

89/173 - 52%

Controllers

Page 90: Grails

90/173 - 53%

Controllers- handle request- create response - can generate the response - delegate to a view- scope: request (a _new_ instance is created for each client request)- Class with Controller suffix- grails-app/controllers

Page 91: Grails

91/173 - 53%

Create controller$ grails create-controller book // default package: tutorial

Page 92: Grails

92/173 - 54%

grails-app/controllers/tutorial/BookController.groovy

package tutorial

class BookController { def index = {}}

// mapped to /book URI

Page 93: Grails

93/173 - 54%

Controller actions// properties that are assigned a block of code// each property maps to an URI// public by default

class BookController { def list = { // some statements }}

// maps to /book/list

Page 94: Grails

94/173 - 55%

Default action// 1. if only one action exists, the default URI // maps to it// 2. if an index action exists, it handle request // when no action specified // 3. explicit declaration

static defaultAction = "list"

Page 95: Grails

95/173 - 55%

ScopesservletContext - application widesession - session of a userrequest - current requestparams - _mutable_ map of incoming request paramsflash - only for this request and the subsequent (e.g. set a message before redirect)

Page 96: Grails

96/173 - 56%

Accessing scopesclass BookController { def find = { def findBy = params["findBy"] def userAgent = request.getHeader("User-Agent") def loggedUser = session["logged_user"] // session.logged_user }

def delete = { def b = Book.get( params.id ) if(!b) { flash.message = "User not found for id ${params.id}" redirect(action:list) } } }

Page 97: Grails

97/173 - 57%

Models and viewsModel

- Map of objects that the view uses to render the response- Keys of map translate to variables in the view

Page 98: Grails

98/173 - 57%

Explicit return of modeldef show = { [ book: Book.get(params.id) ]}

Page 99: Grails

99/173 - 58%

Implicit return of modeclass BookController { List books List authors

def list = { books = Book.list() authors = Author.list() }}

Page 100: Grails

100/173 - 58%

Implicit viewclass BookController { def show = { [ book:Book.get(params.id) ] }}

// grails look for view at // grails-app/views/book/show.gsp (show.jsp first)

Page 101: Grails

101/173 - 59%

Explicit viewdef show = { def map = [ book: Book.get(1) ] render(view: "display", model: map)}

// grails will try grails-app/views/book/display.gsp

def show = { def map = [ book: Book.get(1) ] render(view: "/shared/display", model: map)}

// grails will try grails-app/views/shared/display.gsp

Page 102: Grails

102/173 - 59%

Direct rendering of the responseclass BookController { def greet = { render "hello!" }}

Page 103: Grails

103/173 - 60%

Redirectclass BookController { def greet = { render "hello!" }

def redirect = { redirect(action: greet) }}

Page 104: Grails

104/173 - 61%

Redirect expects- other closure on the same class redirect(action:list)- controller and action redirect(controller: 'author', action: 'list')- URI redirect(uri: "/help.html")- URL redirect(url: 'http://yahoo.com')

Page 105: Grails

105/173 - 61%

Data binding// implicit constructordef save = { def b = new Book(params) b.save()}

// explicit bindingdef save = { def b = Book.get(params.id) b.properties = params // sets every parameter // as a property in the object b.someParam = params.foo // only some parameters are set b.otherParam = params.bar b.save()}

Page 106: Grails

106/173 - 62%

JSON and XML responses

Page 107: Grails

107/173 - 62%

XMLdef listXML = { def results = Book.list() render(contentType: 'text/xml') { books { for(b in results) { book(title: b.title) } } }}

Page 108: Grails

108/173 - 63%

<books> <book title="title one"/> <book title="title two"/></books>

Page 109: Grails

109/173 - 64%

JSONdef listJSON = { def results = Book.list() render(contentType: 'text/json') { books = array { for(b in results) { book(title: b.title) } } }}

Page 110: Grails

110/173 - 64%

"books":[ {"title": "title one"}, {"title": "title two"}]

Page 111: Grails

111/173 - 65%

Automatic XML and JSONmarshaling

Page 112: Grails

112/173 - 65%

XMLimport grails.converters.*

def list = { render Book.list() as XML}

def list2 = { render Book.list().encodeAsXML() // using codecs}

Page 113: Grails

113/173 - 66%

JSONrender Book.list() as JSON

render Book.list().encodeAsJSON()

Page 114: Grails

114/173 - 66%

Type convertersdef total = params.int('total')def checked = params.boolean('checked')

// null safe// safe from parsing errors

Page 115: Grails

115/173 - 67%

Groovy Server Pages

Page 116: Grails

116/173 - 68%

GSPsimilar to ASP, JSP

more flexible

live in grails-app/views

rendered automatically (by convention) or with the render method

Mark-up (HTML) and GSP tags

embedded logic possible but discouraged

uses the model passed by the controller action

Page 117: Grails

117/173 - 68%

Controller// returns a model with key called book.def show = { [ book: Book.get(1) ]}

Page 118: Grails

118/173 - 69%

View<%-- the key named book from the model is referenced by name in the gsp --%><%= book.title %>

Page 119: Grails

119/173 - 69%

GSP Basics<% %> blocks to embed groovy code (discouraged)

<html> <body> <% out << "Hello world!" %> <%= "this is equivalent" %> </body></html>

Page 120: Grails

120/173 - 70%

Variables in GSPs<% now = new Date() %> ...<p>Time: <%= now %></p>

<!-- predefined: - application - applicationContext - flash - grailsApplication - out - params - request - response - session - webRequest-->

Page 121: Grails

121/173 - 70%

GSP Expressions${expression}

<html> <body> Hello ${params.name} Time is: ${new Date()} 2 + 2 is: ${ 2 + 2 } but also: ${ /* any valid groovy statement */ } </body></html>

Page 122: Grails

122/173 - 71%

GSP built-in tags- start with g: prefix- no need to import tag libraries

<g:example param="a string param" otherParam="${new Date()}" aMap="[aString:'a string', aDate: new Date()]"> Hello world</g:example>

Page 123: Grails

123/173 - 72%

<g:set/><g:set var="myVar" value="${new Date()}"/>

<g:set var="myText"> some text with expressions ${myVar}</g:set><p>${myText}</p>

Page 124: Grails

124/173 - 72%

Logic/Iteration<g:if test="${1 > 3}"> some html</g:if><g:else> something else</g:else>

<g:each in="${tutorial.Book.list()}" var="b"> title: ${b.title}</g:each>

<g:set var="n" value="${0}"/><g:while test="${n<5}"> ${n++} </g:while>

Page 125: Grails

125/173 - 73%

Links and resources<g:link action="show" id="2">Item 2</g:link><g:link controller="user" action="list">Users</g:link>

Page 126: Grails

126/173 - 73%

Forms and fields<g:form name="myForm" url="[controller:'book', action:'submit']"> Text: <g:textField name="text"/> <g:submitButton name="button" value="Submit form"/></g:form>

fields- textField- checkbox- radio- hiddenField- select- submitButton

Page 127: Grails

127/173 - 74%

Tags as method callsIn views

Static Resource: ${createLinkTo(dir:"images", file:"logo.jpg")}

<img src="/image/intro/${createLinkTo(dir:'images', file:'logo.jpg')}" />

In controllers and taglibs

def imageLocation = createLinkTo(dir:"images", file:"logo.jpg") def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg")

Page 128: Grails

128/173 - 74%

Templates- maintainable and reusable chunks of views- name starts with a underscore: _myTemplate.gsp

grails-app/views/book/_info.gsp

<div class="book" id="${book.id}"> Title: ${book.title}</div>

from other view

<g:render template="info" var="book" collection="${tutorial.Book.list()}"/>

from controller

def useTemplate = { def b = Book.get(1) render(template: "info", model: [ book:b ])}

Page 129: Grails

129/173 - 75%

GSP debuggingGSPs

- Add "showSource=true" to the url. Only in development mode

Templates Add "debugTemplates" to the url. Only in development mode

Page 130: Grails

130/173 - 76%

Tag libraries

groovy class that ends with TagLib

live in grails-app/taglib

$ grails create-tag-lib utils

implicit out variable. Refers to the output Writer

Page 131: Grails

131/173 - 76%

Taglibs 1Taglib

class UtilsTagLib { def copyright = { attrs, body -> out << "&copy; Copyright 2010" }}

view<div><g:copyright/></div>

Page 132: Grails

132/173 - 77%

Example 2Taglib

import java.text.*

def dateFormat = { attrs, body -> def fmt = new SimpleDateFormat(attrs.format) out << fmt.format(attrs.date)}

view<g:dateFormat format="dd-MM-yyyy" date="${new Date()}"/>

Page 133: Grails

133/173 - 77%

Example 3Taglib

def formatBook = { attrs, body -> out << render(template: "info", model: [ book:attrs.book ])}

view<div><g:formatBook book="${tutorial.Book.get(1)}"/></div>

Page 134: Grails

134/173 - 78%

AJAX

Page 135: Grails

135/173 - 79%

Javascript<head> <g:javascript library="prototype"/> <g:javascript library="scriptaculous"/></head>

Page 136: Grails

136/173 - 79%

Remote linkview

<g:remoteLink action="delete" id="1"> Delete Book</g:remoteLink>

asynchronous request to the delete action of the current controller with an id parameter with value of 1

controller

class AjaxController { def index = {}

def delete = { def b = Book.get(params.id) b.delete() render "Book ${b.title} was deleted" }}

Page 137: Grails

137/173 - 80%

Independent updates for failure and successView

Success: <div id="success"></div>Failure: <div id="error"></div><g:remoteLink action="success" update="[success:'success', failure:'error']"> Success ajax request</g:remoteLink><br/><g:remoteLink action="failure" update="[success:'success', failure:'error']"> Failure ajax request</g:remoteLink>

Page 138: Grails

138/173 - 80%

Independent updates for failure and successController

def success = { render status:200, text:"request OK"}

def failure = { render status:503, text:"request failed"}

Page 139: Grails

139/173 - 81%

Ajax form submissionview

<g:formRemote url="[controller:'ajax', action:'ajaxAdd']" name="ajaxForm" update="[success:'addSuccess', failure:'addError']"> Book title: <input type="text" name="title"/> <input type="submit" value="Add Book!" /></g:formRemote > <div id="addSuccess"></div><div id="addError"></div>

controller

def ajaxAdd = { def b = new Book(params) b.save() render "Book '${b.title}' created"}

Page 140: Grails

140/173 - 81%

Ajax returning contentview

<g:remoteLink action="ajaxContent" update="book"> Update Content</g:remoteLink><div id="book"><!--existing book mark-up --></div>

controller

def ajaxContent = { def b = Book.get(2) render "Book: <strong>${b.title}</strong> found at ${new Date()}!"}

Page 141: Grails

141/173 - 82%

Ajax returning JSONview

<g:javascript> function updateBook(e) { // evaluate the JSON var book = eval("("+e.responseText+")") $("book_title").innerHTML = book.title }</g:javascript><g:remoteLink action="ajaxData" update="foo" onSuccess="updateBook(e)"> Update Book with JSON</g:remoteLink><div id="book"> <div id="book_title">The Hobbit</div></div>

controller

import grails.converters.*

def ajaxData = { def b = new Book(title: 'new book title').save() render b as JSON

Page 142: Grails

142/173 - 83%

Version Control

Page 143: Grails

143/173 - 83%

SubversionCreate repository

$ svnadmin create /home/miguel/repo$ svn list file:///home/miguel/repo

Create empty dir in repository (remote create)$ svn mkdir file:///home/miguel/repo/GrailsProject$ svn mkdir file:///home/miguel/repo/GrailsProject/branches$ svn mkdir file:///home/miguel/repo/GrailsProject/tags$ svn mkdir file:///home/miguel/repo/GrailsProject/trunk

Create grails project (local)$ grails create-app GrailsProject

Add grails code to working copy, inline$ cd GrailsProject$ svn co file:///home/miguel/repo/GrailsProject/trunk .$ svn add .classpath .settings .project *$ svn propset svn:ignore "WEB-INF" web-app/$ svn propset svn:ignore "target" .$ svn rm --force web-app/WEB-INF$ svn commit -m "First commit of GrailsProject"

Checkout$ cd ..$ svn co file:///home/miguel/repo/GrailsProject/trunk OtherUserGrailsProject$ grails upgrade

Page 144: Grails

144/173 - 84%

GitCreate repository

$ grails create-app GrailsProject$ cd GrailsProject$ git init

.gitignore:web-app/WEB-INF/target/

$ git add .$ git commit -m "First commit on GrailsProject"

Clone$ cd ..$ git clone GrailsProject OtherUserGrailsProject$ grails upgrade

Page 145: Grails

145/173 - 84%

Thanks

Miguel Cobá

[email protected]

Groovy Intro by Miguel Cobá is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

Page 146: Grails

146/173 - 85%

Grails Advanced

Page 147: Grails

147/173 - 85%

Advanced

Page 148: Grails

148/173 - 86%

Excel

Page 149: Grails

149/173 - 87%

Options

Apache POI

Grails export plugin

Grails excel-import plugin

Page 150: Grails

150/173 - 87%

Excel with Apache POISource

http://www.michaelvanvliet.nl/node/4

Get POI- http://www.apache.org/dyn/closer.cgi/poi/release/- unzip package- put *.jar in tutorial/lib- grails create-controller excelPOI

Page 151: Grails

151/173 - 88%

package tutorial

import org.apache.poi.hssf.model.Workbook;import org.apache.poi.hssf.usermodel.HSSFWorkbook;import org.apache.poi.hslf.model.Sheetimport org.apache.poi.hssf.record.formula.functions.Row

class ExcelPOIController {

def index = {

def nameOfWorkbook = 'workbook.xls' def locationToStoreWorkbook = '/tmp/' // Or C:\temp def wb = new HSSFWorkbook() def sheet = wb.createSheet("Demo") def row = sheet.createRow((short)0) row.createCell(1).setCellValue('Column 1') row.createCell(2).setCellValue('Column 2') row = sheet.createRow((short)1); row.createCell(0).setCellValue('Row 1') row.createCell(1).setCellValue('Hello') row.createCell(2).setCellValue('World') def fileWorkbook = new FileOutputStream(locationToStoreWorkbook + nameOfWorkbook) wb.write(fileWorkbook) fileWorkbook.close() render('Workbook was saved to: ' + locationToStoreWorkbook + nameOfWorkbook) }}

Page 152: Grails

152/173 - 88%

Web Services

Page 153: Grails

153/173 - 89%

Options

REST: Grails only

SOAP: Grails xfire plugin

SOAP: Grails springws plugin

Page 154: Grails

154/173 - 90%

SOAP with XFire pluginSource

http://groovy.codehaus.org/Using+the+Grails+XFire+plugin+and+GroovyWS

Install XFire plugin

$ grails install-plugin xfire$ grails create-service test

Page 155: Grails

155/173 - 90%

Test Servicepackage tutorial

class TestService {

boolean transactional = false

static expose=['xfire']

static conversions = [ 'AUD': [ 'USD': 100.00D, 'GBP': 44.44D ], 'USD': [ 'AUD': 1.00D, 'GBP': 88.88D ], 'GBP': [ 'AUD': 22.22D, 'USD': 33.33D ] ]

Double convert(String from, String to, Double amount) { conversions[from][to] * amount }}

Page 156: Grails

156/173 - 91%

WSDLhttp://localhost:8080/tutorial/services/test?wsdl

Page 157: Grails

157/173 - 91%

Web Service consumer

client.groovy

import groovyx.net.ws.WSClient

@Grab(group='org.codehaus.groovy.modules', module='groovyws', version='0.5.2')def getProxy(wsdl, classLoader) { new WSClient(wsdl, classLoader)}proxy = getProxy("http://localhost:8080/tutorial/services/test?wsdl", this.class.classLoader)proxy.initialize()

result = proxy.convert("AUD", "USD", 10.0)println "10 AUD are worth ${result} USD"

Page 158: Grails

158/173 - 92%

Consuming the service$ groovy client.groovy10 AUD are worth 1000.0 USD

Page 159: Grails

159/173 - 92%

Jasper

Page 160: Grails

160/173 - 93%

Options

Grails jasper plugin

Grails dynamic-jasper plugin

Page 161: Grails

161/173 - 94%

Reports with dynamic-jasper pluginSource

http://www.grails.org/plugin/dynamic-jasper

Install dynamic-jasper plugin$ grails install-plugin dynamic-jasper$ grails create-domain-class User$ grails create-controller user

Page 162: Grails

162/173 - 94%

User controller class

package tutorial

class UserController {

def scaffold = true}

Page 163: Grails

163/173 - 95%

User domain class

package tutorial

class User { String username String name String lastName Boolean active

static reportable = [:]

static constraints = { }}

Page 164: Grails

164/173 - 95%

View a reporthttp://localhost:8080/tutorial/djReport/index?entity=user

Page 165: Grails

165/173 - 96%

jQuery

Page 166: Grails

166/173 - 96%

Options

Thousands of jQuery libraries

jQuery UI

jQuery Tools

Page 167: Grails

167/173 - 97%

jQuery ToolsSource

http://flowplayer.org/tools/download/index.html

Install jQuery Tools

<script src="http://cdn.jquerytools.org/1.2.5/full/jquery.tools.min.js"></script>

Page 168: Grails

168/173 - 98%

grails-app/view/book/jquery.gsp

<html> <head> <script src="http://cdn.jquerytools.org/1.2.5/full/jquery.tools.min.js"> </script> </head> <body> </body></html>

Page 169: Grails

169/173 - 98%

Book Controllerclass BookController { def jquery = {}}

Page 170: Grails

170/173 - 99%

Date Input

<!-- dateinput styling --><link rel="stylesheet" type="text/css" href="http://static.flowplayer.org/tools/ demos/dateinput/css/skin1.css"/>

<!-- HTML5 date input --><input type="date" />

<!-- make it happen --><script> $(":date").dateinput();</script>

Page 171: Grails

171/173 - 99%

jQuery Toolshttp://jqueryui.com/http://jqueryui.com/demo

- Accordion- Autocomplete- Button- Dialog- Datepicker- Progressbar- Tabs- Slider

Page 172: Grails

172/173 - 100%

SecuritySpring Security Core Plugin

http://www.grails.org/plugin/spring-security-corehttp://burtbeckwith.github.com/grails-spring-security-core/

- Spring Security- DB- LDAP- OpenID- CAS

Page 173: Grails

173/173 - 100%

Thanks

Miguel Cobá

[email protected]

Groovy Intro by Miguel Cobá is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.


Recommended