class Rutter::Builder

The builder map URL's to endpoints.

@!attribute [r] flat_map

@return [Array]
  Defined routes in a flat map.

@!attribute [r] verb_map

@return [Hash]
  Defined routes grouped by verb.

@!attribute [r] named_map

@return [Hash]
  Defined routes grouped by route name.

Constants

NOT_FOUND_RESPONSE

@private

Attributes

flat_map[R]
named_map[R]
verb_map[R]

Public Class Methods

new(base: "http://localhost:9292", &block) click to toggle source

Initializes the builder.

@param base [String]

Base URL, used for generating URLs.

@yield

Executes the block inside the created builder context.

@return [void]

@private

# File lib/rutter/builder.rb, line 39
def initialize(base: "http://localhost:9292", &block)
  @uri = URI(base).freeze
  @flat_map = []
  @verb_map = VERBS.map { |v| [v, []] }.to_h
  @named_map = {}

  instance_eval(&block) if block_given?
end

Public Instance Methods

add(verb, path, to: nil, as: nil, constraints: nil, &block) click to toggle source

Add a new, frozen, route to the map.

@param verb [String]

Request verb to match.

@param path [String]

Path template to match.

@param to [#call]

Rack endpoint.

@param as [Symbol, String]

Route name/identifier.

@param constraints [Hash]

Route segment constraints.

@param &block [Proc]

Endpoint as a block.

@yieldparam env [Hash]

Rack's environment hash.

@return [Rutter::Route]

@raise [ArgumentError]

If verb is unsupported.

@raise [ArgumentError]

If endpoint is missing.

@private

# File lib/rutter/builder.rb, line 210
def add(verb, path, to: nil, as: nil, constraints: nil, &block)
  to = block if block_given?
  raise "Missing endpoint" unless to

  verb = verb.to_s.upcase

  unless VERBS.include?(verb)
    raise ArgumentError, "Unsupported verb '#{verb}'"
  end

  route = Route.new(path, to, constraints)
  @flat_map << route
  @verb_map[verb] << route
  return route unless as

  named_map[Naming.route_name(as)] = route
end
call(env) click to toggle source

Process the request and is compatible with the Rack protocol.

@param env [Hash]

Rack environment hash.

@return [Array]

Serialized Rack response.

@see rack.github.io

@private

# File lib/rutter/builder.rb, line 258
def call(env)
  request_method = env["REQUEST_METHOD"]

  return NOT_FOUND_RESPONSE unless @verb_map.key?(request_method)

  routes = @verb_map[request_method]
  routes.each do |route|
    next unless route.match?(env)
    return route.call(env)
  end

  NOT_FOUND_RESPONSE
end
freeze() click to toggle source

Freeze the state of the router.

@return [self]

Calls superclass method
# File lib/rutter/builder.rb, line 231
def freeze
  @flat_map.freeze
  @verb_map.freeze
  @verb_map.each_value(&:freeze)
  @named_map.freeze

  super
end
mount(app, at:, host: nil) click to toggle source

Mount a Rack compatible at the given path prefix.

@param app [#call]

Application to mount.

@param at [String]

Path prefix to match.

@param host [Regexp]

Match the given host pattern.

@return [Rutter::Mount]

# File lib/rutter/builder.rb, line 97
def mount(app, at:, host: nil)
  route = Mount.new(at, app, host: host)
  @flat_map << route
  VERBS.each { |verb| @verb_map[verb] << route }
  route
end
namespace(name, &block) click to toggle source

Creates a scoped collection of routes with the given name as namespace.

@param name [Symbol, String]

Scope namespace.

@yield

Scope context.

@return [Rutter::Scope]

@example

Rutter.new do
  namespace :admin do
    get "/login", to: "sessions#new", as: :login
  end
end
# File lib/rutter/builder.rb, line 83
def namespace(name, &block)
  scope path: name, namespace: name, as: name, &block
end
path(name, **args) click to toggle source

Generates a path from the given arguments.

@param name [Symbol]

Name of the route to generate path from.

@overload path(name, key: value)

@param key [String, Integer, Array]
  Key value.

@overload path(name, key: value, key2: value2)

@param key2 [String, Integer, Array]
  Key value.

@return [String]

Generated path.

@raise [RuntimeError]

If the route cannot be found.

@see Rutter::Route#expand

@example

router = Rutter.new(base: "http://rutter.org")
router.get "/login", to: "sessions#new", as: :login
router.get "/books/:id", to: "books#show", as: :book

router.path(:login)
  # => "/login"
router.path(:login, return_to: "/")
  # => "/login?return_to=/"
router.path(:book, id: 82)
  # => "/books/82"
# File lib/rutter/builder.rb, line 135
def path(name, **args)
  unless (route = @named_map[name])
    raise "No route called '#{name}' was found"
  end

  route.expand(**args)
end
scope(path: nil, namespace: nil, as: nil, &block) click to toggle source

Create a scoped set of routes.

@param path [String]

Scope path prefix.

@param namespace [String, Symbol]

Scope namespace.

@param as [Symbol]

Scope name prefix.

@yield

Block is evaluated inside the created scope context.

@return [Rutter::Scope]

@see Rutter::Scope

# File lib/rutter/builder.rb, line 63
def scope(path: nil, namespace: nil, as: nil, &block)
  Scope.new(self, path: path, namespace: namespace, as: as, &block)
end
url(name, **args) click to toggle source

Generates a full URL from the given arguments.

@param name [Symbol]

Name of the route to generate URL from.

@overload expand(name, subdomain: value)

@param subdomain [String, Symbol]
  Subdomain to be added to the host.

@overload expand(name, key: value)

@param key [String, Integer, Array]
  Key value.

@overload expand(name, key: value, key2: value2)

@param key2 [String, Integer, Array]
  Key value.

@return [String]

Generated URL.

@raise [RuntimeError]

If the route cannot be found.

@see Rutter::Builder#path

@example

router = Rutter.new(base: "http://rutter.org")
router.get "/login", to: "sessions#new", as: :login
router.get "/books/:id", to: "books#show", as: :book

router.url(:login)
  # => "http://rutter.org/login"
router.url(:login, return_to: "/")
  # => "http://rutter.org/login?return_to=/"
router.url(:book, id: 82)
  # => "http://rutter.org/books/82"
# File lib/rutter/builder.rb, line 177
def url(name, **args)
  host = @uri.scheme + "://"
  host += "#{args.delete(:subdomain)}." if args.key?(:subdomain)
  host += @uri.host
  host += ":#{@uri.port}" if @uri.port != 80 && @uri.port != 443
  host + path(name, **args)
end