SlideShare a Scribd company logo
1 of 46
Download to read offline
DSL	101_
Alexander	(Sascha)	Klein
1
About	me
Alexander	(Sascha)	Klein
Branchmanager	Stuttgart
codecentric	AG
Heßbrühlstr.	7
70565	Stuttgart
	+49	172	529	40	20
	alexander.klein@codecentric.de
	www.codecentric.de
	blog.codecentric.de
	@saschaklein
2
What	are	DSLs?
3
Definition
Domain	Specific	Language
programming	language
to	target	a	specific	problem	area
at	the	same	level	of	abstraction	as	the	problem	domain
using	a	tongue	related	to	the	problem	domain
4
Examples
SQL	-	to	create,	edit	and	query	relational	database	systems
Ant-File	-	to	define	a	build-process
SELECT	*	FROM	USERS;
<project	name="MyProject"	default="dist"	basedir=".">
		<description>	simple	example	build	file	</description>
		<target	name="init">
				<tstamp/>
				<mkdir	dir="${build}"/>
		</target>
		<target	name="compile"	depends="init"	description="compile	the	source">
				<javac	srcdir="${src}"	destdir="${build}"/>
		</target>
</project>
5
Examples	2
CSS	-	to	specify	the	look	and	feel	of	a	webpage
SwingBuilder	-	to	define	Swing-UI’s	in	a	declarative	way
p.info	{
	 font-family:	arial,	sans-serif;
	 line-height:	150%;
}
p.info	span	{
	 font-weight:	bold;
}
p.info	span::after	{
	 content:	":	";
}
sb	=	new	groovy.swing.SwingBuilder()
frame	=	sb.frame(title:	"Click",	size:	[200,	100],
				defaultCloseOperation:	WindowConstants.EXIT_ON_CLOSE)	{
		panel()	{
				button	("Red",	foreground:	Color.RED,
								actionPerformed:	{println	"Red	clicked"})
				button	("Blue",	foreground:	Color.BLUE,
								actionPerformed:	{println	"Blue	clicked"})
		}
}
frame.visible	=	true 6
Types	of	DSL’s
internal
external
non-textual
eg.	Scratch
CC	BY-SA	3.0	http://commons.wikimedia.org/wiki/File:Scratch_Hello_World.png
7
Why	DSL’s
expressive	and	concise	syntax
increases	readability	through	higher	abstraction	level
simplify	development	with	different	skill	levels
integration	of	business	domain	members	into	development
8
Creating	DSLs
9
Scopes	of	DSLs
Command
Closure
@groovy.transform.builder.Builder
class	User	{
				String	name
				int	age
}
println	User.builder().name('Sascha	Klein').age(43).build()				
def	user	=	new	User()
Closure	dsl	=	{
				name	=	'Sascha	Klein'
				age	=	43
}
dsl.resolveStrategy	=	Closure.DELEGATE_FIRST				
dsl.delegate	=	user				
dsl()
println	user
10
Scopes	of	DSLs	2
Script
Script
myscript.groovy
Custom	ScriptBase
Usage
@groovy.transform.BaseScript	UserBaseScript	script				
name	'Sascha	Klein'
age	43
abstract	class	UserBaseScript	extends	Script	{
				@Delegate	User	user	=	new	User()
}
def	cl	=	new	GroovyClassLoader()
Script	script	=	cl.parseClass(new	File('myscript.groovy')).newInstance()				
script.run()
println	script.user
11
Scopes	of	DSLs	3
alternative	way:
Script
myscript.groovy
Usage
name	'Sascha	Klein'
age	43
import	org.codehaus.groovy.control.CompilerConfiguration
def	config	=	new	CompilerConfiguration(scriptBaseClass:	'UserBaseScript')				
def	cl	=	new	GroovyClassLoader(getClass().classLoader,	config)															
Script	script	=	cl.parseClass(new	File('myscript.groovy')).newInstance()
script.run()
println	script.user
12
Script	binding
undefined	variables	access	the	Script’s	binding
'injecting'	variables	and	functions
myscript.groovy
myVar	=	100
println	myVar	//	100
Binding	binding	=	new	Binding()																								
binding.setVariable('myVar',	50)																							
binding.setVariable('myFunc',	{	it.toUpperCase()	})				
Script	script	=	cl.parseClass(new	File('myscript.groovy')).newInstance()
script.setBinding(binding)																													
script.run()
name	myFunc('Sascha	Klein')
age	myVar
13
Command	chains
turn	left	then	right
turn(left).then(right)
take	2.pills	of	aspirin	after	6.hours
take(2.pills).of(aspirin).after(6.hours)
check	that:	margarita	tastes	good
check(that:	margarita).tastes(good)
given	{}	when	{}	then	{}
given({}).when({}).then({})
14
Command	chains	2
please	show	the	square_root	of	100
please(show).the(square_root).of(100)
show	=	{	println	it	}
square_root	=	{	Math.sqrt(it)	}
def	please(action)	{
				new	Object()	{
								Map<String,	Closure>	the(Closure	what)	{				
								}
												[of:	{	n	->	action(what(n))	}]										
				}
}
15
Command	chains	3
split	"a	,_b	,c__"	on	','	trimming	'_'
split("a	,_b	,c__").on(',').trimming('_')
@Grab('com.google.guava:guava:r09')
import	com.google.common.base.*
Splitter.on(',')
				.trimResults(CharMatcher.is('_'	as	char))
				.split("_a	,_b_	,c__")
				.iterator()
				.toList()
@Grab('com.google.guava:guava:r09')
import	com.google.common.base.*
def	split(string)	{
				[on:	{	separator	->															
								[trimming:	{	trimChar	->						
												Splitter.on(separator)
																.trimResults(CharMatcher.is(trimChar	as	char))
																.split(string)
																.iterator()
																.toList()
								}]
				}]
}
16
Method	and	Property	access
Collect	all	key,	value	pairs	in	a	Map:
Intercept	all	method	calls
Caution:
methodMissing	will	be	called	from	invokeMethod
name	'Sascha'
age	43
myProperty	1.2345
abstract	class	MyBaseScript	extends	Script	{
				Map	data	=	[:]
				def	methodMissing(String	name,	args)	{
								data[name]	=	args
				}
}
class	Interception	implements	GroovyInterceptable	{
				def	invokeMethod(String	name,	Object	args)	{	...	}
}
17
Method	and	Property	access	2
Collect	all	key,	value	pairs	in	a	Map:
Intercept	all	property	access
Caution:
propertyMissing	will	be	called	from	get/setProperty
name	=	'Sascha'
age	=	43
myProperty	=	1.2345
abstract	class	MyBaseScript	extends	Script	{
				Map	data	=	[:]
				def	propertyMissing(String	name,	value)	{
								data[name]	=	value
				}
				def	propertyMissing(String	name)	{
								return	data[name]
				}
}
def	getProperty(String	name)	{	...	}
void	setProperty(String	name,	value)	{	...	}
18
MetaClasses
6.pills	calls	6.getPills()
You	have	to	add	getPills	to	the	class	Integer
All	classes	have	a	MetaClass
It	manages	all	properties	and	methods	of	a	class
static	and	dynamic
All	calls	will	be	redirected	through	the	MetaClass
19
Usage	of	MetaClasses
adding	a	method	to	an	existing	class
adding	a	property
Integer.metaClass.getPills	=	{->	[new	Pill()]	*	delegate	}				
assert	3.getPills().every{	it	instanceof	Pill	}
def	storage	=	Collections.synchronizedMap([:])
String.metaClass.getName	=	{->
				return	storage[System.identityHashCode(delegate)	+	"name"]
}
String.metaClass.setName	=	{	String	name	->
				storage[System.identityHashCode(delegate)	+	"name"]	=	name
}
def	test	=	"Test"
test.name	=	"Sascha"
assert	test.name		==	"Sascha"
assert	''.name	==	null
20
Operator	overloading
order	icecream	&	suncream	when	temperature	>	20
order(icecream	&	suncream).when(temperature	>	20)
def	suncream	=	"suncream"
def	icecream	=	"icecream"
def	temperature	=	30
String.metaClass.and	=	{a	->	delegate	+	"	and	"+	a}
def	order	=	{	what	->
				[	when:	{	condition	->
								println	condition	?	what	:	"nothing	to	order"
						}
				]
}
21
Operator	overloading	2
Operator Method Operator Method
a	+	b a.plus(b) -a a.negative()
a	-	b a.minus(b) +a a.positive()
a	*	b a.multiply(b) a++,	++a a.next(b)
a	/	b a.div(b) a--,	--a a.previous(b)
a	%	b a.mod(b) a	==	b a.equals(b)
a	|	b a.or(b) a	!=	b !a.equals(b)
a	<	b a.compareTo(b)<0 a	>	b a.compareTo(b)>0
a	<=	b a.compareTo(b)<=0 a	>=	b a.compareTo(b)>=0
a[b] a.getAt(b) a[b]	=	c a.setAt(b,	c)
~a a.bitwiseNegate(b) a**b a.power(b)
a	in	b a.isCase(b) a	as	b a.asType()
a	<=>	b a.compareTo(b) 22
Extension	Modules
StringHelper.groovy
META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
To	use	multiple	extensionClasses,	separate	them	with	commas
class	StringHelper	{
				static	String	low(String	string)	{
								return	string.toLowerCase()
				}
}
moduleName	=	my-module
moduleVersion	=	1.0
extensionClasses	=	StringHelper
assert	"test"	==	"TeSt".low()
23
AST	transformations
AST	=	abstract	syntax	tree
AST	transformation
manipulating	AST	to	add	or	modify	classes	at	compiletime
local:	triggered	by	an	annotation
global:	always	active	when	registered
most	flexible	instrument
enables	syntaxes	otherwise	not	possible
can	be	used	to	change	the	result	from	ground	up
changes	get	'melted'	into	byte	code
grants	best	IDE	support
24
AST	transformations	2
local	AST-transformations
Annotation
astNodes	contains
AnnotationNode	triggeringAnnotation
Node	annotatedNode
@Retention	(RetentionPolicy.SOURCE)
@Target	([ElementType.METHOD])
@GroovyASTTransformationClass	("MyTransformation")
public	@interface	MyAnnotation	{
				String	value()	default	"this	is	the	default	value"
}
package	my.pkg
@GroovyASTTransformation(phase	=	CompilePhase.CANONICALIZATION)
class	MyTransformation	implements	ASTTransformation	{
				void	visit(ASTNode[]	astNodes,	SourceUnit	sourceUnit)	{	...	}
}
class	MyClass	{
				@MyAnnotation	void	doit()	{	...	}
}
25
AST	transformations	3
global	AST-transformations
ignore	astNodes	parameter
they	have	to	be	part	of	the	classpath
and	have	to	be	registered
META-INF/services/org.codehaus.groovy.transform.ASTTransformation
Groovy	2.5+:	Macros	simplify	AST	transformation	creation
my.pkg.MyTransformation
my.pkg.MySecondTransformation
26
Using	DSLs
27
Embedding	DSLs
GroovyShell
using	CompilationConfiguration
Binding	binding	=	new	Binding()
binding.setVariable("foo",	2)
GroovyShell	shell	=	new	GroovyShell(binding)									
Object	value	=	shell.evaluate(new	File('my.dsl'))				
assert	value	==	20
assert	binding.getVariable("x")	==	123
...
import	org.codehaus.groovy.control.CompilerConfiguration
def	config	=	new	CompilerConfiguration(scriptBaseClass:	'MyBaseScript')				
GroovyShell	shell	=	new	GroovyShell(config,	binding)																							
...
28
Embedding	DSLs	2
GroovyClassLoader
using	CompilationConfiguration
def	cl	=	new	GroovyClassLoader()
Script	script	=	cl.parseClass(new	File('my.dsl')).newInstance()				
script.run()
println	script.user
import	org.codehaus.groovy.control.CompilerConfiguration
def	config	=	new	CompilerConfiguration(scriptBaseClass:	'MyBaseScript')				
def	cl	=	new	GroovyClassLoader(getClass().classLoader,	config)													
...
29
Embedding	DSLs	3
GroovyScriptEngine
using	CompilationConfiguration
import	groovy.lang.Binding
import	groovy.util.GroovyScriptEngine
String[]	roots	=	[	"/my/groovy/script/path",	"/my/other/script/path"	]
GroovyScriptEngine	gse	=	new	GroovyScriptEngine(roots)				
Binding	binding	=	new	Binding()
binding.setVariable("foo",	2)
gse.run("mydsl.groovy",	binding)																										
...
import	org.codehaus.groovy.control.CompilerConfiguration
def	config	=	new	CompilerConfiguration(scriptBaseClass:	'MyBaseScript')				
GroovyScriptEngine	gse	=	new	GroovyScriptEngine(roots)
gs.config	=	config																																																									
...
30
CompilerConfiguration
ScriptBaseClass
ASTTransformationCustomizer
Default	imports
import	org.codehaus.groovy.control.CompilerConfiguration
import	groovy.util.logging.Log
def	config	=	new	CompilerConfiguration()
config.addCompilationCustomizers(new	ASTTransformationCustomizer(Log))				
def	config	=	new	CompilerConfiguration()
def	imports	=	new	ImportCustomizer()
imports.addImport('my.pkg.MyClass')				
imports.addStarImports('java.time')				
config.addCompilationCustomizers(imports)
31
CompilerConfiguration	2
SecureASTCustomizer
SourceAwareCustomizer
def	secureAstCust	=	new	SecureASTCustomizer()
secureAstCust.methodDefinitionAllowed	=	false																													
secureAstCust.statementsBlacklist	=	[SwitchStatement,	AssertStatement]				
def	variableNames	=	{	expr	->
				if	(expr	instanceof	VariableExpression)	{
								!	expr.variable[0]	in	['_',	'$']
				}	else	{	return		true	}
}	as	ExpressionChecker
secureAstCust.addExpressionCheckers(variableNames)																								
config.addCompilationCustomizers(secureAstCust)
def	imports	=	new	ImportCustomizer()
imports.addImport('my.pkg.MyClass')
def	sac	=	new	SourceAwareCustomizer(imports)											
sac.extensionValidator	=	{	ext	->	ext	==	'dsl'	}							
config.addCompilationCustomizers(sac)
32
CompilerConfiguration	3
CompilerCustomizationBuilder
CompilerCustomizationBuilder.withConfig(configuration)		{
				ast(groovy.util.logging.Log)
				ast(includeNames:	true,	groovy.transform.ToString)
				imports	{
								normal	'my.pkg.MyClass'
								star	'java.time'
				}
				def	variableNames	=	{	expr	->
								if	(expr	instanceof	VariableExpression)	{
												!	expr.variable[0]	in	['_',	'$']
								}	else	{	return		true	}
				}	as	ExpressionChecker
				secureAst	{
								addExpressionCheckers	variableNames
								methodDefinitionAllowed	false
								statementsBlacklist	=	[SwitchStatement,	AssertStatement]
				}
				source(extensions:	['dsl'])	{
								imports	{
												normal	'my.pkg.MyClass'
								}
				}
}
33
Compiler	Configuration	Script
config.groovy
build.gradle
import	org.codehaus.groovy.control.customizers.ImportCustomizer
def	imports	=	new	ImportCustomizer()
imports.addImports('org.some.*')
configuration.addCompilationCustomizers(imports)
>>	groovyc	--configscript	pathto/config.groovy
compileGroovy.groovyOptions.configurationScript=file('pathto/config.groovy')
34
Best	practices
35
How	to	design	a	DSL
minimalistic
expose	only	the	behaviour	needed
distilled
remove	all	nonessential	details
extensibel
designed	in	a	way	extensions	have	minimal	impact	on	users
36
Syntax	checking
ExpressionCheckers
can	be	mighty,	but	complex
@TypeChecked	/	@CompileStatic
most	dynamic	features	will	fail
you	have	to	help	the	compiler
37
@DelegatesTo
email	{
				from	'dsl-guru@mycompany.com'
				to	'john.doe@waitaminute.com'
				subject	'The	pope	has	resigned!'
				body	'Really,	the	pope	has	resigned!'
}
class	EmailSpec	{
				void	from(String	from)	{	println	"From:	$from"}
				void	to(String...	to)	{	println	"To:	$to"}
				void	subject(String	subject)	{	println	"Subject:	$subject"}
				void	body(Strig	body)	{	println	body	}
}
def	email(Closure	cl)	{
				def	email	=	new	EmailSpec()
				def	code	=	cl.clone()
				code.delegate	=	email
				code.resolveStrategy	=	Closure.DELEGATE_ONLY
				return	code()
}
38
@DelegatesTo	2
compilation	fails
specification	of	email-Closure	unknown	to	parser
Now	the	compilation	is	successful
@groovy.transform.TypeChecked	//	or	@groovy.transform.CompileStatic
void	sendEmail()	{
				email	{
								from	'dsl-guru@mycompany.com'
								to	'john.doe@waitaminute.com'
								subject	'The	pope	has	resigned!'
								body	'Really,	the	pope	has	resigned!'
				}
}
def	email(@DelegatesTo(EmailSpec)	Closure	cl)	{				
				...
}
def	email(@DelegatesTo(strategy=Closure.DELEGATE_ONLY,				
																							value=EmailSpec)	Closure	cl)	{					
				...
}
39
@DelegatesTo	3
Delegate	to	a	parameter
def	exec(Object	target,	Closure	code)	{
			def	clone	=	code.clone()
			clone.delegate	=	target
			return	clone()
}
class	Greeter	{
			void	sayHello()	{	println	'Hello'	}
}
def	greeter	=	new	Greeter()
exec(greeter)	{
			sayHello()
}
def	exec(@DelegatesTo.Target	Object	target,	@DelegatesTo	Closure	code)	{				
			def	clone	=	code.rehydrate(target,	this,	this)
			clone()
}
40
@DelegatesTo	4
Delegate	to	a	generic	type
For	other	cases	write	a	@TypeChecked	extension
public	<T>	void	configure(List<T>	elements,	Closure	configuration)	{
			elements.each	{	T	element	->
						def	clone	=	configuration.rehydrate(element,	this,	this)
						clone.resolveStrategy	=	Closure.DELEGATE_FIRST
						clone.call()
			}
}
@groovy.transform.ToString
class	Realm	{
				String	name
}
List<Realm>	list	=	[new	Realm()]	*	3
configure(list)	{	name	=	'My	Realm'	}
assert	list.every	{	it.name	==	'My	Realm'	}
public	<T>	void	configure(
				@DelegatesTo.Target	List<T>	elements,																														
				@DelegatesTo(strategy=Closure.DELEGATE_FIRST,																						
																	genericTypeIndex=0)	Closure	configuration)	{	...	}				
http://docs.groovy-lang.org/next/html/documentation/type-checking- 41
Execution	control
@groovy.transform.ThreadInterrupt
adds	thread	interruption	checks	after
loops	(for,	while)
first	instruction	of	a	method
first	instruction	of	a	closure	body
@groovy.transform.TimedInterrupt
interrupts	after	the	given	time
@groovy.transform.ConditionalInterrupt
interrupts	after	the	given	closure	returns	true
42
Dependency	management
Grape
GRoovy	Adaptable	Packaging	Engine
Addable	via	ASTTransformationCustomizer
@Grab(group='org.springframework',
						module='spring-orm',
						version='3.2.5.RELEASE')
import	org.springframework.jdbc.core.JdbcTemplate
@Grab('org.springframework:spring-orm:3.2.5.RELEASE')
import	org.springframework.jdbc.core.JdbcTemplate
@Grapes([
				@Grab(group='commons-primitives',
										module='commons-primitives',
										version='1.0'),
				@Grab(group='org.ccil.cowan.tagsoup',
										module='tagsoup',
										version='0.9.7')
])
43
Errorhandling
Give	detailed	information
clear	message
file,	line
GroovyShell	shell	=	new	GroovyShell()
try	{
				Object	value	=	shell.evaluate	'''	prinln	123	'''
}	catch(ex)	{
				def	ste	=	ex.stackTrace.find	{
								!(it.declaringClass.startsWith("org.codehaus.groovy.")	||
								it.declaringClass.startsWith("groovy.")	||
								it.declaringClass.startsWith("java."))
				}
				println	"Error	at	$ste.fileName(line	$ste.lineNumber):	$ex.message"
}
44
IDE	Support
IntelliJ	IDEA	supports
@DelegatesTo
@BaseScript
GroovyDSL	Script:	.gdsl
abstract	class	UserBaseScript	extends	Script	{
				@Lazy	User	user	=	{	new	User()	}()
				def	methodMissing(String	name,	args)	{
								user."$name"	=	args
				}
}
@groovy.transform.BaseScript	UserBaseScript	script
name	'Sascha	Klein'
age	43
http://www.tothenew.com/blog/gdsl-awesomeness-introduction-to-gdsl-in-
intellij-idea/
45
Questions?
Alexander	(Sascha)	Klein
Branchmanager	Stuttgart
codecentric	AG
Heßbrühlstr.	7
70565	Stuttgart
	+49	172	529	40	20
	alexander.klein@codecentric.de
	www.codecentric.de
	blog.codecentric.de
	@saschaklein
46

More Related Content

Similar to DSL101

BlueClosure - Brochure 2016
BlueClosure - Brochure 2016BlueClosure - Brochure 2016
BlueClosure - Brochure 2016
Matteo Meucci
 
Intro to MVC 3 for Government Developers
Intro to MVC 3 for Government DevelopersIntro to MVC 3 for Government Developers
Intro to MVC 3 for Government Developers
Frank La Vigne
 
android sqlite
android sqliteandroid sqlite
android sqlite
Deepa Rani
 
Windows Azure through the eyes of an it pro
Windows Azure through the eyes of an it proWindows Azure through the eyes of an it pro
Windows Azure through the eyes of an it pro
Mike Martin
 

Similar to DSL101 (20)

Continuous Integration and the Data Warehouse - PASS SQL Saturday Slovenia
Continuous Integration and the Data Warehouse - PASS SQL Saturday SloveniaContinuous Integration and the Data Warehouse - PASS SQL Saturday Slovenia
Continuous Integration and the Data Warehouse - PASS SQL Saturday Slovenia
 
Continuous Integration and the Data Warehouse - PASS SQL Saturday Slovenia
Continuous Integration and the Data Warehouse - PASS SQL Saturday SloveniaContinuous Integration and the Data Warehouse - PASS SQL Saturday Slovenia
Continuous Integration and the Data Warehouse - PASS SQL Saturday Slovenia
 
Day2
Day2Day2
Day2
 
BlueClosure - Brochure 2016
BlueClosure - Brochure 2016BlueClosure - Brochure 2016
BlueClosure - Brochure 2016
 
Intro to-ant
Intro to-antIntro to-ant
Intro to-ant
 
SQL Bits: Containers and Clones
SQL Bits: Containers and ClonesSQL Bits: Containers and Clones
SQL Bits: Containers and Clones
 
Cssdoc
CssdocCssdoc
Cssdoc
 
Introducing U-SQL (SQLPASS 2016)
Introducing U-SQL (SQLPASS 2016)Introducing U-SQL (SQLPASS 2016)
Introducing U-SQL (SQLPASS 2016)
 
Intro to MVC 3 for Government Developers
Intro to MVC 3 for Government DevelopersIntro to MVC 3 for Government Developers
Intro to MVC 3 for Government Developers
 
android sqlite
android sqliteandroid sqlite
android sqlite
 
You Too Can Be a Radio Host Or How We Scaled a .NET Startup And Had Fun Doing It
You Too Can Be a Radio Host Or How We Scaled a .NET Startup And Had Fun Doing ItYou Too Can Be a Radio Host Or How We Scaled a .NET Startup And Had Fun Doing It
You Too Can Be a Radio Host Or How We Scaled a .NET Startup And Had Fun Doing It
 
Spring 4.0 - Evolution or Revolution
Spring 4.0 - Evolution or RevolutionSpring 4.0 - Evolution or Revolution
Spring 4.0 - Evolution or Revolution
 
Windows Azure through the eyes of an it pro
Windows Azure through the eyes of an it proWindows Azure through the eyes of an it pro
Windows Azure through the eyes of an it pro
 
Create a landing page
Create a landing pageCreate a landing page
Create a landing page
 
Professional Recycling - SSIS Custom Control Flow Components With Visual Stud...
Professional Recycling - SSIS Custom Control Flow Components With Visual Stud...Professional Recycling - SSIS Custom Control Flow Components With Visual Stud...
Professional Recycling - SSIS Custom Control Flow Components With Visual Stud...
 
Future-proof Development for Classic SharePoint
Future-proof Development for Classic SharePointFuture-proof Development for Classic SharePoint
Future-proof Development for Classic SharePoint
 
Generating Code with Oracle SQL Developer Data Modeler
Generating Code with Oracle SQL Developer Data ModelerGenerating Code with Oracle SQL Developer Data Modeler
Generating Code with Oracle SQL Developer Data Modeler
 
Generate SSIS packages automatically with Biml and BimlScript (SQLKonferenz 2...
Generate SSIS packages automatically with Biml and BimlScript (SQLKonferenz 2...Generate SSIS packages automatically with Biml and BimlScript (SQLKonferenz 2...
Generate SSIS packages automatically with Biml and BimlScript (SQLKonferenz 2...
 
Azure presentation nnug dec 2010
Azure presentation nnug  dec 2010Azure presentation nnug  dec 2010
Azure presentation nnug dec 2010
 
Design Systems, Pattern Libraries & WordPress
Design Systems, Pattern Libraries & WordPressDesign Systems, Pattern Libraries & WordPress
Design Systems, Pattern Libraries & WordPress
 

More from sascha_klein

More from sascha_klein (10)

Groovy on the shell
Groovy on the shellGroovy on the shell
Groovy on the shell
 
GroovyFX - groove JavaFX Gr8Conf EU 2017
GroovyFX - groove JavaFX Gr8Conf EU 2017GroovyFX - groove JavaFX Gr8Conf EU 2017
GroovyFX - groove JavaFX Gr8Conf EU 2017
 
Bob the Builder - Gr8Conf EU 2017
Bob the Builder - Gr8Conf EU 2017Bob the Builder - Gr8Conf EU 2017
Bob the Builder - Gr8Conf EU 2017
 
GroovyFX - Groove JavaFX
GroovyFX - Groove JavaFXGroovyFX - Groove JavaFX
GroovyFX - Groove JavaFX
 
Using Groovy with Jenkins
Using Groovy with JenkinsUsing Groovy with Jenkins
Using Groovy with Jenkins
 
Introduction to Graphics- and UI-Design
Introduction to Graphics- and UI-DesignIntroduction to Graphics- and UI-Design
Introduction to Graphics- and UI-Design
 
Android on Groovy
Android on GroovyAndroid on Groovy
Android on Groovy
 
Groovy on the shell
Groovy on the shellGroovy on the shell
Groovy on the shell
 
Groovy on the Shell
Groovy on the ShellGroovy on the Shell
Groovy on the Shell
 
Vert.x using Groovy - Simplifying non-blocking code
Vert.x using Groovy - Simplifying non-blocking codeVert.x using Groovy - Simplifying non-blocking code
Vert.x using Groovy - Simplifying non-blocking code
 

Recently uploaded

Recently uploaded (20)

Community is Just as Important as Code by Andrea Goulet
Community is Just as Important as Code by Andrea GouletCommunity is Just as Important as Code by Andrea Goulet
Community is Just as Important as Code by Andrea Goulet
 
Rapidoform for Modern Form Building and Insights
Rapidoform for Modern Form Building and InsightsRapidoform for Modern Form Building and Insights
Rapidoform for Modern Form Building and Insights
 
GraphSummit Milan - Neo4j: The Art of the Possible with Graph
GraphSummit Milan - Neo4j: The Art of the Possible with GraphGraphSummit Milan - Neo4j: The Art of the Possible with Graph
GraphSummit Milan - Neo4j: The Art of the Possible with Graph
 
Novo Nordisk: When Knowledge Graphs meet LLMs
Novo Nordisk: When Knowledge Graphs meet LLMsNovo Nordisk: When Knowledge Graphs meet LLMs
Novo Nordisk: When Knowledge Graphs meet LLMs
 
Abortion Pill Prices Jozini ](+27832195400*)[ 🏥 Women's Abortion Clinic in Jo...
Abortion Pill Prices Jozini ](+27832195400*)[ 🏥 Women's Abortion Clinic in Jo...Abortion Pill Prices Jozini ](+27832195400*)[ 🏥 Women's Abortion Clinic in Jo...
Abortion Pill Prices Jozini ](+27832195400*)[ 🏥 Women's Abortion Clinic in Jo...
 
Abortion Clinic In Pongola ](+27832195400*)[ 🏥 Safe Abortion Pills In Pongola...
Abortion Clinic In Pongola ](+27832195400*)[ 🏥 Safe Abortion Pills In Pongola...Abortion Clinic In Pongola ](+27832195400*)[ 🏥 Safe Abortion Pills In Pongola...
Abortion Clinic In Pongola ](+27832195400*)[ 🏥 Safe Abortion Pills In Pongola...
 
Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...
Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...
Abortion Clinic In Pretoria ](+27832195400*)[ 🏥 Safe Abortion Pills in Pretor...
 
AzureNativeQumulo_HPC_Cloud_Native_Benchmarks.pdf
AzureNativeQumulo_HPC_Cloud_Native_Benchmarks.pdfAzureNativeQumulo_HPC_Cloud_Native_Benchmarks.pdf
AzureNativeQumulo_HPC_Cloud_Native_Benchmarks.pdf
 
GraphSummit Milan - Visione e roadmap del prodotto Neo4j
GraphSummit Milan - Visione e roadmap del prodotto Neo4jGraphSummit Milan - Visione e roadmap del prodotto Neo4j
GraphSummit Milan - Visione e roadmap del prodotto Neo4j
 
A Deep Dive into Secure Product Development Frameworks.pdf
A Deep Dive into Secure Product Development Frameworks.pdfA Deep Dive into Secure Product Development Frameworks.pdf
A Deep Dive into Secure Product Development Frameworks.pdf
 
Transformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with LinksTransformer Neural Network Use Cases with Links
Transformer Neural Network Use Cases with Links
 
Modern binary build systems - PyCon 2024
Modern binary build systems - PyCon 2024Modern binary build systems - PyCon 2024
Modern binary build systems - PyCon 2024
 
UNI DI NAPOLI FEDERICO II - Il ruolo dei grafi nell'AI Conversazionale Ibrida
UNI DI NAPOLI FEDERICO II - Il ruolo dei grafi nell'AI Conversazionale IbridaUNI DI NAPOLI FEDERICO II - Il ruolo dei grafi nell'AI Conversazionale Ibrida
UNI DI NAPOLI FEDERICO II - Il ruolo dei grafi nell'AI Conversazionale Ibrida
 
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
[GeeCON2024] How I learned to stop worrying and love the dark silicon apocalypse
 
From Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST APIFrom Theory to Practice: Utilizing SpiraPlan's REST API
From Theory to Practice: Utilizing SpiraPlan's REST API
 
Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...
Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...
Abortion Clinic In Johannesburg ](+27832195400*)[ 🏥 Safe Abortion Pills in Jo...
 
Lessons Learned from Building a Serverless Notifications System.pdf
Lessons Learned from Building a Serverless Notifications System.pdfLessons Learned from Building a Serverless Notifications System.pdf
Lessons Learned from Building a Serverless Notifications System.pdf
 
Anypoint Code Builder - Munich MuleSoft Meetup - 16th May 2024
Anypoint Code Builder - Munich MuleSoft Meetup - 16th May 2024Anypoint Code Builder - Munich MuleSoft Meetup - 16th May 2024
Anypoint Code Builder - Munich MuleSoft Meetup - 16th May 2024
 
Evolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI EraEvolving Data Governance for the Real-time Streaming and AI Era
Evolving Data Governance for the Real-time Streaming and AI Era
 
Encryption Recap: A Refresher on Key Concepts
Encryption Recap: A Refresher on Key ConceptsEncryption Recap: A Refresher on Key Concepts
Encryption Recap: A Refresher on Key Concepts
 

DSL101