Class: Rack::API::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/api/runner.rb

Constant Summary collapse

HTTP_METHODS =
%w[get post put delete head patch options]
DELEGATE_METHODS =
%w[
  version use prefix basic_auth rescue_from
  helper respond_to default_url_options
  get post put delete head patch options
]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRunner

Returns a new instance of Runner.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/rack/api/runner.rb', line 14

def initialize
  @settings = {
    :middlewares => [],
    :helpers => [],
    :rescuers => [],
    :global => {
      :prefix => "/",
      :formats => %w[json jsonp],
      :middlewares => [],
      :helpers => [],
      :rescuers => []
    }
  }
end

Instance Attribute Details

#settingsObject

Returns the value of attribute settings.



12
13
14
# File 'lib/rack/api/runner.rb', line 12

def settings
  @settings
end

Instance Method Details

#basic_auth(realm = "Restricted Area", &block) ⇒ Object

Require basic authentication before processing other requests. The authentication reques must be defined before routes.

Rack::API.app do
  basic_auth "Protected Area" do |user, pass|
    User.authenticate(user, pass)
  end
end

You can disable basic authentication by providing :none as realm.

Rack::API.app do
  basic_auth "Protected Area" do |user, pass|
    User.authenticate(user, pass)
  end

  version :v1 do
    # this version is protected by the
    # global basic auth block above.
  end

  version :v2 do
    basic_auth :none
    # this version is now public
  end

  version :v3 do
    basic_auth "Admin" do |user, pass|
      user == "admin" && pass == "test"
    end
  end
end


167
168
169
# File 'lib/rack/api/runner.rb', line 167

def basic_auth(realm = "Restricted Area", &block)
  set :auth, (realm == :none ? :none : [realm, block])
end

#call(env) ⇒ Object

Run all routes.



129
130
131
# File 'lib/rack/api/runner.rb', line 129

def call(env) # :nodoc:
  route_set.freeze.call(env)
end

#default_url_options(options) ⇒ Object

Define the server endpoint. Will be used if you call the method Rack::API::Controller#url_for.

The following options are supported:

  • :host – Specifies the host the link should be targeted at.

  • :protocol – The protocol to connect to. Defaults to ‘http’.

  • :port – Optionally specify the port to connect to.

  • :base_path – Optionally specify a base path.

Some usage examples:

default_url_options :host => "myhost.com"
#=> http://myhost.com

default_url_options :host => "myhost.com", :protocol => "https"
#=> https://myhost.com

default_url_options :host => "myhost.com", :port => 3000
#=> http://myhost.com:3000

default_url_options :host => "myhost.com", :base_path => "my/custom/path"
#=> http://myhost.com/my/custom/path


114
115
116
# File 'lib/rack/api/runner.rb', line 114

def default_url_options(options)
  set :url_options, options
end

#helper(mod = nil, &block) ⇒ Object

Add a helper to application.

helper MyHelpers
helper {  }

Raises:

  • (ArgumentError)


84
85
86
87
88
# File 'lib/rack/api/runner.rb', line 84

def helper(mod = nil, &block)
  mod = Module.new(&block) if block_given?
  raise ArgumentError, "you need to pass a module or block" unless mod
  set :helpers, mod, :append
end

#option(name, mode = :any) ⇒ Object

Try to fetch local configuration, defaulting to the global setting. Return nil when no configuration is defined.



47
48
49
50
51
52
53
# File 'lib/rack/api/runner.rb', line 47

def option(name, mode = :any)
  if mode == :merge && (settings[name].kind_of?(Array) || settings[:global][name].kind_of?(Array))
    settings[:global].fetch(name, []) | settings.fetch(name, [])
  else
    settings.fetch(name, settings[:global][name])
  end
end

#prefix(name) ⇒ Object

Set an additional url prefix.



75
76
77
# File 'lib/rack/api/runner.rb', line 75

def prefix(name)
  set :prefix, name
end

#rescue_from(exception, options = {}, &block) ⇒ Object

Rescue from the specified exception.

rescue_from ActiveRecord::RecordNotFound, :status => 404
rescue_from Exception, :status => 500
rescue_from Exception do |error|
  $logger.error error.inspect
  [500, {"Content-Type" => "text/plain"}, []]
end


284
285
286
# File 'lib/rack/api/runner.rb', line 284

def rescue_from(exception, options = {}, &block)
  set :rescuers, {:class_name => exception.name, :options => options, :block => block}, :append
end

#respond_to(*formats) ⇒ Object

Define the formats that this app implements. Respond only to :json by default.

When setting a format you have some alternatives on how this object will be formated.

First, Rack::API will look for a formatter defined on Rack::API::Formatter namespace. If there’s no formatter, it will look for a method to_<format>. It will raise an exception if no formatter method has been defined.

See Rack::API::Formatter::Jsonp for an example on how to create additional formatters.

Local formats will override the global configuration on that context.

Rack::API.app do
  respond_to :json, :xml, :jsonp

  version :v1 do
    respond_to :json
  end
end

The code above will accept only :json as format on version :v1.

Also, the first value provided to this method will be used as default format, which means that requests that don’t provide the :format param, will use this value.

respond_to :fffuuu, :json
#=> the default format is fffuuu


203
204
205
# File 'lib/rack/api/runner.rb', line 203

def respond_to(*formats)
  set :formats, formats
end

#route(method, path, requirements = {}, &block) ⇒ Object

Define a new routing that will be triggered when both request method and path are recognized.

You’re better off using all verb shortcut methods. Implemented verbs are get, post, put, delete, head and patch.

class MyAPI < Rack::API
  version "v1" do
    get "users(.:format)" do
      # do something
    end
  end
end

You don’t have to use version or prefix.

class MyAPI < Rack::API
  get "users(.:format)" do
    # do something
  end
end

Alternatively, you can define your routes pretty much like Rails.

class MyAPI < Rack::API
  get "users(.:format)", :to => "users#index"
end

The route above will require a class Users with an instance method index.

class Users < Rack::API::Controller
  def index
    # do something
  end
end

Note that your controller must inherit from Rack::API::Controller. Otherwise, your world will explode.



252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/rack/api/runner.rb', line 252

def route(method, path, requirements = {}, &block)
  separator = requirements.delete(:separator) { %w[ / . ? ] }

  path = Rack::Mount::Strexp.compile mount_path(path), requirements, separator
  controller_class = Controller

  if requirements[:to]
    controller_name, action_name = requirements.delete(:to).split("#")
    controller_class = controller_name.camelize.constantize
    block = proc { __send__(action_name) }
  end

  route_set.add_route(build_app(controller_class, block), :path_info => path, :request_method => method)
end

#route_setObject

Hold all routes.



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

def route_set # :nodoc:
  @route_set ||= Rack::Mount::RouteSet.new
end

#set(name, value, mode = :override) ⇒ Object

Set configuration based on scope. When defining values outside version block, will set configuration using settings[:global] namespace.

Use the Rack::API::Runner#option method to access a given setting.



34
35
36
37
38
39
40
41
42
# File 'lib/rack/api/runner.rb', line 34

def set(name, value, mode = :override)
  target = settings[:version] ? settings : settings[:global]

  if mode == :override
    target[name] = value
  else
    target[name] << value
  end
end

#use(middleware, *args) ⇒ Object

Add a middleware to the execution stack.

Global middlewares will be merged with local middlewares.

Rack::API.app do
  use ResponseTime

  version :v1 do
    use Gzip
  end
end

The middleware stack will be something like [ResponseTime, Gzip].



69
70
71
# File 'lib/rack/api/runner.rb', line 69

def use(middleware, *args)
  set :middlewares, [middleware, *args], :append
end

#version(name, &block) ⇒ Object

Create a new API version.

Raises:

  • (ArgumentError)


120
121
122
123
124
125
# File 'lib/rack/api/runner.rb', line 120

def version(name, &block)
  raise ArgumentError, "you need to pass a block" unless block_given?
  settings[:version] = name.to_s
  instance_eval(&block)
  settings.delete(:version)
end