Deconstructing DSL Evolution
You could say that I have an interest in DSL at the moment, which is why I was excited when I saw this post. It talks about how you can create natural English as a BDD specification.
In fact, the final "code" in the post looks like this:
Scenario: savings account is in credit Given my savings account balance is 100 And my cash account balance is 10 When I transfer 20 Then my savings account balance should be 80 And my cash account balance should be 30
Looking at this, I went absolutely mad. There was no way this code work. I am not a Ruby expert, but I know enough of the syntax to know that there is simply no way that this would be valid Ruby code.
I got the code and started looking at it. It was complicated a bit because I don't have a Ruby environment on my machine, and I am not even sure that "not a Ruby expert" covers my knowledge very well. A Ruby newbie would be a more accurate term. Let us simply says that I know the syntax of a Ruby if statement, and that about sums it up.
After a while, I let down of grep & notepad, and was able to truly appreciate what was going on there.
In order to explain, I am going to go backward a bit and show the code that is valid Ruby syntax:
Scenario "savings account is in credit" do Given "my savings account balance is 100" And "my cash account balance is 10" When "I transfer 20" Then "my savings account balance should be 80" And "my cash account balance should be 30" end
That is fairly easy, you have methods that accept strings. You configure this using this approach:
define.when("I transfer $amount") do |amount| @savings_account.transfer_to(@cash_account, amount.to_f) end
This is a parser syntax, and a delegate that accepts the extracted parameters from the passed string. The parsing is done with a Regex, and is really easy to follow. The delegate is basic stuff, when you get down to it.
So, when you call the When method, passing "I transfer 20", the delegate (called block by Rubyists) is being called with the string "20", which was extracted from the string. Really elegant way of doing it, I have to say.
But, what about the natural language syntax from the beginning of the post, that is still not valid Ruby code. The answer here is that it really isn't. What happens is that you have a line based parser, and you call the methods based on the first word in the line. For all practical purposes, it comes out the same.
For rspec, this works very well, and it gives you awesome syntax. It does mean that you are using an external DSL, not an internal one. For this scenario, I don't think that it means much, all the processing is done elsewhere.
Really neat approach, I am impressed & awed.
Comments
We are doing something similar in C# on the .Net side but I agree it is not as pretty.
http://www.lostechies.com/blogs/joe_ocampo/archive/2007/08/07/attempting-to-demystify-behavior-driven-development.aspx
Yeah when I saw this, I got a little sad. There's no way NBehave with its fluent interfaces could approach this.
How close could Boo get to this?
Exactly the same, using the exact same approach, actually.
Sweet :)
A different approach to specifications in Boo was made here: http://specter.sourceforge.net/setup.htm. Unfortunately the author has moved on and the project doesn't work in the current Boo release. But the video demonstration is worth watching.
I realize this is truly amazing, and you probably could easily also create something similar in boo as others have pointed out and it is truly impressive to have features like this in a language, but at the end of the day that specific sample basically is more like Cobol than any other programming language... ;)
String conversion is easy but what about more complex types how would boo handle the type casting and assertion?
I know it has an inference model but I was just curious how you would delegate to different types.
This is an external DSL.
As such, it is not fit to deal with types, it is literally a matter of string extraction.
Comment preview