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.

Jesús Gabriel y Galán wrote in post #1161293:

Hi,

2.- I’d rather not have to put quotes around all strings, so I’ve
tried some method missing magic

you have choice between 2 dsl from :

  • internal dsl (use of syntaxe parsing of a host langage)
  • external dsl. (use of a parser and eval his output)

You have choose internal dsl.

With internal dsl, you must respect literal syntaxe of your host langage
(here ruby). If literal syntaxe is not ok, change host lang or change
your dsl definition.

method_missing for string creation is really bad idea :
Object.new.methods.size give 55 methods name. are you sure that your
users
will not need of string which are not in this list ?..

so use external dsl : define a grammar, use a parser, and make
a evaluator of the output of parser…