Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
f056858
adds simplecov and reservation class
Mar 2, 2020
3144873
creates reservation class and tests instantiation
Mar 2, 2020
19952f0
removes nights variable (this will be implemented in an instance meth…
Mar 2, 2020
90bd045
adds pseudocode for wave 1 requirements
Mar 2, 2020
a00f3af
adds front_desk.rb to the required lib files
Mar 2, 2020
306f359
adds frontdesk class and tests for instantiation
Mar 2, 2020
0e64e25
adds a room instance varible
Mar 2, 2020
97eb647
adds a method and test for accessing a list of all rooms
Mar 2, 2020
35e9edd
changes tests to create Date objects and lib code to expect Date objects
Mar 2, 2020
866a9d2
adds reserve_room method and tests, refactors FrontDesk instantiation…
Mar 2, 2020
de5b3ae
throws an exception when an invalid date range is provided
Mar 3, 2020
394d755
defines a method and test that calculates the nights of stay
Mar 3, 2020
418c78f
defines a method and test to calculate total cost
Mar 3, 2020
0b97375
defines date_range method and tests
Mar 3, 2020
bbe2fda
adds .DS_Store
Mar 3, 2020
aaee32a
removes skip_until tests
Mar 3, 2020
78c9850
adds comments for testing and refactoring opportunities
Mar 3, 2020
20c3bb7
adds #add_reservation and #reservations_by_room methods and tests
Mar 3, 2020
1df375f
adds additional tests and functionality for #reserve_room
Mar 3, 2020
0b9fee8
adds a test to check whether #reserve_room assigns a valid room number
Mar 3, 2020
4aa4764
adds #reservations_by_date method and tests
Mar 3, 2020
9b145a8
adds a test for #reservations_by_room to check whether the correct Re…
Mar 3, 2020
53a82f1
cleans up tests for #date_range
Mar 3, 2020
3b79d82
refactors #reservation_by_room method for efficiency
Mar 4, 2020
37d2fd8
adds test to check whether it can access the list of all reservations
Mar 4, 2020
31d256d
adds tests for #overlap? method
Mar 4, 2020
39b6c93
fixes #reservations_by_room so that it returns reservations with a st…
Mar 5, 2020
030b0d3
changes test and variable names for better readability
Mar 5, 2020
d3328f8
fixes error with range_end in the #overlap? tests
Mar 5, 2020
71f6fa0
adds #overlap? method
Mar 5, 2020
ba4e102
adds method and tests for #find_available_room
Mar 5, 2020
7f3d5ff
adds test for #overlap? method
Mar 5, 2020
2aab015
amends tests for #find_available_room, adds a new test variable, adds…
Mar 5, 2020
f796015
adds #find_available_room method
Mar 5, 2020
331bf77
adds a test for #reserve_room to check that an exception is thrown wh…
Mar 5, 2020
abbe760
calls #find_available_room in the #reserve_room method, throws an exc…
Mar 5, 2020
71fa3c9
refactors #find_available_room method
Mar 5, 2020
9abeb0b
adds a test to check attributes and data types during Reservation ins…
Mar 5, 2020
a5d29a1
fixes small numerical errors with reservation ids in the test arrange…
Mar 6, 2020
86db0ec
adds a comment about a way to refactor #find_available_rooms so its m…
Mar 6, 2020
b87155e
adds a DateRange class and tests instantiation
Mar 6, 2020
db6f02c
transfers #nights method from Reservation to DateRange and updates va…
Mar 6, 2020
a3e4a2f
transfers #overlap? method and tests, updates variable names
Mar 6, 2020
4d9eaf1
removes unnecessary @dates instance variable
Mar 6, 2020
3966ea3
adds #include? method and tests, removes inheritance from Range class
Mar 6, 2020
5866515
removes methods and test that is redundant with DateRange class, revi…
Mar 6, 2020
a35d800
changes #reservations_by_room method and tests so that they work with…
Mar 7, 2020
8adbae0
adds daterange.rb to libs list
Mar 7, 2020
03cd299
changes #find_available_room method and tests so that it works with t…
Mar 7, 2020
d00e780
revises comment on #find_available_method regarding reducing space co…
Mar 7, 2020
26f8657
changes #reserve_room method and tests so that they work with DateRan…
Mar 7, 2020
2a561de
creates a Block class and tests instantiation
Mar 7, 2020
dc157fa
adds block.rb to libs list
Mar 7, 2020
676dbb3
changes #reservations_by_room method so that it includes reservations…
Mar 7, 2020
ba9ee7e
adds #each method and test
Mar 7, 2020
aa4ccbf
changes #nights method so it returns an Integer, adds two more tests …
Mar 7, 2020
554bfcc
changes #include? method to a binary search
Mar 7, 2020
623bc3e
removes #each method and tests
Mar 7, 2020
e7b0903
changes #overlap? method so it returns true for DateRanges whose star…
Mar 7, 2020
c47e6e7
refactors #reservations_by_room method and fixes #find_available_room…
Mar 7, 2020
7e3acf0
adds a :block attribute to the Reservation class
Mar 7, 2020
b6930c2
removes first return in #include? method to ensure that the tests are…
Mar 7, 2020
4ca32d3
changes test name for #include? method
Mar 7, 2020
6564809
adds a comment to #reserve_room
Mar 7, 2020
87c63d5
revises Block instantiation and tests
Mar 7, 2020
cd15bfa
throws an exception if the list of rooms is less than 2 or greater th…
Mar 7, 2020
de91b4e
adds a #reserve_block method and tests whether it can create a Block
Mar 8, 2020
38bc2ff
throws an exception if one of the rooms is unavailable for the given …
Mar 8, 2020
96cb9b3
adds an id attribute to the Block class, tests for instantiation
Mar 8, 2020
2670238
adds a @blocks instance variable and tests for instantiation
Mar 8, 2020
bbc8a82
adds an #add_block method and test
Mar 8, 2020
ba556d2
adds tests for #reserve_block method
Mar 8, 2020
b00b3cc
adds the new Block to the @blocks array within the #reserve_block method
Mar 8, 2020
fa831a1
revises tests for #find_available_room to check whether the availabil…
Mar 8, 2020
86050da
realizes that my understanding of hotel blocks and reservations was i…
Mar 8, 2020
08621c5
adds DateRange class and removes Date gem from dependencies
Mar 9, 2020
16cdb77
revises test for 'returns all rooms if there are no Reservations' so …
Mar 9, 2020
ff5ebed
revises #find_available_rooms method so it respects Blocks as well as…
Mar 9, 2020
28db7f9
fixes #reserve_room method so it throws an exception if no rooms are …
Mar 9, 2020
5f3adcb
adds tests for #find_block method and pseudocode for a new test in #r…
Mar 9, 2020
fc88917
adds the #find_block method, adds pseudocode for methods that will al…
Mar 9, 2020
2046f2e
removes unnecessary comments
Mar 9, 2020
4de7cee
adds a new rate attribute to the Reservation class and tests for inst…
Mar 9, 2020
f1023dc
changes rate attribute into an optional keyword for instantiation wit…
Mar 9, 2020
e78198e
adds #find_available_room_in_block method and tests
Mar 9, 2020
9780529
adds tests for #reserve_room_in_block method
Mar 9, 2020
6a9ee84
revises test that checks for unique id numbers in #reserve_room_in_block
Mar 9, 2020
6c13c76
adds #reserve_room_in_block method
Mar 9, 2020
62163c9
adds test to #reservations_by_date to check that it can see a reserva…
Mar 9, 2020
9b67631
adds test to #reserve_block that checks whether an exception is throw…
Mar 9, 2020
b17de34
revises test in #reserve_block that checks whether an exception is th…
Mar 9, 2020
2f1f41c
removes redundant code from the #reserve_block method
Mar 9, 2020
1aa8991
removes require_relative 'date_range'
Mar 9, 2020
634fc4b
adds a test to #total_cost that checks whether it accurately calculat…
Mar 9, 2020
0e3bff9
adds functionality and test to check that custom rates can be convert…
Mar 9, 2020
bd7e40d
adds functionality that allows for setting different rates for differ…
Mar 9, 2020
edff9dd
reorganizes and refactors methods, adds comments
Mar 9, 2020
f200014
creates refactors.txt
Mar 9, 2020
3363369
removes require_relative 'reservation'
Mar 9, 2020
c6689d5
revises notes for #find_available_room
Mar 9, 2020
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store

Choose a reason for hiding this comment

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

*.gem
*.rbc
/.config
Expand Down
17 changes: 17 additions & 0 deletions lib/block.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Hotel
class Block

Choose a reason for hiding this comment

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

This is too much indentation. You should configure VS Code to indent by two spaces.

attr_reader :id, :rooms, :rate, :date_range

def initialize(id:, rooms:, rate:, start_date:, end_date:)
@id = id
@rooms = rooms
@rate = rate
@date_range = Hotel::DateRange.new(
start_date: start_date,
end_date: end_date
)

raise ArgumentError.new("Block must contain between 2 and 5 rooms!") if @rooms.length < 2 || @rooms.length > 5

Choose a reason for hiding this comment

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

I'd generally recommend raising errors like this before initializing fields. It doesn't matter here but can be helpful as constructors get more complex.

Also, it's helpful to include the bad argument when you raise an ArgumentError:

Suggested change
raise ArgumentError.new("Block must contain between 2 and 5 rooms!") if @rooms.length < 2 || @rooms.length > 5
raise ArgumentError.new("Block must contain between 2 and 5 rooms! Got: #{@rooms}") if @rooms.length < 2 || @rooms.length > 5

end
end
end
38 changes: 38 additions & 0 deletions lib/date_range.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module Hotel
class DateRange
attr_accessor :start_date, :end_date

def initialize(start_date:, end_date:)
@start_date = start_date
@end_date = end_date

raise ArgumentError.new("Invalid date range!") if @start_date > @end_date
end

def nights
return (@end_date - @start_date).to_i
end

def overlap?(other)
return @start_date <= other.end_date && @end_date >= other.start_date
end

def include?(date)
low = 0
high = self.nights

while low <= high
mid = (low + high) / 2
if (@start_date + mid) == date
return true
elsif (@start_date + mid) > date
high = mid - 1
elsif (@start_date + mid) < date
low = mid + 1
end
end

return false
Comment on lines +21 to +35

Choose a reason for hiding this comment

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

This is cool but I'm pretty sure binary search is overkill here.

I think you can just do something like return @start_date < date && date < @end_date (after fixing the off by one error that's probably there...)

end
end
end
103 changes: 103 additions & 0 deletions lib/front_desk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
module Hotel
class FrontDesk
attr_reader :rooms, :reservations, :blocks

def initialize
@rooms = (1..20).to_a
@reservations = []
@blocks = []
end

def add_reservation(reservation)
@reservations << reservation
end

def reservations_by_room(room, date_range)
return @reservations.select { |reservation|
reservation.room == room && reservation.date_range.overlap?(date_range)
}
end

def reservations_by_date(date)
return @reservations.select { |reservation|
reservation.date_range.include?(date)
}
end

def find_available_room(date_range)
unavailable_rooms = @reservations.map { |reservation|
reservation.room if reservation.date_range.start_date < date_range.end_date && reservation.date_range.end_date > date_range.start_date
}
@blocks.each { |block|
(block.rooms).each { |room| unavailable_rooms << room } if block.date_range.start_date < date_range.end_date && block.date_range.end_date > date_range.start_date
} # This method is very expensive. See refactors.txt for details.
available_rooms = @rooms - unavailable_rooms

raise ArgumentError.new("No rooms available for that date range!") if available_rooms.empty?
return available_rooms
end
Comment on lines +27 to +38

Choose a reason for hiding this comment

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

This method isn't as expensive as you think. Or at least it's not much worse than an optimal solution.

For space complexity you can't do better than O(n) (because you might need to return an array of every room if they're all available).

For time complexity it's a little trickier. Using a clever data structure you can get individual reservation comparison down to O(log(n)) but that still only gets you O(n*log(n)).

If you use a data structure that O(r) where r is the number of days in all reservations then you can get the time down to O(rd) where d is the number of days in the input range but that uses a lot of space.

Off the top of my head I can't think of a solution that performs well in both space and time so this overall seems pretty reasonable. Some operations are just inherently expensive no matter how well you code your solution.


def reserve_room(date_range, rate = 200.0)
new_reservation = Hotel::Reservation.new(
id: @reservations.length + 1,
room: find_available_room(date_range).first,
rate: rate.to_f,
start_date: date_range.start_date,
end_date: date_range.end_date
Comment on lines +45 to +46

Choose a reason for hiding this comment

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

I probably would have just stored the DateRange object itself here instead of breaking it apart.

)
add_reservation(new_reservation)
return new_reservation
end

def add_block(block)
@blocks << block
end

def find_block(id)
block = @blocks.find { |block| block.id == id }
raise ArgumentError.new("No blocks with the given ID!") if block.nil?
return block
end

def reserve_block(rooms, rate, date_range)
rooms.each { |room|
raise ArgumentError.new("At least one of the rooms is unavailable for the given date range!") if !find_available_room(date_range).include?(room)
}

new_block = Hotel::Block.new(
id: @blocks.length + 1,
rooms: rooms,
rate: rate,
start_date: date_range.start_date,
end_date: date_range.end_date
)
add_block(new_block)
return new_block
end

def find_available_room_in_block(id)
block = find_block(id)
unavailable_rooms = @reservations.map { |reservation|
reservation.room if reservation.block == block.id
}
available_rooms = block.rooms - unavailable_rooms

raise ArgumentError.new("No rooms available in the given block!") if available_rooms.empty?
return available_rooms
end

def reserve_room_in_block(id, room)
block = find_block(id)
new_reservation = Hotel::Reservation.new(
id: @reservations.length + 1,
room: room,
block: block.id,
rate: block.rate,
start_date: block.date_range.start_date,
end_date: block.date_range.end_date
)
add_reservation(new_reservation)
return new_reservation
end
end
end
24 changes: 24 additions & 0 deletions lib/reservation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require_relative 'date_range'

module Hotel
class Reservation
attr_reader :id, :room, :block, :rate, :date_range

def initialize(id:, room:, block: false, rate: 200.0, start_date:, end_date:)
@id = id
@room = room
@block = block
@rate = rate.to_f
@date_range = Hotel::DateRange.new(
start_date: start_date,
end_date: end_date
)

raise ArgumentError.new("Invalid rate!") if rate.class != Float

Choose a reason for hiding this comment

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

If you're going to check types you should probably use kind_of? to allow subclassing.

Suggested change
raise ArgumentError.new("Invalid rate!") if rate.class != Float
raise ArgumentError.new("Invalid rate!") unless rate.kind_of?(Float)

end

def total_cost
return @date_range.nights * @rate
end
end
end
43 changes: 43 additions & 0 deletions refactors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
=================
lib/front_desk.rb
=================

1) class FrontDesk
- Since the program will be used by employees of the hotel and not the general public, I thought FrontDesk would be more descriptive. But if the program is ported to an online reservation system that is open to the general public, the class name should probably change to HotelController or ReservationSystem.

2) #find_available_room
- This method is very expensive because it iterates through two arrays of unknown size (time complexity is O(n^2)) and it creates a new array in memory each time it adds a room to the unavailable_rooms list (space complexity is O(n)).
- If I created a Room class that knew its Reservations and/or the Blocks it belonged to during a DateRange, I could refactor this method to just use the #reject enumerable, rejecting any rooms that met the existing conditionals.

3) any method that has date_range as a parameter
- I was visualizing this program as the backend of a digital form with separate fields for the start and end dates of range (if the function required that information). Each time the 'submit' button is pressed, the program would collect the values of these fields and use them to instantiate a new DateRange object, then pass that object into the method. This is why the start_date and end_date attributes are both readable and writable, so they can be written from outside the program. However, I'm not sure if this will work as intended, so it's possible that many of my tests are passing superficially!

4) #reserve_room & #reserve_room_in_block
- I may be able to merge these methods into one, because a Reservation can be instantiated without a block and rate attribute.
- If I create a Room class, I could have the rate stored in each Room instead of having it passed into these methods.
- But if the program is ported to an online reservation system that is open to the general public, these methods should stay separate because the general public is not allowed to reserve rooms in blocks.

==================
lib/reservation.rb
==================

1) #rate & #total_cost
- If I create a Room class, I could have the rate stored in each Room instead of having it passed into a new Reservation.
- The #total_cost method would then call the room.rate

2) #start_date & #end_date
- I was thinking that since these attributes can be written over in the DateRange class, they should probably not be stored anywhere else. But if each Reservation was able to call its start_date and end_date, this would simplify some method calls in the FrontDesk class. Perhaps this is an opportunity for inheritance (i.e., class Reservation < DateRange)?

=================
lib/date_range.rb
=================

1) #include?
- An earlier version simply returned (start_date..end_date).include?(date), which creates an instance of the Range class, which already has an include? method. I'm wondering if this was a better design, but I also wanted to avoid dependencies (if the Range class changes in the future, any method that creates an instance of that class might break). Instead, I created a binary search method that is not dependent on an additional class.

============
lib/block.rb
============

1) class Block
- If I create a Room class, there could be an opportunity for inheritance here (i.e., a Block is a Room), which could help with the FrontDesk#find_available_room (see notes above)
50 changes: 50 additions & 0 deletions test/block_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require_relative 'test_helper'

describe "Block class" do
before do
@block = Hotel::Block.new(
id: 1,
rooms: (1..5).to_a,
rate: 150.0,
start_date: Date.new(2020,3,2),
end_date: Date.new(2020,3,5)
)
end

describe "Block instantiation" do
it "is an instance of Block" do
expect(@block).must_be_kind_of Hotel::Block
end

it "is set up for specific attributes and data types" do
[:id, :rooms, :rate, :date_range].each do |attribute|
expect(@block).must_respond_to attribute
end

expect(@block.id).must_be_kind_of Integer
expect(@block.rooms).must_be_kind_of Array
expect(@block.rate).must_be_kind_of Float
expect(@block.date_range).must_be_kind_of Hotel::DateRange
end
Comment on lines +19 to +28

Choose a reason for hiding this comment

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

Something confusing happened with your indentation here.


it "throws an exception if the list of rooms is greater than 5" do
expect{ Hotel::Block.new(
id: 1,
rooms: (1..6).to_a,
rate: 150.0,
start_date: Date.new(2020,3,2),
end_date: Date.new(2020,3,5)
) }.must_raise ArgumentError
end

it "throws an exception if the list of rooms is less than 2" do
expect{ Hotel::Block.new(
id: 1,
rooms: [1],
rate: 150.0,
start_date: Date.new(2020,3,2),
end_date: Date.new(2020,3,5)
) }.must_raise ArgumentError
end
end
end
Loading