Spring XD: Tackling Big Data Complexity – 7:30pm - 8:00pm: JAX Innovation Awards - Spring Boot is nominated! • Thursday – 8:30am - 9:45am: Going reactive: High Performance JVM Code with Reactor – 10:30am -11:30am: Functional Groovy – 10:30am - 11:30am: Spring Data Solr – 2:15pm - 3:15pm: Advanced Web Development Techniques with Grails 2 – 2:15pm - 3:15pm: Making the Eclipse IDE fun again • Friday – 9:00am - 5:00pm: From the database into the web - End-to-end REST web services with Spring 4
of the language 38 def adder = { a, b -‐> a + b } ! assert adder(1, 2) == 3 assert adder('a', 'b') == 'ab' Assign a function into a variable Closure parameters
of the language 38 def adder = { a, b -‐> a + b } ! assert adder(1, 2) == 3 assert adder('a', 'b') == 'ab' Short form of: adder.call(‘a’, ‘b’) Assign a function into a variable Closure parameters
of the language 38 def adder = { a, b -‐> a + b } ! assert adder(1, 2) == 3 assert adder('a', 'b') == 'ab' Short form of: adder.call(‘a’, ‘b’) Genericity with duck typing & operator overloading Assign a function into a variable Closure parameters
... elements -‐> elements.sum() } ! assert sum(1, 2) == 3 assert sum('a', 'b', 'c') == 'abc' You can specify the type: int... Variable number of arguments
class Person { String name int age } def persons = [ new Person('Guillaume', 36), new Person('Marion', 6), new Person('Erine', 1) ] def names = persons.findAll { it.age < 18 } .collect { it.name.toUpperCase() } .sort() .join(', ')
class Person { String name int age } def persons = [ new Person('Guillaume', 36), new Person('Marion', 6), new Person('Erine', 1) ] def names = persons.findAll { it.age < 18 } .collect { it.name.toUpperCase() } .sort() .join(', ')
-‐> new File('out.txt').withWriter { w -‐> r.eachLine { line -‐> if (line.contains('Groovy')) w << line.toUpperCase() } } } Take care of properly opening / closing resources
assert "Alibaba" ==~ /.*(ba){2}/ ! def matcher = "Superman" =~ /([A-‐Z][a-‐z]+)man/ assert matcher[0][0] == 'Superman' assert matcher[0][1] == 'Super' ! '75001 Paris'.find(/(\d{5})\s(\w+)/) { match, zip, town -‐> println "The Zip code of ${town} is ${zip}" } Pattern Match Find Nice way to decompose the matched regions
0.9 assert 3 / 2 == 1.5 One of the reasons why micro- benchmarks sometimes showed Groovy to be slow... But you can use doubles & floats for performance, with ‘d’ or ‘f’ suffixes or with explicit type
switch(obj) { case 123: "number 123"; break case "abc": "string abc"; break case String: "is a string"; break case [1, 2, 3]: "in list"; break case ~/.*o+.*/: "regex match"; break case { it < 3 }: "closure criteria"; break default: "unknown" }
double x, y ! double getAt(int idx) { if (idx == 0) x else if (idx == 1) y else throw new Exception("Wrong index") } } ! def (x, y) = new Point(x: 48.3, y: 3.5) ! assert x == 48.3 && y == 3.5
def json = new JsonBuilder() json.person { name 'Guillaume' age 36 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } }
def json = new JsonBuilder() json.person { name 'Guillaume' age 36 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } } Hierarchical data representation
def json = new JsonBuilder() json.person { name 'Guillaume' age 36 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } } Hierarchical data representation Closure blocks delimiting the structure
LineItem line } class LineItem { int quantity Item item } class Item { String name } ! def o = new Order( line: new LineItem( quantity: 2, item: null)) ! println o.line.item.name
LineItem line } class LineItem { int quantity Item item } class Item { String name } ! def o = new Order( line: new LineItem( quantity: 2, item: null)) ! println o.line.item.name With Java, you only get an NPE. No idea where it came from!
LineItem line } class LineItem { int quantity Item item } class Item { String name } ! def o = new Order( line: new LineItem( quantity: 2, item: null)) ! println o.line.item.name With Java, you only get an NPE. No idea where it came from! Groovy will say: Cannot get property ‘name’ on null object
LineItem line } class LineItem { int quantity Item item } class Item { String name } ! def o = new Order( line: new LineItem( quantity: 2, item: null)) ! println o.line.item.name
LineItem line } class LineItem { int quantity Item item } class Item { String name } ! def o = new Order( line: new LineItem( quantity: 2, item: null)) ! println o.line.item.name o?.line?.item?.name
LineItem line } class LineItem { int quantity Item item } class Item { String name } ! def o = new Order( line: new LineItem( quantity: 2, item: null)) ! println o.line.item.name o?.line?.item?.name Safe navigation: will just return null; No NPE
'unknown'] if (x != null && x.size() > 0) x else y if (x && x.size()) x else y if (x) x else y x ? x : y x ?: y Null, empty, zero- sized... false, otherwise true!
'unknown'] if (x != null && x.size() > 0) x else y if (x && x.size()) x else y if (x) x else y x ? x : y x ?: y Null, empty, zero- sized... false, otherwise true! Good old ternary operator
'unknown'] if (x != null && x.size() > 0) x else y if (x && x.size()) x else y if (x) x else y x ? x : y x ?: y Null, empty, zero- sized... false, otherwise true! Good old ternary operator Elvis!
representation of your program before being compiled into bytecode ! • AST transformation == process of transforming the AST of a program before it’s compiled ! • Macro-like compiler hook! 71
name – an int age 76 public final class Person {! private final String name;! private final int age;! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! ! public String getName() {! return name;! }! ! public int getAge() {! return age;! }! ! public int hashCode() {! return age + 31 * name.hashCode();! }! ! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! ! public String toString() {! return "Person(" + name + ", " + age + ")";! }! }!
name – an int age 76 public final class Person {! private final String name;! private final int age;! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! ! public String getName() {! return name;! }! ! public int getAge() {! return age;! }! ! public int hashCode() {! return age + 31 * name.hashCode();! }! ! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! ! public String toString() {! return "Person(" + name + ", " + age + ")";! }! }! Damn verbose Java!
name – an int age 76 public final class Person {! private final String name;! private final int age;! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! ! public String getName() {! return name;! }! ! public int getAge() {! return age;! }! ! public int hashCode() {! return age + 31 * name.hashCode();! }! ! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! ! public String toString() {! return "Person(" + name + ", " + age + ")";! }! }! Damn verbose Java! Although it’s also a valid Groovy program!
closures or methods with the same set of argument values 78 import groovy.transform.* ! @Memoized long fib(long n) { if (n == 0) 0 else if (n == 1) 1 else fib(n -‐ 1) + fib(n -‐ 2) } ! println fib(40)
closures or methods with the same set of argument values 78 import groovy.transform.* ! @Memoized long fib(long n) { if (n == 0) 0 else if (n == 1) 1 else fib(n -‐ 1) + fib(n -‐ 2) } ! println fib(40) Best applied to side-effect free functions
throws compilation errors on... – typos in method and variable names – incompatible return types – wrong type assignments ! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 80 You can even extend the static type checker!
throws compilation errors on... – typos in method and variable names – incompatible return types – wrong type assignments ! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 80 You can even extend the static type checker! Type check DSLs or dynamic features!
trees Java 191 ms 97 ms 3.6 s Static compilation 197 ms 101 ms 4.3 s Primitive optimizations 360 ms 111 ms 23.7 s No prim. optimizations 2590 ms 3220 ms 50.0 s 1.7 1.8 2.x
class MathSpec extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c ! where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } }
class MathSpec extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c ! where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } @Grab a dependency
class MathSpec extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c ! where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } @Grab a dependency Meaningful test method names
class MathSpec extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c ! where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } @Grab a dependency Meaningful test method names Clever use of labels for BDD style
class MathSpec extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c ! where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } @Grab a dependency Meaningful test method names Clever use of labels for BDD style Expression to be asserted
class MathSpec extends Specification { def "maximum of two numbers"() { expect: Math.max(a, b) == c ! where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } } @Grab a dependency Meaningful test method names Clever use of labels for BDD style Expression to be asserted Cute data- driven tests!
{ go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title
{ go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title Find & fill in the form
{ go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title Find & fill in the form Submit the form
{ go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title Find & fill in the form Submit the form In the admin section, yeah!
geb.spock.GebSpec ! class GoogleWikipediaSpec extends GebSpec { ! def "first result for wikipedia search should be wikipedia"() { given: to GoogleHomePage ! expect: at GoogleHomePage ! when: search.field.value("wikipedia") ! then: waitFor { at GoogleResultsPage } ! and: firstResultLink.text() == "Wikipedia" ! when: firstResultLink.click() ! then: waitFor { at WikipediaPage } } } With page objects
• But... – as type safe as you want it — static type checking – as fast as you need it — static compilation – as functional as you make it — closures... 100
• Scripting tasks, build automation • More readable and expressive tests • Extension points for customizing / configuring apps • Full blown apps – for the web with Grails, Ratpack, Gaelyk – for web reactive programming with Reactor – for desktop with Griffon 101