Victor
Victor

Reputation: 13388

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

Upvotes: 0

Views: 326

Answers (2)

Thang
Thang

Reputation: 841

The way I would implement show_star_rating:

def show_star_rating(rating)
  zero_star_icon_name = "star"
  full_star_icon_name = "star_fill"
  half_star_icon_name = "star_lefthalf_fill"
  rating_round_point5 = (rating * 2).round / 2.0

  (1..5).map do |i|
    next(full_star_icon_name) if i <= rating_round_point5
    next(half_star_icon_name) if rating_round_point5 + 0.5 == i
    zero_star_icon_name
  end
end

Upvotes: 1

Simple Lime
Simple Lime

Reputation: 11050

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

Upvotes: 3

Related Questions