Practical and Stupidly Impractical Groovy DSLs
About Me
- Groovy/Grails Developer at Carnegie Mellon
- Creator of the Groovy Document Builder
- www.craigburke.com
- @craigburke1
What is a DSL?
What is a DSL?
Makes use of the language and metaphors used by domain experts
Example (Gradle)
apply plugin: 'groovy'
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.4'
runtime 'org.postgresql:postgresql:9.4-1201-jdbc41'
}
Example (SQL)
INSERT INTO EMPLOYEE (ID, NAME) VALUES (1, 'Craig Burkee');
UPDATE EMPLOYEE SET NAME = 'Craig Burke' WHERE ID = 1;
SELECT ID, NAME FROM EMPLOYEE;
DELETE FROM EMPLOYEE WHERE ID = 1;
Example (CSS)
p {
font-size: 12px;
color: #B41F1F;
}
div.content {
width: 100%;
padding: 5px;
border: 1px solid #000000;
}
Groovy Features
- Simple Syntax (Less Noise)
- Scripting Support
- Operator Overloading
- Meta-Programming
Simple Syntax
Simple Syntax
Simple Syntax
Scripting Support
Operator Overloading
a + b |
a.plus(b) |
a - b |
a.minus(b) |
a * b |
a.multiply(b) |
a ** b |
a.power(b) |
a / b |
a.div(b) |
a % b |
a.mod(b) |
a | b |
a.or(b) |
a & b |
a.and(b) |
a ^ b |
a.xor(b) |
a or a |
a.next() |
a-- or --a |
a.previous() |
a << b |
a.leftShift(b) |
a >> b |
a.rightShift(b) |
a <⇒ b |
a.compareTo(b) |
Getters and Setters
class Person {
String name
}
Person person = new Person()
person.name = 'Craig'
// person.setName('Craig')
println person.name
// println person.getName()
person.nickName = person.name
// person.setNickName(person.getName())
Dynamic Properties
Dynamic Methods
class Person {
def methodMissing(String name, args) {
if (name.startsWith('say')) {
String message = (name - 'say').trim()
println message
}
}
}
Closure Delegation
Let’s Get Serious
Who is Dane Cook?
- Comedy Albums Have Sold Millions
- Voice of Dusty Crophopper from Planes
- Estimated Net Worth of $30 million
- Rides Around in a Sweet Lamborghini
A Proposed DSL
'Dane Cook Sucks'.watch {
tweet 'Hey @craigburke1, somebody said this:' << it
tweet "LEAVE DANE ALONE, @${it.user}!!!"
}
Generating Documents In Java
Groovy Document Builder
- Simple and Readable DSL for Document Creation
- Use Same Code for Word or Pdf Documents
- Shouldn’t Require Knowledge of a Complex Library
Early DSL
builder.generate {
paragraph 'OMFG! Look at the cat!'
picture kitty, [name: 'kitty.png', width: 354, height: 290]
paragraph 'That cat is amazing!!!'
}
Simple Implementation
class DocumentBuilder {
private File file
DocumentBuilder(File file) {
this.file = file
}
void generate(Closure builder) {
builder.delegate = this
builder()
}
abstract void paragraph(String text)
abstract void picture(Map params = [:], byte[] data)
}
Things Get More Complicated
builder.generate {
paragraph 'Check out this table'
table {
row {
cell 'First Name'
cell 'Last Name'
}
row {
cell 'Craig'
cell 'Burke'
}
}
}
Builder Support
- Closure Delegation
- BuilderSupport
- FactoryBuilderSupport
BuilderSupport
class MyBuilder extends BuilderSupport {
def createNode(name) { /* TODO */ }
def createNode(name, value) { /* TODO */ }
def createNode(name, Map attributes) { /* TODO */ }
def createNode(name, Map attributes, value) { /* TODO */ }
void setParent(parent, child) { /* TODO */ }
void nodeCompleted(parent, node) { /* TODO */ }
}
FactoryBuilderSupport
Now We’re Talking
GORM class
class User {
String firstName
String lastName
Date birthDate
static constraints = {
firstName blank: false
lastName blank: false
birthDate nullable: false
}
}
Explicit Types
class User {
String firstName
String lastName
Date birthDate
static Closure constraints = {
firstName([blank:false])
lastName([blank:false])
birthDate([nullable:false])
}
}
Take It Even Further
class User {
String firstName
String lastName
Date birthDate
static Closure constraints = {
Map nameConstrants = [blank:false]
['firstName', 'lastName'].each {
"${it}"(nameContraints)
}
delegate.birthDate([nullable:false])
}
}
Resources
Books
- Domain-Specific Languages (Martin Fowler)
- Groovy for Domain-Specific Languages (Fergal Dearle)
Presentations
- Groovy Domain Specific Languages
- DSL’ing your Groovy
Links
- Domain-Specific Languages (Groovy Docs)