From 2c690a0f5b36896da9b003d4e24159a27ebd7f71 Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Mon, 25 Jul 2011 21:05:06 +0200 Subject: extend ActiveSupport::Deprecation with self, allow other objects to extend/include it also. test local deprecation deprecator object Test ActiveSupport::Deprecation when included --- activesupport/lib/active_support/deprecation.rb | 2 +- .../lib/active_support/deprecation/behaviors.rb | 64 +++++------ .../active_support/deprecation/method_wrappers.rb | 5 +- .../active_support/deprecation/proxy_wrappers.rb | 19 +-- .../lib/active_support/deprecation/reporting.rb | 117 ++++++++++--------- activesupport/test/deprecation_test.rb | 127 +++++++++++++++++++++ 6 files changed, 232 insertions(+), 102 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index e3b4a7240e..968ee1eaf8 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -16,4 +16,4 @@ module ActiveSupport self.silenced = false self.debug = false end -end \ No newline at end of file +end diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index fc962dcb57..c956560a99 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -2,46 +2,44 @@ require "active_support/notifications" module ActiveSupport module Deprecation - class << self - # Whether to print a backtrace along with the warning. - attr_accessor :debug + # Whether to print a backtrace along with the warning. + attr_accessor :debug - # Returns the current behavior or if one isn't set, defaults to +:stderr+ - def behavior - @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] - end + # Returns the current behavior or if one isn't set, defaults to +:stderr+ + def behavior + @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] + end - # Sets the behavior to the specified value. Can be a single value, array, or - # an object that responds to +call+. - # - # Available behaviors: - # - # [+stderr+] Log all deprecation warnings to $stderr. - # [+log+] Log all deprecation warnings to +Rails.logger+. - # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+. - # [+silence+] Do nothing. - # - # Setting behaviors only affects deprecations that happen after boot time. - # Deprecation warnings raised by gems are not affected by this setting because - # they happen before Rails boots up. - # - # ActiveSupport::Deprecation.behavior = :stderr - # ActiveSupport::Deprecation.behavior = [:stderr, :log] - # ActiveSupport::Deprecation.behavior = MyCustomHandler - # ActiveSupport::Deprecation.behavior = proc { |message, callstack| - # # custom stuff - # } - def behavior=(behavior) - @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } - end + # Sets the behavior to the specified value. Can be a single value, array, or + # an object that responds to +call+. + # + # Available behaviors: + # + # [+stderr+] Log all deprecation warnings to +$stderr+. + # [+log+] Log all deprecation warnings to +Rails.logger+. + # [+notify] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+. + # [+silence+] Do nothing. + # + # Setting behaviors only affects deprecations that happen after boot time. + # Deprecation warnings raised by gems are not affected by this setting because + # they happen before Rails boots up. + # + # ActiveSupport::Deprecation.behavior = :stderr + # ActiveSupport::Deprecation.behavior = [:stderr, :log] + # ActiveSupport::Deprecation.behavior = MyCustomHandler + # ActiveSupport::Deprecation.behavior = proc { |message, callstack| + # # custom stuff + # } + def behavior=(behavior) + @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } end # Default warning behaviors per Rails.env. DEFAULT_BEHAVIORS = { :stderr => Proc.new { |message, callstack| - $stderr.puts(message) - $stderr.puts callstack.join("\n ") if debug - }, + $stderr.puts(message) + $stderr.puts callstack.join("\n ") if debug + }, :log => Proc.new { |message, callstack| logger = if defined?(Rails) && Rails.logger diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index 257b70e34a..f4ad15cc63 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -32,8 +32,9 @@ module ActiveSupport target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation| target_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1) def #{target}_with_deprecation#{punctuation}(*args, &block) - ::ActiveSupport::Deprecation.warn( - ::ActiveSupport::Deprecation.deprecated_method_warning( + deprecator = respond_to?(:deprecator) ? deprecator() : ActiveSupport::Deprecation + deprecator.warn( + deprecator.deprecated_method_warning( :#{method_name}, #{options[method_name].inspect}), caller diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index a65fcafb44..382f2e88f0 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -26,9 +26,10 @@ module ActiveSupport end class DeprecatedObjectProxy < DeprecationProxy #:nodoc: - def initialize(object, message) + def initialize(object, message, deprecator = ActiveSupport::Deprecation) @object = object @message = message + @deprecator = deprecator end private @@ -37,15 +38,18 @@ module ActiveSupport end def warn(callstack, called, args) - ActiveSupport::Deprecation.warn(@message, callstack) + @deprecator.warn(@message, callstack) end end # Stand-in for @request, @attributes, @params, etc. # which emits deprecation warnings on any method call (except +inspect+). class DeprecatedInstanceVariableProxy < DeprecationProxy #:nodoc: - def initialize(instance, method, var = "@#{method}") - @instance, @method, @var = instance, method, var + def initialize(instance, method, var = :"@#{method}", deprecator = nil) + @instance = instance + @method = method + @var = var + @deprecator = deprecator || (@instance.respond_to?(:deprecator) ? @instance.deprecator : ActiveSupport::Deprecation) end private @@ -54,14 +58,15 @@ module ActiveSupport end def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) + @deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) end end class DeprecatedConstantProxy < DeprecationProxy #:nodoc:all - def initialize(old_const, new_const) + def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation) @old_const = old_const @new_const = new_const + @deprecator = deprecator end def class @@ -74,7 +79,7 @@ module ActiveSupport end def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) + @deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) end end end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index a1e9618084..69de79b93c 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -1,74 +1,73 @@ module ActiveSupport module Deprecation - class << self - attr_accessor :silenced + attr_accessor :silenced - # Outputs a deprecation warning to the output configured by - # ActiveSupport::Deprecation.behavior. - # - # ActiveSupport::Deprecation.warn("something broke!") - # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" - def warn(message = nil, callstack = caller) - return if silenced - deprecation_message(callstack, message).tap do |m| - behavior.each { |b| b.call(m, callstack) } - end + # Outputs a deprecation warning to the output configured by + # ActiveSupport::Deprecation.behavior. + # + # ActiveSupport::Deprecation.warn("something broke!") + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + def warn(message = nil, callstack = caller) + return if silenced + deprecation_message(callstack, message).tap do |m| + behavior.each { |b| b.call(m, callstack) } end + end - # Silence deprecation warnings within the block. - # - # ActiveSupport::Deprecation.warn("something broke!") - # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" - # - # ActiveSupport::Deprecation.silence do - # ActiveSupport::Deprecation.warn("something broke!") - # end - # # => nil - def silence - old_silenced, @silenced = @silenced, true - yield - ensure - @silenced = old_silenced - end + # Silence deprecation warnings within the block. + # + # ActiveSupport::Deprecation.warn("something broke!") + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + # + # ActiveSupport::Deprecation.silence do + # ActiveSupport::Deprecation.warn("something broke!") + # end + # # => nil + def silence + old_silenced, @silenced = @silenced, true + yield + ensure + @silenced = old_silenced + end - def deprecated_method_warning(method_name, message = nil) - warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}" - case message - when Symbol then "#{warning} (use #{message} instead)" - when String then "#{warning} (#{message})" - else warning - end + def deprecated_method_warning(method_name, message = nil) + warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}" + case message + when Symbol then warning << " (use #{message} instead)" + when String then warning << " (#{message})" end + warning + end - private - def deprecation_message(callstack, message = nil) - message ||= "You are using deprecated behavior which will be removed from the next major or minor release." - message += '.' unless message =~ /\.$/ - "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}" - end + private + + def deprecation_message(callstack, message = nil) + message ||= "You are using deprecated behavior which will be removed from the next major or minor release." + message += '.' unless message =~ /\.$/ + "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 + 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) - rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" - offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first - if offending_line - if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/) - md.captures - else - offending_line - end - end + def extract_callstack(callstack) + rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" + offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first + if offending_line + if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/) + md.captures + else + offending_line end + end end end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index e21f3efe36..308c7c3fe7 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -186,4 +186,131 @@ class DeprecationTest < ActiveSupport::TestCase def test_deprecation_with_explicit_message assert_deprecated(/you now need to do something extra for this one/) { @dtc.d } end + + def test_deprecation_in_other_module_does_not_interfere + messages = [] + + m = Module.new + m.extend ActiveSupport::Deprecation + m.behavior = Proc.new{|message, callstack| messages << message} + assert_not_deprecated do # not globally + assert_difference("messages.size") do # but locally + m.warn("warning") + end + end + end + + def test_deprecated_method_with_deprecator_implemented + deprecator = deprecator_with_messages + def deprecator.deprecated_method_warning(method, *params) + "deprecator.deprecated_method_warning.#{method}" + end + + deprecatee = Class.new() do + def method + end + deprecate :method + define_method(:deprecator){ deprecator } + end + + deprecatee.new.method + assert deprecator.messages.first.match("DEPRECATION WARNING: deprecator.deprecated_method_warning.method") + end + + def test_deprecated_constant_with_deprecator_given + deprecator = deprecator_with_messages + klass = Class.new() + klass.const_set(:OLD, ActiveSupport::Deprecation::DeprecatedConstantProxy.new('klass::OLD', 'Object', deprecator) ) + assert_difference("deprecator.messages.size") do + klass::OLD.to_s + end + end + + def test_deprecated_instance_variable_with_instance_deprecator + deprecator = deprecator_with_messages + + klass = Class.new() do + def initialize + @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request) + @_request = :a_request + end + def request; @_request end + def old_request; @request end + define_method(:deprecator) { deprecator } + end + + assert_difference("deprecator.messages.size") { klass.new.old_request.to_s } + + end + + def test_deprecated_instance_variable_with_given_deprecator + deprecator = deprecator_with_messages + + klass = Class.new() do + define_method(:initialize) do + @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator) + @_request = :a_request + end + def request; @_request end + def old_request; @request end + end + + assert_difference("deprecator.messages.size") { klass.new.old_request.to_s } + end + + def test_included_deprecation_module + klass = Class.new() do + attr_reader :last_message + include ActiveSupport::Deprecation + def deprecated_method + warn(deprecated_method_warning(:deprecated_method, "You are calling deprecated method")) + end + + private + + def deprecated_method_warning(method_name, message = nil) + message || "#{method_name} is deprecated and will be removed from This Library" + end + + def behavior + @behavior ||= [Proc.new { |message| @last_message = message }] + end + end + + object = klass.new + object.deprecated_method + assert_match(/You are calling deprecated method/, object.last_message) + end + + unless defined?(::MiniTest) + def test_assertion_failed_error_doesnt_spout_deprecation_warnings + error_class = Class.new(StandardError) do + def message + ActiveSupport::Deprecation.warn 'warning in error message' + super + end + end + + raise error_class.new('hmm') + + rescue => e + error = Test::Unit::Error.new('testing ur doodz', e) + assert_not_deprecated { error.message } + assert_nil @last_message + end + end + + + private + + + def deprecator_with_messages + deprecator = Object.new + deprecator.extend(ActiveSupport::Deprecation) + deprecator.behavior = Proc.new{|message, callstack| deprecator.messages << message} + def deprecator.messages + @messages ||= [] + end + deprecator + end end -- cgit v1.2.3 From 71993c6f9770b1350aa41fe8c68f1dd2c7800403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Nie=C5=82acny?= Date: Thu, 13 Sep 2012 08:38:34 +0200 Subject: Change ActiveSupport::Deprecation to class. ActiveSupport::Deprecation is now a class rather than a module. You can get instance of ActiveSupport::Deprecation calling #instance method. ActiveSupport::Deprecation.instance But when you need to get new object od ActiveSupport::Deprecation you need to just call #new. @instance = ActiveSupport::Deprecation.new Since you can create a new object, you can change the version and the name of the library where the deprecator concerned. ActiveSupport::Deprecation.new('2.0', 'MyGem') If you need use another deprecator instance you can select it in the options of deprecate method. deprecate :method, :deprecator => deprecator_instance Documentation has been updated. --- activesupport/CHANGELOG.md | 32 +++++ .../active_support/core_ext/module/deprecation.rb | 34 +++++- activesupport/lib/active_support/deprecation.rb | 39 ++++-- .../lib/active_support/deprecation/behaviors.rb | 98 +++++++-------- .../deprecation/instance_delegator.rb | 24 ++++ .../active_support/deprecation/method_wrappers.rb | 71 +++++------ .../active_support/deprecation/proxy_wrappers.rb | 59 +++++++-- .../lib/active_support/deprecation/reporting.rb | 135 ++++++++++++--------- activesupport/test/deprecation_test.rb | 134 +++++++++++--------- 9 files changed, 405 insertions(+), 221 deletions(-) create mode 100644 activesupport/lib/active_support/deprecation/instance_delegator.rb (limited to 'activesupport') diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 3914f4dc4c..6dff6de83a 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,37 @@ ## Rails 4.0.0 (unreleased) ## +* ActiveSupport::Deprecation is now a class. It is possible to create an instance + of deprecator. Backwards compatibility has been preserved. + + You can choose which instance of the deprecator will be used. + + deprecate :method_name, :deprecator => deprecator_instance + + You can use ActiveSupport::Deprecation in your gem. + + require 'active_support/deprecation' + require 'active_support/core_ext/module/deprecation' + + class MyGem + def old_method + end + deprecate :old_method => :new_method, :deprecator => deprecator + + def new_method + end + + def self.deprecator + ActiveSupport::Deprecation.new('2.0', 'MyGem') + end + end + + MyGem.new.old_method + + DEPRECATION WARNING: old_method is deprecated and will be removed from MyGem 2.0 + (use new_method instead). (called from
at file.rb:18) + + *Piotr Niełacny & Robert Pankowecki* + * `ERB::Util.html_escape` encodes single quote as `#39`. Decimal form has better support in old browsers. *Kalys Osmonov* * `ActiveSupport::Callbacks`: deprecate monkey patch of object callbacks. diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb index 9e77ac3c45..9affd38baa 100644 --- a/activesupport/lib/active_support/core_ext/module/deprecation.rb +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -1,10 +1,42 @@ require 'active_support/deprecation/method_wrappers' class Module - # Declare that a method has been deprecated. # deprecate :foo # deprecate :bar => 'message' # deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!' + # + # You can use custom deprecator instance + # deprecate :foo, :deprecator => MyLib::Deprecator.new + # deprecate :foo, :bar => "warning!", :deprecator => MyLib::Deprecator.new + # + # \Custom deprecators must respond to one method + # [deprecation_warning(deprecated_method_name, message, caller_backtrace)] will be called with the deprecated + # method name, the message it was declared + # with and caller_backtrace. Implement + # whatever warning behavior you like here. + # + # Example + # class MyLib::Deprecator + # + # def deprecation_warning(deprecated_method_name, message, caller_backtrace) + # message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}" + # Kernel.warn message + # end + # + # end + # + # module MyLib + # mattr_accessor :deprecator + # self.deprecator = Deprecator.new + # end + # + # When we deprecate method + # class MyLib::Bar + # deprecate :foo => "this is very old method", :deprecator => MyLib.deprecator + # end + # + # It will build deprecation message and invoke deprecator warning by calling + # MyLib.deprecator.deprecation_warning(:foo, "this is a very old method", caller) def deprecate(*method_names) ActiveSupport::Deprecation.deprecate_methods(self, *method_names) end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 968ee1eaf8..678cbbcbf2 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -1,19 +1,40 @@ require 'active_support/core_ext/module/deprecation' +require 'active_support/deprecation/instance_delegator' require 'active_support/deprecation/behaviors' require 'active_support/deprecation/reporting' require 'active_support/deprecation/method_wrappers' require 'active_support/deprecation/proxy_wrappers' +require 'singleton' module ActiveSupport - module Deprecation - class << self - # The version the deprecated behavior will be removed, by default. - attr_accessor :deprecation_horizon - end - self.deprecation_horizon = '4.1' + # \Deprecation specifies the API used by Rails to deprecate + # methods, instance variables, objects and constants. + # The API depends on four methods: + # + # * +initialize+ which expects two parameters + # described below; + class Deprecation + include Singleton + include InstanceDelegator + include Behavior + include Reporting + include MethodWrapper + + # The version the deprecated behavior will be removed, by default. + attr_accessor :deprecation_horizon - # By default, warnings are not silenced and debugging is off. - self.silenced = false - self.debug = false + # It accepts two parameters on initialization. The first is an version of library + # and the second is an library name + # + # == Example + # + # ActiveSupport::Deprecation.new('2.0', 'MyLibrary') + def initialize(deprecation_horizon = '4.1', gem_name = 'Rails') + self.gem_name = gem_name + self.deprecation_horizon = deprecation_horizon + # By default, warnings are not silenced and debugging is off. + self.silenced = false + self.debug = false + end end end diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index c956560a99..b4c8a0e92d 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -1,39 +1,7 @@ require "active_support/notifications" module ActiveSupport - module Deprecation - # Whether to print a backtrace along with the warning. - attr_accessor :debug - - # Returns the current behavior or if one isn't set, defaults to +:stderr+ - def behavior - @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] - end - - # Sets the behavior to the specified value. Can be a single value, array, or - # an object that responds to +call+. - # - # Available behaviors: - # - # [+stderr+] Log all deprecation warnings to +$stderr+. - # [+log+] Log all deprecation warnings to +Rails.logger+. - # [+notify] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+. - # [+silence+] Do nothing. - # - # Setting behaviors only affects deprecations that happen after boot time. - # Deprecation warnings raised by gems are not affected by this setting because - # they happen before Rails boots up. - # - # ActiveSupport::Deprecation.behavior = :stderr - # ActiveSupport::Deprecation.behavior = [:stderr, :log] - # ActiveSupport::Deprecation.behavior = MyCustomHandler - # ActiveSupport::Deprecation.behavior = proc { |message, callstack| - # # custom stuff - # } - def behavior=(behavior) - @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } - end - + class Deprecation # Default warning behaviors per Rails.env. DEFAULT_BEHAVIORS = { :stderr => Proc.new { |message, callstack| @@ -41,21 +9,55 @@ module ActiveSupport $stderr.puts callstack.join("\n ") if debug }, :log => Proc.new { |message, callstack| - logger = - if defined?(Rails) && Rails.logger - Rails.logger - else - require 'active_support/logger' - ActiveSupport::Logger.new($stderr) - end - logger.warn message - logger.debug callstack.join("\n ") if debug - }, - :notify => Proc.new { |message, callstack| - ActiveSupport::Notifications.instrument("deprecation.rails", - :message => message, :callstack => callstack) - }, - :silence => Proc.new { |message, callstack| } + logger = + if defined?(Rails) && Rails.logger + Rails.logger + else + require 'active_support/logger' + ActiveSupport::Logger.new($stderr) + end + logger.warn message + logger.debug callstack.join("\n ") if debug + }, + :notify => Proc.new { |message, callstack| + ActiveSupport::Notifications.instrument("deprecation.rails", + :message => message, :callstack => callstack) + }, + :silence => Proc.new { |message, callstack| } } + + module Behavior + # Whether to print a backtrace along with the warning. + attr_accessor :debug + + # Returns the current behavior or if one isn't set, defaults to +:stderr+ + def behavior + @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] + end + + # Sets the behavior to the specified value. Can be a single value, array, or + # an object that responds to +call+. + # + # Available behaviors: + # + # [+stderr+] Log all deprecation warnings to +$stderr+. + # [+log+] Log all deprecation warnings to +Rails.logger+. + # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+. + # [+silence+] Do nothing. + # + # Setting behaviors only affects deprecations that happen after boot time. + # Deprecation warnings raised by gems are not affected by this setting because + # they happen before Rails boots up. + # + # ActiveSupport::Deprecation.behavior = :stderr + # ActiveSupport::Deprecation.behavior = [:stderr, :log] + # ActiveSupport::Deprecation.behavior = MyCustomHandler + # ActiveSupport::Deprecation.behavior = proc { |message, callstack| + # # custom stuff + # } + def behavior=(behavior) + @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } + end + end end end diff --git a/activesupport/lib/active_support/deprecation/instance_delegator.rb b/activesupport/lib/active_support/deprecation/instance_delegator.rb new file mode 100644 index 0000000000..ff240cb887 --- /dev/null +++ b/activesupport/lib/active_support/deprecation/instance_delegator.rb @@ -0,0 +1,24 @@ +require 'active_support/core_ext/kernel/singleton_class' +require 'active_support/core_ext/module/delegation' + +module ActiveSupport + class Deprecation + module InstanceDelegator + def self.included(base) + base.extend(ClassMethods) + base.public_class_method :new + end + + module ClassMethods + def include(included_module) + included_module.instance_methods.each { |m| method_added(m) } + super + end + + def method_added(method_name) + singleton_class.delegate(method_name, to: :instance) + end + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index f4ad15cc63..d3907b03e5 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -2,46 +2,41 @@ require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/array/extract_options' module ActiveSupport - module Deprecation - # Declare that a method has been deprecated. - # - # module Fred - # extend self - # - # def foo; end - # def bar; end - # def baz; end - # end - # - # ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead') - # # => [:foo, :bar, :baz] - # - # Fred.foo - # # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1." - # - # Fred.bar - # # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)." - # - # Fred.baz - # # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)." - def self.deprecate_methods(target_module, *method_names) - options = method_names.extract_options! - method_names += options.keys + class Deprecation + module MethodWrapper + # Declare that a method has been deprecated. + # + # module Fred + # extend self + # + # def foo; end + # def bar; end + # def baz; end + # end + # + # ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead') + # # => [:foo, :bar, :baz] + # + # Fred.foo + # # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1." + # + # Fred.bar + # # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)." + # + # Fred.baz + # # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)." + def deprecate_methods(target_module, *method_names) + options = method_names.extract_options! + deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation.instance + method_names += options.keys - method_names.each do |method_name| - target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation| - target_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1) - def #{target}_with_deprecation#{punctuation}(*args, &block) - deprecator = respond_to?(:deprecator) ? deprecator() : ActiveSupport::Deprecation - deprecator.warn( - deprecator.deprecated_method_warning( - :#{method_name}, - #{options[method_name].inspect}), - caller - ) - send(:#{target}_without_deprecation#{punctuation}, *args, &block) + method_names.each do |method_name| + target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation| + target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block| + deprecator.deprecation_warning(method_name, options[method_name], caller) + send(:"#{target}_without_deprecation#{punctuation}", *args, &block) end - end_eval + end end end end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 382f2e88f0..2b8282c34e 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -1,7 +1,7 @@ require 'active_support/inflector/methods' module ActiveSupport - module Deprecation + class Deprecation class DeprecationProxy #:nodoc: def self.new(*args, &block) object = args.first @@ -25,8 +25,18 @@ module ActiveSupport end end - class DeprecatedObjectProxy < DeprecationProxy #:nodoc: - def initialize(object, message, deprecator = ActiveSupport::Deprecation) + # This DeprecatedObjectProxy transforms object to depracated object. + # + # Example + # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!") + # Example with custom deprecator + # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance) + # + # When someone execute any method expect +inspect+ on proxy object this will trigger +warn+ method on +deprecator_instance+ + # + # Default deprecator is ActiveSupport::Deprecation + class DeprecatedObjectProxy < DeprecationProxy + def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance) @object = object @message = message @deprecator = deprecator @@ -42,14 +52,34 @@ module ActiveSupport end end - # Stand-in for @request, @attributes, @params, etc. - # which emits deprecation warnings on any method call (except +inspect+). - class DeprecatedInstanceVariableProxy < DeprecationProxy #:nodoc: - def initialize(instance, method, var = :"@#{method}", deprecator = nil) + # This DeprecatedInstanceVariableProxy transforms instance variable to depracated instance variable. + # + # Example + # class Example + # def initialize(deprecator) + # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator) + # @_request = :a_request + # end + # + # def request + # @_request + # end + # + # def old_request + # @request + # end + # end + # + # When someone execute any method on @request variable this will trigger +warn+ method on +deprecator_instance+ + # and will fetch @_request variable via +request+ method and execute the same method on non-proxy instance variable. + # + # Default deprecator is ActiveSupport::Deprecation + class DeprecatedInstanceVariableProxy < DeprecationProxy + def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance) @instance = instance @method = method @var = var - @deprecator = deprecator || (@instance.respond_to?(:deprecator) ? @instance.deprecator : ActiveSupport::Deprecation) + @deprecator = deprecator end private @@ -62,8 +92,17 @@ module ActiveSupport end end - class DeprecatedConstantProxy < DeprecationProxy #:nodoc:all - def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation) + # This DeprecatedConstantProxy transforms constant to depracated constant. + # + # Example + # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST') + # Example with custom deprecator + # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance) + # When someone use old constant this will trigger +warn+ method on +deprecator_instance+ + # + # Default deprecator is ActiveSupport::Deprecation + class DeprecatedConstantProxy < DeprecationProxy + def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance) @old_const = old_const @new_const = new_const @deprecator = deprecator diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 69de79b93c..cf91ca1acb 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -1,73 +1,90 @@ module ActiveSupport - module Deprecation - attr_accessor :silenced + class Deprecation + module Reporting + # Whether to print a message (silent mode) + attr_accessor :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.warn("something broke!") - # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" - def warn(message = nil, callstack = caller) - return if silenced - deprecation_message(callstack, message).tap do |m| - behavior.each { |b| b.call(m, callstack) } + # Outputs a deprecation warning to the output configured by + # ActiveSupport::Deprecation.behavior. + # + # ActiveSupport::Deprecation.warn("something broke!") + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + def warn(message = nil, callstack = caller) + return if silenced + deprecation_message(callstack, message).tap do |m| + behavior.each { |b| b.call(m, callstack) } + end end - end - # Silence deprecation warnings within the block. - # - # ActiveSupport::Deprecation.warn("something broke!") - # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" - # - # ActiveSupport::Deprecation.silence do - # ActiveSupport::Deprecation.warn("something broke!") - # end - # # => nil - def silence - old_silenced, @silenced = @silenced, true - yield - ensure - @silenced = old_silenced - end + # Silence deprecation warnings within the block. + # + # ActiveSupport::Deprecation.warn("something broke!") + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + # + # ActiveSupport::Deprecation.silence do + # ActiveSupport::Deprecation.warn("something broke!") + # end + # # => nil + def silence + old_silenced, @silenced = @silenced, true + yield + ensure + @silenced = old_silenced + end - def deprecated_method_warning(method_name, message = nil) - warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}" - case message - when Symbol then warning << " (use #{message} instead)" - when String then warning << " (#{message})" + def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = caller) + deprecated_method_warning(deprecated_method_name, message).tap do |message| + warn(message, caller_backtrace) + end end - warning - end - private - - def deprecation_message(callstack, message = nil) - message ||= "You are using deprecated behavior which will be removed from the next major or minor release." - message += '.' unless message =~ /\.$/ - "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}" - end + private + # Outputs a deprecation warning message + # ActiveSupport::Deprecation.deprecated_method_warning(:method_name) + # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}" + # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method) + # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)" + # ActiveSupport::Deprecation.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_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})" + def deprecation_message(callstack, message = nil) + message ||= "You are using deprecated behavior which will be removed from the next major or minor release." + message += '.' unless message =~ /\.$/ + "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}" end - end - end - def extract_callstack(callstack) - rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" - offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first - if offending_line - if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/) - md.captures - else - offending_line + 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) + rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/" + offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first + if offending_line + if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/) + md.captures + else + offending_line + end + end end - end end end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 308c7c3fe7..c081103cc7 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -104,6 +104,17 @@ class DeprecationTest < ActiveSupport::TestCase assert_match(/call stack!/, content) end + def test_default_stderr_behavior_with_warn_method + ActiveSupport::Deprecation.behavior = :stderr + + content = capture(:stderr) { + ActiveSupport::Deprecation.warn('Instance error!', ['instance call stack!']) + } + + assert_match(/Instance error!/, content) + assert_match(/instance call stack!/, content) + end + def test_default_silence_behavior ActiveSupport::Deprecation.behavior = :silence behavior = ActiveSupport::Deprecation.behavior.first @@ -187,39 +198,57 @@ class DeprecationTest < ActiveSupport::TestCase assert_deprecated(/you now need to do something extra for this one/) { @dtc.d } end - def test_deprecation_in_other_module_does_not_interfere + def test_deprecation_in_other_object messages = [] - m = Module.new - m.extend ActiveSupport::Deprecation - m.behavior = Proc.new{|message, callstack| messages << message} - assert_not_deprecated do # not globally - assert_difference("messages.size") do # but locally - m.warn("warning") - end + klass = Class.new do + delegate :warn, :behavior=, to: ActiveSupport::Deprecation + end + + o = klass.new + o.behavior = Proc.new { |message, callstack| messages << message } + assert_difference("messages.size") do + o.warn("warning") end end - def test_deprecated_method_with_deprecator_implemented + def test_deprecated_method_with_custom_method_warning deprecator = deprecator_with_messages - def deprecator.deprecated_method_warning(method, *params) - "deprecator.deprecated_method_warning.#{method}" + + class << deprecator + private + def deprecated_method_warning(method, message) + "deprecator.deprecated_method_warning.#{method}" + end end - deprecatee = Class.new() do + deprecatee = Class.new do def method end - deprecate :method - define_method(:deprecator){ deprecator } + deprecate :method, deprecator: deprecator end deprecatee.new.method assert deprecator.messages.first.match("DEPRECATION WARNING: deprecator.deprecated_method_warning.method") end + def test_deprecate_with_custom_deprecator + custom_deprecator = mock('Deprecator') do + expects(:deprecation_warning) + end + + klass = Class.new do + def method + end + deprecate :method, deprecator: custom_deprecator + end + + klass.new.method + end + def test_deprecated_constant_with_deprecator_given deprecator = deprecator_with_messages - klass = Class.new() + klass = Class.new klass.const_set(:OLD, ActiveSupport::Deprecation::DeprecatedConstantProxy.new('klass::OLD', 'Object', deprecator) ) assert_difference("deprecator.messages.size") do klass::OLD.to_s @@ -230,23 +259,21 @@ class DeprecationTest < ActiveSupport::TestCase deprecator = deprecator_with_messages klass = Class.new() do - def initialize - @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request) + def initialize(deprecator) + @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator) @_request = :a_request end def request; @_request end def old_request; @request end - define_method(:deprecator) { deprecator } end - assert_difference("deprecator.messages.size") { klass.new.old_request.to_s } - + assert_difference("deprecator.messages.size") { klass.new(deprecator).old_request.to_s } end def test_deprecated_instance_variable_with_given_deprecator deprecator = deprecator_with_messages - klass = Class.new() do + klass = Class.new do define_method(:initialize) do @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator) @_request = :a_request @@ -258,23 +285,23 @@ class DeprecationTest < ActiveSupport::TestCase assert_difference("deprecator.messages.size") { klass.new.old_request.to_s } end - def test_included_deprecation_module - klass = Class.new() do + def test_delegate_deprecator_instance + klass = Class.new do attr_reader :last_message - include ActiveSupport::Deprecation + delegate :warn, :behavior=, to: ActiveSupport::Deprecation + + def initialize + self.behavior = [Proc.new { |message| @last_message = message }] + end + def deprecated_method warn(deprecated_method_warning(:deprecated_method, "You are calling deprecated method")) end private - - def deprecated_method_warning(method_name, message = nil) - message || "#{method_name} is deprecated and will be removed from This Library" - end - - def behavior - @behavior ||= [Proc.new { |message| @last_message = message }] - end + def deprecated_method_warning(method_name, message = nil) + message || "#{method_name} is deprecated and will be removed from This Library" + end end object = klass.new @@ -282,35 +309,30 @@ class DeprecationTest < ActiveSupport::TestCase assert_match(/You are calling deprecated method/, object.last_message) end - unless defined?(::MiniTest) - def test_assertion_failed_error_doesnt_spout_deprecation_warnings - error_class = Class.new(StandardError) do - def message - ActiveSupport::Deprecation.warn 'warning in error message' - super - end - end - - raise error_class.new('hmm') + def test_default_gem_name + deprecator = ActiveSupport::Deprecation.new - rescue => e - error = Test::Unit::Error.new('testing ur doodz', e) - assert_not_deprecated { error.message } - assert_nil @last_message + deprecator.send(:deprecated_method_warning, :deprecated_method, "You are calling deprecated method").tap do |message| + assert_match(/is deprecated and will be removed from Rails/, message) end end + def test_custom_gem_name + deprecator = ActiveSupport::Deprecation.new('2.0', 'Custom') - private - - - def deprecator_with_messages - deprecator = Object.new - deprecator.extend(ActiveSupport::Deprecation) - deprecator.behavior = Proc.new{|message, callstack| deprecator.messages << message} - def deprecator.messages - @messages ||= [] + deprecator.send(:deprecated_method_warning, :deprecated_method, "You are calling deprecated method").tap do |message| + assert_match(/is deprecated and will be removed from Custom/, message) end - deprecator end + + private + def deprecator_with_messages + klass = Class.new(ActiveSupport::Deprecation) + deprecator = klass.new + deprecator.behavior = Proc.new{|message, callstack| deprecator.messages << message} + def deprecator.messages + @messages ||= [] + end + deprecator + end end -- cgit v1.2.3