C4SC DSL Series Catalog
Continuing on with the DateTime DSL development… It’s time to add some method chaining DateTimeComponents to make the DSL even more robust.
Looking Ahead to the Desired DSL Syntax…
Until now, if you’ve been following along the with this series, the DateTime DSL syntax has been pretty clean. And by “pretty clean” I mean, that the parentheses usage has been kept at a minimum. Now that I’ll be implementing chaining on the DateTimeComponents struct, there’s going to be a few more parentheses than I’d prefer. But hey, we’re not using Ruby… In Ruby, parentheses are really only required when you need them for better readability. As you’ll see below, the chaining introduces consecutive parentheses because of the argument required by the And() method. I wish there was a way to avoid it here, but sometimes you just have to accept the limitations of the language you’re using…
DateTime lunchTime = 3.Hours().And(45.Minutes()).FromNow();
DateTime endOfWorld = 11.Months().And(21.Days()).From(newYearsEve2011);
DateTime openingBell = 6.Hours().And(30.Minutes()).AgoFrom(closingBell);
DateTime raceStart = 3.Minutes().And(42.Seconds()).Ago();
The And() Method
As far as the DSL is concerned, we get an incredible amount of additional functionality from And(). With the addition of this single extension method to DateTimeComponents, the DSL now offers the functionality to modify all elements of DateTime before the conversion to the DateTime Type. It accomplishes this by returning a new instance of DateTimeComponents where all properties are summed from the participating DateTimeComponents operands. The And() method gives you unlimited chaining on DateTimeComponents (unless there’s some language/compiler limitation that I’m not aware of in C#) allowing for essentially any combination. However, for readability’s sake, I would suggest a limit of two or three. The code will simply read better and it will sound a lot more like proper, spoken English. You’ll see from the unit tests below that more than two or three usages of And() gets a little wordy.
/// Extension methods to support method-chaining on DateTimeComponents allowing for modification of all components
/// of date and time before the actual conversion to <see cref="DateTime"/>.
public static class DateTimeComponentsChaining
public static DateTimeComponents And(this DateTimeComponents components, DateTimeComponents operand)
return new DateTimeComponents(components.Years + operand.Years
, components.Months + operand.Months
, components.Days + operand.Days
, components.Minutes + operand.Minutes
, components.Seconds + operand.Seconds
, components.Milliseconds + operand.Milliseconds);
The Unit Tests
And voila! Chaining! For brevity, I’ve only included the addition tests that calculate the DateTime FromNow(). The addition tests that calculate DateTime from a given DateTime (using the From() method) as well as all of the relevant subtraction tests (using the Ago() and AgoFrom() methods) look almost exactly the same. For those of you just joining in who aren’t aware of the _nowAdapter usage below, please refer to the last post on stubbing static dependencies.
public void it_should_add_1_of_each_component_to_now()
DateTime actual = 1.Year()
DateTime expected = _nowAdapter.DateTimeNow().AddYears(1)
[TestCase(1, 2, 3, 4, 5, 6)]
[TestCase(2, 4, 6, 8, 10, 12)]
[TestCase(5, 10, 15, 20, 25, 30)]
[TestCase(100, 200, 300, 400, 500, 600)]
public void it_should_add_all_components_to_now(Int32 years, Int32 months, Int32 days, Int32 minutes, Int32 seconds, Int32 milliseconds)
DateTime actual = years.Years()
DateTime expected = _nowAdapter.DateTimeNow().AddYears(years)
We’re nearing the end of this C4SC series, but there is at least a couple more things I’d like to address. I’m not sure if I’ll have one or two more posts, but here’s a sneak peak at some of the things I’ll be addressing…
- I think the DSL needs some helper methods for common DateTime-related spoken English. In other words, I want a few helpers like Yesterday(),Tomorrow(), etc.; and maybe an addition to the Int32ConversionToDateTimeComponents extension methods for Week(); and possibly a few others… I’ve really been thinking about this a lot over the last few days. With a language feature like modules (i.e. Ruby and VB.NET), I could easily pull off the Yesterday() and Tomorrow() methods. However, since this is all in C#, they’re probably going to be static methods on a static class. Not a big deal at all functionality-wise, but it’s going to take some thought (and maybe a trip or two to the thesaurus) to make the DSL syntax accommodate this.
- I keep thinking there’s a better way to handle the unit test organization, but I haven’t come up with it yet. Between the two current unit test fixtures, every combination of the DSL syntax is covered, but I might break it out into some more fixtures to get rid of the #region blocks partitioning the tests.
See you next time…