Best Practice to Mark Deprecated Code in Ruby

Best practice to mark deprecated code in Ruby?

For almost all cases, depending on a library or metaprogramming for a deprecation is overkill. Just add a comment to the rdoc and call the Kernel#warn method. For example:

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

If you're using Yard instead of rdoc, your doc comment should look like this:

# @deprecated Please use {#useful} instead

Lastly, if you adhere to tomdoc, make your comment look like this:

# Deprecated: Please use `useful` instead

Deprecated: Indicates that the method is deprecated and will be removed in a future version. You SHOULD use this to document methods that were Public but will be removed at the next major version.


Also, don't forget to remove the deprecated method in some future (and properly semver'd) release. Don't make the same mistakes that the Java libraries did.

How to mark a class as Deprecated in Ruby?

You can use const_missing to deprecate constants, and, by extension, classes.

const_missing is being invoked when an undefined constant is referenced.

module MyModule

class PaymentMethod
# ...
end

def self.const_missing(const_name)
super unless const_name == :BillingMethod
warn "DEPRECATION WARNING: the class MyModule::BillingMethod is deprecated. Use MyModule::PaymentMethod instead."
PaymentMethod
end
end

This allows existing code that references MyModule::BillingMethod to continue to work, and warns the user about their use of the deprecated class.

It's the best I've seen so far for the deprecating class purpose.

Ruby: What is the best approach to deprecate just one argument of a method?

You can check if the second argument is provided like so:

  def date_valid?(date_string, fmt=nil)
if fmt
warn "second argument to `date_valid?` is deprecated (#{caller[-2]})"
end
fmt ||= "default value"
# ...
end

But like I mentioned in a comment, not sure why you would want to deprecate the second argument at all (it is optional, after all), unless it's important that the user not customize this value

The best way to deprecate all class methods in Rails

Ruby has a special module Gem::Deprecate for that, here's an example from the official documentation:

class Legacy
def self.klass_method
# ...
end

def instance_method
# ...
end

extend Gem::Deprecate
deprecate :instance_method, "X.z", 2011, 4

class << self
extend Gem::Deprecate
deprecate :klass_method, :none, 2011, 4
end
end

It will result to:

2.5.0 :020 > Legacy.new.instance_method
NOTE: Legacy#instance_method is deprecated; use X.z instead. It will be removed on or after 2011-04-01.
Legacy#instance_method called from (irb):20.
=> nil
2.5.0 :021 > Legacy.klass_method
NOTE: Legacy.klass_method is deprecated with no replacement. It will be removed on or after 2011-04-01.
Legacy.klass_method called from (irb):21.
=> nil

Edit:
To answer your question directly, here is the most elegant way I could imagine to deprecate ALL of the class methods:

class Kek
class << self
def old_method_1
# ...
end

def old_method_2
# ...
end

extend Gem::Deprecate

# instance methods here are our actual class methods + all of the Object's methods from Ruby
instance_methods(false).each { |method_to_deprecate| deprecate(method_to_deprecate, :none, 2011, 4) }
end
end

Mark deprecated method ussing ActiveSupport::Deprecation

As Roman says, it can be done with
ActiveSupport::Deprecation.deprecate_methods(target_module, *deprecated_methods)

where:

  • target_module is the module or class where the method belongs.
  • deprecated_methods is an array of symbols.

In the last methods can be given options to customize the deprecation message.

ActiveSupport::Deprecation.deprecate_methods(target_module, :old_method, \
:other_old_method => :new_method, :another_old_method => "custom message")

This example shows the default message when old_method is called, give a comment to "use new_method instead", in the second one, and the custom message with :another_old_method.

Notes: The deprecated methods should be defined (before) and will be executed. The :new_method is not called automatically. (there are more options, but I don't know them)

Is there a neat way to mark an entire module full of methods as deprecated?

Code

First create a module containing a single module method, setup. This module could be put in a file that is required as needed. This module method will be invoked from a module containing instance methods to be included in a given class. Using aliases, it modifies those instance methods to print a message (containing the method name) before executing the remainder of its code.

Module AddMessage

module AddMessage
def self.setup(mod, msg_pre, msg_post)
mod.instance_methods(false).
each do |m|
am = "_#{m}".to_sym
mod.send(:alias_method, am, m)
mod.send(:private, am)
mod.send(:define_method, m) do |*args, &block|
puts "%s %s %s" % [msg_pre, m, msg_post]
send(am, *args, &block)
end
end
end
end

Module to be included in class

module LegacyStuff
def old1
"hi"
end
def old2(a, b)
yield(a, b)
end
AddMessage.setup(self, "Warning:", "is deprecated")
end

AddMessage::setup is passed three arguments, the name of the calling module and a message prefix and message suffix that are used to form the warning message. When an instance method m in this module is executed by a class instance the message "Warning: #{m} is deprecated" is printed (e.g., "Warning: old1 is deprecated") before the remaining calculations are performed.

Use

LegacyStuff is simply included by a class.

class C
include LegacyStuff
# <constants, methods and so forth go here>
end

c = C.new
c.old1
# Warning: old1 is deprecated
#=> "hi"
c.old2(1,2) { |a,b| a+b }
# Warning: old2 is deprecated
#=> 3
c.cat
#=> NoMethodError: undefined method `cat' for #<C:0x000000008ef0a8>

Explanation of the module method AddMessage:setup

The following (of the generally less-familiar) methods are used by this method: Module#instance_methods, Module#alias_method, Module#private and Module#define_method.

The following three steps are performed for each instance method m defined in module mod (e.g., elements of the array LegacyStuff.instance_methods(false)).

mod.send(:alias_method, am, m)

Create an alias am of the method (e.g., _:old1 for old1).

mod.send(:private, am)

Make the alias am a private method (e.g., _old1).

mod.send(:define_method, m) do |*args, &block| ... end

Redefine the method m (e.g., old1) to print the indicate string and then execute the alias am (e.g., _old1).

Deprecating a renamed attribute in Ruby YARD documentation

By the looks of the documentation, you are supposed to mark a method as deprecated. Something like

# @deprecated Use {#bar} instead.
def foo
bar
end

def bar
...
end

I realize this is not as succinct as using alias_method, but I think this is the only way to do it with YARD.

Best practice for avoiding mutating parameters?

It is responsibility of the caller to shield itself from code being called. Let's say, you have some command line options parsing code. You got this hash of parameters and you want to do some validation (or something). Now, the validating code was written by some other guy who likes to do things in-place for "efficiency". So it is likely that your hash will be mutated and you won't be able to use it later.

Solution? Pass a copy.

validate_params! Marshal.load(Marshal.dump(params)) # deep copy

Note that in some cases mutation is desirable. So it must be the caller who controls the effect (allows or prevents it).



Related Topics



Leave a reply



Submit