Creating Dsl In Groovy

DSL stands for Domain specific language. DSL's are “targeted at a particular type of problem.”
Their syntax is focused on the intended domain or problem. They are not used for general-purpose programming like you use Java, Groovy, or C++, because DSL's have a very limited scope and capability.
(definition quoted from various online sources)

A DSL is small, simple, expressive, and focused on a problem area or domain. DSL's are used widely in native Groovy builders, Grails and GORM, and testing frameworks.

The goal of this blog is to see how easy it is to create a DSL and also see the beautiful dynamic nature of the Groovy programming language which is manifested in designing of DSL.

As a simple example, In Grails we express hibernate mapping with a DSL rather than XML.

class Player {

  String name
  String country

  static mapping = {
    table 'player'
    columns {
      name column:'name'
    }
  }
}

We are trying to define Hibernate mapping in a closure. We are binding the class with the table named 'player' and also the field 'name' to column 'name'. Behind the scenes GORM takes care of parsing the DSL to create table definition.

Creating new DSL's is very easy in Groovy because of the dynamic nature and also it's
Let us consider an example of a player transfer from one football club to another. This can be expressed by a Groovy DSL like below

name  Cristiano_Ronaldo
fromClub   RealMadrid
toClub  ManchesterUnited
transferFee 100m$

This is a simple representation and an easy way to understand the transfer process of a football player from one club to another.

Following are the steps to define, create and run the DSL.

Step 1

We have to define the DSL for player transfer in playerTransfer.dsl with the following
name Cristiano Ronaldo
fromClub Real Madrid, Spain
toClub Manchester United, England
transferFee 100m$
card visa, 4556367517576644

This is infact groovy code and we will see how this DSL is run in Groovy.

Step 2

We then create a PlayerTransferDSL.groovy class that has the business logic to transfer a player

def name = 'Player Name'
def fromClub = 'From Club'
def toClub = 'To Club'
def transferFee = 'Transfer fee'
def card = 'Card details'

transferDetails = [:]

def methodMissing(String name, args) {
  transferDetails[name] = args
}

def transferPlayer(closure) {
  closure.delegate = this
  closure()
    println "Transferring player in process........"
    transferDetails.each { key, value ->
    println "${key} -> ${value.join(', ')}"
  }
}

Step 3

We then run this DSL. We create a groovy class PlayerTransferProcess.groovy

def dslDef = new File('PlayerTransferDSL.groovy' ).text
def dsl = new File('playerTransfer.dsl' ).text
def script = """
${dslDef}
transferPlayer {
${dsl}
}
"""
new GroovyShell().evaluate(script)

The output of this script is as follows
Transferring player in process……..
Player Name -> Cristiano Ronaldo
From Club -> Real Madird, Spain
To Club -> Manchester United, England
Transfer Fee -> 100m$
Card Details -> visa, 4556367517576644

Let me explain the code and also what is happening behind the scenes.

In the third step we are trying to create a final groovy script and run it using the GroovyShell which is a Groovy utility to run a Groovy Script.
the text from the files PlayerTransferDSL.groovy and playerTransfer.dsl are put in to a multiline string script.

This script is executed by GroovyShell line by line.

transferPlayer {
${dsl}
}

The script invokes the transferPlayer method by passing as closure the text from DSL file playerTransfer.dsl. This then call's itself by the method closure().

When the playerTransfer.dsl is executed line by line for example line 'name Cristiano Ronaldo', Groovy tries to find method "name" and it does'nt find it and we know Groovy invokes methodMissing method(if it is present) when it doesn't find any method instead of throwing MissingMethodException. In the methodMissing method we try to put each line into the transferDetails map as key-value pairs. This is how we read the DSL and capture the data to process.

Finally we process the data and printout the details of the player transfer which is like a mock implementation of the transfer.

To summarize we have seen the dynamic design nature of Groovy that it allows missing methods and properties to be intercepted by ovveriding missingMethod(). This design principle can be exploited to design and define the DSL's more complex than the example we took.

So this dynamic nature of Groovy can be leveraged not just in DSL's but in creating mocking frameworks, testing frameworks with very little code. Thanks to the dynamic design of Groovy.

Leave a Reply

Your email address will not be published. Required fields are marked *