The Dev Blog

Putting Family Management on Rails!

A New Rails Plugin: acts_as_rated

Posted by Guy Naor Mon, 05 Feb 2007 03:20:00 GMT

I just released on RubyForge a new rails plugin for rating of any ActiveRecord model. The project page is at rubyforge.org/projects/acts-as-rated.

Though similar to other rating plugins, this one has a ton of options to customize, while still making it very easy to use. And most important for my use, can cache the statistics of the ratings (total/count/average) in the model itself or an external statistics table, eliminating the need to call sum/count/avg on the ratings table itself. . To install:

script/plugin install svn://rubyforge.org/var/svn/acts-as-rated/trunk/acts_as_rated

Usage example:

class Book < ActiveRecord::Base
  acts_as_rated
end

u = User.find_by_name "guy"
b = Book.find "Catch 22"
b.rate 5, u 
u = User.find_by_name "john"
b.rate 3, u

b.rating_average # => 4
Book.find_by_rating 2..3 # => [<Book:"Catch 22">]

b.find_rated_by User.find_by_name("guy") # => [<Book:"Catch 22">]

The plugin comes with a full set of migration methods to make it easy to add to any project, and it also has extensive testing included.

Features:

  • Rate any model
  • Optionally add fields to the rated objects to optimize speed
  • Optionally add an external rating statistics table with a record for each rated model
  • Can work with the added fields, external table or just using direct SQL count/avg calls
  • Use any model as the rater (defaults to User)
  • Limit the range of the ratings
  • Average, total and number of ratings
  • Find objects by ratings or rating ranges
  • Find objects by rater
  • Extensively tested

Enjoy!

Posted in , ,  | 8 comments

del.icio.us:A New Rails Plugin: acts_as_rated digg:A New Rails Plugin: acts_as_rated spurl:A New Rails Plugin: acts_as_rated wists:A New Rails Plugin: acts_as_rated simpy:A New Rails Plugin: acts_as_rated newsvine:A New Rails Plugin: acts_as_rated blinklist:A New Rails Plugin: acts_as_rated furl:A New Rails Plugin: acts_as_rated reddit:A New Rails Plugin: acts_as_rated fark:A New Rails Plugin: acts_as_rated blogmarks:A New Rails Plugin: acts_as_rated Y!:A New Rails Plugin: acts_as_rated smarking:A New Rails Plugin: acts_as_rated magnolia:A New Rails Plugin: acts_as_rated segnalo:A New Rails Plugin: acts_as_rated

Comments

  1. Tiago Serafim said 1 day later:

    Hi,

    This plugin is exactly what i´ve looking for... Thank you very much!

  2. Guy Naor said 2 days later:

    Thanks!

    If you find anything you think I should fix or add (or remove ;-) ), let me know!

  3. Starburger said 3 days later:

    That looks fine!

    Have you ever tried it on mySQL (although not fully tested)?

  4. Guy Naor said 3 days later:

    I didn't test it with MySQL, I just don't have it installed. I'm a 100% Postgres guy ;-)

    But I think MySQL should be fine. There is only one somewhat complex query used to get the rating average if not using added rating statistic fields or the external rating statistics table. This query uses AVG() and COALESCE() SQL functions. So if the database supports those, it should work.

    Guy.

  5. Tiago Serafim said 11 days later:

    Hi again,

    I tested the plugin with MySQL 4.1(comes within InstantRails) and I had some issues...to be more clear, 9 assertions failed.

    To solve them, I had to explicitly use ":decimal, :precision => 10, :scale => 2" instead of ":decimal" ... this should fix 8 of 9 assertions

    The remaining one was because the weirdness of mysql´s round function[1]... I don´t know how to fix this, but I think this won´t be a major problem... at least here, within my project.

    I also created another method called "rated_by_rater?" that I needed to use the "css rating star" thing. Here it is:

        def rated_by_rater? rater
    
          rating_class = acts_as_rated_options[:rating_class].constantize
          if !(acts_as_rated_options[:rater_class].constantize === rater)
            raise RateError, "The rater object must be the one used when defining acts_as_rated (or a descendent of it). other objects are not acceptable" 
          end
          raise RateError, "Rater must be a valid and existing object" if rater.nil? || rater.id.nil?
          raise RateError, 'Rater must be a valid rater' if !rating_class.column_names.include? "rater_id"
          r = ratings.find(:first, :conditions => ['rater_id = ?', rater.id])
    
          !r.nil?
    
        end
    

    I know that I should´ve wrote some tests for this, but since I´m a TDD/ruby newbie and it´s almost a copy of the first part of "unrate" method, it should work fine...

    Thanks again for this great plugin, cheers, Tiago

    [1] http://joseph.randomnetworks.com/archives/2004/11/08/mysqls-funny-math/

  6. Guy Naor said 12 days later:

    Tiago,

    Thanks for the fixes to make it work with MySQL.

    I also added the rated_by_rater? function, but change the name to rated_by?

    I also mede it a bit more efficient, by using count instead of find. The reason is that this way the data isn't loaded from the DB, which would have been a waste as we throw it away after checking it.

    I have some problems connecting to RubyForge at the moment. But I will upload it soon.

    Guy

  7. Tiago Serafim said 12 days later:

    Guy,

    Thanks for the reply!

    the new method´s name and the change you made are nice! I´ll update my code ASAP.

    Tiago

  8. Zach said 28 days later:

    YES! HAHAHA! I love it, exactly what I've needed!

Comments are disabled

Subscribe to The Dev Blog