Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a RuboCop Cop to prevent writing Sidekiq workers using keyword arguments?

Not too long ago, I've found out that you can't use keyword arguments when defining Sidekiq workers. Since it only raises an exception after it gets enqueued, I want to prevent this from reaching production for good. And for that, I'd like to add a cop that checks the arguments for #perform and raises an error if there's a keyword argument being used. This is my failed attempt so far:

module RuboCop
  module Cop
    class SidekiqDontUseKeywordArguments < RuboCop::Cop::Cop
      MSG = "Don't use keyword arguments in workers".freeze
      OBSERVED_METHOD = :perform

      def on_def(node)
        return if node.method_name != OBSERVED_METHOD

        node.arguments.each do |argument|
          if keyword_argument?(argument)
            add_offense(node, location: :expression)
          end
        end
      end

      private

      def keyword_argument?(argument)
        # detect if the argument is a keyword
      end
    end
  end
end

I'm missing two things here. The first and most important is that I don't know how to tell whether an argument is a keyword or not. The second is that I'd like to apply this cop only for Sidekiq workers. Am I following the right approach here? Is there a better way of doing this?

like image 979
joeloui Avatar asked Oct 27 '25 06:10

joeloui


1 Answers

You can enable specific cops for only your Sidekiq workers by using inheritance. For example, if your workers live in app/workers then you can create a new ruleset there that inherits from your project’s root and then enables your custom cop:

# app/workers/.rubocop.yml
inherit_from:
  - ../../.rubocop.yml
require:
  - ../lib/rubocop/cop/custom/sidekiq_dont_use_keyword_arguments.rb
Custom/SidekiqDontUseKeywordArguments:
  Enabled: true
# For validation of this theory only; do not use this
Lint/DuplicateMethods:
  Enabled: false

To make your custom cop detect keyword arguments use the node.type method. The elements of node.arguments are themselves nodes and support the same methods, and reading the type of an argument will return one of four values:

# Standard argument: perform(foo)
:arg

# Optional standard argument: perform(foo = 1)
:optarg

# Keyword argument: perform(foo:)
:kwarg

# Optional keyword argument: perform(foo: 1)
:kwoptarg

So check for :kwarg and :kwoptarg (and note the path change and additional Custom module to define this cop's department):

# app/lib/rubocop/cop/custom/sidekiq_dont_use_keyword_arguments.rb

module RuboCop
  module Cop
    module Custom
      class SidekiqDontUseKeywordArguments < RuboCop::Cop::Cop
        MSG = "Don't use keyword arguments in workers".freeze
        OBSERVED_METHOD = :perform

        def on_def(node)
          return if node.method_name != OBSERVED_METHOD

          node.arguments.each do |argument|
            if argument.type == :kwarg || argument.type == :kwoptarg
              add_offense(node, location: :expression)
            end
          end
        end
      end
    end
  end
end

Let’s use this as an example worker:

# app/workers/example_worker.rb
class ExampleWorker
  # Standard argument; this will not trigger the cop
  def perform(foo); end

  # Optional standard argument; this will not trigger the cop
  def perform(foo = 1); end

  # Keyword argument; this will trigger the cop
  def perform(foo:); end

  # Optional keyword argument; this will trigger the cop
  def perform(foo: 1); end
end

Running Rubocop will return the following output:

rubocop app/workers
Inspecting 1 file
C

Offenses:

app/workers/example_worker.rb:9:3: C: Custom/SidekiqDontUseKeywordArguments: Don't use keyword arguments in workers
  def perform(foo:); end
  ^^^^^^^^^^^^^^^^^^^^^^
app/workers/example_worker.rb:12:3: C: Custom/SidekiqDontUseKeywordArguments: Don't use keyword arguments in workers
  def perform(foo: 1); end
  ^^^^^^^^^^^^^^^^^^^^^^^^

1 file inspected, 2 offenses detected
like image 102
anothermh Avatar answered Oct 29 '25 20:10

anothermh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!