Last night, I was playing with dynamic language groovy to write my very own DSL (domain specific language). In this blog I will explain how to write DSL using groovy.
This blog assumes the programming knowledge of groovy and underlying concept where & why DSL should be used.
Lets starts with my aim. My aim is to write dsl like “take 1 pill of disprin after 10 hours”.
More specifically,
take 1.pill,
of: disprin,
after: 10.hours
Write the above script in IDE of your choice. Try to execute the above script. After execution, following error will result
Caught: groovy.lang.MissingPropertyException: No such property: disprin ..
Quiet logical. Groovy cannot understand “disprin”. Let's add dynamically the property – disprin
this.metaClass.getProperty= {name->
def metaProperty= this.metaClass.getMetaProperty(name)
//'getMetaProperty' gets property, which may be an added or an existing one
metaProperty? metaProperty.getProperty(delegate): "" + delegate
}
Let’s run the script again. After running the script, following exception came
Caught: groovy.lang.MissingPropertyException: No such property: hours for class: java.lang.Integer
The above error tells that, groovy cannot find the property hours in the class Integer. Groovy can magically add the properties to any java class and any jdk class as shown below
Number.metaClass.getHours = { -> new Integer(delegate )
println ("getHours delegate >> " + delegate)
}
After running the script again, the following error came
Caught: groovy.lang.MissingPropertyException: No such property: pill for class: java.lang.Integer
Lets add the property – pill to the Integer
Number.metaClass.getPill = { -> new Integer(delegate )
println ("getPill delegate >> " + delegate)
}
Let run the exception again; now the following exception came
Caught: groovy.lang.MissingMethodException: take()
Finally, lets add the missing method – take. The missing method can added dynamically at runtime as shown below
metaClass.invokeMethod= {name, args->
def metaMethod= this.metaClass.getMetaMethod(name, args)
//'getMetaMethod' gets method, which may be an added or an existing one
// Call out to missing method can be handled here
metaMethod? metaMethod.invoke(delegate,args): name
}
Hence the complete script looks like
this.metaClass.getProperty= {name->
def metaProperty= this.metaClass.getMetaProperty(name)
//'getMetaProperty' gets property, which may be an added or an existing one
metaProperty? metaProperty.getProperty(delegate): "" + delegate
}
Number.metaClass.getHours = { -> new Integer(delegate )
}
Number.metaClass.getPill = { -> new Integer(delegate )
}
metaClass.invokeMethod= {name, args->
def metaMethod= this.metaClass.getMetaMethod(name, args)
//'getMetaMethod' gets method, which may be an added or an existing one
// Call out to missing method can be handled here
metaMethod? metaMethod.invoke(delegate,args): name
}
/*
* Just to test
* println (take (1.pill,of:disprin,after: 6.hours))
*/
take 1.pill,
of:disprin,
after:6.hours
Conclusion
With groovy it’s very simple to create a DSL. Also the groovy specify code mentioned below
this.metaClass.getProperty= {name->
def metaProperty= this.metaClass.getMetaProperty(name)
//'getMetaProperty' gets property, which may be an added or an existing one
metaProperty? metaProperty.getProperty(delegate): "" + delegate
}
Number.metaClass.getHours = { -> new Integer(delegate )
}
Number.metaClass.getPill = { -> new Integer(delegate )
}
metaClass.invokeMethod= {name, args->
def metaMethod= this.metaClass.getMetaMethod(name, args)
//'getMetaMethod' gets method, which may be an added or an existing one
// Call out to missing method can be handled here
metaMethod? metaMethod.invoke(delegate,args): name
}
can be abstracted out in the domain model and hence DSL will simply looks like
take 1.pill,
of:disprin,
after:6.hours