1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
# frozen_string_literal: true
require "rbconfig"
module ActiveSupport
class Deprecation
module Reporting
# Whether to print a message (silent mode)
attr_writer :silenced
# Name of gem where method is deprecated
attr_accessor :gem_name
# Outputs a deprecation warning to the output configured by
# ActiveSupport::Deprecation#behavior.
#
# ActiveSupport::Deprecation.new.warn('something broke!')
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
def warn(message = nil, callstack = nil)
return if silenced
callstack ||= caller_locations(2)
deprecation_message(callstack, message).tap do |full_message|
if deprecation_disallowed?(message)
disallowed_behavior.each { |b| b.call(full_message, callstack, self) }
else
behavior.each { |b| b.call(full_message, callstack, self) }
end
end
end
# Silence deprecation warnings within the block.
#
# deprecator = ActiveSupport::Deprecation.new
# deprecator.warn('something broke!')
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
#
# deprecator.silence do
# deprecator.warn('something broke!')
# end
# # => nil
def silence(&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.
# <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.
#
# deprecator = ActiveSupport::Deprecation.new
# deprecator.disallowed_behavior = :raise
# deprecator.disallowed_warnings = [
# "something broke"
# ]
#
# deprecator.warn('something broke!')
# # => ActiveSupport::DeprecationException
#
# deprecator.allow ['something broke'] do
# deprecator.warn('something broke!')
# end
# # => nil
#
# deprecator.allow ['something broke'], if: Rails.env.production? do
# deprecator.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 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|
warn(msg, caller_backtrace)
end
end
private
# Outputs a deprecation warning message
#
# deprecated_method_warning(:method_name)
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
# deprecated_method_warning(:method_name, :another_method)
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
# deprecated_method_warning(:method_name, "Optional message")
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
def deprecated_method_warning(method_name, message = nil)
warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
case message
when Symbol then "#{warning} (use #{message} instead)"
when String then "#{warning} (#{message})"
else warning
end
end
def deprecation_message(callstack, message = nil)
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
"DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
end
def deprecation_caller_message(callstack)
file, line, method = extract_callstack(callstack)
if file
if line && method
"(called from #{method} at #{file}:#{line})"
else
"(called from #{file}:#{line})"
end
end
end
def extract_callstack(callstack)
return [] if callstack.empty?
return _extract_callstack(callstack) if callstack.first.is_a? String
offending_line = callstack.find { |frame|
# Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
path = frame.absolute_path || frame.path
path && !ignored_callstack?(path)
} || callstack.first
[offending_line.path, offending_line.lineno, offending_line.label]
end
def _extract_callstack(callstack)
ActiveSupport.deprecator.warn(<<~MESSAGE)
Passing the result of `caller` to ActiveSupport::Deprecation#warn is deprecated and will be removed in Rails 8.0.
Please pass the result of `caller_locations` instead.
MESSAGE
offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
if offending_line
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
md.captures
else
offending_line
end
end
end
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
def ignored_callstack?(path)
path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
end
end
end
end
|