atom-tools’ *NIX tools
atom-tools’ bin directory has several UNIXy tools that I’ve never really mentioned before.
These tools operate on “collections”. I’m using the term in a broader sense than RFC 5023; a “collection” can be an AtomPub Collection, a directory containing Atom Entries, or a feed on stdin or stdout.
atom-cp
atom-cp copies the contents of a source collection to a destination collection.
atom-grep
atom-grep prints a feed to stdout containing all the entries in the source feed that match a given regexp.
atom-purge
atom-purge DELETEs every entry in the given collection.
atom-post
atom-post POSTs the contents of a file or stdin to a given URL.
It doesn’t do anything fancy, it’s just a convenient way of getting media or an Entry created by some other tool onto the web. Eventually this will construct Entries too, but not yet.
Use Cases
Back up your blog:
atom-cp http://example.org/coll ./backup/
Restore a backup, or post several pre-created entries:
atom-cp ./backup/ http://example.org/coll
Delete spam:
atom-grep poker http://example.org/comments | atom-purge -
Plagiarise somebody else’s blog:
atom-grep "popular content" http://example.org/coll | atom-cp - http://example.com/seo
Post a picture to your media collection:
atom-post -m image/png http://example.org/media icon.png
(Disclaimer: XML is a terrible format to pass down a pipe. It’s awfully convenient though, and my pipes and my collections haven’t been long enough for it to be a problem.)
Too many Ruby Atom libraries
Ruby now has 3 Atom Publishing Protocol libraries: atom-tools, atomutil and ratom. They all use the Atom namespace, and they’re all incompatible. Not a great situation.
ratom uses libxml-ruby rather than REXML. It’s embarassingly faster than atom-tools. ratom can parse the 1100 Atom Entries in my Venus cache in 0.25s; atom-tools takes 6s.
I haven’t been able to get atomutil to work. I expect it is faster than atom-tools, too (though not nearly so dramatically); atom-tools parses an XML tree into a tree of Ruby objects (instead of just wrapping the XML tree). This may have been a mistake.
atom-tools 2.0
Published atom-tools 2.0 a few days ago.
The XML parsing and building has been completely reworked. This should make handling extensions much easier.
A big feature for people writing clients is HTTP caching support (ported from Joe Gregorio’s httplib2). I actually commited this to the darcs repository the same day I published 1.0 but never released a 1.1 version (oops!).
It’s got some new UNIX-y tools that I’ll write about later.
I’ve gotten rid of the YAML mapping entirely; it wasn’t as human-read/writeable as I had hoped. I think there’s promise in doing something with Maruku for that.
I’m slowly moving from Test::Unit to rspec (thanks to a lot of grunt work by Simon Rozet). I think the result is a lot cleaner.
I’ve tried to keep things as backwards-compatible as possible, but some things have changed since the 1.0 release. The main difference is that Atom::Collection now represents an app:collection element, instead of just being a fancy Atom::Feed.
Celebrating the New Year with Shoes

A bit late for most of the world, but I’ll post it anyways.
Finished cleaning the apartment and waiting for guests to arrive, I thought I’d play around a bit with Shoes. Turns out it’s a lot of fun!
require "time"
midnights = [ [ 5, 'Rio de Janeiro' ],
[ 4, 'Santiago' ],
[ 3.5, "St. John's" ],
[ 3, 'Halifax' ],
[ 2, 'Montréal' ],
[ 1, 'Winnipeg' ],
[ 0, 'Edmonton' ],
[ -1, 'Vancouver' ],
[ -2, 'Anchorage' ],
[ -3, 'Honolulu' ],
[ -4, 'Auckland' ] ]
new_year = Time.parse "2008-01-01"
Shoes.app do
background rgb(0,0,0)
stack do
seconds_left = (new_year - Time.now).to_i
midnight_ps = midnights.map do |offset,place|
para "#{place}: #{seconds_left - offset * 3600}", :stroke => white
end
animate(1) do |i|
seconds_left = (new_year - Time.now).to_i
midnight_ps.each_with_index do |p,i|
offset, place = midnights[i]
s = seconds_left - offset * 3600
stroke = s > 0 ? white : gray(90)
if s.abs < 500
size = 200
else
size = 100_000 / s.abs
end
p.replace "#{place}: #{s}", :font => "Arial #{size}px", :stroke => stroke
end
end
end
end
New Ruby Atom Library
A new Ruby Atom format/Publishing Protocol library, atomutil has just been released. I haven’t had a chance to look at it deeply yet (thanks to final exams), but it’s well documented, and it looks like it has good support for extensions elements (much better than atom-tools has at the moment).
Stupid Atom Tricks: XMPP Bot
Continuing my habit of writing x-to-Atom gateways, here’s a little Jabber bot that will post messages you send it to an Atom Publishing Protocol Collection.
It requires atom-tools and the xmpp4r library.
require "atom/collection"
require "xmpp4r"
$coll_user = "bct"
$coll_pass = "atom-password"
$coll_url = "http://necronomicorp.com/testatom?app"
$bot_jid = "test@atompub.necronomicorp.com"
$bot_pass = "xmpp-password"
http = Atom::HTTP.new
http.user = $coll_user
http.pass = $coll_pass
coll = Atom::Collection.new $coll_url, http
cl = Jabber::Client.new($bot_jid)
cl.connect
cl.auth($bot_pass)
cl.add_message_callback do |msg|
e = Atom::Entry.new
e.content = msg.body
res = coll.post! e
if res.code == "201"
cl.send(Jabber::Message.new(msg.from, "success!"))
else
cl.send(Jabber::Message.new(msg.from, "#{res.code} #{res.message}"))
end
end
Thread.stop
Try it out; send a message to test@atompub.necronomicorp.com. It should appear in my test collection.
(The real version is a server component which handles several collections on different JIDs.)
atom-tools 0.9.0 on its way
The version number’s a bit of a jump, but only because I’d like to hit 1.0 at about the same time the Publishing Protocol makes it to RFC.
No real changelog I’m afraid, but here’s the big stuff:
- actual installer!
- actual documentation!
- an actual test suite! with Conformance Tests!
- an actual license! (X11)
- media collections!
- full parsing and creation of feeds!
- oh, and draft-ietf-atompub-protocol-11
One of these days I’m going to have to set up Trac.
data: URIs in Ruby
Anne van Kesteren’s got a cute little demonstration of <canvas/>. I particularly like the idea of saving something to a data: URI; one of those cool little things about the Web that doesn’t get much attention.
I also like favicons, and not having to use a general-purpose image manipulation program to create them. Hence, a teeny-tiny Camping mount which will convert an image/png data: URI into a favicon (example). At its heart is a class for handling data: URIs that goes something like this:
module URI
class Data < Generic
attr_reader :mime_type, :data
def read; data; end
def initialize(scheme, userinfo, host, port,
registry, path, opaque, query, fragment)
super
notdata, data = opaque.split(",", 2)
match = URI.decode(notdata).match(/^(.*?)(;base64)?$/)
@mime_type = match[1]
if @mime_type.empty?
@mime_type = "text/plain;charset=US-ASCII"
elsif @mime_type[0] == ?;
@mime_type = "text/plain#{@mime_type}"
end
@data = unless match[2].nil?
Base64.decode64(data)
else
URI.decode(data)
end
end
end
@@schemes["DATA"] = Data
end
It’s not a complete implementation of RFC 2397; some legitimate URIs (eg. data:text/plain;charset=iso-8859-7,%be%fg%be) can’t be parsed without fiddling around with uri.rb’s internals.
(BTW, the favicon is much shrunken from Paintr’s canvas; you’ll have to go over your lines several times to get something you can see.)