In Ruby 3.0, positional arguments and keyword arguments will be separated. For now ruby 2.7 brings deprecations warnings that notifies us that our code should be updated. In most cases adding or removing ** should fix the problem but the question is - how to find all occurrences in the code?

If you have high test coverage it should be pretty easy - just run the specs and fix warnings one by one. What if you don't? Well, there is another solution.

Custom warnings handling

Ruby allows us to customize warnings handling by implementing Warning module with warn method:

module Warning def warn ( msg ) # our custom warning logic end end

If you are using Rails you can create warning.rb file in lib directory and include it right after Bundler.require(*Rails.groups) in your application.rb :

Bundler . require ( * Rails . groups ) require_relative '../lib/warning'

You have to explicitly require it since autoloading won't work in this case, because we are overwriting standard library.

If you are using error tracking services like Honeybadger, Sentry or Rollbar you can use them to notify you about warnings (in my example I'll use Raven gem from Sentry).

Simple solution would look like this:

module Warning class DeprecationWarning < StandardError ; end def warn ( msg ) Raven . capture_exception ( DeprecationWarning . new ( msg )) end end

After deploying it to staging/production we will receive notifications about each warning. Be careful! Make sure that your warn implementation isn't causing any warnings! If it is, it will create an infinite loop!

Let's take a look at an example:

/Users/kukicola/.rvm/gems/ruby-2.7.0/gems/activerecord-6.0.2.1/lib/active_record/relation/delegation.rb:115: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call /Users/kukicola/.rvm/gems/ruby-2.7.0/gems/activerecord-6.0.2.1/lib/active_record/relation.rb:27: warning: The called method `initialize' is defined here

In this case warning contains two parts. Our method will be called two times - one for each line. What is really important for us is the path of the invalid method call, so in this case:

/Users/kukicola/.rvm/gems/ruby-2.7.0/gems/activerecord-6.0.2.1/lib/active_record/relation/delegation.rb:115

We can update our code to extract path from the first line and skip the second one:

module Warning class DeprecationWarning < StandardError ; end def warn ( msg ) return unless msg . include? ( 'deprecated' ) path = msg . split ( ':' )[ 0 .. 1 ]. join ( ':' ) Raven . capture_exception ( DeprecationWarning . new ( path )) end end

Our solution is almost perfect! There is only one case left - what if our app is causing some different warnings? We can keep original implementation for them using alias_method . It's not pretty but since it's a module we can't call super :

module Warning class DeprecationWarning < StandardError ; end alias_method :original_warn , :warn def warn ( msg ) return original_warn ( msg ) unless msg . include? ( 'deprecated' ) path = msg . split ( ':' )[ 0 .. 1 ]. join ( ':' ) Raven . capture_exception ( DeprecationWarning . new ( path )) end end

Deploy and fix warnings as they occur

Since Ruby 2.7 does not change arguments behavior it should be safe to deploy it to production environment. With custom Warning implementation you can fix warnings one by one as they occur. Keep in mind that you'll probably see a lot of warnings that comes from gems. It is a good moment to update them as well!

Please enable JavaScript to view the comments powered by Disqus.