Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
JVM could garbage-collect llhttp's native callback trampolines while a parser
was still in use, after which `llhttp_execute` succeeded without invoking any
callbacks. Callback procs are now retained for the lifetime of the parser.
- `Response::Status` now implements the numeric coercion protocol (instance
`#coerce`), so range patterns (`case response in status: 500..599`) and
Integer-first comparisons (`500 <=> status`) work. Previously `Integer#<=>`
returned nil for `Status` operands, so range patterns silently never
matched. ([#847])

## [6.0.4] - 2026-07-14

Expand Down Expand Up @@ -330,6 +335,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#785]: https://github.com/httprb/http/issues/785
[#826]: https://github.com/httprb/http/issues/826
[#841]: https://github.com/httprb/http/pull/841
[#847]: https://github.com/httprb/http/pull/847
[unreleased]: https://github.com/httprb/http/compare/v6.0.4...HEAD
[6.0.4]: https://github.com/httprb/http/compare/v6.0.3...v6.0.4
[6.0.3]: https://github.com/httprb/http/compare/v6.0.2...v6.0.3
Expand Down
16 changes: 16 additions & 0 deletions lib/http/response/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ def <=>(other)
code <=> other.to_i
end

# Numeric coercion protocol, letting Integer compare against Status
#
# Makes `Integer#<=>` (and thus `Range#===`) work with a Status,
# so range patterns like `in status: 500..599` match.
#
# @example
# 500 <=> Status.new(503) # => -1
# (500..599) === Status.new(503) # => true
#
# @param [Numeric] other
# @return [Array(Numeric, Integer)]
# @api public
def coerce(other)
[other, code]
end

# Hash value based on status code
#
# @example
Expand Down
1 change: 1 addition & 0 deletions sig/http.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,7 @@ module HTTP
def to_i: () -> Integer
def to_int: () -> Integer
def <=>: (Numeric | Status other) -> Integer?
def coerce: (Numeric other) -> [Numeric, Integer]
def hash: () -> Integer
def reason: () -> String?
def to_s: () -> String
Expand Down
19 changes: 19 additions & 0 deletions test/http/response/status_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,25 @@ def test_spaceship_compares_with_objects_that_respond_to_to_i_but_not_to_int
assert_equal 1, HTTP::Response::Status.new(200) <=> "abc"
end

# ---------------------------------------------------------------------------
# #coerce
# ---------------------------------------------------------------------------
def test_coerce_returns_pair_comparable_with_integers
assert_equal [500, 503], HTTP::Response::Status.new(503).coerce(500)
end

def test_integer_spaceship_compares_with_status
assert_equal(-1, 500 <=> HTTP::Response::Status.new(503))
end

def test_range_case_equality_matches_status_within_range
assert_operator 500..599, :===, HTTP::Response::Status.new(503)
end

def test_range_case_equality_does_not_match_status_outside_range
refute_operator 500..599, :===, HTTP::Response::Status.new(404)
end

# ---------------------------------------------------------------------------
# #==
# ---------------------------------------------------------------------------
Expand Down
12 changes: 12 additions & 0 deletions test/http/response_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ def test_deconstruct_keys_supports_hash_pattern_matching
assert matched
end

def test_deconstruct_keys_supports_range_pattern_on_status
response = build_response(status: 503)
matched = case response
in { status: 500..599 }
true
else
false
end

assert matched
end

# ---------------------------------------------------------------------------
# #deconstruct
# ---------------------------------------------------------------------------
Expand Down
Loading