Reputation: 99
How would I best go about renaming a Ruby Module in a backwards compatible manner? I have ActiveSupport, so can call deprecation warnings this way, but in this case I'm deprecating the old module name and not the method names.
Example follows:
module OldName
def self.method1
...
end
end
Will become:
module NewName
def self.method1
...
end
end
I want to generate warnings directing the developer to using NewName::method1
rather than OldName::method1
Any suggestions?
Upvotes: 0
Views: 578
Reputation: 99
I was sideswiped by a bug in the gem I was patching, which caused a few false starts, but the final answer is extremely simple and I include the working code as an example for others:
require 'active_support'
OldName = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OldName', 'NewName')
module NewName
def self.method1
puts 'Hello World!'
end
end
NewName.method1
#=> Hello World!
OldName.method1
#=> DEPRECATION WARNING: OldName is deprecated! Use NewName instead. (called from ..)
#=> Hello World!
Simples!
My thanks to @matthewd for re-pointing me in the right direction.
Upvotes: 0
Reputation: 1261
Rails 4.1.8 Ruby 2.2.0p0
module Fred
extend self
def aaa; end
def bbb; end
def ccc; end
def ddd; end
def eee; end
end
module Bar
extend self
def ccc; end
end
ActiveSupport::Deprecation.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead')
Fred.aaa
DEPRECATION WARNING: aaa is deprecated and will be removed from Rails 4.2. (called from __pry__ at (pry):15)
=> nil
Same code tried in rails 5.2.0 but no DEPRECATION WARNING.
Rails 5.2.0 Ruby 2.5.1p57
class Fred
def aaa; end
def bbb; end
def ccc; end
def ddd; end
def eee; end
end
class Bar
def ccc; end
end
ActiveSupport::Deprecation.deprecate_methods(Fred, :aaa, bbb: :zzz, ccc: 'use Bar#ccc instead')
> Fred.new.aaa
DEPRECATION WARNING: aaa is deprecated and will be removed from Rails 6.0 (called from irb_binding at (irb):13)
=> nil
Upvotes: 2
Reputation: 110695
module OldName
def instance_method(*args)
yield(args)
end
instance_methods.each do |im|
alias_name = "_#{im.to_s}"
alias_method alias_name, im
define_method(im) do |*args, &block|
warn "Foo is deprecated please use Bar instead"
public_send(alias_name, *args, &block)
end
end
end
class C
include OldName
end
C.instance_methods && [:im, :_im]
#=> [:im, :_im]
c = C.new
c._instance_method(2,3) { |a,b| a+b }
#=> 5
c.instance_method(2,3) { |a,b| a+b }
# Foo is deprecated please use Bar instead
#=> 5
Alternatively, public_send(alias_name, *args, &block)
can be replaced with method(alias_name).call(*args, &block)
.
Upvotes: 0
Reputation: 29478
You could go with something like:
module Bar
def self.aaa
'Bar::aaa'
end
def baz
'Bar#baz'
end
end
module Foo
{included: :include,extended: :extend,prepended: :prepend}.each do |mod_method, called_method|
singleton_class.define_method(mod_method) do |base|
warn "Foo is deprecated please use Bar instead"
base.send(called_method,Bar)
end
end
def self.method_missing(meth,*args,&block)
warn "Foo is deprecated please use Bar instead"
Bar.respond_to?(meth) ? Bar.send(meth,*args,&block) : super
end
end
class A
include Foo # this will throw a warning
end
Then
Foo.aaa
# Foo is deprecated please use Bar instead
#=> 'Bar::aaa'
A.new.baz
#=> 'Bar#baz'
Upvotes: 1
Reputation: 4435
ActiveSupport provides two ways of deprecating a constant: DeprecatedConstantAccessor and DeprecatedConstantProxy. They each have trade-offs and limitations in when they show the warning (Accessor is when it's accessed; Proxy is when its methods are called) and how they generally behave. You'll probably want to try both to see which one feels right for your use case.
Upvotes: 2