Skip to content

Commit 84eb253

Browse files
authored
Add Faraday#Deprecate to 1.x (#1438)
1 parent 824423c commit 84eb253

4 files changed

Lines changed: 263 additions & 5 deletions

File tree

lib/faraday/connection.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require 'faraday/deprecate'
4+
35
module Faraday
46
# Connection objects manage the default properties and the middleware
57
# stack for fulfilling an HTTP request.
@@ -297,14 +299,12 @@ def #{method}(url = nil, body = nil, headers = nil, &block)
297299
#
298300
# @return [void]
299301
def basic_auth(login, pass)
300-
warn <<~TEXT
301-
WARNING: `Faraday::Connection#basic_auth` is deprecated; it will be removed in version 2.0.
302-
While initializing your connection, use `#request(:basic_auth, ...)` instead.
303-
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
304-
TEXT
305302
set_authorization_header(:basic_auth, login, pass)
306303
end
307304

305+
extend Faraday::Deprecate
306+
deprecate :basic_auth, '#request(:basic_auth, ...)', '2.0'
307+
308308
# Sets up the Authorization header with the given token.
309309
#
310310
# @param token [String]

lib/faraday/deprecate.rb

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# frozen_string_literal: true
2+
3+
module Faraday
4+
# @param new_klass [Class] new Klass to use
5+
#
6+
# @return [Class] A modified version of new_klass that warns on
7+
# usage about deprecation.
8+
# @see Faraday::Deprecate
9+
module DeprecatedClass
10+
def self.proxy_class(origclass, ver = '1.0')
11+
proxy = Class.new(origclass) do
12+
const_set('ORIG_CLASS', origclass)
13+
14+
class << self
15+
extend Faraday::Deprecate
16+
17+
def ===(other)
18+
(superclass == const_get('ORIG_CLASS') && other.is_a?(superclass)) || super
19+
end
20+
end
21+
end
22+
proxy.singleton_class.send(:deprecate, :new, "#{origclass}.new", ver)
23+
proxy.singleton_class.send(:deprecate, :inherited, origclass.name, ver)
24+
proxy
25+
end
26+
end
27+
28+
# Deprecation using semver instead of date, based on Gem::Deprecate
29+
# Provides a single method +deprecate+ to be used to declare when
30+
# something is going away.
31+
#
32+
# class Legacy
33+
# def self.klass_method
34+
# # ...
35+
# end
36+
#
37+
# def instance_method
38+
# # ...
39+
# end
40+
#
41+
# extend Faraday::Deprecate
42+
# deprecate :instance_method, "X.z", '1.0'
43+
#
44+
# class << self
45+
# extend Faraday::Deprecate
46+
# deprecate :klass_method, :none, '1.0'
47+
# end
48+
# end
49+
module Deprecate
50+
def self.skip # :nodoc:
51+
@skip ||= begin
52+
case ENV['FARADAY_DEPRECATE'].to_s.downcase
53+
when '1', 'warn' then :warn
54+
else :skip
55+
end
56+
end
57+
@skip == :skip
58+
end
59+
60+
def self.skip=(value) # :nodoc:
61+
@skip = value ? :skip : :warn
62+
end
63+
64+
# Temporarily turn off warnings. Intended for tests only.
65+
def skip_during
66+
original = Faraday::Deprecate.skip
67+
Faraday::Deprecate.skip = true
68+
yield
69+
ensure
70+
Faraday::Deprecate.skip = original
71+
end
72+
73+
# Simple deprecation method that deprecates +name+ by wrapping it up
74+
# in a dummy method. It warns on each call to the dummy method
75+
# telling the user of +repl+ (unless +repl+ is :none) and the
76+
# semver that it is planned to go away.
77+
# @param name [Symbol] the method symbol to deprecate
78+
# @param repl [#to_s, :none] the replacement to use, when `:none` it will
79+
# alert the user that no replacemtent is present.
80+
# @param ver [String] the semver the method will be removed.
81+
def deprecate(name, repl, ver)
82+
class_eval do
83+
gem_ver = Gem::Version.new(ver)
84+
old = "_deprecated_#{name}"
85+
alias_method old, name
86+
define_method name do |*args, &block|
87+
mod = is_a? Module
88+
target = mod ? "#{self}." : "#{self.class}#"
89+
target_message = if name == :inherited
90+
"Inheriting #{self}"
91+
else
92+
"#{target}#{name}"
93+
end
94+
95+
msg = [
96+
"NOTE: #{target_message} is deprecated",
97+
repl == :none ? ' with no replacement' : "; use #{repl} instead. ",
98+
"It will be removed in or after version #{gem_ver}",
99+
"\n#{target}#{name} called from #{Gem.location_of_caller.join(':')}"
100+
]
101+
warn "#{msg.join}." unless Faraday::Deprecate.skip
102+
send old, *args, &block
103+
end
104+
end
105+
end
106+
107+
module_function :deprecate, :skip_during
108+
end
109+
end

spec/faraday/deprecate_spec.rb

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe Faraday::DeprecatedClass do
4+
class SampleClass < StandardError
5+
attr_accessor :foo
6+
7+
def initialize(foo = nil)
8+
@foo = foo || :foo
9+
end
10+
end
11+
12+
SampleDeprecatedClass = Faraday::DeprecatedClass.proxy_class(SampleClass)
13+
14+
it 'does not raise error for deprecated classes but prints an error message' do
15+
error_message, foobar = with_warn_squelching { SampleDeprecatedClass.new(:foo_bar) }
16+
expect(foobar).to be_a(SampleClass)
17+
expect(foobar.foo).to eq(:foo_bar)
18+
expect(error_message).to match(
19+
Regexp.new(
20+
'NOTE: SampleDeprecatedClass.new is deprecated; '\
21+
'use SampleClass.new instead. It will be removed in or after version 1.0'
22+
)
23+
)
24+
end
25+
26+
it 'does not raise an error for inherited error-namespaced classes but prints an error message' do
27+
error_message, = with_warn_squelching { Class.new(SampleDeprecatedClass) }
28+
29+
expect(error_message).to match(
30+
Regexp.new(
31+
'NOTE: Inheriting SampleDeprecatedClass is deprecated; '\
32+
'use SampleClass instead. It will be removed in or after version 1.0'
33+
)
34+
)
35+
end
36+
37+
it 'allows backward-compatible class to be subclassed' do
38+
expect do
39+
with_warn_squelching { Class.new(SampleDeprecatedClass) }
40+
end.not_to raise_error
41+
end
42+
43+
it 'allows rescuing of a current error with a deprecated error' do
44+
expect { raise SampleClass, nil }.to raise_error(SampleDeprecatedClass)
45+
end
46+
47+
it 'allows rescuing of a current error with a current error' do
48+
expect { raise SampleClass, nil }.to raise_error(SampleClass)
49+
end
50+
51+
it 'allows rescuing of a deprecated error with a deprecated error' do
52+
expect { raise SampleDeprecatedClass, nil }.to raise_error(SampleDeprecatedClass)
53+
end
54+
55+
it 'allows rescuing of a deprecated error with a current error' do
56+
expect { raise SampleDeprecatedClass, nil }.to raise_error(SampleClass)
57+
end
58+
59+
describe 'match behavior' do
60+
class SampleDeprecatedClassA < SampleDeprecatedClass; end
61+
class SampleDeprecatedClassB < SampleDeprecatedClass; end
62+
63+
class SampleDeprecatedClassAX < SampleDeprecatedClassA; end
64+
65+
class SampleClassA < SampleClass; end
66+
67+
describe 'undeprecated class' do
68+
it 'is === to instance of deprecated class' do
69+
expect(SampleDeprecatedClass.new.is_a?(SampleClass)).to be true
70+
end
71+
72+
it 'is === to instance of subclass of deprecated class' do
73+
expect(SampleDeprecatedClassA.new.is_a?(SampleClass)).to be true
74+
end
75+
76+
it 'is === to instance of subclass of subclass of deprecated class' do
77+
expect(SampleDeprecatedClassAX.new.is_a?(SampleClass)).to be true
78+
end
79+
end
80+
81+
describe 'subclass of undeprecated class' do
82+
it 'is not === to instance of undeprecated class' do
83+
expect(SampleClass.new.is_a?(SampleClassA)).to be false
84+
end
85+
86+
it 'is not === to instance of deprecated class' do
87+
expect(SampleDeprecatedClass.new.is_a?(SampleClassA)).to be false
88+
end
89+
end
90+
91+
describe 'deprecated class' do
92+
it 'is === to instance of undeprecated class' do
93+
expect(SampleDeprecatedClass.new.is_a?(SampleClass)).to be true
94+
end
95+
96+
it 'is === to instance of subclass of undeprecated class' do
97+
expect(SampleClassA.superclass == SampleDeprecatedClass.superclass).to be true
98+
end
99+
100+
it 'is === to instance of subclass of deprecated class' do
101+
expect(SampleDeprecatedClassA.new.is_a?(SampleDeprecatedClass)).to be true
102+
end
103+
104+
it 'is === to instance of subclass of subclass of deprecated class' do
105+
expect(SampleDeprecatedClassAX.new.is_a?(SampleDeprecatedClass)).to be true
106+
end
107+
end
108+
109+
describe 'subclass of deprecated class' do
110+
it 'is not === to instance of subclass of undeprecated class' do
111+
expect(SampleClass.new.is_a?(SampleDeprecatedClassA)).to be false
112+
end
113+
114+
it 'is not === to instance of another subclass of deprecated class' do
115+
expect(SampleDeprecatedClassB.new.is_a?(SampleDeprecatedClassA)).to be false
116+
end
117+
118+
it 'is === to instance of its subclass' do
119+
expect(SampleDeprecatedClassAX.new.is_a?(SampleDeprecatedClassA)).to be true
120+
end
121+
122+
it 'is === to instance of deprecated class' do
123+
expect(SampleDeprecatedClassB.new.is_a?(SampleDeprecatedClass)).to be true
124+
end
125+
end
126+
127+
describe 'subclass of subclass of deprecated class' do
128+
it 'is not === to instance of subclass of another subclass of deprecated class' do
129+
expect(SampleDeprecatedClassB.new.is_a?(SampleDeprecatedClassAX)).to be false
130+
end
131+
132+
it 'is not === to instance of its superclass' do
133+
expect(SampleDeprecatedClass.new.is_a?(SampleDeprecatedClassA)).to be false
134+
end
135+
end
136+
end
137+
138+
def with_warn_squelching
139+
stderr_catcher = StringIO.new
140+
original_stderr = $stderr
141+
$stderr = stderr_catcher
142+
result = yield if block_given?
143+
[stderr_catcher.tap(&:rewind).string, result]
144+
ensure
145+
$stderr = original_stderr
146+
end
147+
end

spec/spec_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838

3939
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
4040

41+
Faraday::Deprecate.skip = false
42+
4143
RSpec.configure do |config|
4244
# rspec-expectations config goes here. You can use an alternate
4345
# assertion/expectation library such as wrong or the stdlib/minitest

0 commit comments

Comments
 (0)