DSL question

Hi,

I'm trying to build a little internal DSL to create mini Sinatra
applications for little fakers to test stuff. I want the DSL to be
something like this:

post '/example' do
    content_type application/json
    status 200
    body transaction_status: billed, subscriber_id: asdfasdfasdf,
status: ack, trid: 123412341234
end

Things of note:

1.- I want to be able to pass a Hash as body, and to have it
transparently formatted for the appropriate content type. The default
content type will be application/www-form-urlencoded.

2.- I'd rather not have to put quotes around all strings, so I've
tried some method missing magic. The above example works, so, as you
can see, things like application/json, billed, etc don't need quotes.
I even got this working:

body o-something => other.thing

but there are some things I can't manage. For example the '/example',
or things like

body o-something: otherthing

I also support passing any valid object to body to be returned (like a
string or any valid Sinatra return value). I only format Hashes.

I'm pasting here my current implementation to see if something can be
improved, or you have any advice on how to better approach this:

require 'sinatra/base'
require 'json'

FORMATTERS = {
    "application/www-form-urlencoded" => lambda {|b| b.each.map {|k,v|
"#{CGI.escape(k)}=#{CGI.escape(v)}" }.join("&")},
    "application/json" => lambda {|b| b.to_json}
}

class Stringy
    def initialize o
        @o = o
    end

    # this looks like black magic, and I wonder if in 6 months I'll be
able to understand this :slight_smile:
    def method_missing meth, *args, &blk
        prefix = args.empty? ? "." : ""
        Stringy.new("#{@o.to_s}#{prefix}#{meth}#{args.first.to_s}")
    end

    def to_s
        @o.to_s
    end
end

class MyApp < Sinatra::Base
    def method_missing meth, *args, &blk
        Stringy.new(meth)
    end

    def stringifyKeysAndValues h
        Hash[*h.map{|k,v| [k.to_s, v.to_s]}.flatten]
    end

    before do
        content_type "application/www-form-urlencoded" # default, can
be overriden in the routes
    end

    after do
        if Hash === body
            body FORMATTERS[response.content_type][stringifyKeysAndValues body]
        end
    end
end

MyApp.instance_eval(File.read(ARGV[0]))
MyApp.run!

Any comment is appreciated.

Jesus.