Of the many ways of writing a DSL in groovy I prefer the approach that is taken in
a Groovy DSL from scratch in 2 hours. That article describes a general way to create a DSL and you don’t have to write too much code for it. Moreover it takes the groovy Script as a base of the DSL which is really quite powerful. But there’s still too much unnecessary complexity that you will have to deal with if you want to create a DSL (see the code dealing with ExpandoMetaClass, closures and delegates in the article).
The goal of my experiment is to further simplify creating a DSL. I want to adapt groovy Scripts in such a way that it becomes easy to access the parts of a script and use them to construct your DSL.
For this reason I choose to combine scripts with Expando’s in such a way that closures become accessible as Expando’s in a recursive way. The class that adapts scripts like this is ExpandoDsl.
This post shows what you have to write to create a DSL using (the current version of) ExpandoDsl. I will simply take the architecture example from a Groovy DSL from scratch in 2 hours so it’s easy to compare the code you have to write. Note though that I didn’t rewrite the architecture DSL completely, but it’s enough to make Steven’s example working and it should give you a good idea of what you need to write to create a DSL.
Here is the code:
package nl.rintcius.example.dsl.architecture
import com.seventytwomiles.architecturerules.configuration.Configuration
import com.seventytwomiles.architecturerules.domain.SourceDirectory
import com.seventytwomiles.architecturerules.domain.Rule
import com.seventytwomiles.architecturerules.services.CyclicRedundancyServiceImpl
import com.seventytwomiles.architecturerules.services.RulesServiceImpl
import org.apache.log4j.*
import nl.rintcius.groovy.ExpandoDsl
public class GroovyArchitecture {
static void main(String[] args) {
BasicConfigurator.configure()
LogManager.rootLogger.level = Level.INFO
runArchitectureRules()
}
static void runArchitectureRules() {
runArchitectureRules(new File("architecture.groovy"))
}
static void runArchitectureRules(File dsl) {
Expando root = new ExpandoDsl(dsl.text).create()
Configuration configuration = createConfiguration(root.architecture)
new CyclicRedundancyServiceImpl(configuration).performCyclicRedundancyCheck()
new RulesServiceImpl(configuration).performRulesTest()
}
static Configuration createConfiguration(Expando expando) {
Configuration configuration = new Configuration()
configuration.doCyclicDependencyTest = true
configuration.throwExceptionWhenNoPackages = true
expando.properties.each() { key, value ->
if (key == "jar") { configuration.addSource( new SourceDirectory(value.value, true)) }
else if (key == "rules") { createRules(value).each() { rule -> configuration.addRule(rule)} }
}
configuration
}
static List createRules(Expando expando) {
expando.properties.collect() { key, value ->
createRule(key, value)
}
}
static Rule createRule(String ruleName, Expando expando) {
Rule rule = new Rule(ruleName)
expando.properties.each() { key, value ->
if (key == “comment”) { rule.comment = value }
else if (key == “violation”) { rule.addViolation(value.value) }
else if (key == “package”) { rule.addPackage(value.value) }
}
rule
}
}
As you can see, the code is quite easy to follow. All you have to do is build the root Expando via and then traverse the Expando’s via expando.propertes and build your structure.
new ExpandoDsl(text).create()
Finally, apart from ExpandoDsl there’s also ExpandoBuilder if you prefer the more restrictive builder syntax.
Let me know what you think of it!
Posted: September 24th, 2009 under Java.
Comments: none
©2006 - 2012 Rintcius Blok