lib/multi_mime.rb
require 'thread'
require 'multi_mime/adapter_error'
module MultiMime
extend self
REQUIREMENT_MAP = [
[:mime_types, 'mime/types', :MIME].freeze,
[:mimemagic, 'mimemagic', :MimeMagic].freeze,
[:mime_type, 'action_dispatch/http/mime_type', :Mime].freeze,
[:rack_mime, 'rack/mime', :Rack].freeze
]
SEMAPHORE = Mutex.new
# The default adapter based on what you currently
# have loaded and installed. First checks to see
# if any adapters are already loaded, then checks
# to see which are installed if none are loaded.
def default_adapter
return :mime_types if defined?(::MIME::Types)
return :mimemagic if defined?(::MimeMagic)
return :mime_type if defined?(::Mime::Type)
return :rack_mime if defined?(::Rack::Mime)
REQUIREMENT_MAP.each do |adapter, library, clazz|
begin
require library
return adapter
rescue ::LoadError
next
end
end
fail 'MultiMime hasn\'t been able to detect a default_adapter'
nil
end
# Get the current adapter class.
def adapter
use(default_adapter) unless defined?(@adapter) # load default adapter
@adapter
end
# Set the Mime parser utilizing a symbol, string, or class.
#
# @param [Symbol, String, Class] new_adapter
# * mime_types
# * mimemagic
# * mime_type
# * rack_mime
def use(new_adapter)
SEMAPHORE.synchronize do
@adapter = load_adapter(new_adapter)
end
end
alias_method :adapter=, :use
# Remove the currently loaded adapter
def reset_adapter
remove_instance_variable :@adapter if defined?(@adapter)
end
# Get mime type by mime type
#
# @param [String] mime_type The mime_type to determine against
# @param [Hash] opts
# * adapter [String] If set, the selected adapter will be used for this call.
# @return [String] Mime type
def type_for(mime_type, opts = {})
fail ArgumentError, "Mime Type must be a String. #{mime_type.inspect} given." unless mime_type.is_a? String
adapter = current_adapter(opts)
adapter.type_for(mime_type, opts)
end
alias_method :by_type, :type_for
# Get mime type by extension
#
# @param [String] extension The extension to determine against
# @param [Hash] opts
# * adapter [String] If set, the selected adapter will be used for this call.
# @return [String] Mime type
def type_for_extension(extension, opts = {})
fail ArgumentError, "Extension must be a String. #{extension.inspect} given." unless extension.is_a? String
adapter = current_adapter(opts)
adapter.type_for_extension(extension, opts)
end
alias_method :by_extension, :type_for_extension
# Get mime type by path
#
# @param [String] path The path to determine against
# @param [Hash] opts
# * adapter [String] If set, the selected adapter will be used for this call.
# @return [String] Mime type
def type_for_path(path, opts = {})
fail ArgumentError, "Path must be a String. #{path.inspect} given." unless path.is_a? String
adapter = current_adapter(opts)
adapter.type_for_path(path, opts)
end
alias_method :by_path, :type_for_path
# Get mime type by file
#
# @param [File] file The file to determine against
# @param [Hash] opts
# * adapter [String] If set, the selected adapter will be used for this call.
# @return [String] Mime type
def type_for_file(file, opts = {})
fail ArgumentError, "File must be a File. #{file.inspect} given." unless file.is_a? File
adapter = current_adapter(opts)
adapter.type_for_file(file, opts)
end
alias_method :by_file, :type_for_file
private
def current_adapter(opts = {})
if new_adapter = opts.delete(:adapter)
load_adapter(new_adapter)
else
adapter
end
end
def load_adapter(new_adapter)
case new_adapter
when String, Symbol
adapter_clazz = nil
REQUIREMENT_MAP.each do |adapter, library, clazz|
if new_adapter.to_sym == adapter
require library
require "multi_mime/adapters/#{new_adapter}"
adapter_clazz = MultiMime::Adapters.const_get(:"#{new_adapter.to_s.split('_').map { |s| s.capitalize }.join('')}")
else
next
end
end
if adapter_clazz.nil?
raise ::LoadError, new_adapter
else
return adapter_clazz
end
when NilClass, FalseClass
load_adapter default_adapter
when Class, Module
new_adapter
else
raise ::LoadError, new_adapter
end
rescue ::LoadError => exception
raise AdapterError.build(exception)
end
end