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)
|