Url Class
Parse, build, join, and manipulate URLs without string surgery. All methods are static.
Url.parse(url)
Returns a hash of components. Absent parts are null (never missing keys).
let u = Url.parse("https://user:pw@api.ex.com:8443/v1/items?page=2#top");
u["scheme"]; # "https"
u["host"]; # "api.ex.com"
u["port"]; # 8443
u["path"]; # "/v1/items"
u["query"]; # "page=2"
u["fragment"]; # "top"
Url.params(url) / Url.param(url, name)
Decoded query-parameter access. A pair with no = maps to null; a repeated key keeps its last value. + decodes to a space and an undecodable escape is kept verbatim — the same rules request params use, so Url.params and req["params"] agree on the same query.
Url.params("https://ex.com/?q=red%20shoe&flag");
# { "q": "red shoe", "flag": null }
Url.param("https://ex.com/?page=2", "page"); # "2"
Url.param("https://ex.com/?page=2", "nope"); # null
Url.set_param(url, name, value)
Returns a new URL string. Setting an existing param replaces it; setting null removes it. Only the named param is rewritten — every other pair keeps its exact original text, so a value this helper cannot decode is never altered.
Url.set_param("https://ex.com/?b=2", "a", "1"); # "https://ex.com/?b=2&a=1"
Url.set_param("https://ex.com/?a=1&b=2", "a", null); # "https://ex.com/?b=2"
Url.join(base, relative) / Url.build(hash)
Resolve relative references and construct URLs from parts. build accepts scheme/username/password/host/port/path/query/fragment, and an unknown key is an error so a typo is not a silent no-op; query may be a string or a params hash, whose arrays and nested hashes expand to the bracket names request params use (tags[]=a&tags[]=b, author[name]=x).
Url.join("https://ex.com/a/b", "c"); # "https://ex.com/a/c"
Url.join("https://ex.com/a/b", "?page=2"); # "https://ex.com/a/b?page=2"
Url.build({
"scheme": "https",
"host": "api.ex.com",
"path": "/v1/x",
"query": { "page": 2, "q": "a b" }
});
# "https://api.ex.com/v1/x?page=2&q=a%20b"
Url.encode_component(str) / Url.decode_component(str)
Percent-encoding helpers (spaces become %20). Decoding leaves invalid escapes as-is.
Url.encode_component("Ann Lee"); # "Ann%20Lee"
Url.decode_component("Ann%20Lee"); # "Ann Lee"