arturictus/wrappi

View on GitHub
lib/wrappi/endpoint.rb

Summary

Maintainability
B
5 hrs
Test Coverage
require 'wrappi/response'
module Wrappi
  class Endpoint < Miller.base(
    :verb, :client, :path, :default_params,
    :headers, :follow_redirects, :basic_auth,
    :body_type, :retry_options, :cache, #:cache_options,
    :async_callback,
    default_config: {
      verb: :get,
      client: proc { raise 'client not set' }, # TODO: add proper error
      path: proc { raise 'path not defined' }, # TODO: add proper error
      default_params: {},
      headers: proc { client.headers },
      follow_redirects: true,
      body_type: :json,
      cache: proc { options[:cache] },
      # cache_options: {},
      async_callback: proc {},
      basic_auth: proc { client.basic_auth }
    }
  )

    ##############    ClassMethods     ################
    # ============ API class metnods ================
    def self.call(*args)
      new(*args).call
    end

    def self.call!(*args)
      new(*args).call!
    end

    def self.body(*args)
      new(*args).body
    end

    def self.setup(&block)
      instance_exec(&block)
    end

    # =============      Configs     =================
    def self.async_callback(&block)
      @async_callback = block
    end
    
    def self.around_request(&block)
      @around_request = block
    end
    
    def self.retry_if(&block)
      @retry_if = block
    end
    
    def self.cache_options(&block)
      @cache_options = block
    end
    
    # =============      Inheritance     =================
    def self.inherited(subclass)
      super(subclass)
      subclass.instance_variable_set(:@async_callback, @async_callback)
      subclass.instance_variable_set(:@around_request, @around_request)
      subclass.instance_variable_set(:@retry_if, @retry_if)
      subclass.instance_variable_set(:@cache_options, @cache_options)
    end

    # ============== success behaviour   ===================
    # overridable
    def self.success?(request)
      request.code >= 200 && request.code < 300
    end
    #######################################################
    
    attr_reader :input_params, :options
    def initialize(input_params = {}, options = {})
      @input_params = input_params
      @options = options
    end


    def on_success(&block)
      block.call(self) if success?
      self  
    end

    def on_error(&block)
      block.call(self) unless success?
      self
    end

    def call
      return false unless success?
      self
    end
    
    def call!
      raise UnsuccessfulResponse.new(self) unless success?
      self
    end
    
    def response
      @response ||= Executer.call(self)
    end

    def body; response.body end
    def success?; response.success? end
    def status; response.status end
    def error?; !success? end
    def flush; @response = nil end

    def async(async_options = {})
      async_handler.call(self, async_options)
    end

    # overridable
    def consummated_params
      params
    end

    def url
      _url.to_s
    end

    def url_with_params
      return url unless verb == :get
      _url.tap do |u|
        u.query = URI.encode_www_form(consummated_params) if consummated_params.any?
      end.to_s
    end

    def perform_async_callback(async_options = {})
      instance_exec(async_options, &async_callback)
    end

    def cache_key
      # TODO: think headers have to be in the key as well
      @cache_key ||= "[#{verb.to_s.upcase}]##{url}#{params_cache_key}"
    end

    
    def around_request
      self.class.instance_variable_get(:@around_request)
    end

    def retry_if
      self.class.instance_variable_get(:@retry_if)
    end

    def cache_options
      self.class.instance_variable_get(:@cache_options)
    end

    private

    def async_callback
      self.class.instance_variable_get(:@async_callback) || proc {}
    end

    def logger
      client.logger
    end

    # Overridable
    def async_handler
      client.async_handler
    end

    def params_cache_key
      return if params.empty?
      d = Digest::MD5.hexdigest params.to_json
      "?#{d}"
    end

    # URI behaviour
    # example:
    #
    #     URI.join('https://hello.com/foo/bar', '/bin').to_s
    #     => "https://hello.com/bin"
    #
    #     URI.join('https://hello.com/foo/bar', 'bin').to_s
    #     => "https://hello.com/foo/bin"
    #
    #
    #     URI.join('https://hello.com/foo/bar/', '/bin').to_s
    #     => "https://hello.com/bin"
    #
    #     We want this behaviour:
    #     URI.join('https://hello.com/foo/bar/', 'bin').to_s
    #     => "https://hello.com/foo/bar/bin"
    def _url
      URI.join(domain, path_gen.for_uri)
    end

    def domain
      return client.domain if client.domain =~ /\/$/
      "#{client.domain}/"
    end

    def params
      path_gen.params
    end

    def processed_params
      client.params.merge(default_params.merge(input_params))
    end

    def path_gen
      @path_gen ||= PathGen.new(path, processed_params)
    end
  end
end