Skip to content

Commit

Permalink
Added support for nested hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
dleeman committed Mar 21, 2016
1 parent 9dd93b4 commit eb224dc
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 21 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ tmtags
## VIM
*.swp

## RubyMine
/.idea/

## PROJECT::GENERAL
coverage
rdoc
Expand Down
10 changes: 3 additions & 7 deletions lib/signature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,11 @@ def string_to_sign
def parameter_string
param_hash = @query_hash.merge(@auth_hash || {})

# Convert keys to lowercase strings
hash = {}; param_hash.each { |k,v| hash[k.to_s.downcase] = v }

# Exclude signature from signature generation!
hash.delete("auth_signature")
param_hash.delete(:auth_signature)
param_hash.delete("auth_signature")

hash.sort.map do |k, v|
QueryEncoder.encode_param_without_escaping(k, v)
end.join('&')
QueryEncoder.encode_params_without_escaping(param_hash)
end

def validate_version!
Expand Down
58 changes: 44 additions & 14 deletions lib/signature/query_encoder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,50 @@ module Signature
# Query string encoding extracted with thanks from em-http-request
module QueryEncoder
class << self
# URL encodes query parameters:
# single k=v, or a URL encoded array, if v is an array of values
def encode_param(k, v)
if v.is_a?(Array)
v.map { |e| escape(k) + "[]=" + escape(e) }.join("&")
else
escape(k) + "=" + escape(v)
end
# URL encodes query parameters
def encode_params(hash)
collect_entries_for([], hash) { |v| escape(v) }.join('&')
end

# Like encode_param, but doesn't url escape keys or values
def encode_param_without_escaping(k, v)
if v.is_a?(Array)
v.map { |e| k + "[]=" + e }.join("&")
def encode_params_without_escaping(hash)
collect_entries_for([], hash).join('&')
end

private

def collect_entries_for(prefix, value, entries = [], &block)
case value
when Array
value.each { |v| collect_entries_for(prefix + [nil], v, entries) }
when Hash
value = with_normalized_keys value
value.keys.sort.each do |key|
collect_entries_for(prefix + [key], value[key], entries)
end
else
"#{k}=#{v}"
entries << entry(prefix, value, &block)
end
entries
end

private
def entry(key, value)
query_key = key.inject('') do |memo, part|
if part.nil?
memo << "[]"
else
part = if block_given? then yield part else part end
if memo == ''
memo << part
else
memo << "[#{part}]"
end
end
memo
end
value = if block_given? then yield value else value end
"#{query_key}=#{value}"
end

def escape(s)
if defined?(EscapeUtils)
Expand All @@ -42,6 +66,12 @@ def bytesize(string)
string.size
end
end

def with_normalized_keys(hash)
normalized = {}
hash.each { |key, value| normalized[key.to_s.downcase] = value }
normalized
end
end
end
end
8 changes: 8 additions & 0 deletions spec/signature_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@
@request.send(:string_to_sign).should == "POST\n/some/path\nthings[]=thing1&things[]=thing2"
end

it "should generate correct string when query hash contains nested elements" do
@request.query_hash = {
"things" => [{ "thing_1" => "value1" }, { "thing_2" => "value2" }]
}
@request.send(:string_to_sign).should ==
"POST\n/some/path\nthings[][thing_1]=value1&things[][thing_2]=value2"
end

# This may well change in auth version 2
it "should not escape keys or values in the query string" do
@request.query_hash = {
Expand Down

0 comments on commit eb224dc

Please sign in to comment.