From 727e9dc18a7059f024b922b65951198ff0184fdd Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 17 Apr 2009 21:29:30 -0700 Subject: [PATCH] Dice up ActiveSupport::Deprecation --- .../core_ext/module/deprecation.rb | 9 + .../lib/active_support/deprecation.rb | 194 +----------------- .../active_support/deprecation/behaviors.rb | 32 +++ .../deprecation/method_wrappers.rb | 27 +++ .../deprecation/proxy_wrappers.rb | 72 +++++++ .../active_support/deprecation/reporting.rb | 55 +++++ 6 files changed, 204 insertions(+), 185 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/module/deprecation.rb create mode 100644 activesupport/lib/active_support/deprecation/behaviors.rb create mode 100644 activesupport/lib/active_support/deprecation/method_wrappers.rb create mode 100644 activesupport/lib/active_support/deprecation/proxy_wrappers.rb create mode 100644 activesupport/lib/active_support/deprecation/reporting.rb diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb new file mode 100644 index 0000000000..5a5b4e3f80 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -0,0 +1,9 @@ +class Module + # Declare that a method has been deprecated. + # deprecate :foo + # deprecate :bar => 'message' + # deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!' + def deprecate(*method_names) + ActiveSupport::Deprecation.deprecate_methods(self, *method_names) + end +end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index e7b35fc74e..e1b8211d68 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -1,194 +1,18 @@ -require 'yaml' +require 'active_support/deprecation/behaviors' +require 'active_support/deprecation/reporting' +require 'active_support/deprecation/method_wrappers' +require 'active_support/deprecation/proxy_wrappers' module ActiveSupport module Deprecation #:nodoc: - mattr_accessor :debug - self.debug = false - - # Choose the default warn behavior according to RAILS_ENV. - # Ignore deprecation warnings in production. - DEFAULT_BEHAVIORS = { - 'test' => Proc.new { |message, callstack| - $stderr.puts(message) - $stderr.puts callstack.join("\n ") if debug - }, - 'development' => Proc.new { |message, callstack| - logger = defined?(Rails) ? Rails.logger : Logger.new($stderr) - logger.warn message - logger.debug callstack.join("\n ") if debug - } - } - class << self - def warn(message = nil, callstack = caller) - behavior.call(deprecation_message(callstack, message), callstack) if behavior && !silenced? - end - - def default_behavior - if defined?(RAILS_ENV) - DEFAULT_BEHAVIORS[RAILS_ENV.to_s] - else - DEFAULT_BEHAVIORS['test'] - end - end - - # Have deprecations been silenced? - def silenced? - @silenced = false unless defined?(@silenced) - @silenced - end - - # Silence deprecation warnings within the block. - def silence - old_silenced, @silenced = @silenced, true - yield - ensure - @silenced = old_silenced - end - - attr_writer :silenced - - - private - 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) - if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/) - md.captures - else - callstack.first - end - end + # The version the deprecated behavior will be removed, by default. + attr_accessor :deprecation_horizon end + self.deprecation_horizon = '3.0' - # Behavior is a block that takes a message argument. - mattr_accessor :behavior - self.behavior = default_behavior - - # Warnings are not silenced by default. + # By default, warnings are not silenced and debugging is off. self.silenced = false - - module ClassMethods #:nodoc: - # Declare that a method has been deprecated. - def deprecate(*method_names) - options = method_names.extract_options! - method_names = method_names + options.keys - method_names.each do |method_name| - alias_method_chain(method_name, :deprecation) do |target, punctuation| - class_eval(<<-EOS, __FILE__, __LINE__) - def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) - ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( - self.class.deprecated_method_warning( # self.class.deprecated_method_warning( - :#{method_name}, # :generate_secret, - #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"), - caller # caller - ) # ) - #{target}_without_deprecation#{punctuation}(*args, &block) # generate_secret_without_deprecation(*args, &block) - end # end - EOS - end - end - 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 - end - - def deprecation_horizon - '2.3' - end - end - - class DeprecationProxy #:nodoc: - instance_methods.each { |m| undef_method m unless m =~ /^__/ } - - # Don't give a deprecation warning on inspect since test/unit and error - # logs rely on it for diagnostics. - def inspect - target.inspect - end - - private - def method_missing(called, *args, &block) - warn caller, called, args - target.__send__(called, *args, &block) - end - end - - class DeprecatedObjectProxy < DeprecationProxy - def initialize(object, message) - @object = object - @message = message - end - - private - def target - @object - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.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 - end - - private - def target - @instance.__send__(@method) - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) - end - end - - class DeprecatedConstantProxy < DeprecationProxy #:nodoc: - def initialize(old_const, new_const) - @old_const = old_const - @new_const = new_const - end - - def class - target.class - end - - private - def target - @new_const.to_s.constantize - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) - end - end + self.debug = false end end - -class Module - include ActiveSupport::Deprecation::ClassMethods -end diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb new file mode 100644 index 0000000000..c531a1aa58 --- /dev/null +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -0,0 +1,32 @@ +module ActiveSupport + module Deprecation + class << self + # Behavior is a block that takes a message argument. + attr_writer :behavior + + # Whether to print a backtrace along with the warning. + attr_accessor :debug + + def behavior + @behavior ||= default_behavior + end + + def default_behavior + Deprecation::DEFAULT_BEHAVIORS[defined?(Rails) ? Rails.env.to_s : 'test'] + end + end + + # Default warning behaviors per Rails.env. Ignored in production. + DEFAULT_BEHAVIORS = { + 'test' => Proc.new { |message, callstack| + $stderr.puts(message) + $stderr.puts callstack.join("\n ") if debug + }, + 'development' => Proc.new { |message, callstack| + logger = defined?(Rails) ? Rails.logger : Logger.new($stderr) + logger.warn message + logger.debug callstack.join("\n ") if debug + } + } + end +end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb new file mode 100644 index 0000000000..845bef059f --- /dev/null +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -0,0 +1,27 @@ +require 'active_support/core_ext/module/deprecation' + +module ActiveSupport + class << Deprecation + # Declare that a method has been deprecated. + def deprecate_methods(target_module, *method_names) + options = method_names.extract_options! + 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__) + def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) + ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( + ::ActiveSupport::Deprecation.deprecated_method_warning( # ::ActiveSupport::Deprecation.deprecated_method_warning( + :#{method_name}, # :generate_secret, + #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"), + caller # caller + ) # ) + #{target}_without_deprecation#{punctuation}(*args, &block) # generate_secret_without_deprecation(*args, &block) + end # end + end_eval + end + end + end + end +end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb new file mode 100644 index 0000000000..1c268d0d9c --- /dev/null +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -0,0 +1,72 @@ +module ActiveSupport + module Deprecation + class DeprecationProxy #:nodoc: + instance_methods.each { |m| undef_method m unless m =~ /^__/ } + + # Don't give a deprecation warning on inspect since test/unit and error + # logs rely on it for diagnostics. + def inspect + target.inspect + end + + private + def method_missing(called, *args, &block) + warn caller, called, args + target.__send__(called, *args, &block) + end + end + + class DeprecatedObjectProxy < DeprecationProxy #:nodoc: + def initialize(object, message) + @object = object + @message = message + end + + private + def target + @object + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.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 + end + + private + def target + @instance.__send__(@method) + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack) + end + end + + class DeprecatedConstantProxy < DeprecationProxy #:nodoc: + def initialize(old_const, new_const) + @old_const = old_const + @new_const = new_const + end + + def class + target.class + end + + private + def target + @new_const.to_s.constantize + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) + end + end + end +end diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb new file mode 100644 index 0000000000..fcb05ad8d9 --- /dev/null +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -0,0 +1,55 @@ +module ActiveSupport + module Deprecation + class << self + attr_accessor :silenced + + def warn(message = nil, callstack = caller) + if behavior && !silenced + behavior.call(deprecation_message(callstack, message), callstack) + end + end + + # Silence deprecation warnings within the block. + 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 + 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." + "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) + if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/) + md.captures + else + callstack.first + end + end + end + end +end