This tutorial explores how to add rating functionality to a Rails application. It will go through some of the more popular Ruby gems that provide such functionality, as well as how to implement rating from scratch. We will use the same application used in the the Online store article By Karim El Hussieny, in which the basic application creation process is explained in detail. Please refer to that post to learn how Karim scaffolded and styled the basic application.
Here is the fork of the repository that we will use for our application.
Key Takeaways
- The Ratyrate gem, a fork of the Letsrate gem, allows developers to add rating functionality to a Rails application, with features such as customizable star numbers, the ability to disable rating, and the ability to cancel ratings.
- The Ratyrate gem wraps the functionality of the Raty jQuery plugin and provides helpers for IMDB-style rating. It can be installed by including it in the Gemfile and running ‘bundle install’ in the terminal.
- The Ratyrate gem requires the creation of Rate and RatingCache models linked to the User model, and the addition of the ‘ratyrate_rateable’ and ‘ratyrate_rater’ lines to the Movie and User models respectively.
- The Ratyrate gem offers a variety of options for customization, including changing the number of stars, enabling or disabling the ability to change ratings, enabling half values for ratings, customizing star icons, changing the position of the cancel button, adding a hint to the cancel button, and displaying score hints.
Goals
As you can see in the repository, it is an online store for movies. We need to add rating functionality to each movie. Each user will be able to give a rating to the available movies. Then, each movie will have a score calculated based on all the user rating, much like what IMDB does.
Main Features
- Rating movies: Rating the available store movies. (Login required)
- Show user rating: Each user can visit his/her dashboard and see the movies (s)he rated. The ratings can be changed at anytime.
- Change star numbers Star numbers can be set to whatever suits the application nature.
- Show the score in digits Rating score can also be viewed as digits beside the stars.
- Movie overall average score: Each movie has a score based on all the user ratings. This score is calculated from all the dimensions of the movie, i.e. visual effects, custome design, etc.
- Ability to disable rating Rating can be disabled after the first rate and changed to readonly mode.
- Customizable icons Star and cancel icons can be changed.
- Canceling rating: User can cancel the rating given to any movie at any time.
Skills Needed
- Experience with Ruby on Rails.
- Basic knowledge of HTML/CSS3.
- Understanding the concept of responsive CSS frameworks.
Tools
- Ruby
(2.1.0)
– Programming language - Rails
(4.1.1)
– Back-end framework - Foundation 5 – Front-end CSS framework
- Ratyrate – Ruby gem
- Devise – User authentication
Ratyrate Gem
Ratyrate is a Ruby gem that wraps the functionality of the Raty jQuery plugin. It also adds some helpers along the style of IMDB rating. I forked it from a gem called Letsrate because of Letsrate’s inactivity. Ratyrate has some awesome, new features, so you are welcome to star it :)
Step 1: Installation of Ratyrate gem
To install this Ruby gem simply include it in your Gemfile
like so:
gem 'ratyrate'
In the terminal run:
bundle install
You may want to watch the repository and use the development version of Ratyrate because I am adding new features all the time. To use the development gem add the following to your Gemfile:
gem 'ratyrate', :github => 'wazery/ratyrate', :branch => 'development'
Step 2: Preparing the Application
I assume you have already cloned the moviestore repository and are inside its directory. If you don’t know how to do that, here is the commands:
$ git clone 'git@github.com:wazery/moviestore.git'
$ cd moviestore
Now you need to run the gem’s generator to copy the required assets and files (jQuery Raty plugin files, star icons, and JavaScript files) to their correct locations in your Rails application. In the terminal run:
$ rails g ratyrate User
As you can see, this command takes a single argument: the name of your existing Devise user model. This is necessary to bind the user and rating data.
Output files:
create app/assets/javascripts/jquery.raty.js
create app/assets/images/star-on.png
create app/assets/images/star-off.png
create app/assets/images/star-half.png
identical app/assets/images/mid-star.png
identical app/assets/images/big-star.png
create app/assets/images/cancel-on.png
create app/assets/images/cancel-off.png
create app/assets/javascripts/ratyrate.js.erb
create app/controllers/rater_controller.rb
create app/models/rate.rb
create app/models/rating_cache.rb
create app/models/average_cache.rb
identical app/models/overall_average.rb
route post '/rate' => 'rater#create', :as => 'rate'
create db/migrate/20140814135421_create_rating_caches.rb
identical db/migrate/20140706144643_create_rates.rb
create db/migrate/20140814135422_create_average_caches.rb
create db/migrate/20140814135423_create_overall_averages.rb
Run the migrations created with this generator, which create the required tables for the rating functionality. To run the migration:
$ rake db:migrate
Example
Suppose you have a Devise user model named User
. The Devise generator and ratyrate generator then look like:
$ rails g devise:install
$ rails g devise user
$ rails g ratyrate user # => user is the model generated by devise
This generator will create Rate
and RatingCache
models linked to your User
model. In our case, however, you don’t need to run the Devise generators as the moviestore repository has done that already.
Step 3: Add the ratyrate Associations
Open the Movie
model, or whatever model you want to add the starring capabilities to, and add the following line:
ratyrate_rateable 'visual_effects', 'original_score', 'director', 'custome_design'
This adds some dimensions which the user can rate. Here, we added four dimensions. This line also creates the required associations.
You also need to define the model that will do the actual rating. For our MovieStore application, it is the User
model, so add the following line to it:
ratyrate_rater
This also adds the required association to the User model.
Step 4: Using the Ratyrate Helper in Views
Open up the “show” view of the movies, which you can find in app/views/movies/show.html.erb
<div class="row">
<div class="small-2 large-2 columns">
<%= imdb_style_rating_for @movie, current_user%>
</div>
<br>
<div class="small-2 large-4 columns">
<% if current_user %>
Visual Effects: <%= rating_for @movie, "visual_effects" %>
<br>
Original Score: <%= rating_for @movie, "original_score" %>
<br>
Director: <%= rating_for @movie, "original_score" %>
<br>
Custome Design: <%= rating_for @movie, "custome_design" %>
<% end %>
</div>
</div>
The view uses the provided rating_for
helper to show the rating for each dimension that was specified.
Step 5:** Explore the rating_for Options
Here is how this view currently looks:
As you can see, the user can cancel the rating. This feature can be disabled, as we will discover later.
Stars
The number of stars can be changed by simply using the option stars:
Visual Effects: <%= rating_for @movie, "visual_effects", stars: 10 %>
Disable/Enable Rating
By default, after the user provides a rating the stars are disabled. However, there is an option to let it remain enabled so the user can change the rating. This option is disableafterrate, which is set to true
by default. Set it to false
as follows:
Visual Effects: <%= rating_for @movie, "visual_effects", disable_after_rate: false %>
Now the stars will not be disabled after the user’s first rating.
Half Stars
Once there are several ratings, it is likely that the average score will have half stars and, thus, be half-highlighted. This is the default behavior, but if you want to disable half stars, use the half_show option:
Visual Effects: <%= rating_for @movie, "visual_effects", half_show: false %>
Rating with More Precision
There is a useful option in which user can rate using half values, which is disabled by default. To enable it, just use this option:
Visual Effects: <%= rating_for @movie, "visual_effects", enable_half: true %>
Customizing Star Icons
One of the most useful options in this helper is to customize the icons of the stars. The provided icons include:
- Disabled stars (star_off)
- Enabled stars
- Half stars
- Cancel button in off state
- Cancel button in clicked state
You can customize each icon using its specific option, or just change the path to other icons with the same name.
Here is how to change the path:
Visual Effects: <%= rating_for @movie, "visual_effects", path: 'star_icons' %>
And here is how to change each icon separately:
- star_on
- star_off
- star_half
- cancel_on
- cancel_off
Changing the Position of the Cancel Button
The Cancel button is shown on the left, by default. You can change that to the right if you want:
Visual Effects: <%= rating_for @movie, "visual_effects", cancel_place: 'right' %>
Adding a Hint to Cancel Button
There is an option to add a custom hint message to the cancel button. The default message is “Cancel current rating!”:
Visual Effects: <%= rating_for @movie, "visual_effects", cancel_hint: 'Cancel this vote!' %>
Space Between Stars
You can turn the spacing between stars on/off easily by:
Visual Effects: <%= rating_for @movie, "visual_effects", space: true %>
It is false by default.
Getting Average Score
One of the new options I have added to this gem is the IMDB style average. It is a bit tricky to enable, as you need to enable two options (disable_after_rate, and imdb_avg):
Original Score: <%= rating_for @movie, "original_score", disable_after_rate: true, imdb_avg: true %>
The stars will change to display the average rating score using IMDB style stars after page refresh.
Displaying Score Hint
You can display score hints (bad, poor, regular, gorgeous) or the cancel hint in a custom div
, instead of showing them in tooltips:
Original Score: <%= rating_for @movie, "original_score", target: '#hintDiv' %>
<div id="hintDiv"></div>
As you can see, “hintDiv” is selected by its ID. It is not just limited to div
s, as you can set the target to text fields, text areas, or select boxes.
Add Text to the Hint
You can set a placeholder text to the target div (custom div you selected):
Original Score: <%= rating_for @movie, "original_score", target: '#hintDiv', targetText: 'Some text goes here!' %>
Changing the Type of Target Text
You can display some hints (bad, poor, etc) or just the score numbers. To change that, you have two options:
The default one which is “hint”:
Original Score: <%= rating_for @movie, "original_score", target: "#hintDiv", targetType: "hint" %>
And the score values:
Original Score: <%= rating_for @movie, "original_score", target: "#hintDiv", targetType: "score" %>
Changing the Format of Target Text
What if you want to display the hints or score values with a custom format, like “Score: 5” or “Rating: Good”? It is very simple, just look at these two examples:
Original Score: <%= rating_for @movie, "original_score", target: "#hintDiv", targetFormat: "Score: {score}" %>
Original Score: <%= rating_for @movie, "original_score", target: "#hintDiv", targetFormat: "Rating: {hint}" %>
Rating for User Helper
It would be nice to see the rating score of the current user so it can be changed. There is a helper named ratingforuser for that exact purpose. It takes the rateable object (the movie object in our application case), the current logged in user (provided by Devise), the dimension in which the rating is for, and the options. If the current user hasn’t provided a rating yet, it will display a single star. If the gave a rating of 2, for example, it displays two stars, that can be changed to one, meaning, the user cannot raise the given score.
Here is how to use it:
Visual Effects: <%= rating_for_user @movie, current_user, "visual_effects" %>
IMDB Style Rating Helper
Another useful helper is an overall average score of all dimensions, just like the IMDB. This helper is pretty easy to use, just pass in the rateable object (which is the movie in our application) and the current logged in user:
<%= imdb_style_rating_for @movie, current_user %>
Summary
In this tutorial, the Ratyrate gem added rating functionality to the movies in the MovieStore application. We explored how to use the Ratyrate gem in detail, and the available helpers and options in it. I hope this tutorial rates highly with you today!
Islam is a freelance web developer with experience spanning the full stack of application development. He is a co-founder of Whitespace which is a web development agency. Besides for that he spends his time working on open source projects that he find intriguing or writing tutorials. He was an ex-Google Summer of Code student in 2012 and a mentor for 2 projects in 2013 for KDE. You can find him on Twitter @wazery_ or check his Linkedin profile linkedin.com/in/wazery.