Skip to content

Commit

Permalink
Fix Urlencoded for nested Hashes (and Arrays)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWayfer committed Jul 10, 2018
1 parent 0280035 commit f5386d8
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 4 deletions.
36 changes: 34 additions & 2 deletions lib/http/form_data/urlencoded.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,45 @@ def encoder=(implementation)
end

# Returns form data encoder implementation.
# Default: `URI.encode_www_form`.
# Default: custom realization.
#
# @see .encoder=
# @return [#call]
def encoder
@encoder ||= ::URI.method(:encode_www_form)
@encoder ||= DefaultEncoder.method(:encode)
end

# Default encoder
module DefaultEncoder
class << self
def encode(value, prefix = nil)
case value
when Hash
encode_hash(value, prefix)
when Array
value.map { |v| encode(v, "#{prefix}[]") }.join("&")
when nil then prefix.to_s
else
raise ArgumentError, "value must be a Hash" if prefix.nil?
"#{prefix}=#{escape(value)}"
end
end

private

def encode_hash(hash, prefix)
hash.map do |k, v|
encode(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
end.reject(&:empty?).join("&")
end

def escape(value)
URI.encode_www_form_component(value)
end
end
end

private_constant :DefaultEncoder
end

# @param [#to_h, Hash] data form data key-value Hash
Expand Down
15 changes: 13 additions & 2 deletions spec/lib/http/form_data/urlencoded_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
it { is_expected.to eq "foo%5Bbar%5D=%D1%82%D0%B5%D1%81%D1%82" }
end

context "with nested hashes" do
let(:data) { { "foo" => { "bar" => "test" } } }
it { is_expected.to eq "foo[bar]=test" }
end

it "rewinds content" do
content = form_data.read
expect(form_data.to_s).to eq content
Expand Down Expand Up @@ -57,8 +62,14 @@
end

describe ".encoder=" do
before { described_class.encoder = ::JSON.method(:dump) }
after { described_class.encoder = ::URI.method(:encode_www_form) }
before do
@original_encoder = described_class.encoder
described_class.encoder = ::JSON.method(:dump)
end

after do
described_class.encoder = @original_encoder
end

it "switches form encoder implementation" do
expect(form_data.to_s).to eq('{"foo[bar]":"test"}')
Expand Down

0 comments on commit f5386d8

Please sign in to comment.