mceachen/geonames_api

View on GitHub
lib/geonames_api/base.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'uri'

module GeoNamesAPI
  class Base < Entity

    def self.find(*names_or_params)
      result = where(name_params(names_or_params))
      if result
        endpoint_returns_list? ? result.first : result
      end
    end

    def self.all(*names_or_params)
      result = where(name_params(names_or_params))
      if result
        endpoint_returns_list? ? result : [result]
      end
    end

    def self.where(params={})
      retries_remaining = GeoNamesAPI.retries
      uri = uri(params)
      begin
        response = make_request(uri)
        unless response.empty?
          parse_response(response, params)
        end
      rescue Timeout => e
        if retries_remaining > 0
          retries_remaining -= 1
          sleep_time = rand * GeoNamesAPI.max_sleep_time_between_retries
          GeoNamesAPI.logger.info("GEONAMES RETRIABLE ERROR: #{e}. Retrying in #{sleep_time}s.") if GeoNamesAPI.logger
          sleep sleep_time
          retry
        else
          raise e
        end
      end
    end

    def self.make_request(uri)
      JSON.load(open(uri, "User-Agent" => "#{GeoNamesAPI.name}/#{GeoNamesAPI::VERSION}").read)
    end

    private_class_method :make_request

    def self.parse_response(response, request_params)
      GeoNamesAPI.logger.info "GEONAMES RESPONSE (#{Time.now}): #{response}" if GeoNamesAPI.logger
      if (status = response['status'])
        raise Error.from_status(status)
      end
      new(response, request_params)
    end

    private_class_method :parse_response

    def self.uri(params={})
      endpoint = URI(GeoNamesAPI.url)
      endpoint.path = "/%s" % self::METHOD # URI.path requires a leading /
      endpoint.query = URI.encode_www_form(GeoNamesAPI.params.merge(params))
      GeoNamesAPI.logger.info "GEONAMES REQUEST (#{Time.now}): #{endpoint}" if GeoNamesAPI.logger
      endpoint
    end

    private_class_method :uri

    def self.name_params(names)
      return names.first if names.first.is_a? Hash
      params, n = {}, 0
      if names.any?
        [self::FIND_PARAMS].flatten.each { |i| params[i] = names[n]; n+= 1 }
      end
      params.delete_if { |k, v| v.blank? }
    end

    private_class_method :name_params

  end
end