Skip to content

Commit

Permalink
Merge pull request #37940 from cpruitt/activesupport-disallowed-depre…
Browse files Browse the repository at this point in the history
…cations

Introduce ActiveSupport Disallowed Deprecations
  • Loading branch information
eileencodes committed Jan 15, 2020
2 parents 36e6a51 + 10754f7 commit 464a86f
Show file tree
Hide file tree
Showing 10 changed files with 582 additions and 2 deletions.
3 changes: 3 additions & 0 deletions activesupport/lib/active_support/deprecation.rb
Expand Up @@ -17,6 +17,7 @@ class Deprecation
require "active_support/deprecation/instance_delegator"
require "active_support/deprecation/behaviors"
require "active_support/deprecation/reporting"
require "active_support/deprecation/disallowed"
require "active_support/deprecation/constant_accessor"
require "active_support/deprecation/method_wrappers"
require "active_support/deprecation/proxy_wrappers"
Expand All @@ -27,6 +28,7 @@ class Deprecation
include InstanceDelegator
include Behavior
include Reporting
include Disallowed
include MethodWrapper

# The version number in which the deprecated behavior will be removed, by default.
Expand All @@ -43,6 +45,7 @@ def initialize(deprecation_horizon = "6.2", gem_name = "Rails")
self.silenced = false
self.debug = false
@silenced_thread = Concurrent::ThreadLocalVar.new(false)
@explicitly_allowed_warnings = Concurrent::ThreadLocalVar.new(nil)
end
end
end
13 changes: 13 additions & 0 deletions activesupport/lib/active_support/deprecation/behaviors.rb
Expand Up @@ -67,6 +67,11 @@ def behavior
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
end

# Returns the current behavior for disallowed deprecations or if one isn't set, defaults to +:raise+.
def disallowed_behavior
@disallowed_behavior ||= [DEFAULT_BEHAVIORS[:raise]]
end

# Sets the behavior to the specified value. Can be a single value, array,
# or an object that responds to +call+.
#
Expand All @@ -92,6 +97,14 @@ def behavior=(behavior)
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
end

# Sets the behavior for disallowed deprecations (those configured by
# ActiveSupport::Deprecation.disallowed_warnings=) to the specified
# value. As with +behavior=+, this can be a single value, array, or an
# object that responds to +call+.
def disallowed_behavior=(behavior)
@disallowed_behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
end

private
def arity_coerce(behavior)
unless behavior.respond_to?(:call)
Expand Down
56 changes: 56 additions & 0 deletions activesupport/lib/active_support/deprecation/disallowed.rb
@@ -0,0 +1,56 @@
# frozen_string_literal: true

module ActiveSupport
class Deprecation
module Disallowed
# Sets the criteria used to identify deprecation messages which should be
# disallowed. Can be an array containing strings, symbols, or regular
# expressions. (Symbols are treated as strings). These are compared against
# the text of the generated deprecation warning.
#
# Additionally the scalar symbol +:all+ may be used to treat all
# deprecations as disallowed.
#
# Deprecations matching a substring or regular expression will be handled
# using the configured +ActiveSupport::Deprecation.disallowed_behavior+
# rather than +ActiveSupport::Deprecation.behavior+
attr_writer :disallowed_warnings

# Returns the configured criteria used to identify deprecation messages
# which should be treated as disallowed.
def disallowed_warnings
@disallowed_warnings ||= []
end

private
def deprecation_disallowed?(message)
disallowed = ActiveSupport::Deprecation.disallowed_warnings
return false if explicitly_allowed?(message)
return true if disallowed == :all
disallowed.any? do |rule|
case rule
when String, Symbol
message.include?(rule.to_s)
when Regexp
rule.match(message)
end
end
end

def explicitly_allowed?(message)
allowances = @explicitly_allowed_warnings.value
return false unless allowances
return true if allowances == :all
allowances = [allowances] unless allowances.kind_of?(Array)
allowances.any? do |rule|
case rule
when String, Symbol
message.include?(rule.to_s)
when Regexp
rule.match(message)
end
end
end
end
end
end
44 changes: 43 additions & 1 deletion activesupport/lib/active_support/deprecation/reporting.rb
Expand Up @@ -20,7 +20,11 @@ def warn(message = nil, callstack = nil)

callstack ||= caller_locations(2)
deprecation_message(callstack, message).tap do |m|
behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
if deprecation_disallowed?(message)
disallowed_behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
else
behavior.each { |b| b.call(m, callstack, deprecation_horizon, gem_name) }
end
end
end

Expand All @@ -37,6 +41,44 @@ def silence(&block)
@silenced_thread.bind(true, &block)
end

# Allow previously disallowed deprecation warnings within the block.
# <tt>allowed_warnings<tt> can be an array containing strings, symbols, or regular
# expressions. (Symbols are treated as strings). These are compared against
# the text of deprecation warning messages generated within the block.
# Matching warnings will be exempt from the rules set by
# +ActiveSupport::Deprecation.disallowed_warnings+
#
# The optional <tt>if:</tt> argument accepts a truthy/falsy value or an object that
# responds to <tt>.call</tt>. If truthy, then matching warnings will be allowed.
# If falsey then the method yields to the block without allowing the warning.
#
# ActiveSupport::Deprecation.disallowed_behavior = :raise
# ActiveSupport::Deprecation.disallowed_warnings = [
# "something broke"
# ]
#
# ActiveSupport::Deprecation.warn('something broke!')
# # => ActiveSupport::DeprecationException
#
# ActiveSupport::Deprecation.allow ['something broke'] do
# ActiveSupport::Deprecation.warn('something broke!')
# end
# # => nil
#
# ActiveSupport::Deprecation.allow ['something broke'], if: Rails.env.production? do
# ActiveSupport::Deprecation.warn('something broke!')
# end
# # => ActiveSupport::DeprecationException for dev/test, nil for production
def allow(allowed_warnings = :all, if: true, &block)
conditional = binding.local_variable_get(:if)
conditional = conditional.call if conditional.respond_to?(:call)
if conditional
@explicitly_allowed_warnings.bind(allowed_warnings, &block)
else
yield
end
end

def silenced
@silenced || @silenced_thread.value
end
Expand Down
8 changes: 8 additions & 0 deletions activesupport/lib/active_support/railtie.rb
Expand Up @@ -28,6 +28,14 @@ class Railtie < Rails::Railtie # :nodoc:
if deprecation = app.config.active_support.deprecation
ActiveSupport::Deprecation.behavior = deprecation
end

if disallowed_deprecation = app.config.active_support.disallowed_deprecation
ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
end

if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
end
end

# Sets the default value for Time.zone
Expand Down

0 comments on commit 464a86f

Please sign in to comment.