Colin Harrington
GR8Conf US 2016
Grails is a powerful web framework, for the Java platform aimed at multiplying developers’ productivity thanks to a Convention-over-Configuration, sensible defaults and opinionated APIs. It integrates smoothly with the JVM, allowing you to be immediately productive whilst providing powerful features, including integrated ORM, Domain-Specific Languages, runtime and compile-time meta-programming and Asynchronous programming.
$ curl -s "https://get.sdkman.io" | bash
> $ tree -L 1
.
├── build
├── build.gradle
├── gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── grails-app
└── src
grails create-app myapp --profile=rest-api
> $ grails list-profiles
| Available Profiles
--------------------
* angular - A profile for creating applications using AngularJS
* rest-api - Profile for REST API applications
* base - The base profile extended by other profiles
* plugin - Profile for plugins designed to work across all profiles
* web - Profile for Web applications
* web-plugin - Profile for Plugins designed for Web applications
> $ ./gradlew tasks
:tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Application tasks
-----------------
bootRun - Run the project with support for auto-detecting main class and reloading static resources
Build tasks
-----------
assemble - Assembles the outputs of this project.
bootRepackage - Repackage existing JAR and WAR archives so that they can be executed from the command line using 'java -jar'
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
integrationTestClasses - Assembles integration test classes.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.
war - Generates a war archive with all the compiled classes, the web-app content and the libraries.
Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]
Documentation tasks
-------------------
groovydoc - Generates Groovydoc API documentation for the main source code.
javadoc - Generates Javadoc API documentation for the main source code.
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'bookstore'.
components - Displays the components produced by root project 'bookstore'. [incubating]
dependencies - Displays all dependencies declared in root project 'bookstore'.
dependencyInsight - Displays the insight into a specific dependency in root project 'bookstore'.
help - Displays a help message.
model - Displays the configuration model of root project 'bookstore'. [incubating]
projects - Displays the sub-projects of root project 'bookstore'.
properties - Displays the properties of root project 'bookstore'.
tasks - Displays the tasks runnable from root project 'bookstore'.
IDE tasks
---------
cleanEclipse - Cleans all Eclipse files.
cleanIdea - Cleans IDEA project files (IML, IPR)
eclipse - Generates all Eclipse files.
idea - Generates IDEA project files (IML, IPR, IWS)
Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.
Other tasks
-----------
assetClean
assetPluginPackage
cleanIdeaWorkspace
console
dependencyManagement
mergeTestReports
pathingJar
pathingJarCommand
schemaExport
shell
urlMappingsReport
> $ tree -L 2
.
├── build.gradle
├── gradle
│ └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── grails-app
│ ├── assets
│ ├── conf
│ ├── controllers
│ ├── domain
│ ├── i18n
│ ├── init
│ ├── services
│ ├── taglib
│ ├── utils
│ └── views
└── src
├── integration-test
├── main
└── test
class BookController {
def list() {
// do controller logic
// create model
return model
}
}
class BookController {
def find() {
def findBy = params["findBy"]
def appContext = request["foo"]
def loggedUser = session["logged_user"]
}
}
Scopes
class BookController extends RestfulController {
static responseFormats = ['json', 'xml']
BookController() {
super(Book)
}
}
HTTP Method | URI | Controller Action |
---|---|---|
GET | /books | index |
GET | /books/create | create |
POST | /books | save |
GET | /books/${id} | show |
GET | /books/${id}/edit | edit |
PUT | /books/${id} | update |
DELETE | /books/${id} | delete |
GORM is the data access toolkit used by Grails and provides a rich set of APIs for accessing relational and non-relational data including implementations for Hibernate (SQL), MongoDB, Neo4j, Cassandra, Redis and an in-memory ConcurrentHashMap for testing.
Book.findByTitle("The Hobbit")
"/product"(controller: "product", action: "list")
"/product"(controller: "product")
"/hello"(uri: "/hello.dispatch")
"/books"(resources:'book')
"/"(view: "/index")
"403" (controller: "errors" action:"forbidden")
<g:link controller="book" action="index">My Link</g:link>
import static grails.async.Promises.*
...
def p1 = task { 2 * 2 }
def p2 = task { 4 * 4 }
def p3 = task { 8 * 8 }
assert [4,16,64] == waitAll(p1, p2, p3)
on("myEvent") {
println "Event fired!"
}
notify "myEvent", "myData"
grails test-app
@TestFor(BookController)
@Mock([Book, Author, BookService])
import grails.test.mixin.TestFor
import spock.lang.Specification
@TestFor(SimpleController)
class SimpleControllerSpec extends Specification {
void 'test display'() {
when:
controller.display()
then:
response.text == 'contents of the template'
}
}
grails.test.mixin.TestFor
grails.test.mixin.Mock
grails.test.mixin.TestMixin
grails.test.mixin.support.GrailsUnitTestMixin
grails.test.mixin.domain.DomainClassUnitTestMixin
grails.test.mixin.services.ServiceUnitTestMixin
grails.test.mixin.web.ControllerUnitTestMixin
grails.test.mixin.web.FiltersUnitTestMixin
grails.test.mixin.web.GroovyPageUnitTestMixin
grails.test.mixin.web.UrlMappingsUnitTestMixin
grails.test.mixin.hibernate.HibernateTestMixin
import geb.spock.GebReportingSpec
import spock.lang.*
import pages.*
@Stepwise
class PersonCRUDSpec extends GebReportingSpec {
def "there are no people"() {
when:
to ListPage
then:
personRows.size() == 0
}
def "add a person"() {
when:
newPersonButton.click()
then:
at CreatePage
}
def "enter the details"() {
when:
$("#visible").click()
firstName = "Luke"
lastName = "Daley"
createButton.click()
then:
at ShowPage
}
}
$ grails create-interceptor MyInterceptor
...
class MyInterceptor {
boolean before() { true }
boolean after() { true }
void afterView() {
// no-op
}
}
class LoggingInterceptor {
LoggingInterceptor() {
match(controller:"book", action:"show") // using strings
match(controller: ~/(author|publisher)/) // using regex
}
boolean before() {
…
}
}
Controller:
render(view: "index")
renders index.gsp in the grails-app/views directory
compile 'org.grails.plugins:views-json:1.0.0'
json.person {
name "bob"
}
model {
Person person
}
json {
name person.name
age person.age
}
grails.org
User Guide
(RTFM: Read The Fantastic Manual)
Stack Overflow
Slack
Github!
Please visit our table for giveaways
and to meet the team!
ociweb.com/grails
info@ociweb.com