diff --git a/Gemfile.lock b/Gemfile.lock index 90f2f48..e30b139 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,4 +1,5 @@ GEM + remote: https://rubygems.org/ specs: diff-lcs (1.2.5) rspec (3.1.0) @@ -19,3 +20,6 @@ PLATFORMS DEPENDENCIES rspec + +BUNDLED WITH + 1.15.0 diff --git a/lib/tree.rb b/lib/tree.rb index 6c54019..b427888 100644 --- a/lib/tree.rb +++ b/lib/tree.rb @@ -1,38 +1,78 @@ class NoApplesError < StandardError; end -class AppleTree - attr_#fill_in :height, :age, :apples, :alive +class Tree + attr_reader :height, :age, :apples, :alive - def initialize + def initialize(max_possible_age=10, max_growth: 3, apple_color: 'orange') + raise ArgumentError, 'Ages must be positive and non-zero' if max_possible_age <= 0 + raise ArgumentError, 'Growth rates may only be positive and non-zero' if max_growth <= 0 + + @height = 0 + + @age = 0 + @max_age = rand(1..max_possible_age) + @max_growth = max_growth + + @apples = [] + @apple_color = apple_color + @alive = true end def age! + @age += 1 + + if @age > @max_age + @alive = false + else + @height += rand(1..@max_growth) + add_apples + end end def add_apples + rand(2..8).times { @apples << make_apple } end def any_apples? + !@apples.empty? end def pick_an_apple! - raise NoApplesError, "This tree has no apples" unless self.any_apples? + raise NoApplesError, 'This tree has no apples' unless self.any_apples? + @apples.pop end def dead? + !@alive + end + + private + def make_apple + Apple.new(@apple_color, rand(2..6)) end + end class Fruit + attr_reader :has_seeds + def initialize - has_seeds = true + @has_seeds = true end end -class Apple < - attr_reader #what should go here +class Apple < Fruit + attr_reader :color, :diameter def initialize(color, diameter) + raise ArgumentError, 'An apple must have a color' if color.nil? + raise ArgumentError, 'Diameter of an apple may not be negative or zero' if diameter <= 0 + raise ArgumentError, 'Diameter must be finite' if diameter.is_a?(Float) && diameter.infinite? + + super() + + @color = color + @diameter = diameter end end @@ -55,13 +95,8 @@ def tree_data basket << tree.pick_an_apple! end - diameter_sum = 0 - - basket.each do |apple| - diameter_sum += apple.diameter - end - - avg_diameter = # It's up to you to calculate the average diameter for this harvest. + diameter_sum = basket.map(&:diameter).reduce :+ + avg_diameter = diameter_sum / basket.length puts "Year #{tree.age} Report" puts "Tree height: #{tree.height} feet" diff --git a/spec/tree_spec.rb b/spec/tree_spec.rb index 99c9184..c281b1a 100644 --- a/spec/tree_spec.rb +++ b/spec/tree_spec.rb @@ -1,14 +1,175 @@ require 'rspec' require 'tree' -describe 'Tree' do +describe Tree do + let(:tree) { Tree.new } + it 'should be a Class' do - expect(described_class.is_a? 'Class').to be_true + expect(described_class).to be_a(Class) + end + + it 'can age to a positive, non-zero maximum' do + expect { Tree.new(-1) }.to raise_error(ArgumentError) + expect { Tree.new(0) }.to raise_error(ArgumentError) + + expect { Tree.new(1) }.not_to raise_error + expect { Tree.new }.not_to raise_error + end + + it 'can grow by a positive, non-zero rate' do + expect { Tree.new(max_growth: -1) }.to raise_error(ArgumentError) + expect { Tree.new(max_growth: 0) }.to raise_error(ArgumentError) + + expect { Tree.new(max_growth: 1) }.not_to raise_error + expect { Tree.new }.not_to raise_error + end + + it 'starts out alive' do + expect(tree).not_to be_dead + end + + it 'starts out with no apples' do + expect(tree.apples).to be_empty + end + + it 'starts at age 0 with height 0 and no apples' do + expect(tree.age).to eq 0 + expect(tree.height).to eq 0 + end + + it 'ages a year at a time' do + tree.age! + expect(tree.age).to eq 1 + + 3.times { tree.age! } + expect(tree.age).to eq 4 + end + + it 'grows in height when it ages' do + tree.age! + expect(tree.height).to be > 0 + end + + it 'grows apples when it ages' do + tree.age! + expect(tree).to be_any_apples + end + + it 'grows apples of a particular color' do + tree = Tree.new(apple_color: 'red') + tree.age! until tree.apples.length > 1 + + apple_colors = tree.apples.map &:color + expect(apple_colors).to all( eq 'red' ) + end + + it 'dies at a max age' do + tree = Tree.new 20 + + expect(tree).not_to be_dead + + # If tree is not dead after 21 iterations then the + # tree is undying and that's probably impossible in reality.ss + until tree.dead? or tree.age > 20 + tree.age! + end + + expect(tree).to be_dead + end + + it 'should continue to age in death' do + tree.age! until tree.dead? + + start_age = tree.age + tree.age! + + expect(tree.age - start_age).to eq 1 + + start_age = tree.age + 20.times { tree.age! } + + expect(tree.age - start_age).to eq 20 + end + + it 'does not grow once dead' do + tree.age! until tree.dead? + + start_height = tree.height + tree.age! + expect(tree.height - start_height).to be_zero + end + + it 'does not bear fruit once dead' do + tree.age! until tree.dead? + tree.pick_an_apple! while tree.any_apples? + + tree.age! + expect(tree.apples).to be_empty + end + + it 'provides an apple, if it has any' do + tree.age! + + apple = tree.pick_an_apple! + expect(apple).not_to be nil + expect(apple).to be_instance_of(Apple) + end + + it 'throws a NoApplesError when picking apples if none exists' do + expect { tree.pick_an_apple! }.to raise_error(NoApplesError) + + tree.age! + tree.pick_an_apple! while tree.any_apples? + + expect { tree.pick_an_apple! }.to raise_error(NoApplesError) + end + + it 'can magically have apples added to it. By magic.' do + tree.add_apples + expect(tree.apples).not_to be_empty end end -describe 'Fruit' do +describe Fruit do + let(:fruit) { Fruit.new } + + it 'is a Class' do + expect(described_class).to be_a(Class) + end + + it 'has seeds' do + expect(fruit.has_seeds).to be true + end end -describe 'Apple' do +describe Apple do + let(:apple) { Apple.new('international orange', rand(3..10)) } + + it 'is a Class' do + expect(described_class).to be_a(Class) + end + + it 'is a subclass of Fruit' do + expect(described_class).to be < Fruit + end + + it 'has seeds' do + expect(apple.has_seeds).to be true + end + + it 'has a color' do + expect { Apple.new(nil, 2) }.to raise_error(ArgumentError) + + expect(apple.color).not_to be_nil + expect(apple.color).to eq 'international orange' + end + + it 'has a finite, positive, non-zero diameter' do + expect_failure = lambda { |d| expect { Apple.new('red', d) }.to raise_error(ArgumentError) } + expect_success = lambda { |d| expect { Apple.new('red', d) }.not_to raise_error } + + [Float::INFINITY, -Float::INFINITY].each &expect_failure + [-1, 0].each &expect_failure + [1, 4, 2304].each &expect_success + end end