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

Michelle - Edges - Hotel #31

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8ed137f
created booking_system and room initialize vars plus tests
kangazoom Sep 4, 2018
02b9d70
created room class and test + updated booking system class to begin t…
kangazoom Sep 4, 2018
3275135
Created Reservation#dates_reserved + tests
kangazoom Sep 4, 2018
524d46b
added BookingSystem methods to create, add, and load reservations. Al…
kangazoom Sep 4, 2018
67da127
changed Room#initialize args to hash input + adjusted tests
kangazoom Sep 5, 2018
26bab8a
Added additional tests and error handling for Room#initialize
kangazoom Sep 5, 2018
62b1ef5
Changed BookingSystem#create_reservation and #add_reservation to acce…
kangazoom Sep 5, 2018
eb9f6ac
updated BookingSystem tests (var names) and added tests for #find_roo…
kangazoom Sep 5, 2018
d0e3543
began adding tests for Room#add_reservation
kangazoom Sep 5, 2018
61d1f0a
fixed bugs in BookingSystem tests; #find_room and #create_reservation
kangazoom Sep 5, 2018
c7d10f0
Fixed bugs in BookingSystem tests for #create_reservation
kangazoom Sep 5, 2018
85a6c96
added and tested Reservation #dates_reserved and #total_stay_cost
kangazoom Sep 5, 2018
926a499
Did a lot of work on tests for Reservation#initialize and #dates_rese…
kangazoom Sep 5, 2018
7634b5a
saving whatever work i was doing before hotel temp check; i can't rem…
kangazoom Sep 6, 2018
307fe17
fixed bug in Room #add_reservation method + test
kangazoom Sep 6, 2018
f82bec9
Added some more tests for Room regarding @reservations list
kangazoom Sep 7, 2018
6a60823
added new date format error handling for proper date format
kangazoom Sep 7, 2018
f50d923
added tests for Reservation @daily_rate
kangazoom Sep 7, 2018
28b5efa
added skeletons/pseudocode for BookingSystem changes and methods invo…
kangazoom Sep 7, 2018
eeb5e62
Commented out Room class and tests --> first step toward removal
kangazoom Sep 7, 2018
93dc58b
Spruced up tests in BookingSystem (#initialize and #list_all_rooms)
kangazoom Sep 7, 2018
faf6a0e
experimenting with Calendar
kangazoom Sep 8, 2018
2cd1023
Added inheritance to Reservation from Calendar and changed hash initi…
kangazoom Sep 8, 2018
9178b35
added #overlap to Calendar and wrote a bunch of tests for nominal and…
kangazoom Sep 8, 2018
49b4efa
added has_date? to Calendar and used it to list reservations by date …
kangazoom Sep 9, 2018
c6f1445
Rewrote BookingSystem #list_available_rooms_for_range to pass all tests
kangazoom Sep 10, 2018
be73585
revamped BookingSystem #create_reservation to pass all tests
kangazoom Sep 10, 2018
e714797
Added RoomBlock class and a few tests plus cost calculating functiona…
kangazoom Sep 10, 2018
0519f8e
Added a block id generator to BookingSystem
kangazoom Sep 10, 2018
b336c0f
created ability to make a room block in BookingSystem
kangazoom Sep 10, 2018
2f30c19
tinkered around more in BookingSystem to make RoomBlock a reality
kangazoom Sep 10, 2018
0b7d7d2
combed through my code and added todo+question comments to refactors.txt
kangazoom Sep 10, 2018
2d48f68
answered design activity questions
kangazoom Sep 30, 2018
1ec6730
fixed spacing in markdown file
kangazoom Sep 30, 2018
c600777
added method to Calendar to calculate nights reserved; edited Reserva…
kangazoom Oct 1, 2018
a488922
edited RoomBlock #total_stay_cost_room to incorporate Calendar's #nig…
kangazoom Oct 1, 2018
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
Binary file added .DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*.gem
*.rbc
/.config
/coverage/
coverage
/InstalledFiles
/pkg/
/spec/reports/
Expand Down
3 changes: 1 addition & 2 deletions Guardfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
guard :minitest, bundler: false, rubygems: false do
# with Minitest::Spec
guard :minitest, bundler: false, autorun: false, rubygems: false do # with Minitest::Spec
watch(%r{^spec/(.*)_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
Expand Down
104 changes: 104 additions & 0 deletions lib/booking_system.rb
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

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.

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 }

Choose a reason for hiding this comment

The 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.

Choose a reason for hiding this comment

The 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 BookingSystem knowing about the details of how a Reservation stores dates, it calls a method and lets Reservation take care of the details.


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

Choose a reason for hiding this comment

The 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: [[1, 2, 3], [7, 8, 9]], but your code will only grab the first sub-array.

The array method .flatten does what you're looking for.


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

Choose a reason for hiding this comment

The 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
38 changes: 38 additions & 0 deletions lib/calendar.rb
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:)

Choose a reason for hiding this comment

The 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 Calendar. Calendar implies that it's tracking a list of events, when this just tracks a start and end date. Something like DateRange or CalendarBooking might be a better 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

Choose a reason for hiding this comment

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

Instead of raising a StandardError, best practice is to define your own appropriately named exception type that inherits from StandardError and raise that. The reason is that StandardError is very broad, and if the code that calls this method has to rescue StandardError, that will catch things like ArgumentErrors and NameErrors that indicate true bugs.

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

Choose a reason for hiding this comment

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

Instead of subtracting 1 here and using <=, you can use <.

Choose a reason for hiding this comment

The 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 !(other_dates.check_in <= @check_out-1) || !(other_dates.check_out-1 >= @check_in)

return true
end
end
end
end
21 changes: 21 additions & 0 deletions lib/reservation.rb
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
45 changes: 45 additions & 0 deletions lib/room_block.rb
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
21 changes: 21 additions & 0 deletions refactors.txt
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
Loading