Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.swp
*.rspec
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'https://rubygems.org

gem 'rspec'
gem 'nokogiri'
3 changes: 3 additions & 0 deletions bin/macbeth_analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require_relative '../lib/macbeth_line_analyzer'

puts MacbethLineAnalyzer.new().analyze
23 changes: 23 additions & 0 deletions lib/macbeth_line_analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'open-uri'
require_relative 'shakespeare_line_analysis'
require_relative 'terminal_output'

# This is the entry class for the analysis
# it is doing a bit too much, but, being the entry point,
# something has to have the knowledge of what to send where
class MacbethLineAnalyzer
WEB_SOURCE_LOCATION = "http://www.ibiblio.org/xml/examples/shakespeare/macbeth.xml"

def initialize(source = nil)
source ||= File.read(open(WEB_SOURCE_LOCATION))
@analyzer = ShakespeareLineAnalysis.new(source, TerminalOutput.new)
end

def analyze
@analyzer.print_character_line_count
end

def default_source_location
WEB_SOURCE_LOCATION
end
end
13 changes: 13 additions & 0 deletions lib/output_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class OutputFormatter
def initialize(output)
@output = output
end

def print_values_descending(hash) # sort it so that they are in descending order by key (line count)
sorted_hash = Hash[hash.sort_by { |key, value| value }.reverse]

sorted_hash.each do |key, value|
@output.add_new_line "#{value} #{key.capitalize}"
end
end
end
19 changes: 19 additions & 0 deletions lib/shakespeare_line_analysis.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require_relative 'shakespeare_parser'
require_relative 'output_formatter'

class ShakespeareLineAnalysis
def initialize(source, output)
@source = source
@output = output
end

def print_character_line_count
parser = ShakespeareParser.new(@source)
speakers = parser.speaker_line_count

formatter = OutputFormatter.new(@output)
formatter.print_values_descending(speakers)

@output.print
end
end
18 changes: 18 additions & 0 deletions lib/shakespeare_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'nokogiri'

class ShakespeareParser
def initialize(xml_source_string)
@xml_doc = Nokogiri::XML(xml_source_string) { |config| config.strict }
end

def speaker_line_count
speakers = Hash.new

@xml_doc.xpath("//SPEAKER").each do |node|
siblings = @xml_doc.search("SPEAKER[text()='#{node.content}'] ~ *").map &:text
speakers[node.content] = siblings.length
end

speakers
end
end
14 changes: 14 additions & 0 deletions lib/terminal_output.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class TerminalOutput
def initialize
@buffer = StringIO.new
end

def add_new_line(text)
@buffer.puts text
end

def print
@buffer.seek(0)
@buffer.read.chomp
end
end
21 changes: 21 additions & 0 deletions spec/macbeth_line_analyzer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'spec_helper'
require_relative '../lib/macbeth_line_analyzer'

describe 'displays number of total lines spoken by characters in Macbeth' do
it 'prints the correct number of lines for the characters with the specified document' do
source = File.read('spec/sample_data.xml')
analyzer = MacbethLineAnalyzer.new(source)
results = analyzer.analyze
expect(results).to eq "11 Macbeth\n5 Malcolm\n2 All"
end

it 'has a default source pointing to a web location' do
analyzer = MacbethLineAnalyzer.new
expect(analyzer.default_source_location).to eq "http://www.ibiblio.org/xml/examples/shakespeare/macbeth.xml"
end

it 'returns an error for malformed xml input' do
analyzer = MacbethLineAnalyzer.new("asdsada")
expect{analyzer.analyze}.to raise_error
end
end
13 changes: 13 additions & 0 deletions spec/output_formatter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'spec_helper'
require_relative '../lib/terminal_output'
require_relative '../lib/output_formatter'

describe '.print_values_descending' do
it 'fills output in correct order' do
output_buffer = TerminalOutput.new
formatter = OutputFormatter.new(output_buffer)
h = Hash["mark" => 100, "steve" => 12, "kevin" => 200]
formatter.print_values_descending(h)
expect(output_buffer.print).to eq "200 Kevin\n100 Mark\n12 Steve"
end
end
101 changes: 101 additions & 0 deletions spec/sample_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE PLAY SYSTEM "play.dtd">

<PLAY>
<TITLE>The Tragedy of Macbeth</TITLE>

<FM>
<P>Text placed in the public domain by Moby Lexical Tools, 1992.</P>
<P>SGML markup by Jon Bosak, 1992-1994.</P>
<P>XML version by Jon Bosak, 1996-1998.</P>
<P>This work may be freely copied and distributed worldwide.</P>
</FM>


<PERSONAE>
<TITLE>Dramatis Personae</TITLE>

<PERSONA>DUNCAN, king of Scotland.</PERSONA>

<PGROUP>
<PERSONA>MALCOLM</PERSONA>
<PERSONA>DONALBAIN</PERSONA>
<GRPDESCR>his sons.</GRPDESCR>
</PGROUP>


<PGROUP>
<PERSONA>MACBETH</PERSONA>
<PERSONA>BANQUO</PERSONA>
<GRPDESCR>generals of the king's army.</GRPDESCR>
</PGROUP>


<PGROUP>
<PERSONA>MACDUFF</PERSONA>
<PERSONA>LENNOX</PERSONA>
<PERSONA>ROSS</PERSONA>
<PERSONA>MENTEITH</PERSONA>
<PERSONA>ANGUS</PERSONA>
<PERSONA>CAITHNESS</PERSONA>
<GRPDESCR>noblemen of Scotland.</GRPDESCR>
</PGROUP>

<PERSONA>FLEANCE, son to Banquo.</PERSONA>
<PERSONA>SIWARD, Earl of Northumberland, general of the English forces.</PERSONA>
<PERSONA>YOUNG SIWARD, his son.</PERSONA>
<PERSONA>SEYTON, an officer attending on Macbeth.</PERSONA>
<PERSONA>Boy, son to Macduff. </PERSONA>
<PERSONA>An English Doctor. </PERSONA>
<PERSONA>A Scotch Doctor. </PERSONA>
<PERSONA>A Soldier.</PERSONA>
<PERSONA>A Porter.</PERSONA>
<PERSONA>An Old Man.</PERSONA>
<PERSONA>LADY MACBETH</PERSONA>
<PERSONA>LADY MACDUFF</PERSONA>
<PERSONA>Gentlewoman attending on Lady Macbeth. </PERSONA>
<PERSONA>HECATE</PERSONA>
<PERSONA>Three Witches.</PERSONA>
<PERSONA>Apparitions.</PERSONA>
<PERSONA>Lords, Gentlemen, Officers, Soldiers, Murderers, Attendants, and Messengers. </PERSONA>
</PERSONAE>

<SCNDESCR>SCENE Scotland: England.</SCNDESCR>

<PLAYSUBT>MACBETH</PLAYSUBT>

<ACT><TITLE>ACT I</TITLE>
<SPEECH>
<SPEAKER>MACBETH</SPEAKER>
<LINE>Into the air; and what seem'd corporal melted</LINE>
<LINE>As breath into the wind. Would they had stay'd!</LINE>
</SPEECH>

<SPEECH>
<SPEAKER>ALL</SPEAKER>
<LINE>Fair is foul, and foul is fair:</LINE>
<LINE>Hover through the fog and filthy air.</LINE>
</SPEECH>

<SPEECH>
<SPEAKER>MALCOLM</SPEAKER>
<LINE>This is the sergeant</LINE>
<LINE>Who like a good and hardy soldier fought</LINE>
<LINE>'Gainst my captivity. Hail, brave friend!</LINE>
<LINE>Say to the king the knowledge of the broil</LINE>
<LINE>As thou didst leave it.</LINE>
</SPEECH>

<SPEECH>
<SPEAKER>MACBETH</SPEAKER>
<LINE>Stay, you imperfect speakers, tell me more:</LINE>
<LINE>By Sinel's death I know I am thane of Glamis;</LINE>
<LINE>But how of Cawdor? the thane of Cawdor lives,</LINE>
<LINE>A prosperous gentleman; and to be king</LINE>
<LINE>Stands not within the prospect of belief,</LINE>
<LINE>No more than to be Cawdor. Say from whence</LINE>
<LINE>You owe this strange intelligence? or why</LINE>
<LINE>Upon this blasted heath you stop our way</LINE>
<LINE>With such prophetic greeting? Speak, I charge you.</LINE>
</SPEECH>
</ACT>
</PLAY>
13 changes: 13 additions & 0 deletions spec/shakespeare_line_analysis_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'spec_helper'
require_relative '../lib/terminal_output'
require_relative '../lib/shakespeare_line_analysis'

describe '#print_character_line_count' do
it 'prints the number of lines for each character' do
source = File.open('spec/sample_data.xml')
output = TerminalOutput.new
analyzer = ShakespeareLineAnalysis.new(source, output)
results = analyzer.print_character_line_count
expect(results).to eq "11 Macbeth\n5 Malcolm\n2 All"
end
end
16 changes: 16 additions & 0 deletions spec/shakespeare_parser_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'spec_helper'
require_relative '../lib/shakespeare_parser'

describe '.speaker_line_count' do
it 'creates a hash of characters and their line numbers' do
source = File.read('spec/sample_data.xml')
parser = ShakespeareParser.new(source)
result = parser.speaker_line_count
expect(result['MACBETH']).to eq 11
expect(result['MALCOLM']).to eq 5
end

it 'throws an error for malformed xml' do
expect{ShakespeareParser.new("asdas")}.to raise_error
end
end
23 changes: 23 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# Limit the spec run to only specs with the focus metadata. If no specs have
# the filtering metadata and `run_all_when_everything_filtered = true` then
# all specs will run.
#config.filter_run :focus

# Run all specs when none match the provided filter. This works well in
# conjunction with `config.filter_run :focus`, as it will run the entire
# suite when no specs have `:filter` metadata.
#config.run_all_when_everything_filtered = true

# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
#config.order = 'random'
end
20 changes: 20 additions & 0 deletions spec/terminal_output_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'spec_helper'
require_relative '../lib/terminal_output'

describe '#add_new_line' do
it 'adds a new line to the printed output on the console' do
output = TerminalOutput.new
output.add_new_line "test"
output.add_new_line "print"
expect(output.print).to eq "test\nprint"
end
end

describe '#print' do
it 'displays what has been added to the buffer' do
output = TerminalOutput.new
output.add_new_line "foo"
output.add_new_line "bar"
expect(output.print).to eq "foo\nbar"
end
end