From bff96e2fb5206dbe0b8ef175d5b15f43c1c427f3 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Mon, 5 Mar 2018 12:32:02 -0800 Subject: [PATCH 01/25] Created scaffolding for initial design concept --- Rakefile | 9 +++++++++ lib/admin.rb | 20 ++++++++++++++++++++ lib/reservation.rb | 5 +++++ lib/room.rb | 5 +++++ specs/admin_spec.rb | 5 +++++ specs/reservation_spec.rb | 5 +++++ specs/room_spec.rb | 5 +++++ specs/spec_helper.rb | 12 ++++++++++++ 8 files changed, 66 insertions(+) create mode 100644 Rakefile create mode 100644 lib/admin.rb create mode 100644 lib/reservation.rb create mode 100644 lib/room.rb create mode 100644 specs/admin_spec.rb create mode 100644 specs/reservation_spec.rb create mode 100644 specs/room_spec.rb create mode 100644 specs/spec_helper.rb diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..deb52f2cd --- /dev/null +++ b/Rakefile @@ -0,0 +1,9 @@ +require 'rake/testtask' + +Rake::TestTask.new do |t| + t.libs = ["lib"] + t.warning = true + t.test_files = FileList['specs/*_spec.rb'] +end + +task default: :test diff --git a/lib/admin.rb b/lib/admin.rb new file mode 100644 index 000000000..4eee83b46 --- /dev/null +++ b/lib/admin.rb @@ -0,0 +1,20 @@ +require_relative 'room' +require_relative 'reservation' + +module Hotel + class Admin + attr_reader :rooms, :reservations + + def initialize + @rooms = load_rooms + @reservations = load_reservations + end + + def load_rooms + end + + def load_reservations + end + + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..b6d662ac9 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,5 @@ +module Hotel + class Reservation + + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..9e44dbb8d --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,5 @@ +module Hotel + class Room + + end +end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb new file mode 100644 index 000000000..25ee13db1 --- /dev/null +++ b/specs/admin_spec.rb @@ -0,0 +1,5 @@ +require_relative 'spec_helper' + +describe 'Admin class' do + +end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb new file mode 100644 index 000000000..a62cd3d04 --- /dev/null +++ b/specs/reservation_spec.rb @@ -0,0 +1,5 @@ +require_relative 'spec_helper' + +describe 'Reservation class' do + +end diff --git a/specs/room_spec.rb b/specs/room_spec.rb new file mode 100644 index 000000000..bc3f9f34b --- /dev/null +++ b/specs/room_spec.rb @@ -0,0 +1,5 @@ +require_relative 'spec_helper' + +describe 'Room class' do + +end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 000000000..867f5c5b0 --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,12 @@ +require 'simplecov' +SimpleCov.start + +require 'minitest' +require 'minitest/autorun' +require 'minitest/reporters' + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +require_relative '../lib/admin' +require_relative '../lib/reservation' +require_relative '../lib/room' From a29fe47b2197940b61476089c33b5029ce0fa352 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Mon, 5 Mar 2018 16:20:39 -0800 Subject: [PATCH 02/25] Added Room#initialize and Room#add_reservation methods and tests. All tests passing. --- lib/admin.rb | 60 ++++++++++++++++++++++++++++++++++++++++++++-- lib/reservation.rb | 11 ++++++++- lib/room.rb | 21 ++++++++++++++++ specs/room_spec.rb | 51 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 4eee83b46..f5cf8bf08 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -1,3 +1,4 @@ +require 'date' require_relative 'room' require_relative 'reservation' @@ -7,13 +8,68 @@ class Admin def initialize @rooms = load_rooms - @reservations = load_reservations + @reservations = [] end def load_rooms + @rooms = [] + (1..20).times do |num| + @rooms << Room.new(num) + end + + return @rooms + end + + def reserve_room(start_date, end_date) # Dates must be entered as strings + reservation_data = { + id: next_reservation_id, + start_date: Date.parse(start_date), + end_date: Date.parse(end_date), + room: find_available_rooms(start_date, end_date).first + } + + new_reservation = Reservation.new(reservation_data) + + # TODO: Add reservation to assigned room + new_reservation.room.add_reservation(new_reservation) + + return reservation + + end + + def next_reservation_id + if @reservations.empty? + reservation_id = 1 + else + reservation_id = @reservations.map { |res| res.id }.max + 1 + end + + return reservation_id end - def load_reservations + def find_available_rooms(start_date, end_date) + available_rooms = [] + + reservation_range = (Date.parse(start_date)..Date.parse(end_date)-1).to_a + + @rooms.each do |room| + + conflicts = [] + + room.reservations.each do |reservation| + booked_range = (start_date..end_date - 1).to_a + intersection = reservation_range & booked_range + unless intersection.empty? + conflicts << intersection + end + end + + if conflicts.empty? + available_rooms << room + end + end + + return available_rooms end end diff --git a/lib/reservation.rb b/lib/reservation.rb index b6d662ac9..8e2a59069 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -1,5 +1,14 @@ module Hotel class Reservation - + + attr_reader :id, :start_date, :end_date, :room + + def initialize(input) + @id = input[:id] + @start_date = input[:start_date] + @end_date = input[:end_date] + @room = input[:room] + end + end end diff --git a/lib/room.rb b/lib/room.rb index 9e44dbb8d..49775f108 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -1,5 +1,26 @@ module Hotel class Room + COST_PER_NIGHT = 200 + + attr_reader :number, :reservations + + def initialize(num) + + if num < 1 || num > 20 + raise ArgumentError.new("Room number must be between 1 and 20") + end + + @number = num + @reservations = [] + end + + def add_reservation(reservation) + if reservation.class != Reservation + raise ArgumentError.new("Input is not an instance of Reservation") + end + + @reservations << reservation + end end end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index bc3f9f34b..5c9f89a61 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -2,4 +2,55 @@ describe 'Room class' do + describe 'initialize method' do + before do + @room = Hotel::Room.new(1) + end + + it 'creates an instance of Hotel' do + @room.must_be_instance_of Hotel::Room + @room.must_respond_to :number + @room.must_respond_to :reservations + end + + it 'has a number' do + @room.number.must_be_kind_of Integer + end + + it 'throws an error if the room number is not between 1 and 20' do + proc { Hotel::Room.new(0) }.must_raise ArgumentError + proc { Hotel::Room.new(21) }.must_raise ArgumentError + end + + it 'creates an empty array of reservations' do + @room.reservations.must_be_kind_of Array + @room.reservations.length.must_equal 0 + end + end + + describe 'add_reservation method' do + before do + @room = Hotel::Room.new(1) + reservation_data = { + id: 100, + start_date: Date.parse("Dec 31, 1999"), + end_date: Date.parse("Jan 2, 2000"), + room: @room + } + @reservation = Hotel::Reservation.new(reservation_data) + end + + it 'throws an error if input is not an instance of Reservation' do + proc { @room.add_reservation(100) }.must_raise ArgumentError + end + + it 'returns an array of reservations' do + @room.add_reservation(@reservation).must_be_kind_of Array + @room.reservations.length.must_equal 1 + @room.reservations.each do |reservation| + reservation.must_be_instance_of Hotel::Reservation + end + end + + end end From 7cd30d362ac57d94f6720e961a7f3f23f10eb9aa Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Wed, 7 Mar 2018 15:40:48 -0800 Subject: [PATCH 03/25] Added Room#is_available? method. All Room tests passing --- lib/admin.rb | 59 ++++++++++++++++++++++-------------- lib/reservation.rb | 14 +++++++++ lib/room.rb | 12 +++++++- specs/reservation_spec.rb | 64 +++++++++++++++++++++++++++++++++++++++ specs/room_spec.rb | 34 +++++++++++++++++++++ 5 files changed, 160 insertions(+), 23 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index f5cf8bf08..85dc6b14f 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -12,12 +12,12 @@ def initialize end def load_rooms - @rooms = [] + rooms = [] (1..20).times do |num| - @rooms << Room.new(num) + rooms << Room.new(num) end - return @rooms + return rooms end def reserve_room(start_date, end_date) # Dates must be entered as strings @@ -47,30 +47,45 @@ def next_reservation_id return reservation_id end - def find_available_rooms(start_date, end_date) - available_rooms = [] + def find_reservations(date) + reservations_by_date = [] - reservation_range = (Date.parse(start_date)..Date.parse(end_date)-1).to_a - - @rooms.each do |room| - - conflicts = [] - - room.reservations.each do |reservation| - booked_range = (start_date..end_date - 1).to_a - intersection = reservation_range & booked_range - unless intersection.empty? - conflicts << intersection - end - end - - if conflicts.empty? - available_rooms << room + @reservations.each do |res| + reservation_dates = (res.start_date...res.end_date).to_a + if reservation_dates.include?(date) + reservations_by_date << res end end - return available_rooms + return reservations_by_date end + + # def find_available_rooms(start_date, end_date) + # available_rooms = [] + # + # reservation_range = (Date.parse(start_date)..Date.parse(end_date)-1).to_a + # + # @rooms.each do |room| + # + # conflicts = false + # + # room.reservations.each do |reservation| + # booked_range = (start_date..end_date - 1).to_a + # intersection = reservation_range & booked_range + # unless intersection.empty? + # conflicts = true + # break + # end + # end + # + # if conflicts == false + # available_rooms << room + # end + # end + # + # return available_rooms + # end + end end diff --git a/lib/reservation.rb b/lib/reservation.rb index 8e2a59069..4b3c64f13 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -3,12 +3,26 @@ class Reservation attr_reader :id, :start_date, :end_date, :room + COST_PER_NIGHT = 200 + def initialize(input) @id = input[:id] @start_date = input[:start_date] @end_date = input[:end_date] @room = input[:room] + + if @end_date <= @start_date + raise StandardError.new("End date of reservation must be at least one day later than starting date") + end end + def projected_cost + # exclusive range does not include end date, + # count returns an Integer rather than the Rational returned by subtraction of two dates + nights = (@start_date...@end_date).count + projected_cost = nights * COST_PER_NIGHT +# + return projected_cost + end end end diff --git a/lib/room.rb b/lib/room.rb index 49775f108..b52692d13 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -17,10 +17,20 @@ def initialize(num) def add_reservation(reservation) if reservation.class != Reservation - raise ArgumentError.new("Input is not an instance of Reservation") + raise ArgumentError.new("Input must be an instance of Reservation") end @reservations << reservation end + + def is_available?(date) + @reservations.each do |res| + date_range = (res.start_date...res.end_date).to_a + if date_range.include?(date) + return false + end + end + return true + end end end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb index a62cd3d04..c2b43332a 100644 --- a/specs/reservation_spec.rb +++ b/specs/reservation_spec.rb @@ -1,5 +1,69 @@ require_relative 'spec_helper' describe 'Reservation class' do + describe 'initialize method' do + before do + @room = Hotel::Room.new(1) + reservation_data = { + id: 100, + start_date: Date.parse("Dec 31, 1999"), + end_date: Date.parse("Jan 2, 2000"), + room: @room + } + @reservation = Hotel::Reservation.new(reservation_data) + end + it 'creates an instance of Reservation' do + @reservation.must_be_instance_of Hotel::Reservation + + [:id, :start_date, :end_date, :room].each do |prop| + @reservation.must_respond_to prop + end + + @reservation.id.must_be_kind_of Integer + @reservation.start_date.must_be_kind_of Date + @reservation.end_date.must_be_kind_of Date + @reservation.room.must_be_instance_of Hotel::Room + end + + it 'throws an error if start date is later than end date' do + room = Hotel::Room.new(1) + reservation_data = { + id: 100, + start_date: Date.parse("Jan 2, 2000"), + end_date: Date.parse("Dec 31, 1999"), + room: room + } + + proc { Hotel::Reservation.new(reservation_data) }.must_raise StandardError + end + + it 'throws an error if start date is the same as end date' do + @room = Hotel::Room.new(1) + reservation_data = { + id: 100, + start_date: Date.parse("Jan 2, 2000"), + end_date: Date.parse("Jan 2, 2000"), + room: @room + } + + proc { Hotel::Reservation.new(reservation_data) }.must_raise StandardError + end + end + + describe 'projected_cost method' do + it 'calculates the projected cost of the reservation' do + room = Hotel::Room.new(1) + reservation_data = { + id: 100, + start_date: Date.parse("Dec 31, 1999"), + end_date: Date.parse("Jan 2, 2000"), + room: room + } + reservation = Hotel::Reservation.new(reservation_data) + + reservation.projected_cost.must_be_kind_of Integer + reservation.projected_cost.must_equal 400 + end + end end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index 5c9f89a61..39ea8433b 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -53,4 +53,38 @@ end end + + describe 'is_available? method' do + before do + @room = Hotel::Room.new(1) + reservation_data = { + id: 100, + start_date: Date.parse("Dec 31, 1999"), + end_date: Date.parse("Jan 2, 2000"), + room: @room + } + @reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(@reservation) + end + + it 'returns true if the given date is before the reservation' do + date = Date.parse("Dec 30, 1999") + @room.is_available?(date).must_equal true + end + + it 'returns true if the given date is after the reservation' do + date = Date.parse("Jan 3, 2000") + @room.is_available?(date).must_equal true + end + + it 'returns true if the given date is the check-out date of the reservation' do + date = Date.parse("Jan 2, 2000") + @room.is_available?(date).must_equal true + end + + it 'returns false if the given date overlaps the reservation date range' do + date = Date.parse("Jan 1, 2000") + @room.is_available?(date).must_equal false + end + end end From 9c0faf2d438db299764ed3fd970a81d2dcebff53 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Wed, 7 Mar 2018 16:03:37 -0800 Subject: [PATCH 04/25] Added tests for initializing Admin class --- lib/admin.rb | 4 ++-- lib/room.rb | 10 ++++++---- specs/admin_spec.rb | 20 ++++++++++++++++++++ specs/room_spec.rb | 4 ++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 85dc6b14f..8c27c09f8 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -1,4 +1,4 @@ -require 'date' +# require 'date' require_relative 'room' require_relative 'reservation' @@ -13,7 +13,7 @@ def initialize def load_rooms rooms = [] - (1..20).times do |num| + (1..20).each do |num| rooms << Room.new(num) end diff --git a/lib/room.rb b/lib/room.rb index b52692d13..cc42a2aac 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -24,11 +24,13 @@ def add_reservation(reservation) end def is_available?(date) + if date.class != Date + raise ArgumentError.new("Input must be an instance of Date class") + end + @reservations.each do |res| - date_range = (res.start_date...res.end_date).to_a - if date_range.include?(date) - return false - end + reservation_range = (res.start_date...res.end_date).to_a + return false if reservation_range.include?(date) end return true end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 25ee13db1..a86ec7ac1 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -2,4 +2,24 @@ describe 'Admin class' do + before do + @hotel = Hotel::Admin.new + end + + it 'can be created' do + @hotel.must_be_instance_of Hotel::Admin + @hotel.must_respond_to :rooms + @hotel.must_respond_to :reservations + + @hotel.rooms.must_be_kind_of Array + @hotel.rooms.length.must_equal 20 + @hotel.rooms.each do |room| + room.must_be_instance_of Hotel::Room + end + + @hotel.reservations.must_be_kind_of Array + @hotel.reservations.must_be_empty + end + + end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index 39ea8433b..d3d5e7dcb 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -86,5 +86,9 @@ date = Date.parse("Jan 1, 2000") @room.is_available?(date).must_equal false end + + it 'throws an error if the input is not a date' do + proc { @room.is_available?(1) }.must_raise ArgumentError + end end end From e0d2c8adb11c8681d627a86ef6b2426bf79c63f6 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Wed, 7 Mar 2018 16:45:42 -0800 Subject: [PATCH 05/25] Added tests for Admin#reserve method. --- lib/admin.rb | 5 ++-- specs/admin_spec.rb | 61 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 8c27c09f8..5f76b89eb 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -25,15 +25,14 @@ def reserve_room(start_date, end_date) # Dates must be entered as strings id: next_reservation_id, start_date: Date.parse(start_date), end_date: Date.parse(end_date), - room: find_available_rooms(start_date, end_date).first + room: @rooms.sample # change later to find available rooms } new_reservation = Reservation.new(reservation_data) - # TODO: Add reservation to assigned room new_reservation.room.add_reservation(new_reservation) - return reservation + return new_reservation end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index a86ec7ac1..dbefe8f41 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -1,25 +1,60 @@ require_relative 'spec_helper' describe 'Admin class' do + describe 'initialize method' do + before do + @hotel = Hotel::Admin.new + end + + it 'can be created' do + @hotel.must_be_instance_of Hotel::Admin + @hotel.must_respond_to :rooms + @hotel.must_respond_to :reservations + end - before do - @hotel = Hotel::Admin.new + it 'includes an array of rooms' do + @hotel.rooms.must_be_kind_of Array + @hotel.rooms.length.must_equal 20 + @hotel.rooms.each do |room| + room.must_be_instance_of Hotel::Room + end + end + + it 'includes an empty array of reservations' do + @hotel.reservations.must_be_kind_of Array + @hotel.reservations.must_be_empty + end end - it 'can be created' do - @hotel.must_be_instance_of Hotel::Admin - @hotel.must_respond_to :rooms - @hotel.must_respond_to :reservations + describe 'reserve_room method' do + before do + @hotel = Hotel::Admin.new + @reservation = @hotel.reserve_room("Dec 31, 1999", "Jan 2, 2000") + end + + it 'creates a new reservation' do + @reservation.must_be_instance_of Hotel::Reservation - @hotel.rooms.must_be_kind_of Array - @hotel.rooms.length.must_equal 20 - @hotel.rooms.each do |room| - room.must_be_instance_of Hotel::Room + [:id, :start_date, :end_date, :room].each do |prop| + @reservation.must_respond_to prop + end end - @hotel.reservations.must_be_kind_of Array - @hotel.reservations.must_be_empty - end + it 'accurately loads reservation data' do + @reservation.id.must_be_kind_of Integer + @reservation.id.must_equal 1 + @reservation.start_date.must_be_kind_of Date + @reservation.start_date.must_equal Date.new(1999,12,31) + @reservation.end_date.must_be_kind_of Date + @reservation.end_date.must_equal Date.new(2000,1,2) + @reservation.room.must_be_instance_of Hotel::Room + end + it 'associates the reservation with a room' do + assigned_room = @reservation.room + assigned_room.must_be_instance_of Hotel::Room + assigned_room.reservations.must_include @reservation + end + end end From 3a54f06fddc00ae0d9be8073bbaf7eb4794d4739 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Wed, 7 Mar 2018 17:22:48 -0800 Subject: [PATCH 06/25] Added tests for Admin#find_reservations method --- lib/admin.rb | 2 +- specs/admin_spec.rb | 49 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/admin.rb b/lib/admin.rb index 5f76b89eb..208edd8dd 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -29,7 +29,7 @@ def reserve_room(start_date, end_date) # Dates must be entered as strings } new_reservation = Reservation.new(reservation_data) - + @reservations << new_reservation new_reservation.room.add_reservation(new_reservation) return new_reservation diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index dbefe8f41..056c3e7b6 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -56,5 +56,54 @@ assigned_room.reservations.must_include @reservation end + it 'adds the reservation to the hotels list of reservations' do + @hotel.reservations.must_include @reservation + end + end + + describe 'next_reservation_id helper method' do + it 'assigns an id of 1 if there are no existing reservations' do + hotel = Hotel::Admin.new + hotel.next_reservation_id.must_equal 1 + end + + it 'creates a unique reservation id' do + hotel = Hotel::Admin.new + hotel.reserve_room("May 6, 1982", "May 10, 1982") + hotel.reserve_room("April 30, 1982", "May 1, 1982") + hotel.reserve_room("May 4, 1982", "May 5, 1982") + + res_ids = hotel.reservations.map { |reservation| reservation.id } + res_ids.uniq.length.must_equal 3 + end + end + + describe 'find_reservations method' do + before do + @hotel = Hotel::Admin.new + @reservation_1 = @hotel.reserve_room("May 6, 1982", "May 10, 1982") + @reservation_2 = @hotel.reserve_room("April 30, 1982", "May 1, 1982") + @reservation_3 = @hotel.reserve_room("May 5, 1982", "May 8, 1982") + end + + it 'returns an array of reservations' do + reservations = @hotel.find_reservations(Date.parse("May 6, 1982")) + reservations.must_be_kind_of Array + reservations.each do |reservation| + reservation.must_be_instance_of Hotel::Reservation + end + end + + it 'accurately accounts for the number of reservations that overlap the given date' do + reservations = @hotel.find_reservations(Date.parse("May 6, 1982")) + reservations.length.must_equal 2 + reservations.must_include @reservation_1 + reservations.must_include @reservation_3 + reservations.wont_include @reservation_2 + end + + it 'returns an empty array if the date does not overlap any reservations' do + @hotel.find_reservations(Date.parse("May 2, 1982")).must_equal [] + end end end From ccf130ced6d0e3454f418d87ded9237e0d7b60a8 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Thu, 8 Mar 2018 12:22:51 -0800 Subject: [PATCH 07/25] Added DateRange class and modified other classes to accept instance of DateRange --- lib/admin.rb | 14 +++++------ lib/daterange.rb | 29 ++++++++++++++++++++++ lib/reservation.rb | 13 +++------- lib/room.rb | 5 ++-- specs/admin_spec.rb | 52 ++++++++++++++++++++++++++++++--------- specs/daterange_spec.rb | 51 ++++++++++++++++++++++++++++++++++++++ specs/reservation_spec.rb | 40 ++++++------------------------ specs/room_spec.rb | 8 +++--- specs/spec_helper.rb | 1 + 9 files changed, 145 insertions(+), 68 deletions(-) create mode 100644 lib/daterange.rb create mode 100644 specs/daterange_spec.rb diff --git a/lib/admin.rb b/lib/admin.rb index 208edd8dd..db7cfc116 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -1,4 +1,4 @@ -# require 'date' +require_relative 'daterange' require_relative 'room' require_relative 'reservation' @@ -20,11 +20,10 @@ def load_rooms return rooms end - def reserve_room(start_date, end_date) # Dates must be entered as strings + def reserve_room(date_range) reservation_data = { id: next_reservation_id, - start_date: Date.parse(start_date), - end_date: Date.parse(end_date), + date_range: date_range, room: @rooms.sample # change later to find available rooms } @@ -49,10 +48,9 @@ def next_reservation_id def find_reservations(date) reservations_by_date = [] - @reservations.each do |res| - reservation_dates = (res.start_date...res.end_date).to_a - if reservation_dates.include?(date) - reservations_by_date << res + @reservations.each do |reservation| + if reservation.date_range.include?(date) + reservations_by_date << reservation end end diff --git a/lib/daterange.rb b/lib/daterange.rb new file mode 100644 index 000000000..84e941c0c --- /dev/null +++ b/lib/daterange.rb @@ -0,0 +1,29 @@ +require 'date' + +module Hotel + class DateRange + + attr_reader :start_date, :end_date, :date_range + + def initialize(start_date, end_date) + @start_date = Date.parse(start_date) + @end_date = Date.parse(end_date) + @range = range + + [@start_date, @end_date].each do |date| + if date.class != Date + raise StandardError.new("Invalid date format. Accepted formats include 'YYYY-MM-DD' or 'YYYY/MM/DD'.") + end + end + + if @end_date <= @start_date + raise StandardError.new("End date must be at least one day later than starting date.") + end + end + + def range + return (@start_date...@end_date).to_a + end + + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb index 4b3c64f13..8238c62bc 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -1,27 +1,22 @@ module Hotel class Reservation - attr_reader :id, :start_date, :end_date, :room + attr_reader :id, :date_range, :room COST_PER_NIGHT = 200 def initialize(input) @id = input[:id] - @start_date = input[:start_date] - @end_date = input[:end_date] + @date_range = input[:date_range] @room = input[:room] - - if @end_date <= @start_date - raise StandardError.new("End date of reservation must be at least one day later than starting date") - end end def projected_cost # exclusive range does not include end date, # count returns an Integer rather than the Rational returned by subtraction of two dates - nights = (@start_date...@end_date).count + nights = date_range.count projected_cost = nights * COST_PER_NIGHT -# +# return projected_cost end end diff --git a/lib/room.rb b/lib/room.rb index cc42a2aac..64d86e00c 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -28,9 +28,8 @@ def is_available?(date) raise ArgumentError.new("Input must be an instance of Date class") end - @reservations.each do |res| - reservation_range = (res.start_date...res.end_date).to_a - return false if reservation_range.include?(date) + @reservations.each do |reservation| + return false if reservation.date_range.include?(date) end return true end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 056c3e7b6..79a4f76c4 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -29,13 +29,14 @@ describe 'reserve_room method' do before do @hotel = Hotel::Admin.new - @reservation = @hotel.reserve_room("Dec 31, 1999", "Jan 2, 2000") + @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000").range + @reservation = @hotel.reserve_room(@date_range) end it 'creates a new reservation' do @reservation.must_be_instance_of Hotel::Reservation - [:id, :start_date, :end_date, :room].each do |prop| + [:id, :date_range, :room].each do |prop| @reservation.must_respond_to prop end end @@ -43,10 +44,10 @@ it 'accurately loads reservation data' do @reservation.id.must_be_kind_of Integer @reservation.id.must_equal 1 - @reservation.start_date.must_be_kind_of Date - @reservation.start_date.must_equal Date.new(1999,12,31) - @reservation.end_date.must_be_kind_of Date - @reservation.end_date.must_equal Date.new(2000,1,2) + @reservation.date_range.must_be_kind_of Array + @reservation.date_range.each do |date| + date.must_be_kind_of Date + end @reservation.room.must_be_instance_of Hotel::Room end @@ -69,9 +70,12 @@ it 'creates a unique reservation id' do hotel = Hotel::Admin.new - hotel.reserve_room("May 6, 1982", "May 10, 1982") - hotel.reserve_room("April 30, 1982", "May 1, 1982") - hotel.reserve_room("May 4, 1982", "May 5, 1982") + range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982").range + range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982").range + range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982").range + hotel.reserve_room(range_1) + hotel.reserve_room(range_2) + hotel.reserve_room(range_3) res_ids = hotel.reservations.map { |reservation| reservation.id } res_ids.uniq.length.must_equal 3 @@ -81,9 +85,12 @@ describe 'find_reservations method' do before do @hotel = Hotel::Admin.new - @reservation_1 = @hotel.reserve_room("May 6, 1982", "May 10, 1982") - @reservation_2 = @hotel.reserve_room("April 30, 1982", "May 1, 1982") - @reservation_3 = @hotel.reserve_room("May 5, 1982", "May 8, 1982") + range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982").range + range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982").range + range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982").range + @reservation_1 = @hotel.reserve_room(range_1) + @reservation_2 = @hotel.reserve_room(range_2) + @reservation_3 = @hotel.reserve_room(range_3) end it 'returns an array of reservations' do @@ -106,4 +113,25 @@ @hotel.find_reservations(Date.parse("May 2, 1982")).must_equal [] end end + + # describe 'find_available_rooms method' do + # before do + # @hotel = Hotel::Admin.new + # @hotel.reserve_room("May 6, 1982", "May 10, 1982") + # @hotel.reserve_room("May 5, 1982", "May 8, 1982") + # end + # + # it 'returns an array of rooms' do + # available_rooms = @hotel.find_reservations(Date.parse("May 4, 1982"),Date.parse("May 6, 1982")) + # available_rooms.must_be_kind_of Array + # available_rooms.each do |room| + # room.must_be_instance of Hotel::Room + # end + # end + # + # it 'accurately accounts for the number of available rooms' do + # available_rooms = @hotel.find_reservations(Date.parse("May 4, 1982"),Date.parse("May 6, 1982")) + # available_rooms.length.must_equal 19 + # end + # end end diff --git a/specs/daterange_spec.rb b/specs/daterange_spec.rb new file mode 100644 index 000000000..419b85ed0 --- /dev/null +++ b/specs/daterange_spec.rb @@ -0,0 +1,51 @@ +require_relative 'spec_helper' + +describe 'DateRange class' do + + describe 'initialize method' do + it 'can be created' do + date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') + date_range.must_be_instance_of Hotel::DateRange + [:start_date, :end_date, :date_range].each do |prop| + date_range.must_respond_to prop + end + end + + it 'throws an error if the date entry is an invalid format' do + proc { Hotel::DateRange.new('','') }.must_raise StandardError + # proc { Hotel::DateRange.new(4, 5) }.must_raise StandardError + # proc { Hotel::DateRange.new('33, 4') }.must_raise StandardError + end + + it 'throws an error if the starting and ending dates are the same' do + proc { Hotel::DateRange.new('2018-03-09', '2018-03-09') }.must_raise StandardError + end + + it 'throws an error if the ending date is earlier than the starting date' do + proc { Hotel::DateRange.new('2018-03-09', '2018-03-08') }.must_raise StandardError + end + end + + describe 'range method' do + before do + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') + end + + it 'returns an array of dates' do + @date_range.range.must_be_kind_of Array + @date_range.range.each do |date| + date.must_be_kind_of Date + end + end + + it 'includes the starting date and intermediate dates' do + @date_range.range.must_include @date_range.start_date + @date_range.range.must_include @date_range.start_date + 1 + @date_range.range.must_include @date_range.start_date + 2 + end + + it 'does not include the ending date' do + @date_range.range.wont_include @date_range.end_date + end + end +end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb index c2b43332a..e7004bfbd 100644 --- a/specs/reservation_spec.rb +++ b/specs/reservation_spec.rb @@ -4,10 +4,10 @@ describe 'initialize method' do before do @room = Hotel::Room.new(1) + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12').range reservation_data = { id: 100, - start_date: Date.parse("Dec 31, 1999"), - end_date: Date.parse("Jan 2, 2000"), + date_range: @date_range, room: @room } @reservation = Hotel::Reservation.new(reservation_data) @@ -16,54 +16,30 @@ it 'creates an instance of Reservation' do @reservation.must_be_instance_of Hotel::Reservation - [:id, :start_date, :end_date, :room].each do |prop| + [:id, :date_range, :room].each do |prop| @reservation.must_respond_to prop end @reservation.id.must_be_kind_of Integer - @reservation.start_date.must_be_kind_of Date - @reservation.end_date.must_be_kind_of Date + @reservation.date_range.must_be_kind_of Array @reservation.room.must_be_instance_of Hotel::Room end - - it 'throws an error if start date is later than end date' do - room = Hotel::Room.new(1) - reservation_data = { - id: 100, - start_date: Date.parse("Jan 2, 2000"), - end_date: Date.parse("Dec 31, 1999"), - room: room - } - - proc { Hotel::Reservation.new(reservation_data) }.must_raise StandardError - end - - it 'throws an error if start date is the same as end date' do - @room = Hotel::Room.new(1) - reservation_data = { - id: 100, - start_date: Date.parse("Jan 2, 2000"), - end_date: Date.parse("Jan 2, 2000"), - room: @room - } - - proc { Hotel::Reservation.new(reservation_data) }.must_raise StandardError - end end describe 'projected_cost method' do + it 'calculates the projected cost of the reservation' do room = Hotel::Room.new(1) + date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12').range reservation_data = { id: 100, - start_date: Date.parse("Dec 31, 1999"), - end_date: Date.parse("Jan 2, 2000"), + date_range: date_range, room: room } reservation = Hotel::Reservation.new(reservation_data) reservation.projected_cost.must_be_kind_of Integer - reservation.projected_cost.must_equal 400 + reservation.projected_cost.must_equal 600 end end end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index d3d5e7dcb..e5cc86ef8 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -31,10 +31,10 @@ describe 'add_reservation method' do before do @room = Hotel::Room.new(1) + @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000").range reservation_data = { id: 100, - start_date: Date.parse("Dec 31, 1999"), - end_date: Date.parse("Jan 2, 2000"), + date_range: @date_range, room: @room } @reservation = Hotel::Reservation.new(reservation_data) @@ -57,10 +57,10 @@ describe 'is_available? method' do before do @room = Hotel::Room.new(1) + @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000").range reservation_data = { id: 100, - start_date: Date.parse("Dec 31, 1999"), - end_date: Date.parse("Jan 2, 2000"), + date_range: @date_range, room: @room } @reservation = Hotel::Reservation.new(reservation_data) diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb index 867f5c5b0..432fb70e6 100644 --- a/specs/spec_helper.rb +++ b/specs/spec_helper.rb @@ -8,5 +8,6 @@ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new require_relative '../lib/admin' +require_relative '../lib/daterange' require_relative '../lib/reservation' require_relative '../lib/room' From 47b4b7dd827faf62c6a8075ada5035ea061f6804 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Thu, 8 Mar 2018 13:48:05 -0800 Subject: [PATCH 08/25] Modified Room#is_available? method to accept a date range and updated tests --- lib/room.rb | 9 ++-- specs/room_spec.rb | 100 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/lib/room.rb b/lib/room.rb index 64d86e00c..6588621d2 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -23,13 +23,10 @@ def add_reservation(reservation) @reservations << reservation end - def is_available?(date) - if date.class != Date - raise ArgumentError.new("Input must be an instance of Date class") - end - + def is_available?(date_range) @reservations.each do |reservation| - return false if reservation.date_range.include?(date) + intersection = reservation.date_range & date_range + return false unless intersection.empty? end return true end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index e5cc86ef8..659e30c8b 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -57,38 +57,102 @@ describe 'is_available? method' do before do @room = Hotel::Room.new(1) - @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000").range + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16').range + end + + it 'returns true if the room has no reservations' do + @room.is_available?(@date_range).must_equal true + end + + it 'returns true if the given date range doesnt overlap a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-04-09', '2018-04-16').range reservation_data = { id: 100, - date_range: @date_range, + date_range: reservation_date_range, room: @room } - @reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(@reservation) + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal true + end + + it 'returns true if the given date range ends on the check-in date of a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-03-16', '2018-03-18').range + reservation_data = { + id: 100, + date_range: reservation_date_range, + room: @room + } + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal true end - it 'returns true if the given date is before the reservation' do - date = Date.parse("Dec 30, 1999") - @room.is_available?(date).must_equal true + it 'returns true if the given date range begins on the check-out date of a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-09').range + reservation_data = { + id: 100, + date_range: reservation_date_range, + room: @room + } + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal true end - it 'returns true if the given date is after the reservation' do - date = Date.parse("Jan 3, 2000") - @room.is_available?(date).must_equal true + it 'returns false if the given date range overlaps the beginning of a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-03-15', '2018-03-17').range + reservation_data = { + id: 100, + date_range: reservation_date_range, + room: @room + } + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal false end - it 'returns true if the given date is the check-out date of the reservation' do - date = Date.parse("Jan 2, 2000") - @room.is_available?(date).must_equal true + it 'returns false if the given date range overlaps the end of a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-10').range + reservation_data = { + id: 100, + date_range: reservation_date_range, + room: @room + } + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal false end - it 'returns false if the given date overlaps the reservation date range' do - date = Date.parse("Jan 1, 2000") - @room.is_available?(date).must_equal false + it 'returns false if the given date range is completely contained by a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-18').range + reservation_data = { + id: 100, + date_range: reservation_date_range, + room: @room + } + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal false end - it 'throws an error if the input is not a date' do - proc { @room.is_available?(1) }.must_raise ArgumentError + it 'returns false if the given date range contains a reservation' do + reservation_date_range = Hotel::DateRange.new('2018-03-11', '2018-03-13').range + reservation_data = { + id: 100, + date_range: reservation_date_range, + room: @room + } + reservation = Hotel::Reservation.new(reservation_data) + @room.add_reservation(reservation) + + @room.is_available?(@date_range).must_equal false end end end From 482ab6a095d16411867e9820935b708e14432ec1 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Thu, 8 Mar 2018 14:10:24 -0800 Subject: [PATCH 09/25] Added Admin#find_available_rooms method and tests --- lib/admin.rb | 41 +++++++++-------------------------------- specs/admin_spec.rb | 43 +++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 52 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index db7cfc116..03b23539a 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -24,7 +24,7 @@ def reserve_room(date_range) reservation_data = { id: next_reservation_id, date_range: date_range, - room: @rooms.sample # change later to find available rooms + room: find_available_rooms(date_range).sample # change later to find available rooms } new_reservation = Reservation.new(reservation_data) @@ -46,43 +46,20 @@ def next_reservation_id end def find_reservations(date) - reservations_by_date = [] - - @reservations.each do |reservation| - if reservation.date_range.include?(date) - reservations_by_date << reservation - end + reservations_by_date = @reservations.select do |reservation| + reservation.date_range.include?(date) end return reservations_by_date end + def find_available_rooms(date_range) + available_rooms = @rooms.select do |room| + room.is_available?(date_range) + end - # def find_available_rooms(start_date, end_date) - # available_rooms = [] - # - # reservation_range = (Date.parse(start_date)..Date.parse(end_date)-1).to_a - # - # @rooms.each do |room| - # - # conflicts = false - # - # room.reservations.each do |reservation| - # booked_range = (start_date..end_date - 1).to_a - # intersection = reservation_range & booked_range - # unless intersection.empty? - # conflicts = true - # break - # end - # end - # - # if conflicts == false - # available_rooms << room - # end - # end - # - # return available_rooms - # end + return available_rooms + end end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 79a4f76c4..f2dc941ec 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -114,24 +114,27 @@ end end - # describe 'find_available_rooms method' do - # before do - # @hotel = Hotel::Admin.new - # @hotel.reserve_room("May 6, 1982", "May 10, 1982") - # @hotel.reserve_room("May 5, 1982", "May 8, 1982") - # end - # - # it 'returns an array of rooms' do - # available_rooms = @hotel.find_reservations(Date.parse("May 4, 1982"),Date.parse("May 6, 1982")) - # available_rooms.must_be_kind_of Array - # available_rooms.each do |room| - # room.must_be_instance of Hotel::Room - # end - # end - # - # it 'accurately accounts for the number of available rooms' do - # available_rooms = @hotel.find_reservations(Date.parse("May 4, 1982"),Date.parse("May 6, 1982")) - # available_rooms.length.must_equal 19 - # end - # end + describe 'find_available_rooms method' do + before do + @hotel = Hotel::Admin.new + date_range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982").range + date_range_2 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982").range + @date_range_3 = Hotel::DateRange.new("May 6, 1982", "May 8, 1982").range + @hotel.reserve_room(date_range_1) + @hotel.reserve_room(date_range_2) + end + + it 'returns an array of rooms' do + available_rooms = @hotel.find_available_rooms(@date_range_3) + available_rooms.must_be_kind_of Array + available_rooms.each do |room| + room.must_be_instance_of Hotel::Room + end + end + + it 'accurately accounts for the number of available rooms' do + available_rooms = @hotel.find_available_rooms(@date_range_3) + available_rooms.length.must_equal 18 + end + end end From 92e8ec034fae030da231b377f7a2569e6841c7da Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Thu, 8 Mar 2018 14:25:51 -0800 Subject: [PATCH 10/25] Modified Admin#reserve_room method and tests to raise error for no available rooms --- lib/admin.rb | 6 +++++- specs/admin_spec.rb | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/admin.rb b/lib/admin.rb index 03b23539a..6c7364f6a 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -21,10 +21,14 @@ def load_rooms end def reserve_room(date_range) + if find_available_rooms(date_range).empty? + raise StandardError.new("There are no available rooms for that date range.") + end + reservation_data = { id: next_reservation_id, date_range: date_range, - room: find_available_rooms(date_range).sample # change later to find available rooms + room: find_available_rooms(date_range).sample } new_reservation = Reservation.new(reservation_data) diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index f2dc941ec..bd85e2d94 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -60,6 +60,14 @@ it 'adds the reservation to the hotels list of reservations' do @hotel.reservations.must_include @reservation end + + it 'throws an error if there are no available rooms' do + 19.times do + @hotel.reserve_room(@date_range) + end + + proc { @hotel.reserve_room(@date_range) }.must_raise StandardError + end end describe 'next_reservation_id helper method' do @@ -136,5 +144,14 @@ available_rooms = @hotel.find_available_rooms(@date_range_3) available_rooms.length.must_equal 18 end + + it 'returns an empty array if there are no available rooms' do + date_range = Hotel::DateRange.new("March 17, 2018", "March 18, 2018").range + 20.times do + @hotel.reserve_room(date_range) + end + + @hotel.find_available_rooms(date_range).must_be_empty + end end end From 28067bf80e7f6ffc50202f13218e875e161cfc2b Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Thu, 8 Mar 2018 15:25:47 -0800 Subject: [PATCH 11/25] Added a BlockReservation class to inherit from Reservation. Ammended Room to accomodate block reservations. --- lib/admin.rb | 3 ++- lib/block_reservation.rb | 15 +++++++++++++++ lib/{daterange.rb => date_range.rb} | 0 lib/reservation.rb | 4 +--- lib/room.rb | 19 ++++++++++++++++--- specs/block_reservation_spec.rb | 0 .../{daterange_spec.rb => date_range_spec.rb} | 0 specs/spec_helper.rb | 3 ++- 8 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 lib/block_reservation.rb rename lib/{daterange.rb => date_range.rb} (100%) create mode 100644 specs/block_reservation_spec.rb rename specs/{daterange_spec.rb => date_range_spec.rb} (100%) diff --git a/lib/admin.rb b/lib/admin.rb index 6c7364f6a..3fa78a4f5 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -1,4 +1,5 @@ -require_relative 'daterange' +require_relative 'block_reservation' +require_relative 'date_range' require_relative 'room' require_relative 'reservation' diff --git a/lib/block_reservation.rb b/lib/block_reservation.rb new file mode 100644 index 000000000..4bc9f427d --- /dev/null +++ b/lib/block_reservation.rb @@ -0,0 +1,15 @@ +module Hotel + class BlockReservation + + attr_reader :id, :date_range, :room + + COST_PER_NIGHT = 150 + + def initialize(input, block_id) + super(input) + @block_id = block_id + @is_available = true + end + + end +end diff --git a/lib/daterange.rb b/lib/date_range.rb similarity index 100% rename from lib/daterange.rb rename to lib/date_range.rb diff --git a/lib/reservation.rb b/lib/reservation.rb index 8238c62bc..d53334e8b 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -12,11 +12,9 @@ def initialize(input) end def projected_cost - # exclusive range does not include end date, - # count returns an Integer rather than the Rational returned by subtraction of two dates nights = date_range.count projected_cost = nights * COST_PER_NIGHT -# + return projected_cost end end diff --git a/lib/room.rb b/lib/room.rb index 6588621d2..2cd5346ae 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -1,9 +1,7 @@ module Hotel class Room - COST_PER_NIGHT = 200 - - attr_reader :number, :reservations + attr_reader :number, :reservations, :block_reservations def initialize(num) @@ -13,6 +11,7 @@ def initialize(num) @number = num @reservations = [] + @block_reservations = [] end def add_reservation(reservation) @@ -23,11 +22,25 @@ def add_reservation(reservation) @reservations << reservation end + def add_block_reservation(block_reservation) + if reservation.class != BlockReservation + raise ArgumentError.new("Input must be an instance of BlockReservation") + end + + @block_reservations << block_reservation + end + def is_available?(date_range) @reservations.each do |reservation| intersection = reservation.date_range & date_range return false unless intersection.empty? end + + @block_reservations.each do |block_res| + intersection = block_res.date_range & date_range + return false unless intersection.empty? + end + return true end end diff --git a/specs/block_reservation_spec.rb b/specs/block_reservation_spec.rb new file mode 100644 index 000000000..e69de29bb diff --git a/specs/daterange_spec.rb b/specs/date_range_spec.rb similarity index 100% rename from specs/daterange_spec.rb rename to specs/date_range_spec.rb diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb index 432fb70e6..2e44fad64 100644 --- a/specs/spec_helper.rb +++ b/specs/spec_helper.rb @@ -8,6 +8,7 @@ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new require_relative '../lib/admin' -require_relative '../lib/daterange' +require_relative '../lib/block_reservation' +require_relative '../lib/date_range' require_relative '../lib/reservation' require_relative '../lib/room' From 7fb73b896a3dc177b17734d45a9300cfc3af0cce Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 16:06:32 -0800 Subject: [PATCH 12/25] Added overlap methods to Reservation and DateRange classes and changed tests to accomodate new structure. --- lib/admin.rb | 31 ++++++++++++++++++++++++++++++- lib/date_range.rb | 5 ++++- lib/reservation.rb | 4 ++++ lib/room.rb | 17 +---------------- specs/admin_spec.rb | 26 +++++++++++++------------- specs/room_spec.rb | 18 +++++++++--------- 6 files changed, 61 insertions(+), 40 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 3fa78a4f5..2dc0b8580 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -10,6 +10,7 @@ class Admin def initialize @rooms = load_rooms @reservations = [] + @block_reservations = [] end def load_rooms @@ -40,6 +41,34 @@ def reserve_room(date_range) end + def create_block(num_rooms, date_range) + if find_available_rooms(date_range).empty? + raise StandardError.new("There are no available rooms for that date range.") + end + + num_rooms.times do + reservation_data = { + id: next_reservation_id, + date_range: date_range, + room: find_available_rooms(date_range).sample + } + block_id = 1 #TODO: fix later + new_block_reservation = BlockReservation.new(reservation_data, block_id) + @block_reservations << new_block_reservation + new_block_reservation.room.add_reservation(new_block_reservation) + end + end + + def find_available_block_rooms(block_id) + block_rooms = @block_reservations.select do |room| + block_reservations.id == block_id && block + end + + block_rooms.select + + + end + def next_reservation_id if @reservations.empty? reservation_id = 1 @@ -52,7 +81,7 @@ def next_reservation_id def find_reservations(date) reservations_by_date = @reservations.select do |reservation| - reservation.date_range.include?(date) + reservation.date_range.range.include?(date) end return reservations_by_date diff --git a/lib/date_range.rb b/lib/date_range.rb index 84e941c0c..0abe9ae60 100644 --- a/lib/date_range.rb +++ b/lib/date_range.rb @@ -8,7 +8,6 @@ class DateRange def initialize(start_date, end_date) @start_date = Date.parse(start_date) @end_date = Date.parse(end_date) - @range = range [@start_date, @end_date].each do |date| if date.class != Date @@ -25,5 +24,9 @@ def range return (@start_date...@end_date).to_a end + def overlap?(other) + return !(other.range & self.range).empty? + end + end end diff --git a/lib/reservation.rb b/lib/reservation.rb index d53334e8b..269d0f77f 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -17,5 +17,9 @@ def projected_cost return projected_cost end + + def overlap?(date_range) + return @date_range.overlap?(date_range) + end end end diff --git a/lib/room.rb b/lib/room.rb index 2cd5346ae..f92f7a70e 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -11,7 +11,6 @@ def initialize(num) @number = num @reservations = [] - @block_reservations = [] end def add_reservation(reservation) @@ -22,23 +21,9 @@ def add_reservation(reservation) @reservations << reservation end - def add_block_reservation(block_reservation) - if reservation.class != BlockReservation - raise ArgumentError.new("Input must be an instance of BlockReservation") - end - - @block_reservations << block_reservation - end - def is_available?(date_range) @reservations.each do |reservation| - intersection = reservation.date_range & date_range - return false unless intersection.empty? - end - - @block_reservations.each do |block_res| - intersection = block_res.date_range & date_range - return false unless intersection.empty? + return false if reservation.overlap?(date_range) end return true diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index bd85e2d94..f134051f1 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -29,7 +29,7 @@ describe 'reserve_room method' do before do @hotel = Hotel::Admin.new - @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000").range + @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") @reservation = @hotel.reserve_room(@date_range) end @@ -44,8 +44,8 @@ it 'accurately loads reservation data' do @reservation.id.must_be_kind_of Integer @reservation.id.must_equal 1 - @reservation.date_range.must_be_kind_of Array - @reservation.date_range.each do |date| + @reservation.date_range.must_be_kind_of Hotel::DateRange + @reservation.date_range.range.each do |date| date.must_be_kind_of Date end @reservation.room.must_be_instance_of Hotel::Room @@ -78,9 +78,9 @@ it 'creates a unique reservation id' do hotel = Hotel::Admin.new - range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982").range - range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982").range - range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982").range + range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982") + range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") hotel.reserve_room(range_1) hotel.reserve_room(range_2) hotel.reserve_room(range_3) @@ -93,9 +93,9 @@ describe 'find_reservations method' do before do @hotel = Hotel::Admin.new - range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982").range - range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982").range - range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982").range + range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982") + range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") @reservation_1 = @hotel.reserve_room(range_1) @reservation_2 = @hotel.reserve_room(range_2) @reservation_3 = @hotel.reserve_room(range_3) @@ -125,9 +125,9 @@ describe 'find_available_rooms method' do before do @hotel = Hotel::Admin.new - date_range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982").range - date_range_2 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982").range - @date_range_3 = Hotel::DateRange.new("May 6, 1982", "May 8, 1982").range + date_range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + date_range_2 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") + @date_range_3 = Hotel::DateRange.new("May 6, 1982", "May 8, 1982") @hotel.reserve_room(date_range_1) @hotel.reserve_room(date_range_2) end @@ -146,7 +146,7 @@ end it 'returns an empty array if there are no available rooms' do - date_range = Hotel::DateRange.new("March 17, 2018", "March 18, 2018").range + date_range = Hotel::DateRange.new("March 17, 2018", "March 18, 2018") 20.times do @hotel.reserve_room(date_range) end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index 659e30c8b..1571e8879 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -31,7 +31,7 @@ describe 'add_reservation method' do before do @room = Hotel::Room.new(1) - @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000").range + @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") reservation_data = { id: 100, date_range: @date_range, @@ -57,7 +57,7 @@ describe 'is_available? method' do before do @room = Hotel::Room.new(1) - @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16').range + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16') end it 'returns true if the room has no reservations' do @@ -65,7 +65,7 @@ end it 'returns true if the given date range doesnt overlap a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-04-09', '2018-04-16').range + reservation_date_range = Hotel::DateRange.new('2018-04-09', '2018-04-16') reservation_data = { id: 100, date_range: reservation_date_range, @@ -78,7 +78,7 @@ end it 'returns true if the given date range ends on the check-in date of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-16', '2018-03-18').range + reservation_date_range = Hotel::DateRange.new('2018-03-16', '2018-03-18') reservation_data = { id: 100, date_range: reservation_date_range, @@ -91,7 +91,7 @@ end it 'returns true if the given date range begins on the check-out date of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-09').range + reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-09') reservation_data = { id: 100, date_range: reservation_date_range, @@ -104,7 +104,7 @@ end it 'returns false if the given date range overlaps the beginning of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-15', '2018-03-17').range + reservation_date_range = Hotel::DateRange.new('2018-03-15', '2018-03-17') reservation_data = { id: 100, date_range: reservation_date_range, @@ -117,7 +117,7 @@ end it 'returns false if the given date range overlaps the end of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-10').range + reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-10') reservation_data = { id: 100, date_range: reservation_date_range, @@ -130,7 +130,7 @@ end it 'returns false if the given date range is completely contained by a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-18').range + reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-18') reservation_data = { id: 100, date_range: reservation_date_range, @@ -143,7 +143,7 @@ end it 'returns false if the given date range contains a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-11', '2018-03-13').range + reservation_date_range = Hotel::DateRange.new('2018-03-11', '2018-03-13') reservation_data = { id: 100, date_range: reservation_date_range, From 531c9e44d1ff4c5accf7ff61e766966bc1f67629 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 16:10:21 -0800 Subject: [PATCH 13/25] Deleted BlockReservation class - decided to include in Reservation --- lib/admin.rb | 1 - lib/block_reservation.rb | 15 --------------- lib/room.rb | 2 +- specs/block_reservation_spec.rb | 0 specs/spec_helper.rb | 1 - 5 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 lib/block_reservation.rb delete mode 100644 specs/block_reservation_spec.rb diff --git a/lib/admin.rb b/lib/admin.rb index 2dc0b8580..3dd963c5f 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -1,4 +1,3 @@ -require_relative 'block_reservation' require_relative 'date_range' require_relative 'room' require_relative 'reservation' diff --git a/lib/block_reservation.rb b/lib/block_reservation.rb deleted file mode 100644 index 4bc9f427d..000000000 --- a/lib/block_reservation.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Hotel - class BlockReservation - - attr_reader :id, :date_range, :room - - COST_PER_NIGHT = 150 - - def initialize(input, block_id) - super(input) - @block_id = block_id - @is_available = true - end - - end -end diff --git a/lib/room.rb b/lib/room.rb index f92f7a70e..43026e5ba 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -1,7 +1,7 @@ module Hotel class Room - attr_reader :number, :reservations, :block_reservations + attr_reader :number, :reservations def initialize(num) diff --git a/specs/block_reservation_spec.rb b/specs/block_reservation_spec.rb deleted file mode 100644 index e69de29bb..000000000 diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb index 2e44fad64..ad294f30e 100644 --- a/specs/spec_helper.rb +++ b/specs/spec_helper.rb @@ -8,7 +8,6 @@ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new require_relative '../lib/admin' -require_relative '../lib/block_reservation' require_relative '../lib/date_range' require_relative '../lib/reservation' require_relative '../lib/room' From de3d3f380a8151ab8d852f37e78ae526534194e6 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 17:38:37 -0800 Subject: [PATCH 14/25] Added block_id to Reservation class --- lib/reservation.rb | 13 ++++++++----- specs/reservation_spec.rb | 21 ++++++++++++++++++--- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/reservation.rb b/lib/reservation.rb index 269d0f77f..3d9876f95 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -1,21 +1,24 @@ module Hotel class Reservation - attr_reader :id, :date_range, :room + attr_reader :id, :date_range, :room, :block_id - COST_PER_NIGHT = 200 + COST_PER_NIGHT = 200.0 + BLOCK_DISCOUNT = 0.25 def initialize(input) @id = input[:id] @date_range = input[:date_range] @room = input[:room] + @block_id = input[:block_id] end - def projected_cost + def cost nights = date_range.count - projected_cost = nights * COST_PER_NIGHT + cost = nights * COST_PER_NIGHT + cost *= (1 - BLOCK_DISCOUNT) if @block_id != nil - return projected_cost + return cost end def overlap?(date_range) diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb index e7004bfbd..c53b61f8d 100644 --- a/specs/reservation_spec.rb +++ b/specs/reservation_spec.rb @@ -26,7 +26,7 @@ end end - describe 'projected_cost method' do + describe 'cost method' do it 'calculates the projected cost of the reservation' do room = Hotel::Room.new(1) @@ -38,8 +38,23 @@ } reservation = Hotel::Reservation.new(reservation_data) - reservation.projected_cost.must_be_kind_of Integer - reservation.projected_cost.must_equal 600 + reservation.cost.must_be_kind_of Float + reservation.cost.must_equal 600.0 + end + + it 'calculates the projected cost of the reservation if it is part of a block' do + room = Hotel::Room.new(1) + date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12').range + reservation_data = { + id: 100, + date_range: date_range, + room: room, + block_id: 1 + } + reservation = Hotel::Reservation.new(reservation_data) + + reservation.cost.must_be_kind_of Float + reservation.cost.must_equal 450.0 end end end From 08b8d2bcaaad2682cb508d0c6ee4b7affdae3a00 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 21:35:53 -0800 Subject: [PATCH 15/25] Refactored to eliminate most of what the room class 'knew' --- lib/admin.rb | 42 ++++--- lib/reservation.rb | 1 + lib/room.rb | 31 +++--- specs/admin_spec.rb | 4 +- specs/room_spec.rb | 262 ++++++++++++++++++++++---------------------- 5 files changed, 170 insertions(+), 170 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 3dd963c5f..9bf0f6d59 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -9,7 +9,6 @@ class Admin def initialize @rooms = load_rooms @reservations = [] - @block_reservations = [] end def load_rooms @@ -34,13 +33,13 @@ def reserve_room(date_range) new_reservation = Reservation.new(reservation_data) @reservations << new_reservation - new_reservation.room.add_reservation(new_reservation) + # new_reservation.room.add_reservation(new_reservation) return new_reservation end - def create_block(num_rooms, date_range) + def create_block(num_rooms, date_range, block_id) if find_available_rooms(date_range).empty? raise StandardError.new("There are no available rooms for that date range.") end @@ -49,23 +48,19 @@ def create_block(num_rooms, date_range) reservation_data = { id: next_reservation_id, date_range: date_range, - room: find_available_rooms(date_range).sample + room: find_available_rooms(date_range).sample, + block_id: block_id } - block_id = 1 #TODO: fix later - new_block_reservation = BlockReservation.new(reservation_data, block_id) - @block_reservations << new_block_reservation - new_block_reservation.room.add_reservation(new_block_reservation) + new_block_reservation = Reservation.new(reservation_data) + @reservations << new_block_reservation + #new_block_reservation.room.add_reservation(new_block_reservation) end end def find_available_block_rooms(block_id) - block_rooms = @block_reservations.select do |room| - block_reservations.id == block_id && block + available_block_rooms = @reservations.select do |res| + res.block_id == block_id && res.block_status == :blocked end - - block_rooms.select - - end def next_reservation_id @@ -86,13 +81,24 @@ def find_reservations(date) return reservations_by_date end + # def find_available_rooms(date_range) + # available_rooms = @rooms.select do |room| + # room.is_available?(date_range) + # end + # + # return available_rooms + # end + def find_available_rooms(date_range) - available_rooms = @rooms.select do |room| - room.is_available?(date_range) + available_rooms = [] + + conflicts = @reservations.select do |reservation| + reservation.overlap?(date_range) end - return available_rooms - end + unavailable_rooms = conflicts.map { |res| res.room } + available_rooms = @rooms - unavailable_rooms + end end end diff --git a/lib/reservation.rb b/lib/reservation.rb index 3d9876f95..d3a6e7f39 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -11,6 +11,7 @@ def initialize(input) @date_range = input[:date_range] @room = input[:room] @block_id = input[:block_id] + @block_status = input[:block_status] end def cost diff --git a/lib/room.rb b/lib/room.rb index 43026e5ba..d383fb01a 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -10,23 +10,22 @@ def initialize(num) end @number = num - @reservations = [] end - def add_reservation(reservation) - if reservation.class != Reservation - raise ArgumentError.new("Input must be an instance of Reservation") - end - - @reservations << reservation - end - - def is_available?(date_range) - @reservations.each do |reservation| - return false if reservation.overlap?(date_range) - end - - return true - end + # def add_reservation(reservation) + # if reservation.class != Reservation + # raise ArgumentError.new("Input must be an instance of Reservation") + # end + # + # @reservations << reservation + # end + # + # def is_available?(date_range) + # @reservations.each do |reservation| + # return false if reservation.overlap?(date_range) + # end + # + # return true + # end end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index f134051f1..d67eb59bb 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -51,10 +51,9 @@ @reservation.room.must_be_instance_of Hotel::Room end - it 'associates the reservation with a room' do + it 'assigns a room to the reservation' do assigned_room = @reservation.room assigned_room.must_be_instance_of Hotel::Room - assigned_room.reservations.must_include @reservation end it 'adds the reservation to the hotels list of reservations' do @@ -154,4 +153,5 @@ @hotel.find_available_rooms(date_range).must_be_empty end end + end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index 1571e8879..73e111c86 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -7,10 +7,9 @@ @room = Hotel::Room.new(1) end - it 'creates an instance of Hotel' do + it 'creates an instance of Room' do @room.must_be_instance_of Hotel::Room @room.must_respond_to :number - @room.must_respond_to :reservations end it 'has a number' do @@ -21,138 +20,133 @@ proc { Hotel::Room.new(0) }.must_raise ArgumentError proc { Hotel::Room.new(21) }.must_raise ArgumentError end - - it 'creates an empty array of reservations' do - @room.reservations.must_be_kind_of Array - @room.reservations.length.must_equal 0 - end - end - - describe 'add_reservation method' do - before do - @room = Hotel::Room.new(1) - @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") - reservation_data = { - id: 100, - date_range: @date_range, - room: @room - } - @reservation = Hotel::Reservation.new(reservation_data) - end - - it 'throws an error if input is not an instance of Reservation' do - proc { @room.add_reservation(100) }.must_raise ArgumentError - end - - it 'returns an array of reservations' do - @room.add_reservation(@reservation).must_be_kind_of Array - @room.reservations.length.must_equal 1 - @room.reservations.each do |reservation| - reservation.must_be_instance_of Hotel::Reservation - end - end - end - describe 'is_available? method' do - before do - @room = Hotel::Room.new(1) - @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16') - end - - it 'returns true if the room has no reservations' do - @room.is_available?(@date_range).must_equal true - end - - it 'returns true if the given date range doesnt overlap a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-04-09', '2018-04-16') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal true - end - - it 'returns true if the given date range ends on the check-in date of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-16', '2018-03-18') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal true - end - - it 'returns true if the given date range begins on the check-out date of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-09') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal true - end - - it 'returns false if the given date range overlaps the beginning of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-15', '2018-03-17') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal false - end - - it 'returns false if the given date range overlaps the end of a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-10') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal false - end - - it 'returns false if the given date range is completely contained by a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-18') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal false - end - - it 'returns false if the given date range contains a reservation' do - reservation_date_range = Hotel::DateRange.new('2018-03-11', '2018-03-13') - reservation_data = { - id: 100, - date_range: reservation_date_range, - room: @room - } - reservation = Hotel::Reservation.new(reservation_data) - @room.add_reservation(reservation) - - @room.is_available?(@date_range).must_equal false - end - end + # describe 'add_reservation method' do + # before do + # @room = Hotel::Room.new(1) + # @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") + # reservation_data = { + # id: 100, + # date_range: @date_range, + # room: @room + # } + # @reservation = Hotel::Reservation.new(reservation_data) + # end + # + # it 'throws an error if input is not an instance of Reservation' do + # proc { @room.add_reservation(100) }.must_raise ArgumentError + # end + # + # it 'returns an array of reservations' do + # @room.add_reservation(@reservation).must_be_kind_of Array + # @room.reservations.length.must_equal 1 + # @room.reservations.each do |reservation| + # reservation.must_be_instance_of Hotel::Reservation + # end + # end + # + # end + + # describe 'is_available? method' do + # before do + # @room = Hotel::Room.new(1) + # @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16') + # end + # + # it 'returns true if the room has no reservations' do + # @room.is_available?(@date_range).must_equal true + # end + # + # it 'returns true if the given date range doesnt overlap a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-04-09', '2018-04-16') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal true + # end + # + # it 'returns true if the given date range ends on the check-in date of a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-03-16', '2018-03-18') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal true + # end + # + # it 'returns true if the given date range begins on the check-out date of a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-09') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal true + # end + # + # it 'returns false if the given date range overlaps the beginning of a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-03-15', '2018-03-17') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal false + # end + # + # it 'returns false if the given date range overlaps the end of a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-10') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal false + # end + # + # it 'returns false if the given date range is completely contained by a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-18') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal false + # end + # + # it 'returns false if the given date range contains a reservation' do + # reservation_date_range = Hotel::DateRange.new('2018-03-11', '2018-03-13') + # reservation_data = { + # id: 100, + # date_range: reservation_date_range, + # room: @room + # } + # reservation = Hotel::Reservation.new(reservation_data) + # @room.add_reservation(reservation) + # + # @room.is_available?(@date_range).must_equal false + # end + # end end From 3dc1095bd66007232f89933f4e25fd81881eba43 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 21:56:32 -0800 Subject: [PATCH 16/25] Updated tests for DateRange to include overlap? method --- lib/date_range.rb | 6 ++--- specs/date_range_spec.rb | 48 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/date_range.rb b/lib/date_range.rb index 0abe9ae60..87fb4196e 100644 --- a/lib/date_range.rb +++ b/lib/date_range.rb @@ -1,9 +1,9 @@ -require 'date' +#require 'date' module Hotel class DateRange - attr_reader :start_date, :end_date, :date_range + attr_reader :start_date, :end_date def initialize(start_date, end_date) @start_date = Date.parse(start_date) @@ -16,7 +16,7 @@ def initialize(start_date, end_date) end if @end_date <= @start_date - raise StandardError.new("End date must be at least one day later than starting date.") + raise StandardError.new("End date must be at least one day later than start date.") end end diff --git a/specs/date_range_spec.rb b/specs/date_range_spec.rb index 419b85ed0..7d4fd5335 100644 --- a/specs/date_range_spec.rb +++ b/specs/date_range_spec.rb @@ -6,15 +6,15 @@ it 'can be created' do date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') date_range.must_be_instance_of Hotel::DateRange - [:start_date, :end_date, :date_range].each do |prop| + [:start_date, :end_date].each do |prop| date_range.must_respond_to prop end end it 'throws an error if the date entry is an invalid format' do proc { Hotel::DateRange.new('','') }.must_raise StandardError - # proc { Hotel::DateRange.new(4, 5) }.must_raise StandardError - # proc { Hotel::DateRange.new('33, 4') }.must_raise StandardError + proc { Hotel::DateRange.new(4, 5) }.must_raise StandardError + proc { Hotel::DateRange.new('33', '4') }.must_raise StandardError end it 'throws an error if the starting and ending dates are the same' do @@ -48,4 +48,46 @@ @date_range.range.wont_include @date_range.end_date end end + + describe 'overlap? method' do + before do + @room = Hotel::Room.new(1) + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16') + end + + it 'returns false if there is no overlap between the date ranges' do + other = Hotel::DateRange.new('2018-04-09', '2018-04-16') + @date_range.overlap?(other).must_equal false + end + + it 'returns false if the given date range ends on the start date of the date range instance' do + other = Hotel::DateRange.new('2018-03-16', '2018-03-18') + @date_range.overlap?(other).must_equal false + end + + it 'returns false if the given date range begins on the end date of the date range instance' do + other = Hotel::DateRange.new('2018-03-07', '2018-03-09') + @date_range.overlap?(other).must_equal false + end + + it 'returns true if the given date range overlaps the beginning of the date range instance' do + other = Hotel::DateRange.new('2018-03-15', '2018-03-17') + @date_range.overlap?(other).must_equal true + end + + it 'returns true if the given date range overlaps the end of the date range instance' do + other = Hotel::DateRange.new('2018-03-07', '2018-03-10') + @date_range.overlap?(other).must_equal true + end + + it 'returns true if the given date range is completely contained by the date range instance' do + other = Hotel::DateRange.new('2018-03-07', '2018-03-18') + @date_range.overlap?(other).must_equal true + end + + it 'returns true if the given date range contains the date range instance' do + other = Hotel::DateRange.new('2018-03-11', '2018-03-13') + @date_range.overlap?(other).must_equal true + end + end end From 36179c355a39b2e7811a3bea1734ac630bb146e2 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 22:21:09 -0800 Subject: [PATCH 17/25] Updated Reservation tests to include block_id and block_status --- lib/date_range.rb | 2 -- lib/reservation.rb | 4 ++-- specs/reservation_spec.rb | 20 +++++++++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/date_range.rb b/lib/date_range.rb index 87fb4196e..ce13cb140 100644 --- a/lib/date_range.rb +++ b/lib/date_range.rb @@ -1,5 +1,3 @@ -#require 'date' - module Hotel class DateRange diff --git a/lib/reservation.rb b/lib/reservation.rb index d3a6e7f39..f59d348b6 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -1,7 +1,7 @@ module Hotel class Reservation - attr_reader :id, :date_range, :room, :block_id + attr_reader :id, :date_range, :room, :block_id, :block_status COST_PER_NIGHT = 200.0 BLOCK_DISCOUNT = 0.25 @@ -15,7 +15,7 @@ def initialize(input) end def cost - nights = date_range.count + nights = @date_range.range.count cost = nights * COST_PER_NIGHT cost *= (1 - BLOCK_DISCOUNT) if @block_id != nil diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb index c53b61f8d..96c4b1001 100644 --- a/specs/reservation_spec.rb +++ b/specs/reservation_spec.rb @@ -4,11 +4,13 @@ describe 'initialize method' do before do @room = Hotel::Room.new(1) - @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12').range + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') reservation_data = { id: 100, date_range: @date_range, - room: @room + room: @room, + block_id: 1, + block_status: :blocked } @reservation = Hotel::Reservation.new(reservation_data) end @@ -16,21 +18,22 @@ it 'creates an instance of Reservation' do @reservation.must_be_instance_of Hotel::Reservation - [:id, :date_range, :room].each do |prop| + [:id, :date_range, :room, :block_id, :block_status].each do |prop| @reservation.must_respond_to prop end @reservation.id.must_be_kind_of Integer - @reservation.date_range.must_be_kind_of Array + @reservation.date_range.must_be_instance_of Hotel::DateRange @reservation.room.must_be_instance_of Hotel::Room + @reservation.block_id.must_be_kind_of Integer + @reservation.block_status.must_be_kind_of Symbol end end describe 'cost method' do - it 'calculates the projected cost of the reservation' do room = Hotel::Room.new(1) - date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12').range + date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') reservation_data = { id: 100, date_range: date_range, @@ -44,7 +47,7 @@ it 'calculates the projected cost of the reservation if it is part of a block' do room = Hotel::Room.new(1) - date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12').range + date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') reservation_data = { id: 100, date_range: date_range, @@ -57,4 +60,7 @@ reservation.cost.must_equal 450.0 end end + + # Reservation#overlap? method not tested because it only calls the DateRange#overlap method + # Tests for DateRange#overlap? included in date_range_spec file end From ddfc614dcc3cb27924603433b1dc211606d0d2c6 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Fri, 9 Mar 2018 23:18:15 -0800 Subject: [PATCH 18/25] Reordered methods and tests in Admin class --- lib/admin.rb | 63 ++++++++------------- lib/room.rb | 16 ------ specs/admin_spec.rb | 132 ++++++++++++++++++++++---------------------- specs/room_spec.rb | 128 ------------------------------------------ 4 files changed, 90 insertions(+), 249 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 9bf0f6d59..99076ba81 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -20,6 +20,26 @@ def load_rooms return rooms end + def find_reservations(date) + reservations_by_date = @reservations.select do |res| + res.date_range.range.include?(date) + end + + return reservations_by_date + end + + def find_available_rooms(date_range) + available_rooms = [] + + conflicts = @reservations.select do |reservation| + reservation.overlap?(date_range) + end + + unavailable_rooms = conflicts.map { |res| res.room } + + available_rooms = @rooms - unavailable_rooms + end + def reserve_room(date_range) if find_available_rooms(date_range).empty? raise StandardError.new("There are no available rooms for that date range.") @@ -33,10 +53,13 @@ def reserve_room(date_range) new_reservation = Reservation.new(reservation_data) @reservations << new_reservation - # new_reservation.room.add_reservation(new_reservation) return new_reservation + end + def next_reservation_id + return 1 if @reservations.empty? + return @reservations.map { |res| res.id }.max + 1 end def create_block(num_rooms, date_range, block_id) @@ -62,43 +85,5 @@ def find_available_block_rooms(block_id) res.block_id == block_id && res.block_status == :blocked end end - - def next_reservation_id - if @reservations.empty? - reservation_id = 1 - else - reservation_id = @reservations.map { |res| res.id }.max + 1 - end - - return reservation_id - end - - def find_reservations(date) - reservations_by_date = @reservations.select do |reservation| - reservation.date_range.range.include?(date) - end - - return reservations_by_date - end - - # def find_available_rooms(date_range) - # available_rooms = @rooms.select do |room| - # room.is_available?(date_range) - # end - # - # return available_rooms - # end - - def find_available_rooms(date_range) - available_rooms = [] - - conflicts = @reservations.select do |reservation| - reservation.overlap?(date_range) - end - - unavailable_rooms = conflicts.map { |res| res.room } - - available_rooms = @rooms - unavailable_rooms - end end end diff --git a/lib/room.rb b/lib/room.rb index d383fb01a..0b05f3763 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -11,21 +11,5 @@ def initialize(num) @number = num end - - # def add_reservation(reservation) - # if reservation.class != Reservation - # raise ArgumentError.new("Input must be an instance of Reservation") - # end - # - # @reservations << reservation - # end - # - # def is_available?(date_range) - # @reservations.each do |reservation| - # return false if reservation.overlap?(date_range) - # end - # - # return true - # end end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index d67eb59bb..0c4738235 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -26,69 +26,6 @@ end end - describe 'reserve_room method' do - before do - @hotel = Hotel::Admin.new - @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") - @reservation = @hotel.reserve_room(@date_range) - end - - it 'creates a new reservation' do - @reservation.must_be_instance_of Hotel::Reservation - - [:id, :date_range, :room].each do |prop| - @reservation.must_respond_to prop - end - end - - it 'accurately loads reservation data' do - @reservation.id.must_be_kind_of Integer - @reservation.id.must_equal 1 - @reservation.date_range.must_be_kind_of Hotel::DateRange - @reservation.date_range.range.each do |date| - date.must_be_kind_of Date - end - @reservation.room.must_be_instance_of Hotel::Room - end - - it 'assigns a room to the reservation' do - assigned_room = @reservation.room - assigned_room.must_be_instance_of Hotel::Room - end - - it 'adds the reservation to the hotels list of reservations' do - @hotel.reservations.must_include @reservation - end - - it 'throws an error if there are no available rooms' do - 19.times do - @hotel.reserve_room(@date_range) - end - - proc { @hotel.reserve_room(@date_range) }.must_raise StandardError - end - end - - describe 'next_reservation_id helper method' do - it 'assigns an id of 1 if there are no existing reservations' do - hotel = Hotel::Admin.new - hotel.next_reservation_id.must_equal 1 - end - - it 'creates a unique reservation id' do - hotel = Hotel::Admin.new - range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") - range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982") - range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") - hotel.reserve_room(range_1) - hotel.reserve_room(range_2) - hotel.reserve_room(range_3) - - res_ids = hotel.reservations.map { |reservation| reservation.id } - res_ids.uniq.length.must_equal 3 - end - end - describe 'find_reservations method' do before do @hotel = Hotel::Admin.new @@ -116,6 +53,12 @@ reservations.wont_include @reservation_2 end + it 'does not include reservations that end on the given date' do + reservations = @hotel.find_reservations(Date.parse("May 8, 1982")) + reservations.length.must_equal 1 + reservations.wont_include @reservation_3 + end + it 'returns an empty array if the date does not overlap any reservations' do @hotel.find_reservations(Date.parse("May 2, 1982")).must_equal [] end @@ -127,8 +70,8 @@ date_range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") date_range_2 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") @date_range_3 = Hotel::DateRange.new("May 6, 1982", "May 8, 1982") - @hotel.reserve_room(date_range_1) - @hotel.reserve_room(date_range_2) + @reservation_1 = @hotel.reserve_room(date_range_1) + @reservation_2 = @hotel.reserve_room(date_range_2) end it 'returns an array of rooms' do @@ -142,6 +85,8 @@ it 'accurately accounts for the number of available rooms' do available_rooms = @hotel.find_available_rooms(@date_range_3) available_rooms.length.must_equal 18 + available_rooms.wont_include @reservation_1.room + available_rooms.wont_include @reservation_2.room end it 'returns an empty array if there are no available rooms' do @@ -150,8 +95,63 @@ @hotel.reserve_room(date_range) end - @hotel.find_available_rooms(date_range).must_be_empty + @hotel.find_available_rooms(date_range).must_equal [] end end + describe 'reserve_room method' do + before do + @hotel = Hotel::Admin.new + @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") + @reservation = @hotel.reserve_room(@date_range) + end + + it 'creates a new reservation' do + @reservation.must_be_instance_of Hotel::Reservation + end + + it 'accurately loads reservation data' do + @reservation.id.must_be_kind_of Integer + @reservation.id.must_equal 1 + @reservation.date_range.must_be_instance_of Hotel::DateRange + @reservation.room.must_be_instance_of Hotel::Room + end + + it 'adds the reservation to the hotels list of reservations' do + @hotel.reservations.must_include @reservation + end + + it 'throws an error if there are no available rooms' do + 19.times do + @hotel.reserve_room(@date_range) + end + + proc { @hotel.reserve_room(@date_range) }.must_raise StandardError + end + end + + describe 'next_reservation_id helper method' do + it 'assigns an id of 1 if there are no existing reservations' do + hotel = Hotel::Admin.new + hotel.next_reservation_id.must_equal 1 + end + + it 'creates a unique reservation id' do + hotel = Hotel::Admin.new + range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982") + range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") + hotel.reserve_room(range_1) + hotel.reserve_room(range_2) + hotel.reserve_room(range_3) + + res_ids = hotel.reservations.map { |reservation| reservation.id } + res_ids.uniq.length.must_equal 3 + end + end + + + + + end diff --git a/specs/room_spec.rb b/specs/room_spec.rb index 73e111c86..c762e0515 100644 --- a/specs/room_spec.rb +++ b/specs/room_spec.rb @@ -21,132 +21,4 @@ proc { Hotel::Room.new(21) }.must_raise ArgumentError end end - - # describe 'add_reservation method' do - # before do - # @room = Hotel::Room.new(1) - # @date_range = Hotel::DateRange.new("Dec 31, 1999", "Jan 2, 2000") - # reservation_data = { - # id: 100, - # date_range: @date_range, - # room: @room - # } - # @reservation = Hotel::Reservation.new(reservation_data) - # end - # - # it 'throws an error if input is not an instance of Reservation' do - # proc { @room.add_reservation(100) }.must_raise ArgumentError - # end - # - # it 'returns an array of reservations' do - # @room.add_reservation(@reservation).must_be_kind_of Array - # @room.reservations.length.must_equal 1 - # @room.reservations.each do |reservation| - # reservation.must_be_instance_of Hotel::Reservation - # end - # end - # - # end - - # describe 'is_available? method' do - # before do - # @room = Hotel::Room.new(1) - # @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-16') - # end - # - # it 'returns true if the room has no reservations' do - # @room.is_available?(@date_range).must_equal true - # end - # - # it 'returns true if the given date range doesnt overlap a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-04-09', '2018-04-16') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal true - # end - # - # it 'returns true if the given date range ends on the check-in date of a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-03-16', '2018-03-18') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal true - # end - # - # it 'returns true if the given date range begins on the check-out date of a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-09') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal true - # end - # - # it 'returns false if the given date range overlaps the beginning of a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-03-15', '2018-03-17') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal false - # end - # - # it 'returns false if the given date range overlaps the end of a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-10') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal false - # end - # - # it 'returns false if the given date range is completely contained by a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-03-07', '2018-03-18') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal false - # end - # - # it 'returns false if the given date range contains a reservation' do - # reservation_date_range = Hotel::DateRange.new('2018-03-11', '2018-03-13') - # reservation_data = { - # id: 100, - # date_range: reservation_date_range, - # room: @room - # } - # reservation = Hotel::Reservation.new(reservation_data) - # @room.add_reservation(reservation) - # - # @room.is_available?(@date_range).must_equal false - # end - # end end From c95b523933eb3921bd70bad53fc77f1bf98d1967 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Sat, 10 Mar 2018 00:09:42 -0800 Subject: [PATCH 19/25] Added helper method for assigning block ids --- lib/admin.rb | 23 +++++++++++++++++++---- specs/admin_spec.rb | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 99076ba81..06701e1a1 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -62,21 +62,36 @@ def next_reservation_id return @reservations.map { |res| res.id }.max + 1 end + def next_block_id + if @reservations.select { |res| res.block_id != nil }.empty? + return 1 + else + max_block_id = @reservations.map { |res| res.block_id }.max + return max_block_id + 1 + end + end + def create_block(num_rooms, date_range, block_id) - if find_available_rooms(date_range).empty? - raise StandardError.new("There are no available rooms for that date range.") + if num_rooms > 5 + raise ArgumentError.new("A block can contain a maximum of 5 rooms.") + end + + if find_available_rooms(date_range) < num_rooms + raise StandardError.new("There are not enough available rooms to block for those dates.") end + block_id = next_block_id + num_rooms.times do reservation_data = { id: next_reservation_id, date_range: date_range, room: find_available_rooms(date_range).sample, - block_id: block_id + block_id: block_id, + block_status: :blocked } new_block_reservation = Reservation.new(reservation_data) @reservations << new_block_reservation - #new_block_reservation.room.add_reservation(new_block_reservation) end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 0c4738235..c8988420c 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -136,22 +136,46 @@ hotel.next_reservation_id.must_equal 1 end - it 'creates a unique reservation id' do + it 'assigns an id one higher than the highest existing reservation id' do hotel = Hotel::Admin.new range_1 = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") range_2 = Hotel::DateRange.new("April 30, 1982", "May 1, 1982") - range_3 = Hotel::DateRange.new("May 5, 1982", "May 8, 1982") hotel.reserve_room(range_1) hotel.reserve_room(range_2) - hotel.reserve_room(range_3) - res_ids = hotel.reservations.map { |reservation| reservation.id } - res_ids.uniq.length.must_equal 3 + hotel.next_reservation_id.must_equal 3 end - end - - + describe 'next_block_id helper method' do + before do + @hotel = Hotel::Admin.new + @date_range = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + end + it 'assigns an id of 1 if there are no existing block reservations' do + @hotel.next_block_id.must_equal 1 + end + it 'assigns an id one higher than the highest existing block id' do + reservation_data_1 = { + id: 100, + date_range: @date_range, + room: 15, + block_id: 1, + block_status: :blocked + } + reservation_data_2 = { + id: 200, + date_range: @date_range, + room: 16, + block_id: 4, + block_status: :blocked + } + reservation_1 = Hotel::Reservation.new(reservation_data_1) + reservation_2 = Hotel::Reservation.new(reservation_data_2) + @hotel.reservations.push(reservation_1, reservation_2) + @hotel.next_block_id.must_equal 5 + end + end + end end From 22cabf18fbe4c7171e5fc1e7a75c41d782a4dae3 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Sat, 10 Mar 2018 00:43:31 -0800 Subject: [PATCH 20/25] Added test for Admin#create_block method --- lib/admin.rb | 12 +++++----- specs/admin_spec.rb | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 06701e1a1..ca7e2d522 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -29,15 +29,14 @@ def find_reservations(date) end def find_available_rooms(date_range) - available_rooms = [] - conflicts = @reservations.select do |reservation| reservation.overlap?(date_range) end unavailable_rooms = conflicts.map { |res| res.room } - available_rooms = @rooms - unavailable_rooms + + return available_rooms end def reserve_room(date_range) @@ -71,12 +70,12 @@ def next_block_id end end - def create_block(num_rooms, date_range, block_id) + def create_block(num_rooms, date_range) if num_rooms > 5 raise ArgumentError.new("A block can contain a maximum of 5 rooms.") end - if find_available_rooms(date_range) < num_rooms + if find_available_rooms(date_range).length < num_rooms raise StandardError.new("There are not enough available rooms to block for those dates.") end @@ -99,6 +98,9 @@ def find_available_block_rooms(block_id) available_block_rooms = @reservations.select do |res| res.block_id == block_id && res.block_status == :blocked end + + return available_block_rooms end + end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index c8988420c..39085a1dc 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -128,6 +128,15 @@ proc { @hotel.reserve_room(@date_range) }.must_raise StandardError end + + # it 'throws an error if all rooms have been blocked' do + # 3.times do + # @hotel.create_block(5, @date_range) + # end + # @hotel.create_block(4, @date_range) + # + # proc { @hotel.reserve_room(@date_range) }.must_raise StandardError + # end end describe 'next_reservation_id helper method' do @@ -177,5 +186,49 @@ @hotel.next_block_id.must_equal 5 end end + + describe 'create_block method' do + before do + @hotel = Hotel::Admin.new + @date_range = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + end + + it 'throws an error if the user tries to block more than 5 rooms' do + proc { @hotel.create_block(6, @date_range) }.must_raise ArgumentError + end + + it 'adds the block reservations to the hotels reservations' do + @hotel.create_block(3, @date_range) + @hotel.reservations.length.must_equal 3 + end + + it 'creates reservations with distinct reservation ids and rooms' do + @hotel.create_block(3, @date_range) + @hotel.reservations.map { |res| res.id }.uniq.length.must_equal 3 + @hotel.reservations.map { |res| res.room }.uniq.length.must_equal 3 + end + + it 'creates reservations with the same date ranges and block_ids' do + @hotel.create_block(3, @date_range) + @hotel.reservations.map { |res| res.date_range }.uniq.length.must_equal 1 + @hotel.reservations.map { |res| res.block_id }.uniq.length.must_equal 1 + end + + it 'throws an error if there are not enough rooms to create the block' do + 16.times do + @hotel.reserve_room(@date_range) + end + + proc { @hotel.create_block(5, @date_range) }.must_raise StandardError + end + end + + describe 'find_available_block_rooms method' do + + + it 'returns an empty array if there are no available rooms in the block' do + + end + end end end From ef2fc376946347bb0285ef0808d0f5f3fda342f7 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Sat, 10 Mar 2018 00:57:14 -0800 Subject: [PATCH 21/25] Added Admin#find_available_block_rooms method and tests --- lib/reservation.rb | 10 ++++++++++ specs/admin_spec.rb | 26 ++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/reservation.rb b/lib/reservation.rb index f59d348b6..cd487b1ea 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -25,5 +25,15 @@ def cost def overlap?(date_range) return @date_range.overlap?(date_range) end + + def change_status + if @block_status == :blocked + @block_status = :reserved + else + @block_status = :blocked + end + + return @block_status + end end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 39085a1dc..64ad19002 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -224,10 +224,32 @@ end describe 'find_available_block_rooms method' do - + before do + @hotel = Hotel::Admin.new + @date_range = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + end - it 'returns an empty array if there are no available rooms in the block' do + it 'returns an array of block reservations' do + @hotel.create_block(3, @date_range) + @hotel.find_available_block_rooms(1).must_be_kind_of Array + @hotel.find_available_block_rooms(1).each do |reservation| + reservation.must_be_instance_of Hotel::Reservation + reservation.block_id.wont_be_nil + reservation.block_status.wont_be_nil + end + end + + it 'finds the number of available rooms in a block given a block id' do + @hotel.create_block(3, @date_range) + @hotel.find_available_block_rooms(1).length.must_equal 3 + @hotel.reservations[0].change_status + @hotel.find_available_block_rooms(1).length.must_equal 2 + end + it 'returns an empty array if there are no available rooms in the block' do + @hotel.create_block(1, @date_range) + @hotel.reservations[0].change_status + @hotel.find_available_block_rooms(1).must_equal [] end end end From 47c6a9893ca45259c7a80417efa57eebb9e0418e Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Sun, 11 Mar 2018 23:48:48 -0700 Subject: [PATCH 22/25] Added tests for Admin#reserve_block_room method --- lib/admin.rb | 13 ++++++++++++- lib/reservation.rb | 2 -- specs/admin_spec.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index ca7e2d522..577be3d8f 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -72,7 +72,7 @@ def next_block_id def create_block(num_rooms, date_range) if num_rooms > 5 - raise ArgumentError.new("A block can contain a maximum of 5 rooms.") + raise ArgumentError.new("A block can include a maximum of 5 rooms.") end if find_available_rooms(date_range).length < num_rooms @@ -102,5 +102,16 @@ def find_available_block_rooms(block_id) return available_block_rooms end + def reserve_block_room(block_id) + available_rooms = find_available_block_rooms(block_id) + + if available_rooms.empty? + raise StandardError.new("There are no available rooms in Block #{block_id}.") + end + + available_rooms.first.change_status + end + + end end diff --git a/lib/reservation.rb b/lib/reservation.rb index cd487b1ea..b66838041 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -32,8 +32,6 @@ def change_status else @block_status = :blocked end - - return @block_status end end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 64ad19002..cea6e1a14 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -252,5 +252,31 @@ @hotel.find_available_block_rooms(1).must_equal [] end end + + describe 'reserve_block_room method' do + before do + @hotel = Hotel::Admin.new + @date_range = Hotel::DateRange.new("May 6, 1982", "May 10, 1982") + @hotel.create_block(2, @date_range) + end + + it 'throws an error if there are no availabe rooms in the requested block' do + @hotel.reservations[0].change_status + @hotel.reservations[1].change_status + proc { @hotel.reserve_block_room(1) }.must_raise StandardError + end + + it 'changes the block_status of the first available room from blocked to reserved' do + @hotel.reservations[0].block_status.must_equal :blocked + @hotel.reserve_block_room(1) + @hotel.reservations[0].block_status.must_equal :reserved + end + + it 'changes the number of available rooms in the block' do + @hotel.find_available_block_rooms(1).length.must_equal 2 + @hotel.reserve_block_room(1) + @hotel.find_available_block_rooms(1).length.must_equal 1 + end + end end end From 552ce4bd5092975cbf4d839e6198569ea22ec026 Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Mon, 12 Mar 2018 00:16:11 -0700 Subject: [PATCH 23/25] Added tests for Reservation#change_status method. Failing test for error thrown when all rooms are blocked. --- lib/reservation.rb | 2 +- lib/room.rb | 2 +- specs/admin_spec.rb | 16 ++++++++-------- specs/reservation_spec.rb | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/reservation.rb b/lib/reservation.rb index b66838041..1bdf66d2f 100644 --- a/lib/reservation.rb +++ b/lib/reservation.rb @@ -29,7 +29,7 @@ def overlap?(date_range) def change_status if @block_status == :blocked @block_status = :reserved - else + elsif @block_status == :reserved @block_status = :blocked end end diff --git a/lib/room.rb b/lib/room.rb index 0b05f3763..3fc1c5c74 100644 --- a/lib/room.rb +++ b/lib/room.rb @@ -1,7 +1,7 @@ module Hotel class Room - attr_reader :number, :reservations + attr_reader :number def initialize(num) diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index cea6e1a14..3235caab4 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -129,14 +129,14 @@ proc { @hotel.reserve_room(@date_range) }.must_raise StandardError end - # it 'throws an error if all rooms have been blocked' do - # 3.times do - # @hotel.create_block(5, @date_range) - # end - # @hotel.create_block(4, @date_range) - # - # proc { @hotel.reserve_room(@date_range) }.must_raise StandardError - # end + it 'throws an error if all rooms have been blocked' do + 3.times do + @hotel.create_block(5, @date_range) + end + @hotel.create_block(4, @date_range) + + proc { @hotel.reserve_room(@date_range) }.must_raise StandardError + end end describe 'next_reservation_id helper method' do diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb index 96c4b1001..670f7090f 100644 --- a/specs/reservation_spec.rb +++ b/specs/reservation_spec.rb @@ -63,4 +63,38 @@ # Reservation#overlap? method not tested because it only calls the DateRange#overlap method # Tests for DateRange#overlap? included in date_range_spec file + + describe 'change_status method' do + before do + @hotel = Hotel::Admin.new + @date_range = Hotel::DateRange.new('2018-03-09', '2018-03-12') + @hotel.create_block(1, @date_range) + end + + it 'changes a block status to reserved if initially blocked' do + @hotel.reservations.first.block_status.must_equal :blocked + @hotel.reservations.first.change_status + @hotel.reservations.first.block_status.must_equal :reserved + end + + it 'changes a block status to blocked if initially reserved' do + @hotel.reservations.first.change_status + @hotel.reservations.first.block_status.must_equal :reserved + @hotel.reservations.first.change_status + @hotel.reservations.first.block_status.must_equal :blocked + end + + it 'does not change the block status of a reservation that is not part of a block' do + room = Hotel::Room.new(1) + reservation_data = { + id: 100, + date_range: @date_range, + room: room, + } + reservation = Hotel::Reservation.new(reservation_data) + reservation.block_status.must_be_nil + reservation.change_status + reservation.block_status.must_be_nil + end + end end From b8c9b9bde5f0e197623e2e22a8a6b34565dce4aa Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Mon, 12 Mar 2018 06:46:09 -0700 Subject: [PATCH 24/25] Changed Admin#next_block_id to eliminate error causeed by comparison of block and non-block reservations. --- lib/admin.rb | 6 ++++-- specs/admin_spec.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/admin.rb b/lib/admin.rb index 577be3d8f..b2310bfde 100644 --- a/lib/admin.rb +++ b/lib/admin.rb @@ -62,10 +62,12 @@ def next_reservation_id end def next_block_id - if @reservations.select { |res| res.block_id != nil }.empty? + block_reservations = @reservations.select { |res| res.block_id != nil } + + if block_reservations.empty? return 1 else - max_block_id = @reservations.map { |res| res.block_id }.max + max_block_id = block_reservations.map { |res| res.block_id }.max return max_block_id + 1 end end diff --git a/specs/admin_spec.rb b/specs/admin_spec.rb index 3235caab4..0911f8720 100644 --- a/specs/admin_spec.rb +++ b/specs/admin_spec.rb @@ -134,7 +134,7 @@ @hotel.create_block(5, @date_range) end @hotel.create_block(4, @date_range) - + proc { @hotel.reserve_room(@date_range) }.must_raise StandardError end end From d8ba4cb16999686c3d4b7227a67ac6e07c3efabe Mon Sep 17 00:00:00 2001 From: Caroline Nardi Date: Sun, 1 Apr 2018 23:15:24 -0700 Subject: [PATCH 25/25] Added design activity markdown file --- design-activity.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 design-activity.md diff --git a/design-activity.md b/design-activity.md new file mode 100644 index 000000000..b9d2a8d24 --- /dev/null +++ b/design-activity.md @@ -0,0 +1,43 @@ +###### What classes does each implementation include? Are the lists the same? +Both implementations include CartEntry, ShoppingCart, and Order classes. + +###### Write down a sentence to describe each class. +Implementation A: +* CartEntry stores the unit price and quantity for each item in a cart. +* ShoppingCart stores a list of the items in a cart. +* Order calculates the subtotal of all items in a cart, the sales tax, and the total. + +Implementation B: +* CartEntry stores the unit price and quantity for each item in a cart and calculates the price of that item. +* ShoppingCart stores a list of the items in a cart and calculates the subtotal for all items in the cart. +* Order calculates the sales tax and total for the cart. + +###### How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. +Implementation A: CartEntry and ShoppingCart are not dependent on any other class. Order is dependent on both ShoppingCart (to provide the list of items) and CartEntry (to provide the unit prices and quantities for each item). + +Implementation B: CartEntry is not dependent on any other class. ShoppingCart is dependent on CartEntry to provide prices for the items in the cart. Order is dependent on ShoppingCart to provide a list of items in the cart and the cart subtotal. + +###### What data does each class store? How (if at all) does this differ between the two implementations? +In both implementations CartEntry stores unit prices and quantities, Shopping Cart stores the list of items entered in the cart, and Order stores an instance of ShoppingCart. + +###### What methods does each class have? How (if at all) does this differ between the two implementations? +In Implementation A, CartEntry and ShoppingCart have only initialization methods, and Order has an initialization method and method to calculate the total price of the order. + +In Implementation B, CartEntry, ShoppingCart, and Order all have initialization methods and methods to calculate the price of an item, the subtotal for items in a shopping cart, and the total price of an order, respectively. + +###### Consider the Order#total_price method. In each implementation: +* **Is logic to compute the price delegated to "lower level" classes like ShoppingCart and CartEntry, or is it retained in Order?** +In Implementation A, the logic to compute the total price is all retained in the Order class. In Implementation B, the logic is delegated to the 'lower level' ShoppingCart and CartEntry classes. + +* **Does total_price directly manipulate the instance variables of other classes?** +I'm a little confused by what is meant by 'manipulation' -- to my mind, manipulation implies that the value assigned to an instance variable is modified. The total_price method does not modify the value of another classes' instance variable in either implementation. However, in Implementation A, the total_price method reads the values of the CartEntry classes' instance variables. In Implementation B, the total_price method does not directly read the values of other classes' instance variables. + +###### If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? +If items are cheaper if bought in bulk, we would need to modify the code to include a unit price that is dependent on the quantity. It would be easier to make the modification in Implementation B because the unit price could be altered within the existing CartEntry#price method (or alternatively, in a new CartEntry instance method), and no other changes to ShoppingCart or Order would be necessary. In Implementation A, a new instance method would probably be added to CartEntry to calculate the unit price based on the quantity, and the Order#total_price method would have to be modified to call that method rather than reading the value of CartEntry's unit price instance variable. + +###### Which implementation better adheres to the single responsibility principle? +Implementation B better adheres to the single responsibility principle because the purpose of each class is better encapsulated (more wholly contained) within that class than in Implementation A. + +###### Which implementation is more loosely coupled? +Implementation B is more loosely coupled. +