Najlepsza praktyka oznaczania nieaktualnego kodu w Rubim?


128

Chciałbym oznaczyć metodę jako przestarzałą, aby osoby jej używające mogły łatwo sprawdzić swój kod i nadrobić zaległości. W Javie ustawiasz @Deprecated i wszyscy wiedzą, co to oznacza.

Czy jest więc preferowany sposób (a nawet narzędzia) zaznaczania i sprawdzania przestarzałych elementów w Rubim?


Aby być uczciwym, adnotacja Javy jest do niczego, ponieważ nie ma wartości, aby wskazywać na potencjalną wymianę
Heiko Rupp

Odpowiedzi:


161

W prawie wszystkich przypadkach, zależność od biblioteki lub metaprogramowania dla wycofania jest przesadą. Po prostu dodaj komentarz do rdoc i wywołaj Kernel#warnmetodę. Na przykład:

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

Jeśli używasz Yard zamiast rdoc , Twój komentarz do dokumentu powinien wyglądać następująco:

# @deprecated Please use {#useful} instead

Na koniec, jeśli zastosujesz się do tomdoc , spraw , aby Twój komentarz wyglądał następująco:

# Deprecated: Please use `useful` instead

Przestarzałe: wskazuje, że metoda jest przestarzała i zostanie usunięta w przyszłej wersji. POWINIENEŚ użyć tego do udokumentowania metod, które były publiczne, ale zostaną usunięte w następnej głównej wersji.


Ponadto, nie zapomnij usunąć przestarzałej metody w jakiejś przyszłości (i właściwie semver „d) rozwiązanie . Nie popełniaj tych samych błędów, które popełniały biblioteki Java.


4
Nie jestem pewien, czy jest to tak bardzo „błąd” ze strony języka Java, a raczej ogromny problem z kompatybilnością wsteczną (patrz stackoverflow.com/questions/314540 ), że blindgaenger może nie musieć brać pod uwagę swojego kodu Ruby.
VonC

39
Kod jest zobowiązaniem. Im mniej kodu musisz utrzymywać, tym lepiej. Wycofanie jest dobre dla tymczasowej kompatybilności wstecznej, ale z czasem staje się okrucieństwo. Jeśli ludzie muszą używać wycofanych metod, powinni zamiast tego używać starszych wersji twoich bibliotek.
Ryan McGeary

2
Doskonała odpowiedź. Chcę tylko dodać link do odpowiedzi, w którym pokażę podejście, które ostatnio zastosowałem, które opiera się na Ruby Std Lib: stackoverflow.com/questions/293981/…
Ricardo Valeriano

1
@RicardoValeriano Zgadzam się, twoja odpowiedź powinna być zintegrowana (lub wyżej głosowana, albo obie :)).
Felix

54

Ruby Standard Library ma moduł z logiką ostrzegawczą: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/Gem/Deprecate.html . Wolę utrzymywać wiadomości o wycofaniu w „standardowy” sposób:

# 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.

Zwróć uwagę, że dzięki takiemu podejściu uzyskasz bezpłatne informacje o tym, gdzie odbyło się połączenie.


Fajnie, nie wiedziałem o tym w standardowym lib.
Kris

2
wiodący 0w literale numerycznym powoduje, że jest on ósemkowy, więc prawdopodobnie powinien zostać usunięty.
Matt Whipple,

3
Dzięki za wskazówkę. Zrezygnowałem z całej klasy i zasugerowałem użycie nowszej klasy:deprecate :initialize, UseThisClassInstead, 2017, 5
Jon Kern,

Świetny przykład użycia, Jon. Naprawdę fajny.
Ricardo Valeriano

5
Poprzednia poprawna odpowiedź została wycofana i należy teraz użyć odpowiedzi Ricardo Valueriano
simon

14

Jeśli chcesz być wredny (pod pretekstem bycia pomocnym), możesz wydrukować pierwszą linię stosu wywołań podczas ostrzeżenia, aby poinformować programistów, gdzie używają przestarzałego wywołania.

To znaczy, ponieważ jestem prawie pewien, że to hit wydajnościowy.

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

Jeśli jest używany poprawnie, będzie zawierał bezwzględną ścieżkę do pliku i wiersz, w którym użyto przestarzałego wywołania. Więcej informacji o Kernel :: caller jest dostępnych tutaj


5
Nie uważam tego za podłe. Małe uderzenie w wydajność jest przyjemniejsze niż szukanie miejsca, w którym było przestarzałe wywołanie, i znacznie przyjemniejsze niż coś zepsutego, gdy metoda zostanie ostatecznie usunięta.
Nathan Long

13

Korzystanie z ActiveSupport:

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

Ostrzeżenia są domyślnie wyłączone w środowisku produkcyjnym


12

Możesz także użyć ActiveSupport::Deprecation(dostępne w wersji 4.0+), jako takich:

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)

8

Masz libdeprecated-ruby(2010-2012, już niedostępny na rubygem w 2015)

Mała biblioteka mająca na celu pomóc programistom w pracy z przestarzałym kodem.
Pomysł pochodzi z Djęzyka programowania „ ”, w którym programiści mogą oznaczyć określony kod jako przestarzały, a następnie zezwolić / zabronić wykonywania przestarzałego kodu.

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

Odsyłacz prowadzi mnie do strony o pakiecie Debiana. Wydaje się, że jest podobny (jeśli nie taki sam) i jest RubyGem: rubygems.org/gems/deprecated
Benjamin Oakes

3

Możesz użyć wzorca Class Macros i napisać coś takiego:

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


2

Canivete to perełka, która pozwala w prosty i elegancki sposób zdeprecjonować swoje metody. Trochę więcej na ten temat tutaj .


1

Skończyło się na połączeniu lekkiej metody:

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

Następnie, aby zrezygnować z metody, wstaw wywołanie do treści metody (lub konstruktora dla klasy)

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

Jest dość deklaratywny i zapewnia logowanie z odpowiednimi informacjami. Nie jestem zbyt Rubyistą, więc może wymagać pewnych poprawek / YMMV.


0

Możemy użyć wewnętrznych metod makr. Przykład:

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) 

koniec koniec

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

o = Foo.new p oa

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.