Module: ActiveRecord::Acts::Rated::ClassMethods
- Defined in:
- lib/acts_as_rated.rb,
lib/acts_as_rated.rb
Instance Method Summary collapse
-
#acts_as_rated(options = {}) ⇒ Object
Make the model ratable.
-
#add_ratings_columns ⇒ Object
Create the needed columns for acts_as_rated.
-
#create_ratings_table(options = {}) ⇒ Object
Create the ratings table === Options hash: *
:with_rater- add the rated_id column *:table_name- use a table name other than ratings *:with_stats_table- create also a rating statistics table *:stats_table_name- the name of the rating statistics table. -
#drop_ratings_table(options = {}) ⇒ Object
Drop the ratings table.
-
#find_by_rating(value, precision = 10, round = true) ⇒ Object
Find by rating - pass either a specific value or a range and the precision to calculate with *
value- the value to look for or a range *precision- number of decimal digits to round to. -
#find_rated_by(rater) ⇒ Object
Find all ratings for a specific rater.
-
#generate_ratings_columns(table) ⇒ Object
Generate the ratings columns on a table, to be used when creating the table in a migration.
-
#remove_ratings_columns ⇒ Object
Remove the acts_as_rated specific columns added with add_ratings_columns To be used during migration, but can also be used in other places.
Instance Method Details
#acts_as_rated(options = {}) ⇒ Object
Make the model ratable. Can work both with and without a rater entity (defaults to User). The Rating model, holding the details of the ratings, will be created dynamically if it doesn’t exist.
-
Adds a
has_many :ratingsassociation to the model for easy retrieval of the detailed ratings. -
Adds a
has_many :ratersassociation to the onject, unless:no_rateris given as a configuration parameter. -
Adds a
has_many :ratingsassociations to the rater class. -
Adds a
has_one :rating_statisticassociation to the model, if:with_stats_table => trueis given as a configuration param.
Options
-
:rating_class- class of the model used for the ratings. Defaults to Rating. This class will be dynamically created if not already defined. If the class is predefined, it must have in it the following definitions:belongs_to :rated, :polymorphic => trueand if using a rater (which is true in most cases, see below) alsobelongs_to :rater, :class_name => 'User', :foreign_key => :rater_idreplace user with the rater class if needed. -
:rater_class- class of the model that creates the rating. Defaults to User This class will NOT be created, so it must be defined in the app. Another option will be to keep a session or IP based ID here to prevent multiple ratings from the same client. -
:no_rater- do not keep track of who created the rating. This will change the behaviour to one that just collects and averages ratings, but doesn’t keep track of who posted the rating. Useful in a public application that doesn’t care about individual votes -
:rating_range- A range object for the acceptable rating value range. Defaults to not limited -
:with_stats_table- Use a separate statistics table to hold the count/total/average rating of the rated object instead of adding the columns to the object’s table. This means we do not have to change the model table. It still holds a big performance advantage over using SQL to get the statistics -
:stats_class - Class of the statics table model. Only needed if <tt>:with_stats_tableis set to true. Default to RatingStat. This class need to have the following defined:belongs_to :rated, :polymorphic => true. And must make sure that it has the attributesrating_count,rating_totalandrating_avgand those must be initialized to 0 on new instances
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/acts_as_rated.rb', line 88 def acts_as_rated( = {}) # don't allow multiple calls return if self.included_modules.include?(ActiveRecord::Acts::Rated::RateMethods) send :include, ActiveRecord::Acts::Rated::RateMethods # Create the model for ratings if it doesn't yet exist = [:rating_class] || 'Rating' rater_class = [:rater_class] || 'User' stats_class = [:stats_class] || 'RatingStatistic' if [:with_stats_table] unless Object.const_defined?() Object.class_eval " class \#{rating_class} < ActiveRecord::Base\n belongs_to :rated, :polymorphic => true\n \#{options[:no_rater] ? '' : \"belongs_to :rater, :class_name => \#{rater_class}, :foreign_key => :rater_id\"}\n end\n EOV\n end\n\n unless stats_class.nil? || Object.const_defined?(stats_class)\n Object.class_eval <<-EOV\n class \#{stats_class} < ActiveRecord::Base\n belongs_to :rated, :polymorphic => true\n end\n EOV\n end\n \n raise RatedError, \":rating_range must be a range object\" unless options[:rating_range].nil? || (Range === options[:rating_range])\n write_inheritable_attribute( :acts_as_rated_options , \n { :rating_range => options[:rating_range], \n :rating_class => rating_class,\n :stats_class => stats_class,\n :rater_class => rater_class } )\n class_inheritable_reader :acts_as_rated_options\n \n class_eval do\n has_many :ratings, :as => :rated, :dependent => :delete_all, :class_name => rating_class.to_s\n has_many(:raters, :through => :ratings, :class_name => rater_class.to_s) unless options[:no_rater]\n has_one(:rating_statistic, :class_name => stats_class.to_s, :as => :rated, :dependent => :delete) unless stats_class.nil?\n\n before_create :init_rating_fields\n end\n\n # Add to the User (or whatever the rater is) a has_many ratings if working with a rater\n return if options[:no_rater] \n rater_as_class = rater_class.constantize\n return if rater_as_class.instance_methods.include?('find_in_ratings')\n rater_as_class.class_eval <<-EOS\n has_many :ratings, :foreign_key => :rater_id, :class_name => \#{rating_class.to_s}\n EOS\nend\n" |
#add_ratings_columns ⇒ Object
Create the needed columns for acts_as_rated. To be used during migration, but can also be used in other places.
303 304 305 306 307 308 309 310 |
# File 'lib/acts_as_rated.rb', line 303 def if !self.column_names.include? 'rating_count' self.connection.add_column table_name, :rating_count, :integer self.connection.add_column table_name, :rating_total, :decimal self.connection.add_column table_name, :rating_avg, :decimal, :precision => 10, :scale => 2 self.reset_column_information end end |
#create_ratings_table(options = {}) ⇒ Object
Create the ratings table
Options hash:
-
:with_rater- add the rated_id column -
:table_name- use a table name other than ratings -
:with_stats_table- create also a rating statistics table -
:stats_table_name- the name of the rating statistics table. Defaults to :rating_statistics
To be used during migration, but can also be used in other places
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/acts_as_rated.rb', line 328 def = {} with_rater = [:with_rater] != false name = [:table_name] || :ratings stats_table = [:stats_table_name] || :rating_statistics if [:with_stats_table] self.connection.create_table(name) do |t| t.column(:rater_id, :integer) unless !with_rater t.column :rated_id, :integer t.column :rated_type, :string t.column :rating, :decimal end self.connection.add_index(name, :rater_id) unless !with_rater self.connection.add_index name, [:rated_type, :rated_id] unless stats_table.nil? self.connection.create_table(stats_table) do |t| t.column :rated_id, :integer t.column :rated_type, :string t.column :rating_count, :integer t.column :rating_total, :decimal t.column :rating_avg, :decimal, :precision => 10, :scale => 2 end self.connection.add_index stats_table, [:rated_type, :rated_id] end end |
#drop_ratings_table(options = {}) ⇒ Object
Drop the ratings table.
Options hash:
-
:table_name- the name of the ratings table, defaults to ratings -
:with_stats_table- remove the special rating statistics as well -
:stats_table_name- the statistics table name. Defaults to :rating_statistics
To be used during migration, but can also be used in other places
362 363 364 365 366 367 |
# File 'lib/acts_as_rated.rb', line 362 def = {} name = [:table_name] || :ratings stats_table = [:stats_table_name] || :rating_statistics if [:with_stats_table] self.connection.drop_table name self.connection.drop_table stats_table unless stats_table.nil? end |
#find_by_rating(value, precision = 10, round = true) ⇒ Object
Find by rating - pass either a specific value or a range and the precision to calculate with
-
value- the value to look for or a range -
precision- number of decimal digits to round to. Default to 10. Use 0 for integer numbers comparision -
round_it- round the rating average before comparing?. Defaults to true. Passing false will result in a faster query
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/acts_as_rated.rb', line 386 def value, precision = 10, round = true = [:rating_class].constantize if column_names.include? "rating_avg" if Range === value conds = round ? [ 'round(rating_avg, ?) BETWEEN ? AND ?', precision.to_i, value.begin, value.end ] : [ 'rating_avg BETWEEN ? AND ?', value.begin, value.end ] else conds = round ? [ 'round(rating_avg, ?) = ?', precision.to_i, value ] : [ 'rating_avg = ?', value ] end find :all, :conditions => conds else if round base_sql = " select \#{table_name}.*,round(COALESCE(average,0), \#{precision.to_i}) AS rating_average from \#{table_name} left outer join\n (select avg(rating) as average, rated_id \n from \#{rating_class.table_name}\n where rated_type = '\#{class_name}' \n group by rated_id) as rated \n on rated_id=id \n EOS\n else\n base_sql = <<-EOS\n select \#{table_name}.*,COALESCE(average,0) AS rating_average from \#{table_name} left outer join\n (select avg(rating) as average, rated_id \n from \#{rating_class.table_name}\n where rated_type = '\#{class_name}' \n group by rated_id) as rated \n on rated_id=id \n EOS\n end\n if Range === value\n if round\n where_part = \" WHERE round(COALESCE(average,0), \#{precision.to_i}) BETWEEN \#{connection.quote(value.begin)} AND \#{connection.quote(value.end)}\"\n else\n where_part = \" WHERE COALESCE(average,0) BETWEEN \#{connection.quote(value.begin)} AND \#{connection.quote(value.end)}\"\n end\n else\n if round\n where_part = \" WHERE round(COALESCE(average,0), \#{precision.to_i}) = \#{connection.quote(value)}\"\n else\n where_part = \" WHERE COALESCE(average,0) = \#{connection.quote(value)}\"\n end\n end\n\n find_by_sql base_sql + where_part\n end\nend\n" |
#find_rated_by(rater) ⇒ Object
Find all ratings for a specific rater. Will raise an error if this acts_as_rated is without a rater.
371 372 373 374 375 376 377 378 379 |
# File 'lib/acts_as_rated.rb', line 371 def find_rated_by rater = [:rating_class].constantize 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" if !([:rater_class].constantize === rater) raise RateError, 'Cannot find_rated_by if not using a rater' if !.column_names.include? "rater_id" raise RateError, "Rater must be an existing object with an id" if rater.id.nil? rated_class = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s conds = [ 'rated_type = ? AND rater_id = ?', rated_class, rater.id ] [:rating_class].constantize.find(:all, :conditions => conds).collect {|r| r.rated_type.constantize.find_by_id r.rated.id } end |
#generate_ratings_columns(table) ⇒ Object
Generate the ratings columns on a table, to be used when creating the table in a migration. This is the preferred way to do in a migration that creates new tables as it will make it as part of the table creation, and not generate ALTER TABLE calls after the fact
295 296 297 298 299 |
# File 'lib/acts_as_rated.rb', line 295 def table table.column :rating_count, :integer table.column :rating_total, :decimal table.column :rating_avg, :decimal, :precision => 10, :scale => 2 end |
#remove_ratings_columns ⇒ Object
Remove the acts_as_rated specific columns added with add_ratings_columns To be used during migration, but can also be used in other places
314 315 316 317 318 319 |
# File 'lib/acts_as_rated.rb', line 314 def if self.column_names.include? 'rating_count' self.connection.remove_columns table_name, :rating_count, :rating_total, :rating_avg self.reset_column_information end end |