class Geocoder::Lookup::Base

def base_query_url(query)


produces the full query URL. Should include the "?" a the end.
String which, when concatenated with url_query_string(query)
#
def base_query_url(query)
  fail
end

def cache


The working Cache object.
#
def cache
  if @cache.nil? and store = configuration.cache
    cache_options = configuration.cache_options
    @cache = Cache.new(store, cache_options)
  end
  @cache
end

def cache_key(query)


something else (like the URL before OAuth encoding).
timestamp, etc varies from one request to another, we need to use
request URL, but in cases where OAuth is used and the nonce,
Key to use for caching a geocoding result. Usually this will be the
#
def cache_key(query)
  base_query_url(query) + hash_to_query(cache_key_params(query))
end

def cache_key_params(query)

def cache_key_params(query)
  # omit api_key and token because they may vary among requests
  query_url_params(query).reject do |key,value|
    key.to_s.match(/(key|token)/)
  end
end

def check_api_key_configuration!(query)

def check_api_key_configuration!(query)
  key_parts = query.lookup.required_api_key_parts
  if key_parts.size > Array(configuration.api_key).size
    parts_string = key_parts.size == 1 ? key_parts.first : key_parts
    raise Geocoder::ConfigurationError,
      "The #{query.lookup.name} API requires a key to be configured: " +
      parts_string.inspect
  end
end

def check_response_for_errors!(response)

def check_response_for_errors!(response)
  if response.code.to_i == 400
    raise_error(Geocoder::InvalidRequest) ||
      Geocoder.log(:warn, "Geocoding API error: 400 Bad Request")
  elsif response.code.to_i == 401
    raise_error(Geocoder::RequestDenied) ||
      Geocoder.log(:warn, "Geocoding API error: 401 Unauthorized")
  elsif response.code.to_i == 402
    raise_error(Geocoder::OverQueryLimitError) ||
      Geocoder.log(:warn, "Geocoding API error: 402 Payment Required")
  elsif response.code.to_i == 429
    raise_error(Geocoder::OverQueryLimitError) ||
      Geocoder.log(:warn, "Geocoding API error: 429 Too Many Requests")
  elsif response.code.to_i == 503
    raise_error(Geocoder::ServiceUnavailable) ||
      Geocoder.log(:warn, "Geocoding API error: 503 Service Unavailable")
  end
end

def configuration


An object with configuration data for this particular lookup.
#
def configuration
  Geocoder.config_for_lookup(handle)
end

def configure_ssl!(client); end

def configure_ssl!(client); end

def fetch_data(query)


Returns a parsed search result (Ruby hash).
#
def fetch_data(query)
  parse_raw_data fetch_raw_data(query)
rescue SocketError => err
  raise_error(err) or Geocoder.log(:warn, "Geocoding API connection cannot be established.")
rescue Errno::ECONNREFUSED => err
  raise_error(err) or Geocoder.log(:warn, "Geocoding API connection refused.")
rescue Geocoder::NetworkError => err
  raise_error(err) or Geocoder.log(:warn, "Geocoding API connection is either unreacheable or reset by the peer")
rescue Timeout::Error => err
  raise_error(err) or Geocoder.log(:warn, "Geocoding API not responding fast enough " +
    "(use Geocoder.configure(:timeout => ...) to set limit).")
end

def fetch_raw_data(query)


The result might or might not be cached.
Fetch a raw geocoding result (JSON string).
#
def fetch_raw_data(query)
  key = cache_key(query)
  if cache and body = cache[key]
    @cache_hit = true
  else
    check_api_key_configuration!(query)
    response = make_api_request(query)
    check_response_for_errors!(response)
    body = response.body
    # apply the charset from the Content-Type header, if possible
    ct = response['content-type']
    if ct && ct['charset']
      charset = ct.split(';').select do |s|
        s['charset']
      end.first.to_s.split('=')
      if charset.length == 2
        body.force_encoding(charset.last) rescue ArgumentError
      end
    end
    if cache and valid_response?(response)
      cache[key] = body
    end
    @cache_hit = false
  end
  body
end

def handle


Symbol which is used in configuration to refer to this Lookup.
#
def handle
  str = self.class.to_s
  str[str.rindex(':')+1..-1].gsub(/([a-z\d]+)([A-Z])/,'\1_\2').downcase.to_sym
end

def hash_to_query(hash)


Removes any keys with nil value.
Simulate ActiveSupport's Object#to_query.
#
def hash_to_query(hash)
  require 'cgi' unless defined?(CGI) && defined?(CGI.escape)
  hash.collect{ |p|
    p[1].nil? ? nil : p.map{ |i| CGI.escape i.to_s } * '='
  }.compact.sort * '&'
end

def http_client


Object used to make HTTP requests.
#
def http_client
  proxy_name = "#{protocol}_proxy"
  if proxy = configuration.send(proxy_name)
    proxy_url = !!(proxy =~ /^#{protocol}/) ? proxy : protocol + '://' + proxy
    begin
      uri = URI.parse(proxy_url)
    rescue URI::InvalidURIError
      raise ConfigurationError,
        "Error parsing #{protocol.upcase} proxy URL: '#{proxy_url}'"
    end
    Net::HTTP::Proxy(uri.host, uri.port, uri.user, uri.password)
  else
    Net::HTTP
  end
end

def initialize

def initialize
  @cache = nil
end

def make_api_request(query)


return the response object.
Make an HTTP(S) request to a geocoding API and
#
def make_api_request(query)
  uri = URI.parse(query_url(query))
  Geocoder.log(:debug, "Geocoder: HTTP request being made for #{uri.to_s}")
  http_client.start(uri.host, uri.port, use_ssl: use_ssl?, open_timeout: configuration.timeout, read_timeout: configuration.timeout) do |client|
    configure_ssl!(client) if use_ssl?
    req = Net::HTTP::Get.new(uri.request_uri, configuration.http_headers)
    if configuration.basic_auth[:user] and configuration.basic_auth[:password]
      req.basic_auth(
        configuration.basic_auth[:user],
        configuration.basic_auth[:password]
      )
    end
    client.request(req)
  end
rescue Timeout::Error
  raise Geocoder::LookupTimeout
rescue Errno::EHOSTUNREACH, Errno::ETIMEDOUT, Errno::ENETUNREACH, Errno::ECONNRESET
  raise Geocoder::NetworkError
end

def map_link_url(coordinates)


also provide maps.
Not necessarily implemented by all subclasses as only some lookups

Return the URL for a map of the given coordinates.
#
def map_link_url(coordinates)
  nil
end

def name


Human-readable name of the geocoding API.
#
def name
  fail
end

def parse_json(data)

def parse_json(data)
  if defined?(ActiveSupport::JSON)
    ActiveSupport::JSON.decode(data)
  else
    JSON.parse(data)
  end
rescue
  unless raise_error(ResponseParseError.new(data))
    Geocoder.log(:warn, "Geocoding API's response was not valid JSON")
    Geocoder.log(:debug, "Raw response: #{data}")
  end
end

def parse_raw_data(raw_data)


Parses a raw search result (returns hash or array).
#
def parse_raw_data(raw_data)
  parse_json(raw_data)
end

def protocol


Set in configuration but not available for every service.
Protocol to use for communication with geocoding services.
#
def protocol
  "http" + (use_ssl? ? "s" : "")
end

def query_url(query)


subclss this method, they must also subclass #cache_key.
base_query_url and url_query_string. If absolutely necessary to
Subclasses should not modify this method. Instead they should define

URL to use for querying the geocoding engine.
#
def query_url(query)
  base_query_url(query) + url_query_string(query)
end

def query_url_params(query)

def query_url_params(query)
  query.options[:params] || {}
end

def raise_error(error, message = nil)


Return false if exception not raised.
Raise exception if configuration specifies it should be raised.
#
def raise_error(error, message = nil)
  exceptions = configuration.always_raise
  if exceptions == :all or exceptions.include?( error.is_a?(Class) ? error : error.class )
    raise error, message
  else
    false
  end
end

def required_api_key_parts


Empty array if keys are optional or not required.
Array containing string descriptions of keys required by the API.
#
def required_api_key_parts
  []
end

def result_class


Class of the result objects
#
def result_class
  Geocoder::Result.const_get(self.class.to_s.split(":").last)
end

def results(query)


Geocoder::Result object or nil on timeout or other error.
#
def results(query)
  fail
end

def search(query, options = {})


for reverse geocoding. Returns an array of Geocoder::Results.
"205.128.54.202") for geocoding, or coordinates (latitude, longitude)
Takes a search string (eg: "Mississippi Coast Coliseumf, Biloxi, MS",

Returns +nil+ on timeout or error.
Query the geocoding API and return a Geocoder::Result object.
#
def search(query, options = {})
  query = Geocoder::Query.new(query, options) unless query.is_a?(Geocoder::Query)
  results(query).map{ |r|
    result = result_class.new(r)
    result.cache_hit = @cache_hit if cache
    result
  }
end

def supported_protocols


or [:https] if only HTTPS is supported.
Should be set to [:http] if only HTTP is supported
Array containing the protocols supported by the api.
#
def supported_protocols
  [:http, :https]
end

def url_query_string(query)

def url_query_string(query)
  hash_to_query(
    query_url_params(query).reject{ |key,value| value.nil? }
  )
end

def use_ssl?

def use_ssl?
  if supported_protocols == [:https]
    true
  elsif supported_protocols == [:http]
    false
  else
    configuration.use_https
  end
end

def valid_response?(response)

def valid_response?(response)
  (200..399).include?(response.code.to_i)
end