acts_as_votable 0.10.0 → 0.13.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/main.yml +44 -0
- data/.gitignore +10 -6
- data/.rubocop.yml +121 -0
- data/.ruby-version +1 -0
- data/Appraisals +23 -0
- data/Gemfile +3 -14
- data/{README.markdown → README.md} +87 -52
- data/Rakefile +6 -4
- data/acts_as_votable.gemspec +12 -7
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_5.gemfile +7 -0
- data/gemfiles/rails_5_1.gemfile +7 -0
- data/gemfiles/rails_5_2.gemfile +7 -0
- data/gemfiles/rails_6.gemfile +8 -0
- data/gemfiles/rails_6_1.gemfile +8 -0
- data/gemfiles/rails_6_rc1.gemfile +8 -0
- data/lib/acts_as_votable.rb +9 -9
- data/lib/acts_as_votable/cacheable.rb +174 -0
- data/lib/acts_as_votable/extenders/controller.rb +3 -4
- data/lib/acts_as_votable/extenders/votable.rb +17 -6
- data/lib/acts_as_votable/extenders/voter.rb +4 -6
- data/lib/acts_as_votable/helpers/words.rb +7 -10
- data/lib/acts_as_votable/version.rb +3 -1
- data/lib/acts_as_votable/votable.rb +74 -194
- data/lib/acts_as_votable/vote.rb +10 -12
- data/lib/acts_as_votable/voter.rb +55 -56
- data/lib/generators/acts_as_votable/migration/migration_generator.rb +25 -4
- data/lib/generators/acts_as_votable/migration/templates/active_record/{migration.rb → migration.erb} +1 -6
- data/spec/factories/votable.rb +6 -0
- data/spec/factories/votable_cache.rb +6 -0
- data/spec/factories/votable_cache_update_attributes.rb +6 -0
- data/spec/factories/votable_cache_update_columns.rb +6 -0
- data/spec/factories/votable_child_of_sti_not_votable.rb +6 -0
- data/spec/factories/votable_child_of_sti_votable.rb +6 -0
- data/spec/factories/votable_voter.rb +6 -0
- data/spec/factories/vote.rb +6 -0
- data/spec/factories/voter.rb +6 -0
- data/spec/generators/active_record_generator_spec.rb +13 -0
- data/spec/shared_example/votable_model.rb +542 -0
- data/spec/shared_example/voter_model.rb +280 -0
- data/spec/spec_helper.rb +28 -18
- data/spec/support/factory_bot.rb +9 -0
- data/spec/votable_spec.rb +10 -9
- data/spec/votable_voter_spec.rb +12 -12
- data/spec/voter_spec.rb +9 -10
- data/spec/words_spec.rb +9 -12
- metadata +116 -26
- data/.travis.yml +0 -25
- data/spec/shared_example/votable_model_spec.rb +0 -421
- data/spec/shared_example/voter_model_spec.rb +0 -279
data/Rakefile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler"
|
2
4
|
Bundler::GemHelper.install_tasks
|
3
5
|
|
4
|
-
require
|
6
|
+
require "rspec/core/rake_task"
|
5
7
|
|
6
8
|
desc "Run specs"
|
7
9
|
RSpec::Core::RakeTask.new(:spec)
|
8
10
|
|
9
|
-
desc
|
10
|
-
task :
|
11
|
+
desc "Default: run specs."
|
12
|
+
task default: :spec
|
data/acts_as_votable.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
$:.push File.expand_path("../lib", __FILE__)
|
3
5
|
require "acts_as_votable/version"
|
4
6
|
|
@@ -9,16 +11,19 @@ Gem::Specification.new do |s|
|
|
9
11
|
s.authors = ["Ryan"]
|
10
12
|
s.email = ["ryanto"]
|
11
13
|
s.homepage = "http://rubygems.org/gems/acts_as_votable"
|
12
|
-
s.summary =
|
13
|
-
s.description =
|
14
|
-
|
15
|
-
s.rubyforge_project = "acts_as_votable"
|
14
|
+
s.summary = "Rails gem to allowing records to be votable"
|
15
|
+
s.description = "Rails gem to allowing records to be votable"
|
16
|
+
s.license = "MIT"
|
16
17
|
|
17
18
|
s.files = `git ls-files`.split("\n")
|
18
19
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
20
21
|
s.require_paths = ["lib"]
|
21
22
|
|
22
|
-
s.add_development_dependency "rspec"
|
23
|
-
s.add_development_dependency "sqlite3",
|
23
|
+
s.add_development_dependency "rspec", "~> 3.6"
|
24
|
+
s.add_development_dependency "sqlite3", "~> 1.3.6"
|
25
|
+
s.add_development_dependency "rubocop", "~> 0.49.1"
|
26
|
+
s.add_development_dependency "simplecov", "~> 0.15.0"
|
27
|
+
s.add_development_dependency "appraisal", "~> 2.2"
|
28
|
+
s.add_development_dependency "factory_bot", "~> 4.8"
|
24
29
|
end
|
data/lib/acts_as_votable.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "active_support/inflector"
|
3
5
|
|
4
6
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
7
|
|
6
8
|
module ActsAsVotable
|
7
|
-
|
8
9
|
if defined?(ActiveRecord::Base)
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
10
|
+
require "acts_as_votable/extenders/votable"
|
11
|
+
require "acts_as_votable/extenders/voter"
|
12
|
+
require "acts_as_votable/vote"
|
12
13
|
ActiveRecord::Base.extend ActsAsVotable::Extenders::Votable
|
13
14
|
ActiveRecord::Base.extend ActsAsVotable::Extenders::Voter
|
14
15
|
end
|
15
|
-
|
16
16
|
end
|
17
17
|
|
18
|
-
require
|
18
|
+
require "acts_as_votable/extenders/controller"
|
19
19
|
ActiveSupport.on_load(:action_controller) do
|
20
20
|
include ActsAsVotable::Extenders::Controller
|
21
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActsAsVotable
|
4
|
+
module Cacheable
|
5
|
+
def scope_cache_field(field, vote_scope)
|
6
|
+
return field if vote_scope.nil?
|
7
|
+
|
8
|
+
case field
|
9
|
+
when :cached_votes_total=
|
10
|
+
"cached_scoped_#{vote_scope}_votes_total="
|
11
|
+
when :cached_votes_total
|
12
|
+
"cached_scoped_#{vote_scope}_votes_total"
|
13
|
+
when :cached_votes_up=
|
14
|
+
"cached_scoped_#{vote_scope}_votes_up="
|
15
|
+
when :cached_votes_up
|
16
|
+
"cached_scoped_#{vote_scope}_votes_up"
|
17
|
+
when :cached_votes_down=
|
18
|
+
"cached_scoped_#{vote_scope}_votes_down="
|
19
|
+
when :cached_votes_down
|
20
|
+
"cached_scoped_#{vote_scope}_votes_down"
|
21
|
+
when :cached_votes_score=
|
22
|
+
"cached_scoped_#{vote_scope}_votes_score="
|
23
|
+
when :cached_votes_score
|
24
|
+
"cached_scoped_#{vote_scope}_votes_score"
|
25
|
+
when :cached_weighted_total
|
26
|
+
"cached_weighted_#{vote_scope}_total"
|
27
|
+
when :cached_weighted_total=
|
28
|
+
"cached_weighted_#{vote_scope}_total="
|
29
|
+
when :cached_weighted_score
|
30
|
+
"cached_weighted_#{vote_scope}_score"
|
31
|
+
when :cached_weighted_score=
|
32
|
+
"cached_weighted_#{vote_scope}_score="
|
33
|
+
when :cached_weighted_average
|
34
|
+
"cached_weighted_#{vote_scope}_average"
|
35
|
+
when :cached_weighted_average=
|
36
|
+
"cached_weighted_#{vote_scope}_average="
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def update_cached_votes(vote_scope = nil)
|
41
|
+
updates = {}
|
42
|
+
|
43
|
+
if self.respond_to?(:cached_votes_total=)
|
44
|
+
updates[:cached_votes_total] = count_votes_total(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
if self.respond_to?(:cached_votes_up=)
|
48
|
+
updates[:cached_votes_up] = count_votes_up(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
if self.respond_to?(:cached_votes_down=)
|
52
|
+
updates[:cached_votes_down] = count_votes_down(true)
|
53
|
+
end
|
54
|
+
|
55
|
+
if self.respond_to?(:cached_votes_score=)
|
56
|
+
updates[:cached_votes_score] = (
|
57
|
+
(updates[:cached_votes_up] || count_votes_up(true)) -
|
58
|
+
(updates[:cached_votes_down] || count_votes_down(true))
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
if self.respond_to?(:cached_weighted_total=)
|
63
|
+
updates[:cached_weighted_total] = weighted_total(true)
|
64
|
+
end
|
65
|
+
|
66
|
+
if self.respond_to?(:cached_weighted_score=)
|
67
|
+
updates[:cached_weighted_score] = weighted_score(true)
|
68
|
+
end
|
69
|
+
|
70
|
+
if self.respond_to?(:cached_weighted_average=)
|
71
|
+
updates[:cached_weighted_average] = weighted_average(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
if vote_scope
|
75
|
+
if self.respond_to?(scope_cache_field :cached_votes_total=, vote_scope)
|
76
|
+
updates[scope_cache_field :cached_votes_total, vote_scope] = count_votes_total(true, vote_scope)
|
77
|
+
end
|
78
|
+
|
79
|
+
if self.respond_to?(scope_cache_field :cached_votes_up=, vote_scope)
|
80
|
+
updates[scope_cache_field :cached_votes_up, vote_scope] = count_votes_up(true, vote_scope)
|
81
|
+
end
|
82
|
+
|
83
|
+
if self.respond_to?(scope_cache_field :cached_votes_down=, vote_scope)
|
84
|
+
updates[scope_cache_field :cached_votes_down, vote_scope] = count_votes_down(true, vote_scope)
|
85
|
+
end
|
86
|
+
|
87
|
+
if self.respond_to?(scope_cache_field :cached_weighted_total=, vote_scope)
|
88
|
+
updates[scope_cache_field :cached_weighted_total, vote_scope] = weighted_total(true, vote_scope)
|
89
|
+
end
|
90
|
+
|
91
|
+
if self.respond_to?(scope_cache_field :cached_weighted_score=, vote_scope)
|
92
|
+
updates[scope_cache_field :cached_weighted_score, vote_scope] = weighted_score(true, vote_scope)
|
93
|
+
end
|
94
|
+
|
95
|
+
if self.respond_to?(scope_cache_field :cached_votes_score=, vote_scope)
|
96
|
+
updates[scope_cache_field :cached_votes_score, vote_scope] = (
|
97
|
+
(updates[scope_cache_field :cached_votes_up, vote_scope] || count_votes_up(true, vote_scope)) -
|
98
|
+
(updates[scope_cache_field :cached_votes_down, vote_scope] || count_votes_down(true, vote_scope))
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
if self.respond_to?(scope_cache_field :cached_weighted_average=, vote_scope)
|
103
|
+
updates[scope_cache_field :cached_weighted_average, vote_scope] = weighted_average(true, vote_scope)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
self.send(acts_as_votable_options[:cacheable_strategy], updates) if updates.size > 0
|
108
|
+
end
|
109
|
+
|
110
|
+
# counting
|
111
|
+
def count_votes_total(skip_cache = false, vote_scope = nil)
|
112
|
+
from_cache(skip_cache, :cached_votes_total, vote_scope) do
|
113
|
+
find_votes_for(scope_or_empty_hash(vote_scope)).count
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def count_votes_up(skip_cache = false, vote_scope = nil)
|
118
|
+
from_cache(skip_cache, :cached_votes_up, vote_scope) do
|
119
|
+
get_up_votes(vote_scope: vote_scope).count
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def count_votes_down(skip_cache = false, vote_scope = nil)
|
124
|
+
from_cache(skip_cache, :cached_votes_down, vote_scope) do
|
125
|
+
get_down_votes(vote_scope: vote_scope).count
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def count_votes_score(skip_cache = false, vote_scope = nil)
|
130
|
+
from_cache(skip_cache, :cached_votes_score, vote_scope) do
|
131
|
+
ups = count_votes_up(true, vote_scope)
|
132
|
+
downs = count_votes_down(true, vote_scope)
|
133
|
+
ups - downs
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def weighted_total(skip_cache = false, vote_scope = nil)
|
138
|
+
from_cache(skip_cache, :cached_weighted_total, vote_scope) do
|
139
|
+
ups = get_up_votes(vote_scope: vote_scope).sum(:vote_weight)
|
140
|
+
downs = get_down_votes(vote_scope: vote_scope).sum(:vote_weight)
|
141
|
+
ups + downs
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def weighted_score(skip_cache = false, vote_scope = nil)
|
146
|
+
from_cache(skip_cache, :cached_weighted_score, vote_scope) do
|
147
|
+
ups = get_up_votes(vote_scope: vote_scope).sum(:vote_weight)
|
148
|
+
downs = get_down_votes(vote_scope: vote_scope).sum(:vote_weight)
|
149
|
+
ups - downs
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def weighted_average(skip_cache = false, vote_scope = nil)
|
154
|
+
from_cache(skip_cache, :cached_weighted_average, vote_scope) do
|
155
|
+
count = count_votes_total(skip_cache, vote_scope).to_i
|
156
|
+
if count > 0
|
157
|
+
weighted_score(skip_cache, vote_scope).to_f / count
|
158
|
+
else
|
159
|
+
0.0
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def from_cache(skip_cache, cached_method, vote_scope)
|
167
|
+
if !skip_cache && respond_to?(scope_cache_field(cached_method, vote_scope))
|
168
|
+
send(scope_cache_field(cached_method, vote_scope))
|
169
|
+
else
|
170
|
+
yield
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -1,19 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsVotable
|
2
4
|
module Extenders
|
3
|
-
|
4
5
|
module Controller
|
5
|
-
|
6
6
|
def voter_params(params_object = params[:vote])
|
7
7
|
params_object.permit(:votable_id, :votable_type,
|
8
8
|
:voter_id, :voter_type,
|
9
9
|
:votable, :voter,
|
10
10
|
:vote_flag, :vote_scope)
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def votable_params(params_object = params[:vote])
|
14
14
|
params_object.permit(:vote_registered)
|
15
15
|
end
|
16
|
-
|
17
16
|
end
|
18
17
|
end
|
19
18
|
end
|
@@ -1,25 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsVotable
|
2
4
|
module Extenders
|
3
|
-
|
4
5
|
module Votable
|
6
|
+
ALLOWED_CACHEABLE_STRATEGIES = %i[update update_columns]
|
5
7
|
|
6
8
|
def votable?
|
7
9
|
false
|
8
10
|
end
|
9
11
|
|
10
|
-
def acts_as_votable
|
11
|
-
require
|
12
|
+
def acts_as_votable(args = {})
|
13
|
+
require "acts_as_votable/votable"
|
12
14
|
include ActsAsVotable::Votable
|
13
15
|
|
16
|
+
if args.key?(:cacheable_strategy) && !ALLOWED_CACHEABLE_STRATEGIES.include?(args[:cacheable_strategy])
|
17
|
+
raise ArgumentError, args[:cacheable_strategy]
|
18
|
+
end
|
19
|
+
|
20
|
+
define_method :acts_as_votable_options do
|
21
|
+
self.class.instance_variable_get("@acts_as_votable_options")
|
22
|
+
end
|
23
|
+
|
14
24
|
class_eval do
|
25
|
+
@acts_as_votable_options = {
|
26
|
+
cacheable_strategy: :update
|
27
|
+
}.merge(args)
|
28
|
+
|
15
29
|
def self.votable?
|
16
30
|
true
|
17
31
|
end
|
18
32
|
end
|
19
|
-
|
20
33
|
end
|
21
|
-
|
22
34
|
end
|
23
|
-
|
24
35
|
end
|
25
36
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActsAsVotable
|
2
4
|
module Extenders
|
3
|
-
|
4
5
|
module Voter
|
5
|
-
|
6
6
|
def voter?
|
7
7
|
false
|
8
8
|
end
|
9
9
|
|
10
|
-
def acts_as_voter(*
|
11
|
-
require
|
10
|
+
def acts_as_voter(*_args)
|
11
|
+
require "acts_as_votable/voter"
|
12
12
|
include ActsAsVotable::Voter
|
13
13
|
|
14
14
|
class_eval do
|
@@ -16,9 +16,7 @@ module ActsAsVotable
|
|
16
16
|
true
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
20
19
|
end
|
21
|
-
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
@@ -1,36 +1,33 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
module ActsAsVotable::Helpers
|
4
|
+
# this helper provides methods that help find what words are
|
4
5
|
# up votes and what words are down votes
|
5
6
|
#
|
6
|
-
# It can be called
|
7
|
+
# It can be called
|
7
8
|
#
|
8
9
|
# votable_object.votable_words.that_mean_true
|
9
10
|
#
|
10
11
|
module Words
|
11
|
-
|
12
12
|
def votable_words
|
13
13
|
VotableWords
|
14
14
|
end
|
15
|
-
|
16
15
|
end
|
17
16
|
|
18
17
|
class VotableWords
|
19
|
-
|
20
18
|
def self.that_mean_true
|
21
|
-
[
|
19
|
+
["up", "upvote", "like", "liked", "positive", "yes", "good", "agree", "true", 1, true]
|
22
20
|
end
|
23
21
|
|
24
22
|
def self.that_mean_false
|
25
|
-
[
|
23
|
+
["down", "downvote", "dislike", "disliked", "negative", "no", "bad", "disagree", "false", 0, false]
|
26
24
|
end
|
27
25
|
|
28
26
|
# check is word is a true or bad vote
|
29
27
|
# if the word is unknown, then it counts it as a true/good
|
30
28
|
# vote. this exists to allow all voting to be good by default
|
31
|
-
def self.meaning_of
|
29
|
+
def self.meaning_of(word)
|
32
30
|
!that_mean_false.include?(word)
|
33
31
|
end
|
34
|
-
|
35
32
|
end
|
36
33
|
end
|