4
\$\begingroup\$

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 %>
asked Dec 23, 2019 at 15:00
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

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
answered Dec 25, 2019 at 19:00
\$\endgroup\$
4
  • \$\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\$ Commented Dec 25, 2019 at 19:01
  • \$\begingroup\$ I like the fill approach. \$\endgroup\$ Commented 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\$ Commented Dec 25, 2019 at 22:56
  • \$\begingroup\$ Thanks. I'd move the full_and_half_star_count private too. \$\endgroup\$ Commented Dec 26, 2019 at 13:30
1
\$\begingroup\$

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.
answered Dec 26, 2019 at 8:10
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.