Changeset 558
- Timestamp:
- 06/17/08 10:36:32 (2 months ago)
- Files:
-
- thingfish/branches/mp3-jukebox/docs/manual/src/Developers_Guide/cookbook.page (added)
- thingfish/branches/mp3-jukebox/docs/manual/src/Developers_Guide/index.page (modified) (1 diff)
- thingfish/branches/mp3-jukebox/docs/manual/src/Hackers_Guide/writing-filestores.page (modified) (1 diff)
- thingfish/branches/mp3-jukebox/docs/manual/src/Hackers_Guide/writing-filters.page (modified) (3 diffs)
- thingfish/branches/mp3-jukebox/lib/thingfish/daemon.rb (modified) (1 diff)
- thingfish/branches/mp3-jukebox/lib/thingfish/handler/default.rb (modified) (2 diffs)
- thingfish/branches/mp3-jukebox/lib/thingfish/request.rb (modified) (5 diffs)
- thingfish/branches/mp3-jukebox/plugins/thingfish-handler-mp3jukebox/resources/static/js/singfish.js (modified) (1 prop)
- thingfish/branches/mp3-jukebox/spec/thingfish/daemon_spec.rb (modified) (1 diff)
- thingfish/branches/mp3-jukebox/spec/thingfish/handler/default_spec.rb (modified) (8 diffs)
- thingfish/branches/mp3-jukebox/utils.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
thingfish/branches/mp3-jukebox/docs/manual/src/Developers_Guide/index.page
r546 r558 15 15 * <?link The Javascript Client Library ?> 16 16 * <?link Writing Your Own Client Library ?> 17 * <?link ThingFish Usage Cookbook ?> thingfish/branches/mp3-jukebox/docs/manual/src/Hackers_Guide/writing-filestores.page
r543 r558 11 11 h2. <%= page.config['title'] %> 12 12 13 « Coming Soon! » 13 <div id="auto-toc" /> 14 15 The back end of a ThingFish instance... 16 17 h3. What's a FileStore do? 18 19 h3. The mandatory interface 20 21 h3. Optional interface 14 22 15 23 24 25 thingfish/branches/mp3-jukebox/docs/manual/src/Hackers_Guide/writing-filters.page
r557 r558 60 60 | 5 | Import some useful constants, like the @HTTP::OK@ a little further down | 61 61 | 8 | Actions are defined in special methods that match the naming convention 62 @handle_ <C2><AB>METHOD<C2><BB>_request@. We're only going to accept @GET@ methods for this example.62 @handle_«METHOD»_request@. We're only going to accept @GET@ methods for this example. 63 63 These special methods receive a @ThingFish::Request@@ and a @ThingFish::Response@ object 64 64 to fiddle with as you see fit. | … … 67 67 to the client. | 68 68 | 10 | Set the content type of the response. | 69 | 11 | The default response code is a @404 (Not Found)@ <E2><80><93>after we're sure we've generated69 | 11 | The default response code is a @404 (Not Found)@; after we're sure we've generated 70 70 the content we want, setting this to @OK@ stops further processing, and lets the response 71 71 through successfully. | … … 86 86 87 87 88 h3. Appending Related Resources 88 89 90 (Describe how to append things like thumbnails, previews, etc.) 89 91 thingfish/branches/mp3-jukebox/lib/thingfish/daemon.rb
r553 r558 234 234 return uuid 235 235 end 236 237 238 ### Recursively store all resources in the specified +request+ that are related to +body+. 239 ### Use the specified +uuid+ for the 'related_to' metadata value. 240 def store_related_resources( body, uuid, request ) 241 request.related_resources[ body ].each do |related_resource, related_metadata| 242 related_metadata[ :related_to ] = uuid 243 metadata = request.metadata[related_resource].merge( related_metadata ) 244 245 subuuid = self.store_resource( related_resource, metadata ) 246 self.store_related_resources( related_resource, subuuid, request ) 247 end 248 rescue => err 249 self.log.error "%s while storing related resource: %s" % [ err.class.name, err.message ] 250 self.log.debug "Backtrace: %s" % [ err.backtrace.join("\n ") ] 251 end 252 253 254 ### Remove any resources that have a +related_to+ of the given UUID, and a +relation+ of 255 ### 'appended'. 256 def purge_related_resources( uuid ) 257 uuids = @metastore.find_by_exact_properties( :related_to => uuid.to_s, :relation => 'appended' ) 258 uuids.each do |subuuid| 259 self.log.debug "purging appended resource %s for %s" % [ subuuid, uuid ] 260 @metastore.delete_resource( subuuid ) 261 @filestore.delete( subuuid ) 262 end 263 end 264 236 265 237 266 thingfish/branches/mp3-jukebox/lib/thingfish/handler/default.rb
r464 r558 245 245 uuid = nil 246 246 247 # Store the primary resource 247 248 body, metadata = request.get_body_and_metadata 248 249 uuid = self.daemon.store_resource( body, metadata ) 250 251 # Store any related resources, linked to the primary 252 self.daemon.store_related_resources( body, uuid, request ) 249 253 250 254 response.status = HTTP::CREATED … … 267 271 body, metadata = request.get_body_and_metadata 268 272 self.daemon.store_resource( body, metadata, uuid ) 273 274 # Purge any old related resources, then store any new ones linked to the primary 275 self.daemon.purge_related_resources( uuid ) 276 self.daemon.store_related_resources( body, uuid, request ) 269 277 270 278 response.content_type = RUBY_MIMETYPE thingfish/branches/mp3-jukebox/lib/thingfish/request.rb
r554 r558 314 314 end 315 315 316 related_bodykey = self.make_body_key( related_body ) 317 @body_key_mapping[ related_bodykey ] = related_body 318 316 319 related_metadata[:relation] ||= 'appended' 317 related_bodykey = self.make_body_key( related_body )318 self.metadata[ related_body ] = {}319 @body_key_mapping[ related_bodykey ] = related_body320 320 self.related_resources[ original_body ][ related_body ] = related_metadata 321 321 end … … 324 324 ### Append the specified additional +metadata+ for the given +resource+, which should be one 325 325 ### of the entity bodies yielded by #each_body 326 ### 327 ### TODO: Do we need this method after bodykey removal? 328 ### 326 329 def append_metadata_for( resource, metadata ) 327 330 # Convert the body to the key of the related resources hash … … 385 388 hash.each do |body, _| 386 389 if body.closed? 390 391 # TODO asap: :) 392 # substitute body for a fresh and clean filehandle, 393 # since filehandle closure has been such a poopy problem 394 # in the past. this will remove the need for bodykeys, as well. 395 387 396 self.log.warn "Entity body closed: %p" % [ body ] 388 397 body.open 389 398 end 399 390 400 body.rewind 391 401 end … … 536 546 return @entity_bodies 537 547 end 538 548 539 549 540 550 ### For each resource => metadata pair returned by the current +iterator+, merge the … … 550 560 body_metadata[ :format ] ||= DEFAULT_CONTENT_TYPE 551 561 extracted_metadata = self.metadata[body] || {} 552 562 553 563 # Content metadata is determined per-multipart section 554 564 merged = extracted_metadata.merge( @form_metadata ) thingfish/branches/mp3-jukebox/plugins/thingfish-handler-mp3jukebox/resources/static/js/singfish.js
- Property svn:keywords changed from Id Rev to Date Rev Author URL Id
thingfish/branches/mp3-jukebox/spec/thingfish/daemon_spec.rb
r548 r558 699 699 700 700 701 it "knows how to store related resources" do 702 related_body = mock( "related body IO" ) 703 request = mock( "request object" ) 704 extracted_metadata = { 'stuff' => 'do good vote charlie' } 705 related_metadata = { 'format' => 'image/png' } 706 707 request.should_receive( :related_resources ). 708 at_least( :once ). 709 and_return({ :body => { related_body => related_metadata } }) 710 request.should_receive( :metadata ). 711 and_return({ related_body => extracted_metadata }) 712 713 metadata = extracted_metadata.merge( related_metadata ) 714 metadata[ :related_to ] = TEST_UUID 715 716 @daemon.should_receive( :store_resource ).with( related_body, metadata ) 717 718 @daemon.store_related_resources( :body, TEST_UUID, request ) 719 end 720 721 722 it "knows how to purge related resources" do 723 filestore = mock( "filestore", :null_object => true ) 724 @daemon.instance_variable_set( :@filestore, filestore ) 725 metastore = mock( "metastore", :null_object => true ) 726 @daemon.instance_variable_set( :@metastore, metastore ) 727 728 search_criteria = { 729 :related_to => TEST_UUID, 730 :relation => 'appended' 731 } 732 metastore.should_receive( :find_by_exact_properties ). 733 with( search_criteria ). 734 and_return( [ TEST_UUID2, TEST_UUID3 ]) 735 736 metastore.should_receive( :delete_resource ).with( TEST_UUID2 ) 737 metastore.should_receive( :delete_resource ).with( TEST_UUID3 ) 738 filestore.should_receive( :delete ).with( TEST_UUID2 ) 739 filestore.should_receive( :delete ).with( TEST_UUID3 ) 740 741 @daemon.purge_related_resources( TEST_UUID ) 742 end 743 744 745 it "doesn't propagate errors that occur while storing a related resource" do 746 related_body = mock( "related body IO" ) 747 request = mock( "request object" ) 748 extracted_metadata = { 'stuff' => 'do good vote charlie' } 749 related_metadata = { 'format' => 'image/png' } 750 751 request.should_receive( :related_resources ). 752 at_least( :once ). 753 and_return({ :body => { related_body => related_metadata } }) 754 request.should_receive( :metadata ). 755 and_return({ related_body => extracted_metadata }) 756 757 metadata = extracted_metadata.merge( related_metadata ) 758 metadata[ :related_to ] = TEST_UUID 759 760 @daemon.should_receive( :store_resource ). 761 and_raise( ThingFish::FileStoreQuotaError.new("centipede-infested") ) 762 763 lambda { 764 @daemon.store_related_resources( :body, TEST_UUID, request ) 765 }.should_not raise_error() 766 end 767 768 701 769 it "removes spoolfiles on successful uploads" do 702 770 filestore = mock( "filestore", :null_object => true ) thingfish/branches/mp3-jukebox/spec/thingfish/handler/default_spec.rb
r502 r558 50 50 resdir = Pathname.new( __FILE__ ).expand_path.dirname.parent.parent + 'var/www' 51 51 @handler = ThingFish::DefaultHandler.new( 'resource_dir' => resdir ) 52 @ listener= mock( "thingfish daemon", :null_object => true )52 @daemon = mock( "thingfish daemon", :null_object => true ) 53 53 @filestore = mock( "thingfish filestore", :null_object => true ) 54 54 @metastore = mock( "thingfish metastore", :null_object => true ) … … 58 58 @mockmetadata.stub!( :modified ).and_return('Wed Aug 29 21:24:09 -0700 2007') 59 59 60 @ listener.stub!( :filestore ).and_return( @filestore )61 @ listener.stub!( :metastore ).and_return( @metastore )62 @handler.listener = @ listener60 @daemon.stub!( :filestore ).and_return( @filestore ) 61 @daemon.stub!( :metastore ).and_return( @metastore ) 62 @handler.listener = @daemon 63 63 64 64 @request = mock( "request", :null_object => true ) … … 83 83 @request.should_receive( :uri ).at_least( :once ).and_return( uri ) 84 84 85 @ listener.should_receive( :handler_info ).and_return( :a_hash_of_info )85 @daemon.should_receive( :handler_info ).and_return( :a_hash_of_info ) 86 86 @response.should_receive( :body= ).with( an_instance_of(Hash) ) 87 87 @response.should_receive( :content_type= ).with( RUBY_MIMETYPE ) … … 120 120 @response.should_receive( :content_type= ).with( RUBY_MIMETYPE ) 121 121 122 @ listener.should_receive( :store_resource ).122 @daemon.should_receive( :store_resource ). 123 123 with( body, metadata ). 124 124 and_return( TEST_UUID ) 125 @daemon.should_receive( :store_related_resources ). 126 with( body, TEST_UUID, @request ) 125 127 126 128 @handler.handle_post_request( @request, @response ) … … 136 138 137 139 @request.should_receive( :get_body_and_metadata ).and_return([ body, md ]) 138 @ listener.should_receive( :store_resource ).140 @daemon.should_receive( :store_resource ). 139 141 with( body, md ). 140 142 and_return { raise ThingFish::FileStoreQuotaError, "too NARROW, sucka!" } … … 392 394 393 395 @request.should_receive( :get_body_and_metadata ).once.and_return([ io, {} ]) 394 @ listener.should_receive( :store_resource ).396 @daemon.should_receive( :store_resource ). 395 397 with( io, an_instance_of(Hash), TEST_UUID_OBJ ). 396 398 and_return( TEST_CHECKSUM ) 397 399 @daemon.should_receive( :store_related_resources ). 400 with( io, TEST_UUID_OBJ, @request ) 401 398 402 @response.should_receive( :status= ).with( HTTP::OK ) 399 403 … … 413 417 414 418 @request.should_receive( :get_body_and_metadata ).and_return([ io, {} ]) 415 @ listener.should_receive( :store_resource ).419 @daemon.should_receive( :store_resource ). 416 420 with( io, an_instance_of(Hash), TEST_UUID_OBJ ). 417 421 and_return( TEST_CHECKSUM ) 422 423 @daemon.should_receive( :purge_related_resources ).with( TEST_UUID_OBJ ) 418 424 419 425 @response.should_receive( :status= ).with( HTTP::CREATED ) … … 432 438 433 439 @request.should_receive( :get_body_and_metadata ).and_return([ body, md ]) 434 @ listener.should_receive( :store_resource ).440 @daemon.should_receive( :store_resource ). 435 441 with( body, md, TEST_UUID_OBJ ). 436 442 and_return { raise ThingFish::FileStoreQuotaError, "too large, sucka!" } thingfish/branches/mp3-jukebox/utils.rb
r479 r558 1 1 # 2 # Install/distribution utility functions2 # Whatever/distribution utility functions 3 3 # $Id$ 4 4 #
