The ELC Community Blog
A knowledge exchange on Ruby on Rails and Agile Development
Dataportability: XRDS-Simple
by Ryan Garver on May 20, 2008
I've been getting very excited about the Dataportability project (DP) for quite a while now. Their mission is: to promote the idea that individuals have control over their data by determing how they can use it and who can use it. This includes access to data that is under the control of another entity.
It's a very cool idea that is gaining a lot of support in very high places. So far companies like MySpace, Google, Microsoft, and Facebook have openly announce their support of DP and its proposed mission. With that kind of weight (those were only a small sample of the companies backing DP) a lot of things can get done very quickly... or very slowly as the case may be. Fortunately the DP group has kept itself relatively independent from the commercial sponsors that have pledged themselves. In fact most of the literature on the DP website doesn't even mention these sponsors as contributing to the standards. Lets hope they can continue to use this autonomy to the advantage of us all.
Among other technologies and standards currently under development, DP leverages OpenID, OAuth, and a number of Microformats (e.g.: hCard, XFN), as well as FOAF. I think it's important to increase awareness of these DP technologies and so I'm going to start putting together some posts to dig in to what they are, why they were created or chosen, and how they work. To start I want to explore a relatively new addition to the DP family that hasn't really received much publicity so far: XRDS-Simple.
XRDS-Simple is the standard that the DP group is developing to solve the problem of service discovery. That is, a standard protocol and format for sharing what services a user uses, for what purpose (to share video or photos, to broadcast updates, to store contacts), and with what priority. This is really important for situations like showing a photo gallery on a users profile page. Where does the user keep their photos? Flickr? Photobucket? Picasa Web Albums?
An Alternative: The rel="me" microformat
There are some other alternatives, however the DP group was concerned that these standards were either too heavy or under powered for the full extend of the task. One group that has been involved with the DP group since the beginning is the microformats group (µf). They have a µf that nearly satisfies the need for a discovery/directory system for services. The spec is called rel="me". This µf links resources to individuals by marking them as relevant to their profile. This is a very barebones approach, but it is also non-intrusive, as with all µfs. These links can me casually scattered within a person's profile page without impacting the normal formatting. But, with µf aware browsers the information gains meaning within the context.
For the larger goals of the DP project it seems that while rel="me" was driving the right road, it didn't take us far enough. Because if the intentional simplicity of the µf features like purpose of a linked service, the local usernames and IDs for using the service, or the service priority compared to similar services couldn't be described.
So how does it work?
XRDS-Simple is a reduced version of the XRDS standard which was developed by OASIS in conversation with the OpenID community. If you have done any work with OpenID you may recognize XRDS as the format used by the Yadis protocol. Here is a sample XRDS-Simple file (taken from the XRDS-Simple 1.0 Draft 1)
1 <xrds xmlns="xri://$xrds">
2 <xrd version="2.0" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)">
3 <type>xri://$xrds*simple</type>
4 <service priority="10">
5 <type>http://specs.example.com/wish_list/1.0</type>
6 <uri simple:httpmethod="GET">http://books.example.com/wishlist</uri>
7 <localid>jane</localid><localid>
8 </localid></service>
9 <service priority="20">
10 <type>http://specs.example.com/wish_list/1.0</type>
11 <uri priority="10" simple:httpmethod="GET">https://dvds.example.org/lists/wishes</uri>
12 <uri priority="20" simple:httpmethod="GET">http://dvds.example.org/lists/wishes</uri>
13 <localid>janedoe</localid><localid>
14 </localid></service>
15 </xrd>
16 </xrds>
The XRDS-S lists off a collection of services that are described by the sub-element Type. Each Service element is prioritized and within the service a collection of URIs are prioritized. As you can see, the second service has two URIs and prefers the HTTPS one over the non-SSL URI. The last element in each Service element is a LocalID. The LocalID specifies basically a username or some other identifier that the service will tie to the correct user.
I got a little excited about this and decided to do a quick refresher on my Hpricot skills. I threw together a XRDS-Simple parser that returns a hash of XRDs indexed by id if you have a fragment to work with (see the spec on how this works). Each XRD is a hash of services indexed by the Service > Type. Each Service is an array of URIs which are ordered by overall priority.
1 xml = Hpricot::XML(str)
2 xrds = {}
3 (xml/'XRD').each do |xrd|
4 if xrd.attributes['xmlns'] == 'xri://$XRD*($v*2.0)' &&
5 xrd.attributes['version'] == '2.0' &&
6 (xrd%'Type').inner_text == 'xri://$xrds*simple'
7
8 id = xrd.attributes['id'] || xrds.size
9 xrds[id] = {}
10 (xrd/'Service').each do |service|
11 xrds[id][(service/'Type').inner_text] ||= []
12 xrds[id][(service/'Type').inner_text] << [service.attributes['priority'].to_i, (service/'URI').map do |uri|
13 {
14 :method => (uri.attributes.find{|(k,v)| k =~ /httpMethod/}.last),
15 :priority => uri.attributes['priority'].to_i,
16 :local_id => (service%'LocalID').inner_text,
17 :uri => uri.inner_text
18 }
19 end.sort{|l,r| l[:priority]<=>r[:priority]}]
20 end
21 xrds[id].each_key do |key|
22 xrds[id][key] = xrds[id][key].sort{|l,r| l.first<=>r.first}.map{|e| e.last}.flatten.map{|e| e.delete(:priority);e}
23 end
24 end
25 end
If we run this on the above xml and take a look at xrds we will see:
1 {0=>
2 {"http://specs.example.com/wish_list/1.0"=>
3 [{:local_id=>"jane",
4 :method=>"GET",
5 :uri=>"http://books.example.com/wishlist"},
6 {:local_id=>"janedoe",
7 :method=>"GET",
8 :uri=>"https://dvds.example.org/lists/wishes"},
9 {:local_id=>"janedoe",
10 :method=>"GET",
11 :uri=>"http://dvds.example.org/lists/wishes"}]}}
Comments