redmine_crm 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  module RedmineCrm
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.10"
3
3
  end
@@ -0,0 +1,334 @@
1
+ require 'redmine_crm/helpers/vote_helper'
2
+
3
+ module RedmineCrm
4
+ module ActsAsVotable
5
+ module Votable
6
+
7
+ include ActsAsVotable::Helpers::Words
8
+
9
+ def self.included base
10
+
11
+ # allow the user to define these himself
12
+ aliases = {
13
+
14
+ :vote_up => [
15
+ :up_by, :upvote_by, :like_by, :liked_by,
16
+ :up_from, :upvote_from, :upvote_by, :like_from, :liked_from, :vote_from
17
+ ],
18
+
19
+ :vote_down => [
20
+ :down_by, :downvote_by, :dislike_by, :disliked_by,
21
+ :down_from, :downvote_from, :downvote_by, :dislike_by, :disliked_by
22
+ ],
23
+
24
+ :get_up_votes => [
25
+ :get_true_votes, :get_ups, :get_upvotes, :get_likes, :get_positives, :get_for_votes,
26
+ ],
27
+
28
+ :get_down_votes => [
29
+ :get_false_votes, :get_downs, :get_downvotes, :get_dislikes, :get_negatives
30
+ ],
31
+ :unvote_by => [
32
+ :unvote_up, :unvote_down, :unliked_by, :undisliked_by
33
+ ]
34
+ }
35
+
36
+ base.class_eval do
37
+ has_many :votes_for, :class_name => 'RedmineCrm::ActsAsVotable::Vote', :as => :votable, :dependent => :destroy do
38
+ def voters
39
+ includes(:voter).map(&:voter)
40
+ end
41
+ end
42
+
43
+ aliases.each do |method, links|
44
+ links.each do |new_method|
45
+ alias_method(new_method, method)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ attr_accessor :vote_registered
53
+
54
+ def vote_registered?
55
+ return self.vote_registered
56
+ end
57
+
58
+ def default_conditions
59
+ {
60
+ :votable_id => self.id,
61
+ :votable_type => self.class.base_class.name.to_s
62
+ }
63
+ end
64
+
65
+ # voting
66
+ def vote_by args = {}
67
+
68
+ options = {
69
+ :vote => true,
70
+ :vote_scope => nil
71
+ }.merge(args)
72
+
73
+ self.vote_registered = false
74
+
75
+ if options[:voter].nil?
76
+ return false
77
+ end
78
+
79
+ # find the vote
80
+ _votes_ = find_votes_for({
81
+ :voter_id => options[:voter].id,
82
+ :vote_scope => options[:vote_scope],
83
+ :voter_type => options[:voter].class.base_class.name
84
+ })
85
+
86
+ if _votes_.count == 0 or options[:duplicate]
87
+ # this voter has never voted
88
+ vote = RedmineCrm::ActsAsVotable::Vote.new(
89
+ :votable => self,
90
+ :voter => options[:voter],
91
+ :vote_scope => options[:vote_scope]
92
+ )
93
+ else
94
+ # this voter is potentially changing his vote
95
+ vote = _votes_.last
96
+ end
97
+
98
+ last_update = vote.updated_at
99
+
100
+ vote.vote_flag = votable_words.meaning_of(options[:vote])
101
+
102
+ #Allowing for a vote_weight to be associated with every vote. Could change with every voter object
103
+ vote.vote_weight = (options[:vote_weight].to_i if options[:vote_weight].present?) || 1
104
+
105
+ if vote.save
106
+ self.vote_registered = true if last_update != vote.updated_at
107
+ update_cached_votes options[:vote_scope]
108
+ return true
109
+ else
110
+ self.vote_registered = false
111
+ return false
112
+ end
113
+
114
+ end
115
+
116
+ def unvote args = {}
117
+ return false if args[:voter].nil?
118
+ _votes_ = find_votes_for(:voter_id => args[:voter].id, :vote_scope => args[:vote_scope], :voter_type => args[:voter].class.base_class.name)
119
+
120
+ return true if _votes_.size == 0
121
+ _votes_.each(&:destroy)
122
+ update_cached_votes args[:vote_scope]
123
+ self.vote_registered = false if votes_for.count == 0
124
+ return true
125
+ end
126
+
127
+ def vote_up voter, options={}
128
+ self.vote_by :voter => voter, :vote => true, :vote_scope => options[:vote_scope], :vote_weight => options[:vote_weight]
129
+ end
130
+
131
+ def vote_down voter, options={}
132
+ self.vote_by :voter => voter, :vote => false, :vote_scope => options[:vote_scope], :vote_weight => options[:vote_weight]
133
+ end
134
+
135
+ def unvote_by voter, options = {}
136
+ self.unvote :voter => voter, :vote_scope => options[:vote_scope] #Does not need vote_weight since the votes_for are anyway getting destroyed
137
+ end
138
+
139
+ def scope_cache_field field, vote_scope
140
+ return field if vote_scope.nil?
141
+
142
+ case field
143
+ when :cached_votes_total=
144
+ "cached_scoped_#{vote_scope}_votes_total="
145
+ when :cached_votes_total
146
+ "cached_scoped_#{vote_scope}_votes_total"
147
+ when :cached_votes_up=
148
+ "cached_scoped_#{vote_scope}_votes_up="
149
+ when :cached_votes_up
150
+ "cached_scoped_#{vote_scope}_votes_up"
151
+ when :cached_votes_down=
152
+ "cached_scoped_#{vote_scope}_votes_down="
153
+ when :cached_votes_down
154
+ "cached_scoped_#{vote_scope}_votes_down"
155
+ when :cached_votes_score=
156
+ "cached_scoped_#{vote_scope}_votes_score="
157
+ when :cached_votes_score
158
+ "cached_scoped_#{vote_scope}_votes_score"
159
+ when :cached_weighted_total
160
+ "cached_weighted_#{vote_scope}_total"
161
+ when :cached_weighted_total=
162
+ "cached_weighted_#{vote_scope}_total="
163
+ when :cached_weighted_score
164
+ "cached_weighted_#{vote_scope}_score"
165
+ when :cached_weighted_score=
166
+ "cached_weighted_#{vote_scope}_score="
167
+ when :cached_weighted_average
168
+ "cached_weighted_#{vote_scope}_average"
169
+ when :cached_weighted_average=
170
+ "cached_weighted_#{vote_scope}_average="
171
+ end
172
+ end
173
+
174
+ # caching
175
+ def update_cached_votes vote_scope = nil
176
+
177
+ updates = {}
178
+
179
+ if self.respond_to?(:cached_votes_total=)
180
+ updates[:cached_votes_total] = count_votes_total(true)
181
+ end
182
+
183
+ if self.respond_to?(:cached_votes_up=)
184
+ updates[:cached_votes_up] = count_votes_up(true)
185
+ end
186
+
187
+ if self.respond_to?(:cached_votes_down=)
188
+ updates[:cached_votes_down] = count_votes_down(true)
189
+ end
190
+
191
+ if self.respond_to?(:cached_votes_score=)
192
+ updates[:cached_votes_score] = (
193
+ (updates[:cached_votes_up] || count_votes_up(true)) -
194
+ (updates[:cached_votes_down] || count_votes_down(true))
195
+ )
196
+ end
197
+
198
+ if self.respond_to?(:cached_weighted_total=)
199
+ updates[:cached_weighted_total] = weighted_total(true)
200
+ end
201
+
202
+ if self.respond_to?(:cached_weighted_score=)
203
+ updates[:cached_weighted_score] = weighted_score(true)
204
+ end
205
+
206
+ if self.respond_to?(:cached_weighted_average=)
207
+ updates[:cached_weighted_average] = weighted_average(true)
208
+ end
209
+
210
+ if vote_scope
211
+ if self.respond_to?(scope_cache_field :cached_votes_total=, vote_scope)
212
+ updates[scope_cache_field :cached_votes_total, vote_scope] = count_votes_total(true, vote_scope)
213
+ end
214
+
215
+ if self.respond_to?(scope_cache_field :cached_votes_up=, vote_scope)
216
+ updates[scope_cache_field :cached_votes_up, vote_scope] = count_votes_up(true, vote_scope)
217
+ end
218
+
219
+ if self.respond_to?(scope_cache_field :cached_votes_down=, vote_scope)
220
+ updates[scope_cache_field :cached_votes_down, vote_scope] = count_votes_down(true, vote_scope)
221
+ end
222
+
223
+ if self.respond_to?(scope_cache_field :cached_weighted_total=, vote_scope)
224
+ updates[scope_cache_field :cached_weighted_total, vote_scope] = weighted_total(true, vote_scope)
225
+ end
226
+
227
+ if self.respond_to?(scope_cache_field :cached_weighted_score=, vote_scope)
228
+ updates[scope_cache_field :cached_weighted_score, vote_scope] = weighted_score(true, vote_scope)
229
+ end
230
+
231
+ if self.respond_to?(scope_cache_field :cached_votes_score=, vote_scope)
232
+ updates[scope_cache_field :cached_votes_score, vote_scope] = (
233
+ (updates[scope_cache_field :cached_votes_up, vote_scope] || count_votes_up(true, vote_scope)) -
234
+ (updates[scope_cache_field :cached_votes_down, vote_scope] || count_votes_down(true, vote_scope))
235
+ )
236
+ end
237
+
238
+ if self.respond_to?(scope_cache_field :cached_weighted_average=, vote_scope)
239
+ updates[scope_cache_field :cached_weighted_average, vote_scope] = weighted_average(true, vote_scope)
240
+ end
241
+ end
242
+
243
+ if (::ActiveRecord::VERSION::MAJOR == 3) && (::ActiveRecord::VERSION::MINOR != 0)
244
+ self.update_attributes(updates, :without_protection => true) if updates.size > 0
245
+ else
246
+ self.update_attributes(updates) if updates.size > 0
247
+ end
248
+
249
+ end
250
+
251
+
252
+ # results
253
+ def find_votes_for extra_conditions = {}
254
+ votes_for.where(extra_conditions)
255
+ end
256
+
257
+ def get_up_votes options={}
258
+ vote_scope_hash = scope_or_empty_hash(options[:vote_scope])
259
+ find_votes_for({:vote_flag => true}.merge(vote_scope_hash))
260
+ end
261
+
262
+ def get_down_votes options={}
263
+ vote_scope_hash = scope_or_empty_hash(options[:vote_scope])
264
+ find_votes_for({:vote_flag => false}.merge(vote_scope_hash))
265
+ end
266
+
267
+
268
+ # counting
269
+ def count_votes_total skip_cache = false, vote_scope = nil
270
+ if !skip_cache && self.respond_to?(scope_cache_field :cached_votes_total, vote_scope)
271
+ return self.send(scope_cache_field :cached_votes_total, vote_scope)
272
+ end
273
+ find_votes_for(scope_or_empty_hash(vote_scope)).count
274
+ end
275
+
276
+ def count_votes_up skip_cache = false, vote_scope = nil
277
+ if !skip_cache && self.respond_to?(scope_cache_field :cached_votes_up, vote_scope)
278
+ return self.send(scope_cache_field :cached_votes_up, vote_scope)
279
+ end
280
+ get_up_votes(:vote_scope => vote_scope).count
281
+ end
282
+
283
+ def count_votes_down skip_cache = false, vote_scope = nil
284
+ if !skip_cache && self.respond_to?(scope_cache_field :cached_votes_down, vote_scope)
285
+ return self.send(scope_cache_field :cached_votes_down, vote_scope)
286
+ end
287
+ get_down_votes(:vote_scope => vote_scope).count
288
+ end
289
+
290
+ def weighted_total skip_cache = false, vote_scope = nil
291
+ if !skip_cache && self.respond_to?(scope_cache_field :cached_weighted_total, vote_scope)
292
+ return self.send(scope_cache_field :cached_weighted_total, vote_scope)
293
+ end
294
+ ups = get_up_votes(:vote_scope => vote_scope).sum(:vote_weight)
295
+ downs = get_down_votes(:vote_scope => vote_scope).sum(:vote_weight)
296
+ ups + downs
297
+ end
298
+
299
+ def weighted_score skip_cache = false, vote_scope = nil
300
+ if !skip_cache && self.respond_to?(scope_cache_field :cached_weighted_score, vote_scope)
301
+ return self.send(scope_cache_field :cached_weighted_score, vote_scope)
302
+ end
303
+ ups = get_up_votes(:vote_scope => vote_scope).sum(:vote_weight)
304
+ downs = get_down_votes(:vote_scope => vote_scope).sum(:vote_weight)
305
+ ups - downs
306
+ end
307
+
308
+ def weighted_average skip_cache = false, vote_scope = nil
309
+ if !skip_cache && self.respond_to?(scope_cache_field :cached_weighted_average, vote_scope)
310
+ return self.send(scope_cache_field :cached_weighted_average, vote_scope)
311
+ end
312
+
313
+ count = count_votes_total(skip_cache, vote_scope).to_i
314
+ if count > 0
315
+ weighted_score(skip_cache, vote_scope).to_f / count
316
+ else
317
+ 0.0
318
+ end
319
+ end
320
+
321
+ # voters
322
+ def voted_on_by? voter
323
+ votes = find_votes_for :voter_id => voter.id, :voter_type => voter.class.base_class.name
324
+ votes.count > 0
325
+ end
326
+
327
+ private
328
+
329
+ def scope_or_empty_hash(vote_scope)
330
+ vote_scope ? { :vote_scope => vote_scope } : {}
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,30 @@
1
+ require 'redmine_crm/helpers/vote_helper'
2
+
3
+ module RedmineCrm
4
+ module ActsAsVotable
5
+ class Vote < ActiveRecord::Base
6
+
7
+ include Helpers::Words
8
+
9
+ if defined?(ProtectedAttributes) || ::ActiveRecord::VERSION::MAJOR < 4
10
+ attr_accessible :votable_id, :votable_type,
11
+ :voter_id, :voter_type,
12
+ :votable, :voter,
13
+ :vote_flag, :vote_scope
14
+ end
15
+
16
+ belongs_to :votable, :polymorphic => true
17
+ belongs_to :voter, :polymorphic => true
18
+
19
+ scope :up, lambda{ where(:vote_flag => true) }
20
+ scope :down, lambda{ where(:vote_flag => false) }
21
+ scope :for_type, lambda{ |klass| where(:votable_type => klass) }
22
+ scope :by_type, lambda{ |klass| where(:voter_type => klass) }
23
+
24
+ validates_presence_of :votable_id
25
+ validates_presence_of :voter_id
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,136 @@
1
+ module RedmineCrm
2
+ module ActsAsVotable
3
+ module Voter
4
+
5
+ def self.included(base)
6
+
7
+ # allow user to define these
8
+ aliases = {
9
+ :vote_up_for => [:likes, :upvotes, :up_votes],
10
+ :vote_down_for => [:dislikes, :downvotes, :down_votes],
11
+ :unvote_for => [:unlike, :undislike],
12
+ :voted_on? => [:voted_for?],
13
+ :voted_up_on? => [:voted_up_for?, :liked?],
14
+ :voted_down_on? => [:voted_down_for?, :disliked?],
15
+ :voted_as_when_voting_on => [:voted_as_when_voted_on, :voted_as_when_voting_for, :voted_as_when_voted_for],
16
+ :find_up_voted_items => [:find_liked_items],
17
+ :find_down_voted_items => [:find_disliked_items]
18
+ }
19
+
20
+ base.class_eval do
21
+
22
+ has_many :votes, :class_name => 'RedmineCrm::ActsAsVotable::Vote', :as => :voter, :dependent => :destroy do
23
+ def votables
24
+ includes(:votable).map(&:votable)
25
+ end
26
+ end
27
+
28
+ aliases.each do |method, links|
29
+ links.each do |new_method|
30
+ alias_method(new_method, method)
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ # voting
39
+ def vote args
40
+ args[:votable].vote_by args.merge({:voter => self})
41
+ end
42
+
43
+ def vote_up_for model=nil, args={}
44
+ vote :votable => model, :vote_scope => args[:vote_scope], :vote => true
45
+ end
46
+
47
+ def vote_down_for model=nil, args={}
48
+ vote :votable => model, :vote_scope => args[:vote_scope], :vote => false
49
+ end
50
+
51
+ def unvote_for model, args={}
52
+ model.unvote :voter => self, :vote_scope => args[:vote_scope]
53
+ end
54
+
55
+ # results
56
+ def voted_on? votable, args={}
57
+ votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
58
+ :vote_scope => args[:vote_scope])
59
+ votes.size > 0
60
+ end
61
+
62
+ def voted_up_on? votable, args={}
63
+ votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
64
+ :vote_scope => args[:vote_scope], :vote_flag => true)
65
+ votes.size > 0
66
+ end
67
+
68
+ def voted_down_on? votable, args={}
69
+ votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
70
+ :vote_scope => args[:vote_scope], :vote_flag => false)
71
+ votes.size > 0
72
+ end
73
+
74
+ def voted_as_when_voting_on votable, args={}
75
+ vote = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
76
+ :vote_scope => args[:vote_scope]).select(:vote_flag).last
77
+ return nil unless vote
78
+ return vote.vote_flag
79
+ end
80
+
81
+ def find_votes extra_conditions = {}
82
+ votes.where(extra_conditions)
83
+ end
84
+
85
+ def find_up_votes args={}
86
+ find_votes :vote_flag => true, :vote_scope => args[:vote_scope]
87
+ end
88
+
89
+ def find_down_votes args={}
90
+ find_votes :vote_flag => false, :vote_scope => args[:vote_scope]
91
+ end
92
+
93
+ def find_votes_for_class klass, extra_conditions = {}
94
+ find_votes extra_conditions.merge({:votable_type => klass.name})
95
+ end
96
+
97
+ def find_up_votes_for_class klass, args={}
98
+ find_votes_for_class klass, :vote_flag => true, :vote_scope => args[:vote_scope]
99
+ end
100
+
101
+ def find_down_votes_for_class klass, args={}
102
+ find_votes_for_class klass, :vote_flag => false, :vote_scope => args[:vote_scope]
103
+ end
104
+
105
+ # Including polymporphic relations for eager loading
106
+ def include_objects
107
+ ActsAsVotable::Vote.includes(:votable)
108
+ end
109
+
110
+ def find_voted_items extra_conditions = {}
111
+ options = extra_conditions.merge :voter_id => id, :voter_type => self.class.base_class.name
112
+ include_objects.where(options).collect(&:votable)
113
+ end
114
+
115
+ def find_up_voted_items extra_conditions = {}
116
+ find_voted_items extra_conditions.merge(:vote_flag => true)
117
+ end
118
+
119
+ def find_down_voted_items extra_conditions = {}
120
+ find_voted_items extra_conditions.merge(:vote_flag => false)
121
+ end
122
+
123
+ def get_voted klass, extra_conditions = {}
124
+ klass.joins(:votes_for).merge find_votes(extra_conditions)
125
+ end
126
+
127
+ def get_up_voted klass
128
+ klass.joins(:votes_for).merge find_up_votes
129
+ end
130
+
131
+ def get_down_voted klass
132
+ klass.joins(:votes_for).merge find_down_votes
133
+ end
134
+ end
135
+ end
136
+ end