class Flon::API

This class is Flon's Rack application.

Constants

DEFAULT_RESPONSE_HEADERS

Public Class Methods

new(api) click to toggle source

Creates a new {API} object with the given API.

@param [Object] api the API to use; its class must respond to router and

the result must be a {Router}.

@return [API] the newly constructed object

# File lib/flon/api.rb, line 82
def initialize(api)
  raise ArgumentError, "given API's class does not respond to #routes" unless valid_api?(api)

  @api    = api
  @router = api.class.router
end

Public Instance Methods

call(env) click to toggle source

Implements the Rack interface.

@param [Hash] env a rack environment @return [Array] a rack-suitable response

# File lib/flon/api.rb, line 93
def call(env)
  request = Rack::Request.new(env)

  method = http_method_to_symbol(request.request_method)
  path   = request.path_info

  response = case (match = @router.match(method, path))
             when :bad_path   then Response.new(404, '"404 Not Found"')
             when :bad_method then Response.new(405, '"405 Method Not Allowed"')
             else dispatch(method, request, match)
             end

  rack_response(method, response)
end

Private Instance Methods

call_action(action, request) click to toggle source
# File lib/flon/api.rb, line 139
def call_action(action, request)
  responsify(call_respect_arity(action.bind(@api), request))
end
call_respect_arity(method, *args) click to toggle source
# File lib/flon/api.rb, line 151
def call_respect_arity(method, *args)
  arity = method.arity
  arity.negative? ? method.call(*args) : method.call(*args[0...arity])
end
dispatch(method, rack_request, match) click to toggle source
# File lib/flon/api.rb, line 110
def dispatch(method, rack_request, match)
  if receives_body?(method)
    body = normalise_body(rack_request)
    return Response.new(415, '"415 Unsupported Media Type"') if body == :fail
  else
    body = nil
  end

  request = Request.new(method, rack_request, match.params, body)
  call_action(match.action, request)
end
http_method_to_symbol(http_method) click to toggle source
# File lib/flon/api.rb, line 168
def http_method_to_symbol(http_method)
  http_method.downcase.to_sym
end
normalise_body(request) click to toggle source
# File lib/flon/api.rb, line 122
def normalise_body(request)
  body = request.body.read
  return body if body.empty?

  return :fail unless request.get_header('Content-Type') == 'application/json'

  begin
    JSON.parse(body)
  rescue JSON::JSONError
    :fail
  end
end
rack_response(method, response) click to toggle source
# File lib/flon/api.rb, line 156
def rack_response(method, response)
  [
    response.status || 200,
    DEFAULT_RESPONSE_HEADERS.merge(stringify_values(response.headers || {})),
    method == :head || !response.body ? [] : [response.body]
  ]
end
receives_body?(http_method) click to toggle source
# File lib/flon/api.rb, line 135
def receives_body?(http_method)
  http_method != :get && http_method != :delete
end
responsify(result) click to toggle source
# File lib/flon/api.rb, line 143
def responsify(result)
  if result.is_a?(Response)
    result.tap { |it| it.body = it.body.to_json }
  else
    Response.new(200, result.to_json)
  end
end
stringify_values(hash) click to toggle source
# File lib/flon/api.rb, line 164
def stringify_values(hash)
  hash.transform_values(&:to_s)
end
valid_api?(api) click to toggle source
# File lib/flon/api.rb, line 172
def valid_api?(api)
  api.class.respond_to?(:router) && api.class.router.is_a?(Flon::Router)
end