Module: Mongoid::Rating::Model::ClassMethods

Defined in:
lib/mongoid_rating/model.rb

Instance Method Summary collapse

Instance Method Details

#rateable(field, opt = {}) ⇒ Object

Make model rateable

rateable :overall, range: -5..5

Disable average completely rateable :design, range: -5..5, average: false rateable :quality, range: -5..5, average: true

float: whether to allow non-integer rates (default true)

Parameters:

  • options (Hash)

    a hash containings:



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/mongoid_rating/model.rb', line 19

def rateable(field, opt = {})
  options = {
    range: 1..5,
    rerate: true,
    counters: true,
    float: true,
  }.merge(opt)
  options[:no_rate] ||= options[:float] ? '0.0' : '0'
  options[:format]  ||= options[:float] ? '%.1f' : '%d'

  field = field.to_sym
  sfield = field.inspect

  # total rates count
  field "#{field}_count", type: Integer, default: 0

  # rates data
  embeds_many "#{field}_data", as: :rateable, class_name: 'Mongoid::Rating::Rate', counter_cache: true

  # sum of all rates to calculate average
  field "#{field}_sum".to_sym, type: options[:float] ? Float : Integer

  # average rate value
  avg = "#{field}_average".to_sym
  field avg, type: Float
  savg = avg.inspect

  class_eval <<-RUBY, __FILE__, __LINE__+1
    scope :#{field}_by, ->(rater) {
      where("#{field}_data.rater_id" => rater.id, "#{field}_data.rater_type" => rater.class.to_s)
    }
    scope :#{field}_in, ->(range) {
      where(#{savg}.gte => range.begin, #{savg}.lte => range.end)
    }

    scope :by_#{field}, -> {
      order_by([#{savg}, :desc])
    }
    scope :highest_#{field}, -> {
      where(#{savg}.ne => nil).order_by([#{savg}, :desc])
    }

    # return user's rate if rated otherwise formatted rate value
    # good for Raty JS plugin
    def fmt_#{field}(user = nil)
      if !user.nil? && #{field}_by?(user)
        #{options[:format].inspect} % #{field}_by(user)
      elsif #{field}.nil?
        #{options[:no_rate].class.name == 'String' ? options[:no_rate].inspect : options[:no_rate]}
      else
        #{options[:format].inspect} % #{field}
      end
    end

    def #{field}!(value, rater)
      if #{options[:float]}
       value = value.to_f
      else
       value = value.to_i 
      end
      unless (#{options[:range]}).include?(value)
        raise "bad vote value"
      end
      raise "can't rate" unless can_#{field}?(rater)
      un#{field}!(rater)
      atomically do
        inc("#{field}_count" => 1, "#{field}_sum" => value)
        #{field}_data.create!(rater: rater, value: value)
        set("#{field}_average" => calc_#{field}_avg)
      end
    end
    def calc_#{field}_avg
      if #{field}_count < 1
        nil
      else
        #{field}_sum.to_f / #{field}_count.to_f
      end
    end
    def un#{field}!(rater)
      r = #{field}_data.where(rater_id: rater.id).first
      if r.nil?
        # not rated before
      else
        atomically do
          inc("#{field}_count" => -1, "#{field}_sum" => -r.value)
          set("#{field}_average" => calc_#{field}_avg)
          r.destroy
        end
      end
    end
    alias_method :un#{field}, :un#{field}!
    
    def did_#{field}?(rater)
      !raw_#{field}_by(rater).nil?
    end

    def can_#{field}?(rater)
      if #{options[:rerate]}
        true
      else
        !did_#{field}?(rater)
      end
    end

    def raw_#{field}_by(rater)
      #{field}_data.select do |rate|
        rate[:rater_id] == rater.id && rate[:rater_type] == rater.class.name
      end.first
    end

    def #{field}(value=nil, rater=nil)
      if rater.nil? && value.nil?
        #{field}_count.nil? ? nil : #{field}_average
      else
        #{field}!(value, rater)
      end
    end

    def #{field}_values
      #{field}_data.map(&:value)
    end

    def #{field}_by(rater)
      rate = raw_#{field}_by(rater)
      if rate.nil?
        nil
      else
        rate.value
      end
    end
    def #{field}_by?(rater)
      !#{field}_by(rater).nil?
    end
  RUBY
end