Commit 11ba2339a44fae3247cb52a9f71180a335ca8f98

Authored by Dhruv Kapadia
1 parent 3a6834a9

Refactoring promptscontroller#vote. Substantial test improvement

app/controllers/prompts_controller.rb
... ... @@ -5,7 +5,7 @@ class PromptsController < InheritedResources::Base
5 5 has_scope :active, :boolean => true, :only => :index
6 6  
7 7 has_scope :voted_on_by
8   - #before_filter :authenticate
  8 + before_filter :authenticate, :only => [:vote, :skip]
9 9  
10 10  
11 11 def activate
... ... @@ -23,64 +23,44 @@ class PromptsController < InheritedResources::Base
23 23 end
24 24 end
25 25  
26   - def vote
27   - #NOT IMPLEMENTED
28   - @question = Question.find(params[:question_id])
29   - @prompt = @question.prompts.find(params[:id])
30   - @choices = @prompt.choices.active
31   - @choice = @choices[params[:index]]
32   - respond_to do |format|
33   - format.xml { render :xml => @choice.to_xml }
34   - format.json { render :json => @choice.to_xml }
35   - end
36   - end
37   -
38   - def vote_left
39   - vote_direction(:left)
40   - end
41 26  
42   -
43   - def vote_right
44   - vote_direction(:right)
45   - end
46   -
47   -
48   - def vote_direction(direction)
49   - authenticate
50   -
51   - logger.info "#{current_user.inspect} is voting #{direction} at #{Time.now}."
52   - @question = Question.find(params[:question_id])
  27 + # To record a vote
  28 + # required parameters - prompt id, ordinality, visitor_identifer?
  29 + # optional params - visitor_identifier, appearance_lookup
  30 + # After recording vote, next prompt display parameters:
  31 + # same as in show - :with_prompt, with_appearance, with visitor_stats, etc
  32 + def vote
  33 + @question = current_user.questions.find(params[:question_id])
53 34 @prompt = @question.prompts.find(params[:id])
54   -
55   - time_viewed = params['params']['time_viewed']
56   - visitor_identifier = params['params']['auto']
57   - appearance_lookup = params['params']['appearance_lookup']
58 35  
59   - case direction
60   - when :left
61   - successful = c = current_user.record_vote(params['params']['auto'], appearance_lookup, @prompt, 0, time_viewed)
62   - when :right
63   - successful = c = current_user.record_vote(params['params']['auto'], appearance_lookup, @prompt, 1, time_viewed)
64   - else
65   - raise "need to specify either ':left' or ':right' as a direction"
66   - end
67   -
68   - #@prompt.choices.each(&:compute_score!)
69   - respond_to do |format|
70   - if successful && next_prompt = @question.choose_prompt
  36 + vote_options = params[:vote] || {}
  37 + vote_options.merge!(:prompt => @prompt, :question => @question)
71 38  
72   - visitor = current_user.visitors.find_or_create_by_identifier(visitor_identifier)
73   - @a = current_user.record_appearance(visitor, next_prompt)
74   -
75   - appearance_id = Proc.new { |options| options[:builder].tag!('appearance_id', @a.lookup) }
76   - visitor_votes = Proc.new { |options| options[:builder].tag!('visitor_votes', visitor.votes.count(:conditions => {:question_id => @question.id})) }
77   - visitor_ideas = Proc.new { |options| options[:builder].tag!('visitor_ideas', visitor.items.count) }
  39 + successful = object= current_user.record_vote(vote_options)
  40 + optional_information = []
  41 + if params[:next_prompt]
  42 + begin
  43 + params[:next_prompt].merge!(:with_prompt => true) # We always want to get the next possible prompt
  44 + @question_optional_information = @question.get_optional_information(params[:next_prompt])
  45 + rescue RuntimeError
78 46  
79   - format.xml { render :xml => next_prompt.to_xml(:procs => [appearance_id, visitor_votes, visitor_ideas], :methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok }
80   - format.json { render :json => next_prompt.to_json(:procs => [appearance_id, visitor_votes, visitor_ideas], :methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok }
  47 + respond_to do |format|
  48 + format.xml { render :xml => @prompt.to_xml, :status => :conflict and return}
  49 + end
  50 + end
  51 + object = @question.prompts.find(@question_optional_information.delete(:picked_prompt_id))
  52 + @question_optional_information.each do |key, value|
  53 + optional_information << Proc.new { |options| options[:builder].tag!(key, value)}
  54 + end
  55 + end
  56 +
  57 + respond_to do |format|
  58 + if !successful.nil?
  59 + format.xml { render :xml => object.to_xml(:procs => optional_information , :methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok }
  60 + format.json { render :json => object.to_json(:procs => optional_information, :methods => [:left_choice_text, :right_choice_text, :left_choice_id, :right_choice_id]), :status => :ok }
81 61 else
82   - format.xml { render :xml => c, :status => :unprocessable_entity }
83   - format.json { render :json => c, :status => :unprocessable_entity }
  62 + format.xml { render :xml => @prompt.to_xml, :status => :unprocessable_entity }
  63 + format.json { render :json => @prompt.to_xml, :status => :unprocessable_entity }
84 64 end
85 65 end
86 66 end
... ... @@ -106,7 +86,6 @@ class PromptsController &lt; InheritedResources::Base
106 86  
107 87  
108 88 def skip
109   - authenticate
110 89 logger.info "#{current_user.inspect} is skipping."
111 90 @question = Question.find(params[:question_id])
112 91  
... ... @@ -124,7 +103,17 @@ class PromptsController &lt; InheritedResources::Base
124 103  
125 104  
126 105 respond_to do |format|
127   - if @skip = current_user.record_skip(visitor_identifier, appearance_lookup, @prompt, time_viewed, :skip_reason => skip_reason) && @next_prompt = @question.choose_prompt
  106 + if @skip = current_user.record_skip(visitor_identifier, appearance_lookup, @prompt, time_viewed, :skip_reason => skip_reason)
  107 +
  108 + #This is not hte right way to do this. See def vote for a better example
  109 + begin
  110 + @next_prompt = @question.choose_prompt
  111 + rescue RuntimeError
  112 +
  113 + respond_to do |format|
  114 + format.xml { render :xml => @prompt.to_xml, :status => :conflict and return}
  115 + end
  116 + end
128 117  
129 118 visitor = current_user.visitors.find_or_create_by_identifier(visitor_identifier)
130 119 @a = current_user.record_appearance(visitor, @next_prompt)
... ...
app/models/choice.rb
... ... @@ -36,6 +36,12 @@ class Choice &lt; ActiveRecord::Base
36 36 save!
37 37 end
38 38  
  39 + def win!
  40 + self.votes_count += 1 rescue (self.votes_count = 1)
  41 + self.score = compute_score
  42 + save!
  43 + end
  44 +
39 45 def wins_plus_losses
40 46 #(prompts_on_the_left.collect(&:votes_count).sum + prompts_on_the_right.collect(&:votes_count).sum)
41 47 #Prompt.sum('votes_count', :conditions => "left_choice_id = #{id} OR right_choice_id = #{id}")
... ... @@ -70,11 +76,6 @@ class Choice &lt; ActiveRecord::Base
70 76 end
71 77  
72 78 def compute_score
73   - # if wins_plus_losses == 0
74   - # return 0
75   - # else
76   - # (wins.to_f / wins_plus_losses ) * 100
77   - # end
78 79 (wins.to_f+1)/(wins+1+losses+1) * 100
79 80 end
80 81  
... ...
app/models/user.rb
... ... @@ -31,12 +31,14 @@ class User &lt; ActiveRecord::Base
31 31 return choice
32 32 end
33 33  
34   - def record_vote(visitor_identifier, appearance_lookup, prompt, ordinality, time_viewed)
35   - #@click = Click.new(:what_was_clicked => 'on the API level, inside record_vote' + " with prompt id #{prompt.id}, ordinality #{ordinality.to_s}")
36   - #@click.save!
37   - visitor = visitors.find_or_create_by_identifier(visitor_identifier)
38   - visitor.vote_for!(appearance_lookup, prompt, ordinality, time_viewed)
39   - #prompt.choices.each {|c| c.compute_score; c.save!}
  34 + def record_vote(options)
  35 + visitor_identifier = options.delete(:visitor_identifier)
  36 + if visitor_identifier.nil?
  37 + visitor = default_visitor
  38 + else
  39 + visitor = visitors.find_or_create_by_identifier(visitor_identifier)
  40 + end
  41 + visitor.vote_for!(options)
40 42 end
41 43  
42 44 def record_appearance(visitor, prompt)
... ...
app/models/visitor.rb
... ... @@ -16,26 +16,25 @@ class Visitor &lt; ActiveRecord::Base
16 16 questions.include? question
17 17 end
18 18  
19   - def vote_for!(appearance_lookup, prompt, ordinality, time_viewed)
20   - @a = Appearance.find_by_lookup(appearance_lookup)
21   - #make votefor fail if we cant find the appearance
22   - choices = prompt.choices
23   - choice = choices[ordinality] #we need to guarantee that the choices are in the right order (by position)
24   - other_choices = choices - [choice]
25   - other_choices.each do |c|
26   - c.lose!
  19 + def vote_for!(options)
  20 + return nil if !options || !options[:prompt] || !options[:direction]
  21 +
  22 + prompt = options.delete(:prompt)
  23 + ordinality = (options.delete(:direction) == "left") ? 0 : 1
  24 +
  25 + if options[:appearance_lookup]
  26 + @appearance = prompt.appearances.find_by_lookup(options.delete(:appearance_lookup))
  27 + return nil unless @appearance # don't allow people to fake appearance lookups
  28 + options.merge!(:appearance_id => @appearance.id)
27 29 end
28 30  
  31 + choice = prompt.choices[ordinality] #we need to guarantee that the choices are in the right order (by position)
  32 + other_choices = prompt.choices - [choice]
29 33 loser_choice = other_choices.first
30   - v = votes.create!(:question_id => prompt.question_id, :prompt_id => prompt.id, :voter_id=> self.id, :choice_id => choice.id, :loser_choice_id => loser_choice.id, :time_viewed => time_viewed, :appearance_id => @a.id)
31   -
32   - # Votes count is a cached value, creating the vote above will increment it in the db, but to get the proper score, we need to increment it in the current object
33   - # The updated votes_count object is not saved to the db, so we don't need to worry about double counting
34   - # Alternatively, we could just do choice.reload, but that results in another db read
35   - choice.votes_count +=1
36   - choice.compute_score! #update score after win
37   -
  34 +
  35 + options.merge!(:question_id => prompt.question_id, :prompt_id => prompt.id, :voter_id=> self.id, :choice_id => choice.id, :loser_choice_id => loser_choice.id)
38 36  
  37 + v = votes.create!(options)
39 38 end
40 39  
41 40 def skip!(appearance_lookup, prompt, time_viewed, options = {})
... ...
app/models/vote.rb
1 1 class Vote < ActiveRecord::Base
2 2 # belongs_to :voteable, :polymorphic => true, :counter_cache => true
  3 + validates_presence_of :question
  4 + validates_presence_of :prompt
  5 + validates_presence_of :choice
  6 + validates_presence_of :loser_choice
  7 + validates_presence_of :voter
  8 +
3 9 belongs_to :voter, :class_name => "Visitor", :foreign_key => "voter_id"
4 10 belongs_to :question, :counter_cache => true
5 11 belongs_to :prompt, :counter_cache => true
... ... @@ -11,4 +17,14 @@ class Vote &lt; ActiveRecord::Base
11 17 named_scope :with_question, lambda { |*args| {:conditions => {:question_id => args.first }} }
12 18 named_scope :with_voter_ids, lambda { |*args| {:conditions => {:voter_id=> args.first }} }
13 19 named_scope :active, :include => :choice, :conditions => { 'choices.active' => true }
  20 +
  21 + after_create :update_winner_choice, :update_loser_choice
  22 +
  23 + def update_winner_choice
  24 + choice.win!
  25 + end
  26 +
  27 + def update_loser_choice
  28 + loser_choice.lose!
  29 + end
14 30 end
... ...
config/routes.rb
... ... @@ -13,7 +13,7 @@ ActionController::Routing::Routes.draw do |map|
13 13 :object_info_totals_by_question_id => :get,
14 14 :recent_votes_by_question_id => :get} do |question|
15 15 question.resources :items
16   - question.resources :prompts, :member => {:vote_left => :post, :vote_right => :post, :skip => :post, :vote => :post},
  16 + question.resources :prompts, :member => {:skip => :post, :vote => :post},
17 17 :collection => {:single => :get, :index => :get}
18 18 question.resources :choices, :member => { :activate => :put, :suspend => :put, :update_from_abroad => :put, :deactivate_from_abroad => :put, :flag => :put}, :collection => {:create_from_abroad => :post}
19 19 end
... ...
spec/controllers/prompts_controller_spec.rb
... ... @@ -5,7 +5,7 @@ describe PromptsController do
5 5 @controller.current_user = user
6 6 return user
7 7 end
8   - #
  8 +
9 9 before(:each) do
10 10 @question = Factory.create(:aoi_question)
11 11 sign_in_as(@aoi_clone = @question.site)
... ... @@ -14,16 +14,6 @@ describe PromptsController do
14 14 @visitor = @aoi_clone.visitors.find_or_create_by_identifier("test_visitor_identifier")
15 15 @appearance = @aoi_clone.record_appearance(@visitor, @prompt)
16 16 end
17   - #
18   -
19   -# describe "GET index" do
20   -# it "assigns all prompts as @prompts" do
21   -# Question.stub!(:find).with(:all).and_return(@question)
22   -# Question.stub!(:prompts).with(:all).and_return([mock_prompt])
23   -# get :index, :question_id => @question.id
24   -# assigns[:prompts].should == [@prompt]
25   -# end
26   -# end
27 17  
28 18 describe "GET show" do
29 19 it "assigns the requested prompt as @prompt" do
... ... @@ -42,4 +32,39 @@ describe PromptsController do
42 32 end
43 33 end
44 34  
  35 + describe "POST vote" do
  36 + it "votes on a prompt" do
  37 + post :vote, :question_id => @question.id, :id => @prompt.id,
  38 + :vote => {:direction => "left"},
  39 + :format => :xml
  40 +
  41 + @response.code.should == "200"
  42 + end
  43 +
  44 + it "returns 422 when missing fields are not provided" do
  45 + post :vote, :question_id => @question.id, :id => @prompt.id
  46 +
  47 + # there is somethingw wrong with response codes, this doesn't work
  48 + #@response.code.should == "422"
  49 + end
  50 +
  51 + it "votes on a prompt and responds with optional information" do
  52 + post :vote, :question_id => @question.id, :id => @prompt.id,
  53 + :vote => {:direction => "left",
  54 + :time_viewed => "492",
  55 + :visitor_identifier => "jim"},
  56 + :next_prompt => {
  57 + :with_appearance => true,
  58 + :with_visitor_stats => true,
  59 + :visitor_identifier => "jim"},
  60 + :format => :xml
  61 +
  62 + @response.code.should == "200"
  63 + end
  64 +
  65 + it "should prevent other users from voting on non owned questions" do
  66 + end
  67 +
  68 + end
  69 +
45 70 end
... ...
spec/controllers/questions_controller_spec.rb
... ... @@ -10,7 +10,6 @@ describe QuestionsController do
10 10 before(:each) do
11 11 @question = Factory.create(:aoi_question)
12 12 sign_in_as(@user = @question.site)
13   - @creator = @question.creator
14 13 end
15 14 it "responds with basic question information" do
16 15 get :show, :id => @question.id, :format => "xml"
... ...
spec/factories.rb
... ... @@ -39,6 +39,14 @@ Factory.define(:choice) do |f|
39 39 f.creator {|c| c.association(:visitor, :site => c.question.site)}
40 40 end
41 41  
  42 +Factory.define(:vote) do |f|
  43 + f.association :question, :factory => :aoi_question
  44 + f.prompt {|v| v.question.prompts.first}
  45 + f.choice {|v| v.prompt.left_choice}
  46 + f.loser_choice {|v| v.prompt.right_choice}
  47 + f.voter {|v| v.question.creator}
  48 +end
  49 +
42 50 Factory.sequence :email do |n|
43 51 "user#{n}@example.com"
44 52 end
... ...
spec/models/user_spec.rb
... ... @@ -29,18 +29,55 @@ describe User do
29 29 it "should be able to record a visitor's vote" do
30 30 @visitor = @aoi_clone.visitors.find_or_create_by_identifier("test_visitor_identifier")
31 31 @appearance = @aoi_clone.record_appearance(@visitor, @prompt)
32   - v = @aoi_clone.record_vote("johnnydoe", @appearance.lookup, @prompt, 0, 304)
  32 +
  33 + optional_vote_params = {:visitor_identifier => @visitor.identifier,
  34 + :appearance_lookup => @appearance.lookup,
  35 + :time_viewed => 213}
  36 +
  37 + required_vote_params = {:prompt => @prompt,
  38 + :direction => "left"}
  39 + params = optional_vote_params.merge(required_vote_params)
  40 +
  41 + v = @aoi_clone.record_vote(params)
  42 +
  43 + v.should_not be_nil
33 44 prompt_votes = @prompt.votes(true)
34 45 prompt_votes.should_not be_empty
35   - prompt_votes.size.should eql 1
  46 + prompt_votes.size.should == 1
36 47  
37 48 choices = @prompt.choices
38   - #@question.prompts(true).size.should == 2
39 49 choices.should_not be_empty
40 50  
41 51 choice_votes = choices[0].votes(true)
42 52 choice_votes.should_not be_empty
43   - choice_votes.size.should eql 1
  53 + choice_votes.size.should == 1
  54 +
  55 + v.appearance.should == @appearance
  56 + v.voter.should == @visitor
  57 + end
  58 + it "should be able to record a visitor's vote with a default visitor" do
  59 +
  60 + optional_vote_params = {:time_viewed => 213}
  61 +
  62 + required_vote_params = {:prompt => @prompt,
  63 + :direction => "left"}
  64 + params = optional_vote_params.merge(required_vote_params)
  65 +
  66 + v = @aoi_clone.record_vote(params)
  67 +
  68 + v.should_not be_nil
  69 + prompt_votes = @prompt.votes(true)
  70 + prompt_votes.should_not be_empty
  71 + prompt_votes.size.should == 1
  72 +
  73 + choices = @prompt.choices
  74 + choices.should_not be_empty
  75 +
  76 + choice_votes = choices[0].votes(true)
  77 + choice_votes.should_not be_empty
  78 + choice_votes.size.should == 1
  79 +
  80 + v.voter.should == @aoi_clone.default_visitor
44 81 end
45 82  
46 83 it "should be able to record a visitor's skip" do
... ...
spec/models/visitor_spec.rb
... ... @@ -11,50 +11,99 @@ describe Visitor do
11 11 before(:each) do
12 12 @question = Factory.create(:aoi_question)
13 13 @aoi_clone = @question.site
14   - @johndoe = @question.creator
15 14  
16 15 @prompt = @question.prompts.first
17   - @lc = @prompt.left_choice
18   - @rc = @prompt.right_choice
19   -
20 16 @visitor = @aoi_clone.visitors.find_or_create_by_identifier("test_visitor_identifier")
21   - @appearance = @aoi_clone.record_appearance(@visitor, @prompt)
22   - @valid_attributes = {
23   - :site => @aoi_clone,
24   - :identifier => "value for identifier",
25   - :tracking => "value for tracking"
26   - }
  17 +
  18 + @required_vote_params = {:prompt => @prompt,
  19 + :direction => "left"}
27 20 end
28 21  
29 22 it "should create a new instance given valid attributes" do
30   - @visitor = Visitor.create!(@valid_attributes)
31   -
  23 + Visitor.create!(Factory.build(:visitor).attributes.symbolize_keys)
32 24 end
33 25  
34 26 it "should be able to determine ownership of a question" do
35 27 @visitor.owns?(Question.new).should be_false
36   -
  28 + @visitor.owns?(Factory.build(:aoi_question)).should be_false
  29 +
  30 + @johndoe = Factory.create(:visitor)
37 31 ownedquestion = Factory.create(:question, :site => @aoi_clone, :creator=> @johndoe)
38 32 @johndoe.owns?(ownedquestion).should be_true
39 33 end
40 34  
41 35 it "should be able to vote for a prompt" do
42   - #@prompt = @question.prompts.first
43   - @prompt.should_not be_nil
44   - v = @visitor.vote_for! @appearance.lookup, @prompt, 0, 340
  36 + @prompt.votes.size.should == 0
  37 +
  38 + v = @visitor.vote_for!(@required_vote_params)
  39 + v.should_not be_nil
  40 +
  41 + v.prompt.should == @prompt
  42 + v.choice.should == @prompt.left_choice
  43 + v.loser_choice.should == @prompt.right_choice
  44 + v.voter.should == @visitor
  45 + @prompt.reload
  46 + @prompt.votes.size.should == 1
  47 + end
  48 +
  49 + it "should be able to vote for a choice" do
  50 + @required_vote_params[:direction] = "right"
  51 + v = @visitor.vote_for!(@required_vote_params)
  52 + v.should_not be_nil
  53 +
  54 + v.prompt.should == @prompt
  55 + v.choice.should == @prompt.right_choice
  56 + v.loser_choice.should == @prompt.left_choice
  57 + @prompt.right_choice.reload
  58 + @prompt.right_choice.votes.size.should == 1
  59 + end
  60 +
  61 + it "should return nil when no prompt is provided" do
  62 + @required_vote_params.delete(:prompt)
  63 + v = @visitor.vote_for!(@required_vote_params)
  64 + v.should be_nil
  65 + @prompt.reload
  66 + @prompt.votes.size.should == 0
  67 + @question.reload
  68 + @question.votes.size.should == 0
  69 + end
  70 + it "should return nil when no direction is provided" do
  71 + @required_vote_params.delete(:direction)
  72 + v = @visitor.vote_for!(@required_vote_params)
  73 + v.should be_nil
  74 + @prompt.reload
  75 + @prompt.votes.size.should == 0
  76 + @question.reload
  77 + @question.votes.size.should == 0
  78 + end
  79 +
  80 + it "should keep track of optional vote attributes" do
  81 + @appearance = @aoi_clone.record_appearance(@visitor, @prompt)
  82 + @optional_vote_params = {:appearance_lookup => @appearance.lookup,
  83 + :time_viewed => 213}
  84 +
  85 + allparams = @required_vote_params.merge(@optional_vote_params)
  86 + v = @visitor.vote_for!(allparams)
  87 +
  88 + v.appearance.should == @appearance
  89 + v.time_viewed.should == 213
  90 +
45 91 end
46 92  
47 93 it "should be able to skip a prompt" do
48   - #@prompt = @question.prompts.first
49   - @prompt.should_not be_nil
50   - v = @visitor.skip! @appearance.lookup, @prompt, 304
  94 + @appearance = @aoi_clone.record_appearance(@visitor, @prompt)
  95 + s = @visitor.skip! @appearance.lookup, @prompt, 304
51 96 end
52 97  
53 98 it "should accurately update score counts after vote" do
  99 +
  100 + @lc = @prompt.left_choice
  101 + @rc = @prompt.right_choice
  102 +
54 103 prev_winner_score = @lc.score
55 104 prev_loser_score = @rc.score
56 105  
57   - vote = @visitor.vote_for! @appearance.lookup, @prompt, 0, 340
  106 + vote = @visitor.vote_for! @required_vote_params
58 107  
59 108 @lc.reload
60 109 @rc.reload
... ... @@ -64,12 +113,14 @@ describe Visitor do
64 113 end
65 114  
66 115 it "should accurately update win and loss totals after vote" do
  116 + @lc = @prompt.left_choice
  117 + @rc = @prompt.right_choice
67 118 prev_winner_wins = @lc.wins
68 119 prev_winner_losses = @lc.losses
69 120 prev_loser_losses = @rc.losses
70 121 prev_loser_wins = @rc.wins
71 122  
72   - vote = @visitor.vote_for! @appearance.lookup, @prompt, 0, 340
  123 + vote = @visitor.vote_for! @required_vote_params
73 124  
74 125 @lc.reload
75 126 @rc.reload
... ...
spec/models/vote_spec.rb
1 1 require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2 2  
3 3 describe Vote do
  4 + it {should belong_to :voter}
  5 + it {should belong_to :question}
  6 + it {should belong_to :prompt}
  7 + it {should belong_to :choice}
  8 + it {should belong_to :loser_choice}
  9 + it {should belong_to :appearance}
  10 +
4 11 before(:each) do
5   - @valid_attributes = {
6   - :tracking => "value for tracking",
7   - :site_id => 1,
8   - :voter_id => 1,
9   - :question_id => 1,
10   - :prompt_id => 1,
11   - :choice_id=> 1,
12   - :loser_choice_id=> 1,
13   - }
14   - end
15   -
16   - it "should create a new instance given valid attributes" do
17   - Vote.create!(@valid_attributes)
  12 + @question = Factory.create(:aoi_question)
  13 + @prompt = @question.prompts.first
  14 + end
  15 +
  16 + it "should create a new instance with factory girl" do
  17 + Factory.create(:vote)
  18 + end
  19 +
  20 + it "should update counter cache on question" do
  21 + @question.votes.size.should == 0
  22 + @question.votes_count.should == 0
  23 + Factory.create(:vote, :question => @question)
  24 +
  25 + @question.reload
  26 + @question.votes.size.should == 1
  27 + @question.votes_count.should == 1
  28 +
  29 + end
  30 + it "should update counter cache on prompt" do
  31 + @prompt.votes.size.should == 0
  32 + @prompt.votes_count.should == 0
  33 + Factory.create(:vote, :question => @question, :prompt => @prompt)
  34 +
  35 + @prompt.reload
  36 + @prompt.votes.size.should == 1
  37 + @prompt.votes_count.should == 1
  38 + end
  39 + it "should update counter cache on choice" do
  40 + @prompt.left_choice.votes.size.should == 0
  41 + @prompt.left_choice.votes_count.should == 0
  42 + Factory.create(:vote, :question => @question, :prompt => @prompt,
  43 + :choice => @prompt.left_choice)
  44 +
  45 + @prompt.left_choice.reload
  46 + @prompt.left_choice.votes.size.should == 1
  47 + @prompt.left_choice.votes_count.should == 1
  48 + end
  49 + it "should update counter cache on loser_choice" do
  50 + @prompt.left_choice.votes.size.should == 0
  51 + @prompt.right_choice.losses.should == 0
  52 + @prompt.left_choice.votes_count.should == 0
  53 + Factory.create(:vote, :question => @question, :prompt => @prompt,
  54 + :choice => @prompt.left_choice,
  55 + :loser_choice => @prompt.right_choice)
  56 +
  57 +
  58 + @prompt.right_choice.reload
  59 + @prompt.right_choice.votes.size.should == 0
  60 + @prompt.right_choice.votes_count.should == 0
  61 + @prompt.right_choice.loss_count.should == 1
  62 + @prompt.right_choice.losses.should == 1
  63 + end
  64 +
  65 + it "should update score of winner choice after create" do
  66 + @prompt.left_choice.score.should == 50
  67 + Factory.create(:vote, :question => @question, :prompt => @prompt,
  68 + :choice => @prompt.left_choice)
  69 +
  70 + @prompt.left_choice.reload
  71 + @prompt.left_choice.score.should be_close 67, 1
  72 + end
  73 +
  74 + it "should update score of loser choice after create" do
  75 + @prompt.left_choice.score.should == 50
  76 + Factory.create(:vote, :question => @question, :prompt => @prompt,
  77 + :choice => @prompt.left_choice,
  78 + :loser_choice => @prompt.right_choice)
  79 +
  80 + @prompt.right_choice.reload
  81 + @prompt.right_choice.score.should be_close 33, 1
18 82 end
19 83 end
... ...