diff --git a/docs/user_stories.md b/docs/user_stories.md new file mode 100644 index 0000000000..f71547fd45 --- /dev/null +++ b/docs/user_stories.md @@ -0,0 +1,145 @@ +# User story 1 +As an air traffic controller +So I can get passengers to a destination +I want to instruct a plane to land at an airport + +# Nouns +air traffic controller, passengers, destination, plane, airport + +# Verbs +get, instruct, land + +# Functional representation +Objects: +- plane +- airport + +Messages: +- land + +# Diagram +Airport <--land--> Plane + + +################################## +# User story 2 +As an air traffic controller +So I can get passengers on the way to their destination +I want to instruct a plane to take off from an airport and confirm that it is no longer in the airport + +# Nouns +air traffic controller, passengers, destination, plane, airport + +# Verbs +get, instruct, take off, confirm + +# Functional representation +Objects: +- Airport +- Plane + +Messages: +- take_off +- left_airport? + +# Diagrams +Airport <--take_off--> Plane + +Plane <--left_airport?--> true + + +################################## +# User story 3 +As an air traffic controller +To ensure safety +I want to prevent landing when the airport is full + +# Nouns +air traffic controller, safety, airport, full + +# Verbs +ensure, prevent, landing + +# Functional representation +Objects: +- Airport +- Plane + +Messages: +- landing +- full? + +# Diagrams +Airport <--landing--> Plane + +Airport <--full?--> true/false + + +################################## +# User story 4 +As the system designer +So that the software can be used for many different airports +I would like a default airport capacity that can be overridden as appropriate + +# Functional representation +Objects: +- Airport + +Messages: +- set_capacity + +Airport <--set_capacity--> num + + +################################## +# User story 5 +As an air traffic controller +To ensure safety +I want to prevent takeoff when weather is stormy + +# Functional representation +Objects: +- Airport +- Weather +- Plane + +Messages: +- prevent_take_off + +# Diagrams +Airport <--take_off--> Plane +Weather <--stormy?--> ture/false + + +################################## +# User story 6 +As an air traffic controller +To ensure safety +I want to prevent landing when weather is stormy + +# Functional representation +Objects: +- Airport +- Weather +- Plane + +Messages: +- prevent_landing + +# Diagrams +Airport <--land--> Plane +Weather <--stormy?--> true/false + + +################################## + +# Edge cases +- planes can only take off from airports they are in; +# done +- planes that are already flying cannot take off and/or be in an airport; + (when plane lands left_airport? should be false) +# done +- planes that are landed cannot land again and must be in an airport +# done + +################################## \ No newline at end of file diff --git a/lib/airport.rb b/lib/airport.rb new file mode 100644 index 0000000000..b0d05159b1 --- /dev/null +++ b/lib/airport.rb @@ -0,0 +1,42 @@ +require_relative 'plane' +require_relative 'weather' + +class Airport + + DEFAULT_CAPACITY = 5 + + attr_reader :planes, :capacity + + def initialize(capacity=DEFAULT_CAPACITY) + @planes = [] + @capacity = capacity + end + + def land(plane) + fail 'Airport full' if full? + fail 'Landing not allowed due to adverse weather' if stormy? + fail 'Plane already landed' if @planes.include?(plane) + plane.allowed_land + @planes << plane + plane + end + + def take_off(plane) + fail 'Plane not in the airport' unless @planes.include?(plane) + fail 'Take-off not allowed due to adverse weather' if stormy? + plane.allowed_take_off + @planes.delete(plane) + end + + private + + def full? + @planes.length >= @capacity + end + + def stormy? + weather = Weather.stormy? + weather + end + +end \ No newline at end of file diff --git a/lib/plane.rb b/lib/plane.rb new file mode 100644 index 0000000000..43595e52b3 --- /dev/null +++ b/lib/plane.rb @@ -0,0 +1,19 @@ +class Plane + + def initialize + @left_airport = true + end + + def allowed_take_off + @left_airport = true + end + + def left_airport? + @left_airport + end + + def allowed_land + @left_airport = false + end + +end \ No newline at end of file diff --git a/lib/weather.rb b/lib/weather.rb new file mode 100644 index 0000000000..e634c9b795 --- /dev/null +++ b/lib/weather.rb @@ -0,0 +1,10 @@ +class Weather + + WEATHER = [:sunny, :rainy, :stormy, :windy] + + def self.stormy? + current_weather = WEATHER.sample + current_weather == :stormy + end + +end \ No newline at end of file diff --git a/spec/airport_spec.rb b/spec/airport_spec.rb new file mode 100644 index 0000000000..4182e8ea59 --- /dev/null +++ b/spec/airport_spec.rb @@ -0,0 +1,133 @@ +require './lib/airport' +require './lib/weather' + +describe Airport do + + # Capacity + describe '#capacity' do + + it 'has a default value' do + expect(subject.capacity).to eq Airport::DEFAULT_CAPACITY + end + + it 'can be set to a specific value' do + airport = Airport.new(10) + expect(airport.capacity).to eq 10 + end + + end + + # Lands a plane + describe '#land' do + + it 'airport responds to the method' do + expect(subject).to respond_to :land + end + + it 'accepts one argument' do + expect(subject).to respond_to(:land).with(1).argument + end + + it 'lands a plane' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = double (:plane) + allow(plane).to receive(:allowed_land) + expect(subject.land(plane)).to eq plane + end + + it 'rasies an error when airport is full' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = double (:plane) + allow(plane).to receive(:allowed_land) + subject.capacity.times {subject.land(Plane.new)} + expect {subject.land(plane)}.to raise_error 'Airport full' + end + + it 'raises an error when the weather is stormy' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(true) + + plane = double (:plane) + allow(plane).to receive(:allowed_land) + expect{ subject.land(plane) }.to raise_error 'Landing not allowed due to adverse weather' + end + + it 'raises an error when the plane has already landed' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = double (:plane) + allow(plane).to receive(:allowed_land) + subject.land(plane) + expect{ subject.land(plane) }.to raise_error 'Plane already landed' + end + + it 'changes left_airport? to false' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = Plane.new + expect(subject.land(plane)).not_to be_left_airport + end + + end + + # Takes off a plane + describe '#take_off' do + + it 'airport responds to the method' do + expect(subject).to respond_to :take_off + end + + it 'accepts one argument' do + expect(subject).to respond_to(:take_off).with(1).argument + end + + it 'raises an error when the weather is stormy' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = double (:plane) + allow(plane).to receive_messages(:allowed_land => nil, :allowed_take_off => nil) + subject.land(plane) + + allow(weather).to receive(:stormy?).and_return(true) + expect{ subject.take_off(plane) }.to raise_error 'Take-off not allowed due to adverse weather' + end + + it 'takes-off the plane when the weather is NOT stormy' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = double (:plane) + allow(plane).to receive_messages(:allowed_land => nil, :allowed_take_off => nil) + subject.land(plane) + expect(subject.take_off(plane)).to eq plane + end + + it 'confirms the plane has left_airport?' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = Plane.new + subject.land(plane) + #expect(plane.left_airport?).to eq true + expect(subject.take_off(plane)).to be_left_airport + end + + it 'raises an error when the plane is not in the airport' do + weather = class_double('Weather').as_stubbed_const + allow(weather).to receive(:stormy?).and_return(false) + + plane = double (:plane) + allow(plane).to receive_messages(:allowed_land => nil, :allowed_take_off => nil) + expect{subject.take_off(plane)}.to raise_error 'Plane not in the airport' + end + + end + +end \ No newline at end of file diff --git a/spec/plane_spec.rb b/spec/plane_spec.rb new file mode 100644 index 0000000000..ad79fc8153 --- /dev/null +++ b/spec/plane_spec.rb @@ -0,0 +1,27 @@ +require 'plane' + +describe Plane do + + it 'responds to allowed_take_off method' do + expect(subject).to respond_to :allowed_take_off + end + + it 'responds to _left_airport? method' do + expect(subject).to respond_to :left_airport? + end + + it 'after allowed_take_off returns true for left_airport?' do + subject.allowed_take_off + expect(subject).to be_left_airport + end + + it 'responds to allowed_land method' do + expect(subject).to respond_to :allowed_land + end + + it 'after allowed_land returns false for left_airport' do + subject.allowed_land + expect(subject).not_to be_left_airport + end + +end \ No newline at end of file diff --git a/spec/weather_spec.rb b/spec/weather_spec.rb new file mode 100644 index 0000000000..ef8e2c2bf1 --- /dev/null +++ b/spec/weather_spec.rb @@ -0,0 +1,17 @@ +require 'weather' + +describe Weather do + + it 'initializes with a weather constant' do + expect(Weather::WEATHER).to eq [:sunny, :rainy, :stormy, :windy] + end + + it 'responds to stormy? method' do + expect(Weather).to respond_to :stormy? + end + + xit 'responds with true or false to stormy?' do #could not make it work with a boolean + expect(Weather.stormy?).to be_in([true, false]) + end + +end \ No newline at end of file