Using Rails 6. Here's a piece that I wrote just to display number of stars. Obviously I am disgusted by my own code. How would you refactor?
# application_helper.rb
module ApplicationHelper
def show_star_rating(rating)
zero_star_icon_name = "star"
full_star_icon_name = "star_fill"
half_star_icon_name = "star_lefthalf_fill"
total_stars = []
round_by_half = (rating * 2).round / 2.0
(round_by_half.to_i).times { total_stars << full_star_icon_name }
if round_by_half - round_by_half.to_i == 0.5
total_stars << half_star_icon_name
end
if total_stars.size != 5
(5 - total_stars.size).times { total_stars << zero_star_icon_name }
end
total_stars
end
end
# show.html.erb
<% show_star_rating(agent_review.rating).each do |star| %>
<i class="f7-icons"><%= star %></i>
<% end %>
2 Answers 2
You can make use of the Array.new
, passing in the maximum number of stars you want to show, and defaulting all the stars to empty. Then, you can fill
in the number of full stars you need. Then, finally, thanks to Numeric
's divmod
returning either 0
or 1
for the number of half stars you need, you make one more pass and fill
in the number of half stars you need:
module StarHelper
EMPTY_STAR_ICON = 'star'.freeze
FULL_STAR_ICON = 'star_fill'.freeze
HALF_STAR_ICON = 'star_lefthalf_fill'.freeze
def full_and_half_star_count(rating)
(rating * 2).round.divmod(2)
end
def stars(rating, max_stars: 5)
full_stars, half_stars = full_and_half_star_count(rating)
Array.new(max_stars, EMPTY_STAR_ICON).
fill(FULL_STAR_ICON, 0, full_stars).
fill(HALF_STAR_ICON, full_stars, half_stars)
end
end
-
\$\begingroup\$ Same as my answer on the StackOverflow question, but it seems more on topic here than over there, so adding it over on this side \$\endgroup\$Simple Lime– Simple Lime2019年12月25日 19:01:22 +00:00Commented Dec 25, 2019 at 19:01
-
\$\begingroup\$ I like the
fill
approach. \$\endgroup\$Sebastián Palma– Sebastián Palma2019年12月25日 20:45:04 +00:00Commented Dec 25, 2019 at 20:45 -
\$\begingroup\$ Welcome to Code Review, you answer is a great first answer. Thank you for taking the time to port your answer across. \$\endgroup\$2019年12月25日 22:56:02 +00:00Commented Dec 25, 2019 at 22:56
-
\$\begingroup\$ Thanks. I'd move the
full_and_half_star_count
private too. \$\endgroup\$Victor– Victor2019年12月26日 13:30:29 +00:00Commented Dec 26, 2019 at 13:30
I doubt if all those logic is meant to be in the ApplicationHelper. You could create a new helper file and move that logic.
As you have three constant values declared within the method body, you can move them outside as constants, and modify the value of total_stars
depending on the conditions you have:
ZERO_STAR_ICON_NAME = "star"
FULL_STAR_ICON_NAME = "star_fill"
HALF_STAR_ICON_NAME = "star_lefthalf_fill"
def show_star_rating(rating)
round_by_half = (rating * 2).round / 2.0
total_stars = Array.new(round_by_half, FULL_STAR_ICON_NAME)
total_stars += [HALF_STAR_ICON_NAME] if round_by_half - round_by_half.to_i == 0.5
total_stars += Array.new(5 - total_stars.size, ZERO_STAR_ICON_NAME) unless total_stars.size == 5
end
- You can use
unless
whenever you have a!=
condition to make the statement more clear. - You can use
Array.new
to create a new array and sum (+=
) it to the current value oftotal_stars
.