I created a (Ruby) DSL …

A DSL is a Domain Specific Language – that is, small language that should help frame solutions to particular problems.

In this case, we had some config for a report writer program that was parsed using custom code, things like so:

<report name>
{
  FORMAT = CSV
  RECORD = A.PATH.TO.RECORD
  MODE = streaming
  KEYFIELD = TradeId
  FIELDS {
    Id
    TradeId
    RecordType
    Date
    Time
  }
  WHERE Date = "20120924"
}

The parser was getting hairier and we need to add more features – more complex WHERE clauses etc.  So, it seemed an opportunity to throw in a DSL.  This is the new DSL format for the above:

report "<report name>" do
  format "CSV"
  record "A.PATH.TO.RECORD"
  mode "streaming"
  keyfield "TradeId"
  fields do
    column "Id"
    column "TradeId"
    column "RecordType"
    column "Date"
    column "Time"
  end
  where do
    Date == "20120924"
  end
end

When I first saw things like this, it seemed like magic – Ruby must be doing really complex stuff to handle it – but it isnt :)

Its all down to defining methods with the above names and handling the blocks passed to them.

For example there is a “report” method, like so:

def report(name, &block)
   # save report name and &block of code for later use
end

The “&block” bit is little funky – its a way of capturing the code passed between the “do … end” block above.

To handle the next level down – the code within the report block, I defined a class with those methods and the block is “call”‘d within the context of that block.


class WriterDefInRuby < Java::com.WriterDefBase # to make things fun, the class needs to implement a Java interface :)    def where(&block)      @where_clause = block    end    def setup(&block)      self.instance_eval &block    end ... end [/code] So, the above class is used like this: [code] def report(name, &block)    writer_def = WriterDefInRuby.new    writer_def.name = name    writer_def.setup(&block) end [/code] And thats it. We had a third level down for the fields stuff - but its just more of the same, another class, eval the block in the context of that class. PS Thanks to several sites explaining DSL’s in much better detail and several example gems, like Rose and Docile