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,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequel #:nodoc:
|
4
|
+
module Plugins #:nodoc:
|
5
|
+
# The BallotVoter plugin marks the model as containing objects that can
|
6
|
+
# vote. It creates a polymorphic one-to-many relationship from the
|
7
|
+
# BallotVoter model to Ballot::Sequel::Vote.
|
8
|
+
module BallotVoter
|
9
|
+
def self.apply(model) #:nodoc:
|
10
|
+
require 'ballot/sequel/vote'
|
11
|
+
require 'ballot/words'
|
12
|
+
require 'ballot/voter'
|
13
|
+
|
14
|
+
model.instance_eval do
|
15
|
+
# Create a polymorphic one-to-many relationship for voters. Based
|
16
|
+
# heavily on the one_to_many implementation from sequel_polymorphic,
|
17
|
+
# but customized to sequel-voting's needs.
|
18
|
+
one_to_many :ballots_by,
|
19
|
+
key: :voter_id,
|
20
|
+
reciprocal: :voter,
|
21
|
+
reciprocal_type: :one_to_many,
|
22
|
+
conditions: { voter_type: Ballot::Sequel.type_name(model) },
|
23
|
+
adder: ->(many_of_instance) {
|
24
|
+
many_of_instance.update(
|
25
|
+
voter_id: pk,
|
26
|
+
voter_type: Ballot::Sequel.type_name(model)
|
27
|
+
)
|
28
|
+
},
|
29
|
+
remover: ->(many_of_instance) { many_of_instance.delete },
|
30
|
+
clearer: -> { ballots_by_dataset.delete },
|
31
|
+
class: '::Ballot::Sequel::Vote'
|
32
|
+
|
33
|
+
include Ballot::Voter
|
34
|
+
extend Ballot::Voter::ClassMethods
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethods #:nodoc:
|
39
|
+
def cast_ballot_for(votable = nil, kwargs = {}) #:nodoc:
|
40
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
41
|
+
votable = Ballot::Sequel.votable_for(kwargs)
|
42
|
+
return false unless votable
|
43
|
+
votable.ballot_by(kwargs.merge(voter: self))
|
44
|
+
end
|
45
|
+
alias ballot_for cast_ballot_for
|
46
|
+
|
47
|
+
def remove_ballot_for(votable = nil, kwargs = {}) #:nodoc:
|
48
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
49
|
+
votable = Ballot::Sequel.votable_for(kwargs)
|
50
|
+
return false unless votable
|
51
|
+
votable.remove_ballot_by voter: self, scope: kwargs[:scope]
|
52
|
+
end
|
53
|
+
|
54
|
+
def cast_ballot_for?(votable = nil, kwargs = {}) #:nodoc:
|
55
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
56
|
+
votable_id, votable_type = Ballot::Sequel.votable_id_and_type_name_for(kwargs)
|
57
|
+
return false unless votable_id
|
58
|
+
|
59
|
+
cond = {
|
60
|
+
votable_id: votable_id,
|
61
|
+
votable_type: votable_type,
|
62
|
+
scope: kwargs[:scope]
|
63
|
+
}
|
64
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
65
|
+
|
66
|
+
find_ballots_by(cond).any?
|
67
|
+
end
|
68
|
+
alias ballot_for? cast_ballot_for?
|
69
|
+
|
70
|
+
def ballot_as_cast_for(votable = nil, kwargs = {}) #:nodoc:
|
71
|
+
kwargs = __ballot_voter_kwargs(votable, kwargs)
|
72
|
+
votable = Ballot::Sequel.votable_for(kwargs)
|
73
|
+
return nil unless votable
|
74
|
+
|
75
|
+
cond = {
|
76
|
+
votable_id: votable.id,
|
77
|
+
votable_type: Ballot::Sequel.type_name(votable),
|
78
|
+
scope: kwargs[:scope]
|
79
|
+
}
|
80
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
81
|
+
|
82
|
+
vote = find_ballots_by(cond).
|
83
|
+
order(Sequel.desc(:updated_at), Sequel.desc(:created_at)).
|
84
|
+
limit(1).first
|
85
|
+
vote && vote.vote
|
86
|
+
end
|
87
|
+
|
88
|
+
def ballots_for_class(klass, kwargs = {}) #:nodoc:
|
89
|
+
cond = {
|
90
|
+
votable_type: Ballot::Sequel.type_name(klass),
|
91
|
+
scope: kwargs[:scope]
|
92
|
+
}
|
93
|
+
cond[:vote] = Ballot::Words.truthy?(kwargs[:vote]) if kwargs.key?(:vote)
|
94
|
+
|
95
|
+
find_ballots_by(cond)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def find_ballots_by(*cond, &block)
|
101
|
+
ballots_by_dataset.where(*cond, &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
def __eager_ballot_votables(ds)
|
105
|
+
ballots = ds.naked.select(:votable_type, :votable_id).all
|
106
|
+
partitioned = ballots.group_by { |e| e[:votable_type] }
|
107
|
+
partitioned.each_value do |value|
|
108
|
+
value.map! { |e| e[:votable_id] }
|
109
|
+
end
|
110
|
+
partitioned.each_key do |key|
|
111
|
+
votables = self.class.send(:constantize, key).
|
112
|
+
where(id: partitioned[key]).
|
113
|
+
map { |v| [ v.id, v ] }
|
114
|
+
|
115
|
+
partitioned[key] = Hash[votables]
|
116
|
+
end
|
117
|
+
|
118
|
+
ballots.map { |ballot|
|
119
|
+
partitioned[ballot[:votable_type]][ballot[:votable_id]]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_config'
|
4
|
+
|
5
|
+
describe Ballot::ActiveRecord::Votable do
|
6
|
+
describe_votable_models [
|
7
|
+
ARVotable, ARVotableVoter, ARStiVotable, ARChildOfStiVotable,
|
8
|
+
ARVotableChildOfStiNotVotable, ARVotableCache
|
9
|
+
]
|
10
|
+
describe_non_votable_models [
|
11
|
+
ARVoter, ARNotVoter, ARNotVotable, ARStiNotVotable
|
12
|
+
]
|
13
|
+
describe_ballot_votable
|
14
|
+
describe_ballot_votable_sti_support
|
15
|
+
describe_cached_ballot_summary ARVotableCache
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_config'
|
4
|
+
|
5
|
+
describe Ballot::ActiveRecord::Voter do
|
6
|
+
describe_non_voter_models [
|
7
|
+
ARChildOfStiVotable, ARNotVotable, ARNotVoter, ARStiNotVotable,
|
8
|
+
ARStiVotable, ARVotable, ARVotableCache, ARVotableChildOfStiNotVotable
|
9
|
+
]
|
10
|
+
describe_voter_models [ ARVoter, ARVotableVoter ]
|
11
|
+
describe_ballot_voter
|
12
|
+
describe_ballot_voter_sti_votable_support
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_config'
|
4
|
+
|
5
|
+
describe 'RailsGenerator' do
|
6
|
+
describe 'for Active Record' do
|
7
|
+
let(:orm_name) { 'activerecord' }
|
8
|
+
let(:orm_version) { ActiveRecord::VERSION::STRING }
|
9
|
+
let(:rails_version) { orm_version }
|
10
|
+
|
11
|
+
test_ballot_install(
|
12
|
+
generate: %r{create\s+db/migrate/\d+_install_ballot_vote_migration},
|
13
|
+
migrate: %r{
|
14
|
+
InstallBallotVoteMigration.*
|
15
|
+
--\screate_table\(:ballot_votes(?:,\s{})?\).*
|
16
|
+
--\sadd_index\(:ballot_votes,\s\[:voter_id .*
|
17
|
+
--\sadd_index\(:ballot_votes,\s\[:votable_id
|
18
|
+
}mx,
|
19
|
+
contents: /InstallBallotVoteMigration < ActiveRecord::Migration/
|
20
|
+
)
|
21
|
+
|
22
|
+
test_ballot_summary_votable(
|
23
|
+
generate: %r{create\s+db/migrate/\d+_ballot_cache_for_votable},
|
24
|
+
migrate: /BallotCacheForVotable.*--\schange_table\(:votables(?:,\s{})?\)/mx,
|
25
|
+
contents: /BallotCacheForVotable < ActiveRecord::Migration/
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_config'
|
4
|
+
|
5
|
+
describe ARVotableVoter do
|
6
|
+
describe_ballot_voter do
|
7
|
+
let(:voter) { ARVotableVoter.create(name: 'I can vote!') }
|
8
|
+
let(:voter2) { ARVotableVoter.create(name: 'I, too, can vote!') }
|
9
|
+
let(:votable) { voter2 }
|
10
|
+
let(:votable2) { voter }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe_ballot_votable do
|
14
|
+
let(:voter) { ARVotableVoter.create(name: 'I can vote!') }
|
15
|
+
let(:voter2) { ARVotableVoter.create(name: 'I, too, can vote!') }
|
16
|
+
let(:votable) { voter2 }
|
17
|
+
let(:votable2) { voter }
|
18
|
+
end
|
19
|
+
end
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require File.expand_path('../boot', __FILE__)
|
3
|
+
|
4
|
+
require 'rails'
|
5
|
+
require 'active_model/railtie'
|
6
|
+
require 'active_record/railtie'
|
7
|
+
|
8
|
+
Bundler.require(*Rails.groups)
|
9
|
+
|
10
|
+
module RailsActiverecord
|
11
|
+
class Application < Rails::Application
|
12
|
+
if Rails::VERSION::MAJOR < 5
|
13
|
+
config.active_record.raise_in_transactional_callbacks = true
|
14
|
+
end
|
15
|
+
config.eager_load = false
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
development:
|
2
|
+
secret_key_base: 7dfa450af068027682b897d9cd0143c2bcc0b83001cd3b45088bfd1eb6fa999fe19aca0e535f7676977f5c168624877dcc21c41522af77ffee65ec5f59023937
|
3
|
+
|
4
|
+
test:
|
5
|
+
secret_key_base: 9f4ff20b6abd84c56c2a59805baaf0ae6db6802013cdefa07e5c73dc89ee8b126e84b34ff443da47e2c26af526dcd53692fef60d2c6ae62cfa9332282f1d7c8a
|
@@ -0,0 +1 @@
|
|
1
|
+
# frozen_string_literal: true
|
File without changes
|
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require File.expand_path('../boot', __FILE__)
|
3
|
+
|
4
|
+
require 'rails'
|
5
|
+
require 'active_model/railtie'
|
6
|
+
|
7
|
+
Bundler.require(*Rails.groups)
|
8
|
+
|
9
|
+
module RailsSequel
|
10
|
+
class Application < Rails::Application
|
11
|
+
config.eager_load = false
|
12
|
+
config.sequel.schema_dump = true
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
development:
|
2
|
+
secret_key_base: 7dfa450af068027682b897d9cd0143c2bcc0b83001cd3b45088bfd1eb6fa999fe19aca0e535f7676977f5c168624877dcc21c41522af77ffee65ec5f59023937
|
3
|
+
|
4
|
+
test:
|
5
|
+
secret_key_base: 9f4ff20b6abd84c56c2a59805baaf0ae6db6802013cdefa07e5c73dc89ee8b126e84b34ff443da47e2c26af526dcd53692fef60d2c6ae62cfa9332282f1d7c8a
|
@@ -0,0 +1 @@
|
|
1
|
+
# frozen_string_literal: true
|
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# -*- ruby encoding: utf-8 -*-
|
3
|
+
|
4
|
+
gem 'minitest'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/focus'
|
7
|
+
require 'minitest/moar'
|
8
|
+
require 'minitest/bisect'
|
9
|
+
require 'minitest-bonus-assertions'
|
10
|
+
require 'minitest/hooks/default'
|
11
|
+
|
12
|
+
require 'pathname'
|
13
|
+
|
14
|
+
Dir["#{__dir__}/support/**/*.rb"].sort.each do |f| require_relative f end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_config'
|
4
|
+
|
5
|
+
describe Sequel::Plugins::BallotVotable do
|
6
|
+
describe_votable_models [
|
7
|
+
SequelVotable, SequelVotableVoter, SequelStiVotable,
|
8
|
+
SequelChildOfStiVotable, SequelVotableChildOfStiNotVotable,
|
9
|
+
SequelVotableCache
|
10
|
+
]
|
11
|
+
describe_non_votable_models [
|
12
|
+
SequelVoter, SequelNotVoter, SequelNotVotable, SequelStiNotVotable
|
13
|
+
]
|
14
|
+
describe_ballot_votable
|
15
|
+
describe_ballot_votable_sti_support
|
16
|
+
describe_cached_ballot_summary SequelVotableCache
|
17
|
+
|
18
|
+
describe 'association methods' do
|
19
|
+
it '#add_ballots_for adds a new instance' do
|
20
|
+
vote = Ballot::Sequel::Vote.new(voter: voter)
|
21
|
+
refute_predicate vote, :valid?
|
22
|
+
votable.add_ballots_for(vote)
|
23
|
+
assert_predicate vote, :valid?
|
24
|
+
end
|
25
|
+
|
26
|
+
it '#remove_ballots_for destroys the instance' do
|
27
|
+
votable.ballot_by voter
|
28
|
+
assert_true Ballot::Sequel::Vote.any?
|
29
|
+
votable.remove_ballots_for(votable_dataset(votable).first)
|
30
|
+
assert_true Ballot::Sequel::Vote.none?
|
31
|
+
end
|
32
|
+
|
33
|
+
it '#remove_all_ballots_for destroys all instances' do
|
34
|
+
votable.ballot_by voter
|
35
|
+
votable2.ballot_by voter
|
36
|
+
assert_true Ballot::Sequel::Vote.any?
|
37
|
+
votable.remove_all_ballots_for
|
38
|
+
assert_false Ballot::Sequel::Vote.none?
|
39
|
+
assert_true Ballot::Sequel::Vote.where(
|
40
|
+
votable_id: votable.id,
|
41
|
+
votable_type: ballot_type_name(votable)
|
42
|
+
).none?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_config'
|
4
|
+
|
5
|
+
describe Sequel::Plugins::BallotVoter do
|
6
|
+
describe_non_voter_models [
|
7
|
+
SequelChildOfStiVotable, SequelNotVotable, SequelNotVoter,
|
8
|
+
SequelStiNotVotable, SequelStiVotable, SequelVotable, SequelVotableCache,
|
9
|
+
SequelVotableChildOfStiNotVotable
|
10
|
+
]
|
11
|
+
describe_voter_models [ SequelVoter, SequelVotableVoter ]
|
12
|
+
describe_ballot_voter
|
13
|
+
describe_ballot_voter_sti_votable_support
|
14
|
+
|
15
|
+
describe 'association methods' do
|
16
|
+
it '#add_ballots_by adds a new instance' do
|
17
|
+
vote = Ballot::Sequel::Vote.new(votable: votable)
|
18
|
+
refute_predicate vote, :valid?
|
19
|
+
voter.add_ballots_by(vote)
|
20
|
+
assert_predicate vote, :valid?
|
21
|
+
end
|
22
|
+
|
23
|
+
it '#remove_ballots_by destroys the instance' do
|
24
|
+
voter.ballot_for votable
|
25
|
+
assert_true Ballot::Sequel::Vote.any?
|
26
|
+
voter.remove_ballots_by(voter_dataset(voter).first)
|
27
|
+
assert_true Ballot::Sequel::Vote.none?
|
28
|
+
end
|
29
|
+
|
30
|
+
it '#remove_all_ballots_by destroys all instances' do
|
31
|
+
voter.ballot_for votable
|
32
|
+
voter2.ballot_for votable
|
33
|
+
assert_true Ballot::Sequel::Vote.any?
|
34
|
+
voter.remove_all_ballots_by
|
35
|
+
assert_false Ballot::Sequel::Vote.none?
|
36
|
+
assert_true Ballot::Sequel::Vote.where(
|
37
|
+
voter_id: voter.id,
|
38
|
+
voter_type: ballot_type_name(voter)
|
39
|
+
).none?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|