Playing with Boo's DSLs

time to read 2 min | 326 words

Boo has gotten a lot better in terms of flexibility lately, a lot better. I was able to churn this little DSL in about an hour:

OnCreate Account:
	Entity.AccountNumber = date.Now.Ticks
	
	
 OnCreate Order:
	if Entity.Total > Entity.Account.MaxOrderTotal:
BeginManualApprovalFor Entity

From the perspective of the DSL, it is very easy to use, and from the implementer perspective, this was ridiculously easy to implement.

The only half-way complex thing here is the introduce base class action, which is usually the first approach that you take, which is why I don't consider it complex. Then you can just do create the base class:

abstract class DslBase:

	[property(Entity)] 
entity as object
        def OnCreate(entity as System.Type, action as callable()):
		Actions.RegisterOnCreate(entity.Name, action)
		
	abstract def Execute():
		pass
	
	def BeginManualApprovalFor(order as Order):
		print "Starting approval process for Order #${order.Id}"

In the base class you just do what you would normally do, in this case, it just register the new action. Here is the implementation:

class Actions:
	
	static onCreateActions = {}
	
	static def RegisterOnCreate(entityName as string, action as callable()):
		onCreateActions[entityName] = action
		
	
	static def OnCreate(entity as object):
		entityName = entity.GetType().Name
		action = onCreateActions[entityName] as callable()
		if not action:
			print "No action defined for ${entityName}"
			return
		dsl = (action.Target as DslBase)
		dsl.Entity = entity
		action()

The client code is simply:

SampleDSL().Prepare("Sample.boo")

account = Account(MaxOrderTotal: 100)

print "Before: ${account.AccountNumber}"
Actions.OnCreate( account )
print "After: ${account.AccountNumber}"

order = Order(Account: account, Total: 500 )
Actions.OnCreate( order )

All you have left then is to get a rich enough library of methods on the DslBase, for the business user to work with, document how this is done, and that is it.