Sunday, February 28, 2010

Destructive CouchDocs

‹prev | My Chain | next›

After working with OptionParser a bit, I settle on this updated command line interface for my couch_docs gem:
cstrom@whitefall:~/repos/couch_docs$ ./bin/couch-docs -h
Usage: couch-docs push|dump [OPTIONS] couchdb_url [target_dir]

If a target_dir is not specified, the current working directory will be used.

Push options:
-R, --destructive Drop the couchdb_uri (if it exists) and create a new database
-b, --bulk [BATCH_SIZE=1000] Use bulk insert when pushing new documents

Dump options:
-d, --design Only dump design documents
-D, --data Only dump data documents

Common options:
-v, --version Show version
-h, --help Show this message
The most important of these options for me is the --destructive option. It is a pain to have to drop/create databases manually when I am trying to test things out. Happily this is trivial to implement—thanks to the extreme RESTful nature of CouchDB. Specifically, creating a database is accomplished with the same PUT operation that is used to create a document. The couch_docs gem already has a facility for creating documents and for replacing existing documents as needed—the Store#put! method.

And so I do something I have yet to do in this incarnation of my chain, I write an RSpec spec:
describe CouchDocs do
it "should be able to create (or delete/create) a DB" do

Simple enough. When I do a destructive database create, I tell the Store class to perform a (possibly) destructive put. Running my spec, I get a failure because I have not defined the destructive_database_create method:
cstrom@whitefall:~/repos/couch_docs$ spec ./spec/couch_docs_spec.rb 

NoMethodError in 'CouchDocs should be able to create (or delete/create) a DB'
undefined method `destructive_database_create' for CouchDocs:Module

Finished in 0.077468 seconds

27 examples, 1 failure
Ah, good to be back in the change-the-message, make it pass cycle. After getting the spec to pass, I give it a try from the command line only to find:
cstrom@whitefall:~/repos/couch_docs$ ./bin/couch-docs -R push http://localhost:5984/test ~/repos/eee-code/seed/seed2/
/home/cstrom/repos/couch_docs/lib/couch_docs.rb:63:in `put!': wrong number of arguments (1 for 2) (ArgumentError)
from /home/cstrom/repos/couch_docs/lib/couch_docs.rb:63:in `destructive_database_create'
from /home/cstrom/repos/couch_docs/lib/couch_docs/command_line.rb:26:in `run'
from /home/cstrom/repos/couch_docs/lib/couch_docs/command_line.rb:10:in `run'
from ./bin/couch-docs:8
Ah, bummer. All of my PUTs so far have been of a document to an existing database. When creating a new database, there is no document.

It turns out that I can put anything I like when creating a database and CouchDB will happily ignore it for for me:
cstrom@whitefall:~/repos/couch_docs$ curl -X DELETE http://localhost:5984/test
cstrom@whitefall:~/repos/couch_docs$ curl -X PUT http://localhost:5984/test -d '{"foo":"bar"}'
cstrom@whitefall:~/repos/couch_docs$ curl -X GET http://localhost:5984/test/_all_docs
I do not see any reason to expect that CouchDB will change this behavior, so I code to it:
describe CouchDocs do
it "should be able to create (or delete/create) a DB" do
with("couchdb_url", anything())

The anything RSpec matcher means that I expect the put! method to receive a second argument, but really do not care what it is. The implementation that makes this pass is:
  # Create or recreate the database located at <tt>db_uri</tt>
def self.destructive_database_create(db_uri)
Store.put!(db_uri, "")
To test this out, I install my gem and run it in a seed directory with 4 meal/recipe documents from 2002:
cstrom@whitefall:~/tmp/seed$ ls
2002-08-26-grilled_chicken.json 2002-08-26.json 2002-08-26-pasta.json 2002-08-26-pesto.json
cstrom@whitefall:~/tmp/seed$ couch-docs -R push http://localhost:5984/test
And, checking the test database in Futon, I find that I do indeed have 4 documents now:

That is a good stopping point for tonight. I will pick up tomorrow by being more selective in what I dump to the filesystem (i.e. only design documents, only data, or both).

Day #28

No comments:

Post a Comment