twitter-status-bot/.gems/gems/faraday-0.9.0/lib/faraday/utils.rb

298 lines
7.0 KiB
Ruby
Raw Normal View History

2014-09-03 08:49:59 +00:00
require 'thread'
Faraday.require_libs 'parameters'
module Faraday
module Utils
extend self
# Adapted from Rack::Utils::HeaderHash
class Headers < ::Hash
def self.from(value)
new(value)
end
def initialize(hash = nil)
super()
@names = {}
self.update(hash || {})
end
# need to synchronize concurrent writes to the shared KeyMap
keymap_mutex = Mutex.new
# symbol -> string mapper + cache
KeyMap = Hash.new do |map, key|
value = if key.respond_to?(:to_str)
key
else
key.to_s.split('_'). # :user_agent => %w(user agent)
each { |w| w.capitalize! }. # => %w(User Agent)
join('-') # => "User-Agent"
end
keymap_mutex.synchronize { map[key] = value }
end
KeyMap[:etag] = "ETag"
def [](k)
k = KeyMap[k]
super(k) || super(@names[k.downcase])
end
def []=(k, v)
k = KeyMap[k]
k = (@names[k.downcase] ||= k)
# join multiple values with a comma
v = v.to_ary.join(', ') if v.respond_to? :to_ary
super(k, v)
end
def fetch(k, *args, &block)
k = KeyMap[k]
key = @names.fetch(k.downcase, k)
super(key, *args, &block)
end
def delete(k)
k = KeyMap[k]
if k = @names[k.downcase]
@names.delete k.downcase
super(k)
end
end
def include?(k)
@names.include? k.downcase
end
alias_method :has_key?, :include?
alias_method :member?, :include?
alias_method :key?, :include?
def merge!(other)
other.each { |k, v| self[k] = v }
self
end
alias_method :update, :merge!
def merge(other)
hash = dup
hash.merge! other
end
def replace(other)
clear
self.update other
self
end
def to_hash() ::Hash.new.update(self) end
def parse(header_string)
return unless header_string && !header_string.empty?
header_string.split(/\r\n/).
tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
map { |h| h.split(/:\s+/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
each { |key, value|
# join multiple values with a comma
if self[key]
self[key] << ', ' << value
else
self[key] = value
end
}
end
end
# hash with stringified keys
class ParamsHash < Hash
def [](key)
super(convert_key(key))
end
def []=(key, value)
super(convert_key(key), value)
end
def delete(key)
super(convert_key(key))
end
def include?(key)
super(convert_key(key))
end
alias_method :has_key?, :include?
alias_method :member?, :include?
alias_method :key?, :include?
def update(params)
params.each do |key, value|
self[key] = value
end
self
end
alias_method :merge!, :update
def merge(params)
dup.update(params)
end
def replace(other)
clear
update(other)
end
def merge_query(query, encoder = nil)
if query && !query.empty?
update((encoder || Utils.default_params_encoder).decode(query))
end
self
end
def to_query(encoder = nil)
(encoder || Utils.default_params_encoder).encode(self)
end
private
def convert_key(key)
key.to_s
end
end
def build_query(params)
FlatParamsEncoder.encode(params)
end
def build_nested_query(params)
NestedParamsEncoder.encode(params)
end
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
def escape(s)
s.to_s.gsub(ESCAPE_RE) {|match|
'%' + match.unpack('H2' * match.bytesize).join('%').upcase
}.tr(' ', '+')
end
def unescape(s) CGI.unescape s.to_s end
DEFAULT_SEP = /[&;] */n
# Adapted from Rack
def parse_query(query)
FlatParamsEncoder.decode(query)
end
def parse_nested_query(query)
NestedParamsEncoder.decode(query)
end
def default_params_encoder
@default_params_encoder ||= NestedParamsEncoder
end
class << self
attr_writer :default_params_encoder
end
# Stolen from Rack
def normalize_params(params, name, v = nil)
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
k = $1 || ''
after = $' || ''
return if k.empty?
if after == ""
if params[k]
params[k] = Array[params[k]] unless params[k].kind_of?(Array)
params[k] << v
else
params[k] = v
end
elsif after == "[]"
params[k] ||= []
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
params[k] << v
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
child_key = $1
params[k] ||= []
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
normalize_params(params[k].last, child_key, v)
else
params[k] << normalize_params({}, child_key, v)
end
else
params[k] ||= {}
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
params[k] = normalize_params(params[k], after, v)
end
return params
end
# Normalize URI() behavior across Ruby versions
#
# url - A String or URI.
#
# Returns a parsed URI.
def URI(url)
if url.respond_to?(:host)
url
elsif url.respond_to?(:to_str)
default_uri_parser.call(url)
else
raise ArgumentError, "bad argument (expected URI object or URI string)"
end
end
def default_uri_parser
@default_uri_parser ||= begin
require 'uri'
Kernel.method(:URI)
end
end
def default_uri_parser=(parser)
@default_uri_parser = if parser.respond_to?(:call) || parser.nil?
parser
else
parser.method(:parse)
end
end
# Receives a String or URI and returns just the path with the query string sorted.
def normalize_path(url)
url = URI(url)
(url.path.start_with?('/') ? url.path : '/' + url.path) +
(url.query ? "?#{sort_query_params(url.query)}" : "")
end
# Recursive hash update
def deep_merge!(target, hash)
hash.each do |key, value|
if Hash === value and Hash === target[key]
target[key] = deep_merge(target[key], value)
else
target[key] = value
end
end
target
end
# Recursive hash merge
def deep_merge(source, hash)
deep_merge!(source.dup, hash)
end
protected
def sort_query_params(query)
query.split('&').sort.join('&')
end
end
end