lib/wordnik/request.rb in wordnik-4.07 vs lib/wordnik/request.rb in wordnik-4.08
- old
+ new
@@ -14,11 +14,11 @@
validates_presence_of :format, :http_method
# All requests must have an HTTP method and a path
# Optionals parameters are :params, :headers, :body, :format, :host
- #
+ #
def initialize(http_method, path, attributes={})
attributes[:format] ||= Wordnik.configuration.response_format
attributes[:params] ||= {}
# Set default headers
@@ -30,48 +30,48 @@
# api_key from headers hash trumps the default, even if its value is blank
if attributes[:headers].present? && attributes[:headers].has_key?(:api_key)
default_headers.delete(:api_key)
end
-
+
# api_key from params hash trumps all others (headers and default_headers)
if attributes[:params].present? && attributes[:params].has_key?(:api_key)
default_headers.delete(:api_key)
attributes[:headers].delete(:api_key) if attributes[:headers].present?
end
-
+
# Merge argument headers into defaults
attributes[:headers] = default_headers.merge(attributes[:headers] || {})
-
+
# Stick in the auth token if there is one
if Wordnik.authenticated?
attributes[:headers].merge!({:auth_token => Wordnik.configuration.auth_token})
end
-
+
self.http_method = http_method.to_sym
self.path = path
attributes.each do |name, value|
send("#{name.to_s.underscore.to_sym}=", value)
end
end
# Construct a base URL
#
- def url(options = {})
+ def url(options = {})
u = Addressable::URI.new(
:scheme => Wordnik.configuration.scheme,
:host => Wordnik.configuration.host,
:path => self.interpreted_path,
:query => self.query_string.sub(/\?/, '')
).to_s
-
+
# Drop trailing question mark, if present
u.sub! /\?$/, ''
-
+
# Obfuscate API key?
u.sub! /api\_key=\w+/, 'api_key=YOUR_API_KEY' if options[:obfuscated]
-
+
u
end
# Iterate over the params hash, injecting any path values into the path string
# e.g. /word.{format}/{word}/entries => /word.json/cat/entries
@@ -89,33 +89,33 @@
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s }
p = p.sub(/^(\/?\w+)/, "\\1.#{format}")
end
p = p.sub("{format}", self.format.to_s)
-
+
URI.encode [Wordnik.configuration.base_path, p].join("/").gsub(/\/+/, '/')
end
-
+
# Massage the request body into a state of readiness
# If body is a hash, camelize all keys then convert to a json string
#
- def body=(value)
+ def body=(value)
if value.is_a?(Hash)
value = value.inject({}) do |memo, (k,v)|
memo[k.to_s.camelize(:lower).to_sym] = v
memo
end
end
@body = value
end
-
+
# If body is an object, JSONify it before making the actual request.
- #
+ #
def outgoing_body
body.is_a?(String) ? body : body.to_json
end
-
+
# Construct a query string from the query-string-type params
def query_string
# Iterate over all params,
# .. removing the ones that are part of the path itself.
@@ -125,51 +125,72 @@
next if self.path.include? "{#{key}}" # skip path params
next if value.blank? && value.class != FalseClass # skip empties
key = key.to_s.camelize(:lower).to_sym unless key.to_sym == :api_key # api_key is not a camelCased param
query_values[key] = value.to_s
end
-
+
# We don't want to end up with '?' as our query string
# if there aren't really any params
return "" if query_values.blank?
-
+
# Addressable requires query_values to be set after initialization..
qs = Addressable::URI.new
qs.query_values = query_values
qs.to_s
end
-
- def make
- request = Typhoeus::Request.new(self.url,
+
+ def make(attempt = 0)
+ # url is calculated, so we need to compute it once.
+ u = self.url
+ #Wordnik.logger.debug "Making attempt #{attempt}; now fetching #{u}" if attempt > 0
+ request = Typhoeus::Request.new(u,
:headers => self.headers.stringify_keys,
:method => self.http_method.to_sym)
-
- # Make request proxy-aware
+
+ # Make request proxy-aware
if Wordnik.configuration.proxy.present?
request.proxy = Wordnik.configuration.proxy
request.proxy_username = Wordnik.configuration.proxy_username if Wordnik.configuration.proxy_username.present?
request.proxy_password = Wordnik.configuration.proxy_password if Wordnik.configuration.proxy_password.present?
end
-
- Wordnik.logger.debug "\n #{self.http_method.to_s.upcase} #{self.url}\n body: #{self.outgoing_body}\n headers: #{request.headers}\n\n"
-
+
+ Wordnik.logger.debug "\n #{self.http_method.to_s.upcase} #{u}\n body: #{self.outgoing_body}\n headers: #{request.headers}\n\n"
+
request.body = self.outgoing_body unless self.http_method.to_sym == :get
- # Execute the request
+ # Execute the request — blocking call here.
Typhoeus::Hydra.hydra.queue request
- Typhoeus::Hydra.hydra.run
- Response.new(request.response)
+ Typhoeus::Hydra.hydra.run
+
+ # if we are using local load balancing, check for timeouts and connection errors
+ resp = request.response
+
+ if Wordnik.configuration.load_balancer
+ if (resp.timed_out? || resp.code == 0)
+ # Wordnik.logger.debug "informing load balancer about failure"
+ Wordnik.configuration.load_balancer.inform_failure
+ if (attempt <= 3)
+ # Wordnik.logger.debug "Trying again after failing #{attempt} times..."
+ return make(attempt + 1) if attempt <= 3 # try three times to get a result...
+ end
+ else
+ # Wordnik.logger.debug "informing load balancer about success"
+ Wordnik.configuration.load_balancer.inform_success
+ end
+ end
+
+ Response.new(resp)
end
-
+
def response
self.make
end
-
+
def response_code_pretty
return unless @response.present?
- @response.code.to_s
+ @response.code.to_s
end
-
+
def response_headers_pretty
return unless @response.present?
# JSON.pretty_generate(@response.headers).gsub(/\n/, '<br/>') # <- This was for RestClient
@response.headers.gsub(/\n/, '<br/>') # <- This is for Typhoeus
end