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