Reputation: 3264
I have a line of code like this
"#{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)}"
too long, it's bad to read, and that's why RuboCop is warning me with it's Metrics::LineLength.
I would like to refactor it to not be a long line.
I know a lot of ways to do that, but I wonder which one would be the expected for the ruby style experts.
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
Upvotes: 9
Views: 9095
Reputation: 1607
One thing you might try is to not use string interpolation, instead construct the string using concatenation and 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(' ')
Even more concisely, you could use map
:
"#{envelope_quantity} - envelope " +
[envelope_size,
envelope_paper,
envelope_color,
envelope_grammage,
envelope_model,
envelope_print].map{|x| Budget::util_name(x)}.join(' ')
This might be made more concise by defining an array with all of the envelope properties in the right order and applying map
to that:
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(' ')
Of course, it would help if you happened to have another use for the envelope_properties
array.
Upvotes: 7
Reputation: 35515
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
Okay, given that bit of context you can remove the Budget::util_name
method entirely because it isn't doing anything useful. There are two ways of conditionally calling a method on an object that might be nil
, one provided by the framework and one by the language.
If you are using Ruby 2.2 or earlier, use the try method.
value.try(:name)
if you are using Ruby 2.3 or above, you can use the safe navigation operator
value&.name
In either case, you don't need to specifically test for nil
because it will be automatically coerced into an empty string when interpolated.
"#{envelope_quantity&.name} - envelope #{envelope_size&.name} #{envelope_paper&.name} #{envelope_color&.name} #{envelope_grammage&.name} #{envelope_model&.name} #{envelope_print&.name}"
That's much more reasonable, still probably a bit too long. You could use string templates:
"%{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
}
But I want to focus on something I noticed about this code sample. Every single method begins with envelope
which probably means that these methods are telling you they should be a separate object. If you extract this data into a value object, then the natural location for this helper method becomes obvious...
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
Undoubtedly the real code would be more complicated than that, just food for thought.
Upvotes: 1
Reputation: 1371
You can try this
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)}"
this way you will be able to confine the string within max line length and also its slightly more readable than using join
Upvotes: 17