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.

src/controller.qz:26

Class methods

def self.new -> Bezel::Controller

A controller.

src/controller.qz:28

def self.make -> Bezel::Controller

A controller of this class: what the router calls the action on.

src/controller.qz:31

def self.controller_plugs -> Array[Bezel::ControllerPlug]

The plugs the controller declared, its ancestors' first (generated with dispatch; a subclass's runs them too).

src/controller.qz:43

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).

src/controller.qz:50

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.

src/controller.qz:66

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

src/controller.qz:103

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).

src/controller.qz:113

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.

src/controller.qz:174

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).

src/controller.qz:37

def render(conn: Bezel::Conn, html: Bezel::Html, status: Int = …) -> Bezel::Conn

conn answered with the page html (text/html), and halted.

src/controller.qz:180

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.

src/controller.qz:184

def json(conn: Bezel::Conn, value: JSON::Value, status: Int = …) -> Bezel::Conn

conn answered with value as JSON, and halted.

src/controller.qz:187

def text(conn: Bezel::Conn, text: String, status: Int = …) -> Bezel::Conn

conn answered with the text text, and halted.

src/controller.qz:190

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.

src/controller.qz:194

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.

src/controller.qz:200

def put_flash(conn: Bezel::Conn, kind: Symbol, message: String) -> Bezel::Conn

conn with message in the flash for the next request.

src/controller.qz:207

def put_status(conn: Bezel::Conn, status: Int) -> Bezel::Conn

conn with the status status.

src/controller.qz:210

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

conn, halted.

src/controller.qz:213

def repo(conn: Bezel::Conn) -> Rotor::Repo

The repository of the application's database: Bezel::ConfigError when it has none (no DATABASE_URL).

src/controller.qz:217