module URI
Constants
- KEY_VALUE_SEPARATOR
Public Class Methods
We cannot send nested hash as a param in HTTP requests. For example, when we would like to send something like this:
{:key => "value", :nested => {:nest => "value"}}
It would be (or should be) mapped like this:
/url/?key=value&nested=%7B%3Anest%3D%3E%5C%22value%5C%22%7D
Doesn’t look to good ;) However there is a simple way to convert a nested hash into a params acceptable form. We can convert it to a form, that can be mapped into params like this:
/url/?key=value&nested[nest]=value
Here is method to convert any nested hash to a “one level” equivalent:
@author Maciej Mensfeld
# File lib/standard/facets/uri/parameters.rb, line 42 def self.nested_parameters(value, key=nil, hash={}) case value when Hash value.each do |k,v| akey = key.nil? ? :"#{k}" : :"#{key}[#{k}]" nested_parameters(v, akey, hash) end out_hash when Array value.each do |v| nested_parameters(v, "#{key}[]", hash) end hash when nil then '' else hash[key] = value hash end parameters(hash) end
Allows for taking a hash and turning it into URI/CGI params Since 1.8.x does not have ordered hashes the params might not be ordered.
@todo URI.parameters
needs URI
escaping.
@author Matt Kirk
# File lib/standard/facets/uri/parameters.rb, line 13 def self.parameters(hash) hash.map do |k,v| if v.respond_to?(:join) "#{k}=#{v.join(",")}" else "#{k}=#{v}" end end.join("&") end
Public Instance Methods
CGI
escape
# File lib/standard/facets/uri/cgi_escape.rb, line 12 def cgi_escape(string) string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do '%' + $1.unpack('H2' * $1.size).join('%').upcase end.tr(' ', '+') end
# File lib/standard/facets/uri/cgi_escape.rb, line 30 def cgi_parse(query) params = Hash.new([].freeze) query.split(/[&;]/n).each do |pairs| key, value = pairs.split('=',2).collect{|v| cgi_unescape(v) } if params.has_key?(key) params[key].push(value) else params[key] = [value] end end params end
# File lib/standard/facets/uri/cgi_escape.rb, line 21 def cgi_unescape(string) string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do [$1.delete('%')].pack('H*') end end
Decode the uri components.
# File lib/standard/facets/uri/decode.rb, line 9 def 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 = URI.query_to_hash(query_string) path.gsub!(/\+/, " ") return [path, type, parameters, query_string] end ## this is usefull for uncovering bugs! raise ArgumentError.new("the parameter '#{uri}' is not a valid uri") end
Given a hash with parameter/value pairs construct a standard query string.
URI.hash_to_query(:a => 1, :b => 2) #=> "a=1;b=2"
# File lib/standard/facets/uri/query.rb, line 23 def query(parameters) return '' unless parameters pairs = [] parameters.each do |param, value| pairs << "#{param}=#{cgi_escape(value.to_s)}" end return pairs.join(KEY_VALUE_SEPARATOR) end
Removes the query string from a uri
.
Returns the chomped uri.
# File lib/standard/facets/uri/query.rb, line 92 def query_chomp(uri) return nil unless uri query_string = self.get_query_string(uri) return uri.dup.chomp("?#{query_string}") end
This method returns the query string of a uri.
@param [String] uri
The uri string.
@return [String]
The query string, or `nil` if no query string.
# File lib/standard/facets/uri/query.rb, line 73 def query_get(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
Extend the basic query string parser provided by the cgi module. converts single valued params (the most common case) to objects instead of arrays
Returns hash of parameters, contains arrays for multivalued parameters (multiselect, checkboxes , etc).
If no query string is provided (nil or “”) returns an empty hash.
# File lib/standard/facets/uri/query.rb, line 45 def query_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
Get a uri and a hash of parameters. Inject the hash values as parameters in the query sting path. Returns the full uri.
uri - the uri to filter (String
) parameter - hash of parameters to update
Returns the full updated query string.
@todo Optimize this method.
# File lib/standard/facets/uri/query.rb, line 111 def query_update(uri, parameters) query_string = self.query_get(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.strip.empty? return "#{rest}?#{query_string}" else return rest end end
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.
TODO: find a better name?
# File lib/standard/facets/uri/query.rb, line 135 def update_request_uri(request, parameters) hash = request.parameters.dup() hash.update(parameters) ## use this in hash_to_query query_string = hash.collect { |k, v| "#{k}=#{v}" }.join(";") ## return "#{request.translated_uri}?#{query_string}" return "#{request.path}?#{query_string}" end