Module: URI

Defined in:
lib/standard/facets/uri.rb,
lib/standard/facets/uri/query.rb,
lib/standard/facets/uri/decode.rb,
lib/standard/facets/uri/cgi_escape.rb,
lib/standard/facets/uri/parameters.rb

Defined Under Namespace

Modules: Hash, Kernel

Constant Summary collapse

KEY_VALUE_SEPARATOR =
";"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.cgi_escape(string) ⇒ Object

CGI escape


12
13
14
15
16
# 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

.cgi_parse(query) ⇒ Object


30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 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

.cgi_unescape(string) ⇒ Object


21
22
23
24
25
# 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(uri) ⇒ Object

Decode the uri components.

Raises:

  • (ArgumentError)

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 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

.nested_parameters(value, key = nil, hash = {}) ⇒ Object

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


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 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

.parameters(hash) ⇒ Object

TODO:

URI.parameters needs URI escaping.

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.

Author:

  • Matt Kirk


13
14
15
16
17
18
19
20
21
# 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

.query(parameters) ⇒ Object

Given a hash with parameter/value pairs construct a standard query string.

URI.hash_to_query(:a => 1, :b => 2)#=> "a=1;b=2"


23
24
25
26
27
28
29
30
# 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

.query_chomp(uri) ⇒ Object

Removes the query string from a uri.

Returns the chomped uri.


92
93
94
95
96
# 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

.query_get(uri) ⇒ String Also known as: get_query_string

This method returns the query string of a uri.

Parameters:

  • uri (String)

    The uri string.

Returns:

  • (String)

    The query string, or `nil` if no query string.


73
74
75
76
77
78
79
80
81
82
83
# 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

.query_hash(query_string) ⇒ Object

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.


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 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

.query_update(uri, parameters) ⇒ Object

TODO:

Optimize this method.

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.


111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 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

.update_request_uri(request, parameters) ⇒ Object

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?


135
136
137
138
139
140
141
142
143
144
145
146
# 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

Instance Method Details

#cgi_escape(string) ⇒ Object (private)

CGI escape


12
13
14
15
16
# 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

#cgi_parse(query) ⇒ Object (private)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 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

#cgi_unescape(string) ⇒ Object (private)


21
22
23
24
25
# 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(uri) ⇒ Object (private)

Decode the uri components.


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 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

#get_query_stringObject (private)


85
# File 'lib/standard/facets/uri/query.rb', line 85

alias_method :get_query_string, :query_get

#query(parameters) ⇒ Object (private)

Given a hash with parameter/value pairs construct a standard query string.

URI.hash_to_query(:a => 1, :b => 2)#=> "a=1;b=2"


23
24
25
26
27
28
29
30
# 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

#query_chomp(uri) ⇒ Object (private)

Removes the query string from a uri.

Returns the chomped uri.


92
93
94
95
96
# 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

#query_get(uri) ⇒ String (private)

This method returns the query string of a uri.

Parameters:

  • uri (String)

    The uri string.

Returns:

  • (String)

    The query string, or `nil` if no query string.


73
74
75
76
77
78
79
80
81
82
83
# 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

#query_hash(query_string) ⇒ Object (private)

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.


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 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

#query_update(uri, parameters) ⇒ Object (private)

TODO:

Optimize this method.

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.


111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 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

#update_request_uri(request, parameters) ⇒ Object (private)

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?


135
136
137
138
139
140
141
142
143
144
145
146
# 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