Commit 9ebbf0d9 by Janko Marohnić

Support IO-like objects efficiently

Previously if we passed in an IO object which doesn't respond to #seek,
MimeMagic would read the whole IO into memory. And this is exactly the
type of objects that the Shrine file upload library deals with.

However, we can also "seek" by blank-reading that many bytes, and always
rewinding to the beginning, which is what we've implemented in this
change.

This change also doesn't require IOs to respond to #binmode.
parent 7cd73b35
......@@ -109,28 +109,27 @@ class MimeMagic
end
def self.magic_match(io, method)
if io.respond_to?(:seek) && io.respond_to?(:read)
io.binmode
return magic_match(StringIO.new(io.to_s), method) unless io.respond_to?(:read)
io.binmode if io.respond_to?(:binmode)
io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding)
buffer = "".force_encoding(Encoding::BINARY)
MAGIC.send(method) { |type, matches| magic_match_io(io, matches, buffer) }
else
str = io.respond_to?(:read) ? io.read : io.to_s
magic_match(StringIO.new(str), method)
end
end
def self.magic_match_io(io, matches, buffer)
matches.any? do |offset, value, children|
match =
if Range === offset
io.seek(offset.begin)
io.read(offset.begin, buffer)
x = io.read(offset.end - offset.begin + value.bytesize, buffer)
x && x.include?(value)
else
io.seek(offset)
io.read(offset, buffer)
io.read(value.bytesize, buffer) == value
end
io.rewind
match && (!children || magic_match_io(io, children, buffer))
end
end
......
require 'bacon'
require 'mimemagic'
require 'stringio'
require 'forwardable'
describe 'MimeMagic' do
it 'should have type, mediatype and subtype' do
......@@ -116,12 +117,15 @@ describe 'MimeMagic' do
it 'should handle different file objects' do
MimeMagic.add('application/mimemagic-test', magic: [[0, 'MAGICTEST']])
class ReadableObj
def read
'MAGICTEST'
class IOObject
def initialize
@io = StringIO.new('MAGICTEST')
end
extend Forwardable
delegate [:read, :size, :rewind, :eof?, :close] => :@io
end
MimeMagic.by_magic(ReadableObj.new).should.equal 'application/mimemagic-test'
MimeMagic.by_magic(IOObject.new).should.equal 'application/mimemagic-test'
class StringableObject
def to_s
'MAGICTEST'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment