Skip to content

Commit f493928

Browse files
denis1011101claude
andcommitted
fix: не считать голоса выбывших и не падать на удалённом блоке
Разбор правки плана вскрыл два случая, когда голосование расходится с реальностью. Голос участника оставался в подсчёте после того, как он ушёл из игры: знаменатель пересчитывался по живому составу, а бюллетень ушедшего решал за оставшихся. И блок, удалённый из библиотеки, пока правка ждала своей очереди, ронял её применение на внешнем ключе — правка навсегда застревала в голосовании. Теперь считаем только бюллетени тех, кто ещё в игре, а в план ставим уцелевшие блоки; если не уцелело ни одного — применять нечего. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014tEZQY5PDtiqnKibQkGtRk
1 parent bcaab35 commit f493928

2 files changed

Lines changed: 54 additions & 3 deletions

File tree

app/models/training_plan_proposal.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,13 @@ def reject!
6060
end
6161

6262
def apply!
63+
# Блок могли удалить из библиотеки, пока правка ждала своей очереди: в план
64+
# ставим то, что уцелело, а если не уцелело ничего — применять нечего.
65+
ids = blocks.map(&:id)
66+
return update!(status: "rejected") if ids.empty?
67+
6368
transaction do
64-
game.replace_training_plan!(training_block_ids)
69+
game.replace_training_plan!(ids)
6570
update!(status: "applied")
6671
end
6772
end
@@ -79,16 +84,17 @@ def vote!(voter, in_favor)
7984
return false unless voting? && voter_ids.include?(voter&.id)
8085

8186
training_plan_votes.find_or_initialize_by(user_id: voter.id).update!(in_favor: in_favor)
87+
@ballots = nil
8288
settle!
8389
true
8490
end
8591

8692
def votes_in_favor
87-
training_plan_votes.where(in_favor: true).count + 1
93+
ballots.count(&:in_favor?) + 1
8894
end
8995

9096
def votes_against
91-
training_plan_votes.where(in_favor: false).count
97+
ballots.count { |ballot| !ballot.in_favor? }
9298
end
9399

94100
def votes_expected
@@ -97,6 +103,12 @@ def votes_expected
97103

98104
private
99105

106+
# Голос того, кто уже вышел из игры, не считаем: иначе бюллетень ушедшего
107+
# решает за тех, кто на корт всё-таки выйдет.
108+
def ballots
109+
@ballots ||= training_plan_votes.where(user_id: voter_ids).to_a
110+
end
111+
100112
# Ждать последний голос незачем: как только одна сторона взяла большинство,
101113
# остальные бюллетени ничего не меняют.
102114
def settle!

test/models/training_plan_proposal_test.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,45 @@ class TrainingPlanProposalTest < ActiveSupport::TestCase
2828
assert_equal [ @warmup.id ], proposal.game.reload.training_block_ids
2929
end
3030

31+
# Ушедший из игры на корт не выйдет, и его голос не должен решать за тех, кто выйдет.
32+
test "the ballot of someone who left the game stops counting" do
33+
proposal = proposal_for(players: 3)
34+
voters = proposal.voter_ids.map { |id| User.find(id) }
35+
36+
proposal.approve!
37+
proposal.vote!(voters.first, true)
38+
assert_predicate proposal, :voting?
39+
40+
proposal.game.participations.find_by(user_id: voters.first.id).destroy
41+
proposal = TrainingPlanProposal.find(proposal.id)
42+
43+
proposal.vote!(voters.second, false)
44+
assert_predicate proposal, :voting?
45+
46+
proposal.vote!(voters.third, false)
47+
assert_predicate proposal, :rejected?
48+
end
49+
50+
test "a block deleted from the library drops out of the change" do
51+
proposal = proposal_for(players: 0)
52+
TrainingBlock.find(proposal.training_block_ids.first).destroy
53+
54+
proposal.approve!
55+
56+
assert_predicate proposal, :applied?
57+
assert_equal [ @warmup.id ], proposal.game.reload.training_block_ids
58+
end
59+
60+
test "a change loses its meaning together with the last block" do
61+
proposal = proposal_for(players: 0)
62+
TrainingBlock.where(id: proposal.training_block_ids).find_each(&:destroy)
63+
64+
proposal.approve!
65+
66+
assert_predicate proposal, :rejected?
67+
assert_empty proposal.game.reload.training_block_ids
68+
end
69+
3170
test "a change with nobody else to ask applies right after approval" do
3271
proposal = proposal_for(players: 0)
3372

0 commit comments

Comments
 (0)