class Bezel::Controller
The base of an application's controllers. A controller's actions are
its public methods taking and returning a Conn (but the helpers
below), which the router calls for its routes. Its body declares its
plugs, one plug (or skip) line each, or, without plugs of its own,
writes controller; either generates dispatch over the actions, which
runs the plugs before them:
class Pages < Bezel::Controller
plug :load_visitor, except: [:gone]
def home(conn: Bezel::Conn) -> Bezel::Conn = text(conn, "home #{conn.assign(:visitor)}")
def gone(conn: Bezel::Conn) -> Bezel::Conn = redirect(conn, to: "/")
private def load_visitor(conn: Bezel::Conn) -> Bezel::Conn = conn.assign(:visitor, "Ann")
end
c = Bezel::Conn.build(HTTP::Server::Request.get("/"))
Pages.new.dispatch(:home, c).resp_body # => HTTP::Content::Text(text: "home Ann")
Pages.new.dispatch(:gone, c).status # => 302
The helpers answer, as Conn's methods do, and halt. A controller has
no state: the router makes one for each request (make), so its
initialize takes no argument.
Class methods
def self.new -> Bezel::Controller
A controller.
def self.make -> Bezel::Controller
A controller of this class: what the router calls the action on.
def self.controller_plugs -> Array[Bezel::ControllerPlug]
The plugs the controller declared, its ancestors' first (generated
with dispatch; a subclass's runs them too).
static def self.controller -> Void
Declares a controller without plugs of its own: generates dispatch
over its actions (its public methods taking and returning a Conn,
written or inherited, but those of Bezel::Controller and the plugs),
running the superclass's plugs only. A body with plug lines does
without it (and refuses it).
static def self.controller(&block: Meta::ClassDef[Bezel::Controller].() -> Void) -> Void
Declares the controller and its plugs, which the block lists as plug
lines (plug, skip, below): the form before plug lines could stand
in the body, still accepted, and the same as writing the lines in the
body, in their order among the body's other lines (a body may have
several blocks). An empty block declares the controller as a bare
controller does. An error of a line in the block is reported at the
controller statement.
A subclass of a controller declares its own plug lines or
controller statement for actions of its own: the superclass's
dispatch knows the superclass's actions only.
static def self.plug(method: Symbol, *, only: Array[Symbol] = …, except: Array[Symbol] = …) -> Void
Runs the controller's method method (private ones too, taking and
returning a Conn) before the actions only names (all when empty),
but those except names: a statement of the class body, one per plug,
run in order before the action they apply to, until one halts, after
the plugs the superclass declared. The class's dispatch is generated
once its body ends. A plug method that does not exist or has another
signature, or an action named in only: or except: that does not
exist, is a compile error when the body ends (the methods are usually
written below the lines); only: with except: is one at the line. A
controller without plugs of its own writes controller.
class Posts < Bezel::Controller
plug :require_login, except: [:index, :show]
plug :load_post, only: [:show, :edit]
def index(conn: Bezel::Conn) -> Bezel::Conn = text(conn, "posts")
def show(conn: Bezel::Conn) -> Bezel::Conn = text(conn, "post #{conn.assign(:post)}")
def edit(conn: Bezel::Conn) -> Bezel::Conn = text(conn, "edit #{conn.assign(:post)}")
private def require_login(conn: Bezel::Conn) -> Bezel::Conn = conn.text("log in", status: 401)
private def load_post(conn: Bezel::Conn) -> Bezel::Conn = conn.assign(:post, "7")
end
c = Bezel::Conn.build(HTTP::Server::Request.get("/"))
Posts.new.dispatch(:show, c).resp_body # => HTTP::Content::Text(text: "post 7")
Posts.new.dispatch(:edit, c).status # => 401
static def self.skip(method: Symbol, *, only: Array[Symbol] = …, except: Array[Symbol] = …) -> Void
Keeps the plugs named method (the superclass's, or this body's
before) from running before the actions only names (all when it and
except are empty), but those except names: a statement of the
class body, as plug is (Rails' skip_before_action). A skip
naming no plug is a compile error at the line (at the controller
statement, for a line in its block).
static def self.action?(m: Meta::MethodSig) -> Bool
Whether m has an action's shape: public (as methods lists them),
one parameter, a Conn, returning a Conn, and not a method of
Bezel::Controller.
Methods
def dispatch(action: Symbol, conn: Bezel::Conn) -> Bezel::Conn
Runs action on conn, after the controller's plugs: generated by
the body's plug lines or its controller statement. Without them, an
ArgumentError (a 500: the application routes to a controller it did
not declare).
def render(conn: Bezel::Conn, html: Bezel::Html, status: Int = …) -> Bezel::Conn
conn answered with the page html (text/html), and halted.
def render(conn: Bezel::Conn, html: String, status: Int = …) -> Bezel::Conn
conn answered with the HTML source html (text/html), and
halted: the caller vouches that it is safe HTML.
def json(conn: Bezel::Conn, value: JSON::Value, status: Int = …) -> Bezel::Conn
conn answered with value as JSON, and halted.
def text(conn: Bezel::Conn, text: String, status: Int = …) -> Bezel::Conn
conn answered with the text text, and halted.
def redirect(conn: Bezel::Conn, *, to: String, status: Int = …) -> Bezel::Conn
conn answered with a redirect to to (302 Found, or status; a
path of the application, a route helper's), and halted.
def send_file(conn: Bezel::Conn, path: String, content_type: String? = …) -> Bezel::Conn
conn answered with the file at path (its content type
content_type, else by its extension, as Plug::Static gives it), and
halted: Bezel::NotFound when there is no such file. The path is the
application's, never one from the request.
def put_flash(conn: Bezel::Conn, kind: Symbol, message: String) -> Bezel::Conn
conn with message in the flash for the next request.
def put_status(conn: Bezel::Conn, status: Int) -> Bezel::Conn
conn with the status status.
def halt(conn: Bezel::Conn) -> Bezel::Conn
conn, halted.
def repo(conn: Bezel::Conn) -> Rotor::Repo
The repository of the application's database: Bezel::ConfigError
when it has none (no DATABASE_URL).