class
Analyzer::Gleam::Wisp
- Analyzer::Gleam::Wisp
- Analyzer
- Reference
- Object
Overview
Wisp has no route table — routing is a case over the split path,
and the verb comes either from a second subject in the same case or
from the handler it delegates to. All four shapes are common in real
apps:
case wisp.path_segments(req) { # path only
["users", id] -> user(req, id)
}
case wisp.path_segments(req), req.method { # path, method
["api", "teams"], http.Post -> create_team(req)
}
case req.method, wisp.path_segments(req) { # method, path
Get, ["members"] -> members.list(req)
}
let path = wisp.path_segments(req) # via let bindings
let method = req.method
case method, path {
Get, [] -> home.render(req)
}
Rather than assume an order, the subject is parsed to learn which
comma position holds the path and which holds the method, and each
arm is then read against those positions. When the arm carries no
verb the handler it calls is followed (resolve_handler) to find a
case req.method or a wisp.require_method.
Defined in:
analyzer/analyzers/gleam/wisp.crConstant Summary
-
BODY_VERBS =
Set {"POST", "PUT", "PATCH", "ANY"} -
Verbs that can carry a request body.
-
CALL_REGEX =
/(?:\b([a-z_][A-Za-z0-9_]*)\s*\.\s*)?\b([a-z_][A-Za-z0-9_]*)\s*\(/ -
CASE_KEYWORD =
/\bcase\b/ -
COOKIE_REGEX =
/wisp\.get_cookie\s*\(\s*[^,]+,\s*"([^"]+)"/ -
FORM_FIELD_REGEX =
/key_find\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\.(?:values|files)\s*,\s*"([^"]+)"/ -
list.key_find(formdata.values, "title")— the binder is namedformdata,form, … so match any receiver. -
FORM_REGEX =
/wisp\.require_form\s*\(/ -
FUNCTION_DEF =
/^\s*(?:pub\s+)?fn\s+([a-z_][A-Za-z0-9_]*)\s*\(/ -
HEADER_REGEX =
/\bget_header\s*\(\s*(?:[^,"()]+,\s*)?"([^"]+)"/ -
2-arg
request.get_header(req, "accept")and the piped 1-arg|> request.get_header("accept")are both idiomatic. -
HTTP_METHODS =
{"Get" => "GET", "Post" => "POST", "Put" => "PUT", "Patch" => "PATCH", "Delete" => "DELETE", "Head" => "HEAD", "Options" => "OPTIONS", "Trace" => "TRACE", "Connect" => "CONNECT"} -
IGNORED_CALL_MODULES =
Set {"wisp", "gleam", "http", "request", "response", "io", "list", "string", "int", "float", "result", "option", "json", "dict", "bool", "bit_array", "dynamic", "decode", "uri", "bytes_tree", "string_tree", "mist", "case", "fn", "use", "let"} -
Calls into the standard library and wisp itself are responses and helpers, never route handlers.
-
IMPORT_LINE =
/^\s*import\s+([a-z_][A-Za-z0-9_\/]*)(?:\s*\.\s*\{[^}]*\})?(?:\s+as\s+([a-z_][A-Za-z0-9_]*))?/ -
JSON_FIELD_REGEX =
/\bfield\s*\(\s*"([^"]+)"/ -
A
wisp.require_jsonbody is decoded elsewhere;decode.fieldis where the field names actually appear. -
JSON_REGEX =
/wisp\.require_json\s*\(/ -
MAX_RESOLVE_DEPTH =
3 -
Depth limit for following a route arm through handler functions to the
case req.methodthat names its verb. -
MAX_SUBJECT_LINES =
8 -
A
casesubject spans at most a handful of lines even when the formatter wraps it. -
METHOD_BINDING =
/\blet\s+([a-z_][A-Za-z0-9_]*)\s*=\s*[a-z_][A-Za-z0-9_]*\.method\b/ -
METHOD_CASE =
/\bcase\s+[A-Za-z_][A-Za-z0-9_]*\.method\s*\{/ -
METHOD_SOURCE =
/\.method\b/ -
METHOD_TOKEN =
/\A(?:[a-z_][A-Za-z0-9_]*\.)?([A-Z][A-Za-z0-9_]*)\z/ -
NON_ROUTE_BODY =
/\A\s*(?:wisp\.)?(?:method_not_allowed|not_found|bad_request|unprocessable_entity|internal_server_error|handle_request\s*\(\s*\))/ -
_ -> wisp.method_not_allowed([Get, Post])and_, _ -> wisp.not_found()are the fallthrough arms every wisp router ends with. They match a path but serve no endpoint. -
PATH_BINDING =
/\blet\s+([a-z_][A-Za-z0-9_]*)\s*=[^\n]*\bpath_segments\s*\(/ -
let path = wisp.path_segments(req)/let method = req.method. Apps that bind these before thecasewould otherwise leave the subject looking like two bare identifiers. -
PATH_SOURCE =
/\bpath_segments\s*\(/ -
REQUIRE_METHOD =
/require_method\s*\(\s*[^,)]+,\s*(?:[a-z_][A-Za-z0-9_]*\.)?([A-Z][A-Za-z0-9_]*)/ -
SERVE_STATIC =
/wisp\.serve_static\s*\(/ -
STRING_BODY =
/wisp\.require_(?:string|bit_array)_body\s*\(/
Class Method Summary
Instance Method Summary
- #analyze
-
#tech : String
Instance-side view of the same declaration.
Instance methods inherited from class Analyzer
analyze
analyze,
base_path : String
base_path,
base_paths : Array(String)
base_paths,
base_relative_path(path : String) : String
base_relative_path,
callees_needed? : Bool
callees_needed?,
content_matches?(content : String, markers : Regex) : Bool
content_matches?,
http_header_name(name : String) : String | Nil
http_header_name,
line_number_for_index(content : String, char_index : Int32) : Int32
line_number_for_index,
logger : NoirLogger
logger,
parallel_analyze(files : Array(String), &block : String -> Nil)
parallel_analyze,
read_file_content(path : String) : String
read_file_content,
result : Array(Endpoint)
result,
tech : String
tech,
unique_params(params : Array(Param)) : Array(Param)
unique_params,
url : String
url,
web_root_path(path : String, markers : Array(String)) : String
web_root_path
Constructor methods inherited from class Analyzer
new(options : Hash(String, YAML::Any))
new
Macros inherited from class Analyzer
analyzer_for(tech)
analyzer_for
Instance methods inherited from module FileHelper
all_files : Array(String)
all_files,
get_files_by_basename(basename : String) : Array(String)
get_files_by_basename,
get_files_by_extension(extension : String) : Array(String)
get_files_by_extension,
get_files_by_extensions(extensions : Array(String)) : Array(String)
get_files_by_extensions,
get_files_by_prefix(prefix : String) : Array(String)
get_files_by_prefix,
get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String)
get_files_by_prefix_and_extension,
get_files_by_relative_path(relative_path : String, root : String = "") : Array(String)
get_files_by_relative_path,
get_public_dir_files(base_path : String, folder : String) : Array(String)
get_public_dir_files,
get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String)
get_public_files,
walked_path(expanded : String) : String
walked_path
Class Method Detail
Instance Method Detail
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.