如何编写 RuboCop Cop 以防止使用关键字参数编写 Sidekiq worker?

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

不久前,我发现 you can't use keyword arguments when defining Sidekiq workers。因为它只会在排队后引发异常,所以我想防止它永远进入生产环境。为此,我想添加一个 cop 来检查 #perform 的参数,并在使用关键字参数时引发错误。这是我迄今为止失败的尝试:

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

我在这里遗漏了两件事。第一个也是最重要的是我不知道如何判断一个参数是否是关键字。第二个是我只想将此警察应用于 Sidekiq 工作人员。我在这里遵循正确的方法吗?有更好的方法吗?

您可以使用 inheritance 仅为您的 Sidekiq 工作人员启用特定警察。例如,如果您的工作人员住在 app/workers,那么您可以在那里创建一个新的规则集,该规则集继承自您项目的根目录,然后启用您的自定义警察:

# 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

要让您的自定义 cop 检测关键字参数,请使用 node.type 方法。 node.arguments 的元素本身是节点并支持相同的方法,读取参数的 type 将 return 四个值之一:

# Standard argument: perform(foo)
:arg

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

# Keyword argument: perform(foo:)
:kwarg

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

因此检查 :kwarg:kwoptarg(并注意路径更改和附加 Custom 模块以定义此警察的部门):

# 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

让我们以此作为示例工人:

# 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

运行 Rubocop 将 return 以下输出:

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