Лучшая практика для отметки устаревшего кода в Ruby?

127

Я хотел бы отметить метод как устаревший, чтобы люди, использующие его, могли легко проверить свой код и наверстать упущенное. В Java вы устанавливаете @Deprecated, и все знают, что это значит.

Итак, есть ли предпочтительный способ (или даже инструменты) отмечать и проверять устаревшие версии Ruby?

blindgaenger
источник
Честно говоря, аннотация Java - отстой, поскольку она не имеет ценности, чтобы указать на потенциальную замену
Хайко Рупп

Ответы:

160

Практически во всех случаях, в зависимости от библиотеки или метапрограммирования, устаревание является излишним. Просто добавьте комментарий к 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

Устаревший: означает, что метод устарел и будет удален в будущей версии. Вы ДОЛЖНЫ использовать это для документирования методов, которые были общедоступными, но будут удалены в следующей основной версии.


Кроме того, не забудьте удалить устаревший метод в каком-нибудь будущем (и правильном варианте ) . Не делайте тех же ошибок, что и библиотеки Java.

Райан МакГири
источник
4
Я не уверена , что это так «ошибка» со стороны Java, а огромная проблема обратной совместимости (см stackoverflow.com/questions/314540 ), что blindgaenger не возможно , потребуется рассмотреть его коду Ruby.
VonC
38
Код - это ответственность. Чем меньше кода придется поддерживать, тем лучше. Устаревшие версии хороши для временной обратной совместимости, но со временем они становятся бесполезными. Если людям нужно использовать устаревшие методы, им следует использовать вместо них более старые версии ваших библиотек.
Райан МакГири,
2
Отличный ответ. Я просто хочу добавить ссылку на ответ, где я покажу подход, который я использовал в последнее время, который опирается на Ruby Std Lib: stackoverflow.com/questions/293981/…
Рикардо Валериано
1
@RicardoValeriano Я согласен, ваш ответ должен быть интегрированным (или более высоким, или и тем, и другим :)).
Феликс
54

В стандартной библиотеке Ruby есть модуль с логикой предупреждений: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/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.

Обратите внимание, что при таком подходе вы бесплатно получите информацию о том, где был звонок.

Рикардо Валериано
источник
Приятно, не знал об этом в стандартной библиотеке.
Крис
2
ведущий 0для числового литерала делает его восьмеричным, поэтому его, вероятно, следует удалить.
Мэтт Уиппл
3
Спасибо за чаевые. Я отказался от всего класса и предложил использовать новый класс:deprecate :initialize, UseThisClassInstead, 2017, 5
Джон Керн,
Отличный пример использования, Джон. Действительно хороший.
Рикардо Валериано,
5
Предыдущий правильный ответ был устаревшие и ответ Рикардо Valueriano должен теперь быть использован
SIMON
14

Если вы хотите быть злым (под видом полезности), вы можете распечатать первую строку стека вызовов во время предупреждения, чтобы разработчики знали, где они используют устаревший вызов.

Это плохо, потому что я почти уверен, что это удар по производительности.

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

При правильном использовании это будет включать абсолютный путь к файлу и строку, в которой использовался устаревший вызов. Более подробная информация о Kernel :: caller доступна здесь.

Адам Френч
источник
5
Я не считаю это плохим. Небольшое снижение производительности лучше, чем необходимость искать место, где находился устаревший вызов, и намного приятнее, чем что-то ломающееся, когда метод в конечном итоге удаляется.
Натан Лонг
13

Использование ActiveSupport:

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

По умолчанию предупреждения отключены в производственной среде.

Артур Беляев
источник
12

Вы также можете использовать 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)
Kris
источник
8

У тебя есть libdeprecated-ruby (2010-2012, больше не доступно на rubygem в 2015 году)

Небольшая библиотека, предназначенная для помощи разработчикам, работающим с устаревшим кодом.
Идея исходит из 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
VonC
источник
По ссылке я попадаю на страницу о пакете Debian. Это похоже (если не то же самое) и является RubyGem: rubygems.org/gems/deprecated
Бенджамин Оукс
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

При использовании рельсов у вас есть метод устаревшего модуля #.

Педро Роло
источник
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

Это довольно декларативно и обеспечивает ведение журнала с соответствующей информацией. Я не особо разбираюсь в Rubyist, поэтому может потребоваться некоторая настройка / YMMV.

Мэтт Уиппл
источник
0

Мы можем использовать внутренние методы макросов. Пример:

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) 

конец конец

устаревшие: a,: get_a устаревшие: b,: get_b устаревшие: c,: get_c end

o = Foo.new p oa

EVN Raja
источник