Writing a DSL with commands split by space
We want to have a DSL where we can write statements separated with space. Like this:
Robot.new do
a b c
d e
f g h i
j
end.run
outputs
Go to a
Go to b
Go to c
Go to d
Go to e
Go to f
Go to g
Go to h
Go to i
Go to j
Since separating by space means method invocation in Ruby,
we need to hack into method_missing
.
I am just posting the codes and will not give it further explanation. Look into the details yourself.
class Robot
attr_accessor :movements
def initialize &block
@movements = []
@last_length = 0
instance_eval &block if block
rearrange
end
def method_missing name, *args
case args.size
when 0
rearrange
@movements.push result = Movement.new(name)
@last_length += 1
result
when 1
@movements.push result = Movement.new(name)
@last_length += 1
result
else
super
end
end
def rearrange
last_movements = @movements.last @last_length
@movements[@movements.size - @last_length..] = last_movements.reverse
@last_length = 0
end
def run
puts @movements
end
class Movement
attr_accessor :target
def initialize target
@target = target
end
def to_s
"Go to #@target"
end
end
end
Similar tricks are used in my other Ruby project alda-rb. The codes can be found on GitHub.