class TLAW::Endpoint

This class does all the hard work: actually calling some HTTP API and processing responses.

Each real API endpoint is this class descendant, defining its own params and response processors. On each call small instance of this class is created, {#call}-ed and dies as you don't need it anymore.

Typically, you will neither create nor use endpoint descendants or instances directly:

Attributes

url_template[R]

Public Class Methods

construct_template() click to toggle source

@private

# File lib/tlaw/endpoint.rb, line 44
def construct_template
  tpl = if query_string_params.empty?
          base_url
        else
          joiner = base_url.include?('?') ? '&' : '?'
          "#{base_url}{#{joiner}#{query_string_params.join(',')}}"
        end
  Addressable::Template.new(tpl)
end
inspect() click to toggle source

Inspects endpoint class prettily.

Example:

“`ruby some_api.some_namespace.endpoints # => <SomeApi::SomeNamespace::MyEndpoint call-sequence: my_endpoint(param1, param2: nil), docs: .describe> “`

# File lib/tlaw/endpoint.rb, line 31
def inspect
  "#<#{name || '(unnamed endpoint class)'}:" \
  " call-sequence: #{symbol}(#{param_set.to_code}); docs: .describe>"
end
new(**parent_params) click to toggle source

Creates endpoint class (or descendant) instance. Typically, you never use it directly.

Params defined in parent namespace are passed here.

Calls superclass method TLAW::APIPath::new
# File lib/tlaw/endpoint.rb, line 78
def initialize(**parent_params)
  super

  @client = Faraday.new do |faraday|
    faraday.use FaradayMiddleware::FollowRedirects
    faraday.adapter Faraday.default_adapter
  end
  @url_template = self.class.construct_template
end
parse(body) click to toggle source

@private

# File lib/tlaw/endpoint.rb, line 55
def parse(body)
  if xml
    Crack::XML.parse(body)
  else
    JSON.parse(body)
  end.derp { |response| response_processor.process(response) }
end
to_code() click to toggle source

@private

# File lib/tlaw/endpoint.rb, line 37
def to_code
  "def #{to_method_definition}\n" \
  "  child(:#{symbol}, Endpoint).call({#{param_set.to_hash_code}})\n" \
  'end'
end

Private Class Methods

query_string_params() click to toggle source
# File lib/tlaw/endpoint.rb, line 65
def query_string_params
  param_set.all_params.values.map(&:field).map(&:to_s) -
    Addressable::Template.new(base_url).keys
end

Public Instance Methods

call(**params) click to toggle source

Does the real call to the API, with all params passed to this method and to parent namespace.

Typically, you don't use it directly, that's what called when you do `some_namespace.endpoint_name(**params)`.

@return [Hash,Array] Parsed, flattened and post-processed response

body.
# File lib/tlaw/endpoint.rb, line 96
def call(**params)
  url = construct_url(**full_params(params))

  @client.get(url)
         .tap { |response| guard_errors!(response) }
         .derp { |response| self.class.parse(response.body) }
rescue API::Error
  raise # Not catching in the next block
rescue => e
  raise unless url
  raise API::Error, "#{e.class} at #{url}: #{e.message}"
end

Private Instance Methods

construct_url(**params) click to toggle source
# File lib/tlaw/endpoint.rb, line 129
def construct_url(**params)
  url_params = self.class.param_set.process(**params)
  @url_template
    .expand(url_params).normalize.to_s
    .split('?', 2).derp { |url, param| [url.gsub('%2F', '/'), param] }
    .compact.join('?')
end
full_params(**params) click to toggle source
# File lib/tlaw/endpoint.rb, line 113
def full_params(**params)
  @parent_params.merge(params.reject { |_, v| v.nil? })
end
guard_errors!(response) click to toggle source
# File lib/tlaw/endpoint.rb, line 117
def guard_errors!(response)
  # TODO: follow redirects
  return response if (200...400).cover?(response.status)

  body = JSON.parse(response.body) rescue nil
  message = body && (body['message'] || body['error'])

  fail API::Error,
       "HTTP #{response.status} at #{response.env[:url]}" +
       (message ? ': ' + message : '')
end