在 ruby 中重构长字符串行的最佳方法是什么?

How is the best way to refactor a long string line in ruby?

我有这样一行代码

"#{envelope_quantity} - envelope #{Budget::util_name(envelope_size)} #{Budget::util_name(envelope_paper)} #{Budget::util_name(envelope_color)} #{Budget::util_name(envelope_grammage)} #{Budget::util_name(envelope_model)} #{Budget::util_name(envelope_print)}"

太长了,读起来不好,这就是为什么 RuboCop 警告我 Metrics::LineLength

我想将其重构为不长的一行。

我知道很多方法,但我想知道 ruby 风格专家期望哪种方法。

当我需要一个空字符串(如果它是 nil)时,需要静态方法 util_name 来防止 nil。

def self.util_name(value)
  return '' if value.nil?
  value.name
end

您可以尝试的一件事是不使用字符串插值,而是使用连接和 join:

构造字符串
"#{envelope_quantity} - envelope " + 
[Budget::util_name(envelope_size), 
 Budget::util_name(envelope_paper),
 Budget::util_name(envelope_color),
 Budget::util_name(envelope_grammage),
 Budget::util_name(envelope_model),
 Budget::util_name(envelope_print)].join(' ')

更简洁,您可以使用 map:

"#{envelope_quantity} - envelope " + 
[envelope_size, 
 envelope_paper,
 envelope_color,
 envelope_grammage,
 envelope_model,
 envelope_print].map{|x| Budget::util_name(x)}.join(' ')

通过按正确的顺序定义一个包含所有信封属性的数组并对其应用 map 可能会更简洁:

envelope_properties=[envelope_size, 
                     envelope_paper,
                     envelope_color,
                     envelope_grammage,
                     envelope_model,
                     envelope_print]

"#{envelope_quantity} - envelope " + 
envelope_properties.map{|x| Budget::util_name(x)}.join(' ')

当然,如果您碰巧对 envelope_properties 数组有其他用途,那将会有所帮助。

你可以试试这个

str = "#{envelope_quantity} - envelope #{Budget::util_name(envelope_size)} "\
      "#{Budget::util_name(envelope_paper)} #{Budget::util_name(envelope_color)} "\
      "#{Budget::util_name(envelope_grammage)} #{Budget::util_name(envelope_model)} "\
      "#{Budget::util_name(envelope_print)}"

这样您就可以将字符串限制在最大行长度内,并且比使用 join

更易读

that static method util_name is needed to prevent nil when I need an empty string if it's nil.

def self.util_name(value)
  return '' if value.nil?
  value.name
end

好的,考虑到这些上下文,您可以完全删除 Budget::util_name 方法,因为它没有做任何有用的事情。有两种方法可以有条件地调用可能 nil 的对象上的方法,一种由框架提供,一种由语言提供。

如果您使用的是 Ruby 2.2 或更早版本,请使用 try method.

value.try(:name)

如果您使用的是 Ruby 2.3 或更高版本,您可以使用 safe navigation operator

value&.name

在任何一种情况下,您都不需要专门测试 nil,因为它会在插入时自动强制转换为空字符串。

"#{envelope_quantity&.name} - envelope #{envelope_size&.name} #{envelope_paper&.name} #{envelope_color&.name} #{envelope_grammage&.name} #{envelope_model&.name} #{envelope_print&.name}"

这样比较合理,但可能还是有点太长了。您可以使用字符串模板:

"%{quantity} - envelope %{size} %{paper} %{color} %{grammage} %{model} %{print}" % {
  quantity: envelope_quantity&.name,
  size:     envelope_size&.name,
  paper:    envelope_paper&.name,
  color:    envelope_color&.name,
  grammage: envelope_grammage&.name,
  model:    envelope_model&.name,
  print:    envelope_print&.name
}

但我想重点谈谈我注意到的关于此代码示例的一些事情。每个方法都以 envelope 开头,这可能意味着 these methods are telling you they should be a separate object. If you extract this data into a value object,那么这个辅助方法的自然位置就变得显而易见了......

class Envelope < Struct.new(:quantity, :size, :paper, :color, :grammage, :model, :print)
  def to_s
    "#{quantity&.name} - envelope #{size&.name} #{paper&.name} #{color&.name} #{grammage&.name} #{model&.name} #{print&.name}"
  end
end

毫无疑问,真正的代码会比这更复杂,仅供思考。