Meilleure pratique pour marquer le code obsolète dans Ruby?


127

Je voudrais marquer une méthode comme obsolète, afin que les personnes qui l'utilisent puissent facilement vérifier leur code et rattraper leur retard. En Java, vous définissez @Deprecated et tout le monde sait ce que cela signifie.

Existe-t-il donc un moyen préféré (ou même des outils) de marquer et de vérifier les dépréciations dans Ruby?


Pour être juste, l'annotation de Java est nul, car elle n'a aucune valeur pour pointer vers un remplacement potentiel
Heiko Rupp

Réponses:


160

Dans presque tous les cas, dépendre d'une bibliothèque ou d'une métaprogrammation pour une obsolescence est excessif. Ajoutez simplement un commentaire au rdoc et appelez la Kernel#warnméthode. Par exemple:

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

Si vous utilisez Yard au lieu de rdoc , votre commentaire de doc devrait ressembler à ceci:

# @deprecated Please use {#useful} instead

Enfin, si vous adhérez à tomdoc , faites ressembler votre commentaire à ceci:

# Deprecated: Please use `useful` instead

Obsolète: indique que la méthode est obsolète et sera supprimée dans une version ultérieure. Vous DEVRIEZ l'utiliser pour documenter les méthodes qui étaient publiques mais qui seront supprimées à la prochaine version majeure.


En outre, ne pas oublier d'enlever la méthode désapprouvée dans un avenir (et bien semver version « d) . Ne faites pas les mêmes erreurs que les bibliothèques Java.


4
Je ne suis pas sûr que ce soit tant une "erreur" de la partie Java, plutôt un énorme problème de compatibilité descendante (voir stackoverflow.com/questions/314540 ), que blindgaenger pourrait ne pas avoir besoin de prendre en compte pour son code Ruby.
VonC

38
Le code est une responsabilité. Moins vous avez de code à maintenir, mieux c'est. Les dépréciations sont bonnes pour la rétrocompatibilité temporaire, mais avec le temps elles deviennent cruelles. Si les utilisateurs doivent utiliser des méthodes retirées, ils doivent utiliser des versions plus anciennes de vos bibliothèques à la place.
Ryan McGeary

2
Excellente réponse. Je veux juste ajouter un lien vers la réponse où je montre l'approche que j'ai utilisée ces derniers temps, qui repose sur le Ruby Std Lib: stackoverflow.com/questions/293981/…
Ricardo Valeriano

1
@RicardoValeriano Je suis d'accord, votre réponse doit être intégrée (ou votée plus haut, ou les deux :)).
Felix

54

Ruby Standard Library a un module avec la logique d'avertissement: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/Gem/Deprecate.html . J'ai tendance à le préférer pour conserver mes messages d'obsolescence de manière "standard":

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

Notez qu'avec cette approche, vous obtiendrez gratuitement des informations sur l'endroit où l'appel a eu lieu.


Bien, je n'en savais rien dans la lib standard.
Kris

2
le début 0d'un littéral numérique le rend octal et devrait donc probablement être supprimé.
Matt Whipple

3
Merci pour le conseil. J'ai déprécié une classe entière et j'ai suggéré la nouvelle classe à utiliser:deprecate :initialize, UseThisClassInstead, 2017, 5
Jon Kern

Excellent exemple d'utilisation, Jon. Vraiment sympa.
Ricardo Valeriano

5
La réponse correcte précédente est obsolète et la réponse de Ricardo Valueriano devrait maintenant être utilisée
simon

14

Si vous voulez être méchant (sous prétexte d'être utile), vous pouvez imprimer la première ligne de la pile d'appels pendant un avertissement pour indiquer aux développeurs où ils utilisent un appel obsolète.

C'est méchant parce que je suis presque sûr que c'est un succès.

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

Lorsqu'il est utilisé correctement, cela inclura le chemin absolu vers le fichier et la ligne où l'appel obsolète a été utilisé. Plus d'informations sur Kernel :: caller sont disponibles ici


5
Je ne considère pas cela comme un moyen. Un petit problème de performance est plus agréable que d'avoir à rechercher où l'appel obsolète était, et beaucoup plus agréable que quelque chose qui se brise lorsque la méthode est finalement supprimée.
Nathan Long

13

Utilisation d'ActiveSupport:

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

Les avertissements sont désactivés par défaut dans l'environnement de production


12

Vous pouvez également utiliser ActiveSupport::Deprecation(disponible dans la version 4.0+), en tant que tel:

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

Vous avez libdeprecated-ruby (2010-2012, plus disponible sur rubygem en 2015)

Une petite bibliothèque destinée à aider les développeurs à travailler avec du code obsolète.
L'idée vient du ' D' langage de programmation, où les développeurs peuvent marquer certains codes comme obsolètes, puis autoriser / interdire la possibilité d'exécuter du code obsolète.

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

Le lien me mène à une page sur un paquet Debian. Cela semble similaire (sinon le même) et est un RubyGem: rubygems.org/gems/deprecated
Benjamin Oakes

3

Vous pouvez utiliser le modèle de macros de classe et écrire quelque chose comme ceci:

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

Lorsque vous utilisez des rails, vous disposez de la méthode Module # deprecate.


2

Canivete est un joyau qui vous permet de déprécier vos méthodes de manière simple et élégante. Un peu plus à ce sujet ici .


1

J'ai fini par lancer une méthode légère:

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

Ensuite, pour déprécier une méthode, insérez un appel dans le corps de la méthode (ou un constructeur pour une classe)

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

C'est assez déclaratif et fournit une journalisation avec des informations pertinentes. Je ne suis pas vraiment un Rubyiste, donc il faudra peut-être peaufiner / YMMV.


0

Nous pouvons utiliser des méthodes de macros internes. Exemple:

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) 

fin fin

obsolète: a,: get_a obsolète: b,: get_b obsolète: c,: get_c end

o = Foo.new p oa

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.