abstract class Bezel::Router

include Bezel::Plug

An application's router: a plug (the endpoint's last, usually) that matches a request to a route of its routes statement, runs the route's pipelines, and calls the route's action on its controller (Controller#dispatch, which runs the controller's plugs first).

class ShopItems < Bezel::Controller
  controller
  def home(conn: Bezel::Conn) -> Bezel::Conn = text(conn, "home")
  def show(conn: Bezel::Conn) -> Bezel::Conn = text(conn, "item #{conn.params.int("id")}")
end

class ShopRouter < Bezel::Router
  def self.browser -> Array[Bezel::Plug] = [Bezel::Plug::Accepts.new(["html"])]

  routes do
    scope "/", pipeline: :browser do
      get "/", ShopItems, :home, as: "home"
      get "/items/{id}", ShopItems, :show, as: "item"
    end
  end
end

ShopRouter.item_path(id: 7)                  # => "/items/7"
c = ShopRouter.new.call(Bezel::Conn.build(HTTP::Server::Request.get("/items/7")))
c.resp_body                                   # => HTTP::Content::Text(text: "item 7")

Matching is HTTP::Router's: the most specific pattern wins, HEAD is served by a GET route, and a path that only other methods have is answered 405 Method Not Allowed with Allow. A path no route has is passed on (the endpoint answers 404). The path's parameters are merged into the parameters (conn.params.int("id")) and the request's params.

The routes statement checks, where the router is compiled, that each route's controller has its action (a public method taking and returning a Conn) and each pipeline named is a class method of the router returning plugs. It generates the route helpers: for each named route, a class method <name>_path taking the pattern's parameters as keywords (item_path(id: 7)). A misspelt or missing keyword is a compile error; a value is an Int or a String, checked when the helper runs (ArgumentError for another, or for an empty, . or .. segment), and percent-encoded. with_query adds a query, and Conn#url makes the path a URL.

A controller whose dispatch does not cover the action routed to (no plug lines nor controller statement at all, or a subclass's action under its superclass's dispatch) answers with an ArgumentError (a 500, reported to on_error).

src/router.qz:51

Class methods

def self.new -> Bezel::Router

The router, its pipelines' plugs made now, once.

src/router.qz:58

def self.route_table -> Array[Bezel::Route]

The routes, in order: generated by the routes statement.

src/router.qz:70

static def self.routes(&block: Bezel::RouteBuilder.() -> Void) -> Void

Declares the routes: the block runs where the router is compiled, on a RouteBuilder (scope, get/post/put/patch/delete, resources).

src/router.qz:79

def self.fill(pattern: String, values: Hash[Symbol, Object]) -> String

pattern with its parameters taken from values (an Int or a String each), percent-encoded; a {name...} one keeps its slashes.

src/router.qz:147

def self.with_query(path: String, params: Hash[String, String | Array[String]]) -> String

path with the query params (keys in order; an array value repeats its key), or path alone when params is empty.

Bezel::Router.with_query("/users", {"page" => "2", "tag" => ["a b", "c"]})
# => "/users?page=2&tag=a+b&tag=c"

src/router.qz:183

Methods

def router_class -> Type[Bezel::Router]

The router's class (generated by routes, whose tables it reads).

src/router.qz:67

def call(conn: Bezel::Conn) -> Bezel::Conn

The connection changed by this plug.

from Bezel::Plug

src/router.qz:187