|
Groovy example source code file (Gep3Test.groovy)
The Groovy Gep3Test.groovy source code
package gls.syntax
import static Container.*
import static Ingredient.*
import static CookingAction.*
import static Temperature.*
/**
* Test case for "extended command expressions" (GEP-3) added in Groovy 1.8
*
* Simple table presenting what is possible and what is not
* according to this table old syntax should works the same as now
*
* expression | meaning | before extended command expressions (GEP-3)
* foo {c} | foo({c}) | (same meaning)
* foo a1 | foo(a1) | (same meaning)
* foo a1() | foo(a1()) | (same meaning)
* foo a1 {c} | foo(a1({c})) | (same meaning)
* foo a1 a2 | foo(a1).getA2() | not allowed
* foo a1() a2 | foo(a1()).getA2() | not allowed
* foo a1 a2() | foo(a1).a2() | not allowed
* foo a1 a2 {c} | foo(a1).a2{c} | not allowed
* foo a1 {c} a2 | foo(a1{c}).getA2() | not allowed
* foo a1 {c} a2 {c} | foo(a1{c}).a2{c} | not allowed
* foo a1 a2 a3 | foo(a1).a2(a3) | not allowed
* foo a1() a2 a3() | foo(a1()).a2(a3()) | not allowed
* foo a1 a2() a3 | foo(a1).a2().getA3() | not allowed
* foo a1 a2 a3 {c} | foo(a1).a2(a3({c})) | not allowed
* foo a1 a2 a3 a4 | foo(a1).a2(a3).getA4() | not allowed
* foo a1 a2 a3 a4 {c} | foo(a1).a2(a3).a4{c} | not allowed
* foo {} a1 {} | foo({}).a1({}) | foo({}).call(a1({})) <- breaking change
* foo {} a1 {} a2 {} | foo({}).a1({}).a2({}) | not allowed
* foo a1 a2[1](){} a3 a4 | foo(a1).a2[1](){}.a3(a4) | not allowed
*
* Summary of the pattern
* - A command-expression is composed of an even number of elements
* - The elements are alternating a method name, and its parameters
* (can be named and non-named parameters)
* - A parameter element can be any kind of expression (ie. a method
* call foo(), foo{}, or some expression like x+y)
* - All those pairs of method name and parameters are actually chained
* method calls (ie. send "hello" to "Guillaume" is two methods chained
* one after the other as send("hello").to("Guillaume"))
* - extend command expressions to be allowed on the RHS of assignments.
* def txt = foo a1() not allowed rigth now
*
* @author Lidia Donajczyk-Lipinska
* @author Jochen "blackdrag" Theodorou
* @author Guillaume Laforge
*/
class Gep3Test extends GroovyTestCase {
protected void tearDown() {
Number.metaClass = null
Integer.metaClass = null
}
static String txt = "Lidia is Groovy ;)"
void testSimpleClassicalCommandExpressions() {
foo txt
foo a1()
foo a2{}
foo a2{}, and: txt
}
static void foo(a) { assert a == txt }
static void foo(Map m, a) { assert a == txt && m.and == txt }
static a1() { return txt }
static a2(Closure c) { return txt }
void testNewSyntax() {
def expectedResult = "from:Lidia;to:Guillaume;body:how are you?;"
def e = new Email()
e.from "Lidia" to "Guillaume" body "how are you?"
def result = e.send()
assert expectedResult == result
}
void testContactInfo() {
def contact = [name: { String name -> assert name == "Guillaume"; [age: { int age -> assert age == 33 }]}]
contact.name "Guillaume" age 33
}
void testArtistPaintingWithAMixOfNamedAndNonNamedParams() {
Number.metaClass.getPm = { -> "$delegate PM" }
def artist = [
paint: { String what ->
assert what == "wall"
[
with: { Map m, String c1, String c2 ->
assert m.and == "Blue"
assert c1 == "Red"
assert c2 == "Green"
[
at: { String time ->
assert time == "3 PM"
}
]
}
]
}
]
artist.paint "wall" with "Red", "Green", and: "Blue" at 3.pm
}
void testArgWith() {
def arr = ["he", "ll", "o"]
def concat = { String s1 -> [with: { String s2 -> [and: { String s3 -> assert s1+s2+s3 == "hello"}]}]}
concat arr[0] with arr[1] and arr[2]
}
void testWaitAndExecuteUsingParamsTakingClosureAsArg() {
Number.metaClass.getSecond { -> delegate * 1000 }
def wait = { int t -> [and: { Closure c -> c() }]}
wait 1.second and execute { assert true }
}
static execute(Closure c) { c }
static String message = ""
static drugQuantity, drug
void testMedicine() {
Number.metaClass.getPills = { -> new DrugQuantity(q: delegate, form: "pills") }
Number.metaClass.getHours = { -> new Duration(q: delegate, unit: "hours") }
def chloroquinine = new Drug(name: "Chloroquinine")
take 3.pills of chloroquinine after 6.hours
assert message == "Take 3 pills of Chloroquinine after 6 hours"
}
def take(DrugQuantity dq) { drugQuantity = dq; this }
def of(Drug d) { drug = d; this }
def after(Duration dur) { message = "Take $drugQuantity of $drug after $dur" }
void testRecipeDsl() {
def (once, twice) = [1, 2]
Integer.metaClass.getMinutes { delegate }
Recipe.instructions {
take medium_bowl
combine soy_sauce, vinegar, chili_powder, garlic
place chicken into sauce
turn once to coat
marinate 30.minutes at room_temperature
}
}
void testExtendedCommandExpressionSpanningTwoLinesWithNewlineAfterNamedArg() {
boolean success = false
def good = true
def margherita = [tastes: { boolean b -> success = true }]
def check = { Map m -> margherita }
check that:
margherita tastes good
assert success
}
def check(Map m) { m.that }
void testExtendedCommandExpressionsOnTheRHS() {
def ( coffee, sugar, milk, liquor ) =
["coffee", "sugar", "milk", "liquor"]
def drink = Drink.&drink
def r1 = drink coffee
assert r1.beverage == coffee && !r1.ingredients
def r2 = drink coffee with sugar
assert r2.beverage == coffee && r2.ingredients == [sugar]
def r3 = drink coffee with sugar, milk
assert r3.beverage == coffee && r3.ingredients == [sugar, milk]
r3 = drink coffee with sugar, milk and liquor
assert r3.beverage == coffee && r3.ingredients == [sugar, milk, liquor]
}
/**
* case a b c d
* equivalent a(b).c(d)
*/
void testNominalCase() {
def turned = false
def (left, right) = ['left', 'right']
def turn = { String s -> [then: { turned = true }] }
turn left then right
assert turned
}
/**
* For odd number of elements, treat the last element as a call to a getter
*
* case a b c
* equivalent a(b).getC()
*/
void testTrailingElementAsGetter() {
def drank = false
def more = 'more'
def drink = { String s -> [milk: { drank = true }()] }
def d = drink more milk
assert drank
}
/**
* Only closure parameters, enabling interesting control constructs
*
* case a {} b {} c {}
* equivalent a({}).b({}).c({})
*
*/
void testOnlyClosureParameters() {
def bdd = 0
def given = { c1 -> c1(); [when: { c2 -> c2(); [then: { c3 -> c3() }] }]}
given {
bdd++
} when {
bdd++
} then {
bdd++
}
assert bdd == 3
}
/**
* If the last method takes no arguments, parentheses are required
*
* case a b c()
* equivalent a(b).c()
*/
void testZeroArgMethodCallAtEndOfChain() {
def built = false
def all = 'all'
def select = { String s -> [build: { built = true }] }
select all build()
assert built
}
/**
* A zero-arg method call in the middle of a chain requires parentheses
*
* case a b c() d e
* equivalent a(b).c().d(e)
*/
void testZeroArgMethodCallInTheMiddleOfTheChain() {
def uploaded = false
def (file, here) = ['file', 'here']
def upload = { String s1 -> [check: {-> [decompress: { String s2 -> uploaded = true }] }]}
upload file check() decompress here
assert uploaded
}
/**
* Case where a middle element of the chain is actually a pretty complex exception
*
* case a b c[1](){} d e
* equivalent a(b).c[1](){}.d(e)
*/
void testComplexCaseWithSubscriptAndMethodCallWithClosureArgument() {
def resolved = false
def (cube, topLayer, down) = ['cube', 3, 'down']
def resolve = { String s1 -> [move: [0, 1, 2, { c -> [upside: { String s2 -> resolved = true }] }]] }
resolve cube move [topLayer]() {} upside down
assert resolved
}
/**
* Case where an Integer is used as name
*
* case a b 1 2
* equivalent a(b)."1"(2)
*/
void testIntegerAsName() {
Integer.metaClass."1" = {x-> assert delegate == 10; x}
def a = {x-> assert x == "b"; 10}
def b = "b"
def x = a b 1 2
assert x == 2
assert a(b)."1"(2) == 2
}
void testMethodAndMethodCallTakingClosureArgument() {
def valued = { it() }
def at = { it }
def res1 = valued at {}
def res2 = valued at { 42 }
assert res1 == null
assert res2 == 42
}
// case a b c d
// equivalent a(b).c(d)
void testTurnLeftThenRight() {
def turned = false
def (left, right) = ['left', 'right']
def turn = { String s -> [then: { turned = true }] }
turn left then right
assert turned
}
}
class Drink {
String beverage
List<String> ingredients = []
static Drink drink(String beverage) {
new Drink(beverage: beverage)
}
def with(String... ingredients) {
this.ingredients = ingredients.toList()
return this
}
def and(String ingredient) {
this.ingredients << ingredient
return this
}
}
enum Container { medium_bowl }
enum Ingredient { soy_sauce, vinegar, chili_powder, garlic, chicken, sauce }
enum CookingAction { coat }
enum Temperature { room_temperature }
class Recipe {
static instructions(Closure c) {
def clone = c.clone()
clone.delegate = new Recipe()
clone.resolveStrategy = Closure.DELEGATE_ONLY
clone()
}
void take(Container cont) {
assert cont == medium_bowl
}
void combine(Ingredient... ingr) {
assert ingr.every { it in Ingredient.values() }
}
def place(Ingredient ingr) {
assert ingr == Ingredient.chicken
[into: { Ingredient otherIngr -> assert otherIngr == Ingredient.sauce}]
}
def turn(Integer i) {
assert i == 1
[to: { CookingAction cAct -> assert cAct == CookingAction.coat }]
}
def marinate(Integer minutes) {
assert minutes == 30
[at: { Temperature temp -> assert temp == Temperature.room_temperature }]
}
}
class Drug {
String name
String toString() { name }
}
class DrugQuantity {
Number q
String form
String toString() { "$q $form" }
}
class Duration {
Number q
String unit
String toString() { "$q $unit" }
}
class Email {
String msg = ""
def from(address) {
msg += "from:" + address + ";"
return this
}
def to(address) {
msg += "to:" + address + ";"
return this
}
def body(text) {
msg += "body:" + text + ";"
return this
}
def send() {
return msg
}
}
Other Groovy examples (source code examples)Here is a short list of links related to this Groovy Gep3Test.groovy source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.