Re-define the semantics of deprecate! and add deprecate_class! Include full specs for deprecate!/deprecate_class!
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@668906 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/rb/lib/thrift/deprecation.rb b/lib/rb/lib/thrift/deprecation.rb
index 7456f1c..0ee4e26 100644
--- a/lib/rb/lib/thrift/deprecation.rb
+++ b/lib/rb/lib/thrift/deprecation.rb
@@ -1,19 +1,53 @@
-class Module
- def deprecate!(*method_names)
- method_names.each do |method_name|
- module_eval <<-END
- alias_method :deprecated_#{method_name}, :#{method_name}
- def #{method_name}(*args, &block)
- $stderr.puts "Warning: calling deprecated method: #{self}.#{method_name}"
- deprecated_#{method_name}(*args, &block)
- end
- END
- end
- end
+# provide a backwards-compatible wrapper API and deprecate it
+
+unless Thrift.const_defined?(:DEPRECATION)
+ Thrift::DEPRECATION = true
end
-require 'thrift/transport/ttransport'
+class Module
+ # Wraps the given methods to print a warning and call the real method
+ # Example:
+ # deprecate! :readAll => :read_all
+ def deprecate!(methods)
+ methods.each_pair do |old, new|
+ module_eval <<-EOF
+ def #{old}(*args, &block)
+ old, new = #{[old,new].inspect}
+ STDERR.puts "Warning: calling deprecated method \#{self.is_a?(Module) ? "\#{self}." : "\#{self.class}#"}\#{old}"
+ target = (self.is_a?(Module) ? (class << self;self;end) : self.class)
+ target.send :define_method, old, target.instance_method(new) # unwrap
+ target.instance_method(new).bind(self).call(*args, &block)
+ end
+ EOF
+ end
+ end
+end
-class TTransport
- deprecate! :isOpen, :readAll
-end
\ No newline at end of file
+module Kernel
+ # Provides an alternate name for the class for deprecation purposes
+ # Example:
+ # deprecate_class! :TBinaryProtocol => Thrift::BinaryProtocol
+ #--
+ # at the moment this only works for creating top-level constants
+ # if necessary, this can be extended to take something like :'Thrift::TBinaryProtocol'
+ # alternately, Module can be extended with a similar method
+ #
+ # another idea is to not make the old name a pointer to the new, but rather
+ # a pointer to a proxy class that logs deprecation warnings and forwards methods
+ def deprecate_class!(klasses)
+ klasses.each_pair do |old, new|
+ Object.const_set old, new
+ end
+ end
+end
+
+# TProtocol = Thrift::Protocol
+# TProtocolException = Thrift::ProtocolException
+# ThriftStruct = Thrift::Struct
+# ThriftClient = Thrift::Client
+# TProcessor = Thrift::Processor
+# TType = Thrift::Types
+# TMessageType = Thrift::MessageTypes
+# TException = Thrift::Exception
+# TApplicationException = Thrift::ApplicationException
+# TBinaryProtocol = Thrift::BinaryProtocol
diff --git a/lib/rb/spec/deprecation_spec.rb b/lib/rb/spec/deprecation_spec.rb
new file mode 100644
index 0000000..62ff396
--- /dev/null
+++ b/lib/rb/spec/deprecation_spec.rb
@@ -0,0 +1,152 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe 'deprecate!' do
+ def stub_stderr(callstr)
+ STDERR.should_receive(:puts).with("Warning: calling deprecated method #{callstr}")
+ end
+
+ it "should work for Module methods" do
+ mod = Module.new do
+ class << self
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ end
+ stub_stderr "#{mod.inspect}.old"
+ mod.old.should == "new"
+ end
+
+ it "should work with Modules that extend themselves" do
+ mod = Module.new do
+ extend self
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ stub_stderr "#{mod.inspect}.old"
+ mod.old.should == "new"
+ end
+
+ it "should work wtih Class methods" do
+ klass = Class.new do
+ class << self
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ end
+ stub_stderr "#{klass.inspect}.old"
+ klass.old.should == "new"
+ end
+
+ it "should work with Classes that include Modules" do
+ mod = Module.new do
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ klass = Class.new do
+ include mod
+ end
+ stub_stderr "#{klass.inspect}#old"
+ klass.new.old.should == "new"
+ end
+
+ it "should work with instance methods" do
+ klass = Class.new do
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ stub_stderr "#{klass.inspect}#old"
+ klass.new.old.should == "new"
+ end
+
+ it "should work with multiple method names" do
+ klass = Class.new do
+ def new1
+ "new 1"
+ end
+ def new2
+ "new 2"
+ end
+ deprecate! :old1 => :new1, :old2 => :new2
+ end
+ stub_stderr("#{klass.inspect}#old1").ordered
+ stub_stderr("#{klass.inspect}#old2").ordered
+ inst = klass.new
+ inst.old1.should == "new 1"
+ inst.old2.should == "new 2"
+ end
+
+ it "should only log a message once, even across multiple instances" do
+ klass = Class.new do
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ stub_stderr("#{klass.inspect}#old").once
+ klass.new.old.should == "new"
+ klass.new.old.should == "new"
+ end
+
+ it "should pass arguments" do
+ klass = Class.new do
+ def new(a, b)
+ "new: #{a + b}"
+ end
+ deprecate! :old => :new
+ end
+ stub_stderr("#{klass.inspect}#old")
+ klass.new.old(3, 5).should == "new: 8"
+ end
+
+ it "should pass blocks" do
+ klass = Class.new do
+ def new
+ "new #{yield}"
+ end
+ deprecate! :old => :new
+ end
+ stub_stderr("#{klass.inspect}#old")
+ klass.new.old { "block" }.should == "new block"
+ end
+
+ it "should not freeze the definition of the new method" do
+ klass = Class.new do
+ def new
+ "new"
+ end
+ deprecate! :old => :new
+ end
+ klass.send :define_method, :new do
+ "new 2"
+ end
+ stub_stderr("#{klass.inspect}#old")
+ klass.new.old.should == "new 2"
+ end
+end
+
+describe "deprecate_class!" do
+ it "should create a new global constant that points to the old one" do
+ begin
+ klass = Class.new do
+ def foo
+ "foo"
+ end
+ end
+ deprecate_class! :DeprecationSpecOldClass => klass
+ DeprecationSpecOldClass.should eql(klass)
+ DeprecationSpecOldClass.new.foo.should == "foo"
+ ensure
+ Object.send :remove_const, :DeprecationSpecOldClass
+ end
+ end
+end
diff --git a/lib/rb/spec/spec_helper.rb b/lib/rb/spec/spec_helper.rb
new file mode 100644
index 0000000..1c67cb2
--- /dev/null
+++ b/lib/rb/spec/spec_helper.rb
@@ -0,0 +1,4 @@
+require 'rubygems'
+require 'spec'
+
+require File.dirname(__FILE__) + '/../lib/thrift'