Using Block Scope for a Fancy DSL

In Ruby, blocks are kind of a big deal. We use them for everything from basic iteration to executing callbacks. They are also really handy for writing Domain Specific Languages, or DSLs for short. For example, checkout how blather uses blocks to respond to an XMPP message.

We call the message method with some options and a block to execute when a message with those options is received. Blocks make this code a lot more fun to write because the event handler is hooked up behind the scenes. All we really have to worry about is what happens when we receive a specific message. So how would we implement this?

Without including the extra boiler plate to actually receive messages, it’s pretty simple code. We just register a callback and call it when the event happens. But, I want to take this a step further. If you’ve used sinatra, these examples involving blocks should look pretty familiar. Here’s an example of a simple get request definition to refresh your memory.

Cool! But how is this different from the blather message example? Look at the block we passed in. Notice anything different? We didn’t pass any parameters to the callback, but we used a params variable to get the beverage for the request. We never defined it in the scope before the block in our code. So, where did it come from? Since we always want to have a params hash in the callback of a request it makes sense that it would already be defined for us, but sinatra is actually doing some interesting things behind the scenes to make this happen. Let’s look at another example of how we would implement this DSL.

Now I wouldn’t use this all the time, but for cases like sinatra’s get requests, it can really make for an awesome DSL. Before you implement it in your own project, I would recommend reading over the docs for UnboundMethod and read though the sinatra source code just so you know exactly what’s going on. Since this article just skims the surface of what is important to know when building a DSL, here are a few resources that dig a little deeper: