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,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
module Ballot
|
5
|
+
# The namespace for Ballot generators for Rails.
|
6
|
+
module Generators
|
7
|
+
##
|
8
|
+
# :attr_reader:
|
9
|
+
# The ORM to use when generating the migrations.
|
10
|
+
def orm
|
11
|
+
if defined?(::Rails::Generators.options)
|
12
|
+
::Rails::Generators.options[:rails][:orm]
|
13
|
+
else
|
14
|
+
@orm || :active_record
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Set the ORM to use when generating the migrations. Ignored under Rails.
|
20
|
+
attr_writer :orm
|
21
|
+
|
22
|
+
# The source root for the generator templates.
|
23
|
+
def source_root
|
24
|
+
File.expand_path(
|
25
|
+
File.join('..', 'ballot', generator_name, 'templates', orm.to_s),
|
26
|
+
__FILE__
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Indicates whether the ORM is supported by these generators.
|
31
|
+
def orm_has_migration?
|
32
|
+
%i(active_record sequel).include? orm
|
33
|
+
end
|
34
|
+
|
35
|
+
# The next migration number.
|
36
|
+
def next_migration_number(_path)
|
37
|
+
Time.now.utc.strftime('%Y%m%d%H%M%S')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'generators/ballot'
|
4
|
+
|
5
|
+
##
|
6
|
+
module Ballot
|
7
|
+
module Generators
|
8
|
+
# The Rails generator to install the ballot_votes table.
|
9
|
+
class InstallGenerator < ::Rails::Generators::Base
|
10
|
+
include ::Rails::Generators::Migration if defined?(::Rails::Generators::Migration)
|
11
|
+
extend Ballot::Generators
|
12
|
+
|
13
|
+
desc <<-DESC
|
14
|
+
Description:
|
15
|
+
Create the ballot_votes migration.
|
16
|
+
DESC
|
17
|
+
|
18
|
+
def create_migration_file #:nodoc:
|
19
|
+
if self.class.orm_has_migration?
|
20
|
+
migration_template 'migration.rb', 'db/migrate/install_ballot_vote_migration.rb'
|
21
|
+
else
|
22
|
+
warn "Unsupported ORM #{self.class.orm}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class InstallBallotVoteMigration < ActiveRecord::Migration
|
4
|
+
def change
|
5
|
+
create_table :ballot_votes do |t|
|
6
|
+
t.references :votable, polymorphic: true
|
7
|
+
t.references :voter, polymorphic: true
|
8
|
+
|
9
|
+
t.boolean :vote, null: false, default: true
|
10
|
+
t.string :scope
|
11
|
+
t.integer :weight
|
12
|
+
|
13
|
+
t.timestamps null: false
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :ballot_votes, %i(voter_id voter_type scope)
|
17
|
+
add_index :ballot_votes, %i(votable_id votable_type scope)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Sequel.migration do
|
4
|
+
change do
|
5
|
+
create_table :ballot_votes do
|
6
|
+
primary_key :id
|
7
|
+
|
8
|
+
Integer :votable_id
|
9
|
+
String :votable_type
|
10
|
+
|
11
|
+
Integer :voter_id
|
12
|
+
String :voter_type
|
13
|
+
|
14
|
+
Boolean :vote, null: false, default: true
|
15
|
+
String :scope
|
16
|
+
Integer :weight
|
17
|
+
|
18
|
+
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
19
|
+
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
20
|
+
|
21
|
+
add_index %i(votable_type votable_id scope), name: :ballot_votes_votable_by_scope
|
22
|
+
add_index %i(voter_type voter_id scope), name: :ballot_votes_voter_by_scope
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
#:nocov:
|
6
|
+
|
7
|
+
##
|
8
|
+
module Ballot
|
9
|
+
module Generators
|
10
|
+
# The Ballot standalone generator.
|
11
|
+
class Standalone
|
12
|
+
# Create and run the standalone generator with the command-line arguments.
|
13
|
+
def self.run(argv)
|
14
|
+
new(argv).run
|
15
|
+
end
|
16
|
+
|
17
|
+
# The arguments provided to the Standalone generator.
|
18
|
+
attr_reader :argv
|
19
|
+
|
20
|
+
# Create the standalone generator.
|
21
|
+
def initialize(argv)
|
22
|
+
@argv = argv
|
23
|
+
end
|
24
|
+
|
25
|
+
# Run the standalone generator.
|
26
|
+
def run
|
27
|
+
op = OptionParser.new { |opts|
|
28
|
+
opts.banner = 'Usage: ballot_generator [options]'
|
29
|
+
|
30
|
+
opts.on('--orm ORM', %w(active_record sequel), 'Select the ORM') do |orm|
|
31
|
+
@orm = orm.to_sym
|
32
|
+
end
|
33
|
+
opts.on(
|
34
|
+
'--install', '-I',
|
35
|
+
Ballot::Generators::InstallGenerator.desc
|
36
|
+
) do
|
37
|
+
if generator && !generator.kind_of?(Ballot::Generators::InstallGenerator)
|
38
|
+
warn 'Can only select one generator to run.'
|
39
|
+
$stderr.puts opts
|
40
|
+
return 1
|
41
|
+
else
|
42
|
+
self.generator = Ballot::Generators::InstallGenerator.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
opts.on(
|
46
|
+
'--summary NAME', '-S',
|
47
|
+
Ballot::Generators::SummaryGenerator.desc
|
48
|
+
) do |name|
|
49
|
+
if generator && !generator.kind_of?(Ballot::Generators::SummaryGenerator)
|
50
|
+
warn 'Can only select one generator to run.'
|
51
|
+
$stderr.puts opts
|
52
|
+
return 1
|
53
|
+
else
|
54
|
+
self.generator = Ballot::Generators::SummaryGenerator.new(name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on('-h', '--help', 'Prints this help') do
|
59
|
+
$stdout.puts opts
|
60
|
+
return 1
|
61
|
+
end
|
62
|
+
}
|
63
|
+
op.parse!(argv)
|
64
|
+
|
65
|
+
if generator
|
66
|
+
generator.class.orm = @orm if @orm
|
67
|
+
generator.create_migration_file
|
68
|
+
else
|
69
|
+
$stdout.puts op
|
70
|
+
end
|
71
|
+
|
72
|
+
0
|
73
|
+
rescue => ex
|
74
|
+
$stderr.puts ex.message
|
75
|
+
return 1
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_accessor :generator
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
require_relative 'standalone/support'
|
86
|
+
require_relative 'install/install_generator'
|
87
|
+
require_relative 'summary/summary_generator'
|
88
|
+
|
89
|
+
#:nocov:
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
|
5
|
+
#:nocov:
|
6
|
+
#:stopdoc:
|
7
|
+
|
8
|
+
# Everything in this file is wrong, except that it does the right thing to
|
9
|
+
# simplify the overall implementation of standalone generators.
|
10
|
+
module Rails
|
11
|
+
module Generators
|
12
|
+
# Implement just enough of Generators to be useful.
|
13
|
+
class Base #:nodoc:
|
14
|
+
def self.desc(value = nil)
|
15
|
+
@desc = value.gsub(/^Description:\n\s+/, '').chomp if value
|
16
|
+
@desc
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.generator_name
|
20
|
+
name.split(/::/).last.sub(/Generator/, '').downcase
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :argv
|
24
|
+
attr_accessor :destination
|
25
|
+
|
26
|
+
def migration_template(source, target)
|
27
|
+
data = File.read(File.join(self.class.source_root, source))
|
28
|
+
data = ERB.new(data, 0, '%<>>-').result(binding)
|
29
|
+
|
30
|
+
path, file = File.split(target)
|
31
|
+
|
32
|
+
file = "#{self.class.next_migration_number(nil)}_#{file}"
|
33
|
+
|
34
|
+
File.write(File.join(destination || path, file), data)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class NamedBase < Base #:nodoc:
|
39
|
+
def initialize(name)
|
40
|
+
@name = prepare_name(name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def plural_table_name
|
44
|
+
"#{@name}s"
|
45
|
+
end
|
46
|
+
|
47
|
+
def class_name
|
48
|
+
@name.gsub(/^(.)|_(.)/) { (Regexp.last_match(1) || Regexp.last_match(2)).upcase }
|
49
|
+
end
|
50
|
+
|
51
|
+
def file_name
|
52
|
+
@name
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def prepare_name(name)
|
58
|
+
name.split(/::/).
|
59
|
+
last.
|
60
|
+
gsub(/([A-Z])/, '_\1').
|
61
|
+
downcase.
|
62
|
+
sub(/^_/, '').
|
63
|
+
sub(/s$/, '')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#:startdoc:
|
70
|
+
#:nocov:
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'generators/ballot'
|
4
|
+
|
5
|
+
##
|
6
|
+
module Ballot
|
7
|
+
module Generators
|
8
|
+
# The Rails generator to install the cache_ballot_summary column on the
|
9
|
+
# indicated table.
|
10
|
+
class SummaryGenerator < ::Rails::Generators::NamedBase
|
11
|
+
include ::Rails::Generators::Migration if defined?(::Rails::Generators::Migration)
|
12
|
+
extend Ballot::Generators
|
13
|
+
|
14
|
+
desc <<-DESC
|
15
|
+
Description:
|
16
|
+
Create a migration to add cached ballot summaries to the named table.
|
17
|
+
DESC
|
18
|
+
|
19
|
+
def create_migration_file #:nodoc:
|
20
|
+
if self.class.orm_has_migration?
|
21
|
+
migration_template 'migration.rb',
|
22
|
+
"db/migrate/ballot_cache_for_#{file_name}.rb"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BallotCacheFor<%= class_name %> < ActiveRecord::Migration
|
4
|
+
def change
|
5
|
+
change_table :'<%= plural_table_name %>' do |t|
|
6
|
+
if t.respond_to?(:jsonb)
|
7
|
+
t.jsonb :cached_ballot_summary, null: false, default: {}
|
8
|
+
elsif t.respond_to?(:json)
|
9
|
+
t.json :cached_ballot_summary, null: false, default: {}
|
10
|
+
else
|
11
|
+
t.text :cached_ballot_summary, null: false, default: '{}'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Sequel.migration do
|
4
|
+
change do
|
5
|
+
types = %i(jsonb json String)
|
6
|
+
column_options = {
|
7
|
+
null: false,
|
8
|
+
default: '{}'
|
9
|
+
}
|
10
|
+
|
11
|
+
alter_table :'<%= plural_table_name %>' do
|
12
|
+
begin
|
13
|
+
type = types.shift
|
14
|
+
add_column :cached_ballot_summary, type, column_options
|
15
|
+
rescue
|
16
|
+
types.empty? && raise || retry
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequel # :nodoc:
|
4
|
+
module Plugins # :nodoc:
|
5
|
+
# The votable plugin marks the model as containing objects that can be
|
6
|
+
# voted on. It creates a polymorphic one-to-many relationship from the
|
7
|
+
# Votable model to Ballot::Sequel::Vote.
|
8
|
+
#
|
9
|
+
# This may be used with single_table_inheritance, but should be loaded
|
10
|
+
# *after* single_table_inheritance has been called. It has not been tested
|
11
|
+
# with class_table_inheritance.
|
12
|
+
#
|
13
|
+
# This plug-in causes Ballot::Votable to be included into the affected
|
14
|
+
# model, and Ballot::Votable::ClassMethods to be extended onto the affected
|
15
|
+
# model.
|
16
|
+
module BallotVotable
|
17
|
+
def self.apply(model) # :nodoc:
|
18
|
+
require 'ballot/sequel/vote'
|
19
|
+
require 'ballot/words'
|
20
|
+
require 'ballot/votable'
|
21
|
+
|
22
|
+
model.instance_eval do
|
23
|
+
if columns.include?(:cached_ballot_summary)
|
24
|
+
plugin :serialization, :json, :cached_ballot_summary
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a polymorphic one-to-many relationship for votables. Based
|
28
|
+
# heavily on the one_to_many implementation from sequel_polymorphic,
|
29
|
+
# but customized to sequel-voting's needs.
|
30
|
+
one_to_many :ballots_for,
|
31
|
+
key: :votable_id,
|
32
|
+
reciprocal: :votable,
|
33
|
+
reciprocal_type: :one_to_many,
|
34
|
+
conditions: { votable_type: Ballot::Sequel.type_name(model) },
|
35
|
+
adder: ->(many_of_instance) {
|
36
|
+
many_of_instance.update(
|
37
|
+
votable_id: pk,
|
38
|
+
votable_type: Ballot::Sequel.type_name(model)
|
39
|
+
)
|
40
|
+
},
|
41
|
+
remover: ->(many_of_instance) { many_of_instance.delete },
|
42
|
+
clearer: -> { ballots_for_dataset.delete },
|
43
|
+
class: '::Ballot::Sequel::Vote'
|
44
|
+
|
45
|
+
include Ballot::Votable
|
46
|
+
extend Ballot::Votable::ClassMethods
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module InstanceMethods #:nodoc:
|
51
|
+
def ballot_by(voter = nil, kwargs = {}) #:nodoc:
|
52
|
+
kwargs = { vote: true, scope: nil }.
|
53
|
+
merge(__ballot_votable_kwargs(voter, kwargs))
|
54
|
+
self.ballot_registered = false
|
55
|
+
|
56
|
+
voter_id, voter_type = Ballot::Sequel.voter_id_and_type_name_for(kwargs)
|
57
|
+
return false unless voter_id
|
58
|
+
|
59
|
+
votes_ = find_ballots_for(
|
60
|
+
scope: kwargs[:scope],
|
61
|
+
voter_id: voter_id,
|
62
|
+
voter_type: voter_type
|
63
|
+
)
|
64
|
+
|
65
|
+
vote = if votes_.none? || kwargs[:duplicate]
|
66
|
+
Ballot::Sequel::Vote.new(
|
67
|
+
votable_id: id,
|
68
|
+
votable_type: Ballot::Sequel.type_name(model),
|
69
|
+
voter_id: voter_id,
|
70
|
+
voter_type: voter_type,
|
71
|
+
scope: kwargs[:scope]
|
72
|
+
)
|
73
|
+
else
|
74
|
+
votes_.last
|
75
|
+
end
|
76
|
+
|
77
|
+
flag = Ballot::Words.truthy?(kwargs[:vote])
|
78
|
+
weight = kwargs[:weight] && kwargs[:weight].to_i || 1
|
79
|
+
|
80
|
+
return false if vote.vote == flag && vote.weight == weight
|
81
|
+
|
82
|
+
vote.vote = flag
|
83
|
+
vote.weight = weight
|
84
|
+
|
85
|
+
model.db.transaction do
|
86
|
+
if vote.save
|
87
|
+
self.ballot_registered = true
|
88
|
+
update_cached_votes kwargs[:scope]
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def remove_ballot_by(voter = nil, kwargs = {}) # :nodoc:
|
95
|
+
kwargs = __ballot_votable_kwargs(voter, kwargs)
|
96
|
+
voter_id, voter_type = Ballot::Sequel.voter_id_and_type_name_for(kwargs)
|
97
|
+
return false unless voter_id
|
98
|
+
|
99
|
+
votes_ = find_ballots_for(
|
100
|
+
scope: kwargs[:scope],
|
101
|
+
voter_id: voter_id,
|
102
|
+
voter_type: voter_type
|
103
|
+
)
|
104
|
+
|
105
|
+
return true if votes_.none?
|
106
|
+
|
107
|
+
model.db.transaction do
|
108
|
+
votes_.each(&:destroy)
|
109
|
+
update_cached_votes kwargs[:scope]
|
110
|
+
self.ballot_registered = ballots_for_dataset.any?
|
111
|
+
true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def ballot_by?(voter = nil, kwargs = {}) #:nodoc:
|
116
|
+
kwargs = __ballot_votable_kwargs(voter, kwargs)
|
117
|
+
voter_id, voter_type = Ballot::Sequel.voter_id_and_type_name_for(kwargs)
|
118
|
+
return false unless voter_id
|
119
|
+
|
120
|
+
cond = {
|
121
|
+
voter_id: voter_id,
|
122
|
+
voter_type: voter_type,
|
123
|
+
scope: kwargs[:scope]
|
124
|
+
}
|
125
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
126
|
+
|
127
|
+
find_ballots_for(cond).any?
|
128
|
+
end
|
129
|
+
|
130
|
+
def ballots_by_class(klass, kwargs = {}) #:nodoc:
|
131
|
+
cond = {
|
132
|
+
voter_type: Ballot::Sequel.type_name(klass),
|
133
|
+
scope: kwargs[:scope]
|
134
|
+
}
|
135
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
136
|
+
|
137
|
+
find_ballots_for(cond)
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def caching_ballot_summary?
|
143
|
+
model.columns.include?(:cached_ballot_summary)
|
144
|
+
end
|
145
|
+
|
146
|
+
def find_ballots_for(*cond, &block)
|
147
|
+
ballots_for_dataset.where(*cond, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
def update_cached_votes(scope = nil)
|
151
|
+
return false unless caching_ballot_summary?
|
152
|
+
|
153
|
+
lock!
|
154
|
+
summary = cached_ballot_summary.merge(calculate_summary(scope))
|
155
|
+
self.cached_ballot_summary = summary
|
156
|
+
save_changes
|
157
|
+
end
|
158
|
+
|
159
|
+
def __eager_ballot_voters(ds)
|
160
|
+
ballots = ds.naked.select(:voter_type, :voter_id).all
|
161
|
+
partitioned = ballots.group_by { |e| e[:voter_type] }
|
162
|
+
partitioned.each_value do |value|
|
163
|
+
value.map! { |e| e[:voter_id] }
|
164
|
+
end
|
165
|
+
partitioned.each_key do |key|
|
166
|
+
voters = self.class.send(:constantize, key).
|
167
|
+
where(id: partitioned[key]).
|
168
|
+
map { |v| [ v.id, v ] }
|
169
|
+
|
170
|
+
partitioned[key] = Hash[voters]
|
171
|
+
end
|
172
|
+
|
173
|
+
ballots.map { |ballot|
|
174
|
+
partitioned[ballot[:voter_type]][ballot[:voter_id]]
|
175
|
+
}
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|