Skip to content

Commit

Permalink
Deprecator improvements
Browse files Browse the repository at this point in the history
In preparation for removing deprecations for certain generators,
this commit documents the Deprecator and how to test deprecated
generators. It also adds a unit test using newly added matchers
based on Rails deprecation matchers.

With this change, we can remove generators in a follow up PR
while having the Deprecator covered and documented.
  • Loading branch information
stefannibrasil committed Aug 30, 2024
1 parent 130a504 commit c53bade
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 26 deletions.
71 changes: 68 additions & 3 deletions lib/helpers/deprecator.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,70 @@
# frozen_string_literal: true

# Based on Rails ActiveSupport Deprecator
# https://github.com/rails/rails/blob/6f0d1ad14b92b9f5906e44740fce8b4f1c7075dc/activesupport/lib/active_support/deprecation/constant_accessor.rb
# https://github.com/rails/rails/blob/main/activesupport/lib/active_support/deprecation/constant_accessor.rb

# rubocop:disable Style/ClassVars
module Faker
# Provides a way to rename generators, including their namespaces, with a deprecation cycle in which
# both the old and new names work, but using the old one prints a deprecation message.
#
# Deprecator provides a deprecate_generator method to be used when
# renaming a generator. For example, let's say we want to change the following Generator's
# name to <tt>Faker::NewGenerator</tt>:
#
# module Faker
# class Generator
# def self.generate
# "be kind"
# end
# end
# end
#
# To rename it, you need to do the update the name and declare the deprecation by
# including the <tt>Deprecator</tt> module and using the deprecate_generator method:
#
# module Faker
# class NewGenerator
# def self.generate
# "be kind"
# end
# end
#
# include Deprecator
# deprecate_generator('DeprecatedGenerator', NewGenerator)
# end
#
# The first argument is a constant name (no colons) as a string. It is the name of
# the constant you want to deprecate.
#
# The second argument is the constant path of the replacement (no colons) as a constant.
#
# For this to work, a +const_missing+ hook is installed. When users
# reference the deprecated constant, the callback prints the
# message and constantizes the replacement.
#
# With that in place, references to <tt>Faker::Deprecator</tt> still work, they
# evaluate to <tt>Faker::NewGenerator</tt> now, and trigger a deprecation warning:
#
# Faker::Generator.generate
# # DEPRECATION WARNING: Faker::Generator is deprecated. Use Faker::NewGenerator instead
# # "be kind"
#
# For testing the deprecations, we provide <tt>assert_deprecated</tt>
# and <tt>assert_not_deprecated</tt> matchers.
#
# There's also a <tt>Faker::Deprecator.skip_warning</tt> helper to silence
# the deprecation messages in the *test* output. Use it for generators that have lots of tests
# to avoid too many noise when running the tests.
module Deprecator
def self.included(base)
extension = Module.new do
def const_missing(missing_const_name)
if class_variable_defined?(:@@_deprecated_constants) && (replacement = class_variable_get(:@@_deprecated_constants)[missing_const_name.to_s])
unless Faker::Deprecator.skip_warning?
$stdout.puts("DEPRECATION WARNING: #{name}::#{replacement[:old_generator]} is deprecated. Use #{replacement[:new_constant]} instead.")
deprecated_message = "#{name}::#{replacement[:old_generator]} is deprecated."
replacement_message = "Use #{replacement[:new_constant]} instead."
$stdout.puts("DEPRECATION WARNING: #{deprecated_message} #{replacement_message}")
end

return replacement[:new_constant]
Expand All @@ -22,13 +75,25 @@ def const_missing(missing_const_name)

def deprecate_generator(old_generator_name, new_generator_constant)
class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
class_variable_get(:@@_deprecated_constants)[old_generator_name] = { new_constant: new_generator_constant, old_generator: old_generator_name }
class_variable_get(:@@_deprecated_constants)[old_generator_name] = {
new_constant: new_generator_constant,
old_generator: old_generator_name
}
end
end

base.singleton_class.prepend extension
end

# Silence deprecation warnings within the block.
#
# Faker::Generator.generate
# # => DEPRECATION WARNING: Faker::Generator is deprecated. Use Faker::NewGenerator instead.
#
# Faker::Deprecator.skip_warning do
# Faker::Generator.generate
# end
# # => nil
def self.skip_warning
original = Faker::Deprecator.skip
Faker::Deprecator.skip = true
Expand Down
53 changes: 53 additions & 0 deletions test/helpers/test_faker_deprecator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

require_relative '../test_helper'

class TestFakerDeprecation < Test::Unit::TestCase
def test_using_a_deprecated_generator_returns_a_warning_message
assert_deprecated do
Faker::Dogs.say
end

assert_equal 'meow', Faker::Dogs.say
end

def test_using_a_non_deprecated_generator_does_not_return_a_warning_message
assert_not_deprecated do
Faker::Cats.say
end
assert_equal 'meow', Faker::Cats.say
end

def test_testing_a_deprecated_generator_with_skip_warning_does_not_return_a_warning_message
actual_stdout, actual_stderr = capture_output do
Faker::Deprecator.skip_warning do
Faker::Dogs.say
end
end

assert_empty(actual_stdout)
assert_empty(actual_stderr)
assert_equal 'meow', Faker::Dogs.say
end

def test_deprecated_with_skip_warning_does_not_generate_message
Faker::Deprecator.skip_warning do
assert_not_deprecated do
Faker::Dogs.say
end
end

assert_equal 'meow', Faker::Dogs.say
end
end

module Faker
class Cats < Base
def self.say
'meow'
end
end

include Faker::Deprecator
deprecate_generator('Dogs', Cats)
end
23 changes: 0 additions & 23 deletions test/test_faker_deprecator.rb

This file was deleted.

1 change: 1 addition & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

require_relative 'support/assert_not_english'
require_relative 'support/assert_email_regex'
require_relative 'support/deprecation'
require 'minitest/autorun'
require 'test/unit'
require 'rubygems'
Expand Down

0 comments on commit c53bade

Please sign in to comment.