Commit 1a4f43bd0212213bb58d2daf91f1f9b2f6455531
1 parent
1927f764
Exists in
master
and in
1 other branch
Prevent voters from voting twice on the same appearance
Showing
5 changed files
with
83 additions
and
4 deletions
Show diff stats
app/models/skip.rb
app/models/visitor.rb
... | ... | @@ -25,7 +25,13 @@ class Visitor < ActiveRecord::Base |
25 | 25 | if options[:appearance_lookup] |
26 | 26 | @appearance = prompt.appearances.find_by_lookup(options.delete(:appearance_lookup)) |
27 | 27 | return nil unless @appearance # don't allow people to fake appearance lookups |
28 | - options.merge!(:appearance => @appearance) | |
28 | + | |
29 | + if @appearance.answered? | |
30 | + options.merge!(:valid_record => false) | |
31 | + options.merge!(:validity_information => "Appearance #{@appearance.id} already answered") | |
32 | + else | |
33 | + options.merge!(:appearance => @appearance) | |
34 | + end | |
29 | 35 | end |
30 | 36 | |
31 | 37 | choice = prompt.choices[ordinality] #we need to guarantee that the choices are in the right order (by position) |
... | ... | @@ -45,7 +51,12 @@ class Visitor < ActiveRecord::Base |
45 | 51 | if options[:appearance_lookup] |
46 | 52 | @appearance = prompt.appearances.find_by_lookup(options.delete(:appearance_lookup)) |
47 | 53 | return nil unless @appearance |
48 | - options.merge!(:appearance => @appearance) | |
54 | + if @appearance.answered? | |
55 | + options.merge!(:valid_record => false) | |
56 | + options.merge!(:validity_information => "Appearance #{@appearance.id} already answered") | |
57 | + else | |
58 | + options.merge!(:appearance => @appearance) | |
59 | + end | |
49 | 60 | end |
50 | 61 | |
51 | 62 | options.merge!(:question_id => prompt.question_id, :prompt_id => prompt.id, :skipper_id => self.id) | ... | ... |
app/models/vote.rb
... | ... | @@ -18,7 +18,7 @@ class Vote < ActiveRecord::Base |
18 | 18 | named_scope :with_voter_ids, lambda { |*args| {:conditions => {:voter_id=> args.first }} } |
19 | 19 | named_scope :active, :include => :choice, :conditions => { 'choices.active' => true } |
20 | 20 | |
21 | - default_scope :conditions => {:valid_record => true} | |
21 | + default_scope :conditions => "#{table_name}.valid_record = 1" | |
22 | 22 | |
23 | 23 | serialize :tracking |
24 | 24 | ... | ... |
spec/models/visitor_spec.rb
... | ... | @@ -118,6 +118,46 @@ describe Visitor do |
118 | 118 | s.should be_nil |
119 | 119 | Skip.count.should == skip_count |
120 | 120 | end |
121 | + | |
122 | + it "should mark a skip as invalid if submitted with an already answered appearance" do | |
123 | + @appearance = @aoi_clone.record_appearance(@visitor, @prompt) | |
124 | + @optional_skip_params = {:appearance_lookup => @appearance.lookup} | |
125 | + allparams = @required_skip_params.merge(@optional_skip_params) | |
126 | + | |
127 | + valid_skip = @visitor.skip!(allparams) | |
128 | + @visitor.skips.count.should == 1 | |
129 | + @appearance.reload.answerable.should == valid_skip | |
130 | + | |
131 | + # we need to reset because vote_for deletes keys from the params | |
132 | + allparams = @required_skip_params.merge(@optional_skip_params) | |
133 | + invalid_skip = @visitor.skip!(allparams) | |
134 | + invalid_skip.should_not be_nil | |
135 | + | |
136 | + invalid_skip.valid_record.should be_false | |
137 | + invalid_skip.validity_information.should == "Appearance #{@appearance.id} already answered" | |
138 | + @appearance.reload.answerable.should == valid_skip | |
139 | + @visitor.reload.skips.count.should == 1 | |
140 | + end | |
141 | + | |
142 | + it "should mark a vote as invalid if submitted with an already answered appearance" do | |
143 | + @appearance = @aoi_clone.record_appearance(@visitor, @prompt) | |
144 | + @optional_vote_params = {:appearance_lookup => @appearance.lookup} | |
145 | + allparams = @required_vote_params.merge(@optional_vote_params) | |
146 | + | |
147 | + valid_vote = @visitor.vote_for!(allparams) | |
148 | + @visitor.votes.count.should == 1 | |
149 | + @appearance.reload.answerable.should == valid_vote | |
150 | + | |
151 | + # we need to reset because vote_for deletes keys from the params | |
152 | + allparams = @required_vote_params.merge(@optional_vote_params) | |
153 | + invalid_vote = @visitor.vote_for!(allparams) | |
154 | + invalid_vote.should_not be_nil | |
155 | + | |
156 | + invalid_vote.valid_record.should be_false | |
157 | + invalid_vote.validity_information.should == "Appearance #{@appearance.id} already answered" | |
158 | + @appearance.reload.answerable.should == valid_vote | |
159 | + @visitor.reload.votes.count.should == 1 | |
160 | + end | |
121 | 161 | |
122 | 162 | it "should accurately update score counts after vote" do |
123 | 163 | ... | ... |
spec/models/vote_spec.rb
... | ... | @@ -79,4 +79,32 @@ describe Vote do |
79 | 79 | @prompt.right_choice.reload |
80 | 80 | @prompt.right_choice.score.should be_close 33, 1 |
81 | 81 | end |
82 | + | |
83 | + it "should not display invalid votes by default" do | |
84 | + 5.times do | |
85 | + Factory.create(:vote) | |
86 | + end | |
87 | + Vote.count.should == 5 | |
88 | + | |
89 | + v = Vote.last | |
90 | + v.valid_record.should be_true | |
91 | + | |
92 | + v.valid_record = false | |
93 | + v.save | |
94 | + Vote.count.should == 4 | |
95 | + end | |
96 | + | |
97 | + it "should allow default valid_record behavior to be overriden by default" do | |
98 | + required_params = {:question => @question, :prompt => @prompt, | |
99 | + :choice => @prompt.left_choice, | |
100 | + :voter=> @question.site.default_visitor, | |
101 | + :loser_choice => @prompt.right_choice} | |
102 | + | |
103 | + vote = Vote.create!(required_params) | |
104 | + vote.valid_record.should be_true | |
105 | + | |
106 | + | |
107 | + vote = Vote.create!(required_params.merge!(:valid_record => false)) | |
108 | + vote.valid_record.should be_false | |
109 | + end | |
82 | 110 | end | ... | ... |