class Marcel::Magic

Mime type detection

def self.add(type, options)

* :comment: Comment string
* :magic: Mime magic specification
* :parents: String list or single string of parent mime types
* :extensions: String list or single string of file extensions
Option keys:

* options: Options hash
* type: Mime type
Add custom mime type. Arguments:
def self.add(type, options)
  extensions = [options[:extensions]].flatten.compact
  TYPE_EXTS[type] = extensions
  parents = [options[:parents]].flatten.compact
  TYPE_PARENTS[type] = parents unless parents.empty?
  extensions.each {|ext| EXTENSIONS[ext] = type }
  MAGIC.unshift [type, options[:magic]] if options[:magic]
end

def self.all_by_magic(io)

This is a slower operation.
Lookup all mime types by magic content analysis.
def self.all_by_magic(io)
  magic_match(io, :select).map { |mime| new(mime[0]) }
end

def self.by_extension(ext)

Lookup mime type by file extension
def self.by_extension(ext)
  ext = ext.to_s.downcase
  mime = ext[0..0] == '.' ? EXTENSIONS[ext[1..-1]] : EXTENSIONS[ext]
  mime && new(mime)
end

def self.by_magic(io)

This is a slow operation.
Lookup mime type by magic content analysis.
def self.by_magic(io)
  mime = magic_match(io, :find)
  mime && new(mime[0])
end

def self.by_path(path)

Lookup mime type by filename
def self.by_path(path)
  by_extension(File.extname(path))
end

def self.child?(child, parent)

def self.child?(child, parent)
  child == parent || TYPE_PARENTS[child]&.any? {|p| child?(p, parent) }
end

def self.magic_match(io, method)

def self.magic_match(io, method)
  return magic_match(StringIO.new(io.to_s), method) unless io.respond_to?(:read)
  buffer = "".b
  MAGIC.send(method) { |type, matches| magic_match_io(io, matches, buffer) }
end

def self.magic_match_io(io, matches, buffer)

def self.magic_match_io(io, matches, buffer)
  matches.any? do |offset, value, children|
    match =
      if value
        if value.is_a?(Regexp)
          match_regex(io, offset, value, buffer)
        elsif Range === offset
          io.read(offset.begin, buffer)
          x = io.read(offset.end - offset.begin + value.bytesize, buffer)
          x && x.include?(value)
        else
          io.read(offset, buffer)
          io.read(value.bytesize, buffer) == value
        end
      end
    io.rewind
    match && (!children || magic_match_io(io, children, buffer))
  end
end

def self.match_regex(io, offset, regexp, buffer)

def self.match_regex(io, offset, regexp, buffer)
  start = offset.is_a?(Range) ? offset.begin : offset
  io.read(start, buffer) if start > 0
  data = io.read(256, buffer)
  return false unless data
  data.match?(regexp)
end

def self.remove(type)

* type: The mime type to remove. All associated extensions and magic are removed too.
you're seeing impossible conflicts (for instance, application/x-gmc-link).
Removes a mime type from the dictionary. You might want to do this if
def self.remove(type)
  EXTENSIONS.delete_if {|ext, t| t == type }
  MAGIC.delete_if {|t, m| t == type }
  TYPE_EXTS.delete(type)
  TYPE_PARENTS.delete(type)
end

def audio?; mediatype == 'audio'; end

def audio?; mediatype == 'audio'; end

def child_of?(parent)

Returns true if type is child of parent type
def child_of?(parent)
  self.class.child?(type, parent)
end

def comment

Get mime comment
def comment
  nil # deprecated
end

def eql?(other)

Allow comparison with string
def eql?(other)
  type == other.to_s
end

def extensions

Get string list of file extensions
def extensions
  TYPE_EXTS[type] || []
end

def hash

def hash
  type.hash
end

def image?; mediatype == 'image'; end

Mediatype shortcuts
def image?; mediatype == 'image'; end

def initialize(type)

Mime type by type string
def initialize(type)
  @type = type
  @mediatype, @subtype = type.split('/', 2)
end

def text?; mediatype == 'text' || child_of?('text/plain'); end

Returns true if type is a text format
def text?; mediatype == 'text' || child_of?('text/plain'); end

def to_s

Return type as string
def to_s
  type
end

def video?; mediatype == 'video'; end

def video?; mediatype == 'video'; end