CouchDB Backend for Hx

Just for grins I had a go at implementing a CouchDB backend for Hx today. It’s pretty straightforward; the document id in couch becomes the entry path in Hx, and the actual entry Hash is taken directly from the document JSON. Configuration of a CouchDB source is also quite simple:

filter: Hx::Backend::CouchDB
options:
  couchdb_server: http://localhost:5984/
  couchdb_database: entries

The filter options could actually be omitted entirely in this case, since the example represents the defaults if the server and database options aren’t given explicitly.

Here’s a stripped-down version of the CouchDB backend to demonstrate what an Hx backend looks like:

class Hx::Backend::CouchDB
  include Hx::Source

  def initialize(source, options)
    @couchdb_server = options[:couchdb_server] ||
                      "http://localhost:5984"
    @couchdb_database = options[:couchdb_database] ||
                        "entries"
  end

  def edit_entry(path, prototype=nil)
    begin
      text = get_document(path)
    rescue HTTPError => e
      raise e unless e.code == 404
      raise Hx::NoSuchEntryError, path unless prototype
      text = prototype.to_json
    end
    text = yield text
    entry = JSON.parse(text)
    entry['updated'] = Time.now.xmlschema
    entry['created'] ||= entry['updated']
    put_document(path, entry.to_json)
    self
  end

  def each_entry
    listing = JSON.parse(get_document('_all_docs'))
    for row in listing['rows']
      path = row['id']
      entry = JSON.parse(get_document(path))
      for field in %(created updated)
        entry[field] = Time.parse(entry[field] || "")
      end
      yield path, entry
    end
    self
  end

  def get_document(id)
    # ...
  end

  def put_document(id, body)
    # ...
  end
end

Some error handling has been removed, and I’ve elided the definitions of get_document and put_document for the sake of space. The main points of interest are:

  • CouchDB.new, which takes an upstream source (ignored by most backend sources) and an options hash
  • CouchDB#edit_entry, which passes the raw (backend-dependent) entry text to its block, and replaces the entry in the database with the returned result, updating the 'updated' and 'created' fields.
  • CouchDB#each_entry, which iterates through all the entries provided by this backend instance, yielding path, entry pairs. In Hx, entries are just Ruby hashes, nothing more.

Those three methods are really all any Hx backend needs to provide.