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_URL_SCHEME, 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
188
189
190
# 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, e.headers))

  rescue Exception => e
    logthis = "#{e.backtrace[0]}: #{e.message} (#{e.class})\n"
    e.backtrace[1..-1].each do |bt|
      logthis += "    from #{bt}\n"
    end
    env.logger.error(logthis)
    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.



229
230
231
# File 'lib/goliath/api.rb', line 229

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)


200
201
202
203
# File 'lib/goliath/api.rb', line 200

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.



209
210
211
# File 'lib/goliath/api.rb', line 209

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