# Rails Helper to display rating in stars

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 %>


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

• 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 Dec 25 '19 at 19:01
• I like the fill approach. Dec 25 '19 at 20:45
• Welcome to Code Review, you answer is a great first answer. Thank you for taking the time to port your answer across. Dec 25 '19 at 22:56
• Thanks. I'd move the full_and_half_star_count private too. Dec 26 '19 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 of total_stars.