File: deprecated.rb

package info (click to toggle)
ruby-deprecated 3.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 88 kB
  • sloc: ruby: 152; makefile: 2
file content (203 lines) | stat: -rw-r--r-- 5,819 bytes parent folder | download
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
class DeprecatedError < StandardError; end

#
# Deprecated is a module to help you deprecate code BEFORE you remove it. Don't
# surprise your users, deprecate them!
#
# Usage is simple:
#
#     class Foo
#       include Deprecated
#
#       def moo
#         "cow"
#       end
#
#       deprecated :moo
#
#       def sheep
#         "baaa"
#       end
#
#       deprecated :sheep, "Sounds#baa"
#
#       protected
#
#       def bar
#         true
#       end
#
#       deprecated :bar
#     end
#     
#     Foo.new.moo # warns that the call is deprecated
#     
#     Deprecated.set_action(:raise)
#     Foo.new.moo # raises with the same message
#
#     Deprecated.set_action do |klass, sym, replacement|
#       email_boss(
#         "Someone tried to use #{klass}##{sym}! " +
#         "They should be using #{replacement} instead!"
#       )
#     end
#
#     Foo.new.sheep # do I really need to explain?
#
#     Foo.new.bar # still protected!
#
# Let's do it live!
#
#     class Bar
#       include Deprecated
#
#       # sets it just for this class
#       deprecated_set_action do |klass, sym, replacement|
#         email_boss(
#           "Someone tried to use #{klass}##{sym}! " +
#           "They should be using #{replacement} instead!"
#         )
#       end
#
#       def cow
#         "moo"
#       end
#
#       deprecate :cow # emails your boss when called!
#     end
#
# Please see Deprecated::Module#deprecated, Deprecated.set_action, and
# Deprecated::Module#deprecated_set_action for more information.
#
module Deprecated
    VERSION = "3.0.0"

    def __deprecated_run_action__(sym, replacement)
        if self.class.instance_eval { @__deprecated_run_action__ }
            self.class.instance_eval { @__deprecated_run_action__ }.call(self.class, sym, replacement) 
        else
            Deprecated.run_action(self.class, sym, replacement)
        end
    end

    def self.build_message(klass, sym, replacement)
        message = "#{klass}##{sym} is deprecated."

        if replacement
            message += " Please use #{replacement}."
        end

        return message
    end

    #
    # set_action takes 3 "canned" arguments or an arbitrary block. If you
    # provide the block, any canned argument is ignored.
    #
    # The canned arguments are:
    #
    # :warn:: display a warning
    # :raise:: raise a DeprecatedError (a kind of StandardError) with the warning.
    # :fail:: fail. die. kaput. it's over.
    #
    # Procs take three arguments:
    #
    # - The class of the method
    # - The method name itself, a symbol
    # - A replacement string which may be nil
    #
    def self.set_action(type=nil, &block)
        @action = if block
                      block
                  else
                      case type
                      when :warn
                          proc { |*args| warn build_message(*args) }
                      when :fail
                          proc { |*args| fail build_message(*args) }
                      when :raise
                          proc { |*args| raise DeprecatedError, build_message(*args) }
                      else
                          raise ArgumentError, "you must provide a symbol or a block to set_action()."
                      end
                  end
    end

    # 
    # Is called when an action needs to be run. Proably not in your best
    # interest to run this directly.
    #
    def self.run_action(klass, sym, replacement)
        raise "run_action has no associated hook" unless @action
        @action.call(klass, sym, replacement)
    end

    #
    # Returns the current action; this may be block or Proc.
    #
    def self.action
        @action
    end
end

module Deprecated
    module Module

        #
        # deprecated takes up to three arguments:
        #
        # - A symbol which is the name of the method you wish to deprecate
        #   (required)
        # - A string or symbol which is the replacement method. If you provide
        #   this, your users will be instructed to use that method instead.
        # - A symbol of :public, :private, or :protected which determines the
        #   new scope of the method. If you do not provide one, it will be
        #   searched for in the various collections, and scope will be chosen
        #   that way.
        # 
        def deprecated(sym, replacement=nil, scope=nil)
            unless sym.kind_of?(Symbol)
                raise ArgumentError, "deprecated() requires symbols for its first argument." 
            end

            meth = instance_method(sym)
            unless scope
                pub = public_instance_methods
                pro = protected_instance_methods
                pri = private_instance_methods
                if pub.include?(sym) or pub.include?(sym.to_s)
                    scope = :public
                elsif pro.include?(sym) or pro.include?(sym.to_s)
                    scope = :protected
                elsif pri.include?(sym) or pri.include?(sym.to_s)
                    scope = :private
                end
            end

            define_method(sym) do |*args|
                dep_meth = method(sym).unbind
                __deprecated_run_action__(sym, replacement)
                retval = meth.bind(self).call(*args) 
                dep_meth.bind(self)
                return retval
            end

            method(scope).call(sym) if scope
            return scope
        end

        #
        # Deprecated.set_action for class scope. See Deprecated.set_action.
        #
        def deprecated_set_action(&block)
            raise "You must provide a block" unless block
            @__deprecated_run_action__ = block
        end
    end
    
    def self.included(base)
        base.extend(Module)
    end
end

Deprecated.set_action(:warn)