¿Mejor práctica para marcar código obsoleto en Ruby?

127

Me gustaría marcar un método como obsoleto, para que las personas que lo usan puedan verificar fácilmente su código y ponerse al día. En Java, establece @Deprecated y todos saben lo que esto significa.

Entonces, ¿hay una forma preferida (o incluso herramientas) para marcar y verificar las desvalorizaciones en Ruby?

pasajero
fuente
Para ser justos, la anotación de Java apesta, ya que no tiene ningún valor para señalar un reemplazo potencial
Heiko Rupp

Respuestas:

160

Para casi todos los casos, depender de una biblioteca o metaprogramación para un desuso es excesivo. Simplemente agregue un comentario al rdoc y llame al Kernel#warnmétodo. Por ejemplo:

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 está utilizando Yard en lugar de rdoc , su comentario de documento debería verse así:

# @deprecated Please use {#useful} instead

Por último, si se adhiere a tomdoc , haga que su comentario se vea así:

# Deprecated: Please use `useful` instead

En desuso: indica que el método está en desuso y se eliminará en una versión futura. DEBE usar esto para documentar métodos que eran públicos pero que se eliminarán en la próxima versión principal.


Además, no olvide eliminar el método obsoleto en algún lanzamiento futuro (y correctamente semver 'd) . No cometas los mismos errores que las bibliotecas de Java.

Ryan McGeary
fuente
44
No estoy seguro de que sea tanto un "error" de la parte de Java, sino un gran problema de compatibilidad con versiones anteriores (consulte stackoverflow.com/questions/314540 ), que el ocultador podría no tener en cuenta para su código Ruby.
VonC
38
El código es una responsabilidad. Cuanto menos código tenga que mantener, mejor. Las desvalorizaciones son buenas para la compatibilidad temporal con versiones anteriores, pero con el tiempo se vuelven crudas. Si las personas necesitan usar métodos retirados, deberían usar versiones anteriores de sus bibliotecas.
Ryan McGeary
2
Excelente respuesta Solo quiero agregar un enlace a la respuesta donde muestro el enfoque que he usado últimamente, que se basa en Ruby Std Lib: stackoverflow.com/questions/293981/…
Ricardo Valeriano
1
@RicardoValeriano Estoy de acuerdo, su respuesta debe estar integrada (o más votada, o ambas :)).
Felix
54

Ruby Standard Library tiene un módulo con la lógica de advertencia: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/Gem/Deprecate.html . Tiendo a preferirlo para mantener mis mensajes de desaprobación de una manera "estándar":

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

Tenga en cuenta que con este enfoque obtendrá información gratuita sobre dónde se realizó la llamada.

Ricardo Valeriano
fuente
Bien, no sabía sobre esto en lib estándar.
Kris
2
el comienzo 0de un literal numérico lo hace octal y, por lo tanto, probablemente debería eliminarse.
Matt Whipple
3
Gracias por el consejo. En desuso una clase entera, y sugerí que se usara la clase más nueva:deprecate :initialize, UseThisClassInstead, 2017, 5
Jon Kern
Gran ejemplo de uso, Jon. Una muy buena.
Ricardo Valeriano
55
La respuesta correcta anterior ha quedado en desuso y la respuesta de Ricardo Valueriano ahora debe usarse
Simon
14

Si quieres ser malo (bajo la astucia de ser útil), puedes imprimir la primera línea de la pila de llamadas durante una advertencia para que los desarrolladores sepan dónde están usando una llamada obsoleta.

Esto es malo porque estoy bastante seguro de que es un éxito en el rendimiento.

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

Cuando se usa correctamente, esto incluirá la ruta absoluta al archivo y la línea donde se usó la llamada en desuso. Más información sobre Kernel :: llamador está disponible aquí

Adam French
fuente
55
No considero esto malo. Un pequeño impacto en el rendimiento es mejor que tener que perseguir dónde estaba la llamada en desuso, y mucho mejor que algo que se rompe cuando el método finalmente se elimina.
Nathan Long
13

Usando ActiveSupport:

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

Las advertencias están desactivadas en el entorno de producción de forma predeterminada.

Artur Beljajev
fuente
12

También puede usar ActiveSupport::Deprecation(disponible en la versión 4.0+), como tal:

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

Usted tiene libdeprecated-ruby (2010-2012, ya no está disponible en rubygem en 2015)

Una pequeña biblioteca destinada a ayudar a los desarrolladores que trabajan con código obsoleto.
La idea proviene del Dlenguaje de programación ' ', donde los desarrolladores pueden marcar cierto código como obsoleto y luego permitir / rechazar la capacidad de ejecutar código obsoleto.

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
fuente
El enlace me lleva a una página sobre un paquete Debian. Esto parece similar (si no es lo mismo) y es un RubyGem: rubygems.org/gems/deprecated
Benjamin Oakes
3

Puede usar el patrón Class Macros y escribir algo como esto:

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
fuente
3

Al usar rieles, tiene el método de desaprobación Módulo #.

Pedro Rolo
fuente
2

Canivete es una gema que le permite desaprobar sus métodos de manera simple y elegante. Un poco más al respecto aquí .

skalee
fuente
1

Terminé lanzando un método liviano:

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

Luego, para desaprobar un método, inserte una llamada en el cuerpo del método (o un constructor para una clase)

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

Es bastante declarativo y proporciona registros con información relevante. No soy muy Rubyist, por lo que puede necesitar algunos ajustes / YMMV.

Matt Whipple
fuente
0

Podemos usar métodos internos de macros. Ejemplo:

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

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

o = Foo.new p oa

EVN Raja
fuente