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
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.2.5)
rspec (3.1.0)
Expand All @@ -19,3 +20,6 @@ PLATFORMS

DEPENDENCIES
rspec

BUNDLED WITH
1.15.0
63 changes: 49 additions & 14 deletions lib/tree.rb
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. One thing you'll see are custom errors. Something like...

class Tree
  class AgeError < StandardError; end

  def initialize
    `raise AgeError.new(msg)`
  end
end

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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?
[email protected]?
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

Expand All @@ -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"
Expand Down
169 changes: 165 additions & 4 deletions spec/tree_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

end

it 'starts out with no apples' do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the story approach of your tests. Keep in mind using contexts to improve readability.

www.betterspecs.org/#contexts

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