Module: ActiveSanction::Deprecation

Extended by:
T::Sig
Defined in:
lib/active_sanction/deprecation.rb

Overview

How this library says that something it still supports is going away.

ActiveSanction::Deprecation.warn(
"ActiveSanction.screen(name:)",
replacement: "ActiveSanction.screen(Query.new(...))",
since: "1.4.0"
)
# => active_sanction: ActiveSanction.screen(name:) is deprecated since
#    1.4.0 and will be removed in 1.6.0. Use
#    ActiveSanction.screen(Query.new(...)) instead.
#    Called from app/jobs/screen_job.rb:31

The rules that message is stating are in docs/api_stability.md: a deprecated thing keeps working for one full minor release after the one that deprecated it, and the removal version is computed from since rather than chosen, so nobody has to remember the policy to apply it.

Ruby's own switch, rather than one of ours

This routes through Kernel#warn with category: :deprecated, so a host silences it with Warning[:deprecated] = false -- the same line that silences every other deprecation in their process, and the line they already have if they run a quiet suite. A config.deprecation_mode of our own would be one more thing to discover, and it would not be the thing already sitting in their spec_helper.

Ruby's default for that switch is off outside verbose mode. That is deliberate on Ruby's part and it is kept: the audience for a deprecation is a developer running ruby -w, a test suite or a CI build, and a warning a production process cannot act on is a log line nobody reads.

Once per call site, not once per call

A deprecated method called while looping over 19,000 records would otherwise write 19,000 identical lines. The first call from each source location warns and the rest are silent. reset! clears that memory, which is what a spec asserting a deprecation needs and what nothing else should touch.

Class Method Summary collapse

Class Method Details

.removal_for(since) ⇒ String

The version something deprecated in since may be removed in: the minor after the next one. A patch release never removes anything, so the patch component is dropped rather than carried forward.

Parameters:

  • since (String) —

    the version that deprecated it

Returns:

  • (String) —

    the earliest version it may be removed in



114
115
116
117
# File 'lib/active_sanction/deprecation.rb', line 114

def removal_for(since)
  major, minor = since.split(".").first(2).map(&:to_i)
  "#{major}.#{T.must(minor) + OVERLAP_MINORS}.0"
end

.reset! ⇒ void

This method returns an undefined value.

Forget which call sites have already warned. For a spec that asserts a deprecation fires; nothing in a running application should call it.



124
125
126
# File 'lib/active_sanction/deprecation.rb', line 124

def reset!
  @mutex.synchronize { @seen.clear }
end

.warn(subject, since:, replacement: nil, removal: nil) ⇒ void

This method returns an undefined value.

Parameters:

  • subject (String) —

    what is deprecated, written the way a caller writes it -- a method signature, a constant, a configuration setting.

  • since (String) —

    the released version that deprecated it.

  • replacement (String, nil) (defaults to: nil) —

    what to use instead. Nil says there is nothing to move to, which is worth saying out loud rather than leaving somebody to search for one that does not exist.

  • removal (String, nil) (defaults to: nil) —

    the version it may be removed in. Computed from since when omitted, which is the case that should be normal -- a hand-written removal version is a policy exception, and an exception is worth having to type.



98
99
100
101
102
103
104
105
# File 'lib/active_sanction/deprecation.rb', line 98

def warn(subject, since:, replacement: nil, removal: nil)
  return unless Warning[:deprecated]

  site = call_site
  return unless first_time?("#{subject}@#{site}")

  Kernel.warn(message(subject, since, replacement, removal, site), category: :deprecated)
end