Best Practice zum Markieren von veraltetem Code in Ruby?

127

Ich möchte eine Methode als veraltet markieren, damit die Benutzer ihren Code leicht überprüfen und nachholen können. In Java setzen Sie @Deprecated und jeder weiß, was dies bedeutet.

Gibt es also eine bevorzugte Methode (oder sogar Tools) zum Markieren und Überprüfen auf Verwerfungen in Ruby?

blindgaenger
quelle
Um fair zu sein, ist Javas Anmerkung scheiße, da sie keinen Wert hat, auf einen möglichen Ersatz hinzuweisen
Heiko Rupp

Antworten:

160

In fast allen Fällen ist je nach Bibliothek oder Metaprogrammierung für eine Abwertung ein Overkill. Fügen Sie dem rdoc einfach einen Kommentar hinzu und rufen Sie die Kernel#warnMethode auf. Beispielsweise:

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

Wenn Sie Yard anstelle von rdoc verwenden , sollte Ihr Dokumentkommentar folgendermaßen aussehen:

# @deprecated Please use {#useful} instead

Wenn Sie sich an tomdoc halten , lassen Sie Ihren Kommentar folgendermaßen aussehen:

# Deprecated: Please use `useful` instead

Veraltet: Zeigt an, dass die Methode veraltet ist und in einer zukünftigen Version entfernt wird. Sie sollten dies verwenden, um Methoden zu dokumentieren, die öffentlich waren, aber bei der nächsten Hauptversion entfernt werden.


Auch nicht vergessen die veraltete Methode in einem zukünftigen (und ordnungsgemäß zu entfernen semver ‚d) Freisetzung . Machen Sie nicht die gleichen Fehler wie die Java-Bibliotheken.

Ryan McGeary
quelle
4
Ich bin mir nicht sicher, ob es sich um einen "Fehler" des Java-Teils handelt, sondern um ein großes Problem mit der Abwärtskompatibilität (siehe stackoverflow.com/questions/314540 ), das blindgaenger möglicherweise nicht für seinen Ruby-Code berücksichtigen muss.
VonC
38
Code ist eine Haftung. Je weniger Code Sie pflegen müssen, desto besser. Abwertungen sind gut für die vorübergehende Abwärtskompatibilität, aber im Laufe der Zeit wird es zu Kruft. Wenn die Menschen brauchen im Ruhestand Methoden verwenden , um, sollten sie stattdessen ältere Versionen Ihrer Bibliotheken.
Ryan McGeary
2
Hervorragende Antwort. Ich möchte nur einen Link zu der Antwort hinzufügen, in der ich den Ansatz zeige, den ich in letzter Zeit verwendet habe und der sich auf die Ruby Std Lib stützt: stackoverflow.com/questions/293981/…
Ricardo Valeriano
1
@ RicardoValeriano Ich stimme zu, Ihre Antwort sollte integriert sein (oder höher gewählt oder beides :)).
Felix
53

Die Ruby Standard Library verfügt über ein Modul mit der Warnlogik: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/Gem/Deprecate.html . Ich bevorzuge es, meine Verfallsnachrichten auf "normale" Weise zu pflegen:

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

Beachten Sie, dass Sie mit diesem Ansatz kostenlos Informationen darüber erhalten, wo der Anruf stattgefunden hat.

Ricardo Valeriano
quelle
Schön, wusste nichts davon in der Standardbibliothek.
Kris
2
Die Überschrift 0für ein numerisches Literal macht es oktal und sollte daher wahrscheinlich entfernt werden.
Matt Whipple
3
Danke für den Tipp. Ich habe eine ganze Klasse abgelehnt und vorgeschlagen, die neuere Klasse zu verwenden:deprecate :initialize, UseThisClassInstead, 2017, 5
Jon Kern
Tolles Anwendungsbeispiel, Jon. Wirklich schön.
Ricardo Valeriano
5
Die vorherige richtige Antwort ist veraltet und die Antwort von Ricardo Valueriano sollte jetzt verwendet werden
Simon
14

Wenn Sie gemein sein möchten (unter dem Deckmantel, hilfreich zu sein), können Sie die erste Zeile des Callstacks während einer Warnung ausdrucken, um den Entwicklern mitzuteilen, wo sie einen veralteten Anruf verwenden.

Das ist gemein, weil ich mir ziemlich sicher bin, dass es ein Performance-Hit ist.

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

Bei korrekter Verwendung enthält dies den absoluten Pfad zu der Datei und Zeile, in der der veraltete Aufruf verwendet wurde. Weitere Informationen zu Kernel :: caller finden Sie hier

Adam French
quelle
5
Ich halte das nicht für gemein. Ein kleiner Leistungstreffer ist schöner, als den veralteten Anruf verfolgen zu müssen, und viel schöner als etwas, das kaputt geht, wenn die Methode schließlich entfernt wird.
Nathan Long
13

Verwenden von ActiveSupport:

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

Warnungen sind in der Produktionsumgebung standardmäßig deaktiviert

Artur Beljajev
quelle
12

Sie können auch ActiveSupport::Deprecation(verfügbar in Version 4.0+) als solches verwenden:

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
quelle
8

Sie haben libdeprecated-ruby(2010-2012, 2015 nicht mehr auf rubygem verfügbar)

Eine kleine Bibliothek, die Entwicklern helfen soll, mit veraltetem Code zu arbeiten.
Die Idee stammt aus der DProgrammiersprache ' ', in der Entwickler bestimmten Code als veraltet markieren und dann die Möglichkeit erlauben / ablehnen können, veralteten Code auszuführen.

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
quelle
Der Link führt mich zu einer Seite über ein Debian-Paket. Dies scheint ähnlich (wenn nicht gleich) und ist ein RubyGem: rubygems.org/gems/deprecated
Benjamin Oakes
3

Sie können das Muster "Klassenmakros" verwenden und Folgendes schreiben:

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
Alex Djioev
quelle
3

Wenn Sie Schienen verwenden, haben Sie die Methode "Modul # veraltet".

Pedro Rolo
quelle
2

Canivete ist ein Juwel, mit dem Sie Ihre Methoden auf einfache und elegante Weise ablehnen können. Ein bisschen mehr darüber hier .

Skalee
quelle
1

Am Ende habe ich eine leichte Methode zusammengestellt:

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

Um dann eine Methode zu verwerfen, fügen Sie einen Aufruf in den Methodenkörper (oder einen Konstruktor für eine Klasse) ein.

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

Es ist ziemlich deklarativ und bietet Protokollierung mit relevanten Informationen. Ich bin kein großer Rubyist, daher muss möglicherweise etwas an YMMV angepasst werden.

Matt Whipple
quelle
0

Wir können interne Makromethoden verwenden. Beispiel:

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) 

Ende Ende

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

o = Foo.new p oa

EVN Raja
quelle