diff --git a/README.md b/README.md index e9d049e..9d64276 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,13 @@ class Car < ActiveRecord::Base letsrate_rateable "speed", "engine", "price" end ``` +Set :can_change option if you want to allow users to change their ratings + +```ruby +class Car < ActiveRecord::Base + letsrate_rateable "speed", "engine", "price", :can_change => true +end +``` Then you need to add a call letsrate_rater in the user model. @@ -93,6 +100,12 @@ You can use the rating_for_user helper method to show the star rating for the us Speed : <%= rating_for_user @car, current_user, "speed", :star => 10 %> ``` +Use :readonly option if you need only show rating, without ability to set rating + +```erb +Speed : <%= rating_for @car, "speed", :readonly => true %> +Speed : <%= rating_for_user @car, current_user, "speed", :readonly => true %> +``` ## Feedback If you find bugs please open a ticket at [https://github.com/muratguzel/letsrate/issues](https://github.com/muratguzel/letsrate/issues) diff --git a/lib/letsrate/helpers.rb b/lib/letsrate/helpers.rb index c2130a0..5dd8a21 100644 --- a/lib/letsrate/helpers.rb +++ b/lib/letsrate/helpers.rb @@ -1,4 +1,8 @@ module Helpers + + # show rate widget + # @options[:readonly] show widget in read only mode + # @options[:disable_after_rate] disable widget after rate def rating_for(rateable_obj, dimension=nil, options={}) cached_average = rateable_obj.average dimension @@ -10,6 +14,8 @@ def rating_for(rateable_obj, dimension=nil, options={}) disable_after_rate = options[:disable_after_rate] || true readonly = !(current_user && rateable_obj.can_rate?(current_user, dimension)) + # now we can force readonly attribute + readonly = options[:readonly] || readonly unless readonly content_tag :div, '', "data-dimension" => dimension, :class => "star", "data-rating" => avg, "data-id" => rateable_obj.id, "data-classname" => rateable_obj.class.name, @@ -18,24 +24,27 @@ def rating_for(rateable_obj, dimension=nil, options={}) "data-star-count" => star end + # show rate widget for user + # @options[:readonly] show widget in read only mode + # @options[:disable_after_rate] disable widget after rate def rating_for_user(rateable_obj, rating_user, dimension = nil, options = {}) @object = rateable_obj @user = rating_user - @rating = Rate.find_by_rater_id_and_rateable_id_and_dimension(@user.id, @object.id, dimension) - stars = @rating ? @rating.stars : 0 - + @rating = Rate.find_by_rater_id_and_rateable_id_and_dimension(@user.id, @object.id, dimension) + stars = @rating ? @rating.stars : 0 + stars_count = options[:stars_count] || stars disable_after_rate = options[:disable_after_rate] || false - readonly=false + readonly= options[:readonly] || false if disable_after_rate - readonly = current_user.present? ? !rateable_obj.can_rate?(current_user.id, dimension) : true + readonly = current_user.present? ? !rateable_obj.can_rate?(current_user, dimension) : true end content_tag :div, '', "data-dimension" => dimension, :class => "star", "data-rating" => stars, "data-id" => rateable_obj.id, "data-classname" => rateable_obj.class.name, "data-disable-after-rate" => disable_after_rate, "data-readonly" => readonly, - "data-star-count" => stars + "data-star-count" => stars_count end end diff --git a/lib/letsrate/model.rb b/lib/letsrate/model.rb index 4ae1e5b..e8f9da0 100644 --- a/lib/letsrate/model.rb +++ b/lib/letsrate/model.rb @@ -69,6 +69,10 @@ def can_rate?(user, dimension=nil) user.ratings_given.where(dimension: dimension, rateable_id: id, rateable_type: self.class.name).size.zero? end + def has_rate?(user, dimension=nil) + user.ratings_given.where(dimension: dimension, rateable_id: id, rateable_type: self.class.name).size.nonzero? + end + def rates(dimension=nil) dimension ? self.send("#{dimension}_rates") : rates_without_dimension end @@ -84,24 +88,50 @@ def letsrate_rater end def letsrate_rateable(*dimensions) - has_many :rates_without_dimension, -> { where dimension: nil}, :as => :rateable, :class_name => "Rate", :dependent => :destroy + opts = dimensions.extract_options! + has_many :rates_without_dimension, :as => :rateable, :class_name => "Rate", :dependent => :destroy, :conditions => {:dimension => nil} has_many :raters_without_dimension, :through => :rates_without_dimension, :source => :rater - has_one :rate_average_without_dimension, -> { where dimension: nil}, :as => :cacheable, - :class_name => "RatingCache", :dependent => :destroy + has_one :rate_average_without_dimension, :as => :cacheable, :class_name => "RatingCache", + :dependent => :destroy, :conditions => {:dimension => nil} dimensions.each do |dimension| - has_many "#{dimension}_rates".to_sym, -> {where dimension: dimension.to_s}, - :dependent => :destroy, - :class_name => "Rate", - :as => :rateable + has_many "#{dimension}_rates".to_sym, :dependent => :destroy, + :conditions => {:dimension => dimension.to_s}, + :class_name => "Rate", + :as => :rateable has_many "#{dimension}_raters".to_sym, :through => "#{dimension}_rates", :source => :rater - has_one "#{dimension}_average".to_sym, -> { where dimension: dimension.to_s }, - :as => :cacheable, :class_name => "RatingCache", - :dependent => :destroy + has_one "#{dimension}_average", :as => :cacheable, :class_name => "RatingCache", + :dependent => :destroy, :conditions => {:dimension => dimension.to_s} + end + + if opts[:can_change] + class_eval <<-RUBY, __FILE__, __LINE__+1 + def rate(stars, user, dimension=nil, dirichlet_method=false) + dimension = nil if dimension.blank? + + if has_rate? user, dimension + user.ratings_given.where(dimension: dimension, rateable_id: id, rateable_type: self.class.name).first.update_attribute(:stars,stars) + else + rates(dimension).create! do |r| + r.stars = stars + r.rater = user + end + end + if dirichlet_method + update_rate_average_dirichlet(stars, dimension) + else + update_rate_average(stars, dimension) + end + end + + def can_rate?(user, dimension=nil) + true + end + RUBY end end end