Best practice to mark deprecated code in Ruby?

RubyDeprecated

Ruby Problem Overview


I'd like to mark a method as deprecated, so the people using it can easily check their code and catch up. In Java you set @Deprecated and everybody knows what this means.

So is there a preferred way (or even tools) to mark and check for deprecations in Ruby?

Ruby Solutions


Solution 1 - Ruby

For almost all cases, depending on a library or metaprogramming for a deprecation is overkill. Just add a comment to the rdoc and call the Kernel#warn method. For example:

class Foo
  # <b>DEPRECATED:</b> Please use <tt>useful</tt> instead.
  def useless
    warn "[DEPRECATION] `useless` is deprecated.  Please use `useful` instead."
    useful
  end

  def useful
    # ...
  end
end

If you're using Yard instead of rdoc, your doc comment should look like this:

# @deprecated Please use {#useful} instead

Lastly, if you adhere to tomdoc, make your comment look like this:

# Deprecated: Please use `useful` instead

> Deprecated: Indicates that the method is deprecated and will be removed in a future version. You SHOULD use this to document methods that were Public but will be removed at the next major version.


Also, don't forget to remove the deprecated method in some future (and properly semver'd) release. Don't make the same mistakes that the Java libraries did.

Solution 2 - Ruby

Ruby Standard Library has a module with the warning logic: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/Gem/Deprecate.html. I tend to prefer it to maintain my deprecation messages in a "standard" way:

# my_file.rb

class MyFile
  extend Gem::Deprecate

  def no_more
    close
  end
  deprecate :no_more, :close, 2015, 5

  def close
    # new logic here
  end
end

MyFile.new.no_more
# => NOTE: MyFile#no_more is deprecated; use close instead. It will be removed on or after 2015-05-01.
# => MyFile#no_more called from my_file.rb:16.

Note that with this approach you will gain for free information about where the call took place.

Solution 3 - Ruby

Using ActiveSupport:

class Player < ActiveRecord::Base
  def to_s
    ActiveSupport::Deprecation.warn('Use presenter instead')
    partner_uid
  end
end

Warnings are turned off in production environment by default

Solution 4 - Ruby

If you want to be mean (under the guile of being helpful) you can print out the first line of the callstack during a warning to let devs know where they are using a deprecated call.

This is mean because I'm pretty sure it's a performance-hit.

warn Kernel.caller.first + " whatever deprecation message here"

When used correctly, this will include the absolute path to the file and line where the deprecated call was used. More information about Kernel::caller is available here

Solution 5 - Ruby

You can also use ActiveSupport::Deprecation (available in version 4.0+), as such:

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

class MyGem
  def self.deprecator
    ActiveSupport::Deprecation.new('2.0', 'MyGem')
  end

  def old_method
  end

  def new_method
  end

  deprecate old_method: :new_method, deprecator: deprecator
end

MyGem.new.old_method
# => DEPRECATION WARNING: old_method is deprecated and will be removed from MyGem 2.0 (use new_method instead). (called from <main> at file.rb:18)

Solution 6 - Ruby

You do have libdeprecated-ruby (2010-2012, not available anymore on rubygem in 2015)

A small library intended to aid developers working with deprecated code.
The idea comes from the 'D' programming language, where developers can mark certain code as deprecated, and then allow/disallow the ability to execute deprecated code.

require 'lib/deprecated.rb'
require 'test/unit'

# this class is used to test the deprecate functionality
class DummyClass
  def monkey
    return true
  end

  deprecate :monkey
end

# we want exceptions for testing here.
Deprecate.set_action(:throw)

class DeprecateTest < Test::Unit::TestCase
  def test_set_action
     
    assert_raise(DeprecatedError) { raise StandardError.new unless DummyClass.new.monkey }

    Deprecate.set_action(proc { |msg| raise DeprecatedError.new("#{msg} is deprecated.") })

    assert_raise(DeprecatedError) { raise StandardError.new unless DummyClass.new.monkey }
   

    # set to warn and make sure our return values are getting through.
    Deprecate.set_action(:warn)
    
    assert_nothing_raised(DeprecatedError) { raise StandardError.new unless DummyClass.new.monkey } 
  end
end

Solution 7 - Ruby

You can use Class Macros pattern and write something like this:

class Module     
     def deprecate(old_method, new_method)
          define_method(old_method) do |*args, &block|
               warn "Method #{old_method}() depricated. Use #{new_method}() instead"
               send(new_method, *args, &block)
          end
     end
end


class Test
     def my_new_method
          p "My method"
     end

     deprecate :my_old_method, :my_method
end

Solution 8 - Ruby

When using rails, you have the Module#deprecate method.

Solution 9 - Ruby

Canivete is a gem which enables you to deprecate your methods in simple and elegant way. A little more about it here.

Solution 10 - Ruby

I ended up throwing together a lightweight method:

def deprecate(msg)
  method = caller_locations(1, 1).first.label
  source = caller(2, 1).first
  warn "#{method} is deprecated: #{msg}\ncalled at #{source}"
end

Then to deprecate a method insert a call in the method body (or a constructor for a class)

def foo
  deprecate 'prefer bar, will be removed in version 3'
  ...
end

It's fairly declarative and provides logging with relevant info. I'm not much of a Rubyist so it may need some tweaking/YMMV.

Solution 11 - Ruby

We can use internal macros methods. Example:

class Foo def get_a; puts "I'm an A" end def get_b; puts "I'm an B" end def get_c; puts "I'm an C" end

def self.deprecate(old_method, new_method)
  define_method(old_method) do |*args, &block|
     puts "Warning: #{old_method} is deprecated! Use #{new_method} instead"
     send(new_method, *args, &block) 

end end

deprecate :a, :get_a deprecate :b, :get_b deprecate :c, :get_c end

o = Foo.new p o.a

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionblindgaengerView Question on Stackoverflow
Solution 1 - RubyRyan McGearyView Answer on Stackoverflow
Solution 2 - RubyRicardo ValerianoView Answer on Stackoverflow
Solution 3 - RubyArturView Answer on Stackoverflow
Solution 4 - RubyAdam FrenchView Answer on Stackoverflow
Solution 5 - RubyKrisView Answer on Stackoverflow
Solution 6 - RubyVonCView Answer on Stackoverflow
Solution 7 - RubyAlex DjioevView Answer on Stackoverflow
Solution 8 - RubyPedro RoloView Answer on Stackoverflow
Solution 9 - RubyskaleeView Answer on Stackoverflow
Solution 10 - RubyMatt WhippleView Answer on Stackoverflow
Solution 11 - RubyE V N RajaView Answer on Stackoverflow