How you can deprecate method, constants or instance variables?

First of all you need to require ActiveSupport::Deprecation from active_support gem.

require 'active_support/deprecation'

ActiveSupport::Deprecation is a singleton (almost). You can create instance of deprecator via instance method. But you can also create new object with new method. If you use this class in your gem you should create new instance of deprecator.

ActiveSupport::Deprecation.instance # always return the same object
ActiveSupport::Deprecation.new # always return new object

In previous impelementation of ActiveSupport::Deprecation all methods were class level instead of instance level. To keep old API all top-level instance methods have been delegated to class level.

ActiveSupport::Deprecation.instance.method
# is equal to
ActiveSupport::Deprecation.method

How to deprecate method?

After require ActiveSupport::Deprecation we need to add deprecate method to class.

require 'active_support/core_ext/module/deprecation'

Suppose we have a class with method that will be removed in next release.

require 'active_support/deprecation'
require 'active_support/core_ext/module/deprecation'

class User
  def name
    @name
  end
  deprecate :name
 
  def username
    @_name
  end
end

User.new.name 
# => DEPRECATION WARNING: name is deprecated and will be removed from Rails 3.2. (called from <main> at user.rb:15)

But we don’t use Rails. How change this?

ActiveSupport::Deprecation.gem_name = 'MyGem'
ActiveSupport::Deprecation.deprecation_horizon = '2.0'

When you use singleton instance and another gem use singleton instance to – that can cause problems. Please use new methods instead of instance.

deprecator_instance = ActiveSupport::Deprecation.new
deprecator_instance.gem_name = 'MyGem'
deprecator_instance.deprecation_horizon = '2.0'

# or in simple way

deprecator_instance = ActiveSupport::Deprecation.new('2.0', 'MyGem')

After that we should get

class User
  def name
    @name
  end
  deprecate :name, :deprecator => deprecator_instance
 
  def username
    @_name
  end
end

User.new.name 
# => DEPRECATION WARNING: name is deprecated and will be removed from MyGem 2.0. (called from <main> at user.rb:18)

But how user know what method should use now? You can pass method name as key in hash

depreacate :name => :username, :deprecator => deprecator_instance

# ...

User.new.method
# => DEPRECATION WARNING: name is deprecated and will be removed from MyGem 2.0 (use username instead). (called from <main> at user.rb:18)

When key is kind of string we should get our custom message.

depreacate :name => 'custom message', :deprecator => deprecator_instance

# ...

User.new.method
# => DECATION WARNING: name is deprecated and will be removed from MyGem 2.0 (custom message). (called from <main> at user.rb:18)

You can also use warn method to notice user about deprecation.

class User
  def name
    ActiveSupport::Deprecation.warn("Don't user this method")
    @name
  end
end

User.new.name
# => DEPRECATION WARNING: Don't user this method. (called from name at user.rb:9)

Proxy objects

ActiveSupport::Deprecation provides several useful proxy object

  • proxy for whole object
  • proxy for const
  • proxy for instance variable

Default deprecator for all proxy objects are ActiveSupport::Deprecation instance. You can always pass an instance of deprecator as the last argument.

How to deprecate whole object?

What you should do when you need deprecate whole object with all methods? You need to create DeprecatedObjectProxy proxy object.

@user = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(User.new, "Don't use this object", deprecator_instance)
@user.name
# => DEPRECATION WARNING: Don't use this object. (called from <main> at user.rb:18)

When the instance method is called deprecator will notice user about deprecation.

How to deprecate instance variable?

You can always deprecate accessor method. But what you should do when user refer directly to instance variable?

class User
  def initialize
    @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator_instance)
    @_request = :a_request
  end

  def request
     @_request
   end

  def old_request
     @request
  end
end

When someone execute any method on @request variable this will trigger warn method on deprecator and will fetch @_request variable via request method and execute the same method on non-proxy instance variable.

How to deprecate const?

You need to create proxy const with DeprecatedConstantProxy

  OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)

Custom deprecator

You can use proxy object and deprecate method with custom deprecator. Custom deprecator instance need respond to one method:

  • deprecation_warning
def deprecation_warning(deprecated_method_name, message, caller_backtrace)
end

Example

class MyLib::Deprecator
  def deprecation_warning(deprecated_method_name, message, caller_backtrace)
    message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
    Kernel.warn message
  end
end

When we deprecate method

deprecate :foo => "this is very old method", 
          :deprecator => MyLib::Deprecator.new

How to use custom deprecator?

Proxy

ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 
                                                        'NEW_CONST', 
                                                        deprecator_instance)

depreacate method

depreacate :old_method => :new_method, :deprecator => deprecator_instance


blog comments powered by Disqus

Published

13 September 2012

Tags