SlideShare a Scribd company logo
Writing your own DSL
Yes, it is that easy!
Writing your own DSL
Who am I?
● Rob Kinyon
○ @rkinyon
○ rob.kinyon@gmail.com
● Devops lead for many years
● Developer in Ruby, Python, Perl, JS, and
others.
What is a DSL?
● Domain-Specific Language
What is a DSL?
● Domain-Specific Language
● Language - A vehicle for communication
What is a DSL?
● Domain-Specific Language
● Language - A vehicle for communication
● Domain - A restrained set of concepts
What is a DSL?
● Domain-Specific Language
● Language - A vehicle for communication
● Domain - A restrained set of concepts
● Specific - Limited to.
What is a DSL?
● Domain-Specific Language
● Language - A vehicle for communication
● Domain - A restrained set of concepts
● Specific - Limited to.
○ No, really. :)
Language and Communication
● Communicate in one direction
○ Author -> Executor
Language and Communication
● Communicate in two directions
○ Author -> Executor
○ Author -> Maintainer
Language and Communication
● Communicate in three directions
○ Author -> Executor
○ Author -> Maintainer
○ Specifier -> Author
Language and Communication
● Communicate in four directions
○ Author -> Executor
○ Author -> Maintainer
○ Specifier -> Author
○ Author -> Verifier
Language and Communication
● Communicate in MANY directions
○ Author -> Executor
○ Author -> Maintainer
○ Specifier -> Author
○ Author -> Verifier
○ Author -> Teammate(s)
○ Developer -> Sysadmin/Devops
○ … -> …
Language and Communication
● Communicate in MANY directions
○ Author -> Executor
○ Author -> Maintainer
○ Specifier -> Author
○ Author -> Verifier
○ Author -> Teammate(s)
○ Developer -> Sysadmin/Devops
○ … -> …
The ONLY
computer
Language and Communication
● Communicate in MANY directions
○ Author -> Executor
○ Author -> Maintainer
○ Specifier -> Author
○ Author -> Verifier
○ Author -> Teammate(s)
○ Developer -> Sysadmin/Devops
○ … -> …
All humans
Language and Communication
● Communicate in MANY directions
○ Author -> Executor
○ Author <-> Maintainer
○ Specifier <-> Author
○ Author <-> Verifier
○ Author <-> Teammate(s)
○ Developer <-> Sysadmin/Devops
○ … <-> …
All human
communication
is two-way
Domain-Specific
● Eskimos supposedly have 50+ words for
“snow”
○ Depends on how you count it
● Saami has 1000+ words dealing with
reindeer
○ snarri - a reindeer with short, branched horns
○ busat - a bull with a single, large testicle
Domain-specific : Busat
Busat - The quality of having
appropriately-specific expressiveness
for the domain.
DSLs you already use
● SQL
○ set manipulation DSL
● CSS
○ tree-visitor-defining DSL for setting metadata
● HAML
○ HTML-definition DSL
● Bash
○ A crappy way of issue shell commands with logic
Places for a DSL
● Packaging and orchestration
○ most devops/operations activities
● Configuration file generation
○ web servers
○ monitoring
○ datastores
● Configuration value management across environments
● Anything extremely complicated (such as SQL)
● Anything repetitive (such as CSS)
Reasons for a DSL
● Let the important things shine
● General-purpose is overly-verbose
● Bugs hide in boilerplate
● Non-developers can read and comprehend
○ And maybe even propose changes through PRs?
Reasons for a DSL
DSL is to Ruby
as
Ruby is to Java
Writing a DSL
● Three passes
○ Parsing
○ Validation
○ Production
Writing a DSL - Parsing
● DSL::Maker for parsing
Car = Struct.new(:make, :year, :engine)
Engine = Struct.new(:hemi)
class VehicleDSL < DSL::Maker
add_entrypoint(:car, {
:make => String,
:year => Integer,
:engine => generate_dsl({
:hemi => Boolean,
}) do
Engine.new(hemi)
end,
}) do |*args|
default(:make, args, 0)
Car.new(make, model, engine)
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
Car = Struct.new(:make, :year, :engine)
Engine = Struct.new(:hemi)
class VehicleDSL < DSL::Maker
add_entrypoint(:car, {
:make => String,
:year => Integer,
:engine => generate_dsl({
:hemi => Boolean,
}) do
Engine.new(hemi)
end,
}) do |*args|
default(:make, args, 0)
Car.new(make, model, engine)
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
Car = Struct.new(:make, :year, :engine)
Engine = Struct.new(:hemi)
class VehicleDSL < DSL::Maker
add_entrypoint(:car, {
:make => String,
:year => Integer,
:engine => generate_dsl({
:hemi => Boolean,
}) do
Engine.new(hemi)
end,
}) do |*args|
default(:make, args, 0)
Car.new(make, model, engine)
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
Car = Struct.new(:make, :year, :engine)
Engine = Struct.new(:hemi)
class VehicleDSL < DSL::Maker
add_entrypoint(:car, {
:make => String,
:year => Integer,
:engine => generate_dsl({
:hemi => Boolean,
}) do
Engine.new(hemi)
end,
}) do |*args|
default(:make, args, 0)
Car.new(make, model, engine)
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
Car = Struct.new(:make, :year, :engine)
Engine = Struct.new(:hemi)
class VehicleDSL < DSL::Maker
add_entrypoint(:car, {
:make => String,
:year => Integer,
:engine => generate_dsl({
:hemi => Boolean,
}) do
Engine.new(hemi)
end,
}) do |*args|
default(:make, args, 0)
Car.new(make, model, engine)
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
Car = Struct.new(:make, :year, :engine)
Engine = Struct.new(:hemi)
class VehicleDSL < DSL::Maker
add_entrypoint(:car, {
:make => String,
:year => Integer,
:engine => generate_dsl({
:hemi => Boolean,
}) do
Engine.new(hemi)
end,
}) do |*args|
default(:make, args, 0)
Car.new(make, model, engine)
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
#!/usr/bin/env ruby
require ‘vehicle/dsl’
filename = ARGV.shift ||
raise “No filename provided.”
vehicles = Vehicle::DSL.parse_dsl(
IO.read(filename),
)
# Do something here with vehicles
[
Car[
:make => ‘Accord’,
:year => 1990,
:engine => Engine[
:hemi => true,
],
],
Car[
:make => Civic,
:year => 2014,
:engine => nil,
],
]
. . .
truck ‘F-150’ {
year 1999
}
. . .
. . .
Truck = Struct.new(:make, :year, :engine)
. . .
class VehicleDSL < DSL::Maker
. . .
add_entrypoint(:truck, {
:make => String,
:year => Integer,
:engine => . . .,
}) do |*args|
default(:make, args, 0)
Truck.new(make, model, nil)
end
end
#!/usr/bin/env ruby
require ‘vehicle/dsl’
filename = ARGV.shift ||
raise “No filename provided.”
vehicles = Vehicle::DSL.parse_dsl(
IO.read(filename),
)
# Do something here with vehicles
[
. . .
Truck[
:make => ‘F-150’,
:year => 1999,
:engine => nil
],
. . .
]
Writing a DSL - Validation
● DSL::Maker for parsing
● DSL::Maker for validation
. . .
class VehicleDSL < DSL::Maker
. . .
add_validation(:car) do |car|
unless car.engine
return “Cars must have an engine”
end
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
. . .
class VehicleDSL < DSL::Maker
. . .
add_validation(:car) do |car|
unless car.engine
return “Cars must have an engine”
end
end
end
car {
make ‘Accord’
year 1990
engine {
hemi Yes
}
}
car ‘Civic’ {
year 2014
}
#!/usr/bin/env ruby
require ‘vehicle/dsl’
filename = ARGV.shift ||
raise “No filename provided.”
# This raises the error
vehicles = Vehicle::DSL.parse_dsl(
IO.read(filename),
)
# Do something here with vehicles
Error: Cars must have an engine
Writing a DSL - Production
● DSL::Maker for parsing
● DSL::Maker for validation
● You’re on your own for production
Writing a DSL - Production
● Work from outside in.
○ Parsing is done inside-out.
● Transform in a series of passes.
○ Expand everything (it’s just data)
● Don’t do anything irrevocable until the end
○ Work in temp directories, stage everything
Conclusion
● DSL::Maker 0.1.0 is available right now
● Patches welcome
○ 100% test coverage
● I’m blogging about this at http:
//streamlined-book.blogspot.com
○ First post on the topic
Questions?

More Related Content

PDF
Simplify your CSS with Stylus and Nib
PDF
DSL in scala
PDF
Syntax directed translation
PDF
DSL Construction with Ruby - ThoughtWorks Masterclass Series 2009
PPTX
Bdd and dsl как способ построения коммуникации на проекте
PPTX
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
PDF
Building dsl using groovy
Simplify your CSS with Stylus and Nib
DSL in scala
Syntax directed translation
DSL Construction with Ruby - ThoughtWorks Masterclass Series 2009
Bdd and dsl как способ построения коммуникации на проекте
BDD or DSL как способ построения коммуникации на проекте - опыт комплексного ...
Building dsl using groovy

Similar to Writing your own DSL (20)

PPTX
RubyConf Bangladesh 2017 - Craft beautiful code with Ruby DSL
PDF
Agile DSL Development in Ruby
PDF
DSL Construction rith Ruby
PDF
Writing a DSL for the Dense with Scala - JVMCon
PDF
Domain Specific Languages in Ruby - Medellin.rb
PPT
DSL explained _
PDF
Domain Specific Languages
PDF
Building DSLs On CLR and DLR (Microsoft.NET)
PDF
Os Alrubaie Ruby
PDF
DSLs in Boo Domain Specific Languages in NET 1st Edition Ayende Rahien
PDF
DSLs in Boo Domain Specific Languages in NET 1st Edition Ayende Rahien
PDF
groovy DSLs from beginner to expert
PDF
(Greach 2015) Dsl'ing your Groovy
PDF
Domain Specific Languages
PDF
DSLs: what, why, how
PDF
What if-your-application-could-speak
PDF
What if-your-application-could-speak, by Marcos Silveira
PPTX
AestasIT - Internal DSLs in Scala
PDF
Defining DSL (Domain Specific Language) using Ruby
PDF
DSLs in JavaScript
RubyConf Bangladesh 2017 - Craft beautiful code with Ruby DSL
Agile DSL Development in Ruby
DSL Construction rith Ruby
Writing a DSL for the Dense with Scala - JVMCon
Domain Specific Languages in Ruby - Medellin.rb
DSL explained _
Domain Specific Languages
Building DSLs On CLR and DLR (Microsoft.NET)
Os Alrubaie Ruby
DSLs in Boo Domain Specific Languages in NET 1st Edition Ayende Rahien
DSLs in Boo Domain Specific Languages in NET 1st Edition Ayende Rahien
groovy DSLs from beginner to expert
(Greach 2015) Dsl'ing your Groovy
Domain Specific Languages
DSLs: what, why, how
What if-your-application-could-speak
What if-your-application-could-speak, by Marcos Silveira
AestasIT - Internal DSLs in Scala
Defining DSL (Domain Specific Language) using Ruby
DSLs in JavaScript
Ad

Recently uploaded (20)

PDF
Slides PDF The World Game (s) Eco Economic Epochs.pdf
PDF
FINAL CALL-6th International Conference on Networks & IOT (NeTIOT 2025)
PDF
Testing WebRTC applications at scale.pdf
PDF
Decoding a Decade: 10 Years of Applied CTI Discipline
PPT
isotopes_sddsadsaadasdasdasdasdsa1213.ppt
PDF
Vigrab.top – Online Tool for Downloading and Converting Social Media Videos a...
PPTX
Digital Literacy And Online Safety on internet
PPTX
522797556-Unit-2-Temperature-measurement-1-1.pptx
PDF
Best Practices for Testing and Debugging Shopify Third-Party API Integrations...
PPTX
Introuction about WHO-FIC in ICD-10.pptx
PDF
WebRTC in SignalWire - troubleshooting media negotiation
PPTX
Slides PPTX World Game (s) Eco Economic Epochs.pptx
PPTX
artificial intelligence overview of it and more
PPTX
INTERNET------BASICS-------UPDATED PPT PRESENTATION
PPTX
CHE NAA, , b,mn,mblblblbljb jb jlb ,j , ,C PPT.pptx
PPTX
Introuction about ICD -10 and ICD-11 PPT.pptx
PPT
Design_with_Watersergyerge45hrbgre4top (1).ppt
PDF
Automated vs Manual WooCommerce to Shopify Migration_ Pros & Cons.pdf
PDF
Tenda Login Guide: Access Your Router in 5 Easy Steps
PDF
Introduction to the IoT system, how the IoT system works
Slides PDF The World Game (s) Eco Economic Epochs.pdf
FINAL CALL-6th International Conference on Networks & IOT (NeTIOT 2025)
Testing WebRTC applications at scale.pdf
Decoding a Decade: 10 Years of Applied CTI Discipline
isotopes_sddsadsaadasdasdasdasdsa1213.ppt
Vigrab.top – Online Tool for Downloading and Converting Social Media Videos a...
Digital Literacy And Online Safety on internet
522797556-Unit-2-Temperature-measurement-1-1.pptx
Best Practices for Testing and Debugging Shopify Third-Party API Integrations...
Introuction about WHO-FIC in ICD-10.pptx
WebRTC in SignalWire - troubleshooting media negotiation
Slides PPTX World Game (s) Eco Economic Epochs.pptx
artificial intelligence overview of it and more
INTERNET------BASICS-------UPDATED PPT PRESENTATION
CHE NAA, , b,mn,mblblblbljb jb jlb ,j , ,C PPT.pptx
Introuction about ICD -10 and ICD-11 PPT.pptx
Design_with_Watersergyerge45hrbgre4top (1).ppt
Automated vs Manual WooCommerce to Shopify Migration_ Pros & Cons.pdf
Tenda Login Guide: Access Your Router in 5 Easy Steps
Introduction to the IoT system, how the IoT system works
Ad

Writing your own DSL

  • 1. Writing your own DSL Yes, it is that easy!
  • 3. Who am I? ● Rob Kinyon ○ @rkinyon ○ rob.kinyon@gmail.com ● Devops lead for many years ● Developer in Ruby, Python, Perl, JS, and others.
  • 4. What is a DSL? ● Domain-Specific Language
  • 5. What is a DSL? ● Domain-Specific Language ● Language - A vehicle for communication
  • 6. What is a DSL? ● Domain-Specific Language ● Language - A vehicle for communication ● Domain - A restrained set of concepts
  • 7. What is a DSL? ● Domain-Specific Language ● Language - A vehicle for communication ● Domain - A restrained set of concepts ● Specific - Limited to.
  • 8. What is a DSL? ● Domain-Specific Language ● Language - A vehicle for communication ● Domain - A restrained set of concepts ● Specific - Limited to. ○ No, really. :)
  • 9. Language and Communication ● Communicate in one direction ○ Author -> Executor
  • 10. Language and Communication ● Communicate in two directions ○ Author -> Executor ○ Author -> Maintainer
  • 11. Language and Communication ● Communicate in three directions ○ Author -> Executor ○ Author -> Maintainer ○ Specifier -> Author
  • 12. Language and Communication ● Communicate in four directions ○ Author -> Executor ○ Author -> Maintainer ○ Specifier -> Author ○ Author -> Verifier
  • 13. Language and Communication ● Communicate in MANY directions ○ Author -> Executor ○ Author -> Maintainer ○ Specifier -> Author ○ Author -> Verifier ○ Author -> Teammate(s) ○ Developer -> Sysadmin/Devops ○ … -> …
  • 14. Language and Communication ● Communicate in MANY directions ○ Author -> Executor ○ Author -> Maintainer ○ Specifier -> Author ○ Author -> Verifier ○ Author -> Teammate(s) ○ Developer -> Sysadmin/Devops ○ … -> … The ONLY computer
  • 15. Language and Communication ● Communicate in MANY directions ○ Author -> Executor ○ Author -> Maintainer ○ Specifier -> Author ○ Author -> Verifier ○ Author -> Teammate(s) ○ Developer -> Sysadmin/Devops ○ … -> … All humans
  • 16. Language and Communication ● Communicate in MANY directions ○ Author -> Executor ○ Author <-> Maintainer ○ Specifier <-> Author ○ Author <-> Verifier ○ Author <-> Teammate(s) ○ Developer <-> Sysadmin/Devops ○ … <-> … All human communication is two-way
  • 17. Domain-Specific ● Eskimos supposedly have 50+ words for “snow” ○ Depends on how you count it ● Saami has 1000+ words dealing with reindeer ○ snarri - a reindeer with short, branched horns ○ busat - a bull with a single, large testicle
  • 18. Domain-specific : Busat Busat - The quality of having appropriately-specific expressiveness for the domain.
  • 19. DSLs you already use ● SQL ○ set manipulation DSL ● CSS ○ tree-visitor-defining DSL for setting metadata ● HAML ○ HTML-definition DSL ● Bash ○ A crappy way of issue shell commands with logic
  • 20. Places for a DSL ● Packaging and orchestration ○ most devops/operations activities ● Configuration file generation ○ web servers ○ monitoring ○ datastores ● Configuration value management across environments ● Anything extremely complicated (such as SQL) ● Anything repetitive (such as CSS)
  • 21. Reasons for a DSL ● Let the important things shine ● General-purpose is overly-verbose ● Bugs hide in boilerplate ● Non-developers can read and comprehend ○ And maybe even propose changes through PRs?
  • 22. Reasons for a DSL DSL is to Ruby as Ruby is to Java
  • 23. Writing a DSL ● Three passes ○ Parsing ○ Validation ○ Production
  • 24. Writing a DSL - Parsing ● DSL::Maker for parsing
  • 25. Car = Struct.new(:make, :year, :engine) Engine = Struct.new(:hemi) class VehicleDSL < DSL::Maker add_entrypoint(:car, { :make => String, :year => Integer, :engine => generate_dsl({ :hemi => Boolean, }) do Engine.new(hemi) end, }) do |*args| default(:make, args, 0) Car.new(make, model, engine) end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 26. Car = Struct.new(:make, :year, :engine) Engine = Struct.new(:hemi) class VehicleDSL < DSL::Maker add_entrypoint(:car, { :make => String, :year => Integer, :engine => generate_dsl({ :hemi => Boolean, }) do Engine.new(hemi) end, }) do |*args| default(:make, args, 0) Car.new(make, model, engine) end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 27. Car = Struct.new(:make, :year, :engine) Engine = Struct.new(:hemi) class VehicleDSL < DSL::Maker add_entrypoint(:car, { :make => String, :year => Integer, :engine => generate_dsl({ :hemi => Boolean, }) do Engine.new(hemi) end, }) do |*args| default(:make, args, 0) Car.new(make, model, engine) end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 28. Car = Struct.new(:make, :year, :engine) Engine = Struct.new(:hemi) class VehicleDSL < DSL::Maker add_entrypoint(:car, { :make => String, :year => Integer, :engine => generate_dsl({ :hemi => Boolean, }) do Engine.new(hemi) end, }) do |*args| default(:make, args, 0) Car.new(make, model, engine) end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 29. Car = Struct.new(:make, :year, :engine) Engine = Struct.new(:hemi) class VehicleDSL < DSL::Maker add_entrypoint(:car, { :make => String, :year => Integer, :engine => generate_dsl({ :hemi => Boolean, }) do Engine.new(hemi) end, }) do |*args| default(:make, args, 0) Car.new(make, model, engine) end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 30. Car = Struct.new(:make, :year, :engine) Engine = Struct.new(:hemi) class VehicleDSL < DSL::Maker add_entrypoint(:car, { :make => String, :year => Integer, :engine => generate_dsl({ :hemi => Boolean, }) do Engine.new(hemi) end, }) do |*args| default(:make, args, 0) Car.new(make, model, engine) end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 31. #!/usr/bin/env ruby require ‘vehicle/dsl’ filename = ARGV.shift || raise “No filename provided.” vehicles = Vehicle::DSL.parse_dsl( IO.read(filename), ) # Do something here with vehicles [ Car[ :make => ‘Accord’, :year => 1990, :engine => Engine[ :hemi => true, ], ], Car[ :make => Civic, :year => 2014, :engine => nil, ], ]
  • 32. . . . truck ‘F-150’ { year 1999 } . . . . . . Truck = Struct.new(:make, :year, :engine) . . . class VehicleDSL < DSL::Maker . . . add_entrypoint(:truck, { :make => String, :year => Integer, :engine => . . ., }) do |*args| default(:make, args, 0) Truck.new(make, model, nil) end end
  • 33. #!/usr/bin/env ruby require ‘vehicle/dsl’ filename = ARGV.shift || raise “No filename provided.” vehicles = Vehicle::DSL.parse_dsl( IO.read(filename), ) # Do something here with vehicles [ . . . Truck[ :make => ‘F-150’, :year => 1999, :engine => nil ], . . . ]
  • 34. Writing a DSL - Validation ● DSL::Maker for parsing ● DSL::Maker for validation
  • 35. . . . class VehicleDSL < DSL::Maker . . . add_validation(:car) do |car| unless car.engine return “Cars must have an engine” end end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 36. . . . class VehicleDSL < DSL::Maker . . . add_validation(:car) do |car| unless car.engine return “Cars must have an engine” end end end car { make ‘Accord’ year 1990 engine { hemi Yes } } car ‘Civic’ { year 2014 }
  • 37. #!/usr/bin/env ruby require ‘vehicle/dsl’ filename = ARGV.shift || raise “No filename provided.” # This raises the error vehicles = Vehicle::DSL.parse_dsl( IO.read(filename), ) # Do something here with vehicles Error: Cars must have an engine
  • 38. Writing a DSL - Production ● DSL::Maker for parsing ● DSL::Maker for validation ● You’re on your own for production
  • 39. Writing a DSL - Production ● Work from outside in. ○ Parsing is done inside-out. ● Transform in a series of passes. ○ Expand everything (it’s just data) ● Don’t do anything irrevocable until the end ○ Work in temp directories, stage everything
  • 40. Conclusion ● DSL::Maker 0.1.0 is available right now ● Patches welcome ○ 100% test coverage ● I’m blogging about this at http: //streamlined-book.blogspot.com ○ First post on the topic