Skip to content

Instantly share code, notes, and snippets.

@anowell
Created December 11, 2012 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anowell/4257823 to your computer and use it in GitHub Desktop.
Save anowell/4257823 to your computer and use it in GitHub Desktop.
AnalogClock class, program, and specs (RSpec tests - TDD)
class AnalogClock
attr_reader :hour, :min, :meridiem
def initialize(time)
return nil unless time.instance_of?(String)
# split on space and colon
split_time = time.split(/:| /)
return nil unless split_time.length == 3
@hour = split_time[0].to_i if split_time[0].length <= 2
@min = split_time[1].to_i if split_time[1].length == 2
@meridiem = split_time[2].downcase.to_sym if split_time[2].length == 2
# extra validation to prevent "1:ab AM"
# might be cleaner to implement a good String::is_i? method
@min = nil if @min == 0 and split_time[1] != "00"
end
def valid?
(1..12).include?(hour) and
(0..59).include?(min) and
[:am, :pm].include?(meridiem)
end
def military_hour
return nil unless valid?
mh = hour % 12
mh += 12 if meridiem == :pm
mh
end
def minutes_until(clock)
return nil unless valid? and clock.kind_of?(AnalogClock) and clock.valid?
dif = (clock.military_hour-military_hour)*60 + (clock.min-min)
dif += (24*60) if dif < 0
dif
end
def self.to_degrees(minutes)
minutes * 6 if minutes
end
def self.to_rotations(minutes)
minutes / 60.0 if minutes
end
end
require_relative 'analog_clock'
describe AnalogClock do
context "Parser" do
it "parses 1:00 AM" do
clock = AnalogClock.new "1:00 AM"
clock.should be_valid
clock.hour.should == 1
clock.min.should == 0
clock.meridiem.should == :am
clock.military_hour.should == 1
end
it "parses 11:30 PM" do
clock = AnalogClock.new "11:30 PM"
clock.should be_valid
clock.hour.should == 11
clock.min.should == 30
clock.meridiem.should == :pm
clock.military_hour.should == 23
end
it "parses 12:00 AM" do
clock = AnalogClock.new "12:00 AM"
clock.should be_valid
clock.hour.should == 12
clock.min.should == 0
clock.meridiem.should == :am
clock.military_hour.should == 0
end
it "parses 12:00 PM" do
clock = AnalogClock.new "12:00 PM"
clock.should be_valid
clock.hour.should == 12
clock.min.should == 0
clock.meridiem.should == :pm
clock.military_hour.should == 12
end
it "won't parse invalid times" do
AnalogClock.new(nil).should_not be_valid
AnalogClock.new("").should_not be_valid
AnalogClock.new("13:00 PM").should_not be_valid
AnalogClock.new("13:00 AM").should_not be_valid
AnalogClock.new("1:60 PM").should_not be_valid
AnalogClock.new("0:30 PM").should_not be_valid
AnalogClock.new("0013:00 AM").should_not be_valid
AnalogClock.new("1:1 AM").should_not be_valid
AnalogClock.new("a:00 AM").should_not be_valid
AnalogClock.new("1:ab AM").should_not be_valid
end
end
context "Comparing clocks" do
it "won't compare invalid clocks" do
c1 = AnalogClock.new "12:00 AM"
c2 = AnalogClock.new "12:00 ZZ"
c1.minutes_until(nil).should be_nil
c1.minutes_until("not a clock").should be_nil
c1.minutes_until(c2).should be_nil
c2.minutes_until(c1).should be_nil
end
it "can compare identical clocks" do
clock = AnalogClock.new "12:00 AM"
clock.minutes_until(clock).should == 0
end
it "compares ordered clocks of same meridiem" do
c1 = AnalogClock.new "12:15 AM"
c2 = AnalogClock.new "1:30 AM"
c1.minutes_until(c2).should == 75
c2 = AnalogClock.new "1:00 AM"
c1.minutes_until(c2).should == 45
end
it "compares out-of-order clocks of same meridiem" do
c1 = AnalogClock.new "2:30 AM"
c2 = AnalogClock.new "12:00 AM"
c1.minutes_until(c2).should == (21*60+30) # 21 hrs 30 min
c3 = AnalogClock.new "2:29 AM"
c1.minutes_until(c3).should == 23*60+59 # 23 hours 59 min
end
it "compares ordered clocks of different meridiems" do
c1 = AnalogClock.new "11:59 AM"
c2 = AnalogClock.new "12:00 PM"
c1.minutes_until(c2).should == 1
end
it "compares out-of-order clocks of different meridiems" do
c1 = AnalogClock.new "12:45 PM"
c2 = AnalogClock.new "12:15 AM"
c1.minutes_until(c2).should == (11*60+30) # 11 hr 30 min
end
end
context "Minute converter" do
it "converts minutes to rotations" do
AnalogClock.to_rotations(nil).should == nil
AnalogClock.to_rotations(0).should == 0
AnalogClock.to_rotations(60).should == 1
AnalogClock.to_rotations(90).should == 1.5
AnalogClock.to_rotations(23*60+59).should == (23+59/60.0)
end
it "converts minutes to degrees" do
AnalogClock.to_degrees(nil).should == nil
AnalogClock.to_degrees(0).should == 0
AnalogClock.to_degrees(1).should == 6
AnalogClock.to_degrees(90).should == 540
AnalogClock.to_degrees(23*60+59).should == (24*360-6)
end
end
end
# $ rspec -c analog_clock_spec.rb
# ..............
#
# Finished in 0.00411 seconds
# 13 examples, 0 failures
require_relative 'analog_clock'
def run
clock1 = clock2 = nil
until clock1 and clock1.valid? do
puts 'Enter first time using format "[H]H:MM AM":'
clock1 = AnalogClock.new gets.chomp
end
until clock2 and clock2.valid? do
puts 'Enter second time using format "[H]H:MM AM":'
clock2 = AnalogClock.new gets.chomp
end
minutes = clock1.minutes_until(clock2)
degrees = AnalogClock.to_degrees(minutes)
rotations = AnalogClock.to_rotations(minutes)
puts "Minute hand must travel #{degrees} degrees (#{rotations} rotations)."
end
run
# $ ruby analog_clock.rb
# Enter first time using format "[H]H:MM AM":
# 10:15 AM
# Enter second time using format "[H]H:MM AM":
# 12:45 PM
# Minute hand must travel 900 degrees (2.5 rotations).
# $ ruby analog_clock.rb
# Enter first time using format "[H]H:MM AM":
# 10:00 PM
# Enter second time using format "[H]H:MM AM":
# 9:00 PM
# Minute hand must travel 8280 degrees (23.0 rotations).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment