seangeo / ratom
- Source
- Commits
- Network (8)
- Issues (1)
- Downloads (16)
- Wiki (1)
- Graphs
-
Tree:
54d6897
ratom / README.txt
| 46e74293 » | seangeo | 2008-03-03 | 1 | = rAtom | |
| 2 | |||||
| 3 | rAtom is a library for working with the Atom Syndication Format and | ||||
| 4 | the Atom Publishing Protocol (APP). | ||||
| 5 | |||||
| 6 | * Built using libxml so it is _much_ faster than a REXML based library. | ||||
| 7 | * Uses the libxml pull parser so it has much lighter memory usage. | ||||
| 8 | * Supports {RFC 5005}[http://www.ietf.org/rfc/rfc5005.txt] for feed pagination. | ||||
| 9 | |||||
| 10 | rAtom was originally built to support the communication between a number of applications | ||||
| 11 | built by Peerworks[http://peerworks.org], via the Atom Publishing protocol. However, it | ||||
| 12 | supports, or aims to support, all the Atom Syndication Format and Publication Protocol | ||||
| 13 | and can be used to access Atom feeds or to script publishing entries to a blog supporting APP. | ||||
| 14 | |||||
| 15 | == Prerequisites | ||||
| 16 | |||||
| bf8c851a » | seangeo | 2009-03-19 | 17 | * libxml-ruby, >= 1.1.2 | |
| 46e74293 » | seangeo | 2008-03-03 | 18 | * rspec (Only required for tests) | |
| 19 | |||||
| 20 | libxml-ruby in turn requires the libxml2 library to be installed. libxml2 can be downloaded | ||||
| 21 | from http://xmlsoft.org/downloads.html or installed using whatever tools are provided by your | ||||
| 22 | platform. At least version 2.6.31 is required. | ||||
| 23 | |||||
| 24 | === Mac OSX | ||||
| 25 | |||||
| 26 | Mac OSX by default comes with an old version of libxml2 that will not work with rAtom. You | ||||
| 27 | will need to install a more recent version. If you are using Macports: | ||||
| 28 | |||||
| 29 | port install libxml2 | ||||
| 30 | |||||
| 31 | == Installation | ||||
| 32 | |||||
| 33 | You can install via gem using: | ||||
| 34 | |||||
| 35 | gem install ratom | ||||
| 36 | |||||
| 37 | == Usage | ||||
| 38 | |||||
| 39 | To fetch a parse an Atom Feed you can simply: | ||||
| 40 | |||||
| 41 | feed = Atom::Feed.load_feed(URI.parse("http://example.com/feed.atom")) | ||||
| 42 | |||||
| 43 | And then iterate over the entries in the feed using: | ||||
| 44 | |||||
| 45 | feed.each_entry do |entry| | ||||
| 46 | # do cool stuff | ||||
| 47 | end | ||||
| 48 | |||||
| da124144 » | seangeo | 2008-05-03 | 49 | To construct the following example Feed is from the Atom spec: | |
| 46e74293 » | seangeo | 2008-03-03 | 50 | ||
| da124144 » | seangeo | 2008-05-03 | 51 | <?xml version="1.0" encoding="utf-8"?> | |
| 52 | <feed xmlns="http://www.w3.org/2005/Atom"> | ||||
| 53 | |||||
| 54 | <title>Example Feed</title> | ||||
| 55 | <link href="http://example.org/"/> | ||||
| 56 | <updated>2003-12-13T18:30:02Z</updated> | ||||
| 57 | <author> | ||||
| 58 | <name>John Doe</name> | ||||
| 59 | </author> | ||||
| 60 | <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id> | ||||
| 61 | |||||
| 62 | <entry> | ||||
| 63 | <title>Atom-Powered Robots Run Amok</title> | ||||
| 64 | <link href="http://example.org/2003/12/13/atom03"/> | ||||
| 65 | <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> | ||||
| 66 | <updated>2003-12-13T18:30:02Z</updated> | ||||
| 67 | <summary>Some text.</summary> | ||||
| 68 | </entry> | ||||
| 69 | |||||
| 70 | </feed> | ||||
| 71 | |||||
| 72 | To build this in rAtom we would do: | ||||
| 73 | |||||
| 74 | feed = Atom::Feed.new do |f| | ||||
| 75 | f.title = "Example Feed" | ||||
| 76 | f.links << Atom::Link.new(:href => "http://example.org/") | ||||
| 77 | f.updated = Time.parse('2003-12-13T18:30:02Z') | ||||
| 78 | f.authors << Atom::Person.new(:name => 'John Doe') | ||||
| 79 | f.id = "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6" | ||||
| 80 | f.entries << Atom::Entry.new do |e| | ||||
| 81 | e.title = "Atom-Powered Robots Run Amok" | ||||
| 82 | e.links << Atom::Link.new(:href => "http://example.org/2003/12/13/atom03") | ||||
| 83 | e.id = "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a" | ||||
| 84 | e.updated = Time.parse('2003-12-13T18:30:02Z') | ||||
| 85 | e.summary = "Some text." | ||||
| 86 | end | ||||
| 46e74293 » | seangeo | 2008-03-03 | 87 | end | |
| 88 | |||||
| da124144 » | seangeo | 2008-05-03 | 89 | To output the Feed as XML use to_xml | |
| 46e74293 » | seangeo | 2008-03-03 | 90 | ||
| 91 | > puts feed.to_xml | ||||
| 92 | <?xml version="1.0"?> | ||||
| 93 | <feed xmlns="http://www.w3.org/2005/Atom"> | ||||
| da124144 » | seangeo | 2008-05-03 | 94 | <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id> | |
| 95 | <title>Example Feed</title> | ||||
| 96 | <updated>2003-12-13T18:30:02Z</updated> | ||||
| 97 | <link href="http://example.org/"/> | ||||
| 98 | <author> | ||||
| 99 | <name>John Doe</name> | ||||
| 100 | </author> | ||||
| 101 | <entry> | ||||
| 102 | <title>Atom-Powered Robots Run Amok</title> | ||||
| 103 | <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> | ||||
| 104 | <summary>Some text.</summary> | ||||
| 105 | <updated>2003-12-13T18:30:02Z</updated> | ||||
| 106 | <link href="http://example.org/2003/12/13/atom03"/> | ||||
| 107 | </entry> | ||||
| 46e74293 » | seangeo | 2008-03-03 | 108 | </feed> | |
| da124144 » | seangeo | 2008-05-03 | 109 | ||
| 46e74293 » | seangeo | 2008-03-03 | 110 | ||
| 111 | See Feed and Entry for details on the methods and attributes of those classes. | ||||
| 112 | |||||
| 113 | === Publishing | ||||
| 114 | |||||
| 115 | To publish to a remote feed using the Atom Publishing Protocol, first you need to create a collection to publish to: | ||||
| 116 | |||||
| 117 | require 'atom/pub' | ||||
| 118 | |||||
| 119 | collection = Atom::Pub::Collection.new(:href => 'http://example.org/myblog') | ||||
| 120 | |||||
| 121 | Then create a new entry | ||||
| 122 | |||||
| 123 | entry = Atom::Entry.new do |entry| | ||||
| 124 | entry.title = "I have discovered rAtom" | ||||
| 125 | entry.authors << Atom::Person.new(:name => 'A happy developer') | ||||
| 126 | entry.updated = Time.now | ||||
| 127 | entry.id = "http://example.org/myblog/newpost" | ||||
| 128 | entry.content = Atom::Content::Html.new("<p>rAtom lets me post to my blog using Ruby, how cool!</p>") | ||||
| 129 | end | ||||
| 130 | |||||
| 131 | And publish it to the Collection: | ||||
| 132 | |||||
| 133 | published_entry = collection.publish(entry) | ||||
| 134 | |||||
| 135 | Publish returns an updated entry filled out with any attributes to server may have set, including information | ||||
| 136 | required to let us update to the entry. For example, lets change the content and republished: | ||||
| 137 | |||||
| 138 | published_entry.content = Atom::Content::Html.new("<p>rAtom lets me post to and edit my blog using Ruby, how cool!</p>") | ||||
| 139 | published_entry.updated = Time.now | ||||
| 140 | published_entry.save! | ||||
| f2685066 » | seangeo | 2009-06-18 | 141 | ||
| 142 | To update an existing Entry: | ||||
| 143 | |||||
| 144 | existing_entry = Entry.load_entry(URI.parse("http://example.org/afeedentry.atom")) | ||||
| 145 | |||||
| 146 | existing_entry.title = "I have discovered rAtom" | ||||
| 147 | existing_entry.updated = Time.now | ||||
| 148 | existing_entry.save! | ||||
| 149 | |||||
| 46e74293 » | seangeo | 2008-03-03 | 150 | You can also delete an entry using the <tt>destroy!</tt> method, but we won't do that will we?. | |
| 151 | |||||
| f0e330a6 » | seangeo | 2008-04-01 | 152 | === Extension elements | |
| 153 | |||||
| 154 | As of version 0.3.0, rAtom support simple extension elements on feeds and entries. As defined in the Atom Syndication Format, | ||||
| 155 | simple extension elements consist of XML elements from a non-Atom namespace that have no attributes or child elements, i.e. | ||||
| 156 | they are empty or only contain text content. These elements are treated as a name value pair where the element namespace | ||||
| 157 | and local name make up the key and the content of the element is the value, empty elements will be treated as an empty string. | ||||
| 158 | |||||
| 159 | To access extension elements use the [] method on the Feed or Entry. For example, if we are parsing the follow Atom document | ||||
| 160 | with extensions: | ||||
| 161 | |||||
| 162 | <?xml version="1.0"?> | ||||
| 163 | <feed xmlns="http://www.w3.org/2005/Atom" xmlns:ex="http://example.org"> | ||||
| 164 | <title>Feed with extensions</title> | ||||
| 165 | <ex:myelement>Something important</ex:myelement> | ||||
| 166 | </feed> | ||||
| 167 | |||||
| 168 | We could then access the extension element on the feed using: | ||||
| 169 | |||||
| 170 | > feed["http://example.org", "myelement"] | ||||
| 171 | => ["Something important"] | ||||
| 172 | |||||
| 173 | Note that the return value is an array. This is because XML allows multiple instances of the element. | ||||
| 174 | |||||
| 175 | To set an extension element you append to the array: | ||||
| 176 | |||||
| 177 | > feed['http://example.org', 'myelement'] << 'Something less important' | ||||
| 178 | => ["Something important", "Something less important"] | ||||
| 179 | |||||
| 180 | You can then call to_xml and rAtom will serialize the extension elements into xml. | ||||
| 181 | |||||
| 182 | > puts feed.to_xml | ||||
| 183 | <?xml version="1.0"?> | ||||
| 184 | <feed xmlns="http://www.w3.org/2005/Atom"> | ||||
| 185 | <myelement xmlns="http://example.org">Something important</myelement> | ||||
| 186 | <myelement xmlns="http://example.org">Something less important</myelement> | ||||
| 187 | </feed> | ||||
| 188 | |||||
| 189 | Notice that the output repeats the xmlns attribute for each of the extensions, this is semantically the same the input XML, just a bit | ||||
| 190 | ugly. It seems to be a limitation of the libxml-Ruby API. But if anyone knows a work around I'd gladly accept a patch (or even advice). | ||||
| 46e74293 » | seangeo | 2008-03-03 | 191 | ||
| 5b0202de » | seangeo | 2008-07-27 | 192 | ==== Custom Extension Classes | |
| 193 | |||||
| 194 | As of version 0.5.0 you can also define your own classes for a extension elements. This is done by first creating an alias | ||||
| 195 | for the namespace for the class and then using the +element+ method on the Atom::Feed or Atom::Entry class to tell rAtom | ||||
| 196 | to use your custom class when it encounters the extension element. | ||||
| 197 | |||||
| 198 | For example, say we have the following piece Atom XML with a structured extension element: | ||||
| 199 | |||||
| 200 | <?xml version='1.0' encoding='UTF-8'?> | ||||
| 201 | <entry xmlns='http://www.w3.org/2005/Atom' xmlns:custom='http://custom.namespace'> | ||||
| 202 | <id>https://custom.namespace/id/1</id> | ||||
| 203 | <link rel='self' type='application/atom+xml' href='https://custom.namespace/id/1'/> | ||||
| 204 | <custom:property name='foo' value='bar'/> | ||||
| 205 | <custom:property name='baz' value='bat'/> | ||||
| 206 | </entry> | ||||
| 207 | |||||
| 208 | And we want the +custom:property+ elements to be parsed as our own custom class called Custom::Property that is | ||||
| 209 | defined like this: | ||||
| 210 | |||||
| 211 | class Custom::Property | ||||
| 212 | attr_accessor :name, :value | ||||
| 213 | def initialize(xml) | ||||
| 214 | # Custom XML handling | ||||
| 215 | end | ||||
| 216 | end | ||||
| 217 | |||||
| 218 | We can tell rAtom about our custom namespace and custom class using the following method calls: | ||||
| 219 | |||||
| 220 | Atom::Feed..add_extension_namespace :custom, "http://custom.namespace" | ||||
| 221 | Atom::Entry.elements "custom:property", :class => Custom::Property | ||||
| 222 | |||||
| 223 | The first method call registers an alias for the "http://custom.namespace" namespace and the second method call | ||||
| 224 | tell rAtom that when it encounters a custom:property element within a Feed it should create an instance of Custom::Property | ||||
| 225 | and pass the XML Reader to the constructor of the instance. It is then up to the constructor to populate the objects attributes | ||||
| 3f43294f » | seangeo | 2008-07-29 | 226 | from the XML. Note that the alias you create using +add_extension_namespace+ can be anything you want, it doesn't need | |
| 227 | to match the alias in the actual XML itself. | ||||
| 5b0202de » | seangeo | 2008-07-27 | 228 | ||
| 229 | The custom property will then be available as a method on the rAtom class. In the above example: | ||||
| 230 | |||||
| 231 | @feed.custom_property.size == 2 | ||||
| 232 | @feed.custom_property.first.name == 'foo' | ||||
| 233 | @feed.custom_property.first.value == 'bar' | ||||
| 234 | |||||
| 3f43294f » | seangeo | 2008-07-29 | 235 | There is one caveat to this. By using this type of extension support you are permanently modifying the rAtom classes. | |
| 236 | So if your application process one type of atom extension and you are happy with permanently modified rAtom classes, | ||||
| 237 | the extra extensibility might work for you. If on the other hand you process lots of different types of extension you might | ||||
| 238 | want to stick with simpler extension mechanism using the [namespace, element] method described above. | ||||
| 239 | |||||
| 5b0202de » | seangeo | 2008-07-27 | 240 | (Thanks to nachokb for this feature!!) | |
| 241 | |||||
| ba86d5dd » | seangeo | 2008-06-25 | 242 | === Basic Authentication | |
| 243 | |||||
| 244 | All methods that involve HTTP requests now support HTTP Basic Authentication. Authentication credentials are passed | ||||
| 245 | as :user and :pass parameters to the methods that invoke the request. For example you can load a feed with HTTP Basic Authentication using: | ||||
| 246 | |||||
| 247 | Atom::Feed.load_entry(URI.parse("http://example.org/feed.atom"), :user => 'username', :pass => 'password') | ||||
| 248 | |||||
| 249 | Likewise all the Atom Pub methods support similar parameters, for example you can publish an Entry to a Feed with authentication | ||||
| 250 | using: | ||||
| 251 | |||||
| 252 | feed.publish(entry, :user => 'username', :pass => 'password') | ||||
| 253 | |||||
| 254 | Or destroy an entry with: | ||||
| 255 | |||||
| 256 | entry.destroy!(:user => 'username', :pass => 'password') | ||||
| 257 | |||||
| 258 | rAtom doesn't store these credentials anywhere within the object model so you will need to pass them as arguments to every | ||||
| 259 | method call that requires them. This might be a bit of a pain but it does make things simpler and it means that I'm not responsible | ||||
| 260 | for protecting your credentials, although if you are using HTTP Basic Authentication there is a good chance your credentials aren't | ||||
| 261 | very well protected anyway. | ||||
| 262 | |||||
| 9a466e40 » | seangeo | 2008-08-10 | 263 | === AuthHMAC authentication | |
| 264 | |||||
| 265 | As of version 0.5.1 rAtom also support authentication via HMAC request signing using the AuthHMAC[http://auth-hmac.rubyforge.org] gem. This is made available using the :hmac_access_id and :hmac_secret_key parameters which can be passed to the same methods as the HTTP Basic credentials support. | ||||
| 266 | |||||
| 46e74293 » | seangeo | 2008-03-03 | 267 | == TODO | |
| 268 | |||||
| 269 | * Support partial content responses from the server. | ||||
| 270 | * Support batching of protocol operations. | ||||
| 271 | * All my tests have been against internal systems, I'd really like feedback from those who have tried rAtom using existing blog software that supports APP. | ||||
| 804af5d3 » | seangeo | 2008-03-08 | 272 | * Handle all base uri tests. | |
| 273 | * Add slug support. | ||||
| 46e74293 » | seangeo | 2008-03-03 | 274 | ||
| 275 | == Source Code | ||||
| 276 | |||||
| 277 | The source repository is accessible via GitHub: | ||||
| 278 | |||||
| 279 | git clone git://github.com/seangeo/ratom.git | ||||
| 280 | |||||
| 281 | == Contact Information | ||||
| 282 | |||||
| 283 | The project page is at http://rubyforge.org/projects/ratom. Please file any bugs or feedback | ||||
| 284 | using the trackers and forums there. | ||||
| 285 | |||||
| 286 | == Authors and Contributors | ||||
| 287 | |||||
| 288 | rAtom was developed by Peerworks[http://peerworks.org] and written by Sean Geoghegan. | ||||
| 289 | |||||
