Rubocop 抱怨 Metrics/AbcSize

Wag*_*ira 6 ruby metrics ruby-on-rails code-metrics rubocop

所以我试图在这里采取正确的路径并了解如何解决这个警察,在我看来这看起来像是一小段代码,为什么它会抱怨?

移动嵌套的 if-else 不会改变任何东西,关于如何解决这个警察有什么建议吗?

 class WebPush::Register

  include Interactor

  # rubocop:disable Metrics/AbcSize
  def call
    user = Learner.find_by(id: context.user_id)

    # return if existing
    if user.web_push_subscription
      context.subscription = user.web_push_subscription
    else
      subscription = WebPushSubscription.new(
        endpoint:   context.push_params[:endpoint],
        auth_key:   context.push_params[:keys][:auth],
        p256dh_key: context.push_params[:keys][:p256dh],
        learner:    user
      )

      if subscription.save
        context.subscription = subscription
      else
        context.error = subscription.errors.full_messages
        context.fail!
      end
    end
  end
  # rubocop:enable Metrics/AbcSize

end
Run Code Online (Sandbox Code Playgroud)

Ton*_*rra 17

首先,您需要了解 ABC 是如何计算的。条件语句的嵌套不会影响 ABC。RuboCop 的警告输出显示了计算结果:

Assignment Branch Condition size for call is too high. [<5, 28, 4> 28.72/17]
Run Code Online (Sandbox Code Playgroud)

正如本文所述,这<5, 28, 4>是您的。<Assignments, Branches, Conditionals>

总分计算如下:sqrt(5^2 + 28^2 + 4^2) = 28.72

警察的默认最高分数是 17 分。

我在下面用每行的 ABC 分数注释了您的代码。

def call
  user = Learner.find_by(id: context.user_id) # <1, 3, 0>

  if user.web_push_subscription # <0, 1, 1>
    context.subscription = user.web_push_subscription # <1, 3, 0>
  else # <0, 0, 1>
    # this constructor call is the most expensive part
    subscription = WebPushSubscription.new( # <1, 1, 0>
      endpoint:   context.push_params[:endpoint], # <0, 3, 0>
      auth_key:   context.push_params[:keys][:auth], # <0, 4, 0>
      p256dh_key: context.push_params[:keys][:p256dh], # <0, 4, 0>
      learner:    user # <0, 0, 0>
    )

    if subscription.save # <0, 1, 1>
      context.subscription = subscription # <1, 2, 0>
    else # <0, 0, 1>
      context.error = subscription.errors.full_messages # <1, 4, 0>
      context.fail! # <0, 2, 0>
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

每次context被引用时,指标都会添加一个 B 点。这是因为context不是函数的局部变量call,因此指标每次都假设它是新的方法调用。

如果您设置 cop 选项:CountRepeatedAttributes: false(我推荐),它只会为所有要合并的引用添加一个 B 点context。这使得 ABC 分数降至19.1.

相反,您可以通过将创建提取WebPushSubscription到它自己的方法中来降低分数,如下所示:

  def call
    user = Learner.find_by(id: context.user_id)

    if user.web_push_subscription
      context.subscription = user.web_push_subscription
    else
      create_subscription(user)
    end
  end

  private

  def create_subscription(user)
    push_params = context.push_params
    subscription = WebPushSubscription.new(
      endpoint:   push_params[:endpoint],
      auth_key:   push_params.dig(:key, :auth),
      p256dh_key: push_params.dig(:key, :p256dh),
      learner:    user
    )

    if subscription.save
      context.subscription = subscription
    else
      context.error = subscription.errors.full_messages
      context.fail!
    end
  end
Run Code Online (Sandbox Code Playgroud)

这将在两种方法之间分配分数。请注意 中的一些额外的 ABC 保存策略create_subscription,例如分配push_params给变量以及用于dig嵌套哈希访问器。最终得分create_subscription在 12 到 16 之间,具体取决于您使用的警察选项,并且call在 6 到 8 之间。

一般来说,降低 ABC 分数所需的只是重构为更小的方法。

  • @ShannonScottSchupbach 同意。我手动注释了这些行 - 但为了确认我发布此内容时的数字,我逐行注释掉了每一行,并每次都重新运行 ABC cop。 (2认同)