require 'uri' require 'cgi' require 'facet/string/blank' module Glue # URI utilities collection. # # === Design # # Implement as a module to avoid class polution. You can still # use Ruby's advanced features to include the module in your # class. Passing the object to act upon allows to check for nil, # which isn't possible if you use self. # # The uris passed as parameters are typically strings. #-- # gmosx, TODO: deprecate this. #++ module UriUtils # Decode the uri components. def self.decode(uri) # gmosx: hmm is this needed? # guard against invalid filenames for example pictures with # spaces uploaded by users escaped_uri = uri.gsub(/ /, "+") if md = URI::REGEXP::REL_URI.match(escaped_uri) path = "#{md[5]}#{md[6]}" type = File.extname(path) query_string = md[7] # real_path = "#{$root_dir}/#{path}" parameters = Glue::UriUtils.query_string_to_hash(query_string) path.gsub!(/\+/, " ") return [path, type, parameters, query_string] end # match # this is usefull for uncovering bugs! raise ArgumentError.new("the parameter '#{uri}' is not a valid uri") end # Extend the basic query string parser provided by the cgi module. # converts single valued params (the most common case) to # objects instead of arrays # # Input: # the query string # # Output: # hash of parameters, contains arrays for multivalued parameters # (multiselect, checkboxes , etc) # If no query string is provided (nil or "") returns an empty hash. def self.query_string_to_hash(query_string) return {} unless query_string query_parameters = CGI::parse(query_string) query_parameters.each { |key, val| # replace the array with an object query_parameters[key] = val[0] if 1 == val.length } # set default value to nil! cgi sets this to [] query_parameters.default = nil return query_parameters end # Given a hash with parameter/value pairs construct a # standard query string. This method only encodes simple # types (Numeric, String) to avoid query string polution # with marshal, etc. # # gmosx, FIXME: only numeric and strings are passed to # the latest code, so update old code and optimize this! # # Input: # the parameter hash # # Output: # the query string def self.hash_to_query_string(parameters) return nil unless parameters pairs = [] parameters.each { |param, value| # only encode simple classes ! if value.is_a?(Numeric) or value.is_a?(String) pairs << "#{param}=#{CGI.escape(value.to_s)}" end } return pairs.join(";") end # This method returns the query string of a uri # # Input: # the uri # # Output: # the query string. # returns nil if no query string def self.get_query_string(uri) return nil unless uri # gmosx: INVESTIGATE ruby's URI seems to differently handle # abs and rel uris. if md = URI::REGEXP::ABS_URI.match(uri) return md[8] elsif md = URI::REGEXP::REL_URI.match(uri) return md[7] end return nil end # Removes the query string from a uri # # Input: # the uri # # Output: # the chomped uri. def self.chomp_query_string(uri) return nil unless uri query_string = self.get_query_string(uri) return uri.dup.chomp("?#{query_string}") end # Get a uri and a hash of parameters. Inject the hash values # as parameters in the query sting path. Returns the full # uri. # # Input: # the uri to filter (String) # hash of parameters to update # # Output: # the full updated query string # # === TODO: # optimize this a litle bit. def self.update_query_string(uri, parameters) query_string = self.get_query_string(uri) rest = uri.dup.gsub(/\?#{query_string}/, "") hash = self.query_string_to_hash(query_string) hash.update(parameters) query_string = self.hash_to_query_string(hash) unless query_string.blank? return "#{rest}?#{query_string}" else return rest end end # TODO: find a better name. # Gets the request uri, injects extra parameters in the query string # and returns a new uri. The request object is not modified. # There is always a qs string so an extra test is skipped. def self.update_request_uri(request, parameters) hash = request.parameters.dup() hash.update(parameters) # use this in hash_to_querystring. query_string = hash.collect { |k, v| "#{k}=#{v}" }.join(";") return "#{request.translated_uri}?#{query_string}" end end end # * George Moschovitis