First, you need to understand how ABC is calculated. Nested conditionals do not affect ABC. RuboCop's warning output shows you the calculation results:
Assignment Branch Condition size for call is too high. [<5, 28, 4> 28.72/17]
The <5, 28, 4> is your <Assignments, Branches, Conditionals>, as described in this article.
The overall score is calculated like this: sqrt(5^2 + 28^2 + 4^2) = 28.72
The default maximum score for the cop is 17.
I've annotated your code below with the ABC scores for each line. Take note that every time you reference context, you add a B-point. This is because context is not a local variable to call, so the ABC metric assumes it's a method call every time.
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
if you set the cop option: CountRepeatedAttributes: false (which I recommend), it will bring your score down to 19.1.
In lieu of that, you can bring your score down by extracting the creation of WebPushSubscription into its own method like so:
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
This will split the score up between the two methods. Note some additional ABC-saving strategies in create_subscription, like assigning push_params to a variable, and using dig for the nested hash accessors. Final score for create_subscription is between 12 and 16 depending on the cop options you use, and call is between 6 and 8.
Generally all that's required to bring down ABC score is to refactor to smaller methods.