ballot 1.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 +7 -0
- data/Contributing.md +68 -0
- data/History.md +5 -0
- data/Licence.md +27 -0
- data/Manifest.txt +68 -0
- data/README.rdoc +264 -0
- data/Rakefile +71 -0
- data/bin/ballot_generator +9 -0
- data/lib/ballot.rb +25 -0
- data/lib/ballot/action_controller.rb +32 -0
- data/lib/ballot/active_record.rb +152 -0
- data/lib/ballot/active_record/votable.rb +145 -0
- data/lib/ballot/active_record/vote.rb +35 -0
- data/lib/ballot/active_record/voter.rb +99 -0
- data/lib/ballot/railtie.rb +19 -0
- data/lib/ballot/sequel.rb +170 -0
- data/lib/ballot/sequel/vote.rb +99 -0
- data/lib/ballot/votable.rb +445 -0
- data/lib/ballot/vote.rb +129 -0
- data/lib/ballot/voter.rb +320 -0
- data/lib/ballot/words.rb +32 -0
- data/lib/generators/ballot.rb +40 -0
- data/lib/generators/ballot/install/install_generator.rb +27 -0
- data/lib/generators/ballot/install/templates/active_record/migration.rb +19 -0
- data/lib/generators/ballot/install/templates/sequel/migration.rb +25 -0
- data/lib/generators/ballot/standalone.rb +89 -0
- data/lib/generators/ballot/standalone/support.rb +70 -0
- data/lib/generators/ballot/summary/summary_generator.rb +27 -0
- data/lib/generators/ballot/summary/templates/active_record/migration.rb +15 -0
- data/lib/generators/ballot/summary/templates/sequel/migration.rb +20 -0
- data/lib/sequel/plugins/ballot_votable.rb +180 -0
- data/lib/sequel/plugins/ballot_voter.rb +125 -0
- data/test/active_record/ballot_votable_test.rb +16 -0
- data/test/active_record/ballot_voter_test.rb +13 -0
- data/test/active_record/rails_generator_test.rb +28 -0
- data/test/active_record/votable_voter_test.rb +19 -0
- data/test/generators/rails-activerecord/Rakefile +2 -0
- data/test/generators/rails-activerecord/app/.keep +0 -0
- data/test/generators/rails-activerecord/bin/rails +5 -0
- data/test/generators/rails-activerecord/config/application.rb +17 -0
- data/test/generators/rails-activerecord/config/boot.rb +3 -0
- data/test/generators/rails-activerecord/config/database.yml +12 -0
- data/test/generators/rails-activerecord/config/environment.rb +3 -0
- data/test/generators/rails-activerecord/config/routes.rb +3 -0
- data/test/generators/rails-activerecord/config/secrets.yml +5 -0
- data/test/generators/rails-activerecord/db/seeds.rb +1 -0
- data/test/generators/rails-activerecord/log/.keep +0 -0
- data/test/generators/rails-sequel/Rakefile +2 -0
- data/test/generators/rails-sequel/app/.keep +0 -0
- data/test/generators/rails-sequel/bin/rails +5 -0
- data/test/generators/rails-sequel/config/application.rb +14 -0
- data/test/generators/rails-sequel/config/boot.rb +3 -0
- data/test/generators/rails-sequel/config/database.yml +12 -0
- data/test/generators/rails-sequel/config/environment.rb +3 -0
- data/test/generators/rails-sequel/config/routes.rb +3 -0
- data/test/generators/rails-sequel/config/secrets.yml +5 -0
- data/test/generators/rails-sequel/db/seeds.rb +1 -0
- data/test/generators/rails-sequel/log/.keep +0 -0
- data/test/minitest_config.rb +14 -0
- data/test/sequel/ballot_votable_test.rb +45 -0
- data/test/sequel/ballot_voter_test.rb +42 -0
- data/test/sequel/rails_generator_test.rb +25 -0
- data/test/sequel/votable_voter_test.rb +19 -0
- data/test/sequel/vote_test.rb +105 -0
- data/test/support/active_record_setup.rb +145 -0
- data/test/support/generators_setup.rb +129 -0
- data/test/support/sequel_setup.rb +164 -0
- data/test/support/shared_examples/votable_examples.rb +630 -0
- data/test/support/shared_examples/voter_examples.rb +600 -0
- metadata +333 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
git_path = File.expand_path('../../.git', __FILE__)
|
5
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) if File.exist?(git_path)
|
6
|
+
|
7
|
+
require 'generators/ballot/standalone'
|
8
|
+
|
9
|
+
exit Ballot::Generators::Standalone.run(ARGV).to_i
|
data/lib/ballot.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# Ballot provides a two-way polymorphic scoped voting mechanism for both
|
5
|
+
# ActiveRecord (4 or later) and Sequel (4 or later).
|
6
|
+
#
|
7
|
+
# - Two-way polymorphic: any model can be a voter or a votable.
|
8
|
+
# - Scoped: multiple votes can be recorded for a votable, under different
|
9
|
+
# scopes.
|
10
|
+
#
|
11
|
+
# The API for Ballot is consistent across both supported ORMs.
|
12
|
+
module Ballot
|
13
|
+
VERSION = '1.0' #:nodoc:
|
14
|
+
end
|
15
|
+
|
16
|
+
#:stopdoc:
|
17
|
+
require 'ballot/words'
|
18
|
+
require 'ballot/sequel' if defined?(::Sequel::Model)
|
19
|
+
if defined?(::Rails)
|
20
|
+
require 'ballot/railtie'
|
21
|
+
elsif defined?(::ActiveRecord::Base)
|
22
|
+
require 'ballot/active_record'
|
23
|
+
Ballot::ActiveRecord.inject!
|
24
|
+
end
|
25
|
+
#:startdoc:
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module Ballot
|
5
|
+
# Extensions to \ActionController to support the use of Ballot in a Rails
|
6
|
+
# application.
|
7
|
+
module ActionController
|
8
|
+
# Provide a consistent way to extract ballot parameters from request
|
9
|
+
# parameters. The +ballot+ defaults to <tt>params[:ballot]</tt>.
|
10
|
+
#
|
11
|
+
# It permits:
|
12
|
+
#
|
13
|
+
# * Votables to be described by type and id (+:votable_type+,
|
14
|
+
# +:votable_id+), a GlobalID locator (+:votable_gid+), or an object
|
15
|
+
# (+:votable+).
|
16
|
+
# * Voters to be described by type and id (+:voter_type+, +:voter_id+), a
|
17
|
+
# GlobalID locator (+:voter_gid+), or an object (+:voter+).
|
18
|
+
# * The recorded vote (+:vote+, a word or value that means true or false,
|
19
|
+
# see Ballot::Words).
|
20
|
+
# * The scope for the vote (+:scope+).
|
21
|
+
# * The weight for the vote (+:weight+).
|
22
|
+
def ballot_params(ballot = params[:ballot])
|
23
|
+
ballot.permit(
|
24
|
+
:votable_type, :votable_id, :votable_gid, :votable,
|
25
|
+
:voter_type, :voter_id, :voter_gid, :voter,
|
26
|
+
:vote,
|
27
|
+
:scope,
|
28
|
+
:weight
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module Ballot
|
5
|
+
# Extensions to \ActiveRecord to support Ballot.
|
6
|
+
module ActiveRecord
|
7
|
+
# Class method extensions to \ActiveRecord to support Ballot.
|
8
|
+
module ClassMethods
|
9
|
+
# ActiveRecord classes are not votable by default.
|
10
|
+
def ballot_votable?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
# ActiveRecord classes are not voters by default.
|
15
|
+
def ballot_voter?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
# The primary macro for marking an ActiveRecord class as votable.
|
20
|
+
def acts_as_ballot(*types)
|
21
|
+
types.each do |type|
|
22
|
+
case type.to_s
|
23
|
+
when 'votable'
|
24
|
+
require 'ballot/active_record/votable'
|
25
|
+
next if self < Ballot::ActiveRecord::Votable
|
26
|
+
include Ballot::ActiveRecord::Votable
|
27
|
+
when 'voter'
|
28
|
+
require 'ballot/active_record/voter'
|
29
|
+
next if self < Ballot::ActiveRecord::Voter
|
30
|
+
include Ballot::ActiveRecord::Voter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# A shorthand version of <tt>acts_as_ballot :votable</tt>.
|
36
|
+
def acts_as_ballot_votable
|
37
|
+
acts_as_ballot :votable
|
38
|
+
end
|
39
|
+
|
40
|
+
# A shorthand version of <tt>acts_as_ballot :voter</tt>.
|
41
|
+
def acts_as_ballot_voter
|
42
|
+
acts_as_ballot :voter
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Delegate the question of #votable? to the class.
|
47
|
+
def ballot_votable?
|
48
|
+
self.class.ballot_votable?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Delegate the question of #voter? to the class.
|
52
|
+
def ballot_voter?
|
53
|
+
self.class.ballot_voter?
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
# Put Ballot into ActiveRecord::Base.
|
58
|
+
def inject!
|
59
|
+
::ActiveRecord::Base.instance_eval do
|
60
|
+
return if self < Ballot::ActiveRecord
|
61
|
+
|
62
|
+
include ::Ballot::ActiveRecord
|
63
|
+
extend ::Ballot::ActiveRecord::ClassMethods
|
64
|
+
end
|
65
|
+
require 'ballot/active_record/vote'
|
66
|
+
end
|
67
|
+
|
68
|
+
# Respond with the canonical name for this model. Will be the root class
|
69
|
+
# if an STI model.
|
70
|
+
def type_name(model)
|
71
|
+
return model if model.kind_of?(String)
|
72
|
+
model = model.class if model.kind_of?(::ActiveRecord::Base)
|
73
|
+
model.base_class.name
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return a valid Votable object from the provided item or +nil+.
|
77
|
+
# Permitted values are a Votable, a hash with the key +:votable+, or a
|
78
|
+
# hash with the keys +:votable_type+ and +:votable_id+.
|
79
|
+
def votable_for(item)
|
80
|
+
votable =
|
81
|
+
if item.kind_of?(::Ballot::ActiveRecord::Votable)
|
82
|
+
item
|
83
|
+
elsif item.kind_of?(Hash)
|
84
|
+
if item[:votable]
|
85
|
+
item[:votable]
|
86
|
+
elsif item[:votable_type] && item[:votable_id]
|
87
|
+
__instance_of_model(item[:votable_type], item[:votable_id])
|
88
|
+
elsif item[:votable_gid]
|
89
|
+
fail 'GlobalID is not enabled.' unless defined?(::GlobalID)
|
90
|
+
|
91
|
+
# This should actually be GlobalID::Sequel::Locator when I
|
92
|
+
# get that ported.
|
93
|
+
GlobalID::Locator.locate(item[:votable_gid])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
votable if votable && votable.kind_of?(::ActiveRecord::Base) &&
|
98
|
+
votable.ballot_votable?
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return the id and canonical votable type name for the item, using
|
102
|
+
# #votable_for.
|
103
|
+
def votable_id_and_type_name_for(item)
|
104
|
+
__id_and_type_name(votable_for(item))
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return a valid Voter object from the provided item or +nil+. Permitted
|
108
|
+
# values are a Voter, a hash with the key +:voter+, or a hash with the
|
109
|
+
# keys +:voter_type+ and +:voter_id+.
|
110
|
+
def voter_for(item)
|
111
|
+
voter =
|
112
|
+
if item.kind_of?(::Ballot::ActiveRecord::Voter)
|
113
|
+
item
|
114
|
+
elsif item.kind_of?(Hash)
|
115
|
+
if item[:voter]
|
116
|
+
item[:voter]
|
117
|
+
elsif item[:voter_type] && item[:voter_id]
|
118
|
+
__instance_of_model(item[:voter_type], item[:voter_id])
|
119
|
+
elsif item[:voter_gid]
|
120
|
+
fail 'GlobalID is not enabled.' unless defined?(::GlobalID)
|
121
|
+
|
122
|
+
# This should actually be GlobalID::Sequel::Locator when I
|
123
|
+
# get that ported.
|
124
|
+
GlobalID::Locator.locate(item[:voter_gid])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
voter if voter && voter.kind_of?(::ActiveRecord::Base) &&
|
129
|
+
voter.ballot_voter?
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return the id and canonical voter type name for the item, using
|
133
|
+
# #voter_for.
|
134
|
+
def voter_id_and_type_name_for(item)
|
135
|
+
__id_and_type_name(voter_for(item))
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
# Return the id and canonical type name for the item.
|
141
|
+
def __id_and_type_name(item)
|
142
|
+
[ item.id, type_name(item) ] if item
|
143
|
+
end
|
144
|
+
|
145
|
+
def __instance_of_model(model, id)
|
146
|
+
model.constantize.find(id)
|
147
|
+
rescue
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module Ballot
|
5
|
+
module ActiveRecord
|
6
|
+
# The Ballot::ActiveRecord::Votable module is the ActiveRecord-specific
|
7
|
+
# implementation to enable Ballot::Votable for ActiveRecord.
|
8
|
+
module Votable
|
9
|
+
def self.included(model) #:nodoc:
|
10
|
+
require 'ballot/active_record/vote'
|
11
|
+
require 'ballot/words'
|
12
|
+
require 'ballot/votable'
|
13
|
+
|
14
|
+
model.class_eval do
|
15
|
+
# NOTE: This should only be done when Postgres JSON support is not
|
16
|
+
# enabled.
|
17
|
+
serialize :cached_ballot_summary, JSON
|
18
|
+
|
19
|
+
has_many :ballots_for,
|
20
|
+
class_name: '::Ballot::ActiveRecord::Vote',
|
21
|
+
as: :votable,
|
22
|
+
dependent: :destroy
|
23
|
+
|
24
|
+
include Ballot::Votable
|
25
|
+
extend Ballot::Votable::ClassMethods
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ballot_by(voter = nil, kwargs = {}) #:nodoc:
|
30
|
+
kwargs = { vote: true, scope: nil }.
|
31
|
+
merge(__ballot_votable_kwargs(voter, kwargs))
|
32
|
+
self.ballot_registered = false
|
33
|
+
|
34
|
+
voter_id, voter_type = Ballot::ActiveRecord.voter_id_and_type_name_for(kwargs)
|
35
|
+
return false unless voter_id
|
36
|
+
|
37
|
+
votes_ = find_ballots_for(
|
38
|
+
scope: kwargs[:scope],
|
39
|
+
voter_id: voter_id,
|
40
|
+
voter_type: voter_type
|
41
|
+
)
|
42
|
+
|
43
|
+
vote = if votes_.none? || kwargs[:duplicate]
|
44
|
+
Ballot::ActiveRecord::Vote.new(
|
45
|
+
votable: self,
|
46
|
+
voter_id: voter_id,
|
47
|
+
voter_type: voter_type,
|
48
|
+
scope: kwargs[:scope]
|
49
|
+
)
|
50
|
+
else
|
51
|
+
votes_.last
|
52
|
+
end
|
53
|
+
|
54
|
+
flag = Ballot::Words.truthy?(kwargs[:vote])
|
55
|
+
weight = kwargs[:weight] && kwargs[:weight].to_i || 1
|
56
|
+
|
57
|
+
return false if vote.vote == flag && vote.weight == weight
|
58
|
+
|
59
|
+
vote.vote = flag
|
60
|
+
vote.weight = weight
|
61
|
+
|
62
|
+
transaction do
|
63
|
+
if vote.save
|
64
|
+
self.ballot_registered = true
|
65
|
+
update_cached_votes kwargs[:scope]
|
66
|
+
true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def remove_ballot_by(voter = nil, kwargs = {}) #:nodoc:
|
72
|
+
kwargs = __ballot_votable_kwargs(voter, kwargs)
|
73
|
+
voter_id, voter_type = Ballot::ActiveRecord.voter_id_and_type_name_for(kwargs)
|
74
|
+
return false unless voter_id
|
75
|
+
|
76
|
+
votes_ = find_ballots_for(
|
77
|
+
scope: kwargs[:scope],
|
78
|
+
voter_id: voter_id,
|
79
|
+
voter_type: voter_type
|
80
|
+
)
|
81
|
+
|
82
|
+
return true if votes_.none?
|
83
|
+
|
84
|
+
transaction do
|
85
|
+
votes_.each(&:destroy)
|
86
|
+
update_cached_votes kwargs[:scope]
|
87
|
+
self.ballot_registered = ballots_for.any?
|
88
|
+
true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def ballot_by?(voter = nil, kwargs = {}) #:nodoc:
|
93
|
+
kwargs = __ballot_votable_kwargs(voter, kwargs)
|
94
|
+
voter_id, voter_type = Ballot::ActiveRecord.voter_id_and_type_name_for(kwargs)
|
95
|
+
return false unless voter_id
|
96
|
+
|
97
|
+
cond = {
|
98
|
+
voter_id: voter_id,
|
99
|
+
voter_type: voter_type,
|
100
|
+
scope: kwargs[:scope]
|
101
|
+
}
|
102
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
103
|
+
|
104
|
+
find_ballots_for(cond).any?
|
105
|
+
end
|
106
|
+
|
107
|
+
def ballots_by_class(klass, kwargs = {}) #:nodoc:
|
108
|
+
cond = {
|
109
|
+
voter_type: Ballot::ActiveRecord.type_name(klass),
|
110
|
+
scope: kwargs[:scope]
|
111
|
+
}
|
112
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
113
|
+
|
114
|
+
find_ballots_for(cond)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def caching_ballot_summary?
|
120
|
+
attribute_names.include?('cached_ballot_summary')
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_ballots_for(*cond, &block)
|
124
|
+
if cond.empty? && block.nil?
|
125
|
+
ballots_for
|
126
|
+
else
|
127
|
+
ballots_for.where(*cond, &block)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def update_cached_votes(scope = nil)
|
132
|
+
return false unless caching_ballot_summary?
|
133
|
+
|
134
|
+
lock!
|
135
|
+
summary = cached_ballot_summary.merge(calculate_summary(scope))
|
136
|
+
self.cached_ballot_summary = summary
|
137
|
+
save!
|
138
|
+
end
|
139
|
+
|
140
|
+
def __eager_ballot_voters(ds)
|
141
|
+
ds.includes(:voter).map(&:voter)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module Ballot
|
5
|
+
module ActiveRecord
|
6
|
+
# The ActiveRecord implementation of Ballot::Vote.
|
7
|
+
class Vote < ::ActiveRecord::Base
|
8
|
+
self.table_name = 'ballot_votes'
|
9
|
+
|
10
|
+
scope :up, -> { where(vote: true) }
|
11
|
+
scope :down, -> { where(vote: false) }
|
12
|
+
|
13
|
+
scope :for_type, ->(model_class) {
|
14
|
+
where(votable_type: Ballot::ActiveRecord.type_name(model_class))
|
15
|
+
}
|
16
|
+
scope :by_type, ->(model_class) {
|
17
|
+
where(voter_type: Ballot::ActiveRecord.type_name(model_class))
|
18
|
+
}
|
19
|
+
|
20
|
+
if defined?(::ProtectedAttributes)
|
21
|
+
attr_accessible :votable_id, :votable_type, :votable,
|
22
|
+
:voter_id, :voter_type, :voter,
|
23
|
+
:vote, :scope, :weight
|
24
|
+
end
|
25
|
+
|
26
|
+
belongs_to :votable, polymorphic: true
|
27
|
+
belongs_to :voter, polymorphic: true
|
28
|
+
|
29
|
+
validates :votable_type, presence: { allow_blank: false }
|
30
|
+
validates :votable_id, presence: { allow_blank: false }
|
31
|
+
validates :voter_type, presence: { allow_blank: false }
|
32
|
+
validates :voter_id, presence: { allow_blank: false }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module Ballot
|
5
|
+
module ActiveRecord
|
6
|
+
# The Ballot::ActiveRecord::Voter module is the ActiveRecord-specific
|
7
|
+
# implementation to enable Ballot::Voter for ActiveRecord.
|
8
|
+
# for full details.
|
9
|
+
module Voter
|
10
|
+
def self.included(model) # :nodoc:
|
11
|
+
require 'ballot/active_record/vote'
|
12
|
+
require 'ballot/words'
|
13
|
+
require 'ballot/voter'
|
14
|
+
|
15
|
+
model.class_eval do
|
16
|
+
has_many :ballots_by,
|
17
|
+
class_name: '::Ballot::ActiveRecord::Vote',
|
18
|
+
as: :voter,
|
19
|
+
dependent: :destroy
|
20
|
+
|
21
|
+
include Ballot::Voter
|
22
|
+
extend Ballot::Voter::ClassMethods
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def cast_ballot_for(votable = nil, kwargs = {}) #:nodoc:
|
27
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
28
|
+
votable = Ballot::ActiveRecord.votable_for(kwargs)
|
29
|
+
return false unless votable
|
30
|
+
votable.ballot_by(kwargs.merge(voter: self))
|
31
|
+
end
|
32
|
+
alias ballot_for cast_ballot_for
|
33
|
+
|
34
|
+
def remove_ballot_for(votable = nil, kwargs = {}) #:nodoc:
|
35
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
36
|
+
votable = Ballot::ActiveRecord.votable_for(kwargs)
|
37
|
+
return false unless votable
|
38
|
+
votable.remove_ballot_by voter: self, scope: kwargs[:scope]
|
39
|
+
end
|
40
|
+
|
41
|
+
def cast_ballot_for?(votable = nil, kwargs = {}) #:nodoc:
|
42
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
43
|
+
votable_id, votable_type =
|
44
|
+
Ballot::ActiveRecord.votable_id_and_type_name_for(kwargs)
|
45
|
+
return false unless votable_id
|
46
|
+
|
47
|
+
cond = {
|
48
|
+
votable_id: votable_id,
|
49
|
+
votable_type: votable_type,
|
50
|
+
scope: kwargs[:scope]
|
51
|
+
}
|
52
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
53
|
+
|
54
|
+
find_ballots_by(cond).any?
|
55
|
+
end
|
56
|
+
alias ballot_for? cast_ballot_for?
|
57
|
+
|
58
|
+
def ballot_as_cast_for(votable = nil, kwargs = {}) # :nodoc:
|
59
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
60
|
+
votable = Ballot::ActiveRecord.votable_for(kwargs)
|
61
|
+
return nil unless votable
|
62
|
+
|
63
|
+
cond = {
|
64
|
+
votable_id: votable.id,
|
65
|
+
votable_type: Ballot::ActiveRecord.type_name(votable),
|
66
|
+
scope: kwargs[:scope]
|
67
|
+
}
|
68
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
69
|
+
|
70
|
+
vote = find_ballots_by(cond).last
|
71
|
+
vote && vote.vote
|
72
|
+
end
|
73
|
+
|
74
|
+
def ballots_for_class(klass, kwargs = {}) #:nodoc:
|
75
|
+
cond = {
|
76
|
+
votable_type: Ballot::ActiveRecord.type_name(klass),
|
77
|
+
scope: kwargs[:scope]
|
78
|
+
}
|
79
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
80
|
+
|
81
|
+
find_ballots_by(cond)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def find_ballots_by(*cond, &block)
|
87
|
+
if cond.empty? && block.nil?
|
88
|
+
ballots_by
|
89
|
+
else
|
90
|
+
ballots_by.where(*cond, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def __eager_ballot_votables(ds)
|
95
|
+
ds.includes(:votable).map(&:votable)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|