• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
What makes groovy groovy   codeurs en seine - 2013 - light size
 

What makes groovy groovy codeurs en seine - 2013 - light size

on

  • 293 views

Groovy n'est pas un nouveau venu dans l'arène des langages alternatifs pour la JVM, mais avec plus d'1.7 millions de téléchargements par an, c'est clairement le plus utilisé aujourd'hui !

Groovy n'est pas un nouveau venu dans l'arène des langages alternatifs pour la JVM, mais avec plus d'1.7 millions de téléchargements par an, c'est clairement le plus utilisé aujourd'hui !
Mais pourquoi choisir Groovy pour vos projets ? Que peut-il vous apporter ?
- une courbe d'apprentissage minime
- son intégration transparente avec Java pour mixer Groovy et Java ensemble
- une syntax malléable, concise et lisible adaptée aux Domain-Specific Languages
- une approche pragmatique sur le typage
- un riche écosystème de projets, comme Grails, Gradle, GPars, Spock, Geb, etc...
Dans cette session, vous découvrirez comment tout s'articule dans l'univers Groovy, ainsi que où, quand, comment vous pourrez tirer avantage de Groovy pour améliorer votre productivité.

Statistics

Views

Total Views
293
Views on SlideShare
293
Embed Views
0

Actions

Likes
0
Downloads
4
Comments
0

0 Embeds 0

No embeds

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    What makes groovy groovy   codeurs en seine - 2013 - light size What makes groovy groovy codeurs en seine - 2013 - light size Presentation Transcript

    • What makes Groovy groovy? Guillaume Laforge @glaforge © 2013 Guillaume Laforge. All rights reserved. Do not distribute without permission.
    • Guillaume Laforge Groovy project lead at . @glaforge http://glaforge.appspot.com
    • Les Cast Codeurs
    • 1 t r a P The Groovy vision
    • Simplify the life of (Java) developers
    • Great for scripting
    • Great for scripting Fit for DomainSpecific Languages
    • Great for scripting Fit for DomainSpecific Languages Most seamless integration & interoperability wih java!
    • It’s so easy to learn!
    • Groovy as a Java superset It’s so easy to learn!
    • class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} }
    • As safe and fast as Java with static type checking & compilation
    • As safe and fast as Java with static type checking & compilation
    • new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!rocks!" !!!!!!!!} !!!!} }
    • move!forward!at!3.km/h Expressive, Concise, Readable
    • Speaking of conciseness... A full Spring app in the span of a tweet! @RestController class!App!{ !!!!@RequestMapping("/") !!!!String!home()!{!"Hello!World!"!} }
    • • SmartThings • Carriots Services • European Patent Office • Amadeus IoT Financial • Crédit Suisse • JPMorgan • Fanny Mae • Mutual of Omaha • Hypoport 20 • Energy Transfer • National Cancer Inst. • IRSN Internet • Netflix • LinkedIn • Google Technology Who’s using Groovy?
    • 2 t r a P Cool Groovy gems
    • Most Java code is also valid Groovy code!
    • Most Java code is also valid Groovy code! Any Java developer is a Groovy developer!
    • Flat learning curve
    • Flat learning curve Easy to learn
    • Scripts versus Classes public!class!Main!{ !!!!public!static!void!main(String[]!args)!{ !!!!!!!!System.out.println("Hello"); !!!!} } vs 24
    • Scripts versus Classes public!class!Main!{ !!!!public!static!void!main(String[]!args)!{ !!!!!!!!System.out.println("Hello"); !!!!} } vs println!"Hello" 24
    • Optional
    • Semicolons Optional
    • Semicolons Parentheses Optional
    • Semicolons Parentheses Optional return keyword
    • Semicolons Parentheses Optional return keyword public keyword
    • Semicolons Parentheses Optional Typing! return keyword public keyword
    • Optional... public!class!Greeter!{ !!!!private!String!owner; !!!!public!String!getOwner()!{ !!!!!!!!return!owner; !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner; !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner; !!!!} } Greeter!greeter!=!new!Greeter(); greeter.setOwner("Guillaume"); System.out.println(greeter.greet("Marion")); 26
    • Optional... Semicolons public!class!Greeter!{ !!!!private!String!owner; !!!!public!String!getOwner()!{ !!!!!!!!return!owner; !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner; !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner; !!!!} } Greeter!greeter!=!new!Greeter(); greeter.setOwner("Guillaume"); System.out.println(greeter.greet("Marion")); 26
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner("Guillaume") System.out.println(greeter.greet("Marion")) 27
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner("Guillaume") System.out.println(greeter.greet("Marion")) 28
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Parentheses Greeter!greeter!=!new!Greeter() greeter.setOwner("Guillaume") System.out.println(greeter.greet("Marion")) 28
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 29
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} return keyword !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 29
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 30
    • Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!******!owner !!!!} public keyword !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 30
    • Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 31
    • Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } optional typing Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 31
    • Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 32
    • Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } handy println shortcut def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 32
    • Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 33
    • Optional... verbose Java properties! ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 33
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 34
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Property notation def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 34
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.owner!!!!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 35
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner Named argument !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner constructor !!!!} } def!!!!!greeter!=!new!Greeter() greeter.owner!!!!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 35
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 36
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner Interpolated strings! (aka GStrings) !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 36
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!${name},!I!am!${owner}" !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 37
    • Optional... ******!class!Greeter!{ !!!!*******!String!owner Let’s reformat that mess of whitespace! !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!${name},!I!am!${owner}" !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 37
    • Optional... class!Greeter!{ ***!String!owner ****String!greet(String!name)!{ ******!"Hello!${name},!I!am!${owner}" !!!!} } def!greeter!=!new!Greeter(owner:!"Guillaume") println!greeter.greet("Marion") 38
    • Optional... public!class!Greeter!{ !!!!private!String!owner; !!!!public!String!getOwner()!{ class!Greeter!{ !!!!!!!!return!owner; ***!String!owner !!!!} ****String!greet(String!name)!{ !!!!public!void!setOwner(String!owner)!{ ******!"Hello!${name},!I!am!${owner}" !!!!!!!!this.owner!=!owner; !!!!} !!!!}} !!!!public*String!greet(String!name)!{ def!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner; !!!!}println!greeter.greet("Marion") } Greeter!greeter!=!new!Greeter(); greeter.setOwner("Guillaume"); System.out.println(greeter.greet("Marion")); 38
    • Optional... class!Greeter!{ ***!String!owner ****String!greet(String!name)!{ ******!"Hello!${name},!I!am!${owner}" !!!!} } def!greeter!=!new!Greeter(owner:!"Guillaume") println!greeter.greet("Marion") 38
    • Native syntax constructs • Closures • Lists • Maps • Regular expressions • Ranges 39
    • Closures • Functions as first-class citizen of the language 40
    • Lists def!list!=!['a',!'b',!'c'] list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' 41
    • Lists List definition def!list!=!['a',!'b',!'c'] list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' 41
    • Lists List definition def!list!=!['a',!'b',!'c'] Append an element (operator overloading) list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' 41
    • Lists List definition def!list!=!['a',!'b',!'c'] Append an element (operator overloading) list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' Functional-style map / filter / reduce with closures 41
    • Maps def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') 42
    • Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') 42
    • Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') Indexed access 42
    • Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') Property notation access 42 Indexed access
    • Regular expressions def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
    • Regular expressions Pattern def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
    • Regular expressions Pattern Match def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
    • Regular expressions Pattern Match def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ Find def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
    • Regular expressions Pattern Match def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ Find def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } Nice way to decompose the matched regions 43
    • Ranges def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44
    • Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44
    • Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 Excluded upper bound assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44
    • Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 Excluded upper bound assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44 Reverse range
    • Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 Excluded upper bound assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44 Reverse range Negative index count from the end
    • Strings, GStrings, multiline strings def!name!=!'Groovy' def!tmpl!=!""" !!!!Dear!Mr!${name}, !!!!You're!the!winner!of!the!lottery! !!!!Yours!sincerly, !!!!Dave """ assert!tmpl.toString().contains('Groovy') 45
    • Strings, GStrings, multiline strings Plain java.lang.String def!name!=!'Groovy' def!tmpl!=!""" !!!!Dear!Mr!${name}, !!!!You're!the!winner!of!the!lottery! !!!!Yours!sincerly, !!!!Dave """ assert!tmpl.toString().contains('Groovy') 45
    • Strings, GStrings, multiline strings Plain java.lang.String def!name!=!'Groovy' Multiline string with def!tmpl!=!""" expression interpolation !!!!Dear!Mr!${name}, !!!!You're!the!winner!of!the!lottery! !!!!Yours!sincerly, !!!!Dave """ assert!tmpl.toString().contains('Groovy') 45
    • Surprising numbers... System.out.println(!2.0!c!1.1!); 46
    • Surprising numbers... System.out.println(!2.0!c!1.1!); 0.8999999999999999 46
    • Surprising numbers... System.out.println(!2.0!c!1.1!); 0.8999999999999999 46
    • Surprising numbers... System.out.println(!3!/!2!); 47
    • Surprising numbers... System.out.println(!3!/!2!); 1 47
    • Surprising numbers... System.out.println(!3!/!2!); 1 47
    • BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 48
    • BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 One of the reasons why microbenchmarks sometimes showed Groovy to be slow... 48
    • BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 One of the reasons why microbenchmarks sometimes showed Groovy to be slow... 48 But you can use doubles & floats for performance, with ‘d’ or ‘f ’ suffixes or with explicit type
    • Powerful switch / case on steroids
    • Powerful switch / case on steroids switch(obj)!{ !!!!case!123:!!!!!!!!!!!!!!!!!!"number!123";!!!!!!!!break !!!!case!"abc":!!!!!!!!!!!!!!!!"string!abc";!!!!!!!!break !!!!case!String:!!!!!!!!!!!!!!!"is!a!string";!!!!!!!break !!!!case![1,!2,!3]:!!!!!!!!!!!!"contained!in!list";!break !!!!case!~/.*o+.*/:!!!!!!!!!!!!"match!the!regex";!!!break !!!!case!{!it.isUpperCase()!}:!"closure!criteria";!!break ****default:!"unknown" }
    • Named arguments move!obj,!x:!3,!y:!4 50
    • Named arguments move!obj,!x:!3,!y:!4 Normal argument 50
    • Named arguments move!obj,!x:!3,!y:!4 Normal argument 50 Named argument
    • Named arguments move!obj,!x:!3,!y:!4 Normal argument Calls: move(Map m, Object) 50 Named argument
    • Command chains • Ability to chain method calls without parentheses and dots 51
    • Command chains • Ability to chain method calls without parentheses and dots move!forward!at!3.km/h 51
    • Command chains • Ability to chain method calls without parentheses and dots move!forward!at!3.km/h Actually equivalent to: move(forward).at(3.getKm().div(h)) 51
    • Named arguments and command chains check!that:!vodka!tastes!good 52
    • Named arguments and command chains check!that:!vodka!tastes!good Will call: check(that: vodka).tastes(good) 52
    • Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] (a,!b)!=![b,!a] def!(int!i,!int!j)!=![1,!2] def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
    • Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
    • Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] With types (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
    • Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] With types (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] Method returning a list def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
    • Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] With types (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] Method returning a list def!geocode(String!place)!{ !!!!return![45.4,!2.3] } Destructuring def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
    • Multiple assignment and destructuring class!Point!{ !!!!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 54
    • Multiple assignment and destructuring class!Point!{ !!!!double!x,!y Method signature convention: getAt(int) !!!!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 54
    • Multiple assignment and destructuring class!Point!{ !!!!double!x,!y Method signature convention: getAt(int) !!!!double!getAt(int!idx)!{ !!!!!!!!if!(idx!==!0)!x !!!!!!!!else!if!(idx!==!1)!y !!!!!!!!else!throw!new!Exception("Wrong!index") !!!!} } Transparent destructuring def!(x,!y)!=!new!Point(x:!48.3,!y:!3.5) assert!x!==!48.3!&&!y!==!3.5 54
    • Builders and GPath expressions import!groovy.xml.* new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} !!!!} } 55
    • Builders and GPath expressions import!groovy.xml.* Hierarchical data representation new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} !!!!} } 55
    • Builders and GPath expressions import!groovy.xml.* Hierarchical data representation new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} Closure blocks delimiting !!!!} the structure } 55
    • Builders and GPath expressions import!groovy.xml.* Hierarchical data representation new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} Attributes !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} Closure blocks delimiting !!!!} the structure } 55
    • Power asserts def!(a,!b,!c)!=![20,!30,!40] assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 56
    • Power asserts def!(a,!b,!c)!=![20,!30,!40] assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 Assertion!failed:! assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 !!!!!!!|!|!!|!|!!!!|!!!!|!!!!|!|!|!!!| !!!!!!!|!580|!29!!!58!!!false|!|!60!!61 !!!!!!!20!!!30!!!!!!!!!!!!!!!|!40 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!120 ! at!script1.run(script1t.groovy:4) 56
    • Null handling • Groovy provides a more descriptive NullPointerException than Java • Safe navigation with ?. 57
    • The Truth, the Groovy Truth!
    • The Truth, the Groovy Truth! And what if I could customize the truth?
    • ?:
    • ?: The Elvis operator!
    • Towards Elvis... 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'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 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'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 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'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, zerosized... false, otherwise true! 60
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'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 Good old ternary operator 60 Null, empty, zerosized... false, otherwise true!
    • Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'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 Elvis! 60 Good old ternary operator Null, empty, zerosized... false, otherwise true!
    • AST transformations • Abstract Syntax Tree – in memory 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! 61
    • Lots of AST transformations... • Code generation – @ToString, @EqualsAndHashCode, @Canonical, @TupleConstructor, @InheritConstructors, @Category, @IndexedProperty, @Lazy, @Newify • Class design – @Delegate, @Immutable, @Memoized, @Singleton, @Mixin • Logging – @Log, @Log4j, @Log4j2, @Slf4j 62
    • Lots of AST transformations... • Safer scripting – @ConditionalInterrupt, @ThreadInterrupt, @TimedInterupt • Compiler directives – @Field, @PackageScope, @AnnotationCollector, @DelegatesTo, @TypeChecked, @CompileStatic, @CompileDynamic • Swing patterns – @Bindable, @ListenerList, @Vetoable 63
    • Lots of AST transformations... • Dependencies handling – @Grab, @GrabConfig, @GrabExclude, @GrabResolver • Test assistance – @NotYetImplemented, @ASTTest 64
    • Immutability • Implement immutability by the book – final class – tuple-style constructor – private final backing fields – defensive copying of collections – equals() and hashCode() methods – toString() method – ... 65
    • Immutability • Implement immutability by the book – final class Can be error-prone to – tuple-style constructor write immutable – private final backing fields classes oneself! – defensive copying of collections – equals() and hashCode() methods – toString() method – ... 65
    • Immutability • A Person class with public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } – a String name – an int 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 + ")"; } } 66
    • Immutability • A Person class with Damn verbose Java! public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } – a String name – an int 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 + ")"; } } 66
    • Immutability • A Person class with Damn verbose Java! public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } – a String name – an int 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; } Although it’s also a valid Groovy program! public String toString() { return "Person(" + name + ", " + age + ")"; } } 66
    • @Immutable import!groovy.transform.* @Immutable class!Person!{ !!!!String!name !!!!int!age } 67
    • Memoization • Cache the result of previous invocations of closures or methods with the same set of argument values import!groovy.transform.* @Memoized long!fib(long!n)!{ !!!!if!(n!==!0)!0 !!!!else!if!(n!==!1)!1 !!!!else!fib(n!c!1)!+!fib(n!c!2) } println!fib(40) 68
    • Memoization • Cache the result of previous invocations of closures or methods with the same set of argument values import!groovy.transform.* Best applied to side-effect free functions @Memoized long!fib(long!n)!{ !!!!if!(n!==!0)!0 !!!!else!if!(n!==!1)!1 !!!!else!fib(n!c!1)!+!fib(n!c!2) } println!fib(40) 68
    • Groovy allows you to be lazy
    • Groovy allows you to be lazy The compiler will do the job for you
    • Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you
    • Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you Less stuff to maintain and worry about
    • @TypeChecked and @CompileStatic • Static type checking with @TypeChecked, 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 » 70
    • @TypeChecked and @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types You can even extend the – wrong type assignments static type checker! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 70
    • @TypeChecked and @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types You can even extend the – wrong type assignments static type checker! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 70 Type check DSLs or dynamic features!
    • @TypeChecked and @CompileStatic • What is type checked can also be compiled statically with @CompileStatic – generate the same bytecode as javac – same performance as Java 71
    • Pi (π) Fibonacci quadrature Java 191 ms 97 ms 3.6 s 2.x Static compilation 197 ms 101 ms 4.3 s 1.8 72 Binary trees Primitive optimizations 360 ms 111 ms 23.7 s 1.7 Static compilation performance No prim. optimizations 2590 ms 3220 ms 50.0 s
    • 3 t r a P Superb community!
    • A blossoming Ecosystem
    • M V G
    • GVM
    • GVM GROOVY ENVIRONMENT MANAGER
    • GVM: Groovy enVironment Manager • The new kid on the block – http://gvmtool.net/ — @gvmtool • Manage parallel versions of the various ecosystem projects • Supports... – Groovy, Grails, Griffon, Gradle,Vert.x, Spring Boot • On Linux, MacOS, Cygwin, Solaris, FreeBSD 81
    • I’m Spock...
    • I’m Spock... ...the Spock testing framework
    • I’m Spock... ...the Spock testing framework
    • Spock example @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83
    • Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83
    • Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 Meaningful test method names
    • Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* Meaningful test method names class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c Clever use of labels ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 for BDD style
    • Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* Meaningful test method names class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c Clever use of labels ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 for BDD style Expression to be asserted
    • Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* Meaningful test method names class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c Clever use of labels ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 for BDD style Cute datadriven tests! Expression to be asserted
    • @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpackcframework:ratpackcgroovy:0.9.0cSNAPSHOT") import!static!org.ratpackframework.groovy.RatpackScript.ratpack import!static!org.ratpackframework.groovy.Template.groovyTemplate ratpack!{ !!!!handlers!{ !!!!!!!!get!{ !!!!!!!!!!!!response.send!"Welcome!" !!!!!!!!} !!!!!!!!get("date")!{ !!!!!!!!!!!!render!groovyTemplate("date.html") !!!!!!!!} !!!!!!!!assets!"public" !!!!} }
    • @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpackcframework:ratpackcgroovy:0.9.0cSNAPSHOT") import!static!org.ratpackframework.groovy.RatpackScript.ratpack import!static!org.ratpackframework.groovy.Template.groovyTemplate ratpack!{ !!!!handlers!{ !!!!!!!!get!{ !!!!!!!!!!!!response.send!"Welcome!" !!!!!!!!} Lightweight Netty-based web app toolkit !!!!!!!!get("date")!{ !!!!!!!!!!!!render!groovyTemplate("date.html") !!!!!!!!} !!!!!!!!assets!"public" !!!!} }
    • Geb • Browser automation solution • WebDriver + jQuery selectors + Groovy • Handy for – scripting, scraping, automation... – functional / web / acceptance testing • when integrated with JUnit, TestNG or Spock 85
    • Geb — Example import!geb.Browser 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" } 86
    • Geb — Example Drive the browser to this site import!geb.Browser 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" } 86
    • Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } 86
    • Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } 86 Find & fill in the form
    • Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } 86 Find & fill in the form Submit the form
    • Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } Find & fill in the form Submit the form In the admin section, yeah! 86
    • Geb — With page objects and Spock import!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!} !!!!} } 87
    • Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ With page objects !!!!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!} !!!!} } 87
    • Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ With page objects !!!!def!"first!result!for!wikipedia!search!should!be!wikipedia"()!{ ********given: !!!!!!!!to!GoogleHomePage ********expect: !!!!!!!!at!GoogleHomePage BDD style: given/when/then ********when: !!!!!!!!search.field.value("wikipedia") ********then: !!!!!!!!waitFor!{!at!GoogleResultsPage!} ********and: !!!!!!!!firstResultLink.text()!==!"Wikipedia" ********when: !!!!!!!!firstResultLink.click() ********then: !!!!!!!!waitFor!{!at!WikipediaPage!} !!!!} } 87
    • Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ With page objects !!!!def!"first!result!for!wikipedia!search!should!be!wikipedia"()!{ ********given: !!!!!!!!to!GoogleHomePage ********expect: !!!!!!!!at!GoogleHomePage ********when: !!!!!!!!search.field.value("wikipedia") BDD style: given/when/then Wait for slow loading pages ********then: !!!!!!!!waitFor!{!at!GoogleResultsPage!} ********and: !!!!!!!!firstResultLink.text()!==!"Wikipedia" ********when: !!!!!!!!firstResultLink.click() ********then: !!!!!!!!waitFor!{!at!WikipediaPage!} !!!!} } 87
    • 4 t r a P Summary
    • Java’s best friend • Java derived syntax – Flat learning curve – Easy to learn • But goes beyond Java – Concise, expressive, readable – Fit for Domain-Specific Languages • Seamless & transparent Java integration – Mix and match Groovy and Java classes (joint compil.) – No language barrier to cross 89
    • Groovy’s nature • Object oriented dynamic language... • 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... 90
    • Groovy use cases • Scripting tasks, build automation • Extension points for customizing/configuring apps • Business languages & Domain-Specific Languages • Full blown apps – for desktop with Griffon – for the web with Grails, Ratpack, Gaelyk – for web reactive programming with reactor 91