解决Rubocop ABC Size过高问题:发票编号生成代码优化
问题分析与解决方案
ABC指标得分来源
Rubocop的ABC指标计算公式为 √(A² + B² + C²),其中:
- A(Assignments):赋值操作数量,这里仅
self.number = ...这1次赋值,A=1 - B(Branches):方法调用、属性访问等分支操作数量。原代码里的分支包括:
- 两次执行
entity.invoices.where(period: entity.current_period)查询 - 调用
count.zero?、maximum(:number)方法 - 访问
entity.current_period、invoice_number_format.start_at属性 - 算术操作
+1
累计分支数B=17
- 两次执行
- C(Conditions):条件判断数量,即
if语句的判断逻辑,C=2
代入公式计算:√(1² + 17² + 2²) = √294 ≈ 17.15,超过默认阈值17,因此触发错误。
优化后的代码实现
核心思路是消除重复查询,利用Ruby和ActiveRecord特性简化逻辑:
class Invoice < ApplicationRecord belongs_to :entity has_one :invoice_number_format, through: :entity, dependent: :restrict_with_error before_create :generate_invoice_number private def generate_invoice_number current_period_invoices = entity.invoices.where(period: entity.current_period) max_number = current_period_invoices.maximum(:number) self.number = max_number ? max_number + 1 : invoice_number_format.start_at end end
优化点说明
- 提取重复查询结果到
current_period_invoices变量,避免两次执行相同SQL,同时减少分支计数 - 用
max_number的存在性判断替代count.zero?,逻辑更直接——maximum在无记录时返回nil,刚好适配判断需求 - 代码结构更清晰,ABC指标会显著降低(A=2,B≈8,C=1,计算后得分远低于17)
如果想进一步简化,还可以把默认值逻辑放到数据库层面处理:
def generate_invoice_number max_number = entity.invoices .where(period: entity.current_period) .select(Arel.sql("COALESCE(MAX(number), ?)", invoice_number_format.start_at - 1)) .first[0] self.number = max_number + 1 end
这个方案用数据库的COALESCE函数处理无记录的默认值,Ruby层面仅需执行加1操作;注意用参数绑定避免SQL注入风险,适用于start_at为整数类型的场景。
内容的提问来源于stack exchange,提问作者Ricky883249
相关产品推荐
相关产品推荐

