Domain Specific Languages (DSLs) are the idea of creating syntaxes that model a very specific problem domain. Domain Specific Languages are not a new concept. Some people call them 'little languages'. The Unix world has a bunch of little languages. Grep, awk, sed, lex, and yacc all exhibit features of these domain specific languages. They are little tools that do one thing well. In these cases they are often highly encoded and not in natural language of any sort. Modern domain specific languages should aim to be humane and literate in the language of the user.
Domain Specific Languages should be expressed in the language of the problem being solved. They are a higher level of abstraction than for loops and object instantiation. They are at the level of abstraction of the problem space. Neal Ford uses the example of "venti nonfat decaf whip latte". What am I talking about if I use those terms? If you guessed coffee, then you know the Jargon of one coffee chain out there. The person listening to the order understands that you are ordering a decaf coffee drink of a certain size, with non-fat milk and whipped cream. There is a lot of shared context in the Jargon of the coffee drinker and the coffee order taker. This shared context sets the stage for a rich conversation without a lot of unnecessary noise. This is true of all Jargon.
# CoffeeDSL.rb
# This is the input from the user, likely read from a file
# or input through a user interface of some sort
CoffeeInput = "venti nonfat decaf whip latte"
class Coffee
def method_missing(symbol)
name = symbol.to_s
if %w(venti grande).include?(name)
@size = name
elsif %w(whip nowhip).include?(name)
@whip = 'whip'.eql?(name)
elsif %w(caf decaf halfcaf).include?(name)
@caf = name
elsif %w(regular latte cappachino).include?(name)
@type = name
elsif %w(milk nonfat).include?(name)
@milk = name
else
raise ArgumentError, "Unknown coffee informantion: #{name}."
end
end
def order
params = ''
params += @milk + ' ' if @milk
params += @caf + ' ' if @caf
params += 'whip ' if @whip
print "Ordering coffee: #{@size} #{params}#{@type}\n"
end
def load
# turn one line into multi-line "method calls"
cleaned = CoffeeInput.gsub(/\s+/, "\n")
self.instance_eval(cleaned)
end
end
# this is your code which loads the DSL input and executes it
coffee = Coffee.new
coffee.load # load the user input
coffee.order # submit the order
Jargon is the terminology of a specific proffession or group. Does your user community or problem space have a vocabulary? Can they express the things they want out of a system using that vocabulary or Jargon? If so, there is a very real possibility that you could utilize a DSL to solve some set of problems for those users.
What about field validation?
# ValidationDSL.rb
# Input from the user that would be read in
Input = <<EOF
min_length 2
max_length 5
EOF
class Module
def dsl_accessor(symbol)
define_method("#{symbol}") do |v|
instance_eval
%{
@#{symbol} = v
}
end
end
end
class ValidateDSL
dsl_accessor :max_length
dsl_accessor :min_length
def validate(field)
if @max_length and field.length > @max_length
return false
end
if @min_length and field.length < @min_length
return false
end
return true
end
def load
self.instance_eval(Input)
end
end
val = ValidateDSL.new
val.load
print val.validate('foo')
print "\n"
print val.validate('')
print "\n"
print val.validate('abbbbbbbbbbbbbbbbbbbbbb')
print "\n"
How does your user community talk about a problem? Can they easily express what they intend with simple Jargon that they already know? Is that more natural for a power user than some complicated UI with buttons and checkboxes? Then you might have a good place to use a DSL.