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

595 views
510 views

Published on

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é.

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
595
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
15
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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

  1. 1. What makes Groovy groovy? Guillaume Laforge @glaforge © 2013 Guillaume Laforge. All rights reserved. Do not distribute without permission.
  2. 2. Guillaume Laforge Groovy project lead at . @glaforge http://glaforge.appspot.com
  3. 3. Les Cast Codeurs
  4. 4. 1 t r a P The Groovy vision
  5. 5. Simplify the life of (Java) developers
  6. 6. Great for scripting
  7. 7. Great for scripting Fit for DomainSpecific Languages
  8. 8. Great for scripting Fit for DomainSpecific Languages Most seamless integration & interoperability wih java!
  9. 9. It’s so easy to learn!
  10. 10. Groovy as a Java superset It’s so easy to learn!
  11. 11. 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 !!!!} }
  12. 12. As safe and fast as Java with static type checking & compilation
  13. 13. As safe and fast as Java with static type checking & compilation
  14. 14. new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!rocks!" !!!!!!!!} !!!!} }
  15. 15. move!forward!at!3.km/h Expressive, Concise, Readable
  16. 16. Speaking of conciseness... A full Spring app in the span of a tweet! @RestController class!App!{ !!!!@RequestMapping("/") !!!!String!home()!{!"Hello!World!"!} }
  17. 17. • 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?
  18. 18. 2 t r a P Cool Groovy gems
  19. 19. Most Java code is also valid Groovy code!
  20. 20. Most Java code is also valid Groovy code! Any Java developer is a Groovy developer!
  21. 21. Flat learning curve
  22. 22. Flat learning curve Easy to learn
  23. 23. Scripts versus Classes public!class!Main!{ !!!!public!static!void!main(String[]!args)!{ !!!!!!!!System.out.println("Hello"); !!!!} } vs 24
  24. 24. Scripts versus Classes public!class!Main!{ !!!!public!static!void!main(String[]!args)!{ !!!!!!!!System.out.println("Hello"); !!!!} } vs println!"Hello" 24
  25. 25. Optional
  26. 26. Semicolons Optional
  27. 27. Semicolons Parentheses Optional
  28. 28. Semicolons Parentheses Optional return keyword
  29. 29. Semicolons Parentheses Optional return keyword public keyword
  30. 30. Semicolons Parentheses Optional Typing! return keyword public keyword
  31. 31. 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
  32. 32. 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
  33. 33. 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
  34. 34. 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
  35. 35. 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
  36. 36. 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
  37. 37. 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
  38. 38. 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
  39. 39. 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
  40. 40. 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
  41. 41. 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
  42. 42. 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
  43. 43. 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
  44. 44. 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
  45. 45. 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
  46. 46. 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
  47. 47. 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
  48. 48. 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
  49. 49. 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
  50. 50. 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
  51. 51. 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
  52. 52. 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
  53. 53. 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
  54. 54. 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
  55. 55. 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
  56. 56. 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
  57. 57. Native syntax constructs • Closures • Lists • Maps • Regular expressions • Ranges 39
  58. 58. Closures • Functions as first-class citizen of the language 40
  59. 59. 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
  60. 60. 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
  61. 61. 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
  62. 62. 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
  63. 63. Maps def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') 42
  64. 64. Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') 42
  65. 65. Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') Indexed access 42
  66. 66. Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') Property notation access 42 Indexed access
  67. 67. 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
  68. 68. 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
  69. 69. 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
  70. 70. 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
  71. 71. 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
  72. 72. 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
  73. 73. 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
  74. 74. 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
  75. 75. 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
  76. 76. 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
  77. 77. 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
  78. 78. 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
  79. 79. 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
  80. 80. Surprising numbers... System.out.println(!2.0!c!1.1!); 46
  81. 81. Surprising numbers... System.out.println(!2.0!c!1.1!); 0.8999999999999999 46
  82. 82. Surprising numbers... System.out.println(!2.0!c!1.1!); 0.8999999999999999 46
  83. 83. Surprising numbers... System.out.println(!3!/!2!); 47
  84. 84. Surprising numbers... System.out.println(!3!/!2!); 1 47
  85. 85. Surprising numbers... System.out.println(!3!/!2!); 1 47
  86. 86. BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 48
  87. 87. 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
  88. 88. 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
  89. 89. Powerful switch / case on steroids
  90. 90. 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" }
  91. 91. Named arguments move!obj,!x:!3,!y:!4 50
  92. 92. Named arguments move!obj,!x:!3,!y:!4 Normal argument 50
  93. 93. Named arguments move!obj,!x:!3,!y:!4 Normal argument 50 Named argument
  94. 94. Named arguments move!obj,!x:!3,!y:!4 Normal argument Calls: move(Map m, Object) 50 Named argument
  95. 95. Command chains • Ability to chain method calls without parentheses and dots 51
  96. 96. Command chains • Ability to chain method calls without parentheses and dots move!forward!at!3.km/h 51
  97. 97. 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
  98. 98. Named arguments and command chains check!that:!vodka!tastes!good 52
  99. 99. Named arguments and command chains check!that:!vodka!tastes!good Will call: check(that: vodka).tastes(good) 52
  100. 100. 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
  101. 101. 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
  102. 102. 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
  103. 103. 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
  104. 104. 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
  105. 105. 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
  106. 106. 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
  107. 107. 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
  108. 108. Builders and GPath expressions import!groovy.xml.* new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} !!!!} } 55
  109. 109. 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
  110. 110. 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
  111. 111. 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
  112. 112. Power asserts def!(a,!b,!c)!=![20,!30,!40] assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 56
  113. 113. 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
  114. 114. Null handling • Groovy provides a more descriptive NullPointerException than Java • Safe navigation with ?. 57
  115. 115. The Truth, the Groovy Truth!
  116. 116. The Truth, the Groovy Truth! And what if I could customize the truth?
  117. 117. ?:
  118. 118. ?: The Elvis operator!
  119. 119. Towards Elvis... 60
  120. 120. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] 60
  121. 121. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y 60
  122. 122. 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
  123. 123. 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
  124. 124. 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
  125. 125. 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
  126. 126. 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
  127. 127. 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!
  128. 128. 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!
  129. 129. 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
  130. 130. 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
  131. 131. Lots of AST transformations... • Safer scripting – @ConditionalInterrupt, @ThreadInterrupt, @TimedInterupt • Compiler directives – @Field, @PackageScope, @AnnotationCollector, @DelegatesTo, @TypeChecked, @CompileStatic, @CompileDynamic • Swing patterns – @Bindable, @ListenerList, @Vetoable 63
  132. 132. Lots of AST transformations... • Dependencies handling – @Grab, @GrabConfig, @GrabExclude, @GrabResolver • Test assistance – @NotYetImplemented, @ASTTest 64
  133. 133. 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
  134. 134. 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
  135. 135. 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
  136. 136. 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
  137. 137. 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
  138. 138. @Immutable import!groovy.transform.* @Immutable class!Person!{ !!!!String!name !!!!int!age } 67
  139. 139. 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
  140. 140. 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
  141. 141. Groovy allows you to be lazy
  142. 142. Groovy allows you to be lazy The compiler will do the job for you
  143. 143. Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you
  144. 144. 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
  145. 145. @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
  146. 146. @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
  147. 147. @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!
  148. 148. @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
  149. 149. 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
  150. 150. 3 t r a P Superb community!
  151. 151. A blossoming Ecosystem
  152. 152. M V G
  153. 153. GVM
  154. 154. GVM GROOVY ENVIRONMENT MANAGER
  155. 155. 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
  156. 156. I’m Spock...
  157. 157. I’m Spock... ...the Spock testing framework
  158. 158. I’m Spock... ...the Spock testing framework
  159. 159. 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
  160. 160. 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
  161. 161. 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
  162. 162. 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
  163. 163. 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
  164. 164. 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
  165. 165. @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" !!!!} }
  166. 166. @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" !!!!} }
  167. 167. 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
  168. 168. 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
  169. 169. 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
  170. 170. 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
  171. 171. 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
  172. 172. 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
  173. 173. 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
  174. 174. 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
  175. 175. 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
  176. 176. 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
  177. 177. 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
  178. 178. 4 t r a P Summary
  179. 179. 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
  180. 180. 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
  181. 181. 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

×