This post originated from an RSS feed registered with Ruby Buzz
by Obie Fernandez.
Original Post: Expressing Contract Terms in a DSL
Feed Title: Obie On Rails (Has It Been 9 Years Already?)
Feed URL: http://jroller.com/obie/feed/entries/rss
Feed Description: Obie Fernandez talks about life as a technologist, mostly as ramblings about software development and consulting. Nowadays it's pretty much all about Ruby and Ruby on Rails.
My favorite type of business Domain Specific Language (DSL) is
one that defines a business
process which varies in subtle, yet significant ways depending on the
trading partner involved.
My current project features a Ruby-based DSL crafted to
capture the intricacies of the contractual payment
rules established
between a bank and its partners. Instances of scripts written in that
DSL are executable code, but they are authored by finance
department analysts and stored in a database. My team is currently
fleshing out the functionality provided by this custom DSL syntax,
porting existing logic over to new DSL scripts for each partner, and
doing a bunch of Rails coding related to the script's web-based
authoring tools and approval workflow.
The PL-SQL stored procs and manual workflow that we are
replacing stores its
business
rules as records in the database, as individual rows in a wide rules
table*. On the other hand, the primary entity of our new
system is a Ruleset: a collection of
compensation rules that map to a real-life, paper-based, contractual
obligation.
*A collection of rows in
the database representing discrete rules might be considered more normalized
from
a traditional DBA point-of-view, but it takes a lot more work and
implementation to pull them together into a form that is easily
verified by a non-technical user. (More on that issue in a later
post...)
Really damp DSLs have bubbles, those extra
little words that act as mental padding and keep the code from looking
too scary to a non-technical user. Our actual DSL doesn't have any, but
maybe it should...
Why bother?
How did we arrive at the decision to solve this problem with a
custom DSL. The main factor was accuracy. On any given
month, much work goes into auditing the old system to determine
that it did not make mistakes that 1) jeapordize the client
relationship by underpaying or 2) cost the bank more money than
necessary by overpaying.
During initial analysis I decided that the most effective way
to
"desk-check" the validity of a a Ruleset would be to map
directly to the contract in place. My primary goal was to enable the
finance department to view instances of
Rulesets as actual specifications and validate them
independently of folks from the technology group. By using a Ruby-based
DSL, I would be
able to execute my scripts in different contexts to yield
anything from full-text english, to SQL, to arrays for drop-down boxes.
The Publishing Agreement example
Applications that calculate payouts on a regular basis are
good candidates for a DSL approach. For instance, the following text is
drawn from a real publishing contract:
Except
as otherwise provided in Paragraph 14 of this Agreement, on all copies
of the
original English language version of the Work and revisions thereof in
book
form that we sell in the United States and its possessions, we will pay
you the
following royalties on each edition of the Work, based on the actual
net receipts from the sale or
license of the Work received
by us or our affiliates, if
the sales are made by our affiliates (less discounts and returns, and
excluding
sales taxes and transportation charges) (the “Net
Amount”): twenty percent (20%).
We
agree to report to you on the sale of the Work semi-annually: on or
before
April 1, for the period ending the previous December 31, and on or
before
October 1, for the period ending the previous June 30
What might the above contract language look like in Ruby DSL
code?
publishing agreement dated '9/20/2005'
with_author 'Joe W. Author', social('555-493-3920')
for_title 'DSLs for Dummies'
report do
calculate 'Royalties', as
net_retail_sales.during(last_six_months) * 20.percent end
The header section of the script clearly identifies the
agreement, author and title. The report method takes a block to execute
when a royalty calculation is needed. Depending on
the context, the calculate method might actually go out and
write records to a database fact table or hit a webservice, or
whatever....
In my next blog entry on the subject, I'll refer back to this
example and show you some of the underlying Ruby code that provides
execution contexts.