June 25, 2018

Groovy Goodness: Preorder And Postorder Tree Traversal

The Node class in Groovy has the methods depthFirst and breadthFirst to return a collection of Node objects using either depth or breadth first traversal. Since Groovy 2.5.0 we can specify if we want to use preorder (the default) or postorder traversal. Also the methods now accept a Closure that will be invoked for each visited node. The Closure has the current Node as first argument, the second argument is the tree level of the current node.

In the following example we read some XML and then use depthFirst in several ways to visit the tree of nodes:

// We start with a XML node hierarchy.
def xml = '''
        <A>
          <B>
            <D/>
            <E/>
          </B>
          <C>
            <F/>
          </C>
        </A>
        '''
def root = new XmlParser().parseText(xml)

// Preorder traversal is default, but
// we can also specify it with the boolean
// argument of depthFirst method.
assert root.depthFirst(true)
           .collect { node -> node.name() } == ['A', 'B', 'D', 'E', 'C', 'F']
           
// Groovy 2.5.0 adds possibility to
// directly call closure for 
// each node visited where the first
// Closure argument is the node and
// the second argument the level.
def result = []
root.depthFirst { node, level -> result << "$level${node.name()}" }

assert result == ['1A', '2B', '3D', '3E', '2C', '3F']

// Postorder traversal can be specified
// by setting preorder argment to false.
// When used in combination with Closure
// argument we must using named argument
// preorder.
result = []
root.depthFirst(preorder: false) { node -> result << node.name() }

assert result == ['D', 'E', 'B', 'F', 'C', 'A']

In our second example we use the breadthFirst method. This means the nodes for visited per level in the tree:

// Let's create a Node hierarchy.
def builder = NodeBuilder.newInstance()
def root = builder.A {
    B {
        D()
        E()
    }
    C {
        F()
    }
}


// Preorder traversal is default, but
// we can also specify it with the boolean
// argument of breadthFirst method.
assert root.breadthFirst(true)
           .collect { node -> node.name() } == ['A', 'B', 'C', 'D', 'E', 'F']
           
// Groovy 2.5.0 adds possibility to
// directly call closure for 
// each node visited with node and level.
def result = []
root.breadthFirst { node, level -> result << "$level${node.name()}" }

assert result == ['1A', '2B', '2C', '3D', '3E', '3F']

// Postorder traversal is implemented
// as starting at the lowest level and 
// working our way up.
result = []
root.breadthFirst(preorder: false) { node -> result << node.name() }

assert result == ['D', 'E', 'F', 'B', 'C', 'A']

Written with Groovy 2.5.0.

June 22, 2018

Groovy Goodness: Tuples With Up To 9 Items

A tuple is an ordered, immutable list of elements. Groovy supported tuples with one or two elements before Groovy 2.5.0. Since Groovy 2.5.0 we can use tuples with maximal nine items. Groovy added the classes Tuple3 up to Tuple9. The bonus we get compared to an unmodifiable list with elements is that we can use properties like first, second, third, fourth, fifth, sixth, seventh, eighth, ninth to get items at the specified position.

In the following example we use different Tuple classes:

// We can define the types of the elements when we 
// construct a tuple.
def tuple3 = new Tuple3<String, Integer, BigDecimal>('add', 2, 40.0)

// We can use first, second, third properties
// to get values from the tuple.
assert tuple3.first == 'add'
assert tuple3.second == 2
assert tuple3.third == 40.0

// We can use the [index] syntax to get element.
assert tuple3[0] == 'add'  

// Fully typed tuple.
Tuple4<String, Integer, BigDecimal, Integer> tuple4 = 
    new Tuple4<>('subtract', 100, 55.0, 3)
    
assert tuple4.first == 'subtract'
assert tuple4.second == 100
assert tuple4.third == 55.0
assert tuple4.fourth == 3
assert tuple4[-1] == 3

// With subTuple we can get subsequent
// values from the tuple as a new tuple.
assert tuple4.subTuple(2, tuple4.size()) == new Tuple2<BigDecimal, Integer>(55.0, 3)

// We can imagine how to work with Tuple4..Tuple8 :-)
// ...

// Finally a tuple with 9 items.
def tuple9 = new Tuple9('Groovy', 'rocks', 'and', 'is', 'fun', 'to', 'use', 'as', 'language')

assert tuple9.fifth == 'fun'
assert tuple9.sixth == 'to'
assert tuple9.seventh == 'use'
assert tuple9.eighth == 'as'
assert tuple9.ninth == 'language'

// Tuple extends AbstractList, so we can
// use all methods from List as well.
assert tuple9.join(' ') == 'Groovy rocks and is fun to use as language'

Written with Groovy 2.5.0.

Groovy Goodness: Creating Extra Method Supporting Named Arguments Using @NamedVariant Annotation

Groovy supports named arguments for methods. Actually Groovy collects all named arguments (defined using the name followed by a : and the value) into a Map. The Map must be the first parameter of the method to make it all work. Since Groovy 2.5.0 we can use the @NamedVariant AST transformation annotation to let Groovy create a method where the first parameter is a Map to support named arguments for an existing method. The existing method is still available, but Groovy adds an extra method to our generated class.

By default Groovy will make the first parameters of the original method part of the new method supporting named arguments. If the first parameters is a class type, then the properties of the class can be used as named arguments. We can also explicitly define which parameters of our original method should be named arguments using the annotations @NamedParam and @NamedDelegate. These annotations need to defined for each parameter. The newly created method by the AST transformation invokes the original method.

In the following example we use @NamedVariant for the ConferenceMaker.copy method:

import groovy.transform.NamedVariant
import groovy.transform.NamedDelegate

class Conference {
    String name, location
}

class ConferenceMaker {
    // First parameter's properties will be the 
    // named arguments we can use. Optionally
    // we could add @NamedDelegate before the
    // parameter definition.
    @NamedVariant
    static Conference copy(Conference conf) {
        new Conference(name: conf.name, location: conf.location)
    }

    // We use @NamedDelegate to let the properties of the
    // second parameter be named arguments for the newly
    // created method.
    @NamedVariant
    static Conference copy(boolean upper, @NamedDelegate Conference conf) {
        upper ? new Conference(name: conf.name.toUpperCase(), location: conf.location.toUpperCase())
              : new Conference(name: conf.name, location: conf.location)
    }
}


// All properties of the type of the argument (Conference)
// can be used as named arguments.
def greach = ConferenceMaker.copy(name: 'Greach', location: 'Madrid')

assert greach.name == 'Greach'
assert greach.location == 'Madrid'

// Parameter upper in original method definition is not
// part of the named arguments.
def gr8conf = ConferenceMaker.copy(name: 'Gr8Conf', true, location: 'Copenhagen')

assert gr8conf.name == 'GR8CONF'
assert gr8conf.location == 'COPENHAGEN'

In the next example we use @NamedParam to define the parameters that need to be used as named arguments. We can use the attribute required to indicate the named argument must be provided otherwise an error is thrown:

import static groovy.test.GroovyAssert.shouldFail

import groovy.transform.NamedVariant
import groovy.transform.NamedParam

class Conference {
    String name, location
}

class ConferenceMaker {
    // Using @NamedVariant to create
    // also an implementation of the make 
    // method with a Map argument to 
    // support named arguments.
    @NamedVariant
    static Conference make(
        @NamedParam(required = true) String name, 
        @NamedParam String location) {
        
        new Conference(name: name, location: location)
    }
}

// The named argument 'name' is required,
// 'location' is optional.
def gr8Conf = ConferenceMaker.make(name: 'Gr8Conf')

assert gr8Conf.name == 'Gr8Conf'

def required = shouldFail(AssertionError) {
    // If we don't specify the required named
    // argument an exception is thrown.
    ConferenceMaker.make(location: 'Copenhagen')
}
assert required.message.contains("Missing required named argument 'name'.")


// With named arguments the order of the arguments
// is not important.
def gr8ConfEU = ConferenceMaker.make(location: 'Copenhagen', name: 'Gr8Conf')

assert gr8ConfEU.name == 'Gr8Conf'
assert gr8ConfEU.location == 'Copenhagen'


shouldFail(MissingMethodException) {
    // Because the original make method is
    // invoked by the generated named arguments make 
    // method, so we get an exception when
    // the type of the named argument is incorrect.
    ConferenceMaker.make(name: 'G' as char)
}

Written with Groovy 2.5.0.

June 21, 2018

Groovy Goodness: Unmodifiable Collections

When we wanted to create collections in Groovy that were unmodifiable we could use asImmutable. Since Groovy 2.5.0 we can also use the asUnmodifiable method on collections. The method can be applied on all Collection types including Map.

In the following example we use asUnmodifiable on a List and Map:

import static groovy.test.GroovyAssert.shouldFail

// Create List that is unmodifiable.
def list = ['Groovy', 'Gradle', 'Asciidoctor', 'Micronaut'].asUnmodifiable()

shouldFail(UnsupportedOperationException) {
    // We cannot add new items.
    list << 'Java'
}
    
shouldFail(UnsupportedOperationException) {
    // We cannot change items.
    list[0] = 'Java'
}


// Create Map that is unmodifiable.
def data = [name: 'Messages from mrhaki', subject: 'Gr8 stuff'].asUnmodifiable()

shouldFail(UnsupportedOperationException) {
    // We cannot add a new key.
    data.subject = 'Dev subjects'
}
    
shouldFail(UnsupportedOperationException) {
    // We cannot change the value of a key.
    data.blog = true
}

Written with Groovy 2.5.0.

June 19, 2018

Groovy Goodness: Add Map Constructor With Annotation

Since the early days of Groovy we can create POGO (Plain Old Groovy Objects) classes that will have a constructor with a Map argument. Groovy adds the constructor automatically in the generated class. We can use named arguments to create an instance of a POGO, because of the Map argument constructor. This only works if we don't add our own constructor and the properties are not final. Since Groovy 2.5.0 we can use the @MapConstrutor AST transformation annotation to add a constructor with a Map argument. Using the annotation we can have more options to customize the generated constructor. We can for example let Groovy generate the constructor with Map argument and add our own constructor. Also properties can be final and we can still use a constructor with Map argument.

First we look at the default behaviour in Groovy when we create a POGO:

// Simple POGO.
// Groovy adds Map argument
// constructor to the class.
class Person {
    String name
    String alias
    List<String> likes
}

// Create Person object using
// the Map argument constructor.
// We can use named arguments, 
// with the name of key being
// the property name. Groovy
// converts this to Map.
def mrhaki = 
    new Person(
        alias: 'mrhaki',
        name: 'Hubert Klein Ikkink',
        likes: ['Groovy', 'Gradle'])
        
assert mrhaki.alias == 'mrhaki'
assert mrhaki.name == 'Hubert Klein Ikkink'
assert mrhaki.likes == ['Groovy', 'Gradle']


// Sample class with already
// a constructor. Groovy cannot
// create a Map argument constructor now.
class Student {
    String name
    String alias
    
    Student(String name) {
        this.name = name
    }
}


import static groovy.test.GroovyAssert.shouldFail

// When we try to use named arguments (turns into a Map)
// in the constructor we get an exception.
def exception = shouldFail(GroovyRuntimeException) {
    def student = 
        new Student(
            name: 'Hubert Klein Ikkink', 
            alias: 'mrhaki')
}

assert exception.message.startsWith('failed to invoke constructor: public Student(java.lang.String) with arguments: []')
assert exception.message.endsWith('reason: java.lang.IllegalArgumentException: wrong number of arguments')

Now let's use the @MapConstructor annotation in our next example:

import groovy.transform.MapConstructor

@MapConstructor
class Person {
    final String name // AST transformation supports read-only properties.
    final String alias
    List<String> likes
}

// Create object using the Map argument constructor.
def mrhaki = 
    new Person(
        name: 'Hubert Klein Ikkink', 
        alias: 'mrhaki', 
        likes: ['Groovy', 'Gradle'])
        
assert mrhaki.name == 'Hubert Klein Ikkink'
assert mrhaki.alias == 'mrhaki'
assert mrhaki.likes == ['Groovy', 'Gradle']

// Using the annotation the Map argument
// constructor is added, even though we
// have our own constructor as well.
@MapConstructor
class Student {
    String name
    String alias
    
    Student(String name) {
        this.name = name
    }
}

def student = 
    new Student(
        name: 'Hubert Klein Ikkink', 
        alias: 'mrhaki')
        
assert student.name == 'Hubert Klein Ikkink'
assert student.alias == 'mrhaki'

The AST transformation supports several attributes. We can use the attributes includes and excludes to include or exclude properties that will get a value in the Map argument constructor. In the following example we see how we can use the includes attribute:

import groovy.transform.MapConstructor

@MapConstructor(includes = 'name')
class Person {
    final String name 
    final String alias
    List<String> likes
}

// Create object using the Map argument constructor.
def mrhaki = 
    new Person(
        name: 'Hubert Klein Ikkink', 
        alias: 'mrhaki', 
        likes: ['Groovy', 'Gradle'])
        
assert mrhaki.name == 'Hubert Klein Ikkink'
assert !mrhaki.alias 
assert !mrhaki.likes 

We can add custom code that is executed before or after the generated code by the AST transformation using the attributes pre and post. We assign a Closure to these attributes with the code that needs to be executed.

In the next example we set the pre attribute with code that calculates the alias property value if it is not set via the constructor:

// If alias is set in constructor use it, otherwise
// calculate alias value based on name value.
@MapConstructor(post = { alias = alias ?: name.split().collect { it[0] }.join() })
class Person {
    final String name // AST transformation supports read-only properties.
    final String alias
    List<String> likes
}

// Set alias in constructor.
def mrhaki = 
    new Person(
        name: 'Hubert Klein Ikkink', 
        alias: 'mrhaki', 
        likes: ['Groovy', 'Gradle'])
        
assert mrhaki.name == 'Hubert Klein Ikkink'
assert mrhaki.alias == 'mrhaki'
assert mrhaki.likes == ['Groovy', 'Gradle']

// Don't set alias via constructor.
def hubert = 
    new Person(
        name: 'Hubert A. Klein Ikkink')
        
assert hubert.name == 'Hubert A. Klein Ikkink'
assert hubert.alias == 'HAKI'
assert !hubert.likes

Written with Groovy 2.5.0.

June 15, 2018

Groovy Goodness: Implement Interface And Abstract Methods Automatically

A lot of new AST transformation annotations are added in Groovy 2.5.0. One of them is the @AutoImplement annotation. If we apply this annotation to our class dummy implementations for abstract methods in superclasses or methods in implemented interfaces are created. This can be useful to have something in place and then gradually write real implementations for the abstract or interface methods. The transformation will not alter any method that is already implemented by custom code.

When we apply the @AutoImplement annotation the default implementation for an abstract method from a superclass or method from a interface is simple. If the method has a return type the default value of that return type is returned. For example false for a boolean and null for an object type. But the @AutoImplement annotation has some attributes we can use to change the default implementation. We can set the exception attribute and assign a exception type. The implementation of the methods is than to throw that exception when the method is invoked. With the optional message attribute we can set the exception message. Finally we can use the code attribute to define a Closure with statements that will be called as the implementation of abstract and interface methods.

In the following example we have an interface Creator and create several classes that implement this interface and apply the @AutoImplement annotation with different attribute values:

import groovy.transform.AutoImplement
import static groovy.test.GroovyAssert.shouldFail

// Sample class with two simple properties.
@groovy.transform.Canonical
class Course {
    String name, location
}

// Interface with a single method to 
// create an object based on Map argument.
interface Creator<R> {
    R create(Map args)
}


// Use AutoImplement annotation to create object
// implementing the Creator interface. The compiled
// class will have an implementation for the create
// method. The return value is the default of the
// return type, in our case null.
@AutoImplement
class DefaultCourseCreator implements Creator<Course> { }

def defaultCreator = new DefaultCourseCreator()
assert defaultCreator.create(name: 'Groovy', location: 'Tilburg') == null


// When we use the AutoImplement annotation, only
// methods that are not implemented by ourselves
// will have an auto implementation.
@AutoImplement
class ImplCourseCreator implements Creator<Course> { 
    Course create(Map args) {
        new Course(args)
    }
}

def creator = new ImplCourseCreator()
assert creator.create(name: 'Groovy', location: 'Tilburg') == new Course(name: 'Groovy', location: 'Tilburg')


// Instead of returning the default value for the return type,
// we can throw an exception as default implementation.
// We can specify the type of exception and optionally the
// exception message.
@AutoImplement(exception = UnsupportedOperationException, 
               message = 'Not supported by NotSupportedCourseCreator')
class NotSupportedCourseCreator implements Creator<Course> { }

def creatorUnsupported = new NotSupportedCourseCreator()

def exception = shouldFail(UnsupportedOperationException) {
    creatorUnsupported.create(name: 'Groovy 101')
}
assert exception.message == 'Not supported by NotSupportedCourseCreator'


// We can use the code attribute of the AutoImplement annotation to
// specify a Closure. The Closure is used as implementation for the
// method from the interface.
// In this case we log a warning and return null as value. Notice
// we can access the log variable added by the Log annotation in 
// the Closure of the code attribute.
@groovy.util.logging.Log
@AutoImplement(code = { log.warning('Method needs implementation'); return null })
class ImplementCodeCourseCreator implements Creator<Course> { }

def creatorWarning = new ImplementCodeCourseCreator()
assert !creatorWarning.create(name: 'Groovy') // Prints log message "WARNING: Method needs implementation"

Written with Groovy 2.5.0.

June 14, 2018

Groovy Goodness: Customizing JSON Output

Groovy 2.5.0 adds the possibility to customize JSON output via a JsonGenerator instance. The easiest way to turn an object into a JSON string value is via JsonOutput.toJson. This method uses a default JsonGenerator with sensible defaults for JSON output. But we can customize this generator and create JSON output using the custom generator. To create a custom generator we use a builder accessible via JsonGenerator.Options. Via a fluent API we can for example ignore fields with null values in the output, change the date format for dates and ignore fields by their name or type of the value. And we can add a custom converter for types via either an implementation of the conversion as Closure or implementation of the JsonGenerator.Converter interface. To get the JSON string we simple invoke the toJson method of our generator.

In the following example Groovy code we have a Map with data and we want to convert it to JSON. First we use the default generator and then we create our own to customize the JSON output:

// Sample class to be used in JSON.
@groovy.transform.TupleConstructor
class Student { 
    String firstName, lastName
}

def data = 
    [student: new Student('Hubert', 'Klein Ikkink'),
     dateOfBirth: Date.parse('yyyyMMdd', '19730709'),
     website: 'https://www.mrhaki.com'.toURL(),
     password: 'IamSecret',
     awake: Optional.empty(),
     married: Optional.of(true), 
     location: null,
     currency: '\u20AC' /* Unicode EURO */]
     

import groovy.json.JsonGenerator
import groovy.json.JsonGenerator.Converter
        
// Default JSON generator. This generator is used by
// Groovy to create JSON if we don't specify our own. 
// For this example we define the default generator 
// explicitly to see the default output.       
def jsonDefaultOutput = new JsonGenerator.Options().build()
        
// Use generator to create JSON string.
def jsonDefaultResult = jsonDefaultOutput.toJson(data) // Or use JsonOutput.toJson(data)

assert jsonDefaultResult == '{"student":{"firstName":"Hubert","lastName":"Klein Ikkink"},' + 
    '"dateOfBirth":"1973-07-08T23:00:00+0000","website":"https://www.mrhaki.com","password":"IamSecret",' + 
    '"awake":{"present":false},"married":{"present":true},"location":null,"currency":"\\u20ac"}'


// Define custom rules for JSON that will be generated.
def jsonOutput = 
    new JsonGenerator.Options()
        .excludeNulls()  // Do not include fields with value null.
        .dateFormat('EEEE dd-MM-yyyy', new Locale('nl', 'NL')) // Set format for dates.
        .timezone('Europe/Amsterdam') // Set timezone to be used for formatting dates.
        .excludeFieldsByName('password')  // Exclude fields with given name(s). 
        .excludeFieldsByType(URL)  // Exclude fields of given type(s).
        .disableUnicodeEscaping()  // Do not escape UNICODE.
        .addConverter(Optional) { value -> value.orElse('UNKNOWN') } // Custom converter for given type defined as Closure.
        .addConverter(new Converter() {  // Custom converter implemented via Converter interface.
        
            /**
             * Indicate which type this converter can handle.
             */
            boolean handles(Class<?> type) { 
                return Student.isAssignableFrom(type)
            }
            
            /**
             * Logic to convert Student object.
             */
            Object convert(Object student, String key) {
                "$student.firstName $student.lastName"
            }
            
        })
        .build()  // Create the converter instance.

// Use generator to create JSON from Map data structure.
def jsonResult = jsonOutput.toJson(data)

assert jsonResult == '{"student":"Hubert Klein Ikkink",' + 
    '"dateOfBirth":"maandag 09-07-1973",' + 
    '"awake":"UNKNOWN","married":true,"currency":"€"}'

The JsonBuilder and StreamingJsonBuilder classes now also support the use of a JsonGenerator instance. The generator is used when the JSON output needs to be created. The internal data structure of the builder is not altered by using a custom generator.

In the following example we use the custom generator of the previous example and apply it with a JsonBuilder and StreamingJsonBuilder instance:

import groovy.json.JsonBuilder

// We can use a generator instance as constructor argument
// for JsonBuilder. The generator is used when we create the
// JSON string. It will not effecct the internal JSON data structure.
def jsonBuilder = new JsonBuilder(jsonOutput)
jsonBuilder {
    student new Student('Hubert', 'Klein Ikkink')
    dateOfBirth Date.parse('yyyyMMdd', '19730709')
    website 'https://www.mrhaki.com'.toURL()
    password 'IamSecret'
    awake Optional.empty()
    married Optional.of(true)
    location null
    currency  '\u20AC' 
}

def jsonBuilderResult = jsonBuilder.toString()

assert jsonBuilderResult == '{"student":"Hubert Klein Ikkink",' + 
    '"dateOfBirth":"maandag 09-07-1973",' + 
    '"awake":"UNKNOWN","married":true,"currency":"€"}'

// The internal structure is unaffected by the generator.
assert jsonBuilder.content.password == 'IamSecret'
assert jsonBuilder.content.website.host == 'www.mrhaki.com'


import groovy.json.StreamingJsonBuilder

new StringWriter().withWriter { output -> 

    // As with JsonBuilder we can provide a custom generator via
    // the constructor for StreamingJsonBuilder.
    def jsonStreamingBuilder = new StreamingJsonBuilder(output, jsonOutput)
    jsonStreamingBuilder {
        student new Student('Hubert', 'Klein Ikkink')
        dateOfBirth Date.parse('yyyyMMdd', '19730709')
        website 'https://www.mrhaki.com'.toURL()
        password 'IamSecret'
        awake Optional.empty()
        married Optional.of(true)
        location null
        currency  '\u20AC' 
    }

    def jsonStreamingBuilderResult = output.toString()
    
    assert jsonStreamingBuilderResult == '{"student":"Hubert Klein Ikkink",' + 
        '"dateOfBirth":"maandag 09-07-1973",' + 
        '"awake":"UNKNOWN","married":true,"currency":"€"}'
}

Written with Groovy 2.5.0.

June 13, 2018

Groovy Goodness: Remove Last Item From List Using RemoveLast Method (And Pop/Push Methods Reimplemented)

Versions of Groovy before 2.5.0 implemented pop and push methods for the List class for items at the end of a List object. The pop method removed the last item of a List and push added a item to the List. Groovy 2.5.0 reimplemented the methods so they now work on the first item of a List instance. To remove an item from the end of the list we can use the newly added method removeLast.

In the following example Groovy code we use the removeLast and add methods to remove and add items to the end of the list. And with the pop and push methods we remove and add items to the beginnen of the list:

def list = ['Groovy', 'is', 'great!']
 
// Remove last item from list
// with removeLast().
assert list.removeLast() == 'great!'
assert list == ['Groovy', 'is']
 
// Remove last item which is now 'is'.
list.removeLast()
 
// Add new item to end of the list.
list.add 'rocks!'
 
assert list.join(' ') == 'Groovy rocks!'


/* IMPORTANT */
/* pop() and push() implementations has changed */
/* in Groovy 2.5.0. They now work on the first */
/* item in a List instead of the last. */

// Using pop() we remove the first item
// of a List.
assert list.pop() == 'Groovy'

// And with push we add item to 
// beginning of a List.
list.push 'Spock'

assert list.join(' ') == 'Spock rocks!'

Written with Groovy 2.5.0.

Groovy Goodness: Getting All Init And Tail Values Recursively

For a long time we could get the tail or init values for a collection. Groovy 2.5.0 adds the methods inits and tails for Iterable objects. These methods return a List with List values where the first element is the original collection and the next is the result of init or tail on the previous element. This is repeated until the result of init or tail is an empty List.

In the next example script we have a original collection of letters. We first run the init and tail methods (without the s). Next we look at the result of invoking inits and tails:

def letters = ('a'..'d').toList()

assert letters == ['a', 'b', 'c', 'd']

assert letters.init() == ['a', 'b', 'c']
assert letters.tail() == ['b', 'c', 'd']

// Inits returns collection of all init()
// results for an Iterable. The first element
// has the original values, the next element
// the result of init()
// of the previous element and so on until
// an empty List is the result.
assert letters.inits() == [
    ['a', 'b', 'c', 'd'], 
    ['a', 'b', 'c'], 
    ['a', 'b'], 
    ['a'], 
    []]

// Tails returns collection of all tail()
// results for an Iterable. The first element
// has the original values, the next element
// the result of tail()
// of the previous element and so on until
// an empty List is the result.
assert letters.tails() == [
    ['a', 'b', 'c', 'd'], 
    ['b', 'c', 'd'], 
    ['c', 'd'], 
    ['d'], 
    []]

Written with Groovy 2.5.0.

Groovy Goodness: Truncate And Round BigDecimal Values

Groovy 2.5.0 adds round and truncate methods to the BigDecimal class. These methods were already available on Double and Float classes. The methods can take an argument to denote the number of decimals the rounding or truncating must be applied to.

In the following example we see the methods with and without arguments:

def bigDecimal = 42.576135

// Groovy uses BigDecimal for decimal 
// numbers by default.
assert bigDecimal.class.name == 'java.math.BigDecimal'

assert bigDecimal.round() == 43
assert bigDecimal.round(2) == 42.58

assert bigDecimal.trunc() == 42
assert bigDecimal.trunc(2) == 42.57

Written with Groovy 2.5.0.