In a recent client project, I thought it might be a cool idea to set up a mini-blog chronicling my work on their site, so I started with the blog included in the Camping microframework, and hacked it to meet my needs.
In short order, I ran into one problem that I’m surprised didn’t get more coverage: how to print XML (or anything, really), not HTML from a Camping app. After going over the problem with Evan Weaver on #camping
, I tried out a couple of approaches, and found one that seems to work well.
Suppose you want to generate an Atom feed for your app. First thing you need to do is load up Builder, a handy library for generating XML documents.
require 'builder'
The next thing you want to do is add a controller for getting a feed:
module MyApp::Controllers
class Feed < R '/feed/myfeed.atom'
def get
@headers['Content-type'] = 'application/atom+xml'
@entries = Models::Posts.find :all, :order => 'date DESC', :limit => 20
render :feed
end
end
end
Your @entries
will obviously look different; I’m just collecting the 20 most recent posts for my Atom feed. You’ll also note that I’m setting the Content-type
of the document as well; if you don’t do that, Camping will assume the Content-type
is text/html
.
Now we get to the interesting part. As you know, Camping only prints out HTML by default, and provides no obvious means of specifiying any other type of markup (at least from what I can tell). Here’s the solution:
module Blog::Views
def layout
case @headers['Content-type']
when 'application/atom+xml'
self << yield # no template, please!
else
# html layout goes here
end
end
def feed
b = Builder::XmlMarkup.new(:indent => 2)
xml = b.feed({ :xmlns => "http://www.w3.org/2005/Atom" }) do |x|
x.title("sample feed")
x.link( { :href => "http://localhost:3301/feed/myfeed.atom"})
x.updated(Time.now.strftime("%Y-%m-%dT%H:%M:%SZ"))
x.author do
x.name("Robert Hahn")
end
x.id("http://localhost:3301/feed/myfeed.atom")
# generate the feed entries
@entries.each do |e|
x.entry do
x.title(e.title)
x.link({ :href => "http://localhost:3301//view/#{e.post_id}"})
x.id({ :href => "http://localhost:3301//view/#{e.post_id}"})
x.updated(e.date.strftime("%Y-%m-%dT%H:%M:%SZ"))
x.content({ :type => 'text'}) do
x.text! e.body
end
end
end
end
end
text xml #print the Atom feed
end
So we needed to do a couple of things: first, in the layout, we check the Content-Type
. If it’s application/atom+xml
. , then don’t print the contents of the layout
method. Next, we generate our Atom feed. The last thing to do is print it.
This is probably the best way to do this for the time being. If I get some time, I’ll look at seeing how to modify Camping to support multiple layouts.
Copyright © 2009
Robert Hahn.
All Rights Reserved unless otherwise indicated.