2 module NestedParamsEncoder
3 ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
6 return s.to_s.gsub(ESCAPE_RE) {
7 '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
15 def self.encode(params)
16 return nil if params == nil
18 if !params.is_a?(Array)
19 if !params.respond_to?(:to_hash)
21 "Can't convert #{params.class} into Hash."
23 params = params.to_hash
24 params = params.map do |key, value|
25 key = key.to_s if key.kind_of?(Symbol)
28 # Useful default for OAuth and caching.
29 # Only to be used for non-Array inputs. Arrays should preserve order.
34 to_query = lambda do |parent, value|
36 value = value.map do |key, val|
42 value.each do |key, val|
43 new_parent = "#{parent}%5B#{key}%5D"
44 buffer << "#{to_query.call(new_parent, val)}&"
47 elsif value.is_a?(Array)
49 value.each_with_index do |val, i|
50 new_parent = "#{parent}%5B%5D"
51 buffer << "#{to_query.call(new_parent, val)}&"
55 encoded_value = escape(value)
56 return "#{parent}=#{encoded_value}"
60 # The params have form [['key1', 'value1'], ['key2', 'value2']].
62 params.each do |parent, value|
63 encoded_parent = escape(parent)
64 buffer << "#{to_query.call(encoded_parent, value)}&"
69 def self.decode(query)
70 return nil if query == nil
71 # Recursive helper lambda
72 dehash = lambda do |hash|
73 hash.each do |(key, value)|
74 if value.kind_of?(Hash)
75 hash[key] = dehash.call(value)
78 # Numeric keys implies an array
79 if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
80 hash.sort.inject([]) do |accu, (_, value)|
88 empty_accumulator = {}
89 return ((query.split('&').map do |pair|
90 pair.split('=', 2) if pair && !pair.empty?
91 end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
93 if value.kind_of?(String)
94 value = unescape(value.gsub(/\+/, ' '))
97 array_notation = !!(key =~ /\[\]$/)
98 subkeys = key.split(/[\[\]]+/)
100 for i in 0...(subkeys.size - 1)
102 current_hash[subkey] = {} unless current_hash[subkey]
103 current_hash = current_hash[subkey]
106 current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
107 current_hash[subkeys.last] << value
109 current_hash[subkeys.last] = value
112 end).inject(empty_accumulator.dup) do |accu, (key, value)|
113 accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
119 module FlatParamsEncoder
120 ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
123 return s.to_s.gsub(ESCAPE_RE) {
124 '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
132 def self.encode(params)
133 return nil if params == nil
135 if !params.is_a?(Array)
136 if !params.respond_to?(:to_hash)
138 "Can't convert #{params.class} into Hash."
140 params = params.to_hash
141 params = params.map do |key, value|
142 key = key.to_s if key.kind_of?(Symbol)
145 # Useful default for OAuth and caching.
146 # Only to be used for non-Array inputs. Arrays should preserve order.
150 # The params have form [['key1', 'value1'], ['key2', 'value2']].
152 params.each do |key, value|
153 encoded_key = escape(key)
154 value = value.to_s if value == true || value == false
156 buffer << "#{encoded_key}&"
157 elsif value.kind_of?(Array)
158 value.each do |sub_value|
159 encoded_value = escape(sub_value)
160 buffer << "#{encoded_key}=#{encoded_value}&"
163 encoded_value = escape(value)
164 buffer << "#{encoded_key}=#{encoded_value}&"
170 def self.decode(query)
171 empty_accumulator = {}
172 return nil if query == nil
173 split_query = (query.split('&').map do |pair|
174 pair.split('=', 2) if pair && !pair.empty?
176 return split_query.inject(empty_accumulator.dup) do |accu, pair|
177 pair[0] = unescape(pair[0])
178 pair[1] = true if pair[1].nil?
179 if pair[1].respond_to?(:to_str)
180 pair[1] = unescape(pair[1].to_str.gsub(/\+/, " "))
182 if accu[pair[0]].kind_of?(Array)
183 accu[pair[0]] << pair[1]
185 accu[pair[0]] = [accu[pair[0]], pair[1]]
187 accu[pair[0]] = pair[1]