def quadrado = {it**2} // parâmetro implícito
println quadrado(5) // retorna 25
def soma = { a, b -> a+b }
println soma(4, 10) // retorna 14
Regra: tudo que não for nulo, vazio, zero (ou explicitamente FALSE, óbvio), será avaliado para TRUE
// True | // False
def a = true | def b = false
def numbers_full = [1,2,3] | def numbers = []
def s = 'abc' | def s = ''
def n = 1 | def n = 0
def obj = new Object() | def obj = null
Forma reduzida do operador ternário
a ?: b
equivalente à
a != null ? a : b
// Múltiplas linhas
"""
Este é um
texto muito
longo
"""
// Interpolação de variáveis e expressões
"1 + 1 = ${1+1}"
// Expressões regulares
"12345 world!".replaceAll(/\d+/, 'Hello')
// Estruturas nativas para manipulação de coleções
def list = [5, 6, 7, 8]
def range = 5..8
def map = [cidade:"Porto Alegre",
estado:"RS",
pais:"Brasil",
cep:90000001]
map.get("cidade") // "Porto Alegre"
trait FlyingAbility { String fly() { "I'm flying!" } }
trait WalkAbility { String walk() { "I'm walking!" } }
class Thing implements FlyingAbility { }
def a = new Thing()
assert a.fly() == "I'm flying!"
class Thing { }
def b = new Thing() as FlyingAbility
assert b.fly() == "I'm flying!"
def c = new Thing()
def d = c.withTraits FlyingAbility, WalkAbility
d.fly(); d.walk()
@Mixins estão deprecated a partir da versão 2.3, em favor dos Traits.
Fonte: chrisdail.com
// Cria a aplicação
~ grails create-app MinhaApp
// Executa a aplicação
~ cd MinhaApp
~ grails run-app MinhaApp
//// Convention-over-configuration
|- grails-app
|--- assets // Conteúdo gerenciado pelo Asset Plugin
|--- conf // Arquivos de configuração
|--- controllers
|--- domain
|--- i18n // Internacionalização
|--- services
|--- taglib
|--- utils
|--- views // Telas, templates
|- src
|--- groovy
|--- java
|- test // Testes unitários
|- web-app // Imagens, CSS, JS, HTML ...
// Scaffolding dinâmico, gera as GUIs para CRUD
// em tempo de execução:
// index, show, edit, delete, create, save, update
class BookController { | class AnotherController {
static scaffold = true | static scaffold = Book
} | }
// Scaffolding estático: cria fisicamente os arquivos das
// interfaces CRUD, de acordo com o template padrão
~ grails generate-views [domain-class-name]
~ grails generate-all [domain-class-name]
// Customizar os templates:
~ grails install-templates
// Exemplo de uso:
${flash.message}
* na versão 2.4 substituiu o plugin Resources
// Gera um controller para CRUD (template pré-definido)
~ grails generate-controller [domain-class-name]
// Ou pode ser criado diretamente
package org.sample
class SampleController {
def world() {
render "Hello World!"
}
}
// Por convenção, o mapeamento para as
// seguintes URL e view será:
Requisição -> grails-app/sample/world
Retorno -> grails-app/views/sample/world.gsp
class SomeController {
def find() {
def findBy = params["findBy"]
def appContext = request["foo"]
def loggedUser = session["logged_user"]
}
}
/controller/action/id
grails-app/conf/UrlMappings.groovy
//
// Mapeamento padrão de uma aplicação recém criada
//
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
grails-app/services/
Dê preferência por serviços para orquestrar interações entre recursos da aplicação (domain objects) ou externas (IO).
class SomeService {
void someMethod(param) {
// do something
}
}
/******************************/
class FirstController {
def someService
def someAction() {
try {
someService.someMethod(params.id)
} catch(e) {
log.error e
}
}
}
grails-app/conf/DataSource.groovy
/// DataSource.groovy
dataSource {
pooled = true
}
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = false
cache.region.factory_class=
'org.hibernate.cache.ehcache.EhCacheRegionFactory'
}
environments {
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
driverClassName = "org.h2.Driver"
...
}
}
production {
dataSource {
dbCreate = "update"
url = "jdbc:postgresql://192.168.0.1/SOME_DATABASE"
driverClassName = "org.postgresql.Driver"
...
}
}
}
class Book {
String title
static belongsTo = Author
static hasMany = [authors : Author]
static constraints = {name nullable: false}
}
class Author {
String name
static hasMany = [books : Book]
}
def a = new Author(name:"Author One")
a.addToBooks(new Book(title:"Book One"))
a.addToBooks(new Book(title:"Book Two"))
a.save()
// Acesso direto por id
def book = Book.get(1)
// Dynamic Finders
def book = Book.findByTitle("Book One")
def books = Book.findByTitleIlike("Book%")
def books = Book.findByAuthor(Author.get(1))
// Where Queries
def query = Book.where{title like "Book%"}
def aQuery = query.where{authors {name == "Author One"}}
Book b = aQuery.find()
// Criteria Queries
def c = Book.createCriteria()
def results = c.list(max:4, sort:"title") {
and {
ilike("title", "Book%")
authors{
eq("name", "Author One")
}
}
maxResults(10)
order("holderLastName", "desc")
}
.-.
| |
| | .-. > Por hoje é só, pessoal!
| |-._| |
|_| | | |
/ )|_|_|-|
| | `-^-^ |
| || |
\ ' /
| |
| |