Changeset 558

Show
Ignore:
Timestamp:
06/17/08 10:36:32 (2 months ago)
Author:
mahlon
Message:
  • Added manual page for higher-level "cookbook-y" type stuff.
  • End-to-end completion of related resource additions. (Recursive
    storage for appended resources, DefaultHandler? support.)
  • Remove 'appended' related resources when performing an update (PUT).
  • Minor prep work (in the form of comments) for the removal of bodykey
    stuff.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • thingfish/branches/mp3-jukebox/docs/manual/src/Developers_Guide/index.page

    r546 r558  
    1515* <?link The Javascript Client Library ?> 
    1616* <?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  
    1111h2. <%= page.config['title'] %> 
    1212 
    13 Â« Coming Soon! » 
     13<div id="auto-toc" /> 
     14 
     15The back end of a ThingFish instance... 
     16 
     17h3. What's a FileStore do? 
     18 
     19h3. The mandatory interface 
     20 
     21h3. Optional interface 
    1422 
    1523 
     24 
     25 
  • thingfish/branches/mp3-jukebox/docs/manual/src/Hackers_Guide/writing-filters.page

    r557 r558  
    6060| 5 | Import some useful constants, like the @HTTP::OK@ a little further down | 
    6161| 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. 
    6363These special methods receive a @ThingFish::Request@@ and a @ThingFish::Response@ object  
    6464to fiddle with as you see fit. | 
     
    6767to the client. | 
    6868| 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 generated 
     69| 11 | The default response code is a @404 (Not Found)@; after we're sure we've generated 
    7070the content we want, setting this to @OK@ stops further processing, and lets the response  
    7171through successfully. | 
     
    8686 
    8787 
     88h3. Appending Related Resources 
    8889 
     90(Describe how to append things like thumbnails, previews, etc.) 
    8991 
  • thingfish/branches/mp3-jukebox/lib/thingfish/daemon.rb

    r553 r558  
    234234        return uuid 
    235235    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     
    236265     
    237266 
  • thingfish/branches/mp3-jukebox/lib/thingfish/handler/default.rb

    r464 r558  
    245245        uuid = nil 
    246246         
     247        # Store the primary resource 
    247248        body, metadata = request.get_body_and_metadata 
    248249        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 ) 
    249253 
    250254        response.status = HTTP::CREATED 
     
    267271        body, metadata = request.get_body_and_metadata 
    268272        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 ) 
    269277         
    270278        response.content_type = RUBY_MIMETYPE 
  • thingfish/branches/mp3-jukebox/lib/thingfish/request.rb

    r554 r558  
    314314        end 
    315315 
     316        related_bodykey = self.make_body_key( related_body ) 
     317        @body_key_mapping[ related_bodykey ] = related_body 
     318 
    316319        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_body 
    320320        self.related_resources[ original_body ][ related_body ] = related_metadata 
    321321    end 
     
    324324    ### Append the specified additional +metadata+ for the given +resource+, which should be one 
    325325    ### of the entity bodies yielded by #each_body 
     326    ### 
     327    ### TODO: Do we need this method after bodykey removal? 
     328    ### 
    326329    def append_metadata_for( resource, metadata ) 
    327330        # Convert the body to the key of the related resources hash 
     
    385388            hash.each do |body, _| 
    386389                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                     
    387396                    self.log.warn "Entity body closed: %p" % [ body ] 
    388397                    body.open  
    389398                end 
     399                 
    390400                body.rewind 
    391401            end 
     
    536546        return @entity_bodies 
    537547    end 
    538      
     548 
    539549 
    540550    ### For each resource => metadata pair returned by the current +iterator+, merge the 
     
    550560            body_metadata[ :format ] ||= DEFAULT_CONTENT_TYPE 
    551561            extracted_metadata = self.metadata[body] || {} 
    552  
     562             
    553563            # Content metadata is determined per-multipart section 
    554564            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  
    699699             
    700700             
     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             
    701769            it "removes spoolfiles on successful uploads" do 
    702770                filestore = mock( "filestore", :null_object => true ) 
  • thingfish/branches/mp3-jukebox/spec/thingfish/handler/default_spec.rb

    r502 r558  
    5050        resdir = Pathname.new( __FILE__ ).expand_path.dirname.parent.parent + 'var/www' 
    5151        @handler          = ThingFish::DefaultHandler.new( 'resource_dir' => resdir ) 
    52         @listener         = mock( "thingfish daemon", :null_object => true ) 
     52        @daemon           = mock( "thingfish daemon", :null_object => true ) 
    5353        @filestore        = mock( "thingfish filestore", :null_object => true ) 
    5454        @metastore        = mock( "thingfish metastore", :null_object => true ) 
     
    5858        @mockmetadata.stub!( :modified ).and_return('Wed Aug 29 21:24:09 -0700 2007') 
    5959 
    60         @listener.stub!( :filestore ).and_return( @filestore ) 
    61         @listener.stub!( :metastore ).and_return( @metastore ) 
    62         @handler.listener = @listener 
     60        @daemon.stub!( :filestore ).and_return( @filestore ) 
     61        @daemon.stub!( :metastore ).and_return( @metastore ) 
     62        @handler.listener = @daemon 
    6363 
    6464        @request          = mock( "request", :null_object => true ) 
     
    8383        @request.should_receive( :uri ).at_least( :once ).and_return( uri ) 
    8484 
    85         @listener.should_receive( :handler_info ).and_return( :a_hash_of_info ) 
     85        @daemon.should_receive( :handler_info ).and_return( :a_hash_of_info ) 
    8686        @response.should_receive( :body= ).with( an_instance_of(Hash) ) 
    8787        @response.should_receive( :content_type= ).with( RUBY_MIMETYPE ) 
     
    120120        @response.should_receive( :content_type= ).with( RUBY_MIMETYPE ) 
    121121         
    122         @listener.should_receive( :store_resource ). 
     122        @daemon.should_receive( :store_resource ). 
    123123            with( body, metadata ). 
    124124            and_return( TEST_UUID ) 
     125        @daemon.should_receive( :store_related_resources ). 
     126            with( body, TEST_UUID, @request ) 
    125127 
    126128        @handler.handle_post_request( @request, @response ) 
     
    136138         
    137139        @request.should_receive( :get_body_and_metadata ).and_return([ body, md ]) 
    138         @listener.should_receive( :store_resource ). 
     140        @daemon.should_receive( :store_resource ). 
    139141            with( body, md ). 
    140142            and_return { raise ThingFish::FileStoreQuotaError, "too NARROW, sucka!" } 
     
    392394 
    393395        @request.should_receive( :get_body_and_metadata ).once.and_return([ io, {} ]) 
    394         @listener.should_receive( :store_resource ). 
     396        @daemon.should_receive( :store_resource ). 
    395397            with( io, an_instance_of(Hash), TEST_UUID_OBJ ). 
    396398            and_return( TEST_CHECKSUM ) 
    397              
     399        @daemon.should_receive( :store_related_resources ). 
     400            with( io, TEST_UUID_OBJ, @request ) 
     401                 
    398402        @response.should_receive( :status= ).with( HTTP::OK ) 
    399403 
     
    413417 
    414418        @request.should_receive( :get_body_and_metadata ).and_return([ io, {} ]) 
    415         @listener.should_receive( :store_resource ). 
     419        @daemon.should_receive( :store_resource ). 
    416420            with( io, an_instance_of(Hash), TEST_UUID_OBJ ). 
    417421            and_return( TEST_CHECKSUM ) 
     422     
     423        @daemon.should_receive( :purge_related_resources ).with( TEST_UUID_OBJ ) 
    418424             
    419425        @response.should_receive( :status= ).with( HTTP::CREATED ) 
     
    432438 
    433439        @request.should_receive( :get_body_and_metadata ).and_return([ body, md ]) 
    434         @listener.should_receive( :store_resource ). 
     440        @daemon.should_receive( :store_resource ). 
    435441            with( body, md, TEST_UUID_OBJ ). 
    436442            and_return { raise ThingFish::FileStoreQuotaError, "too large, sucka!" } 
  • thingfish/branches/mp3-jukebox/utils.rb

    r479 r558  
    11# 
    2 #   Install/distribution utility functions 
     2#   Whatever/distribution utility functions 
    33#   $Id$ 
    44#