writing your own dsl
TRANSCRIPT
![Page 1: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/1.jpg)
Writing your own DSLYes, it is that easy!
![Page 2: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/2.jpg)
![Page 3: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/3.jpg)
Who am I?
● Rob Kinyon○ @rkinyon○ [email protected]
● Devops lead for many years● Developer in Ruby, Python, Perl, JS, and
others.
![Page 4: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/4.jpg)
What is a DSL?
● Domain-Specific Language
![Page 5: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/5.jpg)
What is a DSL?
● Domain-Specific Language● Language - A vehicle for communication
![Page 6: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/6.jpg)
What is a DSL?
● Domain-Specific Language● Language - A vehicle for communication● Domain - A restrained set of concepts
![Page 7: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/7.jpg)
What is a DSL?
● Domain-Specific Language● Language - A vehicle for communication● Domain - A restrained set of concepts● Specific - Limited to.
![Page 8: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/8.jpg)
What is a DSL?
● Domain-Specific Language● Language - A vehicle for communication● Domain - A restrained set of concepts● Specific - Limited to.
○ No, really. :)
![Page 9: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/9.jpg)
Language and Communication
● Communicate in one direction○ Author -> Executor
![Page 10: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/10.jpg)
Language and Communication
● Communicate in two directions○ Author -> Executor○ Author -> Maintainer
![Page 11: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/11.jpg)
Language and Communication
● Communicate in three directions○ Author -> Executor○ Author -> Maintainer○ Specifier -> Author
![Page 12: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/12.jpg)
Language and Communication
● Communicate in four directions○ Author -> Executor○ Author -> Maintainer○ Specifier -> Author○ Author -> Verifier
![Page 13: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/13.jpg)
Language and Communication
● Communicate in MANY directions○ Author -> Executor○ Author -> Maintainer○ Specifier -> Author○ Author -> Verifier○ Author -> Teammate(s)○ Developer -> Sysadmin/Devops○ … -> …
![Page 14: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/14.jpg)
Language and Communication
● Communicate in MANY directions○ Author -> Executor○ Author -> Maintainer○ Specifier -> Author○ Author -> Verifier○ Author -> Teammate(s)○ Developer -> Sysadmin/Devops○ … -> …
The ONLY computer
![Page 15: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/15.jpg)
Language and Communication
● Communicate in MANY directions○ Author -> Executor○ Author -> Maintainer○ Specifier -> Author○ Author -> Verifier○ Author -> Teammate(s)○ Developer -> Sysadmin/Devops○ … -> …
All humans
![Page 16: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/16.jpg)
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
![Page 17: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/17.jpg)
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
![Page 18: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/18.jpg)
Domain-specific : Busat
Busat - The quality of having appropriately-specific expressiveness for the domain.
![Page 19: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/19.jpg)
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
![Page 20: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/20.jpg)
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)
![Page 21: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/21.jpg)
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?
![Page 22: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/22.jpg)
Reasons for a DSL
DSL is to Rubyas
Ruby is to Java
![Page 23: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/23.jpg)
Writing a DSL
● Three passes○ Parsing○ Validation○ Production
![Page 24: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/24.jpg)
Writing a DSL - Parsing
● DSL::Maker for parsing
![Page 25: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/25.jpg)
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) endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 26: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/26.jpg)
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) endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 27: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/27.jpg)
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) endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 28: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/28.jpg)
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) endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 29: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/29.jpg)
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) endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 30: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/30.jpg)
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) endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 31: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/31.jpg)
#!/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, ],]
![Page 32: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/32.jpg)
. . .
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) endend
![Page 33: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/33.jpg)
#!/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 ], . . .]
![Page 34: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/34.jpg)
Writing a DSL - Validation
● DSL::Maker for parsing● DSL::Maker for validation
![Page 35: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/35.jpg)
. . .
class VehicleDSL < DSL::Maker . . . add_validation(:car) do |car| unless car.engine return “Cars must have an engine” end endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 36: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/36.jpg)
. . .
class VehicleDSL < DSL::Maker . . . add_validation(:car) do |car| unless car.engine return “Cars must have an engine” end endend
car { make ‘Accord’ year 1990 engine { hemi Yes }}
car ‘Civic’ { year 2014}
![Page 37: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/37.jpg)
#!/usr/bin/env ruby
require ‘vehicle/dsl’
filename = ARGV.shift || raise “No filename provided.”
# This raises the errorvehicles = Vehicle::DSL.parse_dsl( IO.read(filename),)
# Do something here with vehicles
Error: Cars must have an engine
![Page 38: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/38.jpg)
Writing a DSL - Production
● DSL::Maker for parsing● DSL::Maker for validation● You’re on your own for production
![Page 39: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/39.jpg)
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
![Page 40: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/40.jpg)
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
![Page 41: Writing your own DSL](https://reader034.vdocuments.mx/reader034/viewer/2022052607/58874c501a28ab5a628b6475/html5/thumbnails/41.jpg)
Questions?