* Much improved list screen for elections.
* Initial voter-based interface based around tokens.
* New uniq_token regenerating library.
Left things a little bit unstable including:
* uniq_token should be refactored to provide only a class method.
* voter list review page is unwritten
* rankings are committed to the database but some sort somewhere seems
to be botching things.
+require 'uniq_token'
+
class ElectionsController < ApplicationController
model :raw_voter_list, :voter, :vote, :candidate
def process_incoming_voters(raw_voter_list)
incoming_voters = RawVoterList.new( raw_voter_list )
+ token_generator = UniqueTokenGenerator.new( 16 )
unless incoming_voters.entries.empty?
incoming_voters.each do |new_voter|
+ until new_voter.password and \
+ Voter.find_all( [ "password = ?", new_voter.password ]).empty?
+ new_voter.password = token_generator.token
+ end
+
+ breakpoint
if incoming_voters.email == 0
new_voter.contacted = 1
elsif incoming_voters.email == 1
@raw_voter_list = RawVoterList.new
@raw_voter_list.email = incoming_voters.email
end
+
+ def email_voter
+ end
+
end
--- /dev/null
+class VoterController < ApplicationController
+ model :voter
+ model :vote
+ model :election
+
+ def index
+ password = params[:id]
+ @voter = Voter.find_all( [ "password = ?", password ] )[0]
+ end
+
+ def review
+ password = params[:id]
+ @voter = Voter.find_all( [ "password = ?", password ] )[0]
+
+ # destroy the old vote if that's what we need to do
+ @voter.vote.destroy if @voter.vote
+ @voter.reload
+
+ @voter.vote = Vote.new
+ @voter.vote.votestring = params[:vote][:votestring]
+ @voter.vote.save
+ render_text "success"
+ end
+
+end
--- /dev/null
+module VoterHelper
+end
class Candidate < ActiveRecord::Base
belongs_to :election
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+
end
self.candidates.each do |candidate|
candidate.destroy
end
- super destroy
+ super
end
end
--- /dev/null
+class Ranking < ActiveRecord::Base
+ belongs_to :candidate
+ belongs_to :vote
+end
include Enumerable
def initialize(params={})
- @email = params[:email] || 1
+ @email = params[:email].to_i || 1
@input_addresses = params[:input_addresses] || String.new
end
--- /dev/null
+class Vote < ActiveRecord::Base
+ belongs_to :voter
+ has_many :rankings
+
+ def initialize
+ super
+ @votes = []
+ end
+
+ def votestring=(string="")
+ rel_votes = string.split("").collect { |vote| vote.to_i }
+
+ # covert relative orders to absolute candidate ids
+ candidate_ids = voter.election.candidates.sort
+ candidate_ids.collect! { |candidate| candidate.id.to_i }
+
+ rel_votes.collect! { |vote| candidate_ids[ vote - 1 ] }
+ @votes = rel_votes
+ end
+
+ def save
+ rankings.each { destroy } unless rankings.empty?
+ @votes.each_with_index do |candidate, index|
+ ranking = Ranking.new
+ ranking.rank = index + 1
+ ranking.candidate = Candidate.find(candidate)
+ self.rankings << ranking
+ end
+
+ super
+ end
+
+ def destroy
+ rankings.each { destroy }
+ super
+ end
+
+end
class Voter < ActiveRecord::Base
belongs_to :election
+ has_one :vote
end
+++ /dev/null
-class Votes < ActiveRecord::Base
-end
+<% %>
<h1>Listing elections</h1>
-<table>
- <tr>
- <% for column in Election.content_columns %>
- <th><%= column.human_name %></th>
- <% end %>
- </tr>
-
+<table cellpadding="10px">
+
<% for election in @elections %>
<tr>
- <% for column in Election.content_columns %>
- <td><%=h election.send(column.name) %></td>
- <% end %>
- <td><%= link_to 'Show', :action => 'show', :id => election %></td>
- <td><%= link_to 'Edit', :action => 'edit', :id => election %></td>
- <td><%= link_to 'Destroy', { :action => 'destroy', :id => election }, :confirm => 'Are you sure?' %></td>
+ <td valign="top"><h2><%= link_to election.name, :action => 'show', :id => election %></h2>
+ <p><strong>Description:</strong></p>
+ <blockquote><%= election.description %></blockquote>
+
+ <p><strong>Election Information:</strong></p>
+ <ul>
+ <li><%= election.voters.length %> registered voters</li>
+ <li><%= "<strong>Not</strong> " unless election.anonymous == 1 %>Anonymous</li>
+ <li>Starts <%= election.startdate %></li>
+ <li>Ends <%= election.enddate %></li>
+ </ul>
+ </td>
+ <td valign="top">
+ <p><strong>Candidates:</strong></p>
+ <% @election = election %><%= render :partial => 'candidate_list', :id => election.id %>
+ </td>
+ <td valign="top">
+ <%= link_to 'Destroy', { :action => 'destroy', :id => election }, :confirm => 'Are you sure?' %></td>
</tr>
<% end %>
</table>
--- /dev/null
+<% %>
+
+<h1>Vote Below the Line</h1>
+
+<p><strong>Election:</strong> <%= @voter.election.name %></p>
+
+<p><strong>Voter:</strong> <%= @voter.email %></p>
+
+<p><strong>Candidates:</strong></p>
+
+<ol>
+<% for candidate in @voter.election.candidates %>
+ <li><%= candidate.name %></li>
+<% end %>
+</ol>
+
+<p>If this information is incorrect, please notify the vote
+administrator immediatedly!</p>
+
+<hr />
+
+<h2>Place Your Vote Here</h2>
+
+<p>Rank each candidate in order of more preferred to least
+preferred. (e.g., 123 or 321 or 213, etc.)</p>
+
+<%= form_tag :action => 'review', :id => @voter.password %>
+<%= text_field :vote, :votestring -%>
+<%= submit_tag "Submit!" %>
+<%= end_form_tag %>
+
+
+
+
+
--- /dev/null
+require 'digest/md5'
+
+class UniqueTokenGenerator
+ def initialize(length=10)
+ @length = length
+ end
+
+ # this should probably be rewritten as a class method
+ def token
+ token = ""
+ while token.length < @length
+ seed = ""
+ 16.times do
+ seed << ( i = Kernel.rand(62)
+ i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 )) ).chr
+ end
+ token << Digest::MD5.hexdigest( seed )
+ end
+ token.slice( 0..@length )
+ end
+end
+
+# debug code
+# puts UniqueTokenGenerator.new(300).token
+
--- /dev/null
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+another:
+ id: 2
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+require 'voter_controller'
+
+# Re-raise errors caught by the controller.
+class VoterController; def rescue_action(e) raise e end; end
+
+class VoterControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = VoterController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RankingTest < Test::Unit::TestCase
+ fixtures :rankings
+
+ # Replace this with your real tests.
+ def test_truth
+ assert_kind_of Ranking, rankings(:first)
+ end
+end