Reading the request
Every helper that reads the request being served:
params,session, headers, cookies, bodies.
Every helper here is a top-level method that reads the request being served. One request per process, so there is nothing to pass around and no argument to thread through.
params, form, query
get "/notes/:id" do
id = params.fetch(:id).to_i
"note #{id} sorted by #{params.fetch(:sort, "date")}"
end
params merges three sources, later ones winning: path params, then the query string, then urlencoded body
fields. form is the body fields only; query is the query string only. All three return a
Melee::Params, a Symbol-or-String view over a Hash{String => String}.
| Call | Returns |
|---|---|
params[:name] | String or nil — nil when the key is absent |
params.fetch(:name) | String, "" when absent — never nil, never raises |
params.fetch(:name, "date") | String, the default when absent |
params.key?(:name) | true / false |
params.to_h | Hash{String => String} |
| `params.each { | k, v |
params.empty? | true / false |
The idiom. Use params.fetch(:x) whenever you want a String — it always gives one, so .to_i,
.strip, .match? and interpolation are safe. Use params[:x] only when absent and empty must be told
apart. Do not write params[:x].to_s; fetch already did that.
Values are always Strings. Convert explicitly: params.fetch(:n).to_i, params.fetch(:price).to_f.
Validate before you trust:
post "/notes" do
halt 400, "Date must be YYYY-MM-DD" unless params.fetch(:on_date).match?(/\A\d{4}-\d\d-\d\d\z/)
db.run "INSERT INTO notes (on_date, text) VALUES (?, ?)", params.fetch(:on_date), params.fetch(:text)
redirect "/notes"
end
Only application/x-www-form-urlencoded bodies are parsed into form. Multipart file uploads are not
supported; a multipart body leaves form empty and stays available as request.body.
Values arrive already decoded. To go the other way — putting a value into a URL you build — use the two
top-level helpers: url_encode(String) -> String and url_decode(String) -> String.
redirect "/search?q=#{url_encode(params.fetch(:q))}"
request
request returns the Melee::Request.
| Call | Returns |
|---|---|
request.method | String, upper case: "GET", "POST", … |
request.path | String, no query string |
request.query | String, the raw query string, "" when there is none |
request.host | String |
request.scheme | String, "http" or "https" |
request.base_url | String, e.g. "https://kitchen.example.com" |
request.remote_addr | String, the client address |
request.header("Accept") | String or nil, case-insensitive |
request.headers | Array[[String, String]] in wire order |
request.content_type | String, "" when the header is absent |
request.body | String, "" when there is no body |
request.json | the parsed body; raises JSON::ParserError on bad input |
request.cookies | Hash{String => String} |
request.get?, request.post? | true / false |
request.id | Integer, the request id that appears in the logs |
There is no request.ip; the name is request.remote_addr.
post "/webhook", csrf: false do
payload = request.json
halt 400 unless payload.is_a?(Hash)
log.info "webhook", kind: payload["type"].to_s
text "ok"
end
request.json returns whatever the body decoded to — Hash, Array, String, Integer, nil. Index
hashes by String keys (payload["type"], not payload[:type]) and narrow with is_a? before doing
arithmetic on a value.
session
session is a signed cookie holding Strings only.
post "/login" do
if secure_equal?(params.fetch(:secret), ENV["ADMIN_SECRET"].to_s)
session[:admin] = "1"
redirect "/admin"
else
status 401
render :login, error: "That secret is wrong."
end
end
| Call | Returns |
|---|---|
session[:user] | String or nil |
session[:user] = "ada" | stores value.to_s; assigning nil deletes the key |
session.delete(:user) | same as assigning nil |
session.clear | empties it |
session.to_h | Hash{String => String} |
session.csrf_token | String, minted on first read |
The cookie is only rewritten when you touched the session. Details, size limits and the CSRF rules are in
security.md.
Configuration
ENV["NAME"] reads a variable set with melee env NAME value. It is String or nil, so write
ENV["NAME"].to_s. Melee.env("NAME", "default") returns the default when the variable is missing or
empty. For values the app itself writes and reads back — tokens, last-run times — use setting (see
database.md).