Search

Dark theme | Light theme
Showing posts with label GroovyGoodness:DSL. Show all posts
Showing posts with label GroovyGoodness:DSL. Show all posts

March 30, 2017

Groovy Goodness: Redirecting Print Methods In Scripts

To run external Groovy scripts in our Java or Groovy application is easy to do. For example we can use GroovyShell to evaluate Groovy code in our applications. If our script contains print methods like println we can redirect the output of these methods. The Script class, which is a base class to run script code, has an implementation for the print, printf and println methods. The implementation of the method is to look for a property out, either as part of a Script subclass or in the binding added to a Script class. If the property out is available than all calls to print, printf and println methods are delegated to the object assigned to the out property. When we use a PrintWriter instance we have such an object, but we could also write our own class with an implementation for the print methods. Without an assignment to the out property the fallback is to print on System.out.

In the following example we have a external script defined with the variable scriptText, but it could also be a file or other source with the contents of the script we want to run. We assign our own PrintWriter that encapsulates a StringWriter to capture all invocations to the print methods:

// Groovy script to execute.
def scriptText = '''
def s = "Groovy rocks!"

// Print value of s.
println s

// Use printf for formatted printing.
printf 'The answer is %X', 42
'''

// Assign new PrintWriter to "out"
// variable of binding object.
def stringWriter = new StringWriter()
def shellBinding = new Binding(out: new PrintWriter(stringWriter))

// Create GroovyShell to evaluate script.
def shell = new GroovyShell(shellBinding)

// Run the script.
shell.evaluate(scriptText)

// Check the output of print, println and printf methods.
assert stringWriter.toString() == 'Groovy rocks!\nThe answer is 2A'

Another option is to directory set the out property of a Script object:

def scriptText = '''
def s = "Groovy rocks!"

// Print value of s.
println s

// Use printf for formatted printing.
printf 'The answer is %X', 42
'''

def shell = new GroovyShell()

// Parse script text and return Script object.
def script = shell.parse(scriptText)

// Assign new PrintWriter to "out"
// variable of Script class.
def stringWriter = new StringWriter()
script.out = new PrintWriter(stringWriter)

// Run the script.
script.run()

// Check the output of print, println and printf methods.
assert stringWriter.toString() == 'Groovy rocks!\nThe answer is 2A'

Written with Groovy 2.4.10.

November 20, 2013

Groovy Goodness: Set Delegating Class for Script

Since Groovy 2.2 we can assign the DelegatingScript class to a Script as a base script class. We use the CompilerConfiguration to set the base script class. After we have assigned the DelegatingScript class we can then set a delegate class for the Script. And this delegate class is then used to evaluate the script contents. This is a very easy setup for DSL scripts.

import org.codehaus.groovy.control.CompilerConfiguration
 
// Simple Car class to save state and distance.
class Car {
    String state
    Long distance = 0
}

// Custom Script with methods that change the Car's state.
// The Car object is passed via the constructor.
class CarScript {

    private final car
    
    CarScript(final car) {
        this.car = car
    }

    def start() {
        car.state = 'started'
    }

    def stop() {
        car.state = 'stopped'
    }

    def drive(distance) {
        car.distance += distance
    }
}

// Create CompilerConfiguration and assign
// the DelegatingScript class as the base script class.
def compilerConfiguration = new CompilerConfiguration()
compilerConfiguration.scriptBaseClass = DelegatingScript.class.name

// Configure the GroovyShell and pass the compiler configuration.
def shell = new GroovyShell(this.class.classLoader, new Binding(), compilerConfiguration)


// Simple DSL to start, drive and stop the car.
// The methods are defined in the CarScript class.
def carDsl = '''
start()
drive 200
stop()
'''


// Define Car object here, 
// so we can use it in assertions later on.
def car = new Car()

// Define CarScript instance so we can use it as delegate
// for the script.
def carScript = new CarScript(car)

// Set delegate carScript to script, so the DSL
// can be executed.
def script = shell.parse(carDsl)
script.setDelegate(carScript)

// Run DSL script.
script.run()

// Checks to see that Car object has changed.
assert car.distance == 200
assert car.state == 'stopped'

Code written with Groovy 2.2.

November 19, 2013

Groovy Goodness: Set Script Class with BaseScript Annotation

Groovy is of course great for writing DSLs. We can write our own Script class to implement a DSL that can be used when we evaluate a script. We can set the custom Script class via CompilerConfiguration. Since Groovy 2.2 we can use a new AST transformation @BaseScript. We must use this annotation in the script and set it on the custom script class. The name of the variable is not important, but the type must by the custom script class.

In the following DSL script we use the @BaseScript instead of the CompilerConfiguration as shown in previous blog post.

// Simple Car class to save state and distance.
class Car {
    String state
    Long distance = 0
}

// Custom Script with methods that change the Car's state.
// The Car object is passed via the binding.
abstract class CarScript extends Script {
    def start() {
        this.binding.car.state = 'started'
    }

    def stop() {
        this.binding.car.state = 'stopped'
    }

    def drive(distance) {
        this.binding.car.distance += distance
    }
}


// Define Car object here, so we can use it in assertions later on.
def car = new Car()
// Add to script binding (CarScript references this.binding.car).
def binding = new Binding(car: car)

// Configure the GroovyShell.
def shell = new GroovyShell(this.class.classLoader, binding)

// Simple DSL to start, drive and stop the car.
// The methods are defined in the CarScript class.
def carDsl = '''
start()
drive 20
stop()
'''


// Run DSL script.
shell.evaluate """
// Use BaseScript annotation to set script
// for evaluating the DSL.
@groovy.transform.BaseScript CarScript carScript

$carDsl
"""

// Checks to see that Car object has changed.
assert car.distance == 20
assert car.state == 'stopped'

Code written with Groovy 2.2.

May 28, 2013

Groovy Goodness: @DelegatesTo For Type Checking DSL

Groovy 2.1 introduced the @DelegatesTo annotation. With this annotation we can document a method and tell which class is responsible for executing the code we pass into the method. If we use @TypeChecked or @CompileStatic then the static type checker of the compiler will use this information to check at compile-time if the code is correct. And finally this annotation allows an IDE to give extra support like code completion.

Suppose we have the following class Reservation with the method submit(). The method accepts a closure with methods that need to be applied with the instance of the Reservation class. This is a very common pattern for writing simple DSLs in Groovy.

class Reservation {
    private Date date
    private String event
    private String attendee
    
    void date(final Date date) { this.date = date }
    void event(final String event) { this.event = event }
    void attendee(final String attendee) { this.attendee = attendee }
    
    /** Submit a reservation. 
      * @param config Configuration for reservation, invoking method class on Reservation.
      */
    static void submit(final Closure config) {
        final Reservation reservation = new Reservation()
        reservation.with config
    }

}

class Event {
    /** Use Reservation configuration DSL to submit a reservation. */
    void submitReservation() {
        Reservation.submit {
            date Date.parse('yyyyMMdd', '20130522')
            event 'Gr8Conf'
            attendee 'mrhaki'
            reserved true
        }
    }
}

final event = new Event()
event.submitReservation()

When we look at the code we might already see there is an error. In the Event.submitReservation() method we have the line reserved true, which will try to invoke the reserve() method of the Reservation class. But that method is not defined. When we run the application we get the expected error:

Exception thrown
May 28, 2013 6:58:36 AM org.codehaus.groovy.runtime.StackTraceUtils sanitize
WARNING: Sanitizing stacktrace:
groovy.lang.MissingMethodException: No signature of method: Reservation.reserved() is applicable for argument types: (java.lang.Boolean) values: [true]

To get an error for this line at compile-time we must add some annotation so the Groovy compiler can do static type checking on our code. We change the code and get the following sample:

import groovy.transform.*

class Reservation {
    private Date date
    private String event
    private String attendee
    
    void date(final Date date) { this.date = date }
    void event(final String event) { this.event = event }
    void attendee(final String attendee) { this.attendee = attendee }
    
    /** Submit a reservation. 
      * @param config Configuration for reservation, invoking method class on Reservation.
      */
    static void submit(@DelegatesTo(Reservation) final Closure config) {
        final Reservation reservation = new Reservation()
        reservation.with config
    }

}

@TypeChecked
// @CompileStatic - will also do static type checking
class Event {
    /** Use Reservation configuration DSL to submit a reservation. */
    void submitReservation() {
        Reservation.submit {
            date Date.parse('yyyyMMdd', '20130522')
            event 'Gr8Conf'
            attendee 'mrhaki'
            reserved true
        }
    }
}

final event = new Event()
event.submitReservation()

When the code is compiled we immediately get a compilation error:

1 compilation error:

[Static type checking] - Cannot find matching method Event#reserved(boolean). Please check if the declared type is right and if the method exists.
 at line: 26, column: 13

Wow, this useful! We find errors in our DSL before the code is run.

When we create this code in an IDE like IntelliJ IDEA we also get code completion in the Reservation.submit() method invocation in the Event class. The following screenshot shows code completion and a red font for reserved to indicate the compilation error.


Code written in Groovy 2.1.3

June 22, 2012

Groovy Goodness: Multiple Overloaded Operator Methods for Nice API

We saw earlier that Groovy supports Multimethods or multiple dispatch. Groovy will resolve the correct overloaded method to invoke based on the runtime type of the method argument. This is great to use for a clean and simple API for a class.

Suppose we have an Event class with a list of attendees and sessions. We want to use the << operator to add attendees and sessions to an Event object. The following code sample shows how we can implement this in Groovy:

class Event {
    List<Person> attendees = []
    List<Session> sessions = []

    Event leftShift(final Person person) {
        attendees << person
        this
    }
    
    Event leftShift(final Session session) {
        sessions << session
        this
    }  
}
class Session { String title }
class Person { String name }

final Event gr8Conf = new Event()
gr8Conf << new Person(name: 'mrhaki') << new Session(title: /Groovy's Hidden Gems/)

assert gr8Conf.attendees.size() == 1
assert gr8Conf.sessions.size() == 1
assert gr8Conf.attendees[0].name == 'mrhaki'
assert gr8Conf.sessions[0].title == "Groovy's Hidden Gems"

November 23, 2011

Groovy Goodness: Magic Package to Add Custom MetaClass

Groovy is very dynamic. We can add methods to classes at runtime that don't exist at compile time. We can add our own custom MetaClass at startup time of our application if we follow the magic package naming convention. The naming convention is groovy.runtime.metaclass.[package].[class]MetaClass. For example if we want to change the behavior of the java.lang.String class, then we must write a custom MetaClass with the package name groovy.runtime.metaclass.java.lang and class name StringMetaClass. We can do the same for classes we create ourselves. For example if we have a class myapp.RunApp than the custom metaclass implementation RunAppMetaClass would be in package groovy.runtime.metaclass.myapp.

Our custom MetaClass is extended from DelegatingMetaClass and besides the name of the class and the package we can write our code the way we want.

We must first compile the custom MetaClass and then we must put it in the classpath of the application code that is going to use the MetaClass.

// File: StringMetaClass.groovy
package groovy.runtime.metaclass.java.lang

class StringMetaClass extends DelegatingMetaClass {

    StringMetaClass(MetaClass meta) {
        super(meta)
    }

    Object invokeMethod(Object object, String method, Object[] arguments) {
        if (method == 'hasGroovy') {
            object ==~ /.*[Gg]roovy.*/
        } else {
            super.invokeMethod object, method, arguments
        }
    }
}

The code that will use the delegating metaclass implementation:

// File: StringDelegateSample.groovy

// Original methods are still invoked.
assert 'mrhaki'.toUpperCase() == 'MRHAKI'

// Invoke 'hasGroovy' method we added via the DelegatingMetaClass.
assert !'Java'.hasGroovy()
assert 'mrhaki loves Groovy'.hasGroovy()
assert 'Groovy'.toLowerCase().hasGroovy()

First we compile our StringMetaClass:
$ groovyc StringMetaClass.groovy.

Next we can run the StringDelegateSample.groovy file if we put the generated class file in our classpath:
$ groovy -cp . StringDelegateSample


November 17, 2011

Groovy Goodness: Create Simple Builders with Closures

In Groovy we can use pre-defined builders like the JsonBuilder or MarkupBuilder to create data or text structures. It is very easy to create our own builder simply with closures. A node in the builder is simply a method and we can use a closure as the argument of the method to create a new level in the builder hierarchy.

We can use pre-defined method names in our builder syntax, but can also use dynamic or unkown method names by implementing the methodMissing method. The same goes for properties we can implement with real property methods or by implementing the propertyMissing method.

In our example we create a new builder to create a Reservation object for a travel flight. In the builder we can define a list of passengers, the destination airport, the departing airport and if the flight is a two-way flight.

// Builder syntax to create a reservation with passengers,
// departing and destination airport and make it a 2-way flight.
def reservation = new ReservationBuilder().make {
    passengers {
        name 'mrhaki'
        name 'Hubert A. Klein Ikkink'
    }
    from 'Schiphol, Amsterdam'
    to 'Kastrup, Copenhagen'
    retourFlight
}

assert reservation.flight.from == new Airport(name: 'Schiphol', city: 'Amsterdam')
assert reservation.flight.to == new Airport(name: 'Kastrup', city: 'Copenhagen')
assert reservation.passengers.size() == 2
assert reservation.passengers == [new Person(name: 'mrhaki'), new Person(name: 'Hubert A. Klein Ikkink')]
assert reservation.retourFlight


// ----------------------------------------------
// Builder implementation and supporting classes.
// ----------------------------------------------
import groovy.transform.*

@Canonical
class Reservation {
    Flight flight = new Flight()
    List<Person> passengers = []
    Boolean retourFlight = false
}

@Canonical
class Person { String name }

@Canonical
class Airport { String name, city }

@Canonical
class Flight { Airport from, to }

// The actual builder for reservations.
class ReservationBuilder {
    // Reservation to make and fill with property values.
    Reservation reservation

    private Boolean passengersMode = false

    Reservation make(Closure definition) {
        reservation = new Reservation()

        runClosure definition

        reservation
    }

    void passengers(Closure names) {
        passengersMode = true

        runClosure names

        passengersMode = false
    }

    void name(String personName) {
        if (passengersMode) {
            reservation.passengers << new Person(name: personName)
        } else {
            throw new IllegalStateException("name() only allowed in passengers context.")
        }
    }

    def methodMissing(String name, arguments) {
        // to and from method calls will set flight properties
        // with Airport objects.
        if (name in ['to', 'from']) {
            def airport = arguments[0].split(',')
            def airPortname = airport[0].trim()
            def city = airport[1].trim()
            reservation.flight."$name" = new Airport(name: airPortname, city: city)
        }
    }

    def propertyMissing(String name) {
        // Property access of retourFlight sets reservation
        // property retourFlight to true.
        if (name == 'retourFlight') {
            reservation.retourFlight = true
        }
    }

    private runClosure(Closure runClosure) {
        // Create clone of closure for threading access.
        Closure runClone = runClosure.clone()

        // Set delegate of closure to this builder.
        runClone.delegate = this

        // And only use this builder as the closure delegate.
        runClone.resolveStrategy = Closure.DELEGATE_ONLY

        // Run closure code.
        runClone()
    }

}

Groovy Goodness: Create Our Own Script Class

Groovy is a great language to write DSL implementations. The Groovy syntax allows for example to leave out parenthesis or semi colons, which results in better readable DSL (which is actually Groovy code).

To run a DSL script we can use the GroovyShell class and evaluate the script. By default the script is evaluated with an instance of groovy.lang.Script class. But we can extends this Script class and write our DSL allowed methods, which can then be used by the DSL script. We pass our own Script class to the GroovyShell with an CompilerConfiguration object. The CompilerConfiguration allows us to set a new base script class to be used.

The following sample is a simple DSL to change the state of a Car object. Notice we implicitly access the Car object that is passed to the GroovyShell via a binding. The custom CarScript class can access the car object via the binding and change it's state.

import org.codehaus.groovy.control.CompilerConfiguration

// Simple Car class to save state and distance.
class Car {
    String state
    Long distance = 0
}

// Custom Script with methods that change the Car's state.
// The Car object is passed via the binding.
abstract class CarScript extends Script {
    def start() {
        this.binding.car.state = 'started'
    }

    def stop() {
        this.binding.car.state = 'stopped'
    }

    def drive(distance) {
        this.binding.car.distance += distance
    }
}


// Use custom CarScript.
def compilerConfiguration = new CompilerConfiguration()
compilerConfiguration.scriptBaseClass = CarScript.class.name

// Define Car object here, so we can use it in assertions later on.
def car = new Car()
// Add to script binding (CarScript references this.binding.car).
def binding = new Binding(car: car)

// Configure the GroovyShell.
def shell = new GroovyShell(this.class.classLoader, binding, compilerConfiguration)

// Simple DSL to start, drive and stop the car.
// The methods are defined in the CarScript class.
def carDsl = '''
start()
drive 20
stop()
'''

// Run DSL script.
shell.evaluate carDsl

// Checks to see that Car object has changed.
assert car.distance == 20
assert car.state == 'stopped'

May 27, 2011

Groovy Goodness: Command Chain Expressions for Fluid DSLs

Groovy 1.8 introduces command chain expression to further the support for DSLs. We already could leave out parenthesis when invoking top-level methods. But now we can also leave out punctuation when we chain methods calls, so we don't have to type the dots anymore. This results in a DSL that looks and reads like a natural language.

Let 's see a little sample where we can see how the DSL maps to real methods with arguments:

// DSL:
take 3.apples from basket
// maps to:
take(3.apples).from(basket)

// DSL (odd number of elements):
calculate high risk
// maps to:
calculate(high).risk
// or:
calculate(high).getRisk()

// DSL:
// We must use () for last method call, because method doesn't have arguments.
talk to: 'mrhaki' loudly()  
// maps to:
talk(to: 'mrhaki').loudly()

Implementing the methods to support these kind of DSLs can be done using maps and closures. The following sample is a DSL to record the time spent on a task at different clients:

worked 2.hours on design at GroovyRoom
developed 3.hours at OfficeSpace
developed 1.hour at GroovyRoom
worked 4.hours on testing at GroovyRoom

We see how to implement the methods to support this DSL here:

// Constants for tasks and clients.
enum Task { design, testing, developing }
enum Client { GroovyRoom, OfficeSpace }

// Supporting class to save work item info.
class WorkItem {
    Task task
    Client client
    Integer hours
}

// Support syntax 1.hour, 3.hours and so on.
Integer.metaClass.getHour = { -> delegate }
Integer.metaClass.getHours = { -> delegate }

// Import enum values as constants.
import static Task.*
import static Client.*

// List to save hours spent on tasks at
// different clients.
workList = []
 
def worked(Integer hours) {
    ['on': { Task task ->
        ['at': { Client client ->
            workList << new WorkItem(task: task, client: client, hours: hours)
        }]
    }]
}

def developed(Integer hours) {
    ['at': { Client client ->
        workList << new WorkItem(task: developing, client: client, hours: hours)
    }]
}

// -----------------------------------
// DSL
// -----------------------------------
worked 2.hours on design at GroovyRoom
developed 3.hours at OfficeSpace
developed 1.hour at GroovyRoom
worked 4.hours on testing at GroovyRoom


// Test if workList is filled
// with correct data.
def total(condition) {
    workList.findAll(condition).sum { it.hours }
}

assert total({ it.client == GroovyRoom }).hours == 7
assert total({ it.client == OfficeSpace }).hours == 3
assert total({ it.task == developing }).hours == 4
assert total({ it.task == design }).hours == 2
assert total({ it.task == testing }).hours == 4

August 26, 2010

Groovy Goodness: Store Closures in Script Binding

We have several ways to run (external) Groovy scripts from our code. For example we can use GroovyShell and the evaluate() method. If we want to pass variables to the script to be run we must create a Binding object and assign values to the variables and in our script we can use the variables. But if we assign closures to the variables we have methods available in our scripts. We can use it for example to define a DSL externally from the script that uses the DSL.

First we create a script with a simple DSL for sending text to the console or to a file:

// File: Output.groovy
console 'Groovy is great'

file('result.txt') { out ->
    out << 'Yes it is!'
}

Now we define the Groovy script that reads the script file and executes it:

// File: RunOutput.groovy
// Define DSL methods as closure variables in the binding.
def binding = new Binding()
binding.console = { String message ->
    println message
}
binding.file = { String fileName, Closure outputCode ->
    def outputFile = new File(fileName)
    outputFile.withWriter { writer ->
        outputCode.call writer
    }
}

// Create shell with the new binding.
def shell = new GroovyShell(binding)
def script = new File('Output.groovy')

// And execute
shell.evaluate script

We get the following output when we run the script:

$ groovy RunOutput
Groovy is great

And in the current directory we have a new file result.txt with the following contents: Yes it is!.

December 15, 2009

Groovy Goodness: Groovy Mystic Expressions

With Groovy we can omit parenthesis in some cases, key-value parameters for a method are grouped into a group. If we combine all these features we can come up with some pretty mystic expressions. But it is also perfect for writing DSLs, because it doesn't look like real Java/Groovy code anymore. Here is a small sample of an expression that looks strange at first, but at a second look we can see the structure of method calls:

result = []

// Mystic expressions: (see assert for outcome)
say hello:world {
    'Groovy'
}
say hello:world { 
    'Java' 
}

assert "Say 'Hello Groovy world!'" == result[0]
assert "Say 'Hello Java world!'" == result[1]

// Actually we are invoking:
// say([hello: world({ 'Groovy' })])
// say([hello: world({ 'Java' })])
// We notice two methods: save(map) and world(closure).

// The methods:
def world(callable) {
    def result = callable()
    "Hello $result world!"
}

def say(map) {
    result << "Say '${map.hello}'"
}