Saturday, March 08, 2008

rAtom 0.2.1 Gem released

rAtom is a Ruby Gem for working with the Atom Syndication Format and the Atom Publishing Protocol (APP).

rAtom was originally built to support the communication between a number of applications built by Peerworks[http://peerworks.org], via the Atom Publishing protocol. However, it supports, or aims to support, all the Atom Syndication Format and Publication Protocol and can be used to access Atom feeds or to script publishing entries to a blog supporting APP.

Features:
  • Uses libxml-ruby so it is _much_ faster than a REXML based library.
  • Uses the libxml pull parser so it has much lighter memory usage.
  • Supports RFC 5005 (http://www.ietf.org/rfc/rfc5005.txt) for feed pagination.

You can install via gem using:
# sudo gem install ratom

Usage

To fetch and parse an Atom Feed you can simply:
feed = Atom::Feed.load_feed(URI.parse("http://example.com/feed.atom"))

And then iterate over the entries in the feed using:

feed.each_entry do |entry|
# do cool stuff
end

To construct a Feed

feed = Atom::Feed.new do |feed|
feed.title = "My Cool Feed"
feed.id = "http://example.com/my_feed.atom"
feed.updated = Time.now
end

To output a Feed as XML use to_xml

> puts feed.to_xml
<feed xmlns="http://www.w3.org/2005/Atom">
<title>My Cool Feed</title>
<id>http://example.com/my_feed.atom</id>
<updated>2008-03-03T23:19:44+10:30</updated>
</feed>

Publishing

To publish to a remote feed using the Atom Publishing Protocol, first you need to create a collection to publish to:
collection = Atom::Pub::Collection.new(:href => 'http://example.org/myblog')

Then create a new entry:
entry = Atom::Entry.new do |entry|
entry.title = "I have discovered rAtom"
entry.authors << name =""> 'A happy developer')
entry.updated = Time.now
entry.id = "http://example.org/myblog/newpost"
entry.content = Atom::Content::Html.new("<p>rAtom lets me post to my blog using Ruby, how cool!</p>")
end

And publish it to the Collection:
published_entry = collection.publish(entry)

More Information

See http://ratom.rubyforge.org for more information.

Wednesday, September 26, 2007

Building Erlang with Rake

I've recently started using Erlang on a project. This is my first foray into Erlang and I'm using Joe Armstong's new book Programming Erlang as introductory material. It is a great introduction to the language, although I wish it covered more of the OTP. I suppose there is the OTP Design Principles guide but sometimes a good paper book written in conversational style is better when you are learning.

In the book Joe describes building an Erlang project using make and most of the downloadable bits of Erlang code use make too; this makes sense, make is virtually ubiquitous. However the other components of this project are Ruby on Rails applications and make heavy use of Rake, so I figure why subject myself to Make when I can use the beautiful Rake.

Here is my Rakefile for building an Erlang project.

require 'rake/clean'

INCLUDE = "include"
ERLC_FLAGS = "-I#{INCLUDE} +warn_unused_vars +warn_unused_import"

SRC = FileList['src/*.erl']
OBJ = SRC.pathmap("%{src,ebin}X.beam")
CLEAN.include("ebin/*.beam")

directory 'ebin'

rule ".beam" => ["%{ebin,src}X.erl"] do |t|
sh "erlc -pa ebin -W #{ERLC_FLAGS} -o ebin #{t.source}"
end

task :compile => ['ebin'] + OBJ
task :default => :compile


This gives you rake compile which builds all the .erl files in the src subdirectory into the .beam files in the ebin directory. Files will only be built if they have changes since the last build. You also get rake clean which deletes all the .beam files from the ebin directory.

I much prefer this over the equivalent Makefile and it is much easier to extend since you have the full power of Ruby at your disposal.

Enjoy.

Update: Fixed bug when compile many files from after clean.

Monday, September 03, 2007

Testing ActiveResource

I recently started using ActiveResource on a project. ActiveResource is the client side of the REST picture, that is it can consume the REST style web services you get with Rails, think of it as ActiveRecord for HTTP.

One of the difficult parts of using ActiveResource is testing. You don't want to run it over a real HTTP connection, for one it would significantly slow down your tests and secondly you don't know what horrible side effects it might have. So you need some way to fake a HTTP connection and HTTP requests and responses. Enter the HttpMock class.

HttpMock is a fairly nice class put together by the Rails folk that allows you to register a bunch of request and response pairs for a fake HTTP connection. When you call a method on an ActiveResource in a test, it calls the HttpMock class instead of trying to create a real connection. It is fairly easy to use, although there is no documentation the blogging community has come to the rescue.

Here is a quick example:

  @matz  = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/1.xml", {}, @matz
end


This creates a request response pair so that when ActiveResource tries to GET /people/1.xml it gets back the xml for the @matz hash. Pretty simple.

However there is a problem. HttpMock is misnamed - HttpMock is a stub not a mock. But the difference is more than semantic, a mock object, like those provided by the excellent Mocha library, will allow you to set expectations on a object. An expectation is like saying "for this test to pass, this method with these arguments must be called on this object". HttpMock doesn't do that, instead it is just a stub that returns a value when a method is called with a certain argument, there is no verification that the method is actually called.

To show why this is bad, lets look at the test for resource deletion from the ActiveResource unit tests themselves.

Firstly, in the setup method we use HttpMock to create response for the delete method used on the "/people/1.xml" path:

  ActiveResource::HttpMock.respond_to do |mock|
mock.delete "/people/1.xml", {}, nil, 200
end


And here is the test_delete method:

    1   def test_delete
2 assert Person.delete(1)
3 ActiveResource::HttpMock.respond_to do |mock|
4 mock.get "/people/1.xml", {}, nil, 404
5 end
6 assert_raises(ActiveResource::ResourceNotFound) { Person.find(1) }
7 end


So what is happening here? Firstly on line 2 of the test_delete method we call the Person classes delete method. The delete method is a nice one-liner:

      def delete(id, options = {})
connection.delete(element_path(id, options))
end


We can assume here that the the content of the delete method is sending a delete request to "/people/#{id}.xml", that makes sense. The rest of the test_delete method creates a request response pair that returns a 404 error when "/people/1.xml" is requested, all that does is test that ActiveResource::Base.find correctly handles 404.

So what ensures that the delete method does the right thing, all the test does is ensure that it returns something other than nil or false. Lets change the method and see if we can break the test. We'll just return true in the delete method:

      def delete(id, options = {})
true
end


After this change I re-ran the tests and they all passed. This can't be good. If you can effectively remove the body of a method you are testing and the tests still pass you don't have a very good test. So how can we fix it?

Well if HttpMock was truely a mock we could ensure that the HTTP delete method is called on the "/people/1.xml" path. Fortunately HttpMock stores every request it recieves in a class variable. So we can check that the request was received with an assertion.

Here is a new test_delete:

  def test_delete
assert Person.delete(1)
assert ActiveResource::HttpMock.requests.include?(ActiveResource::Request.new(:delete, "/people/1.xml", nil, {}))
end


This one now fails when the body of the delete method is removed and passes when it is put back in, so this way we know that the tests is actually testing the functionality of the method. However it is a bit ugly isn't it? It would be nice if HttpMock allowed you to do this:

    ActiveResource::HttpMock.expects do |http|
http.delete "/people/1.xml", {}, nil, 200
end


But I'll leave that for another blog post.

Monday, May 28, 2007

Index Ruby Objects by an Attribute


class Array
def hash_by(attribute)
self.inject({}) do |hash, e|
hash[e.send(attribute)] = e
hash
end
end
end

Thursday, April 27, 2006

Why GET should always be idempotent: A lesson from Del.icio.us and Sage

This evening I downloaded Sage, a RSS/Atom feed reader extension for Firefox. It is quite a nice feed reader and a big improvement over Firefox's Live Bookmarks which I find hard to use with lots of feeds and IMO over Google's Personalised Homepage which I find doesn't scale too well with lots of feeds and doesn't provide previews of the content.

Sage had a particularly cool feature that jumped out at me, the 'Discover Feeds' feature. This allows you to find all the feeds referenced in a page, rather than just the feed in a link tag in the header. It seems that what it does is follow links that may be potential feeds and checks if they are. You then get a list of all the feeds in the page and can add them to your 'watched' feeds in Sage.

This got me thinking, since I use alot of different computers it is pain trying to manage my list of feeds on each computer, exporting them and copying them around is just asking to get them out of sync and frankly too much effort. ;) So since I already have a Del.icio.us account where I keep my bookmarks I could bookmark all the feeds I care about and tag them with 'feed' in Del.icio.us. Then I just need to navigate to the page for the 'feed' tag click on the Discover Feeds button in Sage and all my bookmarked feeds are there to be added to Sage! Yay online storage of feed links that can easily be added to my feed reader of choice from any computer!!!

Or so I thought. It just so happens that the Delicious 'Delete Bookmark' link is one of the links that Sage follows when it searches for feeds. This has the unfortunate side effect of deleting all my bookmarks in the feed tag. The ideal fix is that Delicious delete action needs to be changed to only respond to a HTTP POST method. This would be comformant with W3C Recommandations too.

I've submitted this bug to Delicious but for now the dream of online bookmarking of feeds to be accessed by Sage remains elusive.

Tags:

Sunday, April 23, 2006

Calling Web Services From Rails

I've notice a few places where people have asked whether Ruby On Rails supports the consumption of web services. Rails itself doesn't. I have found the easiest thing to use is SOAP4R, which according to its web page comes bundled with Ruby 1.8.3 and above.

SOAP4R is really easy to use, it simply generates Ruby stubs from a WSDL which will call the web service for you. You just call the stubs as though they were a Ruby API.

If you have ever used any Java or .NET web service clients, it is pretty much the exact same concept.

Tags:

Thursday, March 09, 2006

Javascript Graph Visualisation

I just found this new graphing library for Javascript. I've been looking for something that does this for a while so I can visualise relationships represented in RDF. The last thing I was looking at was Foafnaut which doesn't seem to be very active and only supports foaf information.

This Javascript library looks really promising though since you should be able update the graph using Ajax which would be really cool. It's just in the beginning stages now but I'll definately be keeping an eye on it.

Technorati: