ballot 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|