diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 4d5b676..c0287dd 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -176,7 +176,7 @@ module Zip end def read_c_dir_entry(io) #:nodoc:all - path = if io.is_a?(::IO) + path = if io.respond_to?(:path) io.path else io @@ -548,7 +548,7 @@ module Zip end def get_raw_input_stream(&block) - if @zipfile.is_a?(::IO) || @zipfile.is_a?(::StringIO) + if @zipfile.respond_to?(:seek) && @zipfile.respond_to?(:read) yield @zipfile else ::File.open(@zipfile, 'rb', &block) diff --git a/lib/zip/file.rb b/lib/zip/file.rb index 9933233..4e5097f 100644 --- a/lib/zip/file.rb +++ b/lib/zip/file.rb @@ -49,6 +49,7 @@ module Zip MAX_SEGMENT_SIZE = 3_221_225_472 MIN_SEGMENT_SIZE = 65_536 DATA_BUFFER_SIZE = 8192 + IO_METHODS = [:tell, :seek, :read, :close] attr_reader :name @@ -117,13 +118,13 @@ module Zip # (This can be used to extract data from a # downloaded zip archive without first saving it to disk.) def open_buffer(io, options = {}) - unless io.is_a?(IO) || io.is_a?(String) || io.is_a?(Tempfile) || io.is_a?(StringIO) - raise "Zip::File.open_buffer expects an argument of class String, IO, StringIO, or Tempfile. Found: #{io.class}" + unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.is_a?(String) + raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}" end if io.is_a?(::String) require 'stringio' io = ::StringIO.new(io) - elsif io.is_a?(IO) + elsif io.respond_to?(:binmode) # https://github.com/rubyzip/rubyzip/issues/119 io.binmode end diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 50c5262..f8e7886 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -111,8 +111,7 @@ module Zip protected def get_io(io_or_file, offset = 0) - case io_or_file - when IO, StringIO + if io_or_file.respond_to?(:seek) io = io_or_file.dup io.seek(offset, ::IO::SEEK_SET) io diff --git a/test/input_stream_test.rb b/test/input_stream_test.rb index 49dd5e4..ade19e4 100644 --- a/test/input_stream_test.rb +++ b/test/input_stream_test.rb @@ -3,6 +3,16 @@ require 'test_helper' class ZipInputStreamTest < MiniTest::Test include AssertEntry + class IOLike + extend Forwardable + + def initialize(path, mode) + @file = File.new(path, mode) + end + + delegate ::Zip::File::IO_METHODS => :@file + end + def test_new zis = ::Zip::InputStream.new(TestZipFile::TEST_ZIP2.zip_name) assert_stream_contents(zis, TestZipFile::TEST_ZIP2) @@ -48,6 +58,13 @@ class ZipInputStreamTest < MiniTest::Test assert_stream_contents(zis, TestZipFile::TEST_ZIP2) end + def test_open_io_like_with_block + ::Zip::InputStream.open(IOLike.new(TestZipFile::TEST_ZIP2.zip_name, 'rb')) do |zis| + assert_stream_contents(zis, TestZipFile::TEST_ZIP2) + assert_equal(true, zis.eof?) + end + end + def test_incomplete_reads ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| entry = zis.get_next_entry # longAscii.txt