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).
Class methods
def self.new -> Bezel::Router
The router, its pipelines' plugs made now, once.
def self.route_table -> Array[Bezel::Route]
The routes, in order: generated by the routes statement.
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).
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.
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"
Methods
def router_class -> Type[Bezel::Router]
The router's class (generated by routes, whose tables it reads).
def call(conn: Bezel::Conn) -> Bezel::Conn
The connection changed by this plug.
from Bezel::Plug