Index: /trunk/Nebulous/Changes
===================================================================
--- /trunk/Nebulous/Changes	(revision 20093)
+++ /trunk/Nebulous/Changes	(revision 20094)
@@ -6,4 +6,5 @@
       no instance
     - retry all calls to open()
+    - add Nebulous::Client->swap() method
 
 0.09 Wed Jul  9 16:36:27 HST 2008
Index: /trunk/Nebulous/MANIFEST
===================================================================
--- /trunk/Nebulous/MANIFEST	(revision 20093)
+++ /trunk/Nebulous/MANIFEST	(revision 20094)
@@ -102,4 +102,5 @@
 t/65_client_mounts.t
 t/66_client_xattr.t
+t/67_client_swap.t
 t/90_nebclient.t
 t/TEST.PL
Index: /trunk/Nebulous/lib/Nebulous/Client.pm
===================================================================
--- /trunk/Nebulous/lib/Nebulous/Client.pm	(revision 20093)
+++ /trunk/Nebulous/lib/Nebulous/Client.pm	(revision 20094)
@@ -1,5 +1,5 @@
 # Copyright (c) 2004-2008  Joshua Hoblitt
 #
-# $Id: Client.pm,v 1.61 2008-10-13 20:54:49 jhoblitt Exp $
+# $Id: Client.pm,v 1.62 2008-10-13 21:20:33 jhoblitt Exp $
 
 package Nebulous::Client;
@@ -907,4 +907,37 @@
 
 
+sub swap
+{
+    my $self = shift;
+
+    my ( $key1, $key2 ) = validate_pos( @_,
+        {
+            type => SCALAR,
+        },
+        {
+            type => SCALAR,
+        },
+    );
+
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->swap_objects( $key1, $key2 );
+    if ( $response->fault ) {
+        $self->set_err($response->faultstring);
+
+        if ($response->faultstring =~ /is valid object key/) {
+            $log->debug( "leaving" );
+            return;
+        }
+
+        $log->logdie("unhandled fault - ", $self->err);
+    }
+
+    $log->debug( "leaving" );
+
+    return 1;
+}
+
+
 sub delete_instance
 {
Index: /trunk/Nebulous/lib/Nebulous/Client.pod
===================================================================
--- /trunk/Nebulous/lib/Nebulous/Client.pod	(revision 20093)
+++ /trunk/Nebulous/lib/Nebulous/Client.pod	(revision 20094)
@@ -30,4 +30,5 @@
     $neb->copy( "key", "new_key", "node01" );
     $neb->move( "key", "new_key" );
+    $neb->swap( "key1", "key2" );
     $neb->delete_instance( $uri );
     my $stats = $neb->stat( "key" );
@@ -349,4 +350,23 @@
 
 =item * new_key
+
+The destination object.
+
+=back
+
+Returns true on success or C<undef> on failure.
+
+
+=item * swap($key1, $key2)
+
+Atomically swaps the names of two storage objects.
+
+=over 4
+
+=item * key1
+
+The source object.
+
+=item * key2
 
 The destination object.
Index: /trunk/Nebulous/t/67_client_swap.t
===================================================================
--- /trunk/Nebulous/t/67_client_swap.t	(revision 20094)
+++ /trunk/Nebulous/t/67_client_swap.t	(revision 20094)
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+
+# Copryight (C) 2004-2005  Joshua Hoblitt
+#
+# $Id: 67_client_swap.t,v 1.1 2008-10-13 21:20:33 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use Apache::Test qw( -withtestmore );
+
+plan tests => 8;
+
+use lib qw( ./t ./lib );
+
+use Nebulous::Client;
+use Nebulous::Util qw( :standard );
+use Test::Nebulous;
+
+my $hostport = Apache::Test->config->{ 'hostport' };
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    my $uri1 = $neb->create( "foo1" );
+    my $uri2 = $neb->create( "foo2" );
+
+    ok($neb->swap( "foo1", "foo2" ), "swap objects");
+
+    my $new_uri1 = ($neb->find_instances( "foo1" ))->[0];
+    my $new_uri2 = ($neb->find_instances( "foo2" ))->[0];
+
+    is($uri1, $new_uri2, "key1 -> key2");
+    is($uri2, $new_uri1, "key2 -> key1");
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('bar');
+
+    ok(!$neb->swap('foo', 'bar'), "key1 doesn't exist" );
+}
+
+Test::Nebulous->setup;
+
+{
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+
+    ok(!$neb->swap('foo', 'bar'), "key2 doesn't exist" );
+}
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->swap();
+};
+like( $@, qr/2 were expected/, "no params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->swap('foo');
+};
+like( $@, qr/2 were expected/, "not enough params" );
+
+Test::Nebulous->setup;
+
+eval {
+    my $neb = Nebulous::Client->new(
+        proxy => "http://$hostport/nebulous",
+    );
+
+    $neb->create('foo');
+    $neb->create('bar');
+
+    $neb->swap('foo', 'bar', 'baz');
+};
+like( $@, qr/2 were expected/, "too many params" );
+
+Test::Nebulous->cleanup;
