Acts As Rateable Plugin

5
Jul
23

Continuing with my work with Ruby on Rails plugins here is my version of the acts_as_rateable plugin. Use this plugin to make your model instances be rated by users. The ratings are numeric and could be from 0 to any number you would like. Typically you can rate articles, posts, reviews, etc from 0 to 5 but this plugin does not dictate the range. To install this plugin run the following command:

script/plugin install http://juixe.com/svn/acts_as_rateable

The installation process will add several ruby files in the vendor/plugins directory. Create a new rails migration and cut and past the following self.up and self.down methods:

def self.up
  create_table :ratings, :force => true do |t|
    t.column :rating, :integer, :default => 0
    t.column :created_at, :datetime, :null => false
    t.column :rateable_type, :string, :limit => 15,
      :default => "", :null => false
    t.column :rateable_id, :integer, :default => 0, :null => false
    t.column :user_id, :integer, :default => 0, :null => false
  end

  add_index :ratings, ["user_id"], :name => "fk_ratings_user"
end

def self.down
  drop_table :ratings
end

Once you have the acts_as_rateable plugin installed you can make your ActiveRecord models act as rateable by calling the acts_as_rateable method.

class Post < ActiveRecord::Base
  acts_as_rateable
end

In your controller you can rate a post with just a few lines of code:

post = Post.find(params[:id])
rating = Rating.new(:rating ==> 2, :submitted ==> Time.new)
@post.ratings << rating

Similarly you can do this:

post = Post.find(params[:id])
post.add_rating Rating.new(:rating => 2)

And of course you can iterate through the ratings of a post by using the ratings property such as the following bit of code:

post.ratings.each { |r|
  logger << "rating value: #{r.rating} for object: #{r.rateable}\n"
}

You can also use the ratings property to get the number of ratings this post has been given.

count = post.ratings.size

And if you want to display the average rating you can use the rating function.

average_rating = post.rating

Stay tuned for more on Ruby on Rails plugins…

Technorati Tags: , , , , , ,

Enjoy. Share. Be Happy.
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • BlinkList
  • MySpace
  • Netvouz
  • NewsVine
  • StumbleUpon
  • TwitThis
Filed under: Ruby, TechKnow
23 Comments

23 Comments

  1. Anand
    7:17 pm on March 2nd, 2007

    Hi

    I used the rateable plugin as well. Many thanks.

    Question on the .rating method. How is the performance when you have hundreds of ratings to calculate the average? Any tips on how to make it higher performance?

    anand

  2. Zach
    5:50 pm on March 4th, 2007

    Hey,

    I’m using your rateable plugin, and I would like to filter my results by highest rating. I can’t come up with a way to make it work. Any suggestions.

    Zach

  3. Sean
    7:37 am on April 4th, 2007

    I can’t seem to get this to work properly, it’s the first plugin I’ve tried to install so I think my mistake might be something simple I overlooked. Whenever I add “acts_as_rateable” to my story model I get the following error on any action that uses the story model (which is just about all of them): “undefined local variable or method `acts_as_rateable’ for Story:Class”. I installed the plugin and it is present inside the vendor/plugins directory and I created the migration and ran rake on it. If I had to guess it seems like rails isn’t finding the plugin. Is there something else I need to do?

  4. TechKnow
    10:36 am on April 4th, 2007

    @Sean - I only get that error when I delete the plugin from a Rails application. I just installed the plugin from the SVN repository and it worked right off the bat. In your case, the spelling of the method is correct and you say the plugin is found in the app/vendor/plugins directory. Sorry, but from your description I can’t see what could be the problem. :(

  5. Daniel Fischer
    8:14 pm on April 11th, 2007

    If you wouldn’t mind TechKnow, could you give me an example of a functionality of how I could have the numbers 1..5 or maybe a dropdown to send to a picture for e.g? regarding view/controller programming?

    So far I thought of something like this:

    def rate
    @picture = Picture.find(params[:id])
    @picture.add_rating Rating.new(:rating => params[:rating])
    end

    but I am not really sure what to do in the view, or where to go/modify from here.

    Thanks!

  6. Daniel Fischer
    9:20 pm on April 11th, 2007

    Also, regarding the user_id, I’m not sure how to take full advantage of that.

    A picture has many users, in the rate method I showed you, how would I set the current rating to be owned by user so and so? I thought something like @picture.user_id = self.current_user.id
    would work, but then I realized it’s not for the picture, it’s for the rating, yet rating is being created with calling the model, so not sure. Any tips?

  7. TechKnow
    11:59 pm on April 15th, 2007

    @Daniel - Thanks for your comments. Here are three tutorials you might like… Rails 4-State Ajax & CSS Star Rating by Igvita. Rails Ajax Rating System by Naffis. CSS Star Rating Part Deux by Komodo Media.

  8. Shawn
    11:40 pm on June 7th, 2007

    Hi, is there any way to make this plugin tally votes numerically instead, like reddit? Where users can /- votes and they can reach negative numbers?

    Thanks for the great plugin!

  9. TechKnow
    4:47 pm on June 8th, 2007

    @Shawn - If you want to have a /- vote like digg or reddit then you can use Acts as Voteable. That might be just what you need.

  10. Ivan Storck
    4:33 pm on August 24th, 2007

    I’m also looking for a way to get a list of things ordered by highest rating.

  11. Ivan Storck
    3:19 pm on August 27th, 2007

    Here’s the SQL I used, my things that are rateable are called “Writings”

    SELECT
      writings.id, writings.title, writings.excerpt, writings.description,
      writings.body, writings.created_on, writings.updated_on,
      writings.created_by, writings.updated_by, writings.featured,
      writings.views, avg(rating) AS avg_rating
    FROM
      writings
    LEFT JOIN ratings ON ratings.rateable_id = writings.id
    GROUP BY rateable_id
    ORDER BY avg_rating DESC
    
  12. Seth
    4:12 am on September 6th, 2007

    Thanks for a really good tutorial!

    Just wondering if there is a method to pull the acts_as_rateable model attributes for a specific user?

    My acts_as_rateable model is Businesses, so I can call @business.ratings.each do |b| b.user.login end - this will obviously return all the users that have rated that particular business… I would like to know how to do this in reverse…

    So pull a user and get the businesses that they have rated…

    So far I have this in the users controller which will only return the business id:
    @businesses = Rating.find(:all,
    :conditions => ["user_id = ?", @user.id],
    :order => “created_at DESC”)

    Does anyone have any ideas?

    Thanks

  13. David Lowenfels
    3:12 pm on December 20th, 2007

    Anand:
    The performance of the average rating calculation can be improved greatly by using SQL instead of ruby.

    # Helper method that returns the average rating
    #
    def rating
      Rating.average( :rating, :conditions => {
        :rateable_id => self.id,
        :rateable_type => self.class.name
      })
    end
    

    Seth:
    use the find_ratings_by_user method

  14. Lukas
    7:19 am on December 30th, 2007

    with the newer version of rails there will be an error called :
    “The :dependent option expects either :destroy, :delete_all, or :nullify (true)”
    which appears in:
    “vendor/plugins/acts_as_rateable/lib/acts_as_rateable.rb:12:in `acts_as_rateable’”

    The Line looks like this :
    “has_many :ratings, :as => :rateable, :dependent => true”

    just change the true to one of these :
    ” :destroy This destroys the associated objects”

    ” :delete_all deletes them directly from the database ”

    ” :nullify to set the keys to null”

    so the line should look somthing like this afterwards :

    “has_many :ratings, :as => :rateable, :dependent => :destroy”

  15. Dom
    12:16 am on January 14th, 2008

    Hi, I’m getting the following error when trying to use this plugin with a model in rails 2.0.1:

    ArgumentError in MediaController#articles

    The :dependent option expects either :destroy, :delete_all, or :nullify (true)

    My model is Article, it is currently using the following plugins:
    has_self_referential_many_to_many :related
    acts_as_taggable
    acts_as_commentable
    acts_as_rateable

    If I comment out the first four plugins and leave rateable in, I still get the error. It’s currently breaking on this line:

    Library/Ruby/Gems/1.8/gems/activerecord-2.0.1/lib/active_record/associations.rb:1149:in `configure_dependency_for_has_many’

    Can you help?

  16. Dom
    2:01 am on January 14th, 2008

    Ok just thought I’d let you know I fixed the issue by replacing the line 12 in acts_as_rateable.rb to:

    has_many :ratings, :as => :rateable, :dependent => :destroy

    The :dependent => true option on has_many association was deprecated in 1.1:

    http://weblog.rubyonrails.org/2006/4/28/associations-arent-dependent-true-anymore

  17. TechKnow
    2:23 am on January 15th, 2008

    @Lukas, @Dom - Thanks for the comment. I have update the plugin accordingly.

  18. kyanh
    6:46 am on January 20th, 2008

    i have installed successfully acts_as_rateable. excellent plugin! keep your great work!

Leave a comment

RSS feed for comments on this post