Code
First create a module containing a single module method, setup. This module could be put in a file that is required as needed. This module method will be invoked from a module containing instance methods to be included in a given class. Using aliases, it modifies those instance methods to print a message (containing the method name) before executing the remainder of its code.
Module 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 to be included in class
module LegacyStuff def old1 "hi" end def old2(a, b) yield(a, b) end AddMessage.setup(self, "Warning:", "is deprecated") end
AddMessage::setup is passed three arguments, the name of the calling module and a message prefix and message suffix that are used to form the warning message. When an instance method m in this module is executed by a class instance the message "Warning: #{m} is deprecated" is printed (e.g., "Warning: old1 is deprecated") before the remaining calculations are performed.
Use
LegacyStuff is simply included by a class.
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>
Explanation of the module method AddMessage:setup
The following (of the generally less-familiar) methods are used by this method: Module#instance_methods, Module#alias_method, Module#private and Module#define_method.
The following three steps are performed for each instance method m defined in module mod (e.g., elements of the array LegacyStuff.instance_methods(false)).
mod.send(:alias_method, am, m)
Create an alias am of the method (e.g., _:old1 for old1).
mod.send(:private, am)
Make the alias am a private method (e.g., _old1).
mod.send(:define_method, m) do |*args, &block| ... end
Redefine the method m (e.g., old1) to print the indicate string and then execute the alias am (e.g., _old1).
method_addedmagic.Module#prependmakes it much easier to do that sort of thing. I intentionally wrote that code withoutModule#prependas a key part of the challenge :)