-
Notifications
You must be signed in to change notification settings - Fork 46
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
Michelle - Edges - Hotel #31
base: master
Are you sure you want to change the base?
Changes from 32 commits
8ed137f
02b9d70
3275135
524d46b
67da127
26bab8a
62b1ef5
eb9f6ac
d0e3543
61d1f0a
c7d10f0
85a6c96
926a499
7634b5a
307fe17
f82bec9
6a60823
f50d923
28b5efa
eeb5e62
93dc58b
faf6a0e
2cd1023
9178b35
49b4efa
c6f1445
be73585
e714797
0519f8e
b336c0f
2f30c19
0b7d7d2
2d48f68
1ec6730
c600777
a488922
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
*.gem | ||
*.rbc | ||
/.config | ||
/coverage/ | ||
coverage | ||
/InstalledFiles | ||
/pkg/ | ||
/spec/reports/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
require "date" | ||
|
||
require_relative 'calendar' | ||
require_relative 'reservation' | ||
require_relative 'room_block' | ||
|
||
module Hotel | ||
class BookingSystem | ||
attr_reader :rooms, :reservations, :room_blocks | ||
|
||
def initialize() | ||
@rooms = list_all_rooms() #<-- array of all room numbeers | ||
@reservations = [] | ||
@room_blocks = [] | ||
end | ||
|
||
def list_all_rooms() | ||
return (1..20).to_a | ||
end | ||
|
||
def construct_cal_checker(check_in:, check_out:) | ||
return Calendar.new(check_in: check_in, check_out: check_out) | ||
end | ||
|
||
def generate_res_id() | ||
if @reservations.empty? | ||
return 1 | ||
else | ||
return @reservations.max_by { |reservation| reservation.id}.id + 1 | ||
end | ||
end | ||
|
||
def generate_block_id() | ||
if @room_blocks.empty? | ||
return 1 | ||
else | ||
return @room_blocks.max_by { |block| block.id}.id + 1 | ||
end | ||
end | ||
|
||
def list_res_for_date(check_date) | ||
matching_res = @reservations.select { |reservation| reservation.has_date?(check_date) } | ||
|
||
return matching_res.empty? ? nil : matching_res | ||
end | ||
|
||
def list_avail_rooms_for_range(check_in:, check_out:) | ||
date_range = construct_cal_checker(check_in: check_in, check_out: check_out) | ||
|
||
booked_rooms = @reservations.select { |reservation| reservation.overlap?(date_range) }.map { |reservation| reservation.room_num } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would probably break this complex string of enumerables out across multiple lines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method has a couple of excellent examples of where your design allowed you to have loose coupling between classes. For example, when you loop through the list of reservations or blocks, instead of |
||
|
||
avail_rooms = @rooms - booked_rooms | ||
|
||
held_rooms = @room_blocks.select { |block| block.overlap?(date_range) }.map {|block| block.rooms} | ||
|
||
if !held_rooms.empty? | ||
held_rooms = held_rooms[0] # list within a list | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't quite do what you want. If you have two overlapping blocks, then you'll end up with an array like this: The array method |
||
|
||
avail_rooms -= held_rooms | ||
|
||
return avail_rooms.empty? ? nil : avail_rooms | ||
end | ||
|
||
def create_reservation(check_in:, check_out:) | ||
id = generate_res_id() #<-- create new reservation id | ||
avail_rooms = list_avail_rooms_for_range(check_in: check_in, check_out: check_out) #<-- grab first available room | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that you've broken out finding the list of available rooms as a separate helper method. That makes reading this method much easier. |
||
if avail_rooms.nil? | ||
raise StandardError, "No rooms are available for the given date range: #{check_in} - #{check_out}." | ||
else | ||
avail_room = avail_rooms[0] | ||
end | ||
|
||
new_reservation = Hotel::Reservation.new( | ||
id: id, | ||
room_num: avail_room, | ||
check_in: check_in, | ||
check_out: check_out) | ||
|
||
@reservations << new_reservation | ||
|
||
return new_reservation | ||
end | ||
|
||
def create_room_block(check_in:, check_out:, block_size:) | ||
avail_rooms = list_avail_rooms_for_range(check_in: check_in, check_out: check_out) | ||
|
||
if avail_rooms.nil? || avail_rooms.length < block_size | ||
raise StandardError, "Not enough rooms are available for the given date range: #{check_in} - #{check_out}." | ||
else | ||
avail_room = avail_rooms[0] | ||
end | ||
|
||
hold_rooms = avail_rooms[0..block_size-1] | ||
id = generate_block_id() | ||
|
||
new_room_block = Hotel::RoomBlock.new(id: id, check_in: check_in, check_out: check_out, rooms: hold_rooms) | ||
|
||
@room_blocks << new_room_block | ||
return new_room_block | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
require 'date' | ||
|
||
module Hotel | ||
class Calendar | ||
attr_reader :check_in, :check_out | ||
|
||
def initialize(check_in:, check_out:) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that you've made this its own class, but I'm not sure I agree with the name |
||
|
||
unless /\d{4}-\d{1,2}-\d{1,2}/.match(check_in) && /\d{4}-\d{1,2}-\d{1,2}/.match(check_out) | ||
raise StandardError, "Improper date format: date must be entered as YYYY-MM-DD." | ||
end | ||
|
||
@check_in = Date.parse(check_in) | ||
@check_out = Date.parse(check_out) | ||
|
||
unless @check_in < @check_out | ||
raise StandardError, "Invalid date range: end date must occur after start date." | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of raising a |
||
end | ||
|
||
def has_date?(other_date) | ||
other_date = Date.parse(other_date) | ||
if (other_date >= @check_in) && (other_date <= @check_out - 1) | ||
return true | ||
else | ||
return false | ||
end | ||
end | ||
|
||
def overlap?(other_dates) # param: Calendar obj | ||
if !(other_dates.check_in <= @check_out-1) || !(other_dates.check_out-1 >= @check_in) | ||
return false | ||
else | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of subtracting 1 here and using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the expression on line 31 will always produce true or false, you don't need an if statement. You could write: |
||
return true | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
require_relative 'calendar' | ||
|
||
module Hotel | ||
class Reservation < Calendar | ||
attr_reader :id, :room_num, :daily_rate | ||
|
||
def initialize(check_in:, check_out:, id:, room_num:, daily_rate: 200) | ||
super(check_in: check_in, check_out: check_out) | ||
|
||
@id = id.to_i | ||
@room_num = room_num.to_i | ||
@daily_rate = daily_rate.to_f | ||
|
||
end | ||
|
||
def total_stay_cost() | ||
length_in_days = @check_out - @check_in | ||
return @daily_rate * length_in_days | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
require_relative 'reservation' | ||
|
||
module Hotel | ||
class RoomBlock < Reservation | ||
attr_reader :block_size, :discount, :rooms, :status | ||
|
||
def initialize(id:, daily_rate: 200, check_in:, check_out:, discount:0, rooms:[], status: :available) | ||
super(id: id, daily_rate: daily_rate, check_in: check_in, check_out: check_out, room_num: room_num) | ||
|
||
unless !rooms.empty? | ||
raise StandardError, "Room blocks cannot be empty!" | ||
end | ||
|
||
@discount = discount/100.to_f | ||
@rooms = rooms # array of ints | ||
@status = status | ||
@block_size = rooms.length | ||
|
||
unless @block_size <= 5 && @block_size > 1 | ||
raise StandardError, "Room blocks must hold at least two rooms and at most five. You entered #{block_size} rooms." | ||
end | ||
end | ||
|
||
def show_status() | ||
rooms_hash = {} | ||
@rooms.each do |room| | ||
rooms_hash[room.to_s] = @status | ||
end | ||
return rooms_hash | ||
end | ||
|
||
def discounted_rate() | ||
return @daily_rate * (1-@discount) | ||
end | ||
|
||
def total_stay_cost_room() | ||
length_in_days = @check_out - @check_in | ||
return discounted_rate() * length_in_days | ||
end | ||
|
||
def total_stay_cost_block() | ||
return total_stay_cost_room() * @block_size | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
Calendar | ||
- add separate eror handling method or class | ||
- spec: add more edge cases: multi month stay? | ||
- in #overlap? maybe we don't need the object (so remove BookingSystem#construct_cal_checker and change date_range checks back to check_in, check_out | ||
|
||
Reservation | ||
- add hundredths place rounding for money | ||
- make @daily_rate a constant? | ||
|
||
RoomBlock | ||
- maybe RoomBlock shouldn't inherit from Reservation b/c I don't use the instance var @room_num in RoomBlock -- but got errors when I didn't include it in super() in initialize() | ||
- like Calendar, this class also has a lot of error handling going on --> split into methods or a class? | ||
- add more functionality (didn't finish wave 3), like maybe check whether a given room number exists within the block? | ||
|
||
BookingSystem | ||
- spec: #initialize: maybe here see if new reservations and room blocks can be added to @reservations and @room_blocks??? Not sure if this is the right place to test that. | ||
- spec: check in #initialize that list_all_rooms() worked | ||
- spec: more testing for #create_reservation?: reject same start-day overlaps, check to see if res are added to @reservations (here?) | ||
|
||
Overall | ||
- Finish Wave 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good use of an enumerable here.
For the return value, I think that an empty array fits better than
nil
here. An empty array is typically used to indicate "I found no matches" when you're returning a collection (it's a collection with no members).nil
is used when you're supposed to return one instance.