Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
7acc072
Added simplecov to spec_helper.rb
krsmith7 Sep 4, 2018
308e03f
Edited spacing between require elements in spec_helper
krsmith7 Sep 4, 2018
7230317
Added hotel.rb file
krsmith7 Sep 4, 2018
89ea0f1
Added hotel_spec.rb
krsmith7 Sep 4, 2018
8e0c9de
Edited first line of Guardfile to be compatible with simplecov gem
krsmith7 Sep 4, 2018
3d424bf
Created room class
krsmith7 Sep 4, 2018
f1954c1
Created booking_manager class for administrator User stories
krsmith7 Sep 4, 2018
25e3199
Deleted hotel.rb
krsmith7 Sep 4, 2018
78e0065
Deleted hotel_spec.rb
krsmith7 Sep 4, 2018
9c891a6
Added module Hotel to class Room and created Room#initialize
krsmith7 Sep 4, 2018
650f522
Added pseudocode to class BookingManager for methods to populate room…
krsmith7 Sep 4, 2018
a8c928f
Added BookingManager#populate_room_list to create list of hotel rooms
krsmith7 Sep 4, 2018
127b6cb
Added instance variable for availabiity and Room#check_availability p…
krsmith7 Sep 4, 2018
5a6f954
Added booking_manager_spec
krsmith7 Sep 4, 2018
8602647
Added require relative booking manager to spec_helper
krsmith7 Sep 4, 2018
fcceb7b
Added tests for BookingManager instantiation
krsmith7 Sep 4, 2018
5a0c061
Added method to make reservation list for instantiation of @reservations
krsmith7 Sep 5, 2018
0e405f9
Deleted require relative to booking_manager in room.rb since booking_…
krsmith7 Sep 5, 2018
e090bb6
Deleted extra line spaces in booking_manager_spec.rb
krsmith7 Sep 5, 2018
cfba273
Deleted hard coding of 20 rooms in constructor and replaced with para…
krsmith7 Sep 5, 2018
c9a97ae
Added paramter to previous calls of BookingManager.new. Added tests f…
krsmith7 Sep 5, 2018
8891e1a
Added test to booking_manager_spec to check that number of rooms is e…
krsmith7 Sep 5, 2018
281659c
Added tests for reservation list creator method to booking_manager_sp…
krsmith7 Sep 5, 2018
af5e592
Changed assertions in Populate Room List tests to explicitly call Boo…
krsmith7 Sep 5, 2018
fbfaa8e
Uncommented BookingManager#list_reservations
krsmith7 Sep 5, 2018
45df5ed
Added test for list_reservations to booking_manager_spec
krsmith7 Sep 5, 2018
ea97fad
Deleted new line in room_spec
krsmith7 Sep 5, 2018
65d9326
Added test for BookingManager#list_rooms to booking_manager_spec
krsmith7 Sep 5, 2018
50a7a42
Added test to check BookingManager#list_rooms number of rooms in book…
krsmith7 Sep 5, 2018
58fa804
Commented out Room#check_availability
krsmith7 Sep 5, 2018
65c9062
Uncommented BookingManager#add_reservation
krsmith7 Sep 5, 2018
285d676
Added test for BookingManager#add_reservation in booking_manager_spec
krsmith7 Sep 5, 2018
89b9670
Created reservation class file and reservation_spec
krsmith7 Sep 5, 2018
bba8e60
Added require_relative for reservation element to booking_manager and…
krsmith7 Sep 5, 2018
e2cd784
Created class Reservation with attr and initialize method
krsmith7 Sep 5, 2018
84f7cfb
Deleted commented out require_relative
krsmith7 Sep 5, 2018
45316b4
Added test to reservation_spec to check that method creates instance …
krsmith7 Sep 5, 2018
4efbbc5
Edited date instance variables in class Reservation to Date.parse input
krsmith7 Sep 6, 2018
2713f1a
Edited spacing
krsmith7 Sep 6, 2018
9a59539
Added test to check that BookingManager#add_reservation adds Reservat…
krsmith7 Sep 6, 2018
bf1b240
Edited dates in Reservation initialization test
krsmith7 Sep 6, 2018
62a4374
Added room_calendar instance variable to initialization and make_room…
krsmith7 Sep 6, 2018
729f3f8
Added BookingManager#make_room_calendar to store room reserved dates
krsmith7 Sep 6, 2018
bde0226
Completed BookingManager#maked_room_calendar and #add_reservation_to_…
krsmith7 Sep 7, 2018
0025f00
Fixed test for add_reservation_to_calendar so does not call nonexiste…
krsmith7 Sep 7, 2018
82b3c87
Added room calendar to attr_accessor and pseudocode for add_reservati…
krsmith7 Sep 7, 2018
f9862e9
Added assertion to add_reservation_to_calendar test to check that dat…
krsmith7 Sep 7, 2018
565cfea
Added code to BookingManager#add_reservation_to_calendar to add each …
krsmith7 Sep 7, 2018
aa20313
Added @number_nights to constructor of class Reservation
krsmith7 Sep 7, 2018
eb91759
Moved Reservation.number_nights conversion to integer from BookingMan…
krsmith7 Sep 7, 2018
5fb51ec
Added BookingManager#get_reservation_cost
krsmith7 Sep 7, 2018
b34e6f0
Added test for BookingManager#get_reservation_cost
krsmith7 Sep 7, 2018
c962098
Added test for invalid date Argument Error in Reservation
krsmith7 Sep 7, 2018
9369c77
Added Reservation#check_dates method
krsmith7 Sep 7, 2018
aac994e
Fixed invalid date range test for Reservation constructor
krsmith7 Sep 7, 2018
5edf06e
Added test for find_reservations_on_date to booking_manager_spec
krsmith7 Sep 8, 2018
8c6d95f
Added BookingManager#find_reservations_on_date
krsmith7 Sep 8, 2018
4b7d2eb
Added booking to find_reservations_on_date test to check list is excl…
krsmith7 Sep 8, 2018
fe06dda
Add test for BookingManager#find_vancancies_on_date to booking_manage…
krsmith7 Sep 9, 2018
1aa1985
Add BookingManager#find_vacancies_on_date. Delete find_reservations_o…
krsmith7 Sep 9, 2018
c25a906
Add assertion to find_vacancies_on_date test for when no vacancies
krsmith7 Sep 9, 2018
ad7a1ba
Edit invalid date error message in Reservation and dates in tests to …
krsmith7 Sep 9, 2018
e04e5a6
Add require date
krsmith7 Sep 9, 2018
85ce565
Change return for no vacancies to String message
krsmith7 Sep 9, 2018
b08a5b5
Add test for reserve_available room and beginning of reserve_availabl…
krsmith7 Sep 9, 2018
6182750
Edit make_room_calendar to create with instance of Room as key instea…
krsmith7 Sep 9, 2018
263dca1
Delete commented comments. Add alternate code for reserve_available_room
krsmith7 Sep 10, 2018
d261b58
Delete surplus comments
krsmith7 Sep 10, 2018
41af7f5
Deleted extra comments. Changed booking_manager_spec test Room.new in…
krsmith7 Sep 10, 2018
46c1597
Add refactors.txt file of possible future changes
krsmith7 Sep 10, 2018
d8a5ede
Delete unneeded loop in reserve_available method
krsmith7 Sep 10, 2018
b9047e2
Add refactor note on overwriting
krsmith7 Sep 10, 2018
304fb98
Add test for reserve_available_room
krsmith7 Sep 10, 2018
1d0f3cc
Add tests for BookingManager#determine_date_range and BookingManager#…
krsmith7 Sep 12, 2018
a4cfa15
Add BookingManager#determine_date_range and BookingManager#find_vacan…
krsmith7 Sep 12, 2018
abf9c23
Edit refactors list
krsmith7 Sep 12, 2018
7e47e4c
Delete surplus definition end comments
krsmith7 Sep 12, 2018
c57e111
Add assertion to test for BookingManager#find_vacancies_in_date_range
krsmith7 Sep 12, 2018
30cfe70
Refactor reserve_available_room with find_vacancies_in_date_range method
krsmith7 Sep 12, 2018
3f0e697
Add design activity file
krsmith7 Sep 25, 2018
3005254
Add answers to design activity questions
krsmith7 Sep 25, 2018
835f757
Add discussion of Hotel design changes
krsmith7 Sep 25, 2018
0b72d5c
Include comment about combining reservation methods in BookingManager
krsmith7 Sep 25, 2018
3db140a
Move get_reservation_cost from BookingManager to Reservation class
krsmith7 Sep 25, 2018
320840c
Move date_range in find_vacancies to before loop. Comment out no_vaca…
krsmith7 Sep 25, 2018
e2a1ab2
Comment out no_vacancies method and test
krsmith7 Sep 25, 2018
d2cbed8
Combine add_reservation method with add_reservation_to_calendar method
krsmith7 Sep 26, 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
2 changes: 1 addition & 1 deletion Guardfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
guard :minitest, bundler: false, rubygems: false do
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" }
Expand Down
30 changes: 30 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Evaluating Responsibility

* What classes does each implementation include? Are the lists the same? | Each has the same classes, CartEntry, ShoppingCart, and Order, but the methods have been reorganized in the second implementation.
* Write down a sentence to describe each class. |
* CartEntry handles an item added, taking in as parameters its unit price and quantity. It also calculates the price of that CartEntry instance.
* ShoppingCart manages the list of entries and price of all entries combined.
* Order stores the constant SALES_TAX and totals the sum of the ShoppingCart plus Sales tax.
* How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.
* Order totals the cost of the ShoppingCart which has a collection of instances of CartEntry.
* What data does each class store? How (if at all) does this differ between the two implementations?
* CartEntry stores the instance variables unit_price and quantity, which are only used in CartEntry. In the second implementation, it does not have an attribute accessor.
* ShoppingCart stores the instance variable entries which is a collection of CartEntry instances. In the second implementation, it does not have an attribute accessor.
* Order stores the constant SALES_TAX. It stores the instance of ShoppingCart.
* What methods does each class have? How (if at all) does this differ between the two implementations?
* Each has an initialize, but they no longer as attribute reader/writers. The Order total_price method no longer calls instance variables from other classes. It only calls an instance variable of ShoppingCart, which was initialized in Order.
* 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?
* Computing the price is retained in Order.
* Does total_price directly manipulate the instance variables of other classes?
* No, only those directly called in Order.
* If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?
* This would be easier to change in Implementation B because we would only need to change what is passed into CartEntry unit price and/or quantity.
* Which implementation better adheres to the single responsibility principle?
* Implementation B better adheres to single responsibility because each class only operates on and calculates its own instance variables.
* Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled?
* Implementation B is more loosely coupled. It has less instances of directly calling/naming an instance variable or attribute from other classes or variables.

## Revisiting Hotel
Identify one place in your Hotel project where a class takes on multiple roles, or directly modifies the attributes of another class. Describe in design-activity.md what changes you would need to make to improve this design, and how the resulting design would be an improvement.
BookingManager class takes on multiple roles, some of which are acting on (or creating) attributes of class Reservation. I would need to move several of the functions from BookingManager to Reservation. Making this change would allow my code to more so follow Single Responsibility.
187 changes: 187 additions & 0 deletions lib/booking_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
require_relative 'room'
require_relative 'reservation'
# require 'pry'

module Hotel
class BookingManager
attr_accessor :rooms, :reservations, :room_calendar

def initialize(number_rooms)
@rooms = populate_room_list(number_rooms) #(20)
@reservations = make_reservation_list
@room_calendar = make_room_calendar(number_rooms)

Copy link
Copy Markdown

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 each of these initialization helpers out as separate methods - the pattern is very clear.

end

# Create list of rooms as list of room Instances
def populate_room_list(number_rooms)
rooms = []
num = 1

number_rooms.times do |room|
room = Room.new(num)
#
rooms << room
num += 1
end

return rooms
end


# Create array to store all of reservations
def make_reservation_list
reservations = []
return reservations
end


# Create list of rooms with reserved dates.
def make_room_calendar(number)
@room_calendar = {}

@rooms.each do |room|
dates_reserved = {}
@room_calendar[room] = dates_reserved
end

return @room_calendar
end


# Method to add a reservation to list of reservations
# def add_reservation(reservation) #combine with add_reservation_to_calendar?
# @reservations << reservation
# end


# Add reservation date range to hash of room reserved dates
def add_reservation_to_calendar(reservation)
date = reservation.start_date
@reservations << reservation

reservation.number_nights.times do
@room_calendar[reservation.room][date] = reservation # dependency
date += 1
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I like the idea of using a hash of hashes to keep track of the reservations for each date for each room. This definitely works to solve the problem.

However, this ends up being an example of tight coupling. The information about what dates a reservation is for is repeated here in the BookingManager. That means that if a reservation's dates ever change, the program needs to know to come back here to the BookingManager and update this list - otherwise the logic to reserve a room will be broken. From the outside it's not obvious this should be true.

The way to resolve this would be to make the reservations the final source of truth about what dates they're for. When the BookingManager needs to build a list of reservations for a certain date, or figure out which rooms are available, it would have to loop through the list of all reservations to do the work, probably calling some method like Reservation#overlap?(start_date, end_date). This may feel like more effort on your part, but in the long run it would make this design less fragile.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Are you saying instead of having BookingManager have a calendar of Rooms with Reservations, that that information should only be accessible by ..one instance variable?Where else would the reservation date be changed besides in the add_reservation/add_reservation_to calendar method? (I have now combined those two. And maybe only need one.) Each reservation does have info on what date it is for.


# binding.pry
return @room_calendar
end


# Check if date range given is valid - start must be before end
def check_dates(start_date, end_date)
if start_date > end_date
raise ArgumentError.new "Invalid date range. Start date must be before end date, both in format of 'Month dd, yyyy'. "
end
end


# Create array of all dates from start date to end date
def determine_date_range(start_date, end_date)
check_dates(start_date, end_date)

res_start_date = Date.parse(start_date)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You do a great job of using vertical whitespace (newlines) to break up the ideas in your code. This makes the whole program much easier to read.

res_end_date = Date.parse(end_date)

date_range = []
search_date = res_start_date

until search_date > res_end_date
date_range << search_date
search_date += 1
end
return date_range
end

# Method to list all reservation instances
def list_reservations
return @reservations
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This does exactly the same thing as attr_reader :reservations, just with a different name. In my mind, the attr_reader meets the requirements here, and is a little cleaner.



# Method to list all rooms in hotel
def list_rooms
return @rooms
end

# # Method to get total cost of reservation
# def get_reservation_cost(nights, cost_per_night)
# total_cost = nights * cost_per_night
# return total_cost
# end


# Return array reservations with matching date from room calendar hash
def find_reservations_on_date(date, calendar)
search_date = Date.parse(date)
found_reservations = []

calendar.each do |room, info|
info.each do |date, reservation|
if date == search_date
found_reservations << reservation
else
next
end
end
end
# Add return message for no reservations found?
return found_reservations
end

def find_vacancies_on_date(date, calendar)
search_date = Date.parse(date)
found_vacancies = []

calendar.each do |room, info|
if info == nil
found_vacancies << room
else
info.any? {|date, reservation| date == search_date}? next : found_vacancies << room
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would probably not use a ternary here - it feels like too many things on one line.

end

return found_vacancies
#.empty? ? no_vacancies_message: found_vacancies
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In general, it's considered bad practice to return a string in a failure case like this. One of the main reasons is that whoever calls this code will have to do a lot of work to determine whether or not it failed.

Instead you should either raise an exception, or (since this is supposed to return a collection) return an empty array. A simple implementation of the second option would be replacing line 143 with

return found_vacancies



def find_vacancies_in_date_range(start_date, end_date)
check_dates(start_date, end_date)
date_range = determine_date_range(start_date, end_date)
rooms_available_in_date_range = []

@room_calendar.each do |room, reserved_dates|
if reserved_dates.empty?
rooms_available_in_date_range << room
else
if (reserved_dates.keys && date_range).length > 0
next
else
rooms_available_in_date_range << room
end
end
end

return rooms_available_in_date_range
end


# def no_vacancies_message
# return "There are no vacancies for the given date range."
# end


def reserve_available_room(guest_name, start_date, end_date)
check_dates(start_date, end_date)
available_rooms = find_vacancies_in_date_range(start_date, end_date)

new_reservation = Reservation.new(available_rooms.first, guest_name: guest_name, start_date: start_date, end_date: end_date)
# add_reservation(new_reservation)
add_reservation_to_calendar(new_reservation)
return new_reservation #unneeded?
end

end
end
29 changes: 29 additions & 0 deletions lib/reservation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module Hotel
class Reservation
attr_reader :room, :number_nights
attr_accessor :guest_name, :start_date, :end_date, :cost_per_night

def initialize(room, guest_name:, start_date:, end_date:, cost_per_night: 200.00)
@room = room
@guest_name = guest_name
check_dates(start_date, end_date)
@start_date = Date.parse(start_date)
@end_date = Date.parse(end_date)
@cost_per_night = cost_per_night
@number_nights = (@end_date - @start_date).to_i
end

def check_dates(start_date, end_date)
if start_date > end_date
raise ArgumentError.new "Invalid date range. Start date must be before end date, both in format of 'Month dd, yyyy'. "
end
end

# # Method to get total cost of reservation
def get_reservation_cost
total_cost = @number_nights * @cost_per_night
return total_cost
end

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

def initialize(number)
@number = number # should this be a string ?
end

# def check_availability(start_date, end_date)
# # Do..something with dates.
# # if date of check is within range
# # return boolean
# availability = true
# return availability
# end # of check_availability

end # of class Room
end # of module Hotel
8 changes: 8 additions & 0 deletions refactors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Possible changes to Hotel:
- Reduce dependency of method for making Reservation on reserve instance variables?
- Split some of Hotel into smaller methods (like reserve_available_room)
- Combine methods that have overlapping but contradictory options? Combine or link. (ex: @reservations & @room_calendar)
- Change name for BookingManager instance(s) in tests? Sometimes listed as "hotel", other times "manager", etc. Change all to BookingSystem?
- Check more of edge cases
- Too many variables with 'reservation' in title?
- Add check that rooms are not overwritten
Loading