deprecation.rb 4.0 KB
Newer Older
1 2
module ActiveSupport
  module Deprecation
3 4 5 6 7 8 9
    # Choose the default warn behavior according to RAILS_ENV.
    # Ignore deprecation warnings in production.
    DEFAULT_BEHAVIORS = {
      'test'        => Proc.new { |message| $stderr.puts message },
      'development' => Proc.new { |message| RAILS_DEFAULT_LOGGER.warn message },
    }

10
    class << self
11
      def warn(message = nil, callstack = caller)
12
        behavior.call(deprecation_message(callstack, message)) if behavior && ! silenced?
13
      end
14

15 16
      def default_behavior
        DEFAULT_BEHAVIORS[RAILS_ENV.to_s] if defined?(RAILS_ENV)
17
      end
18

19 20 21 22 23 24 25 26 27 28 29 30 31 32
      # Have deprecations been silenced?
      def silenced?
        @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

33 34 35 36 37 38 39 40 41
      private
        def deprecation_message(callstack, message = nil)
          file, line, method = extract_callstack(callstack)
          message ||= "WARNING: #{method} is deprecated and will be removed from the next Rails release"
          "#{message} (#{method} at #{file}:#{line})"
        end

        def extract_callstack(callstack)
          callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
42 43
        end
    end
44 45 46 47 48

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

49
    module ClassMethods
50 51 52 53 54 55 56 57 58 59
      # Declare that a method has been deprecated.
      def deprecate(*method_names)
        method_names.each do |method_name|
          class_eval(<<-EOS, __FILE__, __LINE__)
            def #{method_name}_with_deprecation(*args, &block)
              ::ActiveSupport::Deprecation.warn
              #{method_name}_without_deprecation(*args, &block)
            end
          EOS
          alias_method_chain(method_name, :deprecation)
60 61 62
        end
      end
    end
63 64

    module Assertions
65
      def assert_deprecated(match = nil, &block)
66
        last = collect_deprecations(&block).last
67
        assert last, "Expected a deprecation warning within the block but received none"
68 69 70 71
        if match
          match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
          assert_match match, last, "Deprecation warning didn't match #{match}: #{last}"
        end
72 73 74
      end

      def assert_not_deprecated(&block)
75 76
        deprecations = collect_deprecations(&block)
        assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n  #{deprecations * "\n  "}"
77 78 79
      end

      private
80 81
        
        def collect_deprecations
82
          old_behavior = ActiveSupport::Deprecation.behavior
83 84 85 86 87
          deprecations = []
          ActiveSupport::Deprecation.behavior = Proc.new do |message|
            deprecations << message
            old_behavior.call(message) if old_behavior
          end
88
          yield
89
          return deprecations
90 91 92 93
        ensure
          ActiveSupport::Deprecation.behavior = old_behavior
        end
    end
94 95 96 97 98 99 100 101 102 103

    # 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
104 105 106 107
        def warn(callstack, called, args)
          ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
        end

108
        def method_missing(called, *args, &block)
109
          warn caller, called, args
110 111 112
          @instance.__send__(@method).__send__(called, *args, &block)
        end
    end
113 114 115
  end
end

116 117 118 119 120 121 122 123 124 125 126
class Class
  include ActiveSupport::Deprecation::ClassMethods
end

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