为 Rubocop 重构两层以上的块嵌套

Refactoring More Than Two Levels of Block Nesting for Rubocop

我正在努力改进我的 Ruby 代码,特别是在根据 Ruby 习语进行样式设置方面。我有以下方法:

def self.carnivore_convert
  lambda do |value, field|
    diet_type = value
    if field[:header].to_s == 'diet'
      diet_value = value.to_s.downcase
      if diet_value =~ /yes|no/
        diet_value == 'yes' ? diet_type = 'Carnivore' : diet_type = ''
      end
    end
    diet_type
  end
end

Rubocop 抱怨这个,说我应该:

"Avoid more than 2 levels of block nesting."

特指这一行:

diet_value == 'yes' ? diet_type = 'Carnivore' : diet_type = ''

但是,我完全不清楚为什么这是一个 "style violation" 以及我对 "fix" 所做的任何事情似乎都会使代码更加混乱,至少在我的查看。

当我说我不明白为什么这是一种风格违规时,我确实理解 Rubocop 显然觉得我不应该有一个 if...if...if 排序结构。但我不确定为什么会这样。

我想我也许可以通过使用某种保护子句来摆脱这段代码中的第一个 if 条件,但我不能那样做,因为那样我的代码就不起作用了。例如,我这样更改了代码:

def self.carnivore_convert
  lambda do |value, field|
    diet_type = value
    next unless field[:header].to_s == 'diet'
    diet_value = value.to_s.downcase
    if diet_value =~ /yes|no/
      diet_value == 'yes' ? diet_type = 'Carnivore' : diet_type = ''
    end
    diet_type
  end
end

但是,虽然没有出错,但这确实会使代码无法按预期工作。我也尝试了 breakreturn 而不是 next

所以我不确定如何将这段代码变成 Rubocop 不会抱怨并且仍然可读的形状。作为一个附带问题,我觉得我在 "Ruby styling" 上花费了很多时间,即使我的代码确实有效。只要我能理解为什么这种风格是违规的,我不介意,但在这里我很难看到它。话虽这么说,但我确实了解到,要建立我对这些东西的直觉还有很长的路要走。

关于上述问题,有没有人有什么想法?

如果您收到嵌套警告,这基本上意味着 "there is too much going on here"。有时样式还有其他问题,您可以修复它们(例如保护条款)。其他时候你只需要拆分代码。您的方法和 类 越小,可读性就越高,并且可能具有单一职责。下面的代码远非完美,但它只是为了显示一个简单的更改以使其更具可读性并遵循约束:

def self.carnivore_convert
  lambda do |value, field|
    field[:header].to_s == 'diet' ? diet_type(value) || value : value
  end
end

def self.diet_type(diet_value)
  diet_value = diet_value.to_s.downcase
  if diet_value =~ /yes|no/
    diet_value == 'yes' ? 'Carnivore' : ''
  end
end
private_class_method :diet_type

编辑: 这是另一个等同于原始代码的版本,但更容易掌握:

def self.carnivore_convert
  lambda do |value, field|
    simplified_value = value.to_s.downcase

    if diet_header?(field) && simplified_value == 'yes'
      'Carnivore'
    elsif diet_header?(field) && simplified_value =~ /yes|no/
      ''
    else
      value
    end
  end
end

def self.diet_header?(field)
  field[:header].to_s == 'diet'
end
private_class_method :diet_header?