Class: Goliath::API

Inherits:
Object
  • Object
show all
Includes:
Constants, Rack::Validator
Defined in:
lib/goliath/api.rb

Overview

All Goliath APIs subclass Goliath::API. All subclasses must override the #response method.

Examples:

require 'goliath'

class HelloWorld < Goliath::API
  def response(env)
    [200, {}, "hello world"]
  end
end

Direct Known Subclasses

WebSocket

Constant Summary

Constants included from Constants

Constants::ASYNC_BODY, Constants::ASYNC_CALLBACK, Constants::ASYNC_CLOSE, Constants::ASYNC_HEADERS, Constants::CONFIG, Constants::CONNECTION, Constants::CONTENT_LENGTH, Constants::CONTENT_TYPE, Constants::FRAGMENT, Constants::GOLIATH_ENV, Constants::GOLIATH_SIGNATURE, Constants::HTTP_PREFIX, Constants::HTTP_VERSION, Constants::INITIAL_BODY, Constants::LOCALHOST, Constants::OPTIONS, Constants::PATH_INFO, Constants::QUERY_STRING, Constants::RACK_ERRORS, Constants::RACK_EXCEPTION, Constants::RACK_INPUT, Constants::RACK_LOGGER, Constants::RACK_MULTIPROCESS, Constants::RACK_MULTITHREAD, Constants::RACK_RUN_ONCE, Constants::RACK_VERSION, Constants::RACK_VERSION_NUM, Constants::REMOTE_ADDR, Constants::REQUEST_METHOD, Constants::REQUEST_PATH, Constants::REQUEST_URI, Constants::SCRIPT_NAME, Constants::SERVER, Constants::SERVER_NAME, Constants::SERVER_PORT, Constants::SERVER_SOFTWARE, Constants::STATUS, Constants::STREAM_CLOSE, Constants::STREAM_SEND, Constants::STREAM_START, Constants::UPGRADE_DATA

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Rack::Validator

safely, validation_error

Constructor Details

#initialize(opts = {}) ⇒ API

The default constructor does nothing with the options passed, redefine your own to use them.

Parameters:

  • opts (Hash) (defaults to: {})

    options passed to a map call if any



103
104
105
# File 'lib/goliath/api.rb', line 103

def initialize(opts = {})
  @opts = opts
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &blk) ⇒ Object

The API will proxy missing calls to the env object if possible.

The two entries in this example are equivalent as long as you are not in a streaming server.

Examples:

logger.info "Hello"
env.logger.info "Hello"


138
139
140
141
142
143
144
145
# File 'lib/goliath/api.rb', line 138

def method_missing(name, *args, &blk)
  name = name.to_s
  if env.respond_to?(name)
    env.send(name, *args, &blk)
  else
    super(name.to_sym, *args, &blk)
  end
end

Class Method Details

.inherited(subclass) ⇒ Object

Catches the userland class which inherits the Goliath API

In case of further subclassing, the very last class encountered is used.



28
29
30
# File 'lib/goliath/api.rb', line 28

def inherited(subclass)
  Goliath::Application.app_class = subclass.name if defined?(Goliath::Application)
end

.middlewaresArray

Retrieves the middlewares defined by this API server

Returns:

  • (Array)

    array contains [middleware class, args, block]



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/goliath/api.rb', line 35

def middlewares
  @middlewares ||= []

  unless @loaded_default_middlewares
    @middlewares.unshift([::Goliath::Rack::DefaultResponseFormat, nil, nil])
    @middlewares.unshift([::AsyncRack::ContentLength, nil, nil])

    if Goliath.env?(:development) && !@middlewares.detect {|mw| mw.first == ::Rack::Reloader}
      @middlewares.unshift([::Rack::Reloader, 0, nil])
    end

    @loaded_default_middlewares = true
  end

  @middlewares
end

.plugin(name, *args) ⇒ Object

Specify a plugin to be used by the API

Examples:

plugin Goliath::Plugin::Latency

Parameters:

  • name (Class)

    The plugin class to use

  • args

    The arguments to the plugin



92
93
94
# File 'lib/goliath/api.rb', line 92

def plugin(name, *args)
  plugins.push([name, args])
end

.pluginsArray

Returns the plugins configured for this API

Returns:

  • (Array)

    array contains [plugin name, args]



81
82
83
# File 'lib/goliath/api.rb', line 81

def plugins
  @plugins ||= []
end

.use(name, *args, &block) ⇒ Object

Specify a middleware to be used by the API

Examples:

use Goliath::Rack::Validation::RequiredParam, {:key => 'echo'}

use ::Rack::Rewrite do
  rewrite %r{^(.*?)\??gziped=(.*)$}, lambda { |match, env| "#{match[1]}?echo=#{match[2]}" }
end

Parameters:

  • name (Class)

    The middleware class to use

  • args

    Any arguments to pass to the middeware

  • block

    A block to pass to the middleware



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/goliath/api.rb', line 64

def use(name, *args, &block)
  @middlewares ||= []

  if name == Goliath::Rack::Render
    [args].flatten.each do |type|
      type = Goliath::Rack::Formatters.const_get type.upcase
      @middlewares << [type, nil, nil]
    end
  end

  @middlewares << [name, args, block]
  @middlewares = @middlewares.uniq
end

Instance Method Details

#call(env) ⇒ Goliath::Connection::AsyncResponse

#call is executed automatically by the middleware chain and will setup the environment for the #response method to execute. This includes setting up a new Fiber, handing any exceptions thrown from the API and executing the appropriate callback method for the API.

Parameters:

Returns:

  • (Goliath::Connection::AsyncResponse)

    An async response.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/goliath/api.rb', line 160

def call(env)
  begin
    Thread.current[GOLIATH_ENV] = env
    status, headers, body = response(env)

    if status
      if body == Goliath::Response::STREAMING
        env[STREAM_START].call(status, headers)
      else
        env[ASYNC_CALLBACK].call([status, headers, body])
      end
    end

  rescue Goliath::Validation::Error => e
    env[RACK_EXCEPTION] = e
    env[ASYNC_CALLBACK].call(validation_error(e.status_code, e.message))

  rescue Exception => e
    env.logger.error(e.message)
    env.logger.error(e.backtrace.join("\n"))
    env[RACK_EXCEPTION] = e

    message = Goliath.env?(:production) ? 'An error happened' : e.message
    env[ASYNC_CALLBACK].call(validation_error(500, message))
  end

  Goliath::Connection::AsyncResponse
end

#chunked_streaming_response(status_code = 200, headers = {}) ⇒ Object

Helper method for chunked transfer streaming response apis

Chunked transfer streaming is transparent to all clients (it’s just as good as a normal response), but allows an aware client to begin consuming the stream even as it’s produced.

If you are using chunked streaming, you must use env.chunked_stream_send and env.chunked_stream_close

Parameters:

  • status_code (Integer) (defaults to: 200)

    The status code to return.

  • headers (Hash) (defaults to: {})

    Headers to return. The Transfer-Encoding=chunked header is set for you.



226
227
228
# File 'lib/goliath/api.rb', line 226

def chunked_streaming_response(status_code = 200, headers = {})
  streaming_response(status_code, headers.merge(Goliath::Response::CHUNKED_STREAM_HEADERS))
end

#envGoliath::Env

Note:

This will not work in a streaming server. You must pass around the env object.

Accessor for the current env object

Returns:

  • (Goliath::Env)

    The current environment data for the request



125
126
127
# File 'lib/goliath/api.rb', line 125

def env
  Thread.current[GOLIATH_ENV]
end

#options_parser(opts, options) ⇒ Object

Default stub method to add options into the option parser.

Examples:

def options_parser(opts, options)
  options[:test] = 0
  opts.on('-t', '--test NUM', "The test number") { |val| options[:test] = val.to_i }
end

Parameters:

  • opts (OptionParser)

    The options parser

  • options (Hash)

    The hash to insert the parsed options into



117
118
# File 'lib/goliath/api.rb', line 117

def options_parser(opts, options)
end

#respond_to_missing?(name) ⇒ Boolean

Returns True if the API’s method_missing responds to the method.

Parameters:

  • name (Symbol)

    The method to check if we respond to it.

Returns:

  • (Boolean)

    True if the API’s method_missing responds to the method



149
150
151
# File 'lib/goliath/api.rb', line 149

def respond_to_missing?(name, *)
  env.respond_to? name
end

#response(env) ⇒ Array

Response is the main implementation method for Goliath APIs. All APIs should override this method in order to do any actual work.

The response method will be executed in a new Fiber and wrapped in a begin rescue block to handle an thrown API errors.

Parameters:

Returns:

  • (Array)

    Array contains [Status code, Headers Hash, Body]

Raises:

  • (Goliath::Validation::InternalServerError)


197
198
199
200
# File 'lib/goliath/api.rb', line 197

def response(env)
  env.logger.error('You need to implement response')
  raise Goliath::Validation::InternalServerError.new('No response implemented')
end

#streaming_response(status_code = 200, headers = {}) ⇒ Object

Helper method for streaming response apis.

Parameters:

  • status_code (Integer) (defaults to: 200)

    The status code to return (200 by default).

  • headers (Hash) (defaults to: {})

    Headers to return.



206
207
208
# File 'lib/goliath/api.rb', line 206

def streaming_response(status_code = 200, headers = {})
  [status_code, headers, Goliath::Response::STREAMING]
end