require 'uri' require 'eymiha/url/url_exception' module Eymiha # A UrlNormalizer normalizes URLs. They are validated using the URI class # except when they can be identified as file resources. When so, if the # designated file's name starts with a slash (or a drive identifier # followed by a slash on a windows box) the path is used verbatim. If not, # the named file is assumed to be relative to a caller-supplied base # directory. class UrlNormalizer @@using_pc_filesystem = Config::CONFIG['target_vendor'] == "pc" @@relative_base = nil # Returns true if the given relative_base is valid. It should start and # end with a slash (or start with a drive designator and a slash and end # with a slash on a PC). def self.valid_relative_base? relative_base ((@@using_pc_filesystem and (relative_base =~ /^[A-Za-z]:\//)) or (!@@using_pc_filesystem and (relative_base =~ /^\//))) and (relative_base =~ /\/$/) end # Sets and returns the default directory for normalizing relative file # resources. The directory is validated prior to assignment - if invalid # a UrlException will be raised. def self.relative_base=(relative_base) if 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 normalizing relative file resources. def self.relative_base @@relative_base end attr_reader :relative_base # Creates a new instance using the default direcory for normalizing # relative file resources. def initialize() @relative_base = @@relative_base end # Returns a normalized URL relative to the given base if it is a # relative file resource. def normalize(url,relative_base=nil) self.relative_base = relative_base if relative_base (self.class.file_url? url) ? normalize_file(url) : URI.parse(url).to_s end # Returns true if the given URL is a file resource. def self.file_url? url (url =~ /^file:/) or (url =~ /^\//) or ((url =~ /^[A-Za-z]:/) and @@using_pc_filesystem) or !(url =~ /:/) end # Normalizes and returns a relative or absolute URL file resource. def normalize_file file_url "file://#{normalize_file_path((file_url =~ /file:\/\//) ? $' : file_url)}" end # Sets and Returns the directory for normalizing relative file resources. # The directory is validated prior to assignment - if invalid a # UrlException will be raised. def relative_base=(relative_base) if self.class.valid_relative_base? relative_base @relative_base = relative_base else raise UrlException, "Invalid relative file base '#{relative_base}'" end end # Returns true if the given URL file resource is absolute. def self.absolute_file_path? file_url @@using_pc_filesystem ? ((file_url =~ /^[A-Za-z]:\//) or (file_url =~ /^\//)) : (!(file_url =~ /^[A-Za-z]:/) and (file_url =~ /^\//)) end # grafts on the drive designator for an absolute file if not present and # on a pc filesystem def self.absolute_file_path file_url (@@using_pc_filesystem && (file_url =~ /^\//)) ? "C:#{file_url}" : file_url end # Returns an absolute path for the given URL file resource. This is either # the incoming URL if absolute, or the URL anchored at the relative base # if relative. def normalize_file_path file_url if self.class.absolute_file_path? file_url self.class.absolute_file_path file_url elsif @relative_base != nil "#{relative_base}#{file_url}" else raise UrlException, "no relative file base was configured" end end end end