| 161 | | |
|---|
| 162 | | # Skip over the current term by scanning past the next &, or to |
|---|
| 163 | | # then end of the string if there is no next & |
|---|
| 164 | | def skip_term |
|---|
| 165 | | scan_until(%r/\&+/) || scan(/.+/) |
|---|
| 166 | | end |
|---|
| 167 | | |
|---|
| 168 | | # After we see a key, we must look ahead to determine our next action. Cases: |
|---|
| 169 | | # |
|---|
| 170 | | # [] follows the key. Then the value must be an array. |
|---|
| 171 | | # = follows the key. (A value comes next) |
|---|
| 172 | | # & or the end of string follows the key. Then the key is a flag. |
|---|
| 173 | | # otherwise, a hash follows the key. |
|---|
| 174 | | def post_key_check(key) |
|---|
| 175 | | if eos? || check(/\&/) # a& or a\Z indicates a is a flag. |
|---|
| 176 | | bind key, nil # Curiously enough, the flag's value is nil |
|---|
| 177 | | nil |
|---|
| 178 | | elsif scan(/\[\]/) # a[b][] indicates that b is an array |
|---|
| 179 | | container key, Array |
|---|
| 180 | | nil |
|---|
| 181 | | elsif check(/\[[^\]]/) # a[b] indicates that a is a hash |
|---|
| 182 | | container key, Hash |
|---|
| 183 | | nil |
|---|
| 184 | | else # Presumably an = sign is next. |
|---|
| 185 | | key |
|---|
| 186 | | end |
|---|
| 187 | | end |
|---|
| 188 | | |
|---|
| 189 | | # Add a container to the stack. |
|---|
| 190 | | # |
|---|
| 191 | | def container(key, klass) |
|---|
| 192 | | raise TypeError if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass) |
|---|
| 193 | | value = bind(key, klass.new) |
|---|
| 194 | | raise TypeError unless value.is_a? klass |
|---|
| 195 | | push value |
|---|
| 196 | | end |
|---|
| 197 | | |
|---|
| 198 | | # Push a value onto the 'stack', which is actually only the top 2 items. |
|---|
| 199 | | def push(value) |
|---|
| 200 | | @parent, @top = @top, value |
|---|
| 201 | | end |
|---|
| 202 | | |
|---|
| 203 | | # Bind a key (which may be nil for items in an array) to the provided value. |
|---|
| 204 | | def bind(key, value) |
|---|
| 205 | | if top.is_a? Array |
|---|
| 206 | | if key |
|---|
| 207 | | if top[-1].is_a?(Hash) && ! top[-1].key?(key) |
|---|
| 208 | | top[-1][key] = value |
|---|
| | 167 | |
|---|
| | 168 | private |
|---|
| | 169 | # Turn keys like person%5Bname%5D into person[name], so they can be processed as hashes |
|---|
| | 170 | def unescape_keys(query_string) |
|---|
| | 171 | query_string.split('&').collect do |fragment| |
|---|
| | 172 | key, value = fragment.split('=', 2) |
|---|
| | 173 | |
|---|
| | 174 | if key |
|---|
| | 175 | key = key.gsub(/%5D/, ']').gsub(/%5B/, '[') |
|---|
| | 176 | [ key, value ].join("=") |
|---|
| 210 | | top << {key => value}.with_indifferent_access |
|---|
| 211 | | push top.last |
|---|
| 212 | | end |
|---|
| 213 | | else |
|---|
| 214 | | top << value |
|---|
| 215 | | end |
|---|
| 216 | | elsif top.is_a? Hash |
|---|
| 217 | | key = CGI.unescape(key) |
|---|
| 218 | | if top.key?(key) && parent.is_a?(Array) |
|---|
| 219 | | parent << (@top = {}) |
|---|
| 220 | | end |
|---|
| 221 | | return top[key] ||= value |
|---|
| 222 | | else |
|---|
| 223 | | # Do nothing? |
|---|
| 224 | | end |
|---|
| 225 | | return value |
|---|
| 226 | | end |
|---|
| 227 | | end |
|---|
| | 178 | fragment |
|---|
| | 179 | end |
|---|
| | 180 | end.join('&') |
|---|
| | 181 | end |
|---|
| | 182 | |
|---|
| | 183 | # Skip over the current term by scanning past the next &, or to |
|---|
| | 184 | # then end of the string if there is no next & |
|---|
| | 185 | def skip_term |
|---|
| | 186 | scan_until(%r/\&+/) || scan(/.+/) |
|---|
| | 187 | end |
|---|
| | 188 | |
|---|
| | 189 | # After we see a key, we must look ahead to determine our next action. Cases: |
|---|
| | 190 | # |
|---|
| | 191 | # [] follows the key. Then the value must be an array. |
|---|
| | 192 | # = follows the key. (A value comes next) |
|---|
| | 193 | # & or the end of string follows the key. Then the key is a flag. |
|---|
| | 194 | # otherwise, a hash follows the key. |
|---|
| | 195 | def post_key_check(key) |
|---|
| | 196 | if eos? || check(/\&/) # a& or a\Z indicates a is a flag. |
|---|
| | 197 | bind key, nil # Curiously enough, the flag's value is nil |
|---|
| | 198 | nil |
|---|
| | 199 | elsif scan(/\[\]/) # a[b][] indicates that b is an array |
|---|
| | 200 | container key, Array |
|---|
| | 201 | nil |
|---|
| | 202 | elsif check(/\[[^\]]/) # a[b] indicates that a is a hash |
|---|
| | 203 | container key, Hash |
|---|
| | 204 | nil |
|---|
| | 205 | else # Presumably an = sign is next. |
|---|
| | 206 | key |
|---|
| | 207 | end |
|---|
| | 208 | end |
|---|
| | 209 | |
|---|
| | 210 | # Add a container to the stack. |
|---|
| | 211 | # |
|---|
| | 212 | def container(key, klass) |
|---|
| | 213 | raise TypeError if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass) |
|---|
| | 214 | value = bind(key, klass.new) |
|---|
| | 215 | raise TypeError unless value.is_a? klass |
|---|
| | 216 | push value |
|---|
| | 217 | end |
|---|
| | 218 | |
|---|
| | 219 | # Push a value onto the 'stack', which is actually only the top 2 items. |
|---|
| | 220 | def push(value) |
|---|
| | 221 | @parent, @top = @top, value |
|---|
| | 222 | end |
|---|
| | 223 | |
|---|
| | 224 | # Bind a key (which may be nil for items in an array) to the provided value. |
|---|
| | 225 | def bind(key, value) |
|---|
| | 226 | if top.is_a? Array |
|---|
| | 227 | if key |
|---|
| | 228 | if top[-1].is_a?(Hash) && ! top[-1].key?(key) |
|---|
| | 229 | top[-1][key] = value |
|---|
| | 230 | else |
|---|
| | 231 | top << {key => value}.with_indifferent_access |
|---|
| | 232 | push top.last |
|---|
| | 233 | end |
|---|
| | 234 | else |
|---|
| | 235 | top << value |
|---|
| | 236 | end |
|---|
| | 237 | elsif top.is_a? Hash |
|---|
| | 238 | key = CGI.unescape(key) |
|---|
| | 239 | if top.key?(key) && parent.is_a?(Array) |
|---|
| | 240 | parent << (@top = {}) |
|---|
| | 241 | end |
|---|
| | 242 | return top[key] ||= value |
|---|
| | 243 | else |
|---|
| | 244 | # Do nothing? |
|---|
| | 245 | end |
|---|
| | 246 | return value |
|---|
| | 247 | end |
|---|
| | 248 | end |
|---|