1. Intro
1.1. What is Groovy
-
Questionable name
-
Awesome language
-
Flat learning curve (from java)
1.2. Install Groovy
Requirements
Installing Groovy via SDKMAN!:
$ sdk install groovy
1.3. Hello World
$ groovySh
Groovy Shell (2.4.6, JVM: 1.8.0_72)
Type ':help' or ':h' for help.
------------------------------------------------------------------------------
groovy:000> println 'Hello, World!'
Hello, World!
===> null
Groovy Console
2. Syntax
2.1. Java Like
public class HelloWorldGroovy {
public static void main(String[] args) {
System.out.println("Hello, Groovy!");
}
}
This is 100% valid Groovy code |
$ groovy HelloWorldGroovy.groovy
Hello, Groovy!
2.2. Public by default
class HelloWorldGroovy {
void main(String[] args) {
System.out.println("Hello, Groovy!");
}
}
2.3. Semicolons optional
class HelloWorldGroovy {
void main(String[] args) {
System.out.println("Hello, Groovy!")
}
}
2.4. Parenthesis are optional as well
class HelloWorldGroovy {
void main(String[] args) {
System.out.println "Hello, Groovy!"
}
}
2.5. Just get to the point
class HelloWorldGroovy {
void main(String[] args) {
println "Hello, Groovy!"
}
}
2.6. Just say what you need
println "Hello, Groovy!"
3. Strings
3.1. Declare a String
String hello = "Hello"
Double quoted Strings in Groovy are instances of GString
.
GString
allows for String interpolation.
String hello = "Hello"
String greeting = "$hello World" // evaluates to "Hello World"
3.2. Alternative String declarations
String hello = 'Hello'
Single quote strings are not interpolated |
String hello = /Hello/
String hello = $/Hello/$
3.3. Multiline Strings
You can declare multiline Strings using triple single quote, tripe double quote, slashy and dollary slashy strings.
String pizza = '''
Pizza in the morning
Pizza in the evening
Pizza at supper time
'''
String groovy = """
Groovy in the morning
Groovy in the evening
Groovy at supper time
"""
String rocket = /
Prepare for trouble
Make it double
/
String scifi = $/
Use the force Harry
-- Gandalf
/$
println pizza
println groovy
println rocket
println scifi
3.4. String manipulation
String hello = 'Hello'
String message = hello + ' ' + 'World!' (1)
println message // Hello World!
String ello = hello - 'H' (2)
message = "'$ello guvna" (3)
println message //'ello guvna'
1 | Regular old String concatenation |
2 | Remove first instance of 'H' from String hello |
3 | String interpolation |
$ echo HELLO | tr [A-Z] [a-z] (1)
hello
tr
String hello = 'HELLO'.tr(/[A-Z]/, /[a-z]/) (1)
1 | Replace all upper case to lower case |
3.5. String formatting
String message = 'Welcome to GR8ConfUS'
String centered = message.center(24, '-') // --Welcome to GR8ConfUS--
String leftPadded = message.padLeft(24, '-') // ----Welcome to GR8ConfUS
String rightPadded = message.padRight(24, '-') // Welcome to GR8ConfUS----
println centered
println leftPadded
println rightPadded
3.6. Misc functions
String message = 'Hello'
String amplified = message * 10
println amplified // HelloHelloHelloHelloHelloHelloHelloHelloHelloHello
4. Lists
4.1. Creating lists is easy
List emptyList = []
List<String> conferences = ['Greach', 'GR8Conf', 'G3', 'KR8RConf']
4.2. Accessing elements of list
List<String> conferences = ['Greach', 'GR8Conf', 'G3', 'KR8RConf']
// index is size of list - 1
assert conferences[0] == 'Greach'
assert conferences[1] == 'GR8Conf'
assert conferences[2] == 'G3'
assert conferences[3] == 'KR8RConf'
// you can use negative indices as well
assert conferences[-1] == 'KR8RConf'
assert conferences[-2] == 'G3'
assert conferences[-3] == 'GR8Conf'
assert conferences[-4] == 'Greach'
4.3. Mutating the list
List<String> conferences = ['Greach', 'GR8Conf', 'G3', 'KR8RConf']
conferences << 'G3 Summit' (1)
assert conferences[-1] == 'G3 Summit'
assert conferences == ['Greach', 'GR8Conf', 'G3', 'KR8RConf', 'G3 Summit']
conferences = conferences - 'Greach' (2)
assert conferences[0] == 'GR8Conf'
assert conferences == ['GR8Conf', 'G3', 'KR8RConf', 'G3 Summit']
1 | Add G3 Summit to list |
2 | Remove Greach from list and assign resulting list to original list |
5. Maps
5.1. Creating maps is easy
Map emptyMap = [:]
Map<String, String> pokemon = [water: 'Squirtle', flying: 'Pidgey', bug: 'Pinsir'] (1)
1 | Map literal notation with key: 'value' syntax |
5.2. Accessing map entries
Map<String, String> pokemon = [water: 'Squirtle', flying: 'Pidgey', bug: 'Pinsir']
assert pokemon['water'] == 'Squirtle' (1)
assert pokemon.water == 'Squirtle' (2)
1 | Access using bracket notation |
2 | Access using dot notation |
5.3. Mutating the map
Map<String, String> pokemon = [water: 'Squirtle', flying: 'Pidgey', bug: 'Pinsir']
pokemon.water = 'Magikarp' (1)
pokemon.electric = 'Pikachu' (2)
println pokemon // [water:'Magikarp;, flying:'Pidgey', bug:'Pinsir', electric:'Pikachu']
1 | Update water entry of map |
2 | Add new entry to map |
6. Range
A range represents a set of elements between a lower and upper bound.
6.1. Declaring a range
Range oneToTen = 1..10
assert oneToTen == [1, 2 ,3, 4, 5, 6, 7, 8, 9, 10]
6.2. Testing if element is within a range
Range oneToTen = 1..10
assert 5 in oneToTen
Ranges are discrete, not continuous!
Must use < > to assess continuous values
|
Range oneToTen = 1..10
assert !(5.5 in oneToTen)
assert oneToTen.from < 5.5 && 5.5 < oneToTen.to
6.3. Using ranges with Lists
List<String> names = ['Kyle', 'Craig', 'Dane']
List<String> funny = names[0..1] (1)
assert funny == ['Kyle', 'Craig']
List<String> unfunny = names[-1..-1] (2)
assert unfunny == ['Dane']
1 | Take first two elements of list as a new sublist |
2 | Make a new list from last element |
7. Closures
A bit of code that captures environment in which it’s created. It can take arguments.
7.1. Creating closures
Closure c = { println 'Hello' }
c() (1)
c.call() (2)
1 | Invoke closure using parenthesis operator |
2 | Invoke by invoking Closure#call method |
7.2. Closures can return values
Closure greeter = { return 'Hello' }
assert greeter() == 'Hello'
7.3. Closures are aware of enclosing context
String message = 'Hello'
Closure greeter = { return message }
assert greeter() == 'Hello'
7.4. Closures can take arguments
Closure greeter = { message -> return "$message to you" }
assert greeter('Hello') == 'Hello to you'
7.5. Closures can be curried
Closure greeter = { message -> return "$message to you" }
Closure englishGreeter = greeter.curry('Hello') (1)
assert englishGreeter() == 'Hello to you' (2)
Closure spanishGreeter = greeter.curry('Hola') (1)
assert spanishGreeter() == 'Hola to you' (2)
1 | Invoke Closure#curry to return a new Closure with argument frozen in place |
2 | Invoke new Closure with frozen value |
7.6. Closures can be composed
Closure greeter = { message -> return "$message to you" }
Closure formatter = { s -> s.center(20, '*') }
Closure greetThenFormat = formatter << greeter
assert greetThenFormat('Hello') == '****Hello to you****'
Closure formatThenGreet = formatter >> greeter
assert formatThenGreet('Hello') == '*******Hello******** to you'
7.7. Using Closures with Lists
List<String> names = ['Kyle', 'Craig', 'Dane']
names.each { name -> println name } (1)
List<String> upperCased = names.collect { name -> name.toUpperCase() } (2)
assert upperCased == ['KYLE', 'CRAIG', 'DANE']
List<String> funny = names.findAll { name -> name[0] in ['K', 'C'] } (3)
assert funny == ['Kyle', 'Craig']
1 | Consume each element in List and print |
2 | Map each element to upper case and return new List |
3 | Filter for names starting with K or C in List |
7.8. Using Spread operator with List and Closures
For method or closures that take multiple parameters, you can apply a list as arguments to the method or closure
String conferenceAttender(String person, String conference, int year) {
return "Welcome $person to $conference $year!"
}
List<String> me = ['Dan', 'GR8ConfUS', 2016]
assert conferenceAttender('Dan', 'GR8ConfUS', 2016) == conferenceAttender(*me)
assert conferenceAttender('Dan', 'GR8ConfUS', 2016) == 'Welcome Dan to GR8ConfUS 2016!'
assert conferenceAttender(*me) == 'Welcome Dan to GR8ConfUS 2016!'
8. Objects
Objects have nice defaults in Groovy
class Pokemon {
String name
String type
}
This class definition creates setters and getters
class Pokemon {
String name
String type
}
Pokemon rattata = new Pokemon()
rattata.setName('Rattata')
rattata.setType('Normal')
assert rattata.getName() == 'Rattata'
assert rattata.getType() == 'Normal'
These getters and setters can be invoked via dot notation
class Pokemon {
String name
String type
}
Pokemon rattata = new Pokemon()
rattata.name = 'Rattata'
rattata.type = 'Normal'
assert rattata.name == 'Rattata'
assert rattata.type == 'Normal'
You can also use map constructor
class Pokemon {
String name
String type
}
Pokemon rattata = new Pokemon(name: 'Rattata', type: 'Normal')
assert rattata.name == 'Rattata'
assert rattata.type == 'Normal'
8.1. TupleConstructor
import groovy.transform.*
@TupleConstructor
class Pokemon {
String name
String type
}
Pokemon rattata = new Pokemon('Rattata', 'Normal')
assert rattata.name == 'Rattata'
assert rattata.type == 'Normal'
8.2. ToString
import groovy.transform.*
@ToString
@TupleConstructor
class Pokemon {
String name
String type
}
Pokemon rattata = new Pokemon('Rattata', 'Normal')
assert rattata.name == 'Rattata'
assert rattata.type == 'Normal'
assert rattata.toString() == 'Pokemon(Rattata, Normal)'
8.3. EqualsAndHashCode
import groovy.transform.*
@EqualsAndHashCode
@ToString
@TupleConstructor
class Pokemon {
String name
String type
}
Pokemon r1 = new Pokemon('Rattata', 'Normal')
Pokemon r2 = new Pokemon('Rattata', 'Normal')
assert r1 == r2
8.4. Canonical
Canonical == EqAndHashCode + ToString + TupleConstructor
import groovy.transform.*
@Canonical
class Pokemon {
String name
String type
}
Pokemon r1 = new Pokemon('Rattata', 'Normal')
Pokemon r2 = new Pokemon('Rattata', 'Normal')
assert r1 == r2
9. Useful annotations
9.1. CompileStatic
Enables compile time static checks
import groovy.transform.CompileStatic
import groovy.transform.Canonical
@Canonical
@CompileStatic
class Pokemon {
String name
String type
}
Pokemon r = new Pokemon('Rattata', 'Normal')
9.2. Memoized
Caches return value for a given set of arguments to a method or closure call
import groovy.transform.Memoized
@Memoized
int fibonacci(int i) {
if (i == 0) return 0
if (i == 1) return 1
return fibonacci(i - 1) + fibonacci (i - 2)
}
def start = System.nanoTime()
fibonacci(15)
println System.nanoTime() - start // 1161994
start = System.nanoTime()
fibonacci(15)
println System.nanoTime() - start // 403949
10. File IO
Before Java 7’s try-with-resources, Groovy had "execute around" patterns for handling File IO.
Groovy provides methods for handling resources like Writers or Readers and takes care of making sure they’re closed no matter what.
10.1. Reading and writing a file
File secrets = new File('bank-secrets.txt')
secrets.withWriter { writer -> (1)
writer.writeLine 'E Corp: $1 billion'
writer.writeLine 'E Corp: $2 billion'
writer.writeLine 'E Corp: $3 billion'
}
secrets.withReader { reader -> (2)
while (line = reader.readLine()) {
println "Found secret: $line"
}
}
secrets.delete()
1 | Use withWriter with Closure that writes 3 lines to the file |
2 | Use withReader to read lines from the file |
11. Handy Operators
11.1. Null safe dereference
Given two classes we’d like to safely calculate attack probability
class Pokemon {
String name
String type
Attack attack
}
class Attack {
String name
String type
int damage
}
double calculateAttack(Pokemon pkmn) {
if (pkmn != null) {
Attack attack = pkmn.attack
if (attack != null) {
return new Random().nextDouble() * attack.damage
}
}
return -1
}
calculateAttack(new Pokemon(attack: new Attack(damage:5)))
class Pokemon {
String name
String type
Attack attack
}
class Attack {
String name
String type
int damage
}
double calculateAttack(Pokemon pkmn) {
int baseDamage = pkmn?.attack?.damage
return baseDamage ? baseDamage * new Random().nextDouble() : 0
}
calculateAttack(new Pokemon(attack: new Attack(damage:5)))
11.2. Elvis
A terse way to return a default value in face of a falsey value
String greeter (String message) {
return message ?: 'Hello'
}
assert greeter() == 'Hello'
assert greeter('Howdy') == 'Howdy'
class Pokemon {
String name
String type
Attack attack
}
class Attack {
String name
String type
int damage
}
double calculateAttack(Pokemon pkmn) {
int baseDamage = pkmn?.attack?.damage ?: 0
return baseDamage * new Random().nextDouble()
}
calculateAttack(new Pokemon(attack: new Attack(damage:5)))
assert calculateAttack(new Pokemon()) == 0
11.3. Star dot
Executes method on every element in a List
class Pokemon {
String name
String type
Attack attack
}
class Attack {
String name
String type
int damage
}
double calculateAttack(Pokemon pkmn) {
int baseDamage = pkmn?.attack?.damage ?: 0
return baseDamage * new Random().nextDouble()
}
List<Pokemon> pkmn = (1..10).collect { i -> (1)
new Pokemon(
name: 'Bulbasaur',
type: 'Grass',
attack: new Attack(
name: 'Leaf Cutter',
type: 'Grass',
damage: i
)
)
}
List<String> names = pkmn*.name (2)
assert names == ['Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur', 'Bulbasaur']
List<Double> damages = pkmn*.attack.damage (3)
assert damages == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1 | Create a range (1..10) and transform each element into a Pokemon by setting the value as the damage for the attack |
2 | Use Star dot to invoke Pokemon#getName on each element of the list and return as new list |
3 | Use Star dot to invoke Pokemon#getAttack#getDamage on each element of list and return as new list |
11.4. Method reference
Methods and closures are first class objects in Groovy
Use .&
to get a handle to the method or closure from an object
class Pokemon {
String name
String type
Attack attack
}
Pokemon p = new Pokemon(name: 'Eevee')
Closure nameGetter = p.&getName (1)
assert nameGetter() == 'Eevee'
1 | Use .& to dereference method as a closure to pass around |
Closures can be passed by value of reference to methods
class Pokemon {
String name
String type
Attack attack
}
class Attack {
String name
String type
int damage
}
double calculateAttack(Pokemon pkmn) {
int baseDamage = pkmn?.attack?.damage ?: 0
return baseDamage * new Random().nextDouble()
}
List<Pokemon> pkmn = (1..10).collect { i ->
new Pokemon(
name: 'Bulbasaur',
type: 'Grass',
attack: new Attack(
name: 'Leaf Cutter',
type: 'Grass',
damage: i
)
)
}
List<Double> damages = pkmn.collect(this.&calculateAttack) (1)
println damages // [0.9729413212618172, 1.992348079720605, 1.0092590053588697, 3.4810779400638983, 3.955667167493159, 2.063303014744924, 5.943832726036444, 1.05018656250666, 6.461072896131895, 4.267121915844054]
assert damages
1 | In the absence of a owning class, you can use this to dereference the method |
11.5. Spaceship
Used for comparisons
assert 1 <=> 1 == 0
assert 1 <=> 2 == -1
assert 2 <=> 1 == 1
Really handy for things like sorting lists
class Pokemon {
String name
String type
Attack attack
}
class Attack {
String name
String type
int damage
}
double calculateAttack(Pokemon pkmn) {
int baseDamage = pkmn?.attack?.damage ?: 0
return baseDamage * new Random().nextDouble()
}
List<Pokemon> pkmn = (1..10).collect { i ->
new Pokemon(
name: 'Bulbasaur',
type: 'Grass',
attack: new Attack(
name: 'Leaf Cutter',
type: 'Grass',
damage: new Random().nextInt(10) + 1 (1)
)
)
}
pkmn.sort { p1, p2 ->
return p1.attack.damage <=> p2.attack.damage (2)
}
println pkmn*.attack.damage // [1, 3, 3, 3, 4, 4, 5, 6, 6, 7]
1 | Randomize attack damage per pokemon |
2 | Sort list by attack damage ascending |
12. Training your Groovy-fu
13. Real World
14. Web service consumption
Working with the web is a breeze in Groovy
String html = 'http://gr8conf.us'.toURL().text
assert html.length() > 0
assert html.startsWith('<!doctype html> <html class="no-js" ng-app="gr8conf')
@Grab('org.jsoup:jsoup:1.6.1')
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
String html = 'http://gr8conf.us'.toURL().text
Document doc = Jsoup.parse(html) (1)
List<String> links = doc.select('a').collect { link -> (2)
return link.attr('href')
}
println links
1 | Parse raw html to a structured Document |
2 | Use CSS selector a to find all anchor nodes, iterate through them and extract the href attribute from each |
Learn more about JSOUP
import groovy.json.JsonSlurper
String rawJson = 'http://www.reddit.com/r/pokemongo.json'.toURL().getText(requestProperties: ['User-Agent': 'groovy-sample']) (1)
JsonSlurper slurper = new JsonSlurper()
def json = slurper.parseText(rawJson) (2)
List<String> summaries = json.data.children*.data.collect { post -> (3)
return """
Author: $post.author
Score: $post.score
Title: $post.title
"""
}
println summaries.join('-' * 20)
1 | Set user-agent before getting response |
2 | Use JsonSlurper to parse our raw text into a structured object graph |
3 | Use dot notation and spread dot operator to get a high level summary of endpoint |
Author: Juxlos
Score: 3885
Title: Welcome to /r/PokemonGo!
--------------------
Author: PokemonGOmods
Score: 148
Title: "How do I..." and Bugs Megathread - 26/7
--------------------
Author: Kyurun
Score: 5199
Title: Guarantee 1000CP+ Evolutions
--------------------
Author: Brutal_Angel
Score: 1294
Title: Can we at least give credit that for being free app there aren't Ads being jammed into our throat.
--------------------
Author: majorgoober
Score: 4808
Title: Casually browsing SDCC coverage when...
--------------------
Author: Xiiao
Score: 5136
Title: Your coffee is coming, please wait
--------------------
Author: Borgifornia
Score: 4632
Title: I found a geoduo.
--------------------
Author: GreyEagle08
Score: 3426
Title: How I feel playing in the middle of nowhere.
--------------------
Author: tanzanitetnn
Score: 2853
Title: Instinct Players be like:
--------------------
Author: nayfw
Score: 736
Title: This has just been posted to my local PoGo page...
--------------------
Author: Pearlshine1494
Score: 202
Title: When get an excellent throw, but it breaks out at the last second
--------------------
Author: adoseofdanta
Score: 1158
Title: Syther is the Pidgey of La Jolla Cove (San Diego)
--------------------
Author: dalcowboiz
Score: 660
Title: I got rid of this thing you guys keep complaining about
--------------------
Author: silverdollaflapjacks
Score: 3189
Title: My sister saw a familiar scene playing pokemon go at the park this weekend
--------------------
Author: sebs8
Score: 1646
Title: Found this while I was hunting for a Scyther today.
--------------------
Author: Sleepwalks
Score: 412
Title: My friends and I keep finding Sandshrew in the ocean, when we take the ferry or go to the beach. We call them Seashrew, so I decided to draw one.
--------------------
Author: Whosdaman
Score: 6026
Title: My small rural town has one Pokémon Go hotspot, the PD just posted these all around that area
--------------------
Author: Siyliss
Score: 4910
Title: So the game glitched out the other night and make Rhydon and Pinsir look like they were in a Godzilla movie lol. I laughed pretty hard when it happened. Rhydon nailed it.
--------------------
Author: Noticemenot
Score: 5055
Title: Life Before Pokemon GO and After
--------------------
Author: Smileynator
Score: 3350
Title: Why Pokemon Go pisses off the developer inside me.
--------------------
Author: shpitzX
Score: 5196
Title: Oh! it's a cute Jigly..AAAHHHHH!!
--------------------
Author: ZuneNebula
Score: 3021
Title: The Secret of being the strongest trainer ever!!
--------------------
Author: mihitnrun
Score: 132
Title: My girlfriend has started calling transferring Pokémon "harvesting" - I just told her we're going to go to the park for a Poké-walk and this was her response
--------------------
Author: 7omo
Score: 111
Title: Pokéstops need to give higher level trainers more pokéballs!
--------------------
Author: marshmahlow
Score: 2584
Title: We've now had more days of PokemonGO with the three step bug than we had with the original tracking method (U.S.)
--------------------
Author: Kraigius
Score: 1610
Title: The search for intelligent life
--------------------
Author: MacAtack3
Score: 286
Title: An explanation of egg distance. A compsci friend of mine explained this to me yesterday and my eggs have been easier to hatch for it. He said the ping time was ~6 seconds.
14.1. Browser automation
Geb allows you to programmatically control a browser
@Grab('org.gebish:geb-core:0.13.1')
@Grab('org.seleniumhq.selenium:selenium-firefox-driver:2.53.1')
@GrabExclude('org.codehaus.groovy:groovy-all')
import geb.Browser
Browser.drive {
go 'http://gr8conf.us' (1)
js.exec ''' (2)
window.scrollBy(0,700)
'''
assert $('h1')[1].text() == 'Welcome to GR8Conf US 2016!' (3)
$('a[href$="speakers"]')[1].click() (4)
assert $('h1')[1].text() == 'Speakers' (5)
}
1 | Navigate to GR8ConfUS website |
2 | Execute Javascript to scroll down by 700px |
3 | Find second h1 via css selector and assert contents |
4 | Find second a via css selector for speakers and click |
5 | Verify that the page has changed |
More on Geb at http://gebish.org/
14.2. Pokemon Go!
@GrabResolver(name='jitpack', root='https://jitpack.io', m2Compatible='true')
@Grab('com.github.Grover-c13:PokeGOAPI-Java:master-SNAPSHOT')
import com.pokegoapi.api.PokemonGo
import com.pokegoapi.auth.PtcLogin
import okhttp3.OkHttpClient
OkHttpClient http = new OkHttpClient() (1)
def creds = ['user', 'password'] (2)
def auth = new PtcLogin(http).login(*creds) (3)
PokemonGo go = new PokemonGo(auth, http) (4)
1 | Create a new client (comes with PokemonGO Java API) |
2 | Enter user credentials |
3 | Login using Pokemon Trainer Club auth endpoint using spread operator |
4 | Instantiate new PokemonGo |
PokemonGo
is the gateway to interacting with the Pokemon Go API
@GrabResolver(name='jitpack', root='https://jitpack.io', m2Compatible='true')
@Grab('com.github.Grover-c13:PokeGOAPI-Java:master-SNAPSHOT')
import com.pokegoapi.api.PokemonGo
import com.pokegoapi.auth.PtcLogin
import okhttp3.OkHttpClient
OkHttpClient http = new OkHttpClient()
def creds = ['user', 'password']
def auth = new PtcLogin(http).login(*creds)
PokemonGo go = new PokemonGo(auth, http)
def coords = [44.9748004, -93.2776901, 0]
// https://www.google.com/maps/place/44%C2%B058'29.3%22N+93%C2%B016'39.7%22W/@44.9748042,-93.2798788,17z/data=!3m1!4b1!4m5!3m4!1s0x0:0x0!8m2!3d44.9748004!4d-93.2776901
go.setLocation(*coords) (1)
println "Player: $go.playerProfile.username, $go.playerProfile.team, $go.playerProfile.stats"
println "Inventory:"
go.inventories.itemBag.items.each { item -> (2)
println "$item.itemId, $item.count, $item.unseen"
}
def catchablePokemon = go.map.catchablePokemon (3)
println "Pokemon in area:"
catchablePokemon.collect { mon ->
[mon.encounterPokemon(), mon] (4)
}.findAll { encounter, mon ->
encounter.wasSuccessful() (5)
}.each { encounter, mon ->
println mon.pokemonId (6)
}
def nearby = go.map.nearbyPokemon (7)
println "Nearby pokemon:"
nearby.each { n ->
println "$n.pokemonId, $n.distanceInMeters, $n.encounterId"
}
1 | Set current location using spread operator |
2 | Iterate over your inventory |
3 | Find all catchablePokemon |
4 | Invoke encounterPokemon() and pass tuple of the encounter result and pokemon |
5 | Filter for any pokemon successfully encountered |
6 | Print each successfully encountered pokemon’s id |
7 | Find all nearby pokemon |
There are a lot more endpoints available for exploration in the source. Check the examples for more ideas.
14.3. Databases
import groovy.sql.Sql
Sql sql = Sql.newInstance(
url: 'jdbc:h2:mem:db',
driver: 'org.h2.Driver',
user: 'sa', password: '')
sql.execute('create table beer (name char(255))')
def beers = ['Lager', 'Pale Ale', 'Saison', 'Black IPA']
beers.each { b ->
sql.execute('INSERT INTO `beer` (name) VALUES (?)', b) (1)
}
def rows = sql.rows('select * from beer') (2)
assert rows.size() == beers.size()
assert rows.name == beers (3)
1 | Insert each beer name into the db |
2 | Simple select, returns List<GroovyRowResult> |
3 | Use simple dot notation to access name key in each result |
14.4. Desktop Fun
java.awt.Robot for some desktop fun
import java.awt.Robot
Robot r = new Robot() (1)
(300..500).collect { i ->
[i, i] (2)
}.each { args ->
Thread.sleep 30 (3)
r.mouseMove(*args) (4)
}
1 | Create new instance of Robot |
2 | Create pairs of x, y coordinates from 300 to 500 |
3 | Sleep for 30 milliseconds |
4 | Move the cursor by pair of x, y coordinates |
14.5. Creating quick webservices
Ratpack
@Grab('io.ratpack:ratpack-groovy:1.4.0-rc-2')
import static ratpack.groovy.Groovy.ratpack
ratpack {
handlers {
get { (1)
render 'hello, world' (2)
}
get('foo') { (3)
render 'foo' (4)
}
}
}
1 | Define a root handler for GET requests |
2 | Render the String hello, world to the client |
3 | Define a handler for GET /foo |
4 | Render the String foo to the client |
Ratpack binds to port 5050 by default
Try issuing some commands
$ curl localhost:5050 (1)
hello, world
$ curl localhost:5050/foo (2)
foo
Learn more at https://ratpack.io