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;
Craig Burke
Groovy/Grails Developer at Carnegie Mellon
Creator of the Groovy Document Builder
www.craigburke.com
@craigburke1
@groovypgh
Definition and Examples
A Domain Specific Language (DSL) is a computer programming language of limited expressiveness focused on a particular domain.
Makes use of the language and metaphors used by domain experts
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;
p {
font-size: 12px;
color: #B41F1F;
}
div.content {
width: 100%;
padding: 5px;
border: 1px solid #000000;
}
apply plugin: 'groovy'
repositories {
jcenter()
mavenCentral()
}
dependencies {
compile group:'org.codehaus.groovy', name: 'groovy-all', version: '2.4.4'
runtime group:'org.postgresql', name: 'postgresql' version: '9.4-1201-jdbc41'
}
Groovy DSLs
class User {
String firstName
String lastName
Date birthDate
static constraints = {
firstName blank:false
lastName blank:false
birthDate nullable:false
}
}
bower {
installBase = 'src/assets/bower'
'angular'('1.4.x')
'angular-animate'('1.4.x') {
source 'angular-animate.js' >> '/angular/modules/'
}
'bootstrap'('3.3.x') {
source 'dist/css/*.min.css' >> 'styles/'
source 'dist/fonts/**' >> 'fonts/'
excludes 'jquery'
}
}
document {
paragraph 'Hello Groovy Pittsburgh!', font: [size: 36.pt]
paragraph(font: [size: 33.pt, color: '#6598A9']) {
text 'I '
text 'Love ', font: [bold: true, size: 40.pt, color: '#FF0000']
text 'Groovy'
}
}
Simple Syntax (Less Noise)
Operator Overloading
Scripting Support
Meta-Programming
SDKMan!
Windows Installer
curl -s http://get.sdkman.io | bash
sdk install groovy
Windows Installer
The assert keyword take a boolean expression and throws an exception if it’s false.
assert true
assert false // throws exception
Any Object can be coerced into a boolean and used in a boolean expression.
Unlike Java the == in Groovy uses equals() to determine equality.
Map map = [language: 'Groovy']
List list = ['foo', 'bar']
// equivilent to: say(hello).to('Craig')
// equivilent to: say(hello).to('Craig')
say hello to 'Craig'
class Note {
static from(Map args, String name) {
println "${name} said ${args.message}"
}
}
Note.from 'Craig Burke', message: 'Hey, how are you?'
Closures and other language features
Groovy is an imperative language with the optional functional programming features.
A closure is a block of code wrapped up in an object.
It’s a first-class function that can be:
Passed as an argument
Assigned to a variable
Returned from a function
Groovy method | Equivilant to |
findAll | filter |
collect | map |
inject | fold |
Most Groovy collection methods include an optional mutate parameter and an asImmutable() method.
this | instance of the class the closure was defined |
owner | usually the same as this unless it was declared inside another closure |
delegate | same as owner but can be reassigned |
OWNER_FIRST | resolve to owner then delegate (default) |
OWNER_ONLY | resolve to owner only |
DELEGATE_FIRST | resolve to delegate then owner |
DELEGATE_ONLY | resolve to delegate only |
Every class in Groovy has a corresponding metaClass
Class instances can also have custom metaClasses
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) |
class Person {
String name
String nickName
}
Person person = new Person()
person.name = 'Craig'
println person.name
person.nickName = person.name
Number.metaClass.getDollars = { delegate as BigDecimal }
Number.metaClass.getProperty = { String name ->
def rates = [euros: 1.1f, pesos: 0.063f]
delegate * (rates[name] as BigDecimal)
}
def total = 20.dollars + 40.euros + 200.pesos
assert total == 76.60
class Person {
def methodMissing(String name, args) {
if (name.startsWith('say')) {
String message = (name - 'say').trim()
println message
}
}
}
Person you = new Person()
you.sayHello()
you."say Craig is Awesome"()
(Not Really)
Comedy Albums Have Sold Millions
Voice of Dusty Crophopper from Planes
Estimated Net Worth of $30 million
Rides Around in a Sweet Lamborghini
'Dane Cook sucks'.watch { tweetedStatus ->
tweet 'Hey @DaneCook, somebody said this:' << tweetedStatus
tweet "LEAVE DANE ALONE, @${tweetedStatus.user.screenName}!!!"
}
Simple and Readable DSL for Document Creation
Use Same Code for Word or Pdf Documents
Shouldn’t Require Knowledge of a Complex Library
builder.generate {
paragraph 'OMFG! Look at the cat!'
picture kitty, [name: 'kitty.png', width: 354, height: 290]
paragraph 'That cat is amazing!!!'
}
class DocumentBuilder {
File file
Document(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)
}
builder.generate {
paragraph 'Check out this table'
table {
row {
cell 'First Name'
cell 'Last Name'
}
row {
cell 'Craig'
cell 'Burke'
}
}
}
Closure Delegation
BuilderSupport
FactoryBuilderSupport
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 */ }
}
Now We’re Talking
class User {
String firstName
String lastName
Date birthDate
static constraints = {
firstName blank:false
lastName blank:false
birthDate nullable:false
}
}
class User {
String firstName
String lastName
Date birthDate
static constraints = {
firstName blank:false
lastName blank:false
birthDate nullable:false
}
}
class User {
String firstName
String lastName
Date birthDate
static Closure constraints = {
firstName([blank:false])
lastName([blank:false])
birthDate([nullable:false])
}
}
class User {
String firstName
String lastName
Date birthDate
static Closure constraints = {
Map nameConstrants = [blank:false]
['firstName', 'lastName'].each {
"${it}"(nameContraints)
}
delegate.birthDate([nullable:false])
}
}
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)