lib/dry/core/deprecations.rb
require 'logger' module Dry module Core # An extension for issueing warnings on using deprecated methods. # # @example # # class Foo # def self.old_class_api; end # def self.new_class_api; end # # deprecate_class_method :old_class_api, :new_class_api # # def old_api; end # def new_api; end # # deprecate_method :old_api, :new_api, "old_api is no-no" # end # # @example You also can use this module for your custom messages # # Dry::Core::Deprecations.announce("Foo", "use bar instead") # Dry::Core::Deprecations.warn("Baz is going to be removed soon") # # @api public module Deprecations class << self # Prints a warning # # @param [String] msg Warning string def warn(msg) logger.warn(msg.gsub(/^\s+/, '')) end # Wraps arguments with a standard message format and prints a warning # # @param [Object] name what is deprecated # @param [String] msg additional message usually containing upgrade instructions def announce(name, msg) warn(deprecation_message(name, msg)) end # @api private def deprecation_message(name, msg) <<-MSG #{name} is deprecated and will be removed in the next major version #{message(msg)} MSG end # @api private def deprecated_method_message(old, new = nil, msg = nil) if new deprecation_message(old, <<-MSG) Please use #{new} instead. #{msg} MSG else deprecation_message(old, msg) end end # @api private def message(msg) <<-MSG #{msg} #{caller.detect { |l| l !~ %r{(lib/dry/core)|(gems)} }} MSG end # Returns the logger used for printing warnings. # You can provide your own with .set_logger! # # @param [String, Symbol] tag optional prefix for messages # @param [IO] output output stream # # @return [Logger] def logger(tag = nil, output = nil) if defined?(@logger) @logger else set_logger!(output: output, tag: tag) end end # Sets a custom logger. This is a global settings. # # @option [IO] output output stream for messages # @option [String, Symbol] tag optional prefix # # TODO: Add support for per-module loggers so that at least # tag option won't conflict. def set_logger!(output: nil, tag: nil) @logger = Logger.new(output || $stdout) @logger.formatter = proc { |_severity, _datetime, _progname, msg| "[#{tag || 'deprecated'}] #{msg}\n" } @logger end end # Mark instance method as deprecated # # @param [Symbol] old_name deprecated method # @param [Symbol] new_name replacement (not required) # @option [String] message optional deprecation message def deprecate(old_name, new_name = nil, message: nil) full_msg = Deprecations.deprecated_method_message( "#{self.name}##{old_name}", new_name ? "#{self.name}##{new_name}" : nil, message ) if new_name undef_method old_name define_method(old_name) do |*args, &block| Deprecations.warn(full_msg) __send__(new_name, *args, &block) end else aliased_name = :"#{old_name}_without_deprecation" alias_method aliased_name, old_name private aliased_name undef_method old_name define_method(old_name) do |*args, &block| Deprecations.warn(full_msg) __send__(aliased_name, *args, &block) end end end # Mark class-level method as deprecated # # @param [Symbol] old_name deprecated method # @param [Symbol] new_name replacement (not required) # @option [String] message optional deprecation message def deprecate_class_method(old_name, new_name = nil, message: nil) full_msg = Deprecations.deprecated_method_message( "#{self.name}.#{old_name}", new_name ? "#{self.name}.#{new_name}" : nil, message ) meth = new_name ? method(new_name) : method(old_name) singleton_class.instance_exec do undef_method old_name define_method(old_name) do |*args, &block| Deprecations.warn(full_msg) meth.call(*args, &block) end end end end end end