【问题标题】:How is the best way to refactor a long string line in ruby?在 ruby​​ 中重构长字符串的最佳方法是什么?
【发布时间】:2017-06-10 02:28:06
【问题描述】:

我有这样一行代码

"#{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 警告我的原因。

我想将它重构为不长线。

我知道很多方法可以做到这一点,但我想知道哪一种是红宝石风格专家所期望的。

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

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

【问题讨论】:

  • 没有任何上下文,在这里很难给出建议。我将如何处理这在很大程度上取决于所讨论的对象以及它们之间的关系。这段代码的 self 对象是什么?为什么它为所有事情都调用Budget::util_name?这是一种独立的命名方法还是更大逻辑的一部分?
  • 嘿@AdamLassek,谢谢你。我编辑我的问题来解释 util 方法,你的其他问题我真的不明白,或者如果我有我将不得不解释这个应用程序的很多内容
  • 这段代码用于 pdf 报告,如果有帮助,用于上下文

标签: ruby-on-rails ruby ruby-on-rails-4 ruby-style-guide


【解决方案1】:

你可以试试这个

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 更具可读性

【讨论】:

  • 每个预算的新行可能更具可读性,具体取决于您的偏好
  • 接受,因为它是 ruby​​-style-guide 用于长字符串的方式 github.com/bbatsov/ruby-style-guide#no-trailing-backslash
  • 很好地使用了鲜为人知的组合多个字符串的技巧。尽管给问题添加了额外信息,但您可以完全删除Budget::util_name,它会做同样的事情。
  • 不,我不能。在您的答案下方解释了原因
【解决方案2】:

您可能会尝试的一件事是不使用字符串插值,而是使用连接和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 数组的其他用途,那会有所帮助。

【讨论】:

  • 这对我来说非常好,因为我必须多次调用 util_name() 方法。但根据 ruby​​-style-guide “首选字符串插值和字符串格式化而不是字符串连接”github.com/bbatsov/ruby-style-guide#string-interpolation
  • 鉴于额外的上下文,很明显Budget::util_name 根本不需要存在,将Enumerable 加入其中只会增加不必要的复杂性,IMO。
  • 不错的解决方案,尤其是#2。
  • 似乎map 实际上可能是map{ |x| x || "" },或者可以将其替换为compact 以删除空位。
  • AdamLassek 和@DavidAldridge 如果可能的话最好删除该方法,不幸的是,在我对亚当的回答#comment-76030950 的评论中无法解释
【解决方案3】:

当我需要一个空字符串(如果它为 nil)时,需要该静态方法 util_name 来防止出现 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。如果将这些数据提取到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

毫无疑问,真正的代码会比这更复杂,只是值得深思。

【讨论】:

  • 感谢您对我已删除的答案的评论。我在想什么?
  • @AdamLassek 感谢您提供新的好答案。让我在这里澄清一点。实际上 util 静态方法不能被删除,因为它不仅仅是一个“打印空白方法”。我验证一个类“不是属性”是否为 nil,然后打印“name”属性,这一次可以为 nil,并且插值将自动调用 to_s。如果您同意我的观点,请编辑您的答案,否则请告诉我我在这里没有得到什么。
  • 您的代码无法运行,因为您从未在其中任何一个中调用 .name 属性,明白吗?
  • 我相信你以为那些信封大小、_paper _color 等等都是属性,因为它的名字,我知道它们是坏的。不幸的是,该代码来自我正在维护的旧版应用程序......
  • @RamonMarques 你是对的,我错过了给.name 的电话。仍然没有必要,但我会更新以反映这种行为。
猜你喜欢
  • 1970-01-01
  • 2011-09-26
  • 2010-10-19
  • 2011-01-29
  • 2012-05-31
  • 1970-01-01
  • 2010-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多