deprecation.rb 5.6 KB
Newer Older
1 2
require 'yaml'

3 4
module ActiveSupport
  module Deprecation
5 6 7
    mattr_accessor :debug
    self.debug = false

8 9 10
    # Choose the default warn behavior according to RAILS_ENV.
    # Ignore deprecation warnings in production.
    DEFAULT_BEHAVIORS = {
11 12 13 14 15 16 17 18
      'test'        => Proc.new { |message, callstack|
                         $stderr.puts(message)
                         $stderr.puts callstack.join("\n  ") if debug
                       },
      'development' => Proc.new { |message, callstack|
                         RAILS_DEFAULT_LOGGER.warn message
                         RAILS_DEFAULT_LOGGER.debug callstack.join("\n  ") if debug
                       }
19 20
    }

21
    class << self
22
      def warn(message = nil, callstack = caller)
23
        behavior.call(deprecation_message(callstack, message), callstack) if behavior && !silenced?
24
      end
25

26
      def default_behavior
27 28 29 30 31
        if defined?(RAILS_ENV)
          DEFAULT_BEHAVIORS[RAILS_ENV.to_s]
        else
          DEFAULT_BEHAVIORS['test']
        end
32
      end
33

34 35
      # Have deprecations been silenced?
      def silenced?
36
        @silenced = false unless defined?(@silenced)
37 38 39 40 41 42 43 44 45 46 47 48
        @silenced
      end

      # Silence deprecations for the duration of the provided block. For internal
      # use only.
      def silence
        old_silenced, @silenced = @silenced, true # We could have done behavior = nil...
        yield
      ensure
        @silenced = old_silenced
      end

J
Jeremy Kemper 已提交
49 50 51
      attr_writer :silenced


52 53
      private
        def deprecation_message(callstack, message = nil)
54
          message ||= "You are using deprecated behavior which will be removed from Rails 2.0."
55 56 57 58 59 60 61 62 63 64 65 66
          "DEPRECATION WARNING: #{message}  See http://www.rubyonrails.org/deprecation for details. #{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
67 68 69
        end

        def extract_callstack(callstack)
70 71 72 73 74
          if md = callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
            md.captures
          else
            callstack.first
          end
75 76
        end
    end
77 78 79 80 81

    # Behavior is a block that takes a message argument.
    mattr_accessor :behavior
    self.behavior = default_behavior

J
Jeremy Kemper 已提交
82 83 84
    # Warnings are not silenced by default.
    self.silenced = false

85
    module ClassMethods
86 87
      # Declare that a method has been deprecated.
      def deprecate(*method_names)
88 89
        options = method_names.last.is_a?(Hash) ? method_names.pop : {}
        method_names = method_names + options.keys
90
        method_names.each do |method_name|
91 92 93
          alias_method_chain(method_name, :deprecation) do |target, punctuation|
            class_eval(<<-EOS, __FILE__, __LINE__)
              def #{target}_with_deprecation#{punctuation}(*args, &block)
94
                ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:#{method_name}, #{options[method_name].inspect}), caller)
95 96 97 98
                #{target}_without_deprecation#{punctuation}(*args, &block)
              end
            EOS
          end
99 100
        end
      end
101

102 103 104 105 106 107 108
      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
109 110 111 112 113
      end

      def deprecation_horizon
        '2.0'
      end
114
    end
115 116

    module Assertions
117
      def assert_deprecated(match = nil, &block)
118
        result, warnings = collect_deprecations(&block)
119
        assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
120 121
        if match
          match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
122
          assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
123
        end
124
        result
125 126 127
      end

      def assert_not_deprecated(&block)
128
        result, deprecations = collect_deprecations(&block)
129
        assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n  #{deprecations * "\n  "}"
130
        result
131 132 133
      end

      private
134
        def collect_deprecations
135
          old_behavior = ActiveSupport::Deprecation.behavior
136
          deprecations = []
137
          ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
138 139
            deprecations << message
          end
140 141
          result = yield
          [result, deprecations]
142 143 144 145
        ensure
          ActiveSupport::Deprecation.behavior = old_behavior
        end
    end
146 147 148 149 150 151 152 153 154 155

    # Stand-in for @request, @attributes, etc.
    class DeprecatedInstanceVariableProxy
      instance_methods.each { |m| undef_method m unless m =~ /^__/ }

      def initialize(instance, method, var = "@#{method}")
        @instance, @method, @var = instance, method, var
      end

      private
156 157 158 159
        def warn(callstack, called, args)
          ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
        end

160
        def method_missing(called, *args, &block)
161
          warn caller, called, args
162 163 164
          @instance.__send__(@method).__send__(called, *args, &block)
        end
    end
165 166 167
  end
end

168
class Module
169 170 171 172 173 174 175 176 177 178
  include ActiveSupport::Deprecation::ClassMethods
end

module Test
  module Unit
    class TestCase
      include ActiveSupport::Deprecation::Assertions
    end
  end
end