Søren Berg Glasius @sbglasius
Agenda
Agenda (cont'd)
Groovy adj. (or, less commonly, "Groovie" or "Groovey") It is roughly synonymous with words such as "cool", "excellent", "fashionable" or "amazing," depending on context.
Groovy adj. (or, less commonly, "Groovie" or "Groovey") It is roughly synonymous with words such as "cool" , "excellent" , "fashionable" or "amazing" , depending on context.
Groovy is a powerful, optionally typed and dynamic language, with static typing and static compilation capabilities, for the Java platform aimed at multiplying developers’ productivity thanks to a concise, familiar and easy to learn syntax.
It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.
Groovy is a powerful, optionally typed and dynamic language, with static typing and static compilation capabilities, for the Java platform aimed at multiplying developers’ productivity thanks to a concise, familiar and easy to learn syntax.
It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.
Groovy is a powerful, optionally typed and dynamic language, with static typing and static compilation capabilities, for the Java platform aimed at multiplying developers’ productivity thanks to a concise, familiar and easy to learn syntax.
It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.
Note: I will not cover Java 8
java
assert true;
assert !false;
// Object
Object unassignedObject = null;
Object assignedObject = new Object();
assert unassignedObject == null;
assert assignedObject != null;
// Simple type value
int integer = 10;
assert integer != 0;
assert integer == 10;
groovy
assert true
assert !false
assert new Object()
assert !null
assert 10
assert !0
assert 1.0
assert !0.0
assert 'String with content'
assert ''
assert ['list', 'with', 'content']
assert ![] // Empty list
assert [map: 'With', content: '!']
assert ![:]
groovy
// call a closure:
def closure1 = { int it -> 2 * it } // implicit return
assert closure1(21) == 42
assert closure1.call(21) == 42
groovy
// Access context
def answer = 42
def other2 = { return answer } // return value from context
assert other2() == 42
groovy
def closure3 = { a, b, c -> a * b * c }
assert closure3(2, 3, 7) == 42
groovy
// 'it' is implicit defined
assert [1, 2, 3, 4, 5].collect { it * it } == [1, 4, 9, 16, 25]
SimpleGreeter.java
public class SimpleGreeter implements Greeter {
private String greeting;
@Override
public void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names);
System.out.println(helloMessage);
}
public String getGreeting() {return greeting;}
public void setGreeting(String greeting) {this.greeting = greeting;}
private String prepareHelloMessage(String... names) {
String delimiter = "";
StringBuilder stringBuilder = new StringBuilder();
for(String name: names) {
stringBuilder.append(delimiter).append(name);
delimiter = ", ";
}
return this.greeting + " " + stringBuilder.toString() + "!";
}
public static void main(String[] args) {
final Greeter greeter = new SimpleGreeter();
((SimpleGreeter)greeter).setGreeting("Hello");
greeter.sayHello("Peter","Paul","Marry");
}
}
28 loc / 704 chars
SimpleGreeter.groovy
public class SimpleGreeter implements Greeter {
private String greeting;
@Override
public void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names);
System.out.println(helloMessage);
}
public String getGreeting() {return greeting;}
public void setGreeting(String greeting) {this.greeting = greeting;}
private String prepareHelloMessage(String... names) {
String delimiter = "";
StringBuilder stringBuilder = new StringBuilder();
for(String name: names) {
stringBuilder.append(delimiter).append(name);
delimiter = ", ";
}
return this.greeting + " " + stringBuilder.toString() + "!";
}
public static void main(String[] args) {
final Greeter greeter = new SimpleGreeter();
((SimpleGreeter)greeter).setGreeting("Hello");
greeter.sayHello("Peter","Paul","Marry");
}
}
28 loc / 704 chars
Java code is Groovy code
...almost always
SimpleGreeter.groovy
public class SimpleGreeter implements Greeter {
private String greeting
@Override
public void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names)
System.out.println(helloMessage)
}
public String getGreeting() {return greeting}
public void setGreeting(String greeting) {this.greeting = greeting}
private String prepareHelloMessage(String... names) {
String delimiter = ""
StringBuilder stringBuilder = new StringBuilder()
for(String name: names) {
stringBuilder.append(delimiter).append(name)
delimiter = ", "
}
return this.greeting + " " + stringBuilder.toString() + "!"
}
public static void main(String[] args) {
final Greeter greeter = new SimpleGreeter()
((SimpleGreeter)greeter).setGreeting("Hello")
greeter.sayHello("Peter","Paul","Marry")
}
}
28 loc / 691 chars
Semicolons are optional
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
private String greeting
@Override
void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names)
System.out.println(helloMessage)
}
String getGreeting() { return greeting }
void setGreeting(String greeting) { this.greeting = greeting }
private String prepareHelloMessage(String... names) {
String delimiter = ""
StringBuilder stringBuilder = new StringBuilder()
for (String name : names) {
stringBuilder.append(delimiter).append(name)
delimiter = ", "
}
return this.greeting + " " + stringBuilder.toString() + "!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter()
((SimpleGreeter) greeter).setGreeting("Hello")
greeter.sayHello("Peter", "Paul", "Marry")
}
}
28 loc / 661 chars
All classes and methods are public by default
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names)
System.out.println(helloMessage)
}
private String prepareHelloMessage(String... names) {
String delimiter = ""
StringBuilder stringBuilder = new StringBuilder()
for (String name : names) {
stringBuilder.append(delimiter).append(name)
delimiter = ", "
}
return greeting + " " + stringBuilder.toString() + "!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter()
greeter.greeting = "Hello"
greeter.sayHello("Peter", "Paul", "Marry")
}
}
25 loc / 538 chars
Variables not declared private are properties
Property accessor instead of setter and getter
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names)
System.out.println(helloMessage)
}
private String prepareHelloMessage(String... names) {
def joinedNames = names.join(', ')
return greeting + " " + joinedNames + "!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter()
greeter.greeting = "Hello"
greeter.sayHello("Peter", "Paul", "Marry")
}
}
20 loc / 412 chars
Groovy has a nice Collections API
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names)
println(helloMessage)
}
private String prepareHelloMessage(String... names) {
return "$greeting ${names.join(', ')}!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter()
greeter.greeting = "Hello"
greeter.sayHello("Peter", "Paul", "Marry")
}
}
19 loc / 374 chars
Groovy has GStrings (interpolated strings)
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
String helloMessage = prepareHelloMessage(names)
println(helloMessage)
}
private String prepareHelloMessage(String... names) {
"$greeting ${names.join(', ')}!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter()
greeter.greeting = "Hello"
greeter.sayHello("Peter", "Paul", "Marry")
}
}
19 loc / 368 chars
Returns are optional
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
println(prepareHelloMessage(names))
}
private String prepareHelloMessage(String... names) {
"$greeting ${names.join(', ')}!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter(greeting: "Hello")
greeter.sayHello("Peter", "Paul", "Marry")
}
}
17 loc / 329 chars
Named arguments in constructor
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
println prepareHelloMessage(names)
}
private String prepareHelloMessage(String... names) {
"$greeting ${names.join ', '}!"
}
static void main(String[] args) {
final Greeter greeter = new SimpleGreeter(greeting: "Hello")
greeter.sayHello "Peter", "Paul", "Marry"
}
}
17 loc / 323 chars
Parentheses are optional
SimpleGreeter.groovy
class SimpleGreeter implements Greeter {
String greeting
@Override
void sayHello(String... names) {
println prepareHelloMessage(names)
}
private String prepareHelloMessage(String... names) {
"$greeting ${names.join ', '}!"
}
}
12 loc / 201 chars
SimpleGreeterRunner.groovy
greeter = new SimpleGreeter(greeting: "Hello")
greeter.sayHello "Peter", "Paul", "Marry"
2 loc / 80 chars
Main block can be a script
| Java: | 28 loc | 704 chars |
| Groovy: | 14 loc | 281 chars |
| Difference |
50% less
|
60% less
|
|---|
Now, where did that important piece of code go?
PersonJVO.java
public class PersonJVO {
private String firstName;
private String lastName;
private String streetAddress;
private String zipCode;
private String city;
private String state;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getStreetAddress() {
return streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
/**
* State code must be two chars long
* @param state a state code
*/
public void setState(String state) {
assert state != null && state.length() == 2;
this.state = state;
}
}
Now, where did that important piece of code go?
Ahhh.. there it is - hiding
PersonJVO.java
/**
* State code must be two chars long
* @param state a state code
*/
public void setState(String state) {
assert state != null && state.length() == 2;
this.state = state;
}
Focus on the important code - forget the boiler plate!
PersonGVO.groovy
class PersonGVO {
String firstName
String lastName
String streetAddress
String zipCode
String city
String state
/**
* State code must be two chars long
* @param state a state code
*/
public void setState(String state) {
assert state?.size() == 2
this.state = state
}
}
Getters and Setters
PersonScript.groovy
def groovyPerson = new PersonGVO()
// Setters:
groovyPerson.firstName = "Søren"
groovyPerson.lastName = "Glasius"
groovyPerson.setStreetAddress("40 Stevenson Ave") // Java syntax
// Getters:
println groovyPerson.firstName
println groovyPerson.getLastName() // Java syntax
// Java Object
def javaPerson = new PersonJVO()
// Setters:
javaPerson.firstName = 'Joshua'
javaPerson.lastName = 'Bloch'
javaPerson.setStreetAddress("Unknown") // Java syntax
// Getters:
println javaPerson.firstName
println javaPerson.getLastName()
Named arguments constructor
PersonScript.groovy
new PersonGVO(firstName: 'Guillaume', lastName: 'Laforge', city: 'Paris')
new PersonJVO(firstName: 'Neil', lastName: 'Ford')
Groovy GDK enhances the Java collections API
java
String[] strings = new String[]{"Peter", "Paul", "Mary"};
assert strings.length == 3;
assert "Peter".equals(strings[0]);
assert "Paul".equals(strings[1]);
assert "Mary".equals(strings[strings.length - 1]); // Last element in list
groovy
String[] strings = ["Peter", "Paul", "Mary"]
assert strings.size() == 3
assert "Peter" == strings[0]
assert "Paul" == strings.getAt(1)
assert "Mary" == strings[-1] // Last element in array
assert ["Paul", 'Mary'] == strings[1..2]
java
List<String> strings = java.util.Arrays.asList("Peter", "Paul", "Mary");
assert strings.size() == 3;
assert "Peter".equals(strings.get(0));
assert "Paul".equals(strings.get(1));
assert "Mary".equals(strings.get(strings.size() - 1)); // Last element in list
groovy
List<String> strings = ["Peter", "Paul", "Mary"]
assert strings.size() == 3
assert "Peter" == strings[0]
assert "Paul" == strings.getAt(1)
assert "Mary" == strings[-1] // Last element in list
assert ["Paul", 'Mary'] == strings[1..2]
groovy
String[] strings = ["Peter", "Paul", "Mary"]
assert strings.size() == 3
assert "Peter" == strings[0]
assert "Paul" == strings.getAt(1)
assert "Mary" == strings[-1] // Last element in array
assert ["Paul", 'Mary'] == strings[1..2]
groovy
List<String> strings = ["Peter", "Paul", "Mary"]
assert strings.size() == 3
assert "Peter" == strings[0]
assert "Paul" == strings.getAt(1)
assert "Mary" == strings[-1] // Last element in list
assert ["Paul", 'Mary'] == strings[1..2]
java
Map<String, String> map = new HashMap<>();
map.put("firstName", "Søren");
map.put("middleName", "Berg");
map.put("lastName", "Glasius");
assert map.size() == 3;
assert map.containsKey("firstName");
assert "Søren".equals(map.get("firstName"));
assert map.containsValue("Berg");
assert map.values().contains("Glasius");
groovy
Map<String, String> map = [firstName: "Søren", middleName: "Berg", lastName: "Glasius"]
assert map.size() == 3
assert map.firstName // Does the map contain key: firstName
assert 'Søren' == map['firstName']
assert 'middleName' in map.keySet()
assert 'Berg' == map.middleName
assert 'Glasius' in map.values()
assert 'Glasius' == map.getAt('lastName')
java
// Java 8:
int[] ints = IntStream.range(1, 10).toArray();
assert ints.length == 9;
assert ints[0] == 1;
assert ints[ints.length - 1] == 9;
groovy
int[] ints = 1..9
assert ints.size() == 9
assert ints[0] == 1
assert ints[-1] == 9
// Range exclusive
int[] otherInts = 1..<10
assert ints == otherInts
// Range on Stings
def letters = 'a'..'z'
assert letters.size() == 26
Groovy improves existing Java
classes with extra functionality
to save time
and reduce boilerplate
java
static String readFile(File file) throws IOException {
byte[] bytes = Files.readAllBytes(file.toPath());
return new String(bytes, "UTF-8");
}
public static void main(String[] args) {
File file = new File("file.txt");
try {
String content = readFile(file);
assert content.length() > 0;
} catch (IOException e) {
e.printStackTrace();
}
}
groovy
String content = new File('file.txt').text
assert content.size() > 0
java
static String readUrl(URL url) throws IOException {
try (InputStream in = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
return result.toString();
}
}
public static void main(String[] args) {
try {
URL url = new URL("http://www.example.com/");
String content = readUrl(url);
assert content.length() > 0;
} catch (IOException e) {
e.printStackTrace();
}
}
groovy
String content = new URL("http://www.example.com/").text
assert content.size() > 0
java
public static void main(String[] args) {
String str = "java";
String cap = str.length() > 0 ?
str.substring(0, 1).toUpperCase() + str.substring(1) : "";
assert "Java".equals(cap);
String stringUrl = "http://example.com";
try {
URL url = new URL(stringUrl);
assert url.openConnection().getContentType().equals("text/html");
} catch (IOException e) {
// Handle the error.
}
String cmd = "ls -l";
try {
Process process = Runtime.getRuntime().exec(cmd);
// Handle process
} catch (IOException e) {
// Handle the error.
}
}
groovy
assert "Java" == "java".capitalize();
def url = "http://example.com".toURL()
assert url.openConnection().contentType == "text/html";
def process = "ls -l".execute()
// Handle process
java
int answer = 42;
String regularString = "Some string";
String theAnswer = "The answer is " + answer;
String xml = "<GeocodeResponse status=\"OK\">\n" +
" <status>OK</status>\n" +
" <result>\n" +
" <type>university</type>\n" +
" <type>establishment</type>\n" +
" <formatted_address>\n" +
" University of California, Berkeley, Berkeley, CA, USA\n" +
" </formatted_address>\n" +
" </result>\n" +
"</GeocodeResponse";
groovy
def mol = 42
def regularString = 'A string'
def answer = "The Answer is $mol"
def multilinedString = '''\
<GeocodeResponse status="OK">
<status>OK</status>
<result>
<type>university</type>
<type>establishment</type>
<formatted_address>
University of California, Berkeley, Berkeley, CA, USA
</formatted_address>
<result>
</GeocodeResponse>
'''
def betterAnswer = """$mol is The Answer to the Ultimate Question
of 'Life, the Universe, and Everything'"""
GoogleMapsGeocode.xml
<GeocodeResponse>
<status>OK</status>
<result>
<type>university</type>
<type>establishment</type>
<formatted_address>
University of California, Berkeley, Berkeley, CA, USA
</formatted_address>
<address_component>
<long_name>University of California, Berkeley</long_name>
<short_name>UC Berkeley</short_name>
<type>establishment</type>
</address_component>
<address_component>
<long_name>Berkeley</long_name>
<short_name>Berkeley</short_name>
<type>locality</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Alameda County</long_name>
<short_name>Alameda County</short_name>
<type>administrative_area_level_2</type>
<type>political</type>
</address_component>
<address_component>
<long_name>California</long_name>
<short_name>CA</short_name>
<type>administrative_area_level_1</type>
<type>political</type>
</address_component>
<address_component>
<long_name>USA</long_name>
<short_name>US</short_name>
<type>country</type>
<type>political</type>
</address_component>
<geometry>
<location>
<lat>37.8718992</lat>
<lng>-122.2585399</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>37.8623341</lat>
<lng>-122.2719817</lng>
</southwest>
<northeast>
<lat>37.8766204</lat>
<lng>-122.2434660</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>37.8623341</lat>
<lng>-122.2719817</lng>
</southwest>
<northeast>
<lat>37.8766204</lat>
<lng>-122.2434660</lng>
</northeast>
</bounds>
</geometry>
<place_id>ChIJwdcixRh3j4ARiTJrO-o0gNo</place_id>
</result>
</GeocodeResponse>
groovy
url = "http://maps.google.com/maps/api/geocode/xml?address=UC%20Berkeley"
response = new XmlSlurper().parse(url)
def node = response.result // <GeocodeResponse><result>
assert node.formatted_address.text() == 'University of California, Berkeley, Berkeley, CA, USA'
assert node.address_component[0].long_name.text() == 'University of California, Berkeley'
assert node.address_component[0].short_name.text() == 'UC Berkeley'
assert node.geometry.location.lat.toDouble() == 37.8718992
assert node.geometry.location.lng.toDouble() == -122.2585399
<GeocodeResponse>
<result>
<formatted_address>
University of California, Berkeley, Berkeley, CA, USA
</formatted_address>
<address_component>
<long_name>University of California, Berkeley</long_name>
<short_name>UC Berkeley</short_name>
<type>establishment</type>
</address_component>
<geometry>
<location>
<lat>37.8718992</lat>
<lng>-122.2585399</lng>
</location>
</geometry>
</result>
</GeocodeResponse>
GoogleMapsGeocode.json
{
"results": [
{
"address_components": [
{
"long_name": "University of California, Berkeley",
"short_name": "UC Berkeley",
"types": [
"establishment"
]
},
{
"long_name": "Berkeley",
"short_name": "Berkeley",
"types": [
"locality",
"political"
]
},
{
"long_name": "Alameda County",
"short_name": "Alameda County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "California",
"short_name": "CA",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "USA",
"short_name": "US",
"types": [
"country",
"political"
]
}
],
"formatted_address": "University of California, Berkeley, Berkeley, CA, USA",
"geometry": {
"bounds": {
"northeast": {
"lat": 37.8766204,
"lng": -122.243466
},
"southwest": {
"lat": 37.8623341,
"lng": -122.2719817
}
},
"location": {
"lat": 37.8718992,
"lng": -122.2585399
},
"location_type": "APPROXIMATE",
"viewport": {
"northeast": {
"lat": 37.8766204,
"lng": -122.243466
},
"southwest": {
"lat": 37.8623341,
"lng": -122.2719817
}
}
},
"place_id": "ChIJwdcixRh3j4ARiTJrO-o0gNo",
"types": [
"university",
"establishment"
]
}
],
"status": "OK"
}
groovy
url = "http://maps.google.com/maps/api/geocode/json?address=UC%20Berkeley".toURL()
response = new JsonSlurper().parse(url)
def map = response.results[0] // { "results": [
assert map.formatted_address == 'University of California, Berkeley, Berkeley, CA, USA'
assert map.address_components[0].long_name == 'University of California, Berkeley'
assert map.address_components[0].short_name == 'UC Berkeley'
assert map.geometry.location.lat.toDouble() == 37.8718992
assert map.geometry.location.lng.toDouble() == -122.2585399
{
"results": [{
"address_components": [{
"long_name": "University of California, Berkeley",
"short_name": "UC Berkeley",
}],
"formatted_address": "University of California, Berkeley, Berkeley, CA, USA",
"geometry": {
"location": {
"lat": 37.8718992, "lng": -122.2585399
}
}
}]
}
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.
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.
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.
grails
sbg$ grails create-app ucb-demo
| Created Grails Application at /Users/sbg/projects/demos/UcbDemo
sbg$ |
grails
.
├── grails-app
│ ├── assets (images, javascript, stylesheets)
│ ├── conf
│ ├── controllers
│ ├── domain
│ ├── i18n
│ ├── services
│ ├── taglib
│ ├── utils
│ └── views
│ └── layouts
├── lib
├── scripts
├── src
│ ├── groovy
│ └── java
├── test
│ ├── integration
│ └── unit
├── web-app
│ ├── META-INF
│ ├── WEB-INF
│ │ └── tld
└── wrapper
sbg$ grails create-domain-class grails.demo.Author
grails
class Author {
String name
String country
String email
static constraints = {
name nullable: false
country nullable: false, inList: ['Canada', 'Denmark', 'USA']
email nullable: true, email: true
}
static mapping = {
table name: 'AUTHORS'
name column: 'AUTHOR_NAME'
country type: 'VARCHAR(30)'
}
}
grails
class Author {
String name
String country
String email
static hasMany = [books: Book]
static constraints = {
name nullable: false
country nullable: false, inList: ['Canada', 'Denmark', 'UK', 'USA']
email nullable: true, email: true
}
}
grails
class Book {
String isbn
String title
int pages
static belongsTo = [author: Author]
static constraints = {
isbn nullable: false
title nullable: false
pages min: 1
}
}
grails
def author = new Author(name: "Douglas Adams", country: 'UK')
author.addToBooks(isbn: '0-330-25864-8', title: "The Hitchhiker's Guide to the Galaxy", pages: 224)
author.addToBooks(isbn: '0-345-39181-0', title: "The Restaurant at the End of the Universe", pages: 208)
author.save()
grails
def author = new Author(name: "Ira Levin", country: 'France').validate()
assert author.hasErrors()
grails
assert Book.count() == 2
grails
def author = Author.get(1)
assert author.name == "Douglas Adams"
assert author.country == 'UK'
assert author.books.size() == 2
grails
assert Book.list().size() == 2
grails
def author = Author.findByNameIlikeAndCountry('%adams', 'UK')
assert author.name == "Douglas Adams"
grails
def book = Book.where { isbn =~ "03%" && pages > 200 }.get()
assert book.isbn == '0345391810'
assert book.title.startsWith('The Restaurant')
grails
def author = Author.get(1)
author.addToBooks(isbn: '0-330-28700-1', title: 'So Long, and Thanks for All Fish', pages: 192)
author.save()
grails
def author = Author.get(1)
author.delete()
assert Author.count() == 0
sbg$ grails create-scaffolded-controller grails.demo.Book
BookController.groovy
class BookController {
static scaffold = true
}
AuthorController.groovy
@Transactional(readOnly = true)
class AuthorController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Author.list(params), model: [authorInstanceCount: Author.count()]
}
def show(Author authorInstance) {
respond authorInstance
}
def create() {
respond new Author(params)
}
@Transactional
def save(Author authorInstance) {
if (authorInstance == null) {
notFound()
return
}
if (authorInstance.hasErrors()) {
respond authorInstance.errors, view: 'create'
return
}
authorInstance.save flush: true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'author.label', default: 'Author'), authorInstance.id])
redirect authorInstance
}
'*' { respond authorInstance, [status: CREATED] }
}
}
def edit(Author authorInstance) {
respond authorInstance
}
@Transactional
def update(Author authorInstance) {
if (authorInstance == null) {
notFound()
return
}
if (authorInstance.hasErrors()) {
respond authorInstance.errors, view: 'edit'
return
}
authorInstance.save flush: true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Author.label', default: 'Author'), authorInstance.id])
redirect authorInstance
}
'*' { respond authorInstance, [status: OK] }
}
}
@Transactional
def delete(Author authorInstance) {
if (authorInstance == null) {
notFound()
return
}
authorInstance.delete flush: true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'Author.label', default: 'Author'), authorInstance.id])
redirect action: "index", method: "GET"
}
'*' { render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'author.label', default: 'Author'), params.id])
redirect action: "index", method: "GET"
}
'*' { render status: NOT_FOUND }
}
}
}
RemoteBookService.groovy
@Transactional
class RemoteService {
List<Book> fetchBooks() {
String url = "http://bookservice.com/books.json".toURL()
def json = new JsonSlurper().parse(url)
json.books.collect {
new Book(it.isbn, it.title, it.pages.toInteger()).save()
}
}
}
RemoteBookController.groovy
class RemoteBookController {
def remoteBookService // injected by Grails
def index() {
def books = remoteBookService.fetchBooks()
// Render the books as json
render books as JSON
}
}
grails
sbg$ grails create-app ucb-demo
| Created Grails Application at /Users/sbg/projects/demos/UcbDemo
sbg$ |
build.gradle
plugins { id 'com.bluepapa32.watch' version '0.1.5' }
apply plugin: 'idea'
apply plugin: 'groovy'
repositories {
jcenter()
}
dependencies {
// We use the latest groovy 2.x version for building this library
compile 'org.codehaus.groovy:groovy-all:2.4.3'
compile 'net.sourceforge.nekohtml:nekohtml:1.9.14'
compile 'org.apache.commons:commons-lang3:3.3.2'
// We use the awesome Spock testing and specification framework
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
testCompile 'junit:junit:4.11'
}
task slides (dependsOn: 'classes', type: JavaExec) {
main = 'Runner'
classpath = sourceSets.main.runtimeClasspath
}
watch {
slides {
files files('src/main/java', 'src/main/groovy')
tasks 'slides'
}
}
sample-pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.foo</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>my-app</name>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.foo.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
GebDemo.groovy
Browser.drive {
go "http://myapp.com/login"
assert $("h1").text() == "Please Login"
$("form.login").with {
username = "admin"
password = "password"
login().click()
}
assert $("h1").text() == "Admin Section"
}
DataDrivenSpec.groovy
@Unroll
class DataDrivenSpec extends Specification {
def "minimum of #a and #b is #c"() {
expect:
Math.min(a, b) == c
where:
a | b || c
3 | 7 || 3
5 | 4 || 4
9 | 9 || 9
}
def "#person.name is a #sex.toLowerCase() person"() {
expect:
person.getSex() == sex
where:
person || sex
new Person(name: "Fred") || "Male"
new Person(name: "Wilma") || "Female"
}
static class Person {
String name
String getSex() { name == "Fred" ? "Male" : "Female" }
}
}
GParsReactor.groovy
def decryptor = actor {
loop {
react { message ->
if (message instanceof String) reply message.reverse()
else stop()
}
}
}
def console = actor {
decryptor.send 'lellarap si yvoorG'
react {
println 'Decrypted message: ' + it
decryptor.send false // Stops the loop
}
}
[decryptor, console]*.join()
DemoConsoleModel.groovy
class DemoConsoleModel {
String scriptSource
@Bindable def scriptResult
@Bindable boolean enabled = true
}
DemoConsoleView.groovy
application(title:'DemoConsole', pack:true, locationByPlatform:true) {
panel(border:emptyBorder(6)) {
borderLayout()
scrollPane(constraints:CENTER) {
textArea(text:bind(target:model, targetProperty:'scriptSource'),
enabled: bind {model.enabled},
columns:40, rows:10)
}
hbox(constraints:SOUTH) {
button("Execute", actionPerformed:controller.&executeScript,
enabled: bind {model.enabled})
hstrut(5)
label("Result:")
hstrut(5)
label(text:bind {model.scriptResult})
}
}
}
DemoConsoleController.groovy
class DemoConsoleController {
GroovyShell shell = new GroovyShell()
// these will be injected by Griffon
def model
def view
def executeScript(ActionEvent evt = null) {
model.enabled = false
doOutside {
def result
try {
result = shell.evaluate(model.scriptSource)
} finally {
edt {
model.enabled = true
model.scriptResult = result
}
}
}
}
}
Søren Berg Glasius @sbglasius
Søren Berg Glasius @sbglasius