class

Analyzer::Crystal::Kemal

Inherits Analyzer::Crystal::CrystalEngine < FileScanEngine < Analyzer < FileHelper < Reference < Object

Constants

ASSIGN_PATTERN = /(?:^|[^.\w@:])(@{0,2}[a-z_]\w*)\s*(?::\s*[\w:]+\s*)?=(?![=~>])/

Any other plain name = …, which unbinds a router name reassigned to something else (router = HTTP::Client.new in a later method). ||= keeps an existing binding, so it is not one.

KEMAL_EXTRA_ROUTES = [{"QUERY", QUERY_ROUTE_PATTERN}] of Tuple(String, Regex)
MACRO_VAR_PATTERN = /\{\{\s*(\w+)\s*=\s*([A-Za-z_][\w:]*)\s*\}\}/

Compile-time macro var assignment, e.g. {{namespace = Routes::API::V1}}. invidious sets it once, then registers 50+ routes as get "/…", {{namespace}}::Videos, :videos — substituting it back lets those routes resolve their controller and carry callees.

MOUNT_PATTERN = /^\s*mount\s+["'](.+?)["']\s*,\s*(@{0,2}\w+)/
NAMESPACE_PATTERN = /^(\s*)(?:(\w+)\.)?namespace\s+["'](.+?)["']/
PARAM_DEFAULTS_LINE = /^\s*(?:(?:private|protected|abstract)\s+)?(?:def|macro)\b|->\s*\(/

A def/macro line or a lambda literal: its name = … are parameter defaults, not assignments.

QUERY_ROUTE_PATTERN = /(?:^|[^.\w])query\s*(?:\(\s*)?['"](.+?)['"]/

Kemal's query route DSL (RFC 10008, kemalcr/kemal#769) is Kemal-specific, unlike get/post/etc. which the shared engine's SIMPLE_ROUTE_RE already covers for every Crystal analyzer (Amber, Lucky). It's stitched in via match_crystal_simple_route(..., extra:) instead of touching that shared pattern, mirroring Lucky's TRACE extra route. The same (?:^|[^.\w]) lead-in that keeps before_get / after_get from matching get also keeps before_query / after_query filters from matching here — they must not emit endpoints.

ROUTE_HANDLER_RE = /['"]\s*\)?\s*(?:do\b|\{|,\s*(?:do\b|&|\{\{|[A-Z]\w*(?:::\w+)*\s*,\s*:\w))/

A Kemal route hands its path a handler: a block (do |env|, { … }, &handler) or a controller action (Routes::Misc, :home, also via {{ namespace }}). spec-kemal's client helpers share the verb names but take none: get("/items/7"), post "/x", headers: h, body: b.

ROUTER_MOUNT_PATTERN = /(?:^|[^.\w@])(@{0,2}\w+)\.mount\s*\(?\s*(?:["']([^"']*)["']\s*,\s*)?(@{0,2}\w+)/

api.mount "/users", users / api.mount users: group 1 = parent, 2 = path, 3 = child. The bare DSL mount is MOUNT_PATTERN.

ROUTER_NEW_PATTERN = /(?:^|[^.\w@:])(@{0,2}\w+)\s*(?::\s*[\w:]+\s*)?(?:\|\|)?=\s*Kemal::Router\.new\b(?:\s*\(?\s*["']([^"']*)["'])?/

router = Kemal::Router.new / typed : Kemal::Router = Kemal::Router.new("/pre") / @@router ||= Kemal::Router.new: group 1 = name, 2 = the router's own prefix. These names are the only receivers whose router.get "/x" is a route: client.get "/x" on an HTTP::Client (or a spec helper) has the same shape.

ROUTER_ROUTE_PATTERN = /(?:^|[^.\w@])(@{0,2}\w+)\.(get|post|put|delete|patch|head|options|ws|sse|query)\s*(?:\(\s*)?['"](.+?)['"]/

router.get "/x": group 1 = receiver, 2 = verb, 3 = path. The receiver is checked against the file's Kemal::Router names by the caller.

SERVE_STATIC_DISABLED_RE = Regex.union("serve_static false", "serve_static(false)")

serve_static false / serve_static(false) — checked on every source line, so the two fixed OR-ed String#includes? substring scans are precompiled into a single regex union (one PCRE2 JIT scan per line instead of two naive substring scans).

Class methods

tech_name
Source

Instance methods

analyze
Source
analyze_file(path : String) : Array(Endpoint)
Source
line_to_endpoint(content : String) : Endpoint
Source
line_to_param(content : String) : Param
Source
router_line_to_endpoint(content : String, routers : Set(String)) : Tuple(Endpoint, String)

router.get "/x" where router is one of routers, trying every x.verb "..." on the line. Returns the endpoint (empty when nothing matches) and the receiver. Kemal registers sse as a GET route.

Source
tech

Instance-side view of the same declaration. The per-file rescues live on this base class, which has no way to name the analyzer that is running inside them, so a skipped file could not be attributed to a tech. Deriving it from analyzer_for keeps the name written exactly once.

Source

Nested types