Thursday, July 30, 2009

New Features (and maybe a new name) for couch_design_docs

‹prev | My Chain | next›

I take a break from CSS tonight. As much fun as less CSS has been, too much CSS can be a bit... blah.

I decide, instead, to move back to my couch_design_docs gem. It has become apparent that I need to be able to upload regular documents in addition to CouchDB design documents (the gem may need a rename). Specifically, I need to upload a "kids" document when initially creating the database (I ran into this error at the end of the load from legacy application). So...

I dive back into the gem. First up, I rename the internal Directory class to DesignDirectoy. I am going to need another class that is responsible for uploading "regular" (non-design) documents in a directory, so renaming the class should cut down on confusion. I also update the specs to reflect this change.

Once that is done and all specs are passing, I describe examples for instantiating the new DocumentDirectory class:
describe DocumentDirectory do
it "should require a root directory for instantiation" do
lambda { DocumentDirectory.new }.
should raise_error

lambda { DocumentDirectory.new("foo") }.
should raise_error

lambda { DocumentDirectory.new("fixtures")}.
should_not raise_error
end
end
I confess that I skip a few steps in the BDD cycle here. These examples, and the implementation of the initializer are very similar to the implementation of the initializer for DesignDirectory. The code that makes these examples pass:
module CouchDesignDocs
class DocumentDirectory

attr_accessor :couch_doc_dir

def initialize(path)
Dir.new(path)
@couch_doc_dir = path
end
end
end
For now, I need an instance of this class to iterate over the document names and contents. Given two documents:
#fixtures/foo.json
{"foo":"1"}

#fixtures/bar.json
{"bar":"2"}
I expect to iterate over the documents so that I can build a data structure containing the document names and contents:
  context "a valid directory" do
before(:each) do
@it = DocumentDirectory.new("fixtures")
end

it "should be able to iterate over the documents" do
everything = []
@it.each_document do |name, contents|
everything << [name, contents]
end
everything.
should == [['bar', '{"bar":"2"}'],
['foo', '{"foo":"1"}']]
end
end
I can implement this with:
    def each_document
Dir["#{couch_doc_dir}/*.json"].each do |filename|
yield [ File.basename(filename, '.json'),
File.new(filename).read ]

end
end
That is all that I need. The Store class is already capable of puting documents into the specified CouchDB database. I need to iterate over each document and put them in the store. In my example, I simulate iterating over the documents with RSpec's and_yield:
  it "should be able to load documents into CouchDB" do
store = mock("Store")
Store.stub!(:new).and_return(store)

dir = mock("Document Directory")
dir.
stub!(:each_document).
and_yield('foo', {"foo" => "1"})

DocumentDirectory.stub!(:new).and_return(dir)

store.
should_receive(:put!).
with('foo', {"foo" => "1"})

CouchDesignDocs.put_document_dir("uri", "fixtures")
Now that I have both a put_document_dir and a put_design_dir I need a convenience method to upload both:
  it "should be able to load design and normal documents" do
CouchDesignDocs.
should_receive(:put_design_dir).
with("uri", "fixtures/_design")

CouchDesignDocs.
should_receive(:put_document_dir).
with("uri", "fixtures")

CouchDesignDocs.put_dir("uri", "fixtures")
end
Normally, I am not a big fan of two expectations in one example, but for simple methods, I am wont to make an exception. And the put_dir method is simple:
  def self.put_dir(db_uri, dir)
self.put_design_dir(db_uri, "#{dir}/_design")
self.put_document_dir(db_uri, dir)
end
That is a good stopping point for tonight. Tomorrow I will give it a try in my live code, maybe do some minor refactoring, and mess about with github branches for this new version of my gem.

No comments:

Post a Comment