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