I’m migrating a pet project to RSpec’s StoryRunner. I’ll probably blog something about the tool itself soon but I wanted to talk about something that I noticed since the tool heavily uses Domain-Specific Languages.
I’m writing user stories using the Internal Domain-Specific Language (you can also use an external DSL) and just realized something that enforces the fact that the DSL is not the same language as its host language.
The snippet below describes a test scenario.
Scenario "file is already in registro's format" do
Given "A file in Registro's format" do
@file_path = File.dirname(__FILE__) + '/res/load_timeline/dsl_timeline.log'
end
And "A parser for log files in DSL" do
@parser = Registro::File::Parser.new
end
When("Parser parses file") do
@dsl_lines = @parser.parse_file @file_path
end
And "The resulting DSL lines are interpreted" do
@timeline = @dsl_lines.inject(Registro::Timeline.new){ |timeline, line| timeline << Registro::DSL.exec(line)}
end
Then "A timeline should be created with all entries in the file" do
@timeline.size.should eql(13)
@timeline.each{ |entry| entry.text.should =~ /Program/}
end
end
Each block defines the code that is executed for that step. The output is something like this:
Scenario: file is already in registro's format
Given A file in Registro's format
And A parser for log files in DSL
When Parser parses file
And The resulting DSL lines are interpreted
Then A timeline should be created with all entries in the file
1 scenario: 1 succeeded, 0 failed, 0 pending
As most Story-oriented tools, RSpec allows me to reuse a step in other scenario, so in the scenario below I define some new steps and reuse others:
Scenario "File is in a specific format and needs transformation" do
Given "A file in a format that we have a transformer for" do
@file_path = File.dirname(__FILE__) + '/res/load_timeline/bozo_timeline.log'
end
And "A parser for that format" do
class BozoTransformer; def transform(line); line.gsub("bozo", "-"); end ; end
@transformer_class = BozoTransformer
@parser = Registro::File::Parser.new @transformer_class
end
When "Parser parses file"
And "The resulting DSL lines are interpreted"
Then "A timeline should be created with all entries in the file"
end
Notice that the difference is that I don’t supply a block hen I’m reusing a step already defined.
Ok, now suppose I want to check the definition of the When “Parser parses file” step. Even if I have a very good IDE it probably won’t be able to do that without some additional plugin or hack.
The IDE knows about Ruby, it doesn’t know about RSpec’s DSLs. In Ruby a definition of a class or a method have a given syntax while in RSpec Story Runner the definition of a step has a different syntax. In Ruby’s terms the definition of a step is an ordinary method call and the editor won’t be able to see any difference between that call and any other.
Although RSpec’s DSLs are compatible with Ruby syntax (since they are Internal Domain Specific Languages) they are different languages that deal with different primitives and different abstractions.
Lack of tooling could be a strong disadvantage for Internal Domain-Specific Languages but most programmers that come from a language full of magic tools like Java to a language that lacks most of that like Ruby don’t miss the tooling that much.
Most tools are about reducing noise and keeping the developer focused on what matters. In Domain-Specific Language you already have very little noise so I don’t believe this will be a problem at all.

title: s/you/your