ruby Rubocop抱怨度量/AbcSize

v64noz0r  于 2023-03-08  发布在  Ruby
关注(0)|答案(2)|浏览(132)

所以我试着走正确的路,理解如何解决这个问题,这看起来像一小段代码,为什么它会抱怨?
移动嵌套的if-else不会改变任何东西,对如何解决这个cop有什么建议吗?

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
qltillow

qltillow1#

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

Assignment Branch Condition size for call is too high. [<5, 28, 4> 28.72/17]

<5, 28, 4>就是您的<Assignments, Branches, Conditionals>,如this article中所述。
总分计算如下:sqrt(5^2 + 28^2 + 4^2) = 28.72
警察的默认最高分是17分。
我在下面的代码中标注了每一行的ABC值,请注意,每次引用context时,都会添加一个B点,这是因为context不是call的局部变量,所以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

如果设置了cop选项:CountRepeatedAttributes: false(这是我推荐的),它会把你的分数降到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

这将在两种方法之间分配分数。注意create_subscription中的一些额外的ABC节省策略,如将push_params赋值给变量,以及将dig用于嵌套的哈希访问器。create_subscription的最终分数介于12和16之间,具体取决于您使用的cop选项,call介于6和8之间。
一般来说,降低ABC分数所需要做的就是重构为更小的方法。

a0x5cqrl

a0x5cqrl2#

ABC代表Assignment, Branch, Condition. ABC = sqrt(A^2 + B^2 + C^2)
我们数一数。

def call
    user = Learner.find_by(id: context.user_id)  // assignment, branches

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

      if subscription.save // branch, condition
        context.subscription = subscription  // assignment
      else  // condition
        context.error = subscription.errors.full_messages  // assignment, branches
        context.fail!  // branch
      end
    end
  end
  • A = 5
  • B = 17(至少)
  • C = 4

这样得到18.2。默认情况下,17是Rubcop认为满意的最大值。
我们可以用几个简单的提取方法来处理这个问题。

private def user
    @user ||= Learner.find_by(id: context.user_id)
  end

  private def create_web_push_subscription_from_context
    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

  def call
    if user.web_push_subscription
      context.subscription = user.web_push_subscription
    else
      create_web_push_subscription_from_context
    end
  end

经过这种重构,代码变得更简单,每个方法只做一件事,这就是ABC度量所鼓励的。

相关问题