我有一个ruby类,其中包含很多不再使用的逻辑,但是我需要保持(至少一段时间)以实现向后兼容。
我的计划是将这些方法移至LegacyStuff
之类的模块中并将其包含在类中。我想知道是否有一种巧妙的方法来添加一些东西,以便在调用模块中的任何方法时都会生成警告,而不必在每个单独的方法主体中实际添加warn
语句。
我猜我正在寻找的是整个模块上的“调用前”或“调用后”钩子这样的行为。我猜这里的子问题是“这甚至是个好主意吗?”
答案 0 :(得分:4)
虽然这并不是您所要求的(“立即标记所有方法”),并且在ruby本身或大型框架(如rails)中可能存在类似的内容,但对某些人仍然有用。
module DeprecatedMethods
def deprecated(method_name)
prepend(Module.new do
define_method method_name do |*args|
puts "calling deprecated method #{method_name}"
super(*args)
end
end)
end
end
module AncientCode
extend DeprecatedMethods
deprecated def foo # selectively mark methods as deprecated
puts "doing foo"
end
def bar
puts "doing bar"
end
end
class Host
include AncientCode
end
host = Host.new
host.foo
host.bar
# >> calling deprecated method foo
# >> doing foo
# >> doing bar
当前,这会按不推荐使用的方法注入模块,这很浪费。您可以通过以下操作将所有不赞成使用的方法放在一个模块中:
deprecated_methods :foo, :bar
答案 1 :(得分:2)
代码
首先创建一个包含单个模块方法setup
的模块。可以根据需要将此模块放入一个文件中。该模块方法将从包含要包含在给定类中的实例方法的模块中调用。它使用别名修改那些实例方法,以在执行其余代码之前打印一条消息(包含方法名称)。
模块AddMessage
module AddMessage
def self.setup(mod, msg_pre, msg_post)
mod.instance_methods(false).
each do |m|
am = "_#{m}".to_sym
mod.send(:alias_method, am, m)
mod.send(:private, am)
mod.send(:define_method, m) do |*args, &block|
puts "%s %s %s" % [msg_pre, m, msg_post]
send(am, *args, &block)
end
end
end
end
要包含在类中的模块
module LegacyStuff
def old1
"hi"
end
def old2(a, b)
yield(a, b)
end
AddMessage.setup(self, "Warning:", "is deprecated")
end
AddMessage::setup
传递了三个参数,即调用模块的名称和消息前缀和消息后缀,它们用于形成警告消息。当类实例执行此模块中的实例方法m
时,在执行其余计算之前,将打印消息"Warning: #{m} is deprecated"
(例如"Warning: old1 is deprecated"
)。
使用
LegacyStuff
仅包含在类中。
class C
include LegacyStuff
# <constants, methods and so forth go here>
end
c = C.new
c.old1
# Warning: old1 is deprecated
#=> "hi"
c.old2(1,2) { |a,b| a+b }
# Warning: old2 is deprecated
#=> 3
c.cat
#=> NoMethodError: undefined method `cat' for #<C:0x000000008ef0a8>
模块方法AddMessage:setup
的说明
此方法使用以下(通常不太熟悉的)方法:Module#instance_methods,Module#alias_method,Module#private和Module#define_method。
对于模块m
中定义的每个实例方法mod
(例如,数组LegacyStuff.instance_methods(false)
的元素)执行以下三个步骤。
mod.send(:alias_method, am, m)
为该方法创建别名am
(例如,_:old1
为old1
)。
mod.send(:private, am)
将别名am
设为私有方法(例如_old1
)。
mod.send(:define_method, m) do |*args, &block| ... end
重新定义方法m
(例如old1
)以打印指示字符串,然后执行别名am
(例如_old1
)。