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 all 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
77 changes: 77 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
What classes does each implementation include? Are the lists the same?

The three classes are CartEntry, ShoppingCart, and Order. Both implementations hold the same classes.




Write down a sentence to describe each class.

CartEntry holds information about an item that has been added to a ShoppingCart. ShoppingCarts holds an array of CartEntries. Order contains a ShoppingCart and calculates the final price of the order.




How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.

All three classes have a compositional relationship to each other. ShoppingCarts holds an array of CartEntries. Order holds a ShoppingCart.




What data does each class store? How (if at all) does this differ between the two implementations?

The data stored in each class of both implementations are the same. CartEntry stores information on net price and quantity. ShoppingCart stores information on entries. And Order stores a ShoppingCart and the sales tax.




What methods does each class have? How (if at all) does this differ between the two implementations?

In Implementation A, CartEntry and ShoppingCart do not contain any methods. Order contains the method total price.

In Implementation B, CartEntry contains the price method, ShoppingCart contains a different price method, and Order contains the total price method.




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?

In Implementation A the logic to compute price is all within the Order class. In Implementation B, the logic is delegated between all classes.




Does total_price directly manipulate the instance variables of other classes?
total_price reads the instance variables of other classes but it doesn't write to them.




If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?
I would need more information on the logic behind bulk pricing. If it's the same discount for all products once they hit a certain quantity limit then it could go into wherever the price calculations methods are as a percent multiplier if quantity > bulk_quantity_min. Or if the discount differs from product to product, it could be stored as one or more instance variables under CartEntry (e.g. bulk_price, bulk_quantity_min, or discount).

Implementation B would be easier to modify.





Which implementation better adheres to the single responsibility principle?

Implementation B.






Revisiting Hotel
In my previous code, System calls on some of HotelBlock's instance variables to make a new reservation. Instead, I will now change the code so that HotelBlock makes the reservation and return the reservation to be stored in HotelBlock.





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
26 changes: 26 additions & 0 deletions lib/hotelblock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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

def make_res_from_hb(reservation_id)
new_reservation = Hotel::Reservation.new(id: reservation_id, room: @room, start_date: @start_date, end_date: @end_date, cost: @cost)
return new_reservation
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
151 changes: 151 additions & 0 deletions lib/system.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
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 = []

# Hotel::Date objects hold information on reservations and hotel blocks
# made on a specific date
@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.
reservation_id = @reservations.length + 1

new_reservation = hotel_block.make_res_from_hb(reservation_id)

@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
Loading