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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With