<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Coffee Powered &#187; jsonp</title>
	<atom:link href="http://www.coffeepowered.net/tag/jsonp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.coffeepowered.net</link>
	<description>code and content</description>
	<lastBuildDate>Sun, 05 Sep 2010 20:38:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Powerful, easy, DRY, multi-format REST APIs</title>
		<link>http://www.coffeepowered.net/2008/09/27/powerful-easy-dry-multi-format-rest-apis/</link>
		<comments>http://www.coffeepowered.net/2008/09/27/powerful-easy-dry-multi-format-rest-apis/#comments</comments>
		<pubDate>Sun, 28 Sep 2008 03:53:48 +0000</pubDate>
		<dc:creator>Chris Heald</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[jsonp]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[yaml]]></category>

		<guid isPermaLink="false">http://www.coffeepowered.net/?p=38</guid>
		<description><![CDATA[Rails&#8217; baked-in REST support is great. Build your app right, and you can expose a programmatic interface to your users for free. That said, many times providing views in non-HTML formats tends to be bulky and unwieldy. You end up with either very brittle representations of your data, or extremely bulky respond_to blocks in your [...]]]></description>
			<content:encoded><![CDATA[<p>Rails&#8217; baked-in REST support is great. Build your app right, and you can expose a programmatic interface to your users for free.</p>
<p>That said, many times providing views in non-HTML formats tends to be bulky and unwieldy. You end up with either very brittle representations of your data, or extremely bulky respond_to blocks in your controllers.</p>
<p>Fortunately, there&#8217;s a better way! We&#8217;re going to provide two new render targets, <code>:to_yaml</code> and <code>:to_json</code> which will let us write a single XML builder view, and then provide that view in XML, YAML, and JSON formats according to the consuming developer&#8217;s preferences.</p>
<p>In <code>application.rb</code> you&#8217;ll want to override the render method.</p>
<pre class="brush: ruby;">
def render(opts = {}, &amp;block)
  if opts[:to_yaml] then
    headers[&quot;Content-Type&quot;] = &quot;text/plain;&quot;
    render :text =&gt; Hash.from_xml(render_to_string(:template =&gt; opts[:to_yaml], :layout =&gt; false)).to_yaml, :layout =&gt; false
  elsif opts[:to_json] then
    content = Hash.from_xml(render_to_string(:template =&gt; opts[:to_json], :layout =&gt; false)).to_json
    cbparam = params[:callback] || params[:jsonp]
    content = &quot;#{cbparam}(#{content})&quot; unless cbparam.blank?
    render :json =&gt; content, :layout =&gt; false
  else
    super opts, &amp;block
  end
end
</pre>
<p>As you can see, we render a single XML view, and then load it to a hash from XML, and use Rails&#8217; built-in <code>Hash#to_json</code> and <code>Hash#to_yaml</code> methods to provide the data in the desired format. There is a single glaring problem with this approach, though &#8211; <code>Hash#from_xml</code> is <em>dog slow</em> because it uses REXML. There&#8217;s a fantastic solution, though!</p>
<p>Courtesy of a blog post over at <a href="http://www.visnup.com/entries/423-cobravsmongoose-not-slow-vs-hashfrom_xml-slow-vs-faster_xml_simple-fast">cobravsmongoose</a>, we have a libxml drop-in for <code>Hash#from_xml</code></p>
<p>First, install <a href="http://libxml.rubyforge.org/">libxml</a> and then <a href="http://code.google.com/p/faster-xml-simple/">faster_xml_simple</a>.</p>
<p>Second, include a monkeypatch to <code>Hash#from_xml</code> with the following:</p>
<pre class="brush: ruby;">
require 'faster_xml_simple'
class Hash
  def self.from_xml(xml)
    undasherize_keys(typecast_xml_value(FasterXmlSimple.xml_in(xml,
      'forcearray'   =&gt; false,
      'forcecontent' =&gt; true,
      'keeproot'     =&gt; true,
      'contentkey'   =&gt; '__content__')
    ))
  end
end
</pre>
<p>You can run the benchmarks if you&#8217;d like, but it&#8217;s orders of magnitude faster than REXML. Seriously. Don&#8217;t use REXML. It&#8217;s like trying to run a Ferrari off of a 9-volt battery.</p>
<p>Now, let&#8217;s say you have an action you want to provide HTML, XML, JSON, and YAML views for.</p>
<pre class="brush: ruby;">
def index
  ...
  respond_to do |wants|
    wants.html
    wants.xml  { render :layout =&gt; false }
    wants.json { render :to_json =&gt; &quot;posts/index.xml.builder&quot; }
    wants.yaml { render :to_yaml =&gt; &quot;posts/index.xml.builder&quot; }
  end
end
</pre>
<p>Finally, throw together your <code>index.xml.builder</code> file as you best see fit.</p>
<pre class="brush: ruby;">
xml.instruct! :xml, :version=&gt;&quot;1.0&quot;, :encoding=&gt;&quot;UTF-8&quot;
xml.posts do
  @posts.each do |post|
    xml.post(:id =&gt; post.id) do
      xml.user(:id =&gt; post.user.id) +
      xml.content do
        post.post_body
      end
    end
  end
end
</pre>
<p>And all of a sudden, bam! You&#8217;ve got your posts available in HTML&#8230;</p>
<pre><code>/posts/index</code></pre>
<p>&#8230;and in XML, YAML, and JSON, along with the associated User. By using an XML builder, you can make the serialized data as complex and customized as you&#8217;d like. No more funky respond_to blocks, no more exposing data you don&#8217;t want to. Expose what you want, and just what you want, in several formats.</p>
<pre><code>
/posts/index.xml
/posts/index.yml
/posts/index.json
</code></pre>
<p>One final trick is that the JSON views accept an optional <code>callback</code> or <code>jsonp</code> parameter, which will cause the content to be passed to a Javascript function matching the passed parameter, as per the <a href="http://ajaxian.com/archives/jsonp-json-with-padding">JSONP</a> spec.</p>
<p>For example, if you have a <code>/foo/bar.json</code> view that would render the following JSON:</p>
<pre><code>"{\"foo\":\"bar\"}"</code></pre>
<p>Calling <code>/foo/bar.json?jsonp=returnFunc</code> would return the following:</p>
<pre><code>returnFunc("{\"foo\":\"bar\"}")</code></pre>
<p>Check out the <a href="http://ajaxian.com/archives/jsonp-json-with-padding">JSONP</a> spec for more.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coffeepowered.net/2008/09/27/powerful-easy-dry-multi-format-rest-apis/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using memcached
Page Caching using memcached
Database Caching 10/18 queries in 0.010 seconds using memcached

Served from: www.coffeepowered.net @ 2010-09-07 06:15:39 -->