David,
Thanks a lot for your information. I've tried simply using sinatra
and haml, I was able to direct the request to any format I would like to,
but when I started using DC framework (Rabbit), for some reason, haml
always looks for template with the following pattern
xml haml :"index" => index.xml.haml
json haml :"index" => index.json.haml
vs. pure Sinatra and Haml
xml haml :"index" => index.haml
json haml :"index" => index.haml
As I indicated in an earlier post, I would like to use xml as a base
to convert the xml response to other format requested, to be able to do
that, I need to be able to for request other than xml , make haml to deal
with xml template, then work with the response to produce other format.
Where and how can I change this behavior in DC?
Thanks.
Tong Li
Emerging Technologies & Standards
B062/K317
[email protected]
From: David Lutterkort <[email protected]>
To: [email protected]
Date: 09/02/2011 06:25 PM
Subject: Re: DeltaCloud Haml use
On Fri, 2011-09-02 at 11:02 -0400, Tong Li wrote:
> When implementing RESTful APIs, often you have to support both XML and
JSON
> (sometimes also ATOM Feed), Ruby has nice functions to convert between
XML
> and JSON. In DeltaCloud, very often see code like this.
We used to convert directly from model objects to XML/JSON; that's kinda
dangerous, because any change in a model object would lead to a change
in the output XML, i.e. it was too easy to break the API with an
innocuous-looking code change.
That's why we switched to explicit templates, so that output changes
require a more conscious developer action.
Frankly, I hadn't thought about the route of generating JSON from XML;
it would require that you generate the XML, parse it into a DOM, and
then transform it to JSON. We haven't established fixed rules for the
XML -> JSON conversion, but it seems there's a natural mapping that
converts the DOM to a Hash:
dom_to_json(<elt attrs>body</elt>) =
{ "elt" => attrs.inject({}) { |h, k,v| h[k] = v }.merge
(convert(body)) }
dom_to_json(<elt1 ...>...</elt1> rest) =
h = convert(rest)
e = convert(<elt1 ...>...</elt1>)
if h["elt1"]
if h["elt1"].is_a?(Array)
h["elt1"] << e["elt1"]
else
h["elt1"] = [ h["elt1"], e["elt1"] ]
end
end
That of course only works if there's no conflict between attribute names
and the names of child elements; but that should be the case for the
Deltacloud XML.
With that, your typical respond_to block would then look like
respond_to do |format|
format.xml { haml :"index" }
format.html { haml :"index" }
format.json { xml_to_json(xml_parse(haml :"index", :format
=> :xml)) }
end
Of course, all that repetitive json mumbo jumbo should be hidden in a
helper.
BTW, be careful with Rails' Hash.from_xml - it drops attributes, e.g.:
>> Hash.from_xml("<elt attr='1'>body</elt>")
=> {"elt"=>"body"}
> This is to ensure that the json format contains absolutely
same
> information as in xml format and only provide one xml haml file, the
> problem is that now I do not know how to change the request so that the
> template searches for index.xml.haml rather than index.json.haml when
> request for json. I think there should be a way to do this, but within DC
> rabbit, I do not know if there is an easy way.
The 'haml' function actually comes from Sinatra[1]; you can specify a
format explicitly with 'haml :template, :format => :xml' etc.
> of course, hash data can be
> directly converted to json, but sometimes, the xml haml may take in data
> from more than one objects, it will be a lot easier to simply direct the
> request to xml and get the xml object then convert it to json or even
feed,
> Any pointer will be appreciated.
Yes, I really like this idea; do you think you could write a patch for
that ?
David
[1] http://www.sinatrarb.com/intro#Haml%20Templates