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
Rake VS. Thor for Automation Scripts
Generating Unique, Hard-To-Guess "Coupon" Codes
How to Customize Gemfile Per Developer
Is There a Way in Ruby/Rails to Execute Code That Is in a String
Detect Number of Cpus Installed
Does Dependency Injection Exist in Rails
How to Get the Parent's Class Name in Ruby
How to Remove All Non - Ascii Characters from a String in Ruby
How to Specify Local .Gem Files in My Gemfile
How to Handle Constants in Ruby When Using Rails
How to Convert a JSON Formatted Key Value Pair to Ruby Hash with Symbol as Key
How to Set an Attr_Accessor for a Dynamic Instance Variable
How to Find the Source File for a Rake Task
Ruby Loading Config (Yaml) File in Same Dir as Source
How to Password-Protect My /Sidekiq Route (I.E. Require Authentication for the Sidekiq::Web Tool)