在Ruby中标记废弃代码的最佳实践是什么?

149

我希望将一个方法标记为废弃的,这样使用该方法的人就可以轻松检查他们的代码并进行更新。在Java中,您可以设置@Deprecated注释来实现此目的。

那么,在Ruby中,是否有一种首选的方法(或甚至工具)来标记和检查废弃的方法?


公平地说,Java的注解很糟糕,因为它没有值可以指向一个潜在的替代品。 - Heiko Rupp
@HeikoRupp 我知道这已经晚了8年,但这种评论并没有真正帮助。 - Michael Campbell
11个回答

183

对于几乎所有情况来说,依赖库或元编程来进行弃用处理是过度的。只需在rdoc中添加注释并调用Kernel#warn方法即可。例如:

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

如果你使用Yard而不是rdoc,你的文档注释应该长成这样:
# @deprecated Please use {#useful} instead

最后,如果你遵循tomdoc,请将你的注释写成这样:
# Deprecated: Please use `useful` instead

已弃用: 表示该方法已被弃用,并将在未来版本中删除。您应该使用此选项来记录公共方法,但这些方法将在下一个主要版本中被删除。


此外,不要忘记在未来的某个版本中移除已弃用的方法(应该符合semver标准)。不要犯与Java库相同的错误。


4
我不确定这是否完全是Java的“错误”,而更多地是一个巨大的向后兼容性问题(参见https://dev59.com/iXRC5IYBdhLWcg3wYP-h),blindgaenger在他的Ruby代码中可能不需要考虑这个问题。 - VonC
46
代码是一种负担。你要尽可能少地维护代码。废除旧方法有助于暂时保持向后兼容性,但随着时间的推移会变得冗杂。如果人们需要使用已废弃的方法,他们应该使用你的库的较旧版本。 - Ryan McGeary
3
回答很好。我只想添加一个链接到回答中,展示我最近使用的方法,它依赖于 Ruby 标准库:https://dev59.com/OnVC5IYBdhLWcg3wcgqd#23554720 - Ricardo Valeriano
2
@RicardoValeriano 我同意,您的回复应该被整合(或更高投票,或两者兼备 :))。 - Felix

76
Ruby标准库中有一个带有警告逻辑的模块:https://ruby-doc.org/core-3.1.2/Gem/Deprecate.html。我倾向于使用它以一种“标准”的方式来维护我的弃用消息。
# 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.

请注意,通过这种方法,您将免费获得有关通话发生地的信息。

很好,我不知道标准库中有这个。 - Kris
2
数字字面量前导的 0 表示八进制,因此应该将其删除。 - Matt Whipple
7
谢谢你的提示。我已经弃用了整个类,并建议使用新的类:deprecate :initialize, UseThisClassInstead, 2017, 5 - Jon Kern
很棒的用法示例,Jon。真是一个很好的。 - Ricardo Valeriano
10
之前的正确答案已经被弃用,现在应该使用Ricardo Valueriano提供的答案。 - simon
非常感谢@lime;您能添加一个源链接吗? - gfd

21

使用ActiveSupport

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

默认情况下,在生产环境中警告被关闭。


14

你还可以使用ActiveSupport::Deprecation(在4.0+版本中可用),如下所示:

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)

14
如果你想要表现得很刻薄(伪装成在帮助),你可以在警告时打印出调用栈的第一行,让开发人员知道他们正在使用一个已废弃的调用。但这种做法是很刻薄的,因为我相信它会影响性能。
warn Kernel.caller.first + " whatever deprecation message here"

当正确使用时,这将包括已弃用调用所在文件和行的绝对路径。有关Kernel::caller的更多信息,请参见此处


5
我不认为这是刻薄的。一个小的性能损失比追踪废弃调用的位置要好,而且比方法最终被删除时出现故障要好得多。 - Nathan Long

8

您确实拥有 libdeprecated-ruby(2010-2012年发布,但在2015年后不再在rubygem上提供)

这是一个小型库,旨在帮助处理弃用代码的开发人员。
这个想法来自于“D”编程语言,开发人员可以将某些代码标记为已弃用,然后允许/禁止执行已弃用的代码。

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

该链接带我到一个关于Debian软件包的页面。这似乎类似(如果不是相同的),并且是一个RubyGem:https://rubygems.org/gems/deprecated - Benjamin Oakes

3
您可以使用类宏模式,编写以下内容:

您可以使用类宏模式来实现:

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

3
当使用Rails时,您可以使用Module#deprecate方法。

2
"Canivete"是一款宝石,可以让您以简单而优雅的方式弃用方法。更多关于它的信息请见这里

1

我最终拼凑出了一种轻量级的方法:

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

如果要废弃一个方法,可以在方法体中(或类的构造函数中)插入一个调用。

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

它相当声明性,并提供了相关信息的日志记录。我不是很擅长Ruby,所以可能需要一些微调/效果因人而异。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接