DSLs in Boo : Implementing the Scheduling DSL

8/22/2012 5:30:22 PM

Listing 1 will refresh your memory about what the Scheduling DSL looks like.

Listing 1. Sample code from the Scheduling DSL
task "warn if website is down":
    every 3.Minutes()
    starting now
    when WebSite("http://example.org").IsNotResponding
        notify "admin@example.org", "server down!"

It doesn’t look much like code, right? But take a look at the class diagram in figure 1.

Figure 1. Class diagram of BaseScheduler, the implicit base class for the Scheduling DSL

This is the implicit base class for the Scheduling DSL. An implicit base class is one of the more common ways to define and work with a DSL. 

For now, please assume that the DSL code you see is being magically placed in the Prepare() method of a derived class. This means that you have full access to all the methods that the BaseScheduler exposes, because those are exposed by the base class.

What this means, in turn, is that you can now look at the DSL and the class diagram and suddenly understand that most of what goes on here involves plain old method calls. Nothing fancy or hard to understand—we’re merely using a slightly different syntax to call them than you usually do.

We’re adding a minor extension to the language here. Two methods in the BaseScheduler aren’t part of the API, but rather are part of the language extension:

  • Minutes()—This is a simple extension method that allows us to specify 3.Minutes(), which reads better than TimeSpan.FromMinutes(3), which is how we would usually perform the same task.

  • when(Expression)—This is a meta-method, which is a method that can modify the language. It specifies that the expression that’s passed to it will be wrapped in a delegate and stored in an instance variable. 

That doesn’t make much sense right now, I know, so let’s start taking this DSL apart. We’ll use the exact opposite approach from what we do when we’re building the DSL. We’ll add the programming concepts to the existing DSL until we fully understand how this works.

Let’s start by adding parentheses and removing some compiler syntactic sugar. Listing 2 shows the results of that.

Listing 2. The Scheduling DSL after removing most of the syntactic sugar
task("warn if website is down", do() :
    self.every( self.Minutes(3) )
    self.starting ( self.now )
    self.when( WebSite("http://example.org").IsNotResponding)
    self.then( do():
        notify( "admin@example.org", "server down!")

A couple of notes about this before we continue:

  • self in Boo is the equivalent of this in C# or Java or of Me in VB.NET.

  • do(): is the syntax for anonymous delegates in Boo.

That looks a lot more like code now (and a lot less like a normal language). But we’re not done yet. We still need to resolve the when meta-method. When we run that, we’ll get the result shown in listing 3.

Listing 3. The Scheduling DSL after resolving the when meta-method
task("warn if website is down", do() :
    self.every( self.Minutes(3) )
    starting ( self.now )
    condition = do():
        return WebSite("http://example.org").IsNotResponding
    then( do():
        notify( "admin@example.org", "server down!")

As you can see, we completely removed the when method, replacing it with an assignment of an anonymous delegate for the instance variable. This is the only piece of compiler magic we’ve performed. Everything else is already in the Boo language.

Meta-methods and anonymous blocks

Take a look at the when and then methods. Both of them end up with a similar syntax, but they’re implemented in drastically different ways. The when method is a meta-method. It changes the code at compilation time. The then method uses an anonymous block as a way to pass the delegate to execute.

The reason we have two different approaches that end up with nearly the same end result (passing a delegate to a method) has to do with the syntax we want to achieve.

With the when method, we want to achieve a keyword-like behavior, so the when method accepts an expression and transforms that to a delegate. The then keyword has a different syntax that accepts a block of code, so we use Boo’s anonymous blocks to help us out there.

Now we can take the code in listing 3.13 and make a direct translation to C#, which will give us the code in listing 4.

Listing 4. The Scheduling DSL code, translated to C#
task("warn if website is down", delegate
    this.every( this.Minutes(3) );
    this.starting ( this.now );
    this.condition = delegate
        return new WebSite("http://example.org"). IsNotResponding;
    this.then( delegate
        this.notify( "admin@example.org", "server down!");

Take a look back at the original DSL text in listing 3.11, and compare it to listing 4. In terms of functionality, they’re the same, but the syntactic differences between them are huge, and we want a good syntax for our DSL.

We’ve skipped one important part; we haven’t talked yet about what the implicit base class will do. The result of the implicit base class resolving its base class is shown in listing 5.

Listing 5. The full class that was generated using the implicit base class
public class MyDemoTask ( BaseScheduler ):
            def override Prepare():
                 task("warn if website is down"), def():
                     # the rest of the code

Now that we have a firm grasp of what code we’re getting out of the DSL, we need to get to grips with how we can run this code.

  •  DSLs in Boo : Combining domain-driven design and DSLs
  •  Active Directory Domain Services 2008 : Restore Group Policy Objects and Starter GPOs
  •  Active Directory Domain Services 2008 : Comment Group Policy Objects and Starter GPOs, View, Print, and Save a Report for Group Policy Objects, Back Up Group Policy Objects and Starter GPOs
  •  Apple Society … Money x Property
  •  Active Directory Domain Services 2008 : Create a New Group Policy Object from a Starter GPO, Edit Group Policy Objects and Starter GPOs, Copy Group Policy Objects and Starter GPOs
  •  Active Directory Domain Services 2008 : Create Group Policy Objects, Delete Group Policy Objects, Create Starter GPOs, Delete Starter GPOs
  •  System Center Configuration Manager 2007 : Architecture Design Planning - Device Management Planning
  •  System Center Configuration Manager 2007 : Architecture Design Planning - Software Update Planning
  •  Programming .NET Components : Marshaling-by-Reference Activation Modes (part 2) - Server-Activated Singleton
  •  Programming .NET Components : Marshaling-by-Reference Activation Modes (part 1) - Server-Activated Single Call
  •  Programming .NET Components : Remoting - Remote Object Types
  •  Active Directory Domain Services 2008 : Seize the RID Master Role, Seize the PDC Emulator Role, Seize the Infrastructure Master Role
  •  Active Directory Domain Services 2008 : Seize the Schema Master Role, Seize the Domain Naming Master Role
  •  Synology DS212+
  •  QNAP TS-219P II Turbo NAS
  •  Netgear Readynas Duo V2
  •  Iomega Storcenter Ix2 Network Storage Cloud Edition
  •  Freecom Silverstore 2-Drive NAS 2TB
  •  IBM WebSphere Process Server 7 : Installing WID on Windows
  •  IBM WebSphere Process Server 7 : WebSphere Integration Developer overview
    Top 10
    Nikon 1 J2 With Stylish Design And Dependable Image And Video Quality
    Canon Powershot D20 - Super-Durable Waterproof Camera
    Fujifilm Finepix F800EXR – Another Excellent EXR
    Sony NEX-6 – The Best Compact Camera
    Teufel Cubycon 2 – An Excellent All-In-One For Films
    Dell S2740L - A Beautifully Crafted 27-inch IPS Monitor
    Philips 55PFL6007T With Fantastic Picture Quality
    Philips Gioco 278G4 – An Excellent 27-inch Screen
    Sony VPL-HW50ES – Sony’s Best Home Cinema Projector
    Windows Vista : Installing and Running Applications - Launching Applications
    Most View
    Bamboo Splash - Powerful Specs And Friendly Interface
    Powered By Windows (Part 2) - Toshiba Satellite U840 Series, Philips E248C3 MODA Lightframe Monitor & HP Envy Spectre 14
    MSI X79A-GD65 8D - Power without the Cost
    Canon EOS M With Wonderful Touchscreen Interface (Part 1)
    Windows Server 2003 : Building an Active Directory Structure (part 1) - The First Domain
    Personalize Your iPhone Case
    Speed ​​up browsing with a faster DNS
    Using and Configuring Public Folder Sharing
    Extending the Real-Time Communications Functionality of Exchange Server 2007 : Installing OCS 2007 (part 1)
    Google, privacy & you (Part 1)
    iPhone Application Development : Making Multivalue Choices with Pickers - Understanding Pickers
    Microsoft Surface With Windows RT - Truly A Unique Tablet
    Network Configuration & Troubleshooting (Part 1)
    Panasonic Lumix GH3 – The Fastest Touchscreen-Camera (Part 2)
    Programming Microsoft SQL Server 2005 : FOR XML Commands (part 3) - OPENXML Enhancements in SQL Server 2005
    Exchange Server 2010 : Track Exchange Performance (part 2) - Test the Performance Limitations in a Lab
    Extra Network Hardware Round-Up (Part 2) - NAS Drives, Media Center Extenders & Games Consoles
    Windows Server 2003 : Planning a Host Name Resolution Strategy - Understanding Name Resolution Requirements
    Google’s Data Liberation Front (Part 2)
    Datacolor SpyderLensCal (Part 1)