Turtles All The Way Down: Building Simple and Powerful Ruby DSLs

Graham Jenson
Maori Geek
Published in
2 min readFeb 17, 2017

--

A Domain Specific Language (DSL) is a specialized way to clearly describe a problem domain. Ruby is a great language for creating DSLs because it lets developers decide how the language looks and is used. For example:

RSpec for writing tests:

FactoryGirl for mocking objects:

GeoEngineer for defining cloud resources:

This post will briefly describe how to use Ruby to create DSLs like the examples above.

Building Blocks

Ruby DSLs typically use functions that take a block given as an argument to build an instance of an domain object:

Calling instance_exec(obj, block) means that self in the block is equal to obj. In this example that means self is the instance of the newly created turtle.

We can build more of the problem domain by adding methods to the base object:

This is similar to the way that RSpec creates its describe and it methods. The DSL then builds a tree of domain objects, and once defined it can be used to solve the domains problem.

Removing Self

An annoying “gotcha” in the above example is that to assign values you must call self. This is because calling name = (rather than self.name =) creates name variable scoped to the block instead of the self object. self is also really ugly, given the goal of a Ruby DSL is to hide the "cruft" that is not part of the domain.

To remove self we must use methods rather than =:

This is starting to look a bit nicer.

The Infinite DSL

Some DSLs like FactoryGirl and GeoEngineer have user defined properties that cannot be defined ahead of time. Doing this dynamically requires overriding the method_missing function:

This is the general case of assigning variables so that now any variable can be assigned. This has one significant downside, that now the turtle can never have a method missing exception, so be careful.

Lazy Evaluation

In many DSLs there are attributes that are expensive to calculate and not always needed. In GeoEngineer attributes can be lazily evaluated and then cached so only ever executed once. This is done by assigning the attribute as a Proc and executing that Proc only when it is being retrieved:

Turtles All the Way Down

Some DSLs need to be recursive, like a family tree of turtles. This can be accomplished by also handling blocks in method_missing to then create a domain object:

This DSL is now capable of building complex structures of turtles, to solve the may philosophical questions they pose.

The End

Language affects the way someone thinks about a problem domain and how they search for a solution. The closer the language you use to the problem the less hurdles you have to leap to finding a solution. Building a DSL in Ruby that can represent a problem domain can help you quickly define, describe and solve problems. I like Turtles.

--

--