Raise specific error classes for 4xx and 5xx errors#839
Conversation
|
Why are the tests failing? I can't even tell |
|
@tarcieri Tests are all passing but exit non-zero because code coverage dropped below 100%. Also, RuboCop also found linting errors, Steep type check failed, and Mutant failed. Assuming those issues are fixed, I’m curious what you think about this proposal, in general? |
|
Yeah, happy to fix the Rubocop linter issues if the maintainers like the overall idea. It's fairly trivial (method/case statement too long). Happy to add more test coverage for each branch too. |
|
This seems fine to me as a general direction |
110e972 to
1a07d7a
Compare
|
I don't think we should explode amount of exceptions. To be quite frank I also think we should get rid of predicates in response status. Because
class StatusError
# ...
def deconstruct(...) = response.deconstruct(...)
def deconstruct_keys(...) = response.deconstruct_keys(...)
endThis will allow us writing code: begin
HTTP.post
rescue StatusError => e
case e
in status: 500..599
retry
in status: 429
sleep(1) # throttle
retry
else
fail
end
endEven without changes to StatusError: begin
HTTP.post(...)
rescue StatusError => e
case e.response
in status: 500..599
retry
in status: 429
sleep(1) # throttle
retry
else
fail
end
endOr without pattern matching: begin
HTTP.post(...)
rescue StatusError => e
case e.response.status
when 500..599
retry
when 429
sleep(1) # throttle
retry
else
fail
end
end |
Context/problem
When using the
http.rbgem in production code, I often find myself writing boilerplate code to wrap theStatusErrorthrown by the gem and raise a more specific error based on the response code.This is often in some client class or shared HTTP classes that can be used across the codebase. This works, but has a few issues:
responseinstance var to be useful. You end up re-implementingStatusErrorin the app, or you inherit directly from the gem class.Proposal (this PR)
I would like to propose that the HTTP gem enumerate the roughly ~40 or so 4xx and 5xx status codes. This would provide a clean interface that allows callers to catch errors at varying levels of granularity, while being fully backwards compatible.
To achieve this, I'd like to propose 2 subclasses of
StatusError:ClientError, an entry point for all 4xx errorsServerError, an entry point for all 5xx errorsBecause all specific error classes are ultimately instances of
StatusError, this should not break any existingrescueblocks. Users can opt-in to more granular errors as they see fit.Example migration
Existing code
After change
Existing code still works as-is
Would love any feedback or thoughts, thanks.