class Toast::RackApp

Public Instance Methods

call(env) click to toggle source
# File lib/toast/rack_app.rb, line 16
def call(env)

  request = ActionDispatch::Request.new(env)
  Toast.request = request

  Toast.logger.info "processing: <#{request.method} #{URI.decode(request.fullpath)}>"

  # Authentication: respond with 401 on exception or falsy return value:
  begin
    unless (auth = Toast::ConfigDSL::Settings::AuthenticateContext.new.
                    instance_exec(request, &Toast.settings.authenticate))
       return response :unauthorized, msg: "authentication failed"
    end
  rescue Toast::Errors::CustomAuthFailure => caf
    return response(caf.response_data[:status] || :unauthorized,
             msg: caf.response_data[:body],
             headers: caf.response_data[:headers])
  rescue => error
    return response :unauthorized, msg: "authentication failed: `#{error.message}'"
  end

  path       = request.path_parameters[:toast_path].split('/')

  # look up requested model
  model_class = resolve_model(path, Toast.path_tree)

  if model_class.nil?
    return response :not_found,
                    msg: "no API configuration found for endpoint /#{path.join('/')}"
  end

  # select base configuration
  base_config = get_config(model_class)

  # remove path prefix
  path = path[(base_config.prefix_path.length)..-1]

  toast_request =
    case path.length
    when 1 # root collection: /apples

      if base_config.collections[:all].nil?
        return response :not_found, msg: "collection `/#{path[0]}' not configured"
      else
        # root collection
        Toast::CollectionRequest.new( base_config.collections[:all],
                                      base_config,
                                      auth,
                                      request)
      end
    when 2 # canonical, single or collection: /apples/10 , /apples/first, /apples/red_ones
      if path.second =~ /\A\d+\z/
        Toast::CanonicalRequest.new( path.second.to_i,
                                     base_config,
                                     auth,
                                     request )
      else
        if col_config = base_config.collections[path.second.to_sym]
          Toast::CollectionRequest.new(col_config,
                                       base_config,
                                       auth,
                                       request)

        elsif sin_config = base_config.singles[path.second.to_sym]
          Toast::SingleRequest.new(sin_config,
                                   base_config,
                                   auth,
                                   request)
        else
          return response :not_found,
                          msg: "collection or single `#{path.second}' not configured in: #{base_config.source_location}"
        end
      end

    when 3 # association: /apples/10/tree, /tree/5/apples

      if assoc_config = base_config.associations[path.third.to_sym]
        if assoc_config.singular

          Toast::SingularAssocRequest.new( path.second.to_i,
                                           assoc_config,
                                           base_config,
                                           auth,
                                           request )

        else
          # process_plural_association assoc_config, path.second.to_i
          Toast::PluralAssocRequest.new( path.second.to_i,
                                         assoc_config,
                                         base_config,
                                         auth,
                                         request )
        end
      else
        return response :not_found,
                        msg: "association `#{model_class.name}##{path.third}' not configured"
      end

    else
      return response :not_found,
                      msg: "#{request.url} not found (invalid path)"
    end

  toast_request.respond

end

Private Instance Methods

resolve_model(path, path_tree) click to toggle source

gets the model class from the it's similar to Hash#dig but stops at a non string

# File lib/toast/rack_app.rb, line 126
def resolve_model path, path_tree
  if path_tree[ path.first ].is_a?(Hash)
    # dig deeper
    resolve_model(path[1..-1], path_tree[ path.first ])
  else
    path_tree[ path.first ]
  end
end