class

Analyzer::Erlang::Cowboy

Inherits Analyzer < FileHelper < Reference < Object

Cowboy keeps its routes in a dispatch table rather than in per-handler annotations:

Dispatch = cowboy_router:compile([
    {'_', [
        {"/",             hello_handler, []},
        {"/users/:id",    user_handler,  []},
        {"/static/[...]", cowboy_static, {priv_dir, app, "static"}}
    ]}
]),

The verb is deliberately absent from the table — Cowboy leaves method negotiation to the handler (allowed_methods/2 for REST handlers, or a match on cowboy_req:method/1). Resolving it therefore means following the handler atom to its module, which is what handler_info does; routes whose handler can't be resolved fall back to "ANY".

Constants

ALLOWED_METHODS_REGEX = /allowed_methods\s*\([^)]*\)\s*->\s*\{\s*\[([^\]]*)\]/
BINDING_REGEX = /cowboy_req:binding\s*\(\s*([a-z][A-Za-z0-9_@]*)/
BODY_VERBS = Set {"POST", "PUT", "PATCH", "ANY"}

Verbs that can carry a request body. Handler params are collected per module rather than per clause, so a module that both serves GET and reads a body on POST would otherwise hang a body param off its GET route.

HANDLER_ATOM = /\A[a-z][A-Za-z0-9_@]*\z/
HEADER_REGEX = /cowboy_req:header\s*\(\s*<<\s*"([^"]+)"\s*>>/
HTTP_VERBS = Set {"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "TRACE", "CONNECT"}
MATCH_COOKIE_START = /cowboy_req:match_cookies\s*\(\s*\[/
MATCH_QS_START = /cowboy_req:match_qs\s*\(\s*\[/
MAX_TUPLE_LINES = 20

A dispatch tuple that spans more lines than this is not a shape we can read; bounding the window also bounds the joined-text cost so a pathological file can't turn the scan quadratic.

METHOD_BINARY_REGEX = /<<\s*"([A-Za-z]+)"\s*>>/
PATH_LITERAL = /\A\s*(?:"((?:[^"\\]|\\.)*)"|<<\s*"((?:[^"\\]|\\.)*)"\s*>>)\s*\z/
QS_FIELD_REGEX = /\A\{?\s*([a-z][A-Za-z0-9_@]*)/

A match_qs field is either a bare atom (id) or a constraint tuple whose first element is the field name ({token, nonempty}, {page, int, 1}). Only that leading atom is the parameter — the rest is the constraint and its default.

READ_BODY_REGEX = /cowboy_req:read_(?:body|part)\s*\(/
REQ_METHOD_REGEX = /cowboy_req:method\s*\(/
ROUTE_TUPLE_START = /\{\s*(?:"\/|<<\s*"\/)/

Cowboy dispatch entries are {PathMatch, Handler, InitialState} or {PathMatch, Constraints, Handler, InitialState}, and PathMatch is always an absolute path — as a string or as a binary. Requiring the leading / right in the gate keeps every other Erlang tuple out.

SPLIT_TOP_LEVEL_RULES = Noir::TopLevelSplit::Rules.new(nest: (Noir::TopLevelSplit::Nest::Paren | Noir::TopLevelSplit::Nest::Bracket) | Noir::TopLevelSplit::Nest::Brace, quotes: "\"'", escape: Noir::TopLevelSplit::Escape::InQuotes, strip: true, empties: Noir::TopLevelSplit::Empties::DropTrailing, per_kind: false, clamp: false)

Rules::JAVA except that a closer at depth 0 is NOT clamped: the bare depth -= 1 of the body this replaces drives depth negative on a fragment that closes a bracket the caller's regex already sliced away (")x, y"), which suppresses every later split. Preserved verbatim because it is observable on exactly the unbalanced input each_qs_field hands over.

The original pushed interior parts unstripped and dropped the tail only when tail.strip.empty?, then ran parts.map(&.strip) over everything. That is strip: true + Empties::DropTrailing: the emptiness test is on the stripped tail either way.

File-local because cowboy is the only splitter combining clamp: false with stripping.

STATIC_HANDLER = "cowboy_static"

cowboy_static is Cowboy's built-in file handler. It never reaches user code, so there is no allowed_methods/2 to follow — but it only ever serves GET and HEAD.

URLENCODED_REGEX = /cowboy_req:read_urlencoded_body\s*\(/

Class methods

tech_name
Source

Instance methods

analyze
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