M lib/thingfish/daemon.rb +5 -2
@@ 177,11 177,13 @@ class ThingFish::Daemon < Mongrel::HttpS
new_resource = uuid.nil?
uuid ||= UUID.timestamp_create
+ # :BRANCH: Deletes new resources if there's an error while storing
+ # :BRANCH: Doesn't delete it if it's an update
# Store the data, catching and cleaning up on error
begin
checksum = @filestore.store_io( uuid, body )
rescue => err
- self.log.notice "Error saving resource to filestore."
+ self.log.error "Error saving resource to filestore: %s" % [ err.message ]
@filestore.delete( uuid ) if new_resource
raise
end
@@ 189,8 191,9 @@ class ThingFish::Daemon < Mongrel::HttpS
# Store some default metadata about the resource
now = Time.now
metadata = @metastore[ uuid ]
- metadata.merge( body_metadata )
+ metadata.update( body_metadata )
+ # :BRANCH: Won't overwrite an existing creation date
metadata.checksum = checksum
metadata.created = now unless metadata.created
metadata.modified = now
M lib/thingfish/metastore.rb +4 -3
@@ 72,13 72,14 @@ class ThingFish::MetaStore
end
- ### Merge the values from the given +hash+ into the metadata for the proxied
- ### resource.
- def merge( hash )
+ ### Adds the contents of +hash+ to the values for the referenced UUID, overwriting entries
+ ### with duplicate keys.
+ def update( hash )
hash.each_pair do |key, value|
@store.set_property( @uuid, key.to_s, value )
end
end
+ alias_method :merge!, :update
### Returns true if the +other+ object is equivalent to the receiver
M spec/daemon_spec.rb +99 -2
@@ 454,9 454,106 @@ describe ThingFish::Daemon do
### Resource storing
- it "knows how to store a new resource"
- it "knows how to update the data and metadata for an existing resource"
+ it "knows how to store a new resource" do
+ UUID.stub!( :timestamp_create ).and_return( TEST_UUID_OBJ )
+ body = mock( "body IO" )
+ metadata = mock( "metadata hash", :null_object => true )
+
+ # Replace the filestore and metastore with mocks
+ filestore = mock( "filestore", :null_object => true )
+ @daemon.instance_variable_set( :@filestore, filestore )
+ metastore = mock( "metastore", :null_object => true )
+ @daemon.instance_variable_set( :@metastore, metastore )
+
+ filestore.should_receive( :store_io ).
+ with( TEST_UUID_OBJ, body ).
+ and_return( :a_checksum )
+
+ metadata_proxy = mock( "metadata proxy", :null_object => true )
+ metastore.should_receive( :[] ).with( TEST_UUID_OBJ ).and_return( metadata_proxy )
+ metadata_proxy.should_receive( :update ).with( metadata )
+
+ metadata_proxy.should_receive( :checksum= ).with( :a_checksum )
+ metadata_proxy.should_receive( :created ).and_return( nil )
+ metadata_proxy.should_receive( :created= ).with an_instance_of( Time )
+ metadata_proxy.should_receive( :modified= ).with( an_instance_of(Time) )
+
+ metadata_proxy.stub!( :content_length ).and_return( 1 )
+
+ @daemon.store_resource( body, metadata ).should == TEST_UUID_OBJ
+ end
+
+ it "knows how to update the data and metadata for an existing resource" do
+ body = mock( "body IO" )
+ metadata = mock( "metadata hash", :null_object => true )
+
+ # Replace the filestore and metastore with mocks
+ filestore = mock( "filestore", :null_object => true )
+ @daemon.instance_variable_set( :@filestore, filestore )
+ metastore = mock( "metastore", :null_object => true )
+ @daemon.instance_variable_set( :@metastore, metastore )
+
+ filestore.should_receive( :store_io ).
+ with( TEST_UUID_OBJ, body ).
+ and_return( :a_checksum )
+
+ metadata_proxy = mock( "metadata proxy", :null_object => true )
+ metastore.should_receive( :[] ).with( TEST_UUID_OBJ ).and_return( metadata_proxy )
+ metadata_proxy.should_receive( :update ).with( metadata )
+
+ metadata_proxy.should_receive( :checksum= ).with( :a_checksum )
+ metadata_proxy.should_receive( :created ).and_return( Time.now )
+ metadata_proxy.should_not_receive( :created= )
+ metadata_proxy.should_receive( :modified= ).with( an_instance_of(Time) )
+
+ metadata_proxy.stub!( :content_length ).and_return( 1 )
+
+ @daemon.store_resource( body, metadata, TEST_UUID_OBJ ).should == TEST_UUID_OBJ
+ end
+
+
+ it "cleans up new resources if there's an error while storing" do
+ UUID.stub!( :timestamp_create ).and_return( TEST_UUID_OBJ )
+ body = mock( "body IO" )
+ metadata = mock( "metadata hash", :null_object => true )
+
+ # Replace the filestore and metastore with mocks
+ filestore = mock( "filestore", :null_object => true )
+ @daemon.instance_variable_set( :@filestore, filestore )
+ metastore = mock( "metastore", :null_object => true )
+ @daemon.instance_variable_set( :@metastore, metastore )
+
+ filestore.should_receive( :store_io ).
+ with( TEST_UUID_OBJ, body ).
+ and_raise( RuntimeError.new('Happy time explosion!') )
+ filestore.should_receive( :delete ).with( TEST_UUID_OBJ )
+
+ lambda {
+ @daemon.store_resource( body, metadata )
+ }.should raise_error( RuntimeError, 'Happy time explosion!' )
+ end
+
+
+ it "does not delete the resource if there's an error while updating" do
+ body = mock( "body IO" )
+ metadata = mock( "metadata hash", :null_object => true )
+
+ # Replace the filestore and metastore with mocks
+ filestore = mock( "filestore", :null_object => true )
+ @daemon.instance_variable_set( :@filestore, filestore )
+ metastore = mock( "metastore", :null_object => true )
+ @daemon.instance_variable_set( :@metastore, metastore )
+
+ filestore.should_receive( :store_io ).
+ with( TEST_UUID_OBJ, body ).
+ and_raise( RuntimeError.new('Happy time explosion!') )
+ filestore.should_not_receive( :delete ).with( TEST_UUID_OBJ )
+
+ lambda {
+ @daemon.store_resource( body, metadata, TEST_UUID_OBJ )
+ }.should raise_error( RuntimeError, 'Happy time explosion!' )
+ end
end
M spec/metastore_spec.rb +1 -1
@@ 138,7 138,7 @@ describe ThingFish::MetaStore::ResourceP
it "can merge new data from a hash" do
@store.should_receive( :set_property ).with( TEST_UUID, 'format', TEST_PROPVALUE )
@store.should_receive( :set_property ).with( TEST_UUID, 'owner', 'perqualee' )
- @proxy.merge( :format => TEST_PROPVALUE, :owner => 'perqualee' )
+ @proxy.update( :format => TEST_PROPVALUE, :owner => 'perqualee' )
end
end