Prática recomendada para marcar código obsoleto no Ruby?

127

Gostaria de marcar um método como obsoleto, para que as pessoas que o utilizam possam verificar facilmente seu código e atualizá-lo. Em Java, você define @ Deprecated e todo mundo sabe o que isso significa.

Então, existe uma maneira preferida (ou mesmo ferramentas) de marcar e verificar deprecações no Ruby?

cego
fonte
Para ser justo, anotação de Java é uma porcaria, como ele não tem nenhum valor para apontar para um potencial substituto
Heiko Rupp

Respostas:

160

Para quase todos os casos, dependendo de uma biblioteca ou metaprogramação para uma depreciação, é um exagero. Basta adicionar um comentário ao rdoc e chamar o Kernel#warnmétodo Por exemplo:

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

Se você estiver usando o Yard em vez do rdoc , o comentário do seu documento deverá ficar assim:

# @deprecated Please use {#useful} instead

Por fim, se você aderir ao tomdoc , faça seu comentário assim:

# Deprecated: Please use `useful` instead

Descontinuado: indica que o método está descontinuado e será removido em uma versão futura. Você DEVE usar isso para documentar métodos que eram Públicos, mas serão removidos na próxima versão principal.


Além disso, não se esqueça de remover o método descontinuado em alguma versão futura (e corretamente sempre ) . Não cometa os mesmos erros que as bibliotecas Java.

Ryan McGeary
fonte
4
Não tenho certeza de que seja tanto um "erro" da parte Java, mas um enorme problema de compatibilidade com versões anteriores (consulte stackoverflow.com/questions/314540 ), que cegogaenger talvez não precise considerar para seu código Ruby.
VonC 31/12/08
38
Código é uma responsabilidade. Quanto menos código você tiver para manter, melhor. As preterições são boas para compatibilidade com versões anteriores temporárias, mas, com o tempo, elas se tornam um problema. Se as pessoas precisarem usar métodos aposentados, deverão usar versões mais antigas de suas bibliotecas.
Ryan McGeary
2
Excelente resposta. Eu só quero adicionar um link para a resposta onde eu mostrar a abordagem que eu usei recentemente, que conta com o rubi Std Lib: stackoverflow.com/questions/293981/...
Ricardo Valeriano
1
@RicardoValeriano Eu concordo, sua resposta deve ser integrada (ou mais votada, ou ambas :)).
Felix
53

A Biblioteca Padrão do Ruby possui um módulo com a lógica de aviso: https://ruby-doc.org/stdlib/libdoc/rubygems/rdoc/Gem/Deprecate.html . Costumo preferir manter minhas mensagens de reprovação de uma maneira "padrão":

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

Observe que, com essa abordagem, você obterá informações gratuitas sobre o local da chamada.

Ricardo Valeriano
fonte
Bom, não sabia disso na biblioteca padrão.
Kris
2
o início 0de um literal numérico o torna octal e, portanto, provavelmente deve ser removido.
Matt Whipple
3
Obrigado pela dica. Eu preteri uma classe inteira e sugeri que a classe mais nova fosse usada:deprecate :initialize, UseThisClassInstead, 2017, 5
Jon Kern
Ótimo exemplo de uso, Jon. Muito bom.
Ricardo Valeriano
5
A resposta correta anterior foi substituído e a resposta por Ricardo Valueriano agora deve ser usado
simon
14

Se você quiser ser mesquinho (sob o truque de ser útil), poderá imprimir a primeira linha da pilha de chamadas durante um aviso para informar aos desenvolvedores onde eles estão usando uma chamada obsoleta.

Isso é mau porque tenho certeza de que é um sucesso.

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

Quando usado corretamente, isso inclui o caminho absoluto para o arquivo e a linha em que a chamada descontinuada foi usada. Mais informações sobre o Kernel :: caller estão disponíveis aqui

Adam French
fonte
5
Eu não considero isso mau. Um pequeno impacto no desempenho é mais agradável do que ter que procurar onde estava a chamada descontinuada e muito melhor do que algo interrompido quando o método é finalmente removido.
Nathan Long
13

Usando o ActiveSupport:

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

Os avisos são desativados no ambiente de produção por padrão

Artur Beljajev
fonte
12

Você também pode usar ActiveSupport::Deprecation(disponível na versão 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
fonte
8

Você realmente tem libdeprecated-ruby (2010-2012, não está mais disponível no rubygem em 2015)

Uma pequena biblioteca destinada a ajudar desenvolvedores que trabalham com código obsoleto.
A ideia vem da Dlinguagem de programação ' ', na qual os desenvolvedores podem marcar determinado código como obsoleto e depois permitir / não permitir a capacidade de executar 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
fonte
O link me leva a uma página sobre um pacote Debian. Isso parece semelhante (se não for o mesmo) e é um RubyGem: rubygems.org/gems/deprecated
Benjamin Oakes
3

Você pode usar o padrão de macros de classe e escrever algo como isto:

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

Ao usar trilhos, você tem o método Module # deprecate.

Pedro Rolo
fonte
2

Canivete é uma jóia que permite descontinuar seus métodos de maneira simples e elegante. Um pouco mais sobre isso aqui .

skalee
fonte
1

Acabei lançando um método leve:

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

Em seguida, para descontinuar um método, insira uma chamada no corpo do método (ou um construtor para uma classe)

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

É bastante declarativo e fornece informações relevantes ao registro. Eu não sou muito rubiista, então pode precisar de alguns ajustes / YMMV.

Matt Whipple
fonte
0

Podemos usar métodos de macros internos. Exemplo:

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) 

fim fim

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

o = Foo.new p oa

EVN Raja
fonte