From 966792228a4955788de3589a3438273679d747e6 Mon Sep 17 00:00:00 2001 From: Clay Shentrup Date: Sun, 1 Jul 2012 19:49:05 -0700 Subject: [PATCH] Refactor to pass the target directory, rather than set a global constant. --- lib/add_ranking_to_candidate_list.rb | 15 +++++++++ lib/ballot_image_line.rb | 19 +++++------ lib/ballot_image_reader.rb | 16 +++++----- lib/ballot_image_vote.rb | 22 ------------- lib/conductor.rb | 40 ++++++++++++++++------- lib/directory_reader.rb | 9 ++++++ lib/file_reader.rb | 31 ++++++++++++++++++ lib/master_lookup.rb | 8 ++--- lib/master_lookup_line.rb | 1 - lib/sfbp.rb | 39 ----------------------- sfbparse.rb | 4 ++- spec/lib/conductor_spec.rb | 47 ++++++---------------------- 12 files changed, 114 insertions(+), 137 deletions(-) create mode 100644 lib/add_ranking_to_candidate_list.rb delete mode 100644 lib/ballot_image_vote.rb create mode 100644 lib/directory_reader.rb create mode 100644 lib/file_reader.rb delete mode 100644 lib/sfbp.rb mode change 100644 => 100755 sfbparse.rb diff --git a/lib/add_ranking_to_candidate_list.rb b/lib/add_ranking_to_candidate_list.rb new file mode 100644 index 0000000..905527d --- /dev/null +++ b/lib/add_ranking_to_candidate_list.rb @@ -0,0 +1,15 @@ +class AddRankingToCandidateList < Struct.new(:ranking_lines, :candidate_list) + def self.process(*args) + new(*args).add_ranking + end + + def add_ranking + candidate_list.add_ranking(*ranked_candidates) + end + + private + + def ranked_candidates + ranking_lines.map {|ranking_line| BallotImageLine.new(ranking_line, candidate_list).candidate} + end +end \ No newline at end of file diff --git a/lib/ballot_image_line.rb b/lib/ballot_image_line.rb index b63516d..d735281 100644 --- a/lib/ballot_image_line.rb +++ b/lib/ballot_image_line.rb @@ -1,18 +1,15 @@ -class BallotImageLine +class BallotImageLine < Struct.new(:line, :candidate_list) CANDIDATE_ID_START_INDEX = 36 CANDIDATE_ID_LENGTH = 7 - - def initialize(line, candidate_list) - @line = line - @candidate_list = candidate_list - end - def record_id - @line[CANDIDATE_ID_START_INDEX, CANDIDATE_ID_LENGTH] - end - def candidate return nil if record_id == '0000000' - @candidate_list.find(record_id) + candidate_list.find(record_id) + end + + private + + def record_id + line[CANDIDATE_ID_START_INDEX, CANDIDATE_ID_LENGTH] end end \ No newline at end of file diff --git a/lib/ballot_image_reader.rb b/lib/ballot_image_reader.rb index 53d43e2..4854771 100644 --- a/lib/ballot_image_reader.rb +++ b/lib/ballot_image_reader.rb @@ -1,26 +1,26 @@ -require 'ballot_image_vote' - -class BallotImageReader +class BallotImageReader < Struct.new(:candidate_list, :directory_reader) BALLOT_IMAGE_PATH = '*-BallotImage.txt' RANKINGS_PER_VOTER = 3 - def initialize(candidate_list) - @candidate_list = candidate_list + def self.process(*args) + new(*args).process + end + + def process calculate_preferences end private def calculate_preferences - SFBP.file_from_path(BALLOT_IMAGE_PATH).handle_file do |file| + directory_reader.handle_file(BALLOT_IMAGE_PATH) do |file| process_rankings(file) end end def process_rankings(file) file.each_slice(RANKINGS_PER_VOTER) do |lines| - # maybe change this to ask, rather than tell? - BallotImageVote.new(lines, @candidate_list) + AddRankingToCandidateList.process(lines, candidate_list) end end end \ No newline at end of file diff --git a/lib/ballot_image_vote.rb b/lib/ballot_image_vote.rb deleted file mode 100644 index df68f69..0000000 --- a/lib/ballot_image_vote.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'ballot_image_line' - -class BallotImageVote - def initialize(ranking_lines, candidate_list) - @first_line = BallotImageLine.new(ranking_lines[0], candidate_list) - @second_line = BallotImageLine.new(ranking_lines[1], candidate_list) - @third_line = BallotImageLine.new(ranking_lines[2], candidate_list) - - @candidate_list = candidate_list - add_ranking - end - - private - - def add_ranking - @candidate_list.add_ranking(*ranked_candidates) - end - - def ranked_candidates - [@first_line.candidate, @second_line.candidate, @third_line.candidate] - end -end \ No newline at end of file diff --git a/lib/conductor.rb b/lib/conductor.rb index 70929bb..d338e49 100644 --- a/lib/conductor.rb +++ b/lib/conductor.rb @@ -1,22 +1,36 @@ -require 'sfbp' -require 'candidate' -require 'master_lookup' -require 'ballot_image_reader' -require 'present_results' +require File.join(File.dirname(__FILE__), 'file_reader') +require File.join(File.dirname(__FILE__), 'directory_reader') +require File.join(File.dirname(__FILE__), 'candidate') +require File.join(File.dirname(__FILE__), 'candidate_list') +require File.join(File.dirname(__FILE__), 'master_lookup_line') +require File.join(File.dirname(__FILE__), 'master_lookup') +require File.join(File.dirname(__FILE__), 'ballot_image_line') +require File.join(File.dirname(__FILE__), 'add_ranking_to_candidate_list') +require File.join(File.dirname(__FILE__), 'ballot_image_reader') +require File.join(File.dirname(__FILE__), 'present_results') -class Conductor +class Conductor < Struct.new(:source_directory_path_from_root) attr_reader :candidates - def print_matchups - present_results.print_matchups + def self.conduct(source_dir_path_from_root) + new(source_dir_path_from_root).conduct + end + + def conduct + calculate_preferences + print_matchups end def calculate_preferences - BallotImageReader.new(candidate_list) + BallotImageReader.process(candidate_list, directory_reader) end private - + + def print_matchups + present_results.print_matchups + end + def present_results PresentResults.new(candidate_list) end @@ -30,6 +44,10 @@ def candidates end def master_lookup - @master_lookup ||= MasterLookup.new + @master_lookup ||= MasterLookup.new(directory_reader) + end + + def directory_reader + @directory_reader ||= DirectoryReader.new(source_directory_path_from_root) end end \ No newline at end of file diff --git a/lib/directory_reader.rb b/lib/directory_reader.rb new file mode 100644 index 0000000..e75e9e1 --- /dev/null +++ b/lib/directory_reader.rb @@ -0,0 +1,9 @@ +class DirectoryReader < Struct.new(:source_directory_path_from_root) + def file_from_path(source_directory_path_from_root) + new(source_directory_path_from_root) + end + + def handle_file(filename, &block) + FileReader.process(source_directory_path_from_root, filename, &block) + end +end diff --git a/lib/file_reader.rb b/lib/file_reader.rb new file mode 100644 index 0000000..03efb4e --- /dev/null +++ b/lib/file_reader.rb @@ -0,0 +1,31 @@ +class FileReader < Struct.new(:source_directory_path_from_root, :filename) + def self.process(source_directory_path_from_root, filename, &block) + new(source_directory_path_from_root, filename).process(&block) + end + + def process + File.open(path) { |file| yield file } + end + + private + + def path + Dir::glob(glob).first + end + + def glob + File.expand_path(unexpanded_glob) + end + + def unexpanded_glob + File.join(source_dir_path_from_here, filename) + end + + def source_dir_path_from_here + source_directory_path_from_root + end + + def this_dir + File.dirname(__FILE__) + end +end diff --git a/lib/master_lookup.rb b/lib/master_lookup.rb index 36b2d1e..bb9f885 100644 --- a/lib/master_lookup.rb +++ b/lib/master_lookup.rb @@ -1,11 +1,8 @@ -require 'master_lookup_line' -require 'candidate_list' - -class MasterLookup +class MasterLookup < Struct.new(:directory_reader) MASTER_LOOKUP_PATH = '*-MasterLookup.txt' def candidate_list - @candidate_list ||= SFBP.file_from_path(MASTER_LOOKUP_PATH).handle_file do |file| + @candidate_list ||= directory_reader.handle_file(MASTER_LOOKUP_PATH) do |file| candidates_from_master_lookup(file) end end @@ -14,7 +11,6 @@ def candidate_list def candidates_from_master_lookup(file) candidate_list = CandidateList.new - line_count = 0 while line = file.gets lookup_line = MasterLookupLine.new(line) break unless lookup_line.is_candidate? diff --git a/lib/master_lookup_line.rb b/lib/master_lookup_line.rb index 32888b3..1a21fa1 100644 --- a/lib/master_lookup_line.rb +++ b/lib/master_lookup_line.rb @@ -4,7 +4,6 @@ class MasterLookupLine RECORD_DESCRIPTION_START_INDEX = 17 RECORD_DESCRIPTION_LENGTH = 50 - def initialize(line) @line = line end diff --git a/lib/sfbp.rb b/lib/sfbp.rb deleted file mode 100644 index c7b8da1..0000000 --- a/lib/sfbp.rb +++ /dev/null @@ -1,39 +0,0 @@ -module SFBP - class << self - def file_from_path(filename) - FileFromPath.new(filename) - end - end -end - -class SFBP::FileFromPath - def initialize(filename) - @filename = filename - end - - def handle_file - File.open(path) { |file| yield file } - end - - private - - def path - Dir.glob(glob).first - end - - def glob - File.expand_path(unexpanded_glob) - end - - def unexpanded_glob - File.join(source_dir_path_from_here, @filename) - end - - def source_dir_path_from_here - File.join(this_dir, '../', SFBP::SOURCE_DIR_PATH_FROM_ROOT) - end - - def this_dir - File.dirname(__FILE__) - end -end \ No newline at end of file diff --git a/sfbparse.rb b/sfbparse.rb old mode 100644 new mode 100755 index e85f1ae..12575cc --- a/sfbparse.rb +++ b/sfbparse.rb @@ -1 +1,3 @@ -require './lib/conductor' \ No newline at end of file +require File.join('./', File.dirname(__FILE__), 'lib/conductor') + +Conductor.conduct(ARGV[0]) \ No newline at end of file diff --git a/spec/lib/conductor_spec.rb b/spec/lib/conductor_spec.rb index ac12871..de6beb7 100644 --- a/spec/lib/conductor_spec.rb +++ b/spec/lib/conductor_spec.rb @@ -1,48 +1,19 @@ require 'conductor' describe Conductor do - SFBP::SOURCE_DIR_PATH_FROM_ROOT = 'spec/fixtures/2012-mayor' - - def get_candidate_by_name(name) - subject.send(:candidates).find {|candidate| candidate.name == name } - end - - describe '#calculate_preferences' do - it 'sets the preference counts' do - avalos = get_candidate_by_name('JOHN AVALOS') - yee = get_candidate_by_name('LELAND YEE') - chiu = get_candidate_by_name('DAVID CHIU') - lee = get_candidate_by_name('ED LEE') - - subject.calculate_preferences - - avalos.pref_count(yee).should == 1 - avalos.pref_count(chiu).should == 1 - lee.pref_count(yee).should == 1 - yee.pref_count(lee).should == 0 - yee.pref_count(chiu).should == 1 - end - end - - describe '#print_matchups' do - before do - subject.calculate_preferences - end - + describe '.conduct' do it 'prints results for all matchups' do - checked_lines = [ - ["JOHN AVALOS", "LELAND YEE", 1], - ["JOHN AVALOS", "DAVID CHIU", 1] - ].map do |subject, opponent, pref_count| - "#{subject} is preferred to #{opponent} by #{pref_count} voter(s).\n" - end + checked_lines = [] $stdout.stub(:puts) do |string| - checked_lines -= [string] + checked_lines << string end - subject.print_matchups - - checked_lines.should be_empty + + Conductor.conduct('spec/fixtures/2012-mayor') + + checked_lines.should include "LELAND YEE is preferred to DAVID CHIU by 1 voter(s).\n" + checked_lines.should include "LELAND YEE is preferred to EMIL LAWRENCE by 1 voter(s).\n" + checked_lines.should include "TERRY JOAN BAUM is preferred to WRITE-IN JOHN EDWARD FITCH by 1 voter(s).\n" end end end \ No newline at end of file