module Marcel class MimeType BINARY = "application/octet-stream" class << self def extend(type, extensions: [], parents: [], magic: nil) existing = Marcel::TYPES[type] || [[], [], ""] extensions = (Array(extensions) + existing[0]).uniq parents = (Array(parents) + existing[1]).uniq comment = existing[2] Magic.add(type, extensions: extensions, magic: magic, parents: parents, comment: comment) end def for(pathname_or_io = nil, name: nil, extension: nil, declared_type: nil) type_from_data = for_data(pathname_or_io) fallback_type = for_declared_type(declared_type) || for_name(name) || for_extension(extension) || BINARY if type_from_data most_specific_type type_from_data, fallback_type else fallback_type end end private def for_data(pathname_or_io) if pathname_or_io with_io(pathname_or_io) do |io| if magic = Marcel::Magic.by_magic(io) magic.type.downcase end end end end def for_name(name) if name if magic = Marcel::Magic.by_path(name) magic.type.downcase end end end def for_extension(extension) if extension if magic = Marcel::Magic.by_extension(extension) magic.type.downcase end end end def for_declared_type(declared_type) type = parse_media_type(declared_type) if type != BINARY && !type.nil? type.downcase end end def with_io(pathname_or_io, &block) if defined?(Pathname) && pathname_or_io.is_a?(Pathname) pathname_or_io.open(&block) else yield pathname_or_io end end def parse_media_type(content_type) if content_type result = content_type.downcase.split(/[;,\s]/, 2).first result if result && result.index("/") end end # For some document types (notably Microsoft Office) we recognise the main content # type with magic, but not the specific subclass. In this situation, if we can get a more # specific class using either the name or declared_type, we should use that in preference def most_specific_type(from_magic_type, fallback_type) if (root_types(from_magic_type) & root_types(fallback_type)).any? fallback_type else from_magic_type end end def root_types(type) if TYPES[type].nil? || TYPES[type][1].empty? [ type ] else TYPES[type][1].map {|t| root_types t }.flatten end end end end end require 'marcel/mime_type/definitions'