Giter Club home page Giter Club logo

deprecation_toolkit's Introduction

⚒ Deprecation Toolkit ⚒

Introduction

Deprecation Toolkit is a gem that helps you get rid of deprecations in your codebase. Having deprecations in your application usually means that something will break whenever you upgrade third-party dependencies. The sooner the better to fix them! Fixing all deprecations at once might be tough depending on the size of your app and the number of deprecations. You might have to progressively resolve them while making sure your team doesn't add new ones. This is where this gem comes handy!

How it works

The Deprecation Toolkit gem works by using a shitlist approach. First, the gem records all existing deprecations into .yml files. When running a test that have non-recorded deprecations after the initial recording, Deprecation Toolkit triggers a behavior of your choice (by default it raises an error).

Recording Deprecations

As said above, the Deprecation Toolkit works by using a shitlist approach. You have two ways to record deprecations. Either set DeprecationToolkit::Configuration.behavior to DeprecationToolkit::Behaviors::Record (see the Configuration Reference below) Or run your tests with the --record-deprecations flag (or simply the -r shortcut)

rails test <path_to_my_test.rb> -r

Configuration Reference

🔨 #DeprecationToolkit::Configuration#deprecation_path

You can control where recorded deprecations are saved and read from. By default, deprecations are recorded in the test/deprecations folder.

The deprecation_path either accepts a string or a proc. The proc is called with the path of the running test file.

DeprecationToolkit::Configuration.deprecation_path = 'test/deprecations'
DeprecationToolkit::Configuration.deprecation_path = -> (test_location) do
  if test_location == 'admin_test.rb'
    'test/deprecations/admin'
  else
    'test/deprecations/storefront'
  end
end

🔨 #DeprecationToolkit::Configuration#behavior

Behaviors define what happens when non-recorded deprecations are encountered.

This gem provides 4 behaviors, the default one being DeprecationToolkit::Behaviors::Raise.

  • DeprecationToolkit::Behaviors::Raise will raise either:
    • DeprecationToolkit::Behaviors::DeprecationIntroduced error if a new deprecation is introduced.
    • DeprecationToolkit::Behaviors::DeprecationRemoved error if a deprecation was removed (compare to the one recorded in the shitlist).
  • DeprecationToolkit::Behaviors::Record will record deprecations.
  • DeprecationToolkit::Behaviors::CIRecordHelper See separate explanation below.
  • DeprecationToolkit::Behaviors::Disabled will do nothing.
    • This is useful if you want to disable this gem for a moment without removing the gem from your Gemfile.
DeprecationToolkit::Configuration.behavior = DeprecationToolkit::Behaviors::Record

You can also create your own behavior object and perform the logic you want. Your behavior needs to respond to trigger.

class StatsdBehavior
  def trigger(test, deprecations, recorded_deprecations)
     # Could send some statsd event for example
  end
end

DeprecationToolkit::Configuration.behavior = StatsdBehavior.new

DeprecationToolkit::Behaviors::CIRecordHelper

This is a special type of behaviour meant to help you record deprecations if you have a lof of them. Imagine if you have thousands of tests and need to record deprecations for each on your machine, this is going to take ages. Recording deprecations on CI with the regular Record behavior isn't possible because of the way CI parallelize test in multiple container (Multiple tests from the same file runs in parallel in diferrent machines, the deprecation files that get created are then splitted. Regrouping them is a nightmare.)

This behavior will output a JSON representation of your deprecations. Your CI should have a way to download the log generated from the test run. Download it on your locale machine. Finally run the rake task FILEPATH=<path_to_downloaded_log> deprecation_toolkit:record_from_ci_output. This task will parse your CI log and grab the output generated by the DeprecationToolkit and will finally convert everything back to YML files.

🔨 #DeprecationToolkit::Configuration#allowed_deprecations

You can ignore some deprecations using allowed_deprecations. allowed_deprecations accepts an array of Regexp and Procs.

Whenever a deprecation matches one of the regex, it is ignored.

DeprecationToolkit::Configuration.allowed_deprecations = [/Hello World/]

ActiveSupport::Deprecation.warn('Hello World') # ignored by Deprecation Toolkit

When passing procs, each proc will get passed the deprecation message and the callstack. This is useful if you want to whitelist deprecations based on the caller.

DeprecationToolkit::Configuration.allowed_deprecations = [
  ->(message, stack) { message ~= 'Foo' && stack.first.label == 'method_triggering_deprecation' }
]

def method_triggering_deprecation
  ActiveSupport::Deprecation.warn('Foo') # Ignored by the the DeprecationToolkit
end

🔨 #DeprecationToolkit::Configuration#warnings_treated_as_deprecation

Most gems don't use ActiveSupport::Deprecation to deprecate their code but instead just use Kernel#warn to output a message in the console.

Deprecation Toolkit allows you to configure which warnings should be treated as deprecations in order for you to keep track of them as if they were regular deprecations.

This setting accepts an array of regular expressions. To match on all warnings, you can provide a empty regex:

DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [//]

In addition to regexps, anything that responds to === can be a matcher, for instance a proc:

DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [
  ->(warning) { !warning.match?(/not a deprecation/)}
]

🔨 #DeprecationToolkit::Configuration#deprecation_file_path_format

DeprecationToolkit allows you to choose the file path format for deprecation files.

For Minitest, it defaults to using class name so the following code would correspond to #{deprecation_path}/deprecation_toolkit/behaviors/raise_test.yml

module DeprecationToolkit
  module Behaviors
    class RaiseTest < ActiveSupport::TestCase
    end
  end
end

For rspec if defaults to the file location with spec removed. /spec/models/user_spec.rb would correspond to /models/user.yml.

If you have a specific use case you can configure this with a custom format using a proc. The proc is called with an instance of the test.

Configuration.deprecation_file_path_format = -> (test) do
  Kernel.const_source_location(test.class.name)[0].sub(%r{^./test/}, "").sub(/_test.rb$/, "")
end

RSpec

By default Deprecation Toolkit uses Minitest as its test runner. To use Deprecation Toolkit with RSpec you'll have to configure it.

DeprecationToolkit::Configuration.test_runner = :rspec

Also make sure to require the before/after hooks in your spec_helper.rb or rails_helper.rb.

require "deprecation_toolkit/rspec"

It's possible to record deprecations while running your specs by adding an ENV['DEPRECATION_BEHAVIOR'] variable to your test run. Run your specs with this ENV set to "record-deprecations", "record" (or simply the "r" shortcut).

DEPRECATION_BEHAVIOR="record" bundle exec rspec path/to/file_spec.rb

License

Deprecation Toolkit is licensed under the MIT license.

deprecation_toolkit's People

Contributors

andyw8 avatar brendo avatar byroot avatar casperisfine avatar cursedcoder avatar davidstosik avatar dependabot[bot] avatar dylanahsmith avatar edouard-chin avatar etiennebarrie avatar gmcgibbon avatar jasonnoble avatar m-nakamura145 avatar natt-eyre avatar rafaelfranca avatar rmacklin avatar sambostock avatar shioyama avatar shopify-rails[bot] avatar splattael avatar stephsachrajda avatar xrxr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

deprecation_toolkit's Issues

How do you use the CIRecordHelper?

I'm trying to use the CIRecordHelper when running my tests in parallel on circleci, but as far as I can tell all it does is raise this error.

raise "#{HEADER} #{JSON.dump(to_output)}"

So my test output looks like:

Failures:

  1) User bar is expected to eq "foo"
     Failure/Error: raise "#{HEADER} #{JSON.dump(to_output)}"

     RuntimeError:
       [DeprecationToolkit] {"/tmp/test-results/deprecations/models/user.yml":{"test_user_bar_is_expected_to_eq_\"foo\"":["DEPRECATION WARNING: /Users/scott/dev/portal/spec/models/user_spec.rb:588: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call\n/Users/scott/dev/portal/app/models/user.rb:151: warning: The called method `foo' is defined here\n"]}}
     # /Users/scott/.asdf/installs/ruby/2.7.7/lib/ruby/gems/2.7.0/gems/deprecation_toolkit-2.0.3/lib/deprecation_toolkit/behaviors/ci_record_helper.rb:21:in `trigger'
     # /Users/scott/.asdf/installs/ruby/2.7.7/lib/ruby/gems/2.7.0/gems/deprecation_toolkit-2.0.3/lib/deprecation_toolkit/test_triggerer.rb:9:in `trigger_deprecation_toolkit_behavior'
     # /Users/scott/.asdf/installs/ruby/2.7.7/lib/ruby/gems/2.7.0/gems/deprecation_toolkit-2.0.3/lib/deprecation_toolkit/rspec.rb:7:in `block (2 levels) in <main>'
     # /Users/scott/.asdf/installs/ruby/2.7.7/lib/ruby/gems/2.7.0/gems/rspec-core-3.11.0/lib/rspec/core/example.rb:457:in `instance_exec'
...

And no json file is written. What am I doing wrong?

Update README to include detailed installation instructions

Currently the README skips straight from "what does it do" to "how to use it". I think that "how to set it up" is a missing step between these two.

While attempting to use this Gem I encountered difficulty actually getting it to work for minitest (it was fine in RSpec), so along with the Gem installation instructions it would be useful to go into more detail about where configuration should live. Is it in an initializer? Does it live in test/test_helper.rb?

It would also be useful to have some information about how to use the Gem in a project which has both minitest and RSpec suites (if that's even possible). I love the idea of the Gem - I'm just struggling to get it to work in our dual-suite application right now!

DeprecationToolkit adds private methods in the Minitest module - should we avoid that?

These two methods:

def add_notify_behavior
notify = ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[:notify]
behaviors = ActiveSupport::Deprecation.behavior
unless behaviors.find { |behavior| behavior == notify }
ActiveSupport::Deprecation.behavior = behaviors << notify
end
end
def attach_subscriber
DeprecationToolkit::Configuration.attach_to.each do |gem_name|
DeprecationToolkit::DeprecationSubscriber.attach_to gem_name
end
end

are defined inside the Minitest module. However, the methods themselves are independent. It seems safer to define them inside the DeprecationToolkit namespace, to keep this gem isolated from potential conflicts with other gems that could redefine Minitest#add_notify_behavior and Minitest#attach_subscriber.

If you agree, I'd be happy to open a PR.

SystemStackError with RubyGems v3 and Ruby 2.5+

Attempting to boot a rails server results in a crash after upgrading to RubyGems v3:

Traceback (most recent call last):
	9739: from /Users/brendanabbott/src/github.com/Shopify/brochure2/bin/rails:4:in `<main>'
	9738: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/activesupport-5.2.1.1/lib/active_support/dependencies.rb:287:in `require'
	9737: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/activesupport-5.2.1.1/lib/active_support/dependencies.rb:253:in `load_dependency'
	9736: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/activesupport-5.2.1.1/lib/active_support/dependencies.rb:287:in `block in require'
	9735: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:29:in `require'
	9734: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:20:in `require_with_bootsnap_lfi'
	9733: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/loaded_features_index.rb:65:in `register'
	9732: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `block in require_with_bootsnap_lfi'
	 ... 9727 levels...
	   4: from /opt/rubies/2.5.3-alans-special-1/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_warn.rb:17:in `warn'
	   3: from /Users/brendanabbott/.gem/ruby/2.5.3/gems/deprecation_toolkit-1.2.0/lib/deprecation_toolkit/warning.rb:19:in `warn'
	   2: from /opt/rubies/2.5.3-alans-special-1/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_warn.rb:17:in `block in <module:Kernel>'
	   1: from /opt/rubies/2.5.3-alans-special-1/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_warn.rb:17:in `call'
/opt/rubies/2.5.3-alans-special-1/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_warn.rb:17:in `warn': stack level too deep (SystemStackError)

After doing some digging it looks like the monkey patch in warning.rb plays with some changes made in this rubygems commit (thanks @rafaelfranca!) and causes the error.

ruby 2.5.3
rails 5.2.1.1
deprecation-toolkit 1.2.0
rubygems 3.0.2

MacOS 10.14.2

please add a way to run in production

Especially for legacy projects that don't have 100% coverage, it would be nice to be able to configure DeprecationToolkit to report the deprecations to a logging service like sentry. AFAICT, currently it is only possible to use this in test environment.

DeprecationToolkit patches Kernel#warn but not Kernel.warn

Here is a failing test that demonstrates this:

diff --git a/test/deprecation_toolkit/warning_test.rb b/test/deprecation_toolkit/warning_test.rb
index 0c519cc..c3f9e69 100644
--- a/test/deprecation_toolkit/warning_test.rb
+++ b/test/deprecation_toolkit/warning_test.rb
@@ -22,6 +22,16 @@ module DeprecationToolkit
       end
     end
 
+    test 'treats warnings as deprecations using Kernel.warn' do
+      Configuration.warnings_treated_as_deprecation = [/#example is deprecated/]
+
+      assert_raises Behaviors::DeprecationIntroduced do
+        Kernel.warn '#example is deprecated'
+
+        trigger_deprecation_toolkit_behavior
+      end
+    end
+
     test 'warn can be called with an array' do
       Configuration.warnings_treated_as_deprecation = [/#example is deprecated/, /#something is deprecated/]

Kernel.warn is used in place of Kernel#warn in places where warn would not resolve to Kernel#warn, e.g. here.

Include stacktrace in record behavior

Currently, the record DeprecationBehavior removes the stacktrace from the deprecation message before writing to YAML- https://github.com/Shopify/deprecation_toolkit/blob/main/lib/deprecation_toolkit/behaviors/record.rb#L11

This stacktrace is pretty useful info but it is removed from the YAML content - https://github.com/Shopify/deprecation_toolkit/blob/main/lib/deprecation_toolkit/collector.rb#L36

I am willing to submit a pull request that can change the behaviour, but would like to confirm why this done.

@Edouard-chin @rafaelfranca

Configure CI

Since CircleCI is deprecated let's use Shopify-Build

Absolute Path vs Relative Path on deprecations

Hi there!

When recording deprecations as suggested on https://about.gitlab.com/blog/2021/02/03/how-we-automatically-fixed-hundreds-of-ruby-2-7-deprecation-warnings/ we are seeing recordings like:

-  DEPRECATION WARNING: /Users/sobrinho/Developer/my-app/models/concerns/logable.rb:36: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call

But when running on CI, we have:

+  DEPRECATION WARNING: /app/app/models/concerns/logable.rb:36: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call

Which leads to a DeprecationToolkit::Behaviors::DeprecationMismatch failure.

I'm currently fixing those with a dirty sed:

find spec/deprecation -type f | xargs gsed -i 's/.Users.sobrinho.Developer.my-app./\/app\//g'

Does it worth to have a way of replacing paths/values from deprecations or at least mention how on README?

Throwing errors when dependencies are using deprecated methods

Behaviour

DeprecationToolkit is throwing errors if a consumer is using a deprecated method. For example, if FooApp is using Rails ~6.0, and a dependency is using Module#parent, FooApp will throw an error.

Expected behaviour

DeprecationToolkit only throws an error if there's a deprecated method used in the source code. For example, FooApp shouldn't throw and error if a dependency is using Module#parent, but rather only if Module#parent is being used in the source code.

What people are doing to solve it

I see apps like core are ignoring these warnings.

Opinion

I'm not sure if this is by design or not, but if it is I don't think it should be default behaviour.

Shitlists are a great pattern to update your codebase—but it's not the task of an app to also update their dependencies codebases as well. For example, if Rails deprecates a method, it should be up to the app maintainers to update their codebase, and separately up to the gem maintainers to update their own codebase. In this case, each maintainer would create a shitlist for their respected codebases.

Override Ruby Warning.warn

A lot of gems (resque-scheduler, minitest, dogapi ...) doesn't use ActiveSupport::Deprecation and instead just trigger a deprecation using Kernel.warn. We have no way on our side to keep track of these deprecations.

My idea was to implement something similar to the ruby-warning gem and catch any warning that might be deprecations (most of the time the warning is prefixed with the [DEPRECATION] tag) and monkey patch the Warning module to instead use ActiveSupport::Deprecation.warn whenever a deprecation is triggered.

WDYT @rafaelfranca @wvanbergen

How to load the rake task for CIRecordHelper?

The README mentions a deprecation_toolkit:record_from_ci_output rake task, but it's not loaded by default (I guess it might be if you didn't use require: false in the Gemfile? But I'd rather not load the gem outside of our tests) and I can't figure out how to require it such that it'd be available to use.

Rspec support?

Currently only minitest is supported but it would be great to include out-of-the-box support for rspec as well.

I managed to get this (sort-of) working by adding it to my rspec rails_helper file.

config.before(:all) do
    notify = ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[:notify]
    behaviors = ActiveSupport::Deprecation.behavior

    unless behaviors.find { |behavior| behavior == notify }
      ActiveSupport::Deprecation.behavior = behaviors << notify
    end

    DeprecationToolkit::Configuration.attach_to.each do |gem_name|
      DeprecationToolkit::DeprecationSubscriber.attach_to gem_name
    end
  end

  config.after do
    current_deprecations = DeprecationToolkit::Collector.new(DeprecationToolkit::Collector.deprecations)
    recorded_deprecations = DeprecationToolkit::Collector.load(self.class)
    if current_deprecations != recorded_deprecations
      DeprecationToolkit::Configuration.behavior.trigger(self.class, current_deprecations, recorded_deprecations)
    end
  end

And this config in my config/test.rb

  DeprecationToolkit::Configuration.deprecation_path = Rails.root.join('spec/deprecations')
  DeprecationToolkit::Configuration.behavior = DeprecationToolkit::Behaviors::Record
  DeprecationToolkit::Configuration.warnings_treated_as_deprecation

While this works (it'll create a yaml file where you tell it to go) it's not in the correct folder structure nor does it have the right test naming. The challenge is in how minitest vs rspec names it's test cases. There would have to be some change in ReadWriteHelper for it to work as self.name is not a thing in rspec. (In my example I've hackily gotten around this by passing in self.class which does respond_to .name).

Happy to help more with this one if you want!

Support older versions of ActiveSupport

Hey there!

Really appreciate you open sourcing this gem! Just saw that you tested against different versions of activesupport and was wondering if it's possible to support older versions of active support (4.2.x). Right now we're still on the upgrade path from Rails 4.2 to 5 and this gem would help us get there!

Happy to open a PR if that helps!

Many thanks! 👌

Andrew

Deprecation toolkit itself using deprecated code?

Hi,

When I use the deprecation-toolkit gem, and it issues a warning, I get the following extra warning from Rails:

DEPRECATION WARNING: Calling warn on ActiveSupport::Deprecation is deprecated and will be removed from Rails (use your own Deprecation object instead) (called from warn at /Users/work/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/activesupport-7.1.1/lib/active_support/deprecation/instance_delegator.rb:55)

Looks like the following code may need to be patched to use a locally stored ActiveSupport::Deprecation instance rather than the ActiveSupport::Deprecation singleton: https://github.com/Shopify/deprecation_toolkit/blob/main/lib/deprecation_toolkit/warning.rb#L53

This also goes for the code here: https://github.com/Shopify/deprecation_toolkit/blob/main/lib/deprecation_toolkit.rb#L48.

Refer to rails/rails#47354.

I'm using Rails 7.1.1. and deprecation-toolkit 2.0.3.

CI and local ruby keyword args YAMLs don't match

Hi

I'm using deprecation-toolkit to capture ruby 2.7 warnings in our codebase. We'd disabled them earlier but want to enable again.

I recorded warnings using CIRecordHelper but I've hit a issue where the ruby code and gem paths in warnings are different on CI and developer environment. How do I handle this?

On local,

---
test_the_banking_inline_form_add_an_attachment_to_an_explanation:
- |
  DEPRECATION WARNING: /Users/snehasomwanshi/dev/accouting/app/uploaders/attachment_attachment_uploader.rb:136: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
  /Users/snehasomwanshi/.gem/ruby/2.7.1/gems/shrine-3.2.1/lib/shrine/plugins/store_dimensions.rb:75: warning: The called method `extract_metadata' is defined here
- |
  DEPRECATION WARNING: /Users/snehasomwanshi/dev/accouting/app/uploaders/attachment_attachment_uploader.rb:124: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
  /Users/snehasomwanshi/.gem/ruby/2.7.1/gems/shrine-3.2.1/lib/shrine.rb:222: warning: The called method `generate_location' is defined here

On CI,

  ---
test_the_banking_inline_form_adding_a_new_explanation:
- |
  DEPRECATION WARNING: /home/ubuntu/workspace/fac/accounting/app/uploaders/attachment_attachment_uploader.rb:136: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
  /home/ubuntu/.gem/ruby/2.7.1/gems/shrine-3.2.1/lib/shrine/plugins/store_dimensions.rb:75: warning: The called method `extract_metadata' is defined here
- |
  DEPRECATION WARNING: /home/ubuntu/workspace/fac/accounting/app/uploaders/attachment_attachment_uploader.rb:124: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
  /home/ubuntu/.gem/ruby/2.7.1/gems/shrine-3.2.1/lib/shrine.rb:222: warning: The called method `generate_location' is defined here

Does this mean that a developer can not record a new warning locally? How do I handle this?

RSpec deprecation warnings not recorded without additional ENV var

README needs to be updated for RSpec.

With the below configuration
DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [//]

expected the below code to trigger a deprecation warning, which it didn't

require 'spec_helper'

class MyClass
  def foo(k: 1)
    p k
  end
end


RSpec.describe MyClass do

    it 'calls method' do
      h = { k: 42 }
      MyClass.new.foo(h)
    end
end

expected the following warning "warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call"

Need to run with additional ENV VAR RUBYOPT='-W:deprecated'
RUBYOPT='-W:deprecated' DEPRECATION_BEHAVIOR="record" bundle exec rspec spec

Run deprecated tests

We are currently using this script when we fix one exception that is spread over a lot of specs:

for file in $(git grep module_parent spec/deprecation/ | cut -d: -f1 | sort -u); do
  DEPRECATION_BEHAVIOR=record bin/rspec "${"${file/deprecation\//}"/.yml/_spec.rb}" || return 1;
done

Would be nice to have some native solution to get deprecated specs like bundle exec deprecation_toolkit deprecated which would print all spec files with deprecation.

And to search something, we could have a simple grep like the script such as bundle exec deprecation_toolkit deprecated module_parent.

Record deprecations outside of rspec/minitest

For apps with less than 100% test coverage, is there any means of recording deprecations while running the app normally (i.e., in local development or a staging environment)? I've added the following early in the application's initialization, but this doesn't have the intended effect:

DeprecationToolkit::Configuration.behavior = DeprecationToolkit::Behaviors::Record
DeprecationToolkit.send(:initialize)

[RFC] "compact" deprecation lists

What

Currently, the YAML files produced by deprecation toolkit will contain hundreds of identical deprecations produced by a single test. The reason is rooted in Collector's implementation.

In this RFC I'd like to suggest a more compact format.

Why

I find the volume of these deprecation files daunting, and because deprecations are not sorted, it can be difficult to evaluate whether a new deprecation is completely new within a test, or "one more" in a long list of deprecations that were already produced by that test.

The same volume will produce some noise in pull requests, unnecessarily inflating the diff count (which at least might have a psychological effect), and potentially making it hard to scroll in a code diff.

How

My proposition is to change for a format where, instead of repeating identical deprecations, we count them. The format could look something like this:

---
test_my_test_title:
- deprecation: 'DEPRECATION WARNING: Don''t use bla bla'
  count: 1
- deprecation: 'DEPRECATION WARNING: Don''t do this'
  count: 87
I quickly tried writing a patch in an existing app, and it looked like this. (Click to open.)
require "deprecation_toolkit/read_write"

# I limited my changes to the YAML read/write logic, but this could be more efficient.

module DeprecationToolkit
  module ReadWriteHelper
    def read(test)
      deprecation_file = Bundler.root.join(recorded_deprecations_path(test))
      data = YAML.load(deprecation_file.read).fetch(test_name(test), [])
      if data.first.is_a?(Hash)
        data.flat_map do |hash|
          [hash["deprecation"]] * hash["count"]
        end
      else # for backwards compatibility
        data
      end
    rescue Errno::ENOENT
      []
    end

    def write(deprecation_file, deprecations_to_record)
      create_deprecation_file(deprecation_file) unless deprecation_file.exist?

      content = YAML.load_file(deprecation_file)

      deprecations_to_record.each do |test, deprecations|
        if deprecations.any?
          content[test] = deprecations.tally.sort.map do |deprecation, count|
            {
              "deprecation" => deprecation,
              "count" => count,
            }
          end
        else
          content.delete(test)
        end
      end

      if content.any?
        deprecation_file.write(YAML.dump(content))
      else
        deprecation_file.delete
      end
    end
  end
end

Anything else

I guess this is really not a big deal, but somehow I found myself annoyed by these huge lists and thought I'd spend a bit of time understanding and thinking about a possible improvement.

I noticed that the current logic reads and rewrites a given test file's deprecation list YAML after each test in the file is completed. In big test suites where single test files contain a lot of tests, and lots of repeated deprecations, this could have a slight beneficial impact on file read/write time. (Less stuff to read/write every time.)

I also wrote a script that could update all deprecation list files to the new format, it's rather trivial.
require "yaml"

Dir["test/deprecations/**/*.yml"].each do |file|
  compacted = YAML.load_file(file).transform_values do |deprecations|
    deprecations.tally.sort.map do |deprecation, count|
      {
        "deprecation" => deprecation,
        "count" => count,
      }
    end
  end
  File.write(file, YAML.dump(compacted))
end

I haven't run the test suite on this change, so it is possible it needs more work to prevent breaking anything.

Before putting more work in, I'd like to hear opinions. 🙏🏻

DeprecationToolkit currently can't catch deprecations that are generated before tests are executed

Consider this example of a deprecation warning:
https://github.com/thoughtbot/shoulda-matchers/blob/94b28e4ef4cc001b1c0b4386143b8c05ac163550/lib/shoulda/matchers/warn.rb#L20-L25

A test like this would trigger that deprecation:

class FooModelTest < ActiveSupport::TestCase
  should define_enum_for(:status).with([:pending, :completed, :failed])
end

Here's what gets logged:

************************************************************************
Warning from shoulda-matchers:

The `with` qualifier on `define_enum_for` is deprecated and will be
removed in the next major release. Please use `with_values` instead.
************************************************************************

However, because the deprecated method (with) is called in the code that declares the test cases, not called from within a test, DeprecationToolkit won't catch this deprecation. The deprecation gets logged before attach_subscriber has been invoked.

Is it possible to subscribe to deprecations earlier in the life of the test process?

`uninitialized constant Minitest::DeprecationToolkit` after deprecation_toolkit added

Adding deprecation_toolkit to a very small gem resulted in:

Mocha deprecation warning at /Users/aaron/Repo/backup-custodian/test/test_backup_custodian.rb:2:in `require': `require 'mocha/mini_test'` has been deprecated. Please use `require 'mocha/minitest' instead.
/Users/aaron/.gem/ruby/2.5.3/gems/google-api-client-0.28.0/lib/google/apis/core/batch.rb:169: warning: assigned but unused variable - length
/Users/aaron/.gem/ruby/2.5.3/gems/httpclient-2.8.3/lib/httpclient/ssl_config.rb:370: warning: assigned but unused variable - pathlen
/Users/aaron/.gem/ruby/2.5.3/gems/httpclient-2.8.3/lib/httpclient/ssl_config.rb:51: warning: method redefined; discarding old initialize
/Users/aaron/.gem/ruby/2.5.3/gems/httpclient-2.8.3/lib/httpclient/ssl_config.rb:58: warning: method redefined; discarding old add_cert
/Users/aaron/.gem/ruby/2.5.3/gems/httpclient-2.8.3/lib/httpclient/ssl_config.rb:58: warning: method redefined; discarding old add_file
/Users/aaron/.gem/ruby/2.5.3/gems/httpclient-2.8.3/lib/httpclient/ssl_config.rb:58: warning: method redefined; discarding old add_path
/Users/aaron/.gem/ruby/2.5.3/gems/google-api-client-0.28.0/lib/google/apis/core/base_service.rb:215: warning: method redefined; discarding old client
/Users/aaron/Repo/backup-custodian/lib/backup_custodian/google_storage_fixup.rb:15: warning: method redefined; discarding old update_gapi!
/Users/aaron/.gem/ruby/2.5.3/gems/google-cloud-storage-1.15.0/lib/google/cloud/storage/file.rb:1662: warning: previous definition of update_gapi! was here
/Users/aaron/Repo/backup-custodian/lib/backup_custodian/google_storage_fixup.rb:34: warning: method redefined; discarding old rewrite_gapi
/Users/aaron/.gem/ruby/2.5.3/gems/google-cloud-storage-1.15.0/lib/google/cloud/storage/file.rb:1689: warning: previous definition of rewrite_gapi was here
Traceback (most recent call last):
	5: from /Users/aaron/.gem/ruby/2.5.3/gems/minitest-5.11.3/lib/minitest.rb:63:in `block in autorun'
	4: from /Users/aaron/.gem/ruby/2.5.3/gems/minitest-5.11.3/lib/minitest.rb:130:in `run'
	3: from /Users/aaron/.gem/ruby/2.5.3/gems/minitest-5.11.3/lib/minitest.rb:79:in `init_plugins'
	2: from /Users/aaron/.gem/ruby/2.5.3/gems/minitest-5.11.3/lib/minitest.rb:79:in `each'
	1: from /Users/aaron/.gem/ruby/2.5.3/gems/minitest-5.11.3/lib/minitest.rb:81:in `block in init_plugins'
/Users/aaron/.gem/ruby/2.5.3/gems/deprecation_toolkit-1.2.1/lib/minitest/deprecation_toolkit_plugin.rb:17:in `plugin_deprecation_toolkit_init': uninitialized constant Minitest::DeprecationToolkit (NameError)
rake aborted!
Command failed with status (1)
/Users/aaron/.gem/ruby/2.5.3/gems/rake-12.3.2/exe/rake:27:in `<top (required)>'
/Users/aaron/.gem/ruby/2.5.3/bin/bundle:23:in `load'
/Users/aaron/.gem/ruby/2.5.3/bin/bundle:23:in `<main>'
Tasks: TOP => default => test
(See full trace by running task with --trace)

@rafaelfranca
previously identified the problem here -- https://github.com/Shopify/deprecation_toolkit/blob/master/lib/minitest/deprecation_toolkit_plugin.rb#L2

The plugin does not require deprecation_toolkit -- if I add the require line to my local copy of the file (in my .gem folder) then my tests pass successfully.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.