Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

QueryMapper parses query strings including nested params #337

Closed
wants to merge 1 commit into from

2 participants

@YanhaoYang

The QueryMapper in current release can not parse the query strings which includes nested and mixed hash and array, like {"first" => [{"two" => [{"three" => "four"}, "five"]}]}. The commit improves that.

@bblimke
Owner

@YanhaoYang thank you for adding support for that. I admire you managed to understand existing
code copied from addressable.

The code in query mapper which was copied from addressable was already complex,
and now it's even more complex so it's difficult to understand what it's doing :)
There is a spec so I do believe it works obviously.

It would be nice to refactor it and extract methods. With this level of nesting and 'if''s,
I hope it will just work and I won't have to maintain it. Do you think it's worth it? Makes sense?

@bblimke
Owner

I guess Addressable supports nested params nowadays?

@simonoff simonoff referenced this pull request from a commit in dotpromo/webmock
@simonoff simonoff Fix for #337 with refactoring and more tests c0a72ad
@simonoff simonoff referenced this pull request from a commit in dotpromo/webmock
@simonoff simonoff Fix for #337 with refactoring and more tests 963abd8
@bblimke bblimke closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 78 additions and 16 deletions.
  1. +58 −16 lib/webmock/util/query_mapper.rb
  2. +20 −0 spec/unit/util/query_mapper_spec.rb
View
74 lib/webmock/util/query_mapper.rb
@@ -81,24 +81,66 @@ def self.query_to_values(query, options={})
if options[:notation] == :dot
array_value = false
subkeys = key.split(".")
+ current_hash = accumulator
+ subkeys[0..-2].each do |subkey|
+ subkey = subkeys[i]
+ current_hash[subkey] = {} unless current_hash[subkey]
+ current_hash = current_hash[subkey]
+ end
+ if array_value
+ if current_hash[subkeys.last] && !current_hash[subkeys.last].is_a?(Array)
+ current_hash[subkeys.last] = [current_hash[subkeys.last]]
+ end
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
+ current_hash[subkeys.last] << value
+ else
+ current_hash[subkeys.last] = value
+ end
elsif options[:notation] == :subscript
- array_value = !!(key =~ /\[\]$/)
- subkeys = key.split(/[\[\]]+/)
- end
- current_hash = accumulator
- for i in 0...(subkeys.size - 1)
- subkey = subkeys[i]
- current_hash[subkey] = {} unless current_hash[subkey]
- current_hash = current_hash[subkey]
- end
- if array_value
- if current_hash[subkeys.last] && !current_hash[subkeys.last].is_a?(Array)
- current_hash[subkeys.last] = [current_hash[subkeys.last]]
+ current_node = accumulator
+ subkeys = key.split(/(?=\[\w)/)
+ subkeys[0..-2].each do |subkey|
+ node = subkey =~ /\[\]$/ ? [] : {}
+ subkey = subkey.gsub(/[\[\]]/, '')
+ if current_node.is_a? Array
+ container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(subkey) }
+ if container
+ current_node = container[subkey]
+ else
+ current_node << {subkey => node}
+ current_node = node
+ end
+ else
+ current_node[subkey] = node unless current_node[subkey]
+ current_node = current_node[subkey]
+ end
+ end
+ last_key = subkeys.last
+ array_value = !!(last_key =~ /\[\]$/)
+ last_key = last_key.gsub(/[\[\]]/, '')
+ if current_node.is_a? Array
+ container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(last_key) }
+ if container
+ if array_value
+ container[last_key] << value
+ else
+ container[last_key] = value
+ end
+ else
+ if array_value
+ current_node << {last_key => [value]}
+ else
+ current_node << {last_key => value}
+ end
+ end
+ else
+ if array_value
+ current_node[last_key] = [] unless current_node[last_key]
+ current_node[last_key] << value
+ else
+ current_node[last_key] = value
+ end
end
- current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
- current_hash[subkeys.last] << value
- else
- current_hash[subkeys.last] = value
end
end
accumulator
View
20 spec/unit/util/query_mapper_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe WebMock::Util::QueryMapper do
+
+ it "should parse hash queries" do
+ # {"one" => {"two" => {"three" => ["four", "five"]}}}
+ query = "one%5Btwo%5D%5Bthree%5D%5B%5D=four&one%5Btwo%5D%5Bthree%5D%5B%5D=five"
+ hsh = WebMock::Util::QueryMapper.query_to_values(query)
+ hsh["one"]["two"]["three"].should == ["four", "five"]
+ end
+
+ it "should parse nested queries" do
+ # [{"b"=>[{"c"=>[{"d"=>["1", {"e"=>"2"}]}]}]}]
+ query = "a%5B%5D%5Bb%5D%5B%5D%5Bc%5D%5B%5D%5Bd%5D%5B%5D=1&a%5B%5D%5Bb%5D%5B%5D%5Bc%5D%5B%5D%5Bd%5D%5B%5D%5Be%5D=2"
+ hsh = WebMock::Util::QueryMapper.query_to_values(query)
+ hsh["a"][0]["b"][0]["c"][0]["d"][0].should == "1"
+ hsh["a"][0]["b"][0]["c"][0]["d"][1]["e"].should == "2"
+ end
+
+end
Something went wrong with that request. Please try again.