<p>A common pattern with Ruby libraries is including internal domain-specific languages, embedded within <code>do</code> blocks in the program. These blocks have code that is mostly Ruby-like, but only works inside this block. For example, this is used for configuring Rails applications:</p>
<pre class="lang-ruby prettyprint-override"><code>Rails.application.routes.draw do
  root to: 'home#index'
  resources :photos, only: [:index, :show]
end
</code></pre>
<p>and to produce web pages in Sinatra:</p>
<pre class="lang-ruby prettyprint-override"><code>get '/:year/:month/:day/:id/' do
    post = Post.get(params[:id])
    stop [ 404, &quot;Page not found&quot; ] unless post
    @title = post.title
    template post: post
end
</code></pre>
<p>and many other libraries and frameworks — sometimes almost the whole program is a mix of these. I see them all over following this general structure.</p>
<p>How does this work, and what do I need to use to create my own embedded DSL this way? Are there any big caveats I need to keep in mind?</p>

<domain-specific-languages><internal-dsl><ruby>
How do I create an internal DSL with Ruby's do notation?
CC BY-SA 4.0
2023-05-24T00:19:49.333Z
You might want to check out `method_missing` ([here's](https://www.destroyallsoftware.com/talks/wat) an entertaining video that talks about it (the Ruby stuff is at the beginning but the rest is interesting too (though not relevant to your question)))
user
<p>DSLs working in this way use the <a href="https://ruby-doc.org/3.2.2/BasicObject.html#method-i-instance_eval" rel="nofollow noreferrer"><code>instance_eval</code></a> method, or one of its relatives, which allows executing a block of code <em>as though it were written inside another object</em>. When this code runs, it has access to all methods and instance variables of that object.</p>
<p>A very trivial DSL operating in this way is:</p>
<pre class="lang-ruby prettyprint-override"><code>class MyDSL
  def initialize
    @name = &quot;world&quot;
  end
  def hello(who=@name)
    puts &quot;Hello, #{who}!&quot;
  end
  def world; &quot;world&quot; end
end
def greetlang(&amp;p)
  MyDSL.new.instance_eval &amp;p
end
</code></pre>
<p>The class definition is to let us make an object: you might prefer to use a module and/or singleton instance in reality. The entry point to the language is the <code>greetlang</code> method, which we can use as:</p>
<pre class="lang-ruby prettyprint-override"><code>greetlang do
  hello world
  @name = &quot;PLDI&quot;
  hello
end
</code></pre>
<p>The whole <code>do</code> block is given to <code>greetlang</code> as <code>&amp;p</code>, a <a href="https://ruby-doc.org/3.2.2/Proc.html" rel="nofollow noreferrer">Proc object</a>. It hasn't executed at this point. When we call <code>instance_eval &amp;p</code>, it will evaluate the code as though it were written directly inside the MyDSL object. All methods and instance variables are available as though you wrote the code there — this includes inherited and included ones.</p>
<hr />
<p>You define the language you want as ordinary methods in that object. A few particular things you can do to tailor the apparent language to the domain are</p>
<ol>
<li>define methods with convenient constants like <code>&quot;world&quot;</code> or a configuration object</li>
<li>take parameters as a hash so <code>def root(params)</code> can be used like <code>root to: 'home#index'</code></li>
<li>use <a href="https://apidock.com/ruby/BasicObject/method_missing" rel="nofollow noreferrer"><code>method_missing</code></a> or <code>const_missing</code> to simulate unlimited methods or constants when they're used</li>
<li>define further methods taking blocks so that you can have nested DSLs</li>
</ol>
<p>You can define other objects and modules for your DSL and expose them through the entry-point object. These can represent data values in your language, or just provide fluent interfaces to other behaviour. For example, you can define <code>method_missing</code> on these other objects to enable <code>metadata.place = &quot;Earth&quot;</code> to work for any key, bookkeeping, or hang other sub-DSLs off them.</p>
<p>All of the code is still Ruby and still has to conform to the basic syntax rules, but you can choose what you provide to make the language feel natural for the domain it's working in.</p>
<hr />
<p>One element that can be confusing in this approach is that <code>instance_eval</code> <em>completely replaces</em> the current object scope around the block. Methods in the scope surrounding the block are unavailable, while more methods in the object scope than intended may be exposed. There are some ugly mitigations with their own tradeoffs. Local variables surrounding the block do remain available, so they can be used to inject data into the DSL.</p>

CC BY-SA 4.0
2023-05-24T21:51:34.600Z
2023-05-24T21:51:34.600Z
Michael Homer
# How do I create an internal DSL with Ruby's do notation?
Posted by: username_0 on 2023-05-24
A common pattern with Ruby libraries is including internal domain-specific languages, embedded within do blocks in the program. These blocks have code that is mostly Ruby-like, but only works inside this block. For example, this is used for configuring Rails applications:
Rails.application.routes.draw do
  root to: 'home#index'
  resources :photos, only: [:index, :show]
end

and to produce web pages in Sinatra:
get '/:year/:month/:day/:id/' do
    post = Post.get(params[:id])
    stop [ 404, "Page not found" ] unless post
    @title = post.title
    template post: post
end

and many other libraries and frameworks — sometimes almost the whole program is a mix of these. I see them all over following this general structure.
How does this work, and what do I need to use to create my own embedded DSL this way? Are there any big caveats I need to keep in mind?
**Tags:** domain-specific-languages internal-dsl ruby
> **Comments:**
> **username_1**: You might want to check out `method_missing` ([here's](https://www.destroyallsoftware.com/talks/wat) an entertaining video that talks about it (the Ruby stuff is at the beginning but the rest is interesting too (though not relevant to your question)))  
---
## 1 answers
### Answer by username_0 on 2023-05-24. Score: 3
DSLs working in this way use the instance_eval method, or one of its relatives, which allows executing a block of code as though it were written inside another object. When this code runs, it has access to all methods and instance variables of that object.
A very trivial DSL operating in this way is:
class MyDSL
  def initialize
    @name = "world"
  end
  def hello(who=@name)
    puts "Hello, #{who}!"
  end
  def world; "world" end
end
def greetlang(&p)
  MyDSL.new.instance_eval &p
end

The class definition is to let us make an object: you might prefer to use a module and/or singleton instance in reality. The entry point to the language is the greetlang method, which we can use as:
greetlang do
  hello world
  @name = "PLDI"
  hello
end

The whole do block is given to greetlang as &p, a Proc object. It hasn't executed at this point. When we call instance_eval &p, it will evaluate the code as though it were written directly inside the MyDSL object. All methods and instance variables are available as though you wrote the code there — this includes inherited and included ones.

You define the language you want as ordinary methods in that object. A few particular things you can do to tailor the apparent language to the domain are

define methods with convenient constants like "world" or a configuration object
take parameters as a hash so def root(params) can be used like root to: 'home#index'
use method_missing or const_missing to simulate unlimited methods or constants when they're used
define further methods taking blocks so that you can have nested DSLs

You can define other objects and modules for your DSL and expose them through the entry-point object. These can represent data values in your language, or just provide fluent interfaces to other behaviour. For example, you can define method_missing on these other objects to enable metadata.place = "Earth" to work for any key, bookkeeping, or hang other sub-DSLs off them.
All of the code is still Ruby and still has to conform to the basic syntax rules, but you can choose what you provide to make the language feel natural for the domain it's working in.

One element that can be confusing in this approach is that instance_eval completely replaces the current object scope around the block. Methods in the scope surrounding the block are unavailable, while more methods in the object scope than intended may be exposed. There are some ugly mitigations with their own tradeoffs. Local variables surrounding the block do remain available, so they can be used to inject data into the DSL.
---
