Build your own Domain-Specific Language with Ruby

Alican Sungur
2 min readJan 6, 2021

--

Photo by Randy Fath on Unsplash

“So, what is your favourite design pattern?” As odd as it sounds, it is a common question between fellow engineers and in job interviews.

But aren’t we supposed to weigh our options according to our application logic and pick a suitable one? Leaving that aside, my answer would probably be internal DSLs.

Arguably, one of the most famous books written on this subject is Design Patterns: Elements of Reusable Object-Oriented Software by the ”Gang of Four”. However, you won’t find DSLs in this book, and which is why exactly it is interesting. Most of the original design patterns described in this book were developed without Ruby in mind. By leveraging Ruby’s features, DSL provides a way to implement our own little language.

When writing Ruby code, we use internal DSLs quite often. Rake, RSpec, ActiveRecord and Sinatra come with their own internal DSL. Let’s examine two common testing libraries among Rubyists: minitest and RSpec.

The former minitest example uses pure Ruby, our PlaneTest class inherits from MiniTest::Test and we create instance methods for each test. On the RSpec example, we use the two methods describe and it for the same purposes. Under the hood, RSpec uses a DSL to allow this syntax that looks like plain English.

Simplistic DSL syntax is enabled by a couple of features in Ruby:

  • Omitting parentheses on method invocations:
  • eval method evaluates the argument and runs the content as Ruby program text.
  • instance_eval will go one step further and change the context(self) to the object that instance_evalis being called on.

Now that we have covered the basic principles, let’s implement our own little JSON parser that iterates over a collection of objects that have similar structure and extracts a Ruby array with the selected properties. It can be useful when dealing with large JSON files. The below example is from Yahoo’s woeid locations collection:

Our JSONParser class with on_path and extract methods to setup the parser:

We can pass in a block with the relevant method calls while creating a new JSONParser:

We can use instance_eval to get rid of the need of calling methods on our parser object by changing our initialize method:

With our new initialize and by omitting parentheses our parser is much more DSL-like now:

DSLs are great when dealing with an isolated problem repeatedly. Now that we’ve developed one of our own, we know what is going on in the background when we come across one.

For more on Ruby and DSLs:

--

--

Alican Sungur

I write about distributed systems, and other programming related things https://www.linkedin.com/in/sunguralican/