Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leaves - Tiffany #25

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ffd84bd
Created all the lib files. No content yet.
TiffanyChio Sep 3, 2019
45c3a5c
Created Room class. All it does is initialize Rooms
TiffanyChio Sep 3, 2019
b3cf64e
Added simple cov and lib files to test_helper.
TiffanyChio Sep 3, 2019
5e39d57
Created test for room.rb (initiation). Everything passes.
TiffanyChio Sep 3, 2019
b320079
Created initialize and helper methods for reservations. Untested.
TiffanyChio Sep 3, 2019
783424f
Fixed multi use of Date name in reservations variables. Fixed reserva…
TiffanyChio Sep 3, 2019
092ea03
added argument error checks for start and end date. Tests created and…
TiffanyChio Sep 3, 2019
023e763
Minor fix to total_cost in reservation.rb
TiffanyChio Sep 3, 2019
00455f0
added HotelDate class. No tests have been added or processed yet.
TiffanyChio Sep 3, 2019
2f10378
added HotelDate test file. All tests passed. HotelDate editted to ref…
TiffanyChio Sep 4, 2019
f09ba52
added make reservation method for system.rb
TiffanyChio Sep 4, 2019
42df5a2
Removed date range from reservation class and all tests still pass.
TiffanyChio Sep 4, 2019
f0c6e82
Tested make reservation method in system. Works great. Haven't done o…
TiffanyChio Sep 4, 2019
15f2043
Added tests for methods of hoteldate. find available room works for s…
TiffanyChio Sep 5, 2019
86b7cdb
Completed all tests for wave 2. Everything passes.
TiffanyChio Sep 5, 2019
df40439
Changed Reservation instantiation from total cost to cost)
TiffanyChio Sep 5, 2019
4e6f127
moved room generation from system to room class. started creating hot…
TiffanyChio Sep 5, 2019
fa6ab8b
tested hotelblock.rb and it passes. Still need to tweak hotelblock cr…
TiffanyChio Sep 6, 2019
48918bf
tested hotel block creation, appears to be working fine.
TiffanyChio Sep 6, 2019
67dbe11
last working code before we change hbs list over to hash
TiffanyChio Sep 6, 2019
76486fe
changed hotel blocks list over to hash. tests are still passing.
TiffanyChio Sep 6, 2019
bafa5ed
Completed wave 3. All tests pass
TiffanyChio Sep 6, 2019
b9b6026
changed hoteldate logic to reflect both reservations and hotel blocks.
TiffanyChio Sep 6, 2019
1c841a9
save before changing hoteldate to date
TiffanyChio Sep 6, 2019
dc88859
refactoring system.rb changed all the make reservations from string i…
TiffanyChio Sep 7, 2019
746d394
refactoring system.rb about to change list reservations over to objects
TiffanyChio Sep 7, 2019
18f55d2
complete refactor of system.rb
TiffanyChio Sep 7, 2019
5a4a8e4
last working commit before trying to change namespaces again
TiffanyChio Sep 7, 2019
551edc3
tentatively saving. nothing broke after changing hotelname class nome…
TiffanyChio Sep 7, 2019
5e24692
refactored room, reservation, hotelblock test files
TiffanyChio Sep 7, 2019
05dae1d
refactored all tests except for system
TiffanyChio Sep 7, 2019
be55658
added edge cases
TiffanyChio Sep 8, 2019
748f2e0
added hotel block testing
TiffanyChio Sep 8, 2019
273e3cf
clean up comments
TiffanyChio Sep 8, 2019
9e6cbd3
added refactor.txt file
TiffanyChio Sep 9, 2019
25aa0fa
added ideas to refactor.txt
TiffanyChio Sep 9, 2019
5f1c7b3
completed design_activity.md
TiffanyChio Sep 26, 2019
78862b7
made changes to more loosely couple objects in hotel
TiffanyChio Sep 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/custom_errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class FullOccupancyError < StandardError

Choose a reason for hiding this comment

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

This is a great example of defining your own appropriately named exception type.

end
21 changes: 21 additions & 0 deletions lib/hotelblock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'date'
require_relative 'reservation'

module Hotel
class HotelBlock < Reservation

Choose a reason for hiding this comment

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

Great use of inheritance.


attr_reader :status

# id here is the id of the hotel block
# shared by all rooms within same block
def initialize(id:, room:, start_date:, end_date:, cost:)
super
@status = :AVAILABLE
end

def change_status
@status = :UNAVAILABLE
end
end
end

34 changes: 34 additions & 0 deletions lib/hoteldate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require_relative 'room'
require_relative 'reservation'

module Hotel
class Date

attr_reader :id, :occupied

def initialize(id)
# id is a date object
@id = id
# occupied is a hash with key room_id and values of reservation instance
@occupied = {}
end

# Adds either reservation or hotel blocks to @occupied hash.
# Below the parameter is called "reservation" but
# logic also works for hotel blocks.
def add_occupancy(reservation)
@occupied[reservation.room] = reservation
end

# Returns only reservations and not hotel blocks as per specs.
def list_reservations
return @occupied.values.select { |value| value.class == Hotel::Reservation }

Choose a reason for hiding this comment

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

Ruby will be looking for names locally first, prioritizing local names, since they're all in the same module, there's no need to namespace Hotel::Reservation. Reservation should just work! Same with the rest of the namespacing in the code.

Choose a reason for hiding this comment

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

clever use of select!

end

# Returns all occupied rooms, either reserved or blocked.
def rooms_unavailable
return @occupied.keys
end

end
end
26 changes: 26 additions & 0 deletions lib/reservation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'date'
require_relative 'room'

module Hotel
class Reservation

attr_reader :id, :room, :start_date, :end_date, :cost

def initialize(id:, room:, start_date:, end_date:, cost: room.cost)
if end_date <= start_date
raise ArgumentError, 'End date must be after start date.'
end

@id = id
@room = room # this is a room object
@start_date = start_date # this is a date instance
@end_date = end_date # this is a date instance
@cost = cost
end

def find_total_cost
return (@end_date - @start_date) * @cost.to_f
end

end
end
22 changes: 22 additions & 0 deletions lib/room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Hotel
class Room

attr_reader :id, :cost

def initialize(id, cost)
@id = id
@cost = cost
end

def self.generate_rooms
array_of_room_obj = []

(1..20).each do |i|
array_of_room_obj << Hotel::Room.new(i, 200.0)
end

return array_of_room_obj
end

end
end
152 changes: 152 additions & 0 deletions lib/system.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
require 'date'

require_relative 'room'
require_relative 'reservation'
require_relative 'hoteldate'
require_relative 'hotelblock'
require_relative 'custom_errors'

module Hotel
class System

attr_reader :rooms, :reservations, :dates, :hotelblocks

def initialize
@rooms = Hotel::Room.generate_rooms

Choose a reason for hiding this comment

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

Nice use of a class method.

@reservations = []
@dates = []

Choose a reason for hiding this comment

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

A comment to tell us what @dates is meant to hold would be useful, as it's not immediately obvious.


# Keys will be hotel block ids (shared by all rooms within block).
# Values will be arrays of hotel block objects within same block.
# Each array element represents one room.
@hotelblocks = {}
end

def find_room(room_id)
return @rooms.find { |room| room.id == room_id }
end

def find_date(date_obj)
return @dates.find { |hotel_date| hotel_date.id == date_obj }
end

def find_all_available_rooms(start_date, end_date)
current_date = start_date.dup
available_rooms = @rooms.dup

until current_date == end_date
hotel_date = find_date(current_date)
available_rooms -= hotel_date.rooms_unavailable if hotel_date
current_date += 1
end

return available_rooms
end

def make_reservation(start_date, end_date)
id = @reservations.length + 1

# assign the first available room to the reservation
room = find_all_available_rooms(start_date, end_date)[0]
raise FullOccupancyError.new('No rooms available for the date range.') if room == nil

new_reservation = Hotel::Reservation.new(id: id, room: room, start_date: start_date, end_date: end_date)

@reservations << new_reservation
add_to_dates(start_date, end_date, new_reservation)

return new_reservation
end

# new_occupancy refers to reservation or hotel block to be added to dates
def add_to_dates(start_date, end_date, new_occupancy)
current_date = start_date.dup

until current_date == end_date
date_obj = find_date(current_date)

if date_obj
date_obj.add_occupancy(new_occupancy)
else
new_date_obj = Hotel::Date.new(current_date)
@dates << new_date_obj
new_date_obj.add_occupancy(new_occupancy)
end

current_date += 1
end
end

def list_reservations_for(date)
hotel_date = find_date(date)

if hotel_date
return hotel_date.list_reservations
else
return nil
end
end

def create_hotelblock(start_date:, end_date:, hb_rooms:, discount_rate:)
# hb_rooms is an array of room_ids
hb_rooms.map! { |room_id| find_room(room_id) }

if hb_rooms.length > 5
raise ArgumentError, 'A block can only contain a maximum of 5 rooms.'
end

available_rooms = find_all_available_rooms(start_date, end_date)

# Change hb_rooms into a array of boolean values for whether they are available
# Does the array contain false (room is not available)?
# If so raise an error.
if hb_rooms.map{ |room| available_rooms.include? room}.include? false
raise ArgumentError, 'Block contains room that is already booked.'
end

hb_id = @hotelblocks.length + 1
@hotelblocks[hb_id] = []

hb_rooms.each do |room|
new_hotel_block = Hotel::HotelBlock.new(id: hb_id, room: room, start_date: start_date, end_date: end_date, cost:discount_rate.to_f)

@hotelblocks[hb_id] << new_hotel_block

add_to_dates(start_date, end_date, new_hotel_block)
end
end

def find_open_rooms_from_block(hb_id)
hotel_blocks = @hotelblocks[hb_id]
open_rooms = hotel_blocks.select { |hotel_block| hotel_block.status == :AVAILABLE}

# Return open rooms as room IDs rather than Room objects for end user readability.
open_rooms.map! { |hotel_block| hotel_block.room.id}

return open_rooms
end

def reserve_from_block(hb_id, room_id)
unless find_open_rooms_from_block(hb_id).include? room_id
raise ArgumentError, 'The room you are trying to book is not available.'
end

# Change room status so room can no longer be booked.
hotel_block = @hotelblocks[hb_id].find {|hb| hb.room.id == room_id}
hotel_block.change_status

# Reservation is hardcoded to use hotel block dates and cost.
id = @reservations.length + 1
room = find_room(room_id)
start_date = hotel_block.start_date
end_date = hotel_block.end_date
cost = hotel_block.cost

new_reservation = Hotel::Reservation.new(id: id, room: room, start_date: start_date, end_date: end_date, cost: cost)

@reservations << new_reservation
end

end
end

19 changes: 19 additions & 0 deletions refactors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Possible future changes:
- limit reservations to 30 days at a time maximum to discourage users from making long reservations
- include validation for date ranges to throw error if date range is over 30 days

- change Hotel::HotelBlock to simply Hotel::Block
- cmd + shift + F for HotelBlock and hotel_block across all files
- change delete hotel or hotel_ (case insensitive)

Possible but improbable future changes:
- remove room and date class
- requires rewriting the entire code
- reduces dependencies
- however, will shift most of the responsibilities to system class
- difficult to flesh out concrete ideas
- room class was created to allow for easier refactoring if room pricing structure changed
- date class allows for more readable code by creating another database that acts as a calendar
- tracking room availability can be done by iterating through all reservations & hotel blocks to look for date matches
- if match, mark the room as unavailable
- or if match, add the reservation to an array to be returned after iteration complete
73 changes: 73 additions & 0 deletions test/hotelblock_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require_relative 'test_helper'

describe "HotelBlock class" do
describe "HotelBlock instantiation" do
before do
room_15 = Hotel::Room.new(15, 200.00)
start_time = ::Date.parse('2019-09-03')
end_time = ::Date.parse('2019-09-08')

@hotelblock = Hotel::HotelBlock.new(id: 101, room: room_15, start_date: start_time, end_date: end_time, cost: 100)
end

it "is an instance of HotelBlock" do
expect(@hotelblock).must_be_kind_of Hotel::HotelBlock
end

it "is set up for specific attributes and data types" do
[:id, :room, :start_date, :end_date, :cost, :status].each do |prop|
expect(@hotelblock).must_respond_to prop
end

expect(@hotelblock.id).must_be_instance_of Integer
expect(@hotelblock.room).must_be_instance_of Hotel::Room
expect(@hotelblock.start_date).must_be_instance_of Date
expect(@hotelblock.end_date).must_be_instance_of Date
expect(@hotelblock.status).must_be_instance_of Symbol
end

it "correctly assigns cost value" do
expect(@hotelblock.cost).must_be_close_to 100.0
end

it "correctly calculates total_cost" do
expect(@hotelblock.find_total_cost).must_be_close_to 500.0
end
end

describe "the status of hotelblock" do
before do
room_15 = Hotel::Room.new(15, 200.00)
start_time = ::Date.parse('2019-09-03')
end_time = ::Date.parse('2019-09-08')

@hotelblock = Hotel::HotelBlock.new(id: 101, room: room_15, start_date: start_time, end_date: end_time, cost: 100)
end

it "initializes hotelblocks with :AVAILABLE" do
expect(@hotelblock.status).must_equal :AVAILABLE
end

it "can be changed to :UNVAILABLE with the change_status method" do
@hotelblock.change_status
expect(@hotelblock.status).must_equal :UNAVAILABLE
end
end

describe "raises an exception when an invalid date range is provided" do
before do
@room_15 = Hotel::Room.new(15, 200.00)
@start_time = ::Date.parse('2019-09-03')
end

it "raises an error if end date is before start date" do
end_time = ::Date.parse('2019-09-01')

expect{Hotel::HotelBlock.new(id: 101, room: @room_15, start_date: @start_time, end_date: end_time, cost: 100)}.must_raise ArgumentError
end

it "raises an error if end date is on same day as start date" do
expect{Hotel::HotelBlock.new(id: 101, room: @room_15, start_date: @start_time, end_date: @start_time, cost: 100)}.must_raise ArgumentError
end
end
end
Loading