Use duck typing to detect IO-like objects.

This commit is contained in:
Seth Kingsley 2015-09-03 06:16:32 -07:00
parent 1a028fcbaa
commit 05a9ba3f20
4 changed files with 24 additions and 7 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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