[go: nahoru, domu]

Skip to content

Commit

Permalink
Merge pull request #46049 from jonathanhefner/add-rails_application_d…
Browse files Browse the repository at this point in the history
…eprecators

Add `Rails.application.deprecators`
  • Loading branch information
jonathanhefner committed Oct 5, 2022
2 parents 007b040 + d240e8a commit 32aaf11
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 8 deletions.
3 changes: 2 additions & 1 deletion activesupport/lib/active_support/deprecation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Deprecation
require "active_support/deprecation/constant_accessor"
require "active_support/deprecation/method_wrappers"
require "active_support/deprecation/proxy_wrappers"
require "active_support/deprecation/deprecators"
require "active_support/core_ext/module/deprecation"
require "concurrent/atomic/thread_local_var"

Expand All @@ -44,7 +45,7 @@ def initialize(deprecation_horizon = "7.2", gem_name = "Rails")
# By default, warnings are not silenced and debugging is off.
self.silenced = false
self.debug = false
@silenced_thread = Concurrent::ThreadLocalVar.new(false)
@silence_counter = Concurrent::ThreadLocalVar.new(0)
@explicitly_allowed_warnings = Concurrent::ThreadLocalVar.new(nil)
end
end
Expand Down
96 changes: 96 additions & 0 deletions activesupport/lib/active_support/deprecation/deprecators.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# frozen_string_literal: true

module ActiveSupport
class Deprecation
# A managed collection of deprecators. Configuration methods, such as
# #behavior=, affect all deprecators in the collection. Additionally, the
# #silence method silences all deprecators in the collection for the
# duration of a given block.
class Deprecators
def initialize
@options = {}
@deprecators = {}
end

# Returns a deprecator added to this collection via #[]=.
def [](name)
@deprecators[name]
end

# Adds a given +deprecator+ to this collection. The deprecator will be
# immediately configured with any options previously set on this
# collection.
#
# deprecators = ActiveSupport::Deprecation::Deprecators.new
# deprecators.debug = true
#
# foo_deprecator = ActiveSupport::Deprecation.new("2.0", "Foo")
# foo_deprecator.debug # => false
#
# deprecators[:foo] = foo_deprecator
# deprecators[:foo].debug # => true
# foo_deprecator.debug # => true
#
def []=(name, deprecator)
apply_options(deprecator)
@deprecators[name] = deprecator
end

# Iterates over all deprecators in this collection. If no block is given,
# returns an +Enumerator+.
def each(&block)
return to_enum(__method__) unless block
@deprecators.each_value(&block)
end

# Sets the silenced flag for all deprecators in this collection.
def silenced=(silenced)
set_option(:silenced, silenced)
end

# Sets the debug flag for all deprecators in this collection.
def debug=(debug)
set_option(:debug, debug)
end

# Sets the deprecation warning behavior for all deprecators in this
# collection.
#
# See ActiveSupport::Deprecation::Behavior#behavior=.
def behavior=(behavior)
set_option(:behavior, behavior)
end

# Sets the disallowed deprecation warning behavior for all deprecators in
# this collection.
#
# See ActiveSupport::Deprecation::Behavior#disallowed_behavior=.
def disallowed_behavior=(disallowed_behavior)
set_option(:disallowed_behavior, disallowed_behavior)
end

# Silences all deprecators in this collection for the duration of the
# given block.
#
# See ActiveSupport::Deprecation::Reporting#silence.
def silence(&block)
each { |deprecator| deprecator.begin_silence }
block.call
ensure
each { |deprecator| deprecator.end_silence }
end

private
def set_option(name, value)
@options[name] = value
each { |deprecator| deprecator.public_send("#{name}=", value) }
end

def apply_options(deprecator)
@options.each do |name, value|
deprecator.public_send("#{name}=", value)
end
end
end
end
end
21 changes: 16 additions & 5 deletions activesupport/lib/active_support/deprecation/reporting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,22 @@ def warn(message = nil, callstack = nil)
# end
# # => nil
def silence(&block)
@silenced_thread.bind(true, &block)
begin_silence
block.call
ensure
end_silence
end

def begin_silence # :nodoc:
@silence_counter.value += 1
end

def end_silence # :nodoc:
@silence_counter.value -= 1
end

def silenced
@silenced || @silence_counter.value.nonzero?
end

# Allow previously disallowed deprecation warnings within the block.
Expand Down Expand Up @@ -79,10 +94,6 @@ def allow(allowed_warnings = :all, if: true, &block)
end
end

def silenced
@silenced || @silenced_thread.value
end

def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
caller_backtrace ||= caller_locations(2)
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
Expand Down
130 changes: 130 additions & 0 deletions activesupport/test/deprecation/deprecators_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# frozen_string_literal: true

require_relative "../abstract_unit"

class DeprecationTest < ActiveSupport::TestCase
setup do
@deprecator_names = [:fubar, :foo, :bar]
@deprecators = ActiveSupport::Deprecation::Deprecators.new
@deprecator_names.each do |name|
@deprecators[name] = ActiveSupport::Deprecation.new("2.0", name.to_s)
end
end

test "#[] gets an individual deprecator" do
@deprecator_names.each do |name|
assert_equal name.to_s, @deprecators[name].gem_name
end
end

test "#each iterates over each deprecator" do
gem_names = []
@deprecators.each { |deprecator| gem_names << deprecator.gem_name }

assert_equal @deprecator_names.map(&:to_s).sort, gem_names.sort
end

test "#each without block returns an Enumerator" do
assert_kind_of Enumerator, @deprecators.each
assert_equal @deprecator_names.map(&:to_s).sort, @deprecators.each.map(&:gem_name).sort
end

test "#silenced= applies to each deprecator" do
@deprecators.each { |deprecator| assert_not_predicate deprecator, :silenced }

@deprecators.silenced = true
@deprecators.each { |deprecator| assert_predicate deprecator, :silenced }

@deprecators.silenced = false
@deprecators.each { |deprecator| assert_not_predicate deprecator, :silenced }
end

test "#debug= applies to each deprecator" do
@deprecators.each { |deprecator| assert_not_predicate deprecator, :debug }

@deprecators.debug = true
@deprecators.each { |deprecator| assert_predicate deprecator, :debug }

@deprecators.debug = false
@deprecators.each { |deprecator| assert_not_predicate deprecator, :debug }
end

test "#behavior= applies to each deprecator" do
callback = proc { }

@deprecators.behavior = callback
@deprecators.each { |deprecator| assert_equal [callback], deprecator.behavior }
end

test "#disallowed_behavior= applies to each deprecator" do
callback = proc { }

@deprecators.disallowed_behavior = callback
@deprecators.each { |deprecator| assert_equal [callback], deprecator.disallowed_behavior }
end

test "#silence silences each deprecator" do
@deprecators.each { |deprecator| assert_not_silencing(deprecator) }

@deprecators.silence do
@deprecators.each { |deprecator| assert_silencing(deprecator) }
end

@deprecators.each { |deprecator| assert_not_silencing(deprecator) }
end

test "#silence returns the result of the block" do
assert_equal 123, @deprecators.silence { 123 }
end

test "#silence ensures silencing is reverted after an error is raised" do
assert_raises do
@deprecators.silence { raise }
end

@deprecators.each { |deprecator| assert_not_silencing(deprecator) }
end

test "#silence blocks can be nested" do
@deprecators.each { |deprecator| assert_not_silencing(deprecator) }

@deprecators.silence do
@deprecators.each { |deprecator| assert_silencing(deprecator) }

@deprecators.silence do
@deprecators.each { |deprecator| assert_silencing(deprecator) }
end

@deprecators.each { |deprecator| assert_silencing(deprecator) }
end

@deprecators.each { |deprecator| assert_not_silencing(deprecator) }
end

test "#silence only affects the current thread" do
@deprecators.silence do
@deprecators.each { |deprecator| assert_silencing(deprecator) }

Thread.new do
@deprecators.each { |deprecator| assert_not_silencing(deprecator) }

@deprecators.silence do
@deprecators.each { |deprecator| assert_silencing(deprecator) }
end

@deprecators.each { |deprecator| assert_not_silencing(deprecator) }
end.join

@deprecators.each { |deprecator| assert_silencing(deprecator) }
end
end

private
def assert_silencing(deprecator)
assert_not_deprecated(deprecator) { deprecator.warn }
end

def assert_not_silencing(deprecator)
assert_deprecated(/./, deprecator) { deprecator.warn }
end
end
16 changes: 14 additions & 2 deletions activesupport/test/deprecation_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,11 @@ def setup
flunk "assert_deprecated should match any warning in block, not just the last one"
end

test "assert_not_deprecated returns result of block" do
test "assert_not_deprecated returns the result of the block" do
assert_equal 123, assert_not_deprecated(@deprecator) { 123 }
end

test "assert_deprecated_returns result of block" do
test "assert_deprecated returns the result of the block" do
result = assert_deprecated("abc", @deprecator) do
@deprecator.warn "abc"
123
Expand All @@ -355,6 +355,18 @@ def setup
assert_not_deprecated(@deprecator) { @deprecator.warn }
end

test "silence returns the result of the block" do
assert_equal 123, @deprecator.silence { 123 }
end

test "silence ensures silencing is reverted after an error is raised" do
assert_raises do
@deprecator.silence { raise }
end

assert_deprecated(/./, @deprecator) { @deprecator.warn }
end

test "silence only affects the current thread" do
@deprecator.silence do
assert_not_deprecated(@deprecator) { @deprecator.warn }
Expand Down
34 changes: 34 additions & 0 deletions railties/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
* Add `Rails.application.deprecators` as a central point to manage deprecators
for an application.

Individual deprecators can be added and retrieved from the collection:

```ruby
Rails.application.deprecators[:my_gem] = ActiveSupport::Deprecation.new("2.0", "MyGem")
Rails.application.deprecators[:other_gem] = ActiveSupport::Deprecation.new("3.0", "OtherGem")
```

And the collection's configuration methods affect all deprecators in the
collection:
```ruby
Rails.application.deprecators.debug = true
Rails.application.deprecators[:my_gem].debug
# => true
Rails.application.deprecators[:other_gem].debug
# => true
```
Additionally, all deprecators in the collection can be silenced for the
duration of a given block:
```ruby
Rails.application.deprecators.silence do
Rails.application.deprecators[:my_gem].warn # => silenced (no warning)
Rails.application.deprecators[:other_gem].warn # => silenced (no warning)
end
```
*Jonathan Hefner*
* Move dbconsole logic to Active Record connection adapter.
Instead of hosting the connection logic in the command object, the
Expand Down
16 changes: 16 additions & 0 deletions railties/lib/rails/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "active_support/core_ext/object/blank"
require "active_support/key_generator"
require "active_support/message_verifiers"
require "active_support/deprecation"
require "active_support/encrypted_configuration"
require "active_support/hash_with_indifferent_access"
require "active_support/configuration_file"
Expand Down Expand Up @@ -113,6 +114,7 @@ def initialize(initial_variable_values = {}, &block)
@railties = nil
@key_generators = {}
@message_verifiers = nil
@deprecators = nil
@ran_load_hooks = false

@executor = Class.new(ActiveSupport::Executor)
Expand Down Expand Up @@ -219,6 +221,20 @@ def message_verifier(verifier_name)
message_verifiers[verifier_name]
end

# A managed collection of deprecators (ActiveSupport::Deprecation::Deprecators).
# The collection's configuration methods affect all deprecators in the
# collection. Additionally, the collection's +silence+ method silences all
# deprecators in the collection for the duration of a given block.
#
# The collection is prepopulated with a default deprecator, which can be
# accessed via <tt>deprecators[:rails]</tt>. More deprecators can be added
# via <tt>deprecators[name] = deprecator</tt>.
def deprecators
@deprecators ||= ActiveSupport::Deprecation::Deprecators.new.tap do |deprecators|
deprecators[:rails] = ActiveSupport::Deprecation.instance
end
end

# Convenience for loading config/foo.yml for the current Rails env.
#
# Examples:
Expand Down
Loading

0 comments on commit 32aaf11

Please sign in to comment.