From 9faa9daacab151ebd89241cead8cb4261f6b0702 Mon Sep 17 00:00:00 2001 From: Matt Lyon Date: Mon, 5 Jan 2009 00:44:12 -0800 Subject: [PATCH] support for couchdb's support for the COPY and MOVE verbs. depends on my commit to RestClient, currently only in http://github.com/mattly/rest-client/commit/b5d75acc6874ccb861bf0351fba0816454856502 I have considered adding this to the CouchRest::Document class as well ("@doc.copy new-id" and such) but haven't yet. --- lib/couchrest.rb | 8 +++ lib/couchrest/core/database.rb | 30 +++++++++- spec/couchrest/core/database_spec.rb | 83 ++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/lib/couchrest.rb b/lib/couchrest.rb index 1dc3581..ddd58fd 100644 --- a/lib/couchrest.rb +++ b/lib/couchrest.rb @@ -110,6 +110,14 @@ module CouchRest def delete uri JSON.parse(RestClient.delete(uri)) end + + def copy uri, destination + JSON.parse(RestClient.copy(uri, {'Destination' => destination})) + end + + def move uri, destination + JSON.parse(RestClient.move(uri, {'Destination' => destination})) + end def paramify_url url, params = {} if params && !params.empty? diff --git a/lib/couchrest/core/database.rb b/lib/couchrest/core/database.rb index fc21a68..cf2494c 100644 --- a/lib/couchrest/core/database.rb +++ b/lib/couchrest/core/database.rb @@ -171,7 +171,35 @@ module CouchRest slug = CGI.escape(doc['_id']) CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}" end - + + # COPY an existing document to a new id. If the destination id currently exists, a rev must be provided. + # dest can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc + # hash with a '_rev' key + def copy doc, dest + raise ArgumentError, "_id is required for copying" unless doc['_id'] + slug = CGI.escape(doc['_id']) + destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev'] + "#{dest['_id']}?rev=#{dest['_rev']}" + else + dest + end + CouchRest.copy "#{@root}/#{slug}", destination + end + + # MOVE an existing document to a new id. If the destination id currently exists, a rev must be provided. + # dest can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc + # hash with a '_rev' key + def move doc, dest + raise ArgumentError, "_id and _rev are required for moving" unless doc['_id'] && doc['_rev'] + slug = CGI.escape(doc['_id']) + destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev'] + "#{dest['_id']}?rev=#{dest['_rev']}" + else + dest + end + CouchRest.move "#{@root}/#{slug}?rev=#{doc['_rev']}", destination + end + # Compact the database, removing old document revisions and optimizing space use. def compact! CouchRest.post "#{@root}/_compact" diff --git a/spec/couchrest/core/database_spec.rb b/spec/couchrest/core/database_spec.rb index 0639e86..132d3c7 100644 --- a/spec/couchrest/core/database_spec.rb +++ b/spec/couchrest/core/database_spec.rb @@ -474,6 +474,89 @@ describe CouchRest::Database do end end + describe "COPY existing document" do + before :each do + @r = @db.save({'artist' => 'Zappa', 'title' => 'Muffin Man'}) + @docid = 'tracks/zappa/muffin-man' + @doc = @db.get(@r['id']) + end + describe "to a new location" do + it "should work" do + @db.copy @doc, @docid + newdoc = @db.get(@docid) + debugger + newdoc['artist'].should == 'Zappa' + end + it "should fail without an _id" do + lambda{@db.copy({"not"=>"a real doc"})}.should raise_error(ArgumentError) + end + end + describe "to an existing location" do + before :each do + @db.save({'_id' => @docid, 'will-exist' => 'here'}) + end + it "should fail without a rev" do + lambda{@db.copy @doc, @docid}.should raise_error(RestClient::RequestFailed) + end + it "should succeed with a rev" do + @to_be_overwritten = @db.get(@docid) + @db.copy @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}" + newdoc = @db.get(@docid) + newdoc['artist'].should == 'Zappa' + end + it "should succeed given the doc to overwrite" do + @to_be_overwritten = @db.get(@docid) + @db.copy @doc, @to_be_overwritten + newdoc = @db.get(@docid) + newdoc['artist'].should == 'Zappa' + end + end + end + + describe "MOVE existing document" do + before :each do + @r = @db.save({'artist' => 'Zappa', 'title' => 'Muffin Man'}) + @docid = 'tracks/zappa/muffin-man' + @doc = @db.get(@r['id']) + end + describe "to a new location" do + it "should work" do + @db.move @doc, @docid + newdoc = @db.get(@docid) + newdoc['artist'].should == 'Zappa' + lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound) + end + it "should fail without an _id or _rev" do + lambda{@db.move({"not"=>"a real doc"})}.should raise_error(ArgumentError) + lambda{@db.move({"_id"=>"not a real doc"})}.should raise_error(ArgumentError) + end + end + describe "to an existing location" do + before :each do + @db.save({'_id' => @docid, 'will-exist' => 'here'}) + end + it "should fail without a rev" do + lambda{@db.move @doc, @docid}.should raise_error(RestClient::RequestFailed) + lambda{@db.get(@r['id'])}.should_not raise_error + end + it "should succeed with a rev" do + @to_be_overwritten = @db.get(@docid) + @db.move @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}" + newdoc = @db.get(@docid) + newdoc['artist'].should == 'Zappa' + lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound) + end + it "should succeed given the doc to overwrite" do + @to_be_overwritten = @db.get(@docid) + @db.move @doc, @to_be_overwritten + newdoc = @db.get(@docid) + newdoc['artist'].should == 'Zappa' + lambda {@db.get(@r['id'])}.should raise_error(RestClient::ResourceNotFound) + end + end + end + + it "should list documents" do 5.times do @db.save({'another' => 'doc', 'will-exist' => 'anywhere'})