Skip to content

feat(rack): complete Rack::Utils surface (nested query / q-values / cookies / status table) - #125

Merged
tannevaled merged 1 commit into
mainfrom
feat/rack-utils-nested-query-status
Jul 3, 2026
Merged

tannevaled merged 1 commit into
mainfrom
feat/rack-utils-nested-query-status

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Context

go-ruby-rack was already bound into rbgo (PR #104): Rack::Request,
Rack::Response, and a first slice of Rack::Utils
(escape/unescape/escape_html/escape_path/parse_query/build_query).
This PR extends the existing binding rather than reinventing it, completing
the Rack::Utils pure-compute surface over the same authoritative library.

What's added to Rack::Utils

  • parse_nested_query(qs, sep = nil) — nested Hash/Array params
  • build_nested_query(value, prefix = nil) — backed by a new insertion-ordered
    rackToGoNested converter (a sub-hash maps to *rack.Params, so Ruby Hash
    order survives — buildNested recognises *Params, not map[string]any)
  • q_values(header) / best_q_match(header, available) — Accept-header quality
    negotiation (best_q_match returns nil on no match, like MRI)
  • parse_cookies_header(header) and parse_cookies(env) (reads HTTP_COOKIE)
  • HTTP_STATUS_CODES constant (code → reason phrase, ascending order)

Fixed

Rack::Utils.status_code previously coerced its argument straight to an int, so
status_code(:not_found) wrongly returned 500. It now routes a Symbol through
rack.SymbolToStatusCode (:not_found → 404, obsolete aliases like
:unprocessable_entity → 422 too) and raises ArgumentError on an unknown
symbol, while Integer/String still coerce to int.

Value mapping

Reuses the existing helpers (rackParamsToHash / rackFromGo recurse through
*rack.Params → ordered Hash, []any → Array, nil → nil). Adds
rackToGoNested (ordered nested conversion for build_nested_query),
rackStrArray (available list for best_q_match), and rackCookieHeader
(pull HTTP_COOKIE out of an env Hash).

Verification

Executed through rbgo (not just parsed) and diffed byte-for-byte against
MRI 4.0.5 + rack 3.2.6 across nested queries, arrays, cookies, q-values,
status codes and the constant table — identical on every case. rack.go /
rack_helpers.go stay at 100% coverage; go build ./..., go vet ./...,
go test ./... and the webapp conformance suite are green.

Scoped out (upstream-lib follow-up)

Absent from go-ruby-rack/rack, so flagged for the owning library rather than
hand-rolled here (keeps the authoritative impl + oracle tests in one place):
secure_compare, clean_path_info, valid_path?, select_best_encoding,
forwarded_values.

🤖 Generated with Claude Code

…tus table

The go-ruby-rack binding already exposed escape/unescape/parse_query/
build_query and Rack::Request/Response. This completes the Rack::Utils
pure-compute surface over the same authoritative library:

  - parse_nested_query(qs, sep = nil) — nested Hash/Array params
  - build_nested_query(value, prefix = nil) — via a new insertion-ordered
    rackToGoNested converter (a sub-hash maps to *rack.Params so Ruby Hash
    order survives, which buildNested requires)
  - q_values / best_q_match — Accept-header quality negotiation
  - parse_cookies_header(header) and parse_cookies(env) (reads HTTP_COOKIE)
  - HTTP_STATUS_CODES constant (code -> reason phrase, ascending)

Also fixes status_code: it now maps a Symbol through
rack.SymbolToStatusCode (:not_found -> 404, obsolete aliases too, raising
ArgumentError on an unknown symbol) instead of blindly coercing the
argument to an int.

Verified byte-identical to MRI 4.0.5 + rack 3.2.6 for every method
(executed through rbgo, not just parsed). rack.go / rack_helpers.go stay
at 100% coverage; go build / vet / test ./... and the webapp conformance
suite are green.

Not bound (absent from go-ruby-rack/rack, flagged as an upstream-lib
follow-up rather than reinvented host-side): secure_compare,
clean_path_info, valid_path?, select_best_encoding, forwarded_values.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit b615499 into main Jul 3, 2026
9 checks passed
@tannevaled
tannevaled deleted the feat/rack-utils-nested-query-status branch July 3, 2026 21:49
tannevaled added a commit that referenced this pull request Jul 3, 2026
…o/valid_path?/select_best_encoding/forwarded_values) (#126)

Bind the five Rack::Utils methods PR #125 skipped. They are exported by
github.com/go-ruby-rack/rack's utils.go (SecureCompare, CleanPathInfo,
ValidPath, SelectBestEncoding, ForwardedValues); bump the rack pin so they
resolve, then dispatch through the existing value-mapping helpers:

- secure_compare(a, b)              -> SecureCompare      (constant-time bool)
- clean_path_info(path)             -> CleanPathInfo      (traversal-safe)
- valid_path?(path)                 -> ValidPath          (bool)
- select_best_encoding(avail, acc)  -> SelectBestEncoding (MRI arg order:
    available list first, pre-parsed [name, quality] accept list second;
    nil when nothing acceptable)
- forwarded_values(header)          -> ForwardedValues    (RFC 7239; symbol
    keys in header order, nil on a falsy/disallowed header)

ForwardedValues returns an unordered map[string][]string, so the binding
reconstructs MRI's header-appearance key order with rackForwardedOrder
(mirrors the Go tokeniser, names only) before materialising the symbol-keyed
Hash. New helpers rackFloat / rackQValues / rackForwardedArg carry the value
mapping.

Verified byte-identical to MRI 4.0.5 + rack 3.2.6 through the rbgo binary for
all five (including reordered / quoted / IPv6 / duplicate-key / empty-header
Forwarded cases). 100% coverage on rack.go and rack_helpers.go.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant