require 'eymiha' require 'eymiha/url/url_exception' require 'eymiha/url/url_normalizer' optional_require 'win32/registry' module Eymiha # UrlOpener opens a URL in a user's URL viewer of choice. URLs are # normalized prior to being opened. # # Instances use the Kernel::system method to open the resource in its # own Thread to avoid blocking. Any problems are raised through a # caller-supplied exception handler's handle(Exception) method. # # For Apple and Windows, the viewer-of-choice is defaulted to the user's # designated viewer in the environment. For other platforms, the caller # must assign the viewer prior to opening the URL. class UrlOpener @@url_viewer = nil # Sets and returns the user's default URL viewer-or-choice. def self.url_viewer=(url_viewer) @@url_viewer = url_viewer end # Returns the user's default URL viewer-of-choice. def self.url_viewer @@url_viewer end @@relative_base = nil # Sets and returns the default directory for opening relative file # resources. The directory is validated prior to assignment - if invalid # a UrlException will be raised. def self.relative_base=(relative_base) if UrlNormalizer.valid_relative_base? relative_base @@relative_base = relative_base else raise UrlException, "Invalid relative file base '#{relative_base}'" end end # Returns the default directory for opening relative file resources. def self.relative_base @@relative_base end @@exception_handler = nil # Sets and returns the default exception handler. Problems are reported # through its handle method, so it should either have the method, or # construct it or delegate at runtime. def self.exception_handler=(exception_handler) @@exceptionHandler = exception_handler end # Returns the default exception handler. def self.exception_handler @@exception_handler end attr_accessor :url_viewer, :exception_handler attr_reader :relative_base # Constructs and returns a new instance, using the default exception # handler, directory for opening relative file resources, and viewer of # choice. def initialize(exception_handler=@@exception_handler, relative_base=@@relative_base, url_viewer=@@url_viewer) @exception_handler = exception_handler @relative_base = relative_base @url_viewer = url_viewer @url_normalizer = UrlNormalizer.new end # Sets and Returns the directory for opening relative file resources. The # directory is validated prior to assignment - if invalid a UrlException # will be raised. def relative_base=(relative_base) if UrlNormalizer.valid_relative_base? relative_base @relative_base = relative_base else raise UrlException, "Invalid relative file base '#{relative_base}'" end end # opens the given URL in the user's viewer of choice. The method uses a # separate Thread to avoid blocking. Local file resources are opened # relative to the value of the current relative base directory, and any # problems are reported through the exception handler. def open url Thread.new { begin send "open_#{Config::CONFIG['target_vendor']}".to_sym, @url_normalizer.normalize(url.to_s,@relative_base) rescue Exception => exception @exception_handler.handle exception end terminate } end def method_missing(method,*args) open_other(*args) if method.to_s =~ /^open_/ end private def open_apple url system "#{@url_viewer||"open"} #{url}" end def windows_browser @@windows_browser ||= Win32::Registry::HKEY_CLASSES_ROOT. open('htmlfile\shell\open\command') { |reg| reg_type, reg_value = reg.read('') return reg_value } end def open_pc url system "#{@url_viewer||windows_browser} #{url}" end def open_other *args if @url_viewer system "#{@url_viewer} #{args.join(' ')}" else raise UrlException, "No program was designated to open the URL." end end end end