Changeset 580
- Timestamp:
- 07/02/08 13:55:58 (2 months ago)
- Files:
-
- thingfish/trunk/Rakefile (modified) (2 diffs)
- thingfish/trunk/lib/thingfish/client.rb (modified) (1 diff)
- thingfish/trunk/lib/thingfish/request.rb (modified) (4 diffs)
- thingfish/trunk/misc/rake/benchmark.rb (modified) (1 diff)
- thingfish/trunk/misc/rake/docs.rb (moved) (moved from thingfish/trunk/misc/rake/rdoc.rb) (3 diffs)
- thingfish/trunk/plugins/thingfish-filestore-filesystem/lib/thingfish/filestore/filesystem.rb (modified) (2 diffs)
- thingfish/trunk/plugins/thingfish-filter-image/lib/thingfish/filter/image.rb (modified) (7 diffs)
- thingfish/trunk/plugins/thingfish-filter-image/spec/thingfish/filter/image_spec.rb (modified) (1 diff)
- thingfish/trunk/plugins/thingfish-filter-mp3/lib/thingfish/filter/mp3.rb (modified) (5 diffs)
- thingfish/trunk/plugins/thingfish-handler-formupload/resources/upload.rhtml (modified) (1 diff)
- thingfish/trunk/spec/thingfish/request_spec.rb (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
thingfish/trunk/Rakefile
r568 r580 89 89 end 90 90 91 # Load task plugins91 ### Load task libraries 92 92 require RAKE_TASKDIR + 'svn.rb' 93 93 require RAKE_TASKDIR + 'verifytask.rb' 94 94 Pathname.glob( RAKE_TASKDIR + '*.rb' ).each do |tasklib| 95 trace "Loading task lib #{tasklib}" 96 require tasklib 95 next if tasklib =~ %r{/(helpers|svn|verifytask)\.rb$} 96 begin 97 require tasklib 98 rescue ScriptError => err 99 fail "Task library '%s' failed to load: %s: %s" % 100 [ tasklib, err.class.name, err.message ] 101 trace "Backtrace: \n " + err.backtrace.join( "\n " ) 102 rescue => err 103 log "Task library '%s' failed to load: %s: %s. Some tasks may not be available." % 104 [ tasklib, err.class.name, err.message ] 105 trace "Backtrace: \n " + err.backtrace.join( "\n " ) 106 end 97 107 end 108 98 109 99 110 ### Default task … … 115 126 multitask :docs => [ :manual, :coverage, :rdoc ] do 116 127 log "All documentation built." 117 end118 119 120 ### Task: manual121 require 'misc/rake/lib/manual'122 123 directory MANUALOUTPUTDIR.to_s124 directory RDOCDIR.to_s125 126 Manual::GenTask.new do |manual|127 manual.metadata.version = PKG_VERSION128 manual.metadata.api_dir = RDOCDIR129 manual.output_dir = MANUALOUTPUTDIR130 manual.base_dir = MANUALDIR131 manual.source_dir = 'src'132 end133 134 task :clobber_manual do135 rmtree( MANUALOUTPUTDIR, :verbose => true )136 128 end 137 129 thingfish/trunk/lib/thingfish/client.rb
r498 r580 141 141 142 142 ### Create a new ThingFish client object which will interact with the 143 ### server at the specified +endpoint+, which can be either the host ane,143 ### server at the specified +endpoint+, which can be either the hostname, 144 144 ### the IP address, or the URI of the server. Also set any options 145 145 ### specified as attributes on the client. thingfish/trunk/lib/thingfish/request.rb
r568 r580 326 326 ### metadata required for the filestore and metastore themselves. 327 327 def each_body( include_appended=false, &block ) # :yields: body_io, metadata_hash 328 329 # Build an iterator over entity_bodies and start the traversal of resources 330 iter = Enumerable::Enumerator.new( self.entity_bodies, :each ) 331 self.yield_each_resource( iter, include_appended, &block ) 328 self.yield_each_resource( self.entity_bodies, include_appended, &block ) 332 329 end 333 330 … … 348 345 ### Check the body IO objects to ensure they're still open. 349 346 def check_body_ios 350 self.each_body do |body,_|347 self.each_body( true ) do |body,_| 351 348 if body.closed? 352 349 self.log.warn "Body IO unexpectedly closed -- reopening a new handle" 353 350 354 351 # Create a new IO based on what the original type was 355 clone = case body356 when StringIO357 StringIO.new( body.string )358 else359 File.open( body.path, 'r' )360 end352 clone = nil 353 if body.is_a?( StringIO ) 354 clone = StringIO.new( body.string ) 355 else 356 clone = File.open( body.path, 'r' ) 357 end 361 358 362 # Retain the original IO's metadata359 # Retain the original IO's file and appended metadata 363 360 @entity_bodies[ clone ] = @entity_bodies.delete( body ) if @entity_bodies.key?( body ) 361 @metadata[ clone ] = @metadata.delete( body ) if @metadata.key?( body ) 362 363 # Splice in the cloned IO into both keys and values of the related resources 364 # tree 364 365 @related_resources[ clone ] = @related_resources.delete( body ) if @related_resources.key?( body ) 365 @metadata[ clone ] = @metadata.delete( body ) if @metadata.key?( body )366 366 @related_resources.each do |_,hash| 367 367 hash[ clone ] = hash.delete( body ) if hash.key?( body ) 368 368 end 369 369 370 self.log.debug "Body %p (%d) replaced with %p (%d)" % [ 371 body, body.object_id, clone, clone.object_id 372 ] 370 self.log.debug "Body %p (%d) replaced with %p (%d)" % 371 [ body, body.object_id, clone, clone.object_id ] 373 372 else 374 373 body.rewind … … 539 538 ### toplevel metadata with the resource-specific metadata and pass both to the 540 539 ### block. 541 def yield_each_resource( iterator, include_appended, &block )540 def yield_each_resource( resources, include_appended, &block ) 542 541 immutable_metadata = self.get_immutable_metadata 542 self.log.debug "Yielding for resources: %p" % [ resources ] 543 543 544 544 # Call the block for every resource 545 iterator.each do |body, body_metadata| 545 resources.keys.each do |body| 546 self.log.debug "Resource is: %p" % [ body ] 547 body_metadata = resources[ body ] 548 appended = self.related_resources[ body ].dup if self.related_resources.key?( body ) 549 546 550 body_metadata[ :format ] ||= DEFAULT_CONTENT_TYPE 547 551 extracted_metadata = self.metadata[body] || {} … … 555 559 556 560 # Recurse if the appended resources should be included 557 if include_appended 558 iter = Enumerable::Enumerator.new( self.related_resources[ body ], :each ) 559 self.yield_each_resource( iter, true, &block ) 561 if appended && !appended.empty? && include_appended 562 self.yield_each_resource( appended, true, &block ) 560 563 end 561 564 end thingfish/trunk/misc/rake/benchmark.rb
r529 r580 91 91 92 92 task :benchmarks => [ 'benchmarks:all' ] 93 task :bench => :benchmarks 93 94 94 95 thingfish/trunk/misc/rake/docs.rb
r550 r580 5 5 6 6 require 'rake/rdoctask' 7 8 # Try to require Darkfish for rdoc, but don't mandate it 9 begin 10 require 'rubygems' 11 gem 'darkfish-rdoc' 12 rescue LoadError; end # (ignored) 13 14 begin 15 require 'rdoc/generator/darkfish' 16 rescue LoadError; end # (ignored) 17 18 directory RDOCDIR.to_s 7 19 8 20 ### Task: rdoc … … 15 27 '-SHN', 16 28 '-i', BASEDIR.to_s, 17 '-f', 'darkfish',18 29 '-m', 'README', 19 30 '-W', 'http://opensource.laika.com/browser/thingfish/trunk/' 20 31 ] 32 33 rdoc.options += [ '-f', 'darkfish' ] 21 34 22 35 rdoc.rdoc_files.include 'README' … … 28 41 end 29 42 43 44 ### Task: manual generation 45 begin 46 require 'misc/rake/lib/manual' 47 48 directory MANUALOUTPUTDIR.to_s 49 50 Manual::GenTask.new do |manual| 51 manual.metadata.version = PKG_VERSION 52 manual.metadata.api_dir = RDOCDIR 53 manual.output_dir = MANUALOUTPUTDIR 54 manual.base_dir = MANUALDIR 55 manual.source_dir = 'src' 56 end 57 58 task :clobber_manual do 59 rmtree( MANUALOUTPUTDIR, :verbose => true ) 60 end 61 62 rescue LoadError => err 63 task :no_manual do 64 $stderr.puts "Manual-generation tasks not defined: %s" % [ err.message ] 65 end 66 67 task :manual => :no_manual 68 task :clobber_manual => :no_manual 69 end 70 71 72 thingfish/trunk/plugins/thingfish-filestore-filesystem/lib/thingfish/filestore/filesystem.rb
r548 r580 186 186 end 187 187 188 self.log.info "Wrote %d bytes." % [ data.length ] 188 189 self.update_size( path, data.length ) 189 190 return Digest::MD5.hexdigest( data ) … … 214 215 end 215 216 217 self.log.info "Wrote %d bytes (buffered)." % [ uploadsize ] 216 218 self.update_size( path, uploadsize ) 217 219 return digest.hexdigest thingfish/trunk/plugins/thingfish-filter-image/lib/thingfish/filter/image.rb
r496 r580 51 51 # SVN Id 52 52 SVNId = %q$Id$ 53 54 # The default dimensions of thumbnails 55 DEFAULT_THUMBNAIL_DIMENSIONS = [ 100, 100 ] 53 56 54 57 … … 117 120 ### Set up a new Filter object 118 121 def initialize( options={} ) # :notnew: 119 120 # Transform the installed ImageMagick's list of formats into AcceptParams 121 # for easy comparison later. 122 # A hash of image formats and their properties. Each key in the returned 123 # hash is the name of a supported image format. Each value is a string in 124 # the form "BRWA". The details are in this table: 125 # B is "*" if the format has native blob support, and "-" otherwise. 126 # R is "r" if ÃMagick can read the format, and "-" otherwise. 127 # W is "w" if ÃMagick can write the format, and "-" otherwise. 128 # A is "+" if the format supports multi-image files, and "-" otherwise. 129 @supported_formats = {} 130 Magick.formats.each do |ext,support| 131 operations = MagickOperations.new( ext, support ) 132 unless mimetype = MIMETYPE_MAP[ '.' + ext.downcase ] 133 next 134 end 135 136 # Skip some mediatypes that are too generic to be useful 137 next if IGNORED_MIMETYPES.include?( mimetype ) 138 139 self.log.debug "Registering image format %s (%s)" % [ mimetype, operations ] 140 @supported_formats[ mimetype ] = operations 141 end 122 options ||= {} 123 124 @supported_formats = find_supported_formats() 142 125 self.log.debug "Registered mimetype mapping for %d of %d support image types" % 143 126 [ @supported_formats.keys.length, Magick.formats.length ] … … 153 136 collect {|type, op| ThingFish::AcceptParam.parse(type) } 154 137 138 @thumb_dimensions = options[:thumbnail_dimensions] || DEFAULT_THUMBNAIL_DIMENSIONS 139 raise ThingFish::ConfigError, "invalid thumbnail dimensions %p" % [ @thumb_dimensions ] unless 140 @thumb_dimensions.is_a?( Array ) && @thumb_dimensions.all? {|dim| dim.is_a?(Fixnum) } 141 self.log.debug "Thumbnail dimensions are: %p" % [ @thumb_dimensions ] 142 143 @thumb_dimensions = options[:thumbnail_dimensions] || DEFAULT_THUMBNAIL_DIMENSIONS 144 raise ThingFish::ConfigError, "invalid thumbnail dimensions %p" % [ @thumb_dimensions ] unless 145 @thumb_dimensions.is_a?( Array ) && @thumb_dimensions.all? {|dim| dim.is_a?(Fixnum) } 146 self.log.debug "Thumbnail dimensions are: %p" % [ @thumb_dimensions ] 147 155 148 super 156 149 end … … 175 168 request.http_method == 'POST' 176 169 177 request.each_body do |body,metadata| 170 request.each_body( true ) do |body,metadata| 171 next if metadata[:relation] == 'thumbnail' # No thumbnails of thumbnails 172 178 173 if self.accept?( metadata[:format] ) 179 174 … … 184 179 image = images.first 185 180 image_attributes = self.extract_metadata( image ) 186 187 181 request.append_metadata_for( body, image_attributes ) 182 183 thumbnail, thumb_metadata = self.create_thumbnail( image, metadata[:title] ) 184 self.log.debug "Appending a thumbnail as a related resource" 185 request.append_related_resource( body, StringIO.new(thumbnail), thumb_metadata ) 188 186 else 189 187 self.log.debug "Skipping unhandled file type (%s)" % [metadata[:format]] … … 244 242 } 245 243 end 246 247 248 249 244 245 246 ######### 247 protected 248 ######### 249 250 ### Extract metadata from the given +image+ (a Magick::Image object) and return it in 251 ### a Hash. 252 def extract_metadata( image ) 253 image_attributes = {} 254 255 image_attributes['image_height'] = image.rows 256 image_attributes['image_width'] = image.columns 257 image_attributes['image_depth'] = image.depth 258 image_attributes['image_density'] = image.density 259 image_attributes['image_gamma'] = image.gamma 260 image_attributes['image_bounding_box'] = image.bounding_box 261 262 return image_attributes 263 end 264 265 266 ### Create a thumbnail from the given +image+ and return it in a string along with any 267 ### associated metadata. 268 def create_thumbnail( image, title ) 269 self.log.debug "Making thumbnail of max dimensions: [%d X %d]" % @thumb_dimensions 270 thumb = image.resize_to_fit( *@thumb_dimensions ) 271 imgdata = thumb.to_blob 272 273 metadata = self.extract_metadata( thumb ) 274 metadata.merge!({ 275 :format => thumb.mime_type, 276 :relation => 'thumbnail', 277 :title => "Thumbnail of %s" % [ title || image.inspect ], 278 :extent => imgdata.length, 279 }) 280 281 self.log.debug "Made thumbnail for %p" % [ image ] 282 return imgdata, metadata 283 end 284 285 250 286 ####### 251 287 private … … 309 345 310 346 311 ######### 312 protected 313 ######### 314 315 316 ### Extract metadata from the given +image+ (a Magick::Image object) and return it in 317 ### a Hash. 318 def extract_metadata( image ) 319 image_attributes = {} 320 321 image_attributes['image_height'] = image.rows 322 image_attributes['image_width'] = image.columns 323 image_attributes['image_depth'] = image.depth 324 image_attributes['image_density'] = image.density 325 image_attributes['image_gamma'] = image.gamma 326 image_attributes['image_bounding_box'] = image.bounding_box 327 328 return image_attributes 329 end 330 331 347 ### Transform the installed ImageMagick's list of formats into AcceptParams 348 ### for easy comparison later. 349 def find_supported_formats 350 formats = {} 351 352 # A hash of image formats and their properties. Each key in the returned 353 # hash is the name of a supported image format. Each value is a string in 354 # the form "BRWA". The details are in this table: 355 # B is "*" if the format has native blob support, and "-" otherwise. 356 # R is "r" if ÃMagick can read the format, and "-" otherwise. 357 # W is "w" if ÃMagick can write the format, and "-" otherwise. 358 # A is "+" if the format supports multi-image files, and "-" otherwise. 359 Magick.formats.each do |ext,support| 360 operations = MagickOperations.new( ext, support ) 361 unless mimetype = MIMETYPE_MAP[ '.' + ext.downcase ] 362 next 363 end 364 365 # Skip some mediatypes that are too generic to be useful 366 next if IGNORED_MIMETYPES.include?( mimetype ) 367 368 self.log.debug "Registering image format %s (%s)" % [ mimetype, operations ] 369 formats[ mimetype ] = operations 370 end 371 372 return formats 373 end 374 375 332 376 end # class ThingFish::ImageFilter 333 377 thingfish/trunk/plugins/thingfish-filter-image/spec/thingfish/filter/image_spec.rb
r496 r580 104 104 105 105 106 it "extracts dimension metadatafrom uploaded image data using RMagick" do106 it "extracts image metadata and thumbnail from uploaded image data using RMagick" do 107 107 @request.should_receive( :http_method ).at_least( :once ).and_return( 'POST' ) 108 108 109 image = mock( "image object", :null_object => true ) 109 # Image metadata 110 image = mock( "image object" ) 110 111 Magick::Image.should_receive( :from_blob ).with( :imagedata ).and_return([ image ]) 111 112 112 @extracted_metadata.each do |key, val| 113 image.should_receive( val ).and_return( val.to_s ) 114 @extracted_metadata[ key ] = val.to_s 113 # Set up the mock to emulate an ImageMagick object and build the expected metadata that'll 114 # be extracted by that interface 115 expected_metadata = {} 116 @extracted_metadata.each do |magick_method, metadata_key| 117 image.stub!( metadata_key ).and_return( metadata_key.to_s ) 118 expected_metadata[ magick_method ] = metadata_key.to_s 115 119 end 116 120 117 @request.should_receive( :append_metadata_for ).with( @io, @extracted_metadata ) 121 @request.should_receive( :append_metadata_for ).with( @io, expected_metadata ) 122 123 # Thumbnail 124 thumbnail = mock( "thumbnail image object" ) 125 image.should_receive( :resize_to_fit ).with( *ThingFish::ImageFilter::DEFAULT_THUMBNAIL_DIMENSIONS ). 126 and_return( thumbnail ) 127 128 thumb_metadata = {} 129 @extracted_metadata.each do |magick_method, metadata_key| 130 thumb_metadata[ metadata_key ] = metadata_key.to_s 131 end 132 133 image.stub!( :inspect ).and_return( '<inspected image>' ) 134 thumbnail.stub!( :mime_type ).and_return( :mimetype ) 135 thumb_metadata = { 136 :format => :mimetype, 137 :relation => 'thumbnail', 138 :title => "Thumbnail of <inspected image>", 139 :extent => "thumbnail_data".length, 140 } 141 142 # Set up the mock to emulate an ImageMagick object and build the expected metadata that'll 143 # be extracted by that interface 144 @extracted_metadata.each do |metadata_key, magick_method| 145 thumbnail.stub!( magick_method ).and_return( magick_method.to_s ) 146 thumb_metadata[ metadata_key ] = magick_method.to_s 147 end 148 149 thumbnail.should_receive( :to_blob ).and_return( "thumbnail_data" ) 150 StringIO.should_receive( :new ).with( "thumbnail_data" ).and_return( :thumbio ) 151 @request.should_receive( :append_related_resource ).with( @io, :thumbio, thumb_metadata ) 118 152 119 153 # Run the request filter thingfish/trunk/plugins/thingfish-filter-mp3/lib/thingfish/filter/mp3.rb
r572 r580 76 76 77 77 # The Array of types this filter is interested in 78 HANDLED_TYPES = [ ThingFish::AcceptParam.parse('audio/mpeg') ] 78 HANDLED_TYPES = [ 'audio/mpeg', 'audio/mpg', 'audio/mp3' ]. 79 collect {|mimetype| ThingFish::AcceptParam.parse(mimetype) } 79 80 HANDLED_TYPES.freeze 80 81 … … 162 163 ### Normalize metadata from the MP3Info object and return it as a hash. 163 164 def extract_id3_metadata( id3 ) 165 self.log.debug "Extracting MP3 metadata" 166 164 167 mp3_metadata = { 165 168 :mp3_frequency => id3.samplerate, … … 203 206 ### } 204 207 def extract_images( id3 ) 208 self.log.debug "Extracting embedded images" 205 209 data = {} 206 return data unless id3.hastag2? 210 211 unless id3.hastag2? 212 self.log.debug "...no id3v2 tag, so no embedded images possible." 213 return 214 end 215 216 self.log.debug "...id3v2 tag present..." 207 217 208 218 if id3.tag2.APIC 219 self.log.debug "...extracting APIC (id3v2.3+) image data." 220 209 221 images = [ id3.tag2.APIC ].flatten 210 222 images.each do |img| … … 218 230 219 231 elsif id3.tag2.PIC 232 self.log.debug "...extracting PIC (id3v2.2) image data." 233 220 234 images = [ id3.tag2.PIC ].flatten 221 235 images.each do |img| … … 228 242 } 229 243 end 244 245 else 246 self.log.debug "...no known image tag types in tags: %p" % [ id3.tag2.keys.sort ] 230 247 end 231 248 thingfish/trunk/plugins/thingfish-handler-formupload/resources/upload.rhtml
r397 r580 2 2 <h2>Uploaded <%= files.length %> file(s)</h2> 3 3 <ul> 4 <% files.sort_by { |mk, mv| mv[:title] }.each do |file, meta| %>4 <% files.sort_by { |mk, mv| mv[:title] || '' }.each do |file, meta| %> 5 5 <li><a href="<%= meta[:uuid] %>" title="<%= meta[:uuid] %>"><%= meta[:title] %></a></li> 6 6 <% end %> thingfish/trunk/spec/thingfish/request_spec.rb
r568 r580 39 39 TEMPFILE_PATH = '/var/folders/k7/k7DNYX+ZGSOBod3-pJ-Lhk++0+I/-Tmp-/thingfish.65069.0' 40 40 41 before( :all) do41 before( :all ) do 42 42 setup_logging( :fatal ) 43 43 end … … 118 118 end 119 119 120 120 121 it "knows when it does not have a multipart/form-data body" do 121 122 params = @default_params.merge({ … … 181 182 request.metadata[ upload ].should have(1).member 182 183 request.metadata[ upload ][ 'relation' ].should == 'thumbnail' 184 end 185 186 187 it "requires appended metadata to be associated with a body that's part of the request" do 188 params = { 189 'HTTP_CONTENT_TYPE' => 'image/x-bitmap', 190 'HTTP_CONTENT_LENGTH' => 18181, 191 'HTTP_USER_AGENT' => 'GarglePants/1.0', 192 'REMOTE_ADDR' => '127.0.0.1', 193 } 194 upload = StringIO.new( TEST_CONTENT ) 195 @mongrel_request.stub!( :params ).and_return( params ) 196 @mongrel_request.stub!( :body ).and_return( upload ) 197 request = ThingFish::Request.new( @mongrel_request, @config ) 198 199 mystery_resource = StringIO.new( "mystery content" ) 200 201 lambda { 202 request.each_body do |body, _| 203 metadata = { 'relation' => 'thumbnail' } 204 request.append_metadata_for( mystery_resource, metadata ) 205 end 206 }.should raise_error( ThingFish::ResourceError, /cannot append/i ) 183 207 end 184 208 … … 290 314 request = ThingFish::Request.new( @mongrel_request, @config ) 291 315 292 293 316 mystery_resource = StringIO.new( "mystery content" ) 294 317 generated_resource = StringIO.new( "generated content" ) … … 535 558 request.is_ajax_request?.should == false 536 559 end 537 538 539 describe " with cache headers" do 560 561 562 describe "with regular request headers" do 563 564 before( :all ) do 565 setup_logging( :fatal ) 566 end 567 568 after( :all ) do 569 reset_logging() 570 end 540 571 541 572 before( :each ) do … … 545 576 'SERVER_NAME' => 'thingfish.laika.com', 546 577 'SERVER_PORT' => '3474', 578 'REMOTE_ADDR' => '127.0.0.1', 547 579 'REQUEST_PATH' => '/', 548 580 'QUERY_STRING' => '', 581 'CONTENT_TYPE' => 'image/jpeg', 549 582 }) 550 583 @request_headers = mock( "request headers", :null_object => true ) … … 555 588 @etag = %{"%s"} % [ TEST_CHECKSUM ] 556 589 @weak_etag = %{W/"v1.2"} 590 end 591 592 593 ### Body IO checks 594 595 it "can sanity-check the IO objects that contain entity body data for EOF state" do 596 io = mock( "Entity body IO" ) 597 598 @request.instance_variable_set( :@entity_bodies, { io => {} } ) 599 @request.instance_variable_set( :@form_metadata, {} ) 600 601 io.should_receive( :closed? ).and_return( false ) 602 io.should_receive( :rewind ) 603 604 @request.check_body_ios 605 end 606 607 608 it "can replace a File IO object that contains entity body data if it becomes closed" do 609 io = mock( "Entity body File IO" ) 610 clone_io = mock( "Cloned entity body File IO" ) 611 @request.instance_variable_set( :@entity_bodies, { io => {} } ) 612 @request.instance_variable_set( :@form_metadata, {} ) 613 614 io.should_receive( :closed? ).and_return( true ) 615 io.should_receive( :is_a? ).with( StringIO ).and_return( false ) 616 io.should_receive( :path ).and_return( :filepath ) 617 File.should_receive( :open ).with( :filepath, 'r' ).and_return( clone_io ) 618 619 @request.check_body_ios 620 621 @request.instance_variable_get( :@entity_bodies ).should_not have_key( io ) 622 @request.metadata.should_not have_key( io ) 623 @request.instance_variable_get( :@entity_bodies ).should have_key( clone_io ) 624 @request.metadata.should have_key( clone_io ) 625 end 626 627 628 it "can replace a StringIO object that contains entity body data if it becomes closed" do 629 io = mock( "Entity body StringIO" ) 630 clone_io = mock( "Cloned entity body StringIO" ) 631 @request.instance_variable_set( :@entity_bodies, { io => {} } ) 632 @request.instance_variable_set( :@form_metadata, {} ) 633 634 io.should_receive( :closed? ).and_return( true ) 635 io.should_receive( :is_a? ).with( StringIO ).and_return( true ) 636 io.should_receive( :string ).and_return( :stringdata ) 637 StringIO.should_receive( :new ).with( :stringdata ).and_return( clone_io ) 638 639 # clone_io.should_receive( :closed? ).and_return( false ) 640 # clone_io.should_receive( :rewind ) 641 642 @request.check_body_ios 557 643 end 558 644 … … 672 758 and_return([ entity_bodies, form_metadata ]) 673 759 760 thumb_metadata = { 761 :relation => 'thumbnail', 762 :format => 'image/jpeg', 763 :title => 'filename1_thumb.jpg', 764 } 765 674 766 yielded_pairs = {} 767 @request.each_body do |res, parsed_metadata| 768 if res == io1 769 @request.append_related_resource( res, resource1, thumb_metadata ) 770 end 771 end 675 772 @request.each_body( true ) do |res, parsed_metadata| 676 if res == io1677 thumb_metadata = {678 :relation => 'thumbnail',679 :format => 'image/jpeg',680 :title => 'filename1_thumb.jpg',681 }682 @request.append_related_resource( io1, resource1, thumb_metadata )683 end684 685 773 yielded_pairs[ res ] = parsed_metadata 686 774 end … … 829 917 end 830 918 919 920 ### Body IO checks 921 922 it "can sanity-check multiple IO objects that contain entity body data for EOF state" do 923 io1 = mock( "filehandle 1" ) 924 sub_io1 = mock( "extracted filehandle 1" ) 925 io2 = mock( "filehandle 2" ) 926 927 parser = mock( "multipart parser", :null_object => true ) 928 entity_bodies = { 929 io1 => {:title => "filename1", :extent => 100292}, 930 io2 => {:title => "filename2", :extent => 100234} 931 } 932 related_resources = { 933 io1 => { sub_io1 => {} } 934 } 935 936 @request.instance_variable_set( :@entity_bodies, { io1 => {}, io2 => {} } ) 937 @request.instance_variable_set( :@form_metadata, {} ) 938 @request.instance_variable_set( :@related_resources, related_resources ) 939 940 io1.should_receive( :closed? ).and_return( false ) 941 io1.should_receive( :rewind ) 942 943 sub_io1.should_receive( :closed? ).and_return( false ) 944 sub_io1.should_receive( :rewind ) 945 946 io2.should_receive( :closed? ).and_return( false ) 947 io2.should_receive( :rewind ) 948 949 @request.check_body_ios 950 end 951 952 953 it "can replace an appended File IO object if it becomes closed" do 954 io1 = mock( "filehandle 1" ) 955 sub_io1 = mock( "extracted filehandle 1" ) 956 clone_sub_io1 = mock( "extracted filehandle 1" ) 957 io2 = mock( "filehandle 2" ) 958 959 parser = mock( "multipart parser", :null_object => true ) 960 entity_bodies = { 961 io1 => {:title => "filename1", :extent => 100292}, 962 io2 => {:title => "filename2", :extent => 100234} 963 } 964 related_resources = { 965 io1 => { sub_io1 => {} } 966 } 967 968 @request.instance_variable_set( :@entity_bodies, { io1 => {}, io2 => {} } ) 969 @request.instance_variable_set( :@form_metadata, {} ) 970 @request.instance_variable_set( :@related_resources, related_resources ) 971 972 io1.should_receive( :closed? ).and_return( false ) 973 io1.should_receive( :rewind ) 974 975 sub_io1.should_receive( :closed? ).and_return( true ) 976 sub_io1.should_receive( :is_a? ).with( StringIO ).and_return( false ) 977 sub_io1.should_receive( :path ).and_return( :filepath ) 978 File.should_receive( :open ).with( :filepath, 'r' ).and_return( clone_sub_io1 ) 979 980 io2.should_receive( :closed? ).and_return( false ) 981 io2.should_receive( :rewind ) 982 983 @request.check_body_ios 984 985 @request.related_resources[ io1 ].keys.should == [clone_sub_io1] 986 @request.metadata.should_not have_key( sub_io1 ) 987 @request.metadata.should have_key( clone_sub_io1 ) 988 end 989 990 991 it "can replace a StringIO object that contains entity body data if it becomes closed" do 992 io1 = mock( "filehandle 1" ) 993 sub_io1 = mock( "extracted filehandle 1" ) 994 clone_sub_io1 = mock( "extracted filehandle 1" ) 995 io2 = mock( "filehandle 2" ) 996 997 parser = mock( "multipart parser", :null_object => true ) 998 entity_bodies = { 999 io1 => {:title => "filename1", :extent => 100292}, 1000 io2 => {:title => "filename2", :extent => 100234} 1001 } 1002 related_resources = { 1003 io1 => { sub_io1 => {} } 1004 } 1005 1006 @request.instance_variable_set( :@entity_bodies, { io1 => {}, io2 => {} } ) 1007 @request.instance_variable_set( :@form_metadata, {} ) 1008 @request.instance_variable_set( :@related_resources, related_resources ) 1009 1010 io1.should_receive( :closed? ).and_return( false ) 1011 io1.should_receive( :rewind ) 1012 1013 sub_io1.should_receive( :closed? ).and_return( true ) 1014 sub_io1.should_receive( :is_a? ).with( StringIO ).and_return( true ) 1015 sub_io1.should_receive( :string ).and_return( :datastring ) 1016 StringIO.should_receive( :new ).with( :datastring ).and_return( clone_sub_io1 ) 1017 1018 io2.should_receive( :closed? ).and_return( false ) 1019 io2.should_receive( :rewind ) 1020 1021 @request.check_body_ios 1022 1023 @request.related_resources[ io1 ].keys.should == [clone_sub_io1] 1024 @request.metadata.should_not have_key( sub_io1 ) 1025 @request.metadata.should have_key( clone_sub_io1 ) 1026 end 1027 831 1028 end 832 1029
