mirror of
https://github.com/lostisland/faraday.git
synced 2025-10-04 00:02:03 -04:00
Merge pull request #1054 from BobbyMcWho/create-faraday-deprecation-class
Create faraday deprecation class
This commit is contained in:
commit
a4e9bc2c19
97
lib/faraday/deprecate.rb
Normal file
97
lib/faraday/deprecate.rb
Normal file
@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @param new_klass [Class] new Klass to use
|
||||
#
|
||||
# @return [Class] A modified version of new_klass that warns on
|
||||
# usage about deprecation.
|
||||
# @see Faraday::Deprecate
|
||||
module DeprecatedClass
|
||||
def self.proxy_class(new_klass)
|
||||
Class.new(new_klass).tap do |k|
|
||||
class << k
|
||||
extend Faraday::Deprecate
|
||||
# Make this more human readable than #<Class:Faraday::ClientError>
|
||||
klass_name = superclass.to_s[/^#<Class:(\w*::\w*)>$/, 1]
|
||||
deprecate :new, "#{klass_name}.new", '1.0'
|
||||
deprecate :inherited, klass_name, '1.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Deprecation using semver instead of date, based on Gem::Deprecate
|
||||
# Provides a single method +deprecate+ to be used to declare when
|
||||
# something is going away.
|
||||
#
|
||||
# class Legacy
|
||||
# def self.klass_method
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# def instance_method
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# extend Faraday::Deprecate
|
||||
# deprecate :instance_method, "X.z", '1.0'
|
||||
#
|
||||
# class << self
|
||||
# extend Faraday::Deprecate
|
||||
# deprecate :klass_method, :none, '1.0'
|
||||
# end
|
||||
# end
|
||||
module Deprecate
|
||||
def self.skip # :nodoc:
|
||||
@skip ||= false
|
||||
end
|
||||
|
||||
def self.skip=(value) # :nodoc:
|
||||
@skip = value
|
||||
end
|
||||
|
||||
# Temporarily turn off warnings. Intended for tests only.
|
||||
def skip_during
|
||||
original = Faraday::Deprecate.skip
|
||||
Faraday::Deprecate.skip, = true
|
||||
yield
|
||||
ensure
|
||||
Faraday::Deprecate.skip = original
|
||||
end
|
||||
|
||||
# Simple deprecation method that deprecates +name+ by wrapping it up
|
||||
# in a dummy method. It warns on each call to the dummy method
|
||||
# telling the user of +repl+ (unless +repl+ is :none) and the
|
||||
# semver that it is planned to go away.
|
||||
# @param name [Symbol] the method symbol to deprecate
|
||||
# @param repl [#to_s, :none] the replacement to use, when `:none` it will
|
||||
# alert the user that no replacemtent is present.
|
||||
# @param ver [String] the semver the method will be removed.
|
||||
def deprecate(name, repl, ver)
|
||||
class_eval do
|
||||
old = "_deprecated_#{name}"
|
||||
alias_method old, name
|
||||
define_method name do |*args, &block|
|
||||
mod = is_a? Module
|
||||
target = mod ? "#{self}." : "#{self.class}#"
|
||||
target_message = if name == :inherited
|
||||
"Inheriting #{self}"
|
||||
else
|
||||
"#{target}#{name}"
|
||||
end
|
||||
|
||||
msg = [
|
||||
"NOTE: #{target_message} is deprecated",
|
||||
repl == :none ? ' with no replacement' : "; use #{repl} instead. ",
|
||||
"It will be removed in or after version #{Gem::Version.new(ver)}",
|
||||
"\n#{target}#{name} called from #{Gem.location_of_caller.join(':')}"
|
||||
]
|
||||
warn "#{msg.join}." unless Faraday::Deprecate.skip
|
||||
send old, *args, &block
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module_function :deprecate, :skip_during
|
||||
end
|
||||
end
|
@ -1,21 +1,25 @@
|
||||
module Faraday
|
||||
class Error < StandardError; end
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ClientError < Error
|
||||
require 'faraday/deprecate'
|
||||
|
||||
# Faraday namespace.
|
||||
module Faraday
|
||||
# Faraday error base class.
|
||||
class Error < StandardError
|
||||
attr_reader :response, :wrapped_exception
|
||||
|
||||
def initialize(ex, response = nil)
|
||||
def initialize(exc, response = nil)
|
||||
@wrapped_exception = nil
|
||||
@response = response
|
||||
|
||||
if ex.respond_to?(:backtrace)
|
||||
super(ex.message)
|
||||
@wrapped_exception = ex
|
||||
elsif ex.respond_to?(:each_key)
|
||||
super("the server responded with status #{ex[:status]}")
|
||||
@response = ex
|
||||
if exc.respond_to?(:backtrace)
|
||||
super(exc.message)
|
||||
@wrapped_exception = exc
|
||||
elsif exc.respond_to?(:each_key)
|
||||
super("the server responded with status #{exc[:status]}")
|
||||
@response = exc
|
||||
else
|
||||
super(ex.to_s)
|
||||
super(exc.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
@ -28,39 +32,80 @@ module Faraday
|
||||
end
|
||||
|
||||
def inspect
|
||||
inner = ''
|
||||
if @wrapped_exception
|
||||
inner << " wrapped=#{@wrapped_exception.inspect}"
|
||||
end
|
||||
if @response
|
||||
inner << " response=#{@response.inspect}"
|
||||
end
|
||||
if inner.empty?
|
||||
inner << " #{super}"
|
||||
end
|
||||
inner = +''
|
||||
inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
|
||||
inner << " response=#{@response.inspect}" if @response
|
||||
inner << " #{super}" if inner.empty?
|
||||
%(#<#{self.class}#{inner}>)
|
||||
end
|
||||
end
|
||||
|
||||
class ConnectionFailed < ClientError; end
|
||||
class ResourceNotFound < ClientError; end
|
||||
class ParsingError < ClientError; end
|
||||
# Faraday client error class. Represents 4xx status responses.
|
||||
class ClientError < Error
|
||||
end
|
||||
|
||||
class TimeoutError < ClientError
|
||||
def initialize(ex = nil)
|
||||
super(ex || "timeout")
|
||||
# Raised by Faraday::Response::RaiseError in case of a 400 response.
|
||||
class BadRequestError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 401 response.
|
||||
class UnauthorizedError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 403 response.
|
||||
class ForbiddenError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 404 response.
|
||||
class ResourceNotFound < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 407 response.
|
||||
class ProxyAuthError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 409 response.
|
||||
class ConflictError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 422 response.
|
||||
class UnprocessableEntityError < ClientError
|
||||
end
|
||||
|
||||
# Faraday server error class. Represents 5xx status responses.
|
||||
class ServerError < Error
|
||||
end
|
||||
|
||||
# A unified client error for timeouts.
|
||||
class TimeoutError < ServerError
|
||||
def initialize(exc = 'timeout', response = nil)
|
||||
super(exc, response)
|
||||
end
|
||||
end
|
||||
|
||||
class SSLError < ClientError
|
||||
# A unified error for failed connections.
|
||||
class ConnectionFailed < Error
|
||||
end
|
||||
|
||||
class RetriableResponse < ClientError; end
|
||||
|
||||
[:ClientError, :ConnectionFailed, :ResourceNotFound,
|
||||
:ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const|
|
||||
Error.const_set(const, Faraday.const_get(const))
|
||||
# A unified client error for SSL errors.
|
||||
class SSLError < Error
|
||||
end
|
||||
|
||||
# Raised by FaradayMiddleware::ResponseMiddleware
|
||||
class ParsingError < Error
|
||||
end
|
||||
|
||||
# Exception used to control the Retry middleware.
|
||||
#
|
||||
# @see Faraday::Request::Retry
|
||||
class RetriableResponse < Error
|
||||
end
|
||||
|
||||
%i[ClientError ConnectionFailed ResourceNotFound
|
||||
ParsingError TimeoutError SSLError RetriableResponse].each do |const|
|
||||
Error.const_set(
|
||||
const,
|
||||
DeprecatedClass.proxy_class(Faraday.const_get(const))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
84
spec/faraday/error_spec.rb
Normal file
84
spec/faraday/error_spec.rb
Normal file
@ -0,0 +1,84 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Faraday::ClientError do
|
||||
describe '.initialize' do
|
||||
subject { described_class.new(exception, response) }
|
||||
let(:response) { nil }
|
||||
|
||||
context 'with exception only' do
|
||||
let(:exception) { RuntimeError.new('test') }
|
||||
|
||||
it { expect(subject.wrapped_exception).to eq(exception) }
|
||||
it { expect(subject.response).to be_nil }
|
||||
it { expect(subject.message).to eq(exception.message) }
|
||||
it { expect(subject.backtrace).to eq(exception.backtrace) }
|
||||
it { expect(subject.inspect).to eq('#<Faraday::ClientError wrapped=#<RuntimeError: test>>') }
|
||||
end
|
||||
|
||||
context 'with response hash' do
|
||||
let(:exception) { { status: 400 } }
|
||||
|
||||
it { expect(subject.wrapped_exception).to be_nil }
|
||||
it { expect(subject.response).to eq(exception) }
|
||||
it { expect(subject.message).to eq('the server responded with status 400') }
|
||||
it { expect(subject.inspect).to eq('#<Faraday::ClientError response={:status=>400}>') }
|
||||
end
|
||||
|
||||
context 'with string' do
|
||||
let(:exception) { 'custom message' }
|
||||
|
||||
it { expect(subject.wrapped_exception).to be_nil }
|
||||
it { expect(subject.response).to be_nil }
|
||||
it { expect(subject.message).to eq('custom message') }
|
||||
it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: custom message>>') }
|
||||
end
|
||||
|
||||
context 'with anything else #to_s' do
|
||||
let(:exception) { %w[error1 error2] }
|
||||
|
||||
it { expect(subject.wrapped_exception).to be_nil }
|
||||
it { expect(subject.response).to be_nil }
|
||||
it { expect(subject.message).to eq('["error1", "error2"]') }
|
||||
it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: ["error1", "error2"]>>') }
|
||||
end
|
||||
|
||||
context 'maintains backward-compatibility until 1.0' do
|
||||
it 'does not raise an error for error-namespaced classes but prints an error message' do
|
||||
error_message, error = with_warn_squelching { Faraday::Error::ClientError.new('foo') }
|
||||
|
||||
expect(error).to be_a Faraday::ClientError
|
||||
expect(error_message).to match(
|
||||
Regexp.new(
|
||||
'NOTE: Faraday::Error::ClientError.new is deprecated; '\
|
||||
'use Faraday::ClientError.new instead. It will be removed in or after version 1.0'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not raise an error for inherited error-namespaced classes but prints an error message' do
|
||||
error_message, = with_warn_squelching { class E < Faraday::Error::ClientError; end }
|
||||
|
||||
expect(error_message).to match(
|
||||
Regexp.new(
|
||||
'NOTE: Inheriting Faraday::Error::ClientError is deprecated; '\
|
||||
'use Faraday::ClientError instead. It will be removed in or after version 1.0'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'allows backward-compatible class to be subclassed' do
|
||||
expect { class CustomError < Faraday::Error::ClientError; end }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
def with_warn_squelching
|
||||
stderr_catcher = StringIO.new
|
||||
original_stderr = $stderr
|
||||
$stderr = stderr_catcher
|
||||
result = yield if block_given?
|
||||
[stderr_catcher.tap(&:rewind).string, result]
|
||||
ensure
|
||||
$stderr = original_stderr
|
||||
end
|
||||
end
|
||||
end
|
@ -26,7 +26,7 @@ module Adapters
|
||||
conn = create_connection(:request => {:timeout => 1, :open_timeout => 1})
|
||||
begin
|
||||
conn.get '/slow'
|
||||
rescue Faraday::Error::ClientError
|
||||
rescue Faraday::Error::TimeoutError
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user