# Creating a DSL in ruby

**URL:** <https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122>\
**Category:** Ruby\
**Created:** [June 29, 2007, 6:50am UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122 "2007-06-29T06:50:29Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![snacktime](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/7feea3/32.png) [@snacktime](https://www.ruby-forum.com/u/snacktime)\
**Post date:** [June 29, 2007, 6:50am UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122/1 "2007-06-29T06:50:29Z")

</div>

I’m getting back to a project I’ve wanted to do for a while now, which  
is to create a DSL for recurring payments. Something like the  
following:

Charge $10 on date X then $20 every month for 12 months starting on date  
Y.

or

Charge $10 prorated to end of this month then charge $10 every month  
on the first day starting next month for 5 years.

I’m thinking I’m better off taking the time to learn a real parser and  
handle it that way. I have a feeling it would be easier to debug a  
dsl created with a parser then one using regular expressions.

Any suggestions?

Chris

---

<div class="post-metadata">

**Author:** ![snacktime](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/7feea3/32.png) [@snacktime](https://www.ruby-forum.com/u/snacktime)\
**Post date:** [June 29, 2007, 9:10am UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122/2 "2007-06-29T09:10:44Z")

</div>

On 6/28/07, snacktime [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I’m thinking I’m better off taking the time to learn a real parser and  
> handle it that way. I have a feeling it would be easier to debug a  
> dsl created with a parser then one using regular expressions.
> 
> Any suggestions?

racc [Racc](http://i.loveruby.net/en/projects/racc/)

---

<div class="post-metadata">

**Author:** ![snacktime](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/7feea3/32.png) [@snacktime](https://www.ruby-forum.com/u/snacktime)\
**Post date:** [June 29, 2007, 6:15pm UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122/3 "2007-06-29T18:15:49Z")

</div>

snacktime wrote:

> I’m thinking I’m better off taking the time to learn a real parser and  
> handle it that way. I have a feeling it would be easier to debug a  
> dsl created with a parser then one using regular expressions.
> 
> Any suggestions?
> 
> Chris

I spent the last day developing a DSL in ruby, so it’s fairly fresh in  
my mind. Off the top of my  
head, you could represent your above statement in ruby as something  
like:

charge 10.dollars do  
on “10 July 2007”  
repeat 12 do  
charge 20.dollars  
every :month  
starting “30 July 2007”  
end  
end

or perhaps:

charge 10.dollars, “10 July 2007”  
charge 20.dollars, “30 July 2007” do  
repeat 12  
every :month  
end

or maybe just:

charge 10.dollars, “10 July 2007”  
charge 20.dollars, “30 July 2007”, :repeat =\> 12, :every =\> :month

No need for regular expressions in any of the above.

There are a number of ways you could represent money. I’ve picked one  
possible approach above. Be  
wary of using floats for money, due to rounding problems. To represent  
fractional amounts, you could  
use “10.32”.dollars if you don’t consider that to be too ugly.

For manipulating dates you might want to check out the ActiveSupport  
gem.

As far as choosing between using ruby and using a parser generator I’d  
say it comes down to  
analysing your user base. If your users are all programmers, then the  
ruby approach is an extremely  
productive and powerful way to develop a DSL. In the space of a day or  
two, you can develop a DSL  
which allows people to specify concepts such as you described, but also  
end up with a DSL that has  
all the power of ruby and its libraries.

If your users are not programmers, then I’d imagine that they’d struggle  
with using the DSL, since  
exposure to “all the power of ruby” is likely to cause them pain. For  
example, if they do something  
wrong (e.g. forget a comma), they’ll end up with cryptic messages from  
the ruby interpreter. That’s  
something a programmer has no problems handling, but I’d imagine your  
non-programmer users would be  
cursing your decision.

cheers,  
mick

PS: The DSL I’m developing is to help me with project planning. It  
includes entities such as  
requirements, tasks, groups of entities, dependencies between entities,  
etc. For example, you can say:

```
 group "group-key", "Group Description" , :target_version => "1.0" 

```

do  
group “sub-group-key”, “Sub Group Description” do  
requirement “1”, “Summary of Requirement 1”  
requirement “2”, “Summary of Requirement 2”  
requirement “3”, “Summary of Requirement 3”, :target\_version =\>  
“2.0”  
end  
end

For inspiration I looked at how Markaby (which BTW is very cool) is  
implemented.

---

<div class="post-metadata">

**Author:** ![snacktime](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/7feea3/32.png) [@snacktime](https://www.ruby-forum.com/u/snacktime)\
**Post date:** [July 2, 2007, 3:35pm UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122/4 "2007-07-02T15:35:52Z")

</div>

If I may, you could look into a fluent interface (http://  
[FluentInterface](http://www.martinfowler.com/bliki/FluentInterface.html)) instead of a DSL. I  
make this distinction because “DSL” seems to have become a term  
meaning “something with lots of class methods and blocks” because  
that’s one simple Ruby implementation.

Michael H. suggested something like

charge 10.dollars do  
on “10 July 2007”  
repeat 12 do  
charge 20.dollars  
every :month  
starting “30 July 2007”  
end  
end

but how about something like

charge(10.dollars).on(date\_x).then.charge(20.dollars).every(:month).times(12).starting(date2)

instead?

If that’s not enough to turn you on, look at how well it works with  
mocking/stubbing in places like RSpec and Mocha (http://  
[mocha.rubyforge.org/examples/mocha.html](http://mocha.rubyforge.org/examples/mocha.html)).

---

<div class="post-metadata">

**Author:** ![snacktime](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/7feea3/32.png) [@snacktime](https://www.ruby-forum.com/u/snacktime)\
**Post date:** [July 2, 2007, 8:35pm UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122/5 "2007-07-02T20:35:53Z")

</div>

On Jul 2, 11:08 am, “David C.” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> You can even get more natural language using eval - imagine this expressed as:
> 
> charge 10 dollars on 07/02/2007 then charge 20 dollars every month for  
> 12 months starting on 08/01/2007
> 
> Check out Jay Fields’ writing on Business Natural Languages:
> 
> [http://bnl.jayfields.com/01\_introduction.html](http://bnl.jayfields.com/01_introduction.html)

Tricky. It’s like a super-fluent interface hidden behind a parser.  
Personally, I have no trouble with all the dots and parens, but I can  
definitely appreciate how it would be nicer for domain experts not to  
necessarily have to deal with them.

---

<div class="post-metadata">

**Author:** ![snacktime](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/7feea3/32.png) [@snacktime](https://www.ruby-forum.com/u/snacktime)\
**Post date:** [July 2, 2007, 6:11pm UTC](https://www.ruby-forum.com/t/creating-a-dsl-in-ruby/108122/6 "2007-07-02T18:11:36Z")

</div>

On 7/2/07, Yossef M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> repeat 12 do
> 
> ```
> 
> charge(10.dollars).on(date\_x).then.charge(20.dollars).every(:month).times(12).starting(date2)  
> You can even get more natural language using eval - imagine this  
> expressed as:

charge 10 dollars on 07/02/2007 then charge 20 dollars every month for  
12 months starting on 08/01/2007

Check out Jay Fields’ writing on Business Natural Languages:

[http://bnl.jayfields.com/01\_introduction.html](http://bnl.jayfields.com/01_introduction.html)
