# frozen_string_literal: truerequire"logger"moduleDrymoduleCore# An extension for issuing 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 :old_api, :new_api, message: "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 publicmoduleDeprecationsSTACK=->{caller.find{|l|l!~%r{(lib/dry/core)|(gems)}}}class<<self# Prints a warning## @param [String] msg Warning string# @param [String] tag Tag to help identify the source of the warning.# Defaults to "deprecated"# @param [Integer] Caller frame to add to the messagedefwarn(msg,tag: nil,uplevel: nil)caller_info=uplevel.nil??nil:"#{caller_locations(uplevel+2,1)[0]} "tag="[#{tag||"deprecated"}] "hint=msg.gsub(/^\s+/,"")logger.warn("#{caller_info}#{tag}#{hint}")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 instructionsdefannounce(name,msg,tag: nil,uplevel: nil)# Bump the uplevel (if provided) by one to account for the uplevel calculation# taking place one frame deeper in `.warn`uplevel+=1ifuplevelwarn(deprecation_message(name,msg),tag: tag,uplevel: uplevel)end# @api privatedefdeprecation_message(name,msg)<<-MSG#{name} is deprecated and will be removed in the next major version
#{msg} MSGend# @api privatedefdeprecated_name_message(old,new=nil,msg=nil)ifnewdeprecation_message(old,<<-MSG)
Please use #{new} instead.
#{msg} MSGelsedeprecation_message(old,msg)endend# Returns the logger used for printing warnings.# You can provide your own with .set_logger!## @param [IO] output output stream## @return [Logger]deflogger(output=$stderr)ifdefined?(@logger)@loggerelseset_logger!(output)endend# Sets a custom logger. This is a global setting.## @overload set_logger!(output)# @param [IO] output Stream for messages## @overload set_logger!# Stream messages to stdout## @overload set_logger!(logger)# @param [#warn] logger## @api publicdefset_logger!(output=$stderr)ifoutput.respond_to?(:warn)@logger=outputelse@logger=::Logger.new(output).tapdo|logger|logger.formatter=proc{|_,_,_,msg|"#{msg}\n"}endendenddef[](tag)Tagged.new(tag)endend# @api privateclassTagged<::Moduledefinitialize(tag)super()@tag=tagenddefextended(base)base.extendInterfacebase.deprecation_tag@tagendendmoduleInterface# Sets/gets deprecation tag## @option [String,Symbol] tag tagdefdeprecation_tag(tag=nil)ifdefined?(@deprecation_tag)@deprecation_tagelse@deprecation_tag=tagendend# Issue a tagged warning message## @param [String] msg warning messagedefwarn(msg)Deprecations.warn(msg,tag: deprecation_tag)end# Mark instance method as deprecated## @param [Symbol] old_name deprecated method# @param [Symbol] new_name replacement (not required)# @option [String] message optional deprecation messagedefdeprecate(old_name,new_name=nil,message: nil)full_msg=Deprecations.deprecated_name_message("#{name}##{old_name}",new_name?"#{name}##{new_name}":nil,message)mod=selfifnew_nameundef_methodold_nameifmethod_defined?(old_name)define_method(old_name)do|*args,&block|mod.warn("#{full_msg}\n#{STACK.()}")__send__(new_name,*args,&block)endelsealiased_name=:"#{old_name}_without_deprecation"alias_methodaliased_name,old_nameprivatealiased_nameundef_methodold_namedefine_method(old_name)do|*args,&block|mod.warn("#{full_msg}\n#{STACK.()}")__send__(aliased_name,*args,&block)endendend# Mark class-level method as deprecated## @param [Symbol] old_name deprecated method# @param [Symbol] new_name replacement (not required)# @option [String] message optional deprecation messagedefdeprecate_class_method(old_name,new_name=nil,message: nil)full_msg=Deprecations.deprecated_name_message("#{name}.#{old_name}",new_name?"#{name}.#{new_name}":nil,message)meth=method(new_name||old_name)singleton_class.instance_execdoundef_methodold_nameifmethod_defined?(old_name)define_method(old_name)do|*args,&block|warn("#{full_msg}\n#{STACK.()}")meth.call(*args,&block)endendend# Mark a constant as deprecated# @param [Symbol] constant_name constant name to be deprecated# @option [String] message optional deprecation messagedefdeprecate_constant(constant_name,message: nil)value=const_get(constant_name)remove_const(constant_name)full_msg=Deprecations.deprecated_name_message("#{name}::#{constant_name}",message)mod=::Module.newdodefine_method(:const_missing)do|missing|ifmissing==constant_namewarn("#{full_msg}\n#{STACK.()}")valueelsesuper(missing)endendendextend(mod)endendendendend