Monday, March 29, 2010

Small couch_docs Updates—Getting It Done

‹prev | My Chain | next›

Picking up from last night, I need couch_docs to update individual CouchDB documents when updated (currently all documents are updated when anything in the watched directory is updated). An RSpec example describing this:
        it "should update documents (if any)" do
file_mock = mock("File", :path => "/foo")
@it.stub!(:documents).and_return([file_mock])

CouchDocs.
should_receive(:put_file).
with("/foo")

@it.directory_watcher_update(@args)
end
I get that passing easily enough by adding a call to put_file inside an iterator over the document updates:
  def directory_watcher_update(args)
#...
documents.each do |update|
CouchDocs.put_file(update.path)
end
#...
end
That get all of my specs passing. There's just one tiny problem: there is no CouchDocs.put_file method.

The put_file method should read a file, use the basename as the document ID, and put it on the CouchDB store. The couch_docs gem already has a Store object with a put! method. The RSpec example describing the behavior I am after is:
  it "should be able to upload a single document into CouchDB" do
Store.
should_receive(:put!).
with('uri/foo', {"foo" => "1"})

File.stub!(:read).and_return('{"foo": "1"}')

CouchDocs.put_file("uri", "/foo")
end
My first pass at making this pass is:
  def self.put_file(db_uri, file_path)
contents = File.read(file_path)
name = File.basename(file_path, ".json")
Store.put!("#{db_uri}/#{name}", contents)
end
I read the file, derive the name/ID from the file's basename and PUT the contents in the store. Unfortunately, this fails with:
1)
Spec::Mocks::MockExpectationError in 'CouchDocs should be able to upload a single document into CouchDB'
<CouchDocs::Store (class)> received :put! with unexpected arguments
expected: ("uri/foo", {"foo"=>"1"})
got: ("uri/foo", "{\"foo\": \"1\"}")
./spec/couch_docs_spec.rb:61:
Ah, I forgot. The CouchDocs::Store.put! method expects the contents to be in hash format, but the file are stored in JSON. At some point I really need to consider switching that, but this is not the time. Instead I parse the JSON:
  def self.put_file(db_uri, file_path)
contents = JSON.parse(File.read(file_path))
name = File.basename(file_path, ".json")
Store.put!("#{db_uri}/#{name}", contents)
end
And the example passes.

That should just about take care of uploading only the changed files with couch_docs. I will smoke test tomorrow and possibly factor the directory watching code out of the command line (where it has a definite odor).

Day #57

No comments:

Post a Comment