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

Branches, C. Gutierrez #46

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0612381
Initial comit with largely empty files
CEsGutierrez Sep 3, 2019
98f9960
created empty classes to test Rake and Guard functionality
CEsGutierrez Sep 3, 2019
737b3e6
pseudocode for tests of basic functionality for classes
CEsGutierrez Sep 3, 2019
92ec155
basic room functionality: instance of room is Room, it knows its ID a…
CEsGutierrez Sep 3, 2019
03ee434
basic functionality of reservation
CEsGutierrez Sep 4, 2019
c70bda1
booker populates totel with instances of Room
CEsGutierrez Sep 4, 2019
29738cf
Fixed typo to get SimpleCov to run better in test_helper file
CEsGutierrez Sep 4, 2019
797298a
initial functionality of Booker class
CEsGutierrez Sep 4, 2019
5ac5a5f
Reporter class is being abandoned
CEsGutierrez Sep 4, 2019
f9989a0
Booker class has a master list of all rooms and can access their ID n…
CEsGutierrez Sep 4, 2019
77c7eda
Some functionality of booker is available, however, date mediator is …
CEsGutierrez Sep 4, 2019
970630a
Prelimenary test in DateMediator Class. Note, works best with instanc…
CEsGutierrez Sep 5, 2019
f74b267
Changed instances of Time to Date
CEsGutierrez Sep 5, 2019
a11aa9f
DateMediator functions to tell gross overlap, additional tests moved …
CEsGutierrez Sep 5, 2019
523e5cc
DateMediator functions for dates that touch but do not overlap. Still…
CEsGutierrez Sep 5, 2019
defcf9d
Changed room-assign functiont to random as Wave 1 does not require bo…
CEsGutierrez Sep 6, 2019
ea17e3f
Refactor to standardize references and responsibilities for different…
CEsGutierrez Sep 6, 2019
7337ebf
Added getting a list of reservations that fall in a given date range,…
CEsGutierrez Sep 7, 2019
8de841d
New functionality, can view list of available rooms for a given range
CEsGutierrez Sep 7, 2019
3150fad
At current, tests reflect that booker randomly chooses a room when ma…
CEsGutierrez Sep 7, 2019
c39a9b3
Added functionality where Booker selects a room based on availability…
CEsGutierrez Sep 8, 2019
c806172
Verified that Booker will not exceed 20 reservations
CEsGutierrez Sep 8, 2019
dd07788
Shifted responsibility of hotel capacity from Reservation to Booker
CEsGutierrez Sep 8, 2019
8e48cc3
Added check for validity of date range to Booker
CEsGutierrez Sep 8, 2019
77c4af3
broke up new_reservation method in Booker to increase ease of change
CEsGutierrez Sep 8, 2019
551bc71
modified Booker to utilize date_digester method
CEsGutierrez Sep 8, 2019
93a20ba
Fixed typo to utilize hotel_capacity constant in Booker
CEsGutierrez Sep 8, 2019
07881e8
Removed Block files and updated test_helper
CEsGutierrez Sep 8, 2019
f8656a8
created tests for instances of Block
CEsGutierrez Sep 8, 2019
5ee3b10
created tests for instances of Block and supporting files
CEsGutierrez Sep 8, 2019
89f62ff
basic functionality for instances of block
CEsGutierrez Sep 8, 2019
0e89412
rethinking logic for block class
CEsGutierrez Sep 8, 2019
fd65f08
new tests for new block logic
CEsGutierrez Sep 8, 2019
78afcde
additional tests for block
CEsGutierrez Sep 9, 2019
578a3d0
partial block tests written in block and booker, missing code, missin…
CEsGutierrez Sep 9, 2019
1a38f66
basic functionality of instances of block verified
CEsGutierrez Sep 9, 2019
fab64da
code written for creating instances of block, missing are tests and c…
CEsGutierrez Sep 9, 2019
03765c7
some functionality of converting blocks into reservations
CEsGutierrez Sep 9, 2019
af21e35
Added design-activity file
CEsGutierrez Sep 24, 2019
369bb2f
refactoring ideas
CEsGutierrez Sep 24, 2019
74ccea7
better defined refactoring plan
CEsGutierrez Sep 25, 2019
e49068f
Refactor plan
CEsGutierrez Sep 25, 2019
7cf97f5
Very broken commit
CEsGutierrez Sep 30, 2019
226f7cd
merged refactoring plans
CEsGutierrez Mar 14, 2020
bca3862
Revert "better defined refactoring plan"
CEsGutierrez Mar 14, 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
Binary file added .DS_Store
Binary file not shown.
134 changes: 134 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
What classes does each implementation include?
* Both implementation A and B have the following classes:
* CartEntry
* Shopping Cart
* Order

Write down a sentence to describe each class:
* Implementation A:
* CartEntry: Instances of CartEntry are used to wed unit prices and quantity for an entry.
* Shopping Cart: Instances of ShoppingCart are used to store entries.
* Order: Instances of Order are used to create new instances of ShoppingCart, and can also calculate the total price for the contents of the cart.
* Implementation B:
* CartEntry: Instances of CartEntry are used to wed unit prices and quantity for an entry, it also knows the price for a given grouping of items like how much "carrots" would cost using their quantity and unit price.
* Shopping Cart: Instances of ShoppingCart are used to store entires and can determine the subtotal price (without tax) for the current cart contents
* Order: Instances of Order are used to create new instances of ShoppingCart and can calculate the total price by accessing the subtotal price from the aforementioned ShoppingCart.

How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.
* Cart entry has a many-to-one relationship with ShoppingCart. Order and ShoppingCart have a one-to-one relationship.

What data does each class store?
* Instances of CartEntry store their individual unit prices and quantities
* Instances of ShoppingCart store entries, which I believe are instances of CartEntry
* Order does not store anything

How (if at all) does this differ between the two implementations?
* These are the same between Implementation A and B.

What methods does each class have? How (if at all) does this differ between the two implementations?
* Implementation A:
* CartEntry has an initialize method which creates instances of itself. Other classes may access the unit price and quantity of each instance.
* ShoppingCart has an initialize method in which it creates an array to hold entries.This array may be accessed by other classes.
* Order has an initialize method in which it creates new instances of ShoppingCart. It also has a total_price method in which it calculates the total cost of the cart by accessing the ShoppingCart's entry array and within that each entry's unit price and quantity.
* Implementation B:
* CartEntry has an initialize method which creates instances of itself. However, as it does not have an attr_reader, it can only return the price of the items in the entry as a whole.
* ShoppingCart has an initialize method in which it creates an array to hold entries. However, as it does not have an attr_reader, it relies on a price method to return the subtotal price for all the items in the cart. It does this by accessing the entry's price method from CartEntry class.
* Order has an initialize method in which it creates new instances of ShoppingCart. In addition, it has a total_price method in which it calculates the total cost of the cart using the ShoppingCart's price method and the sales tax which Order stores as a constant.

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?
* Implementation A: The price computation is retained in Order.
* Implementation B: The price computation is delegated to lower level classes.

Does total_price directly manipulate the instance variables of other classes?
* Implementation A: Yes. It accesses individual CartEntries stored in ShoppingCart individually to calculate the total price.
* Implementation B: No. It only calls the price method in ShoppingCart class.

If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?
* It depends on what "in bulk" means:
* It could be a single item that's literally a bigger size but might still be a single entry, in which case it would change nothing.
* If, instead, it meant buying lots of small items that are identical such as a dozen cans of tomatoes rather an a single can and these items would be considered a single entry, it would change only the quantity in CartEntry. This would have virtually no consequences in either Implementation.
* If, however, it meant that each of the cans of tomatoes would be considered its own entry, that would have the following consequences:
* Implementation A: Due to Order digging through ShoppingCart for individual CartEntries, I imagine this might slow down total_price calculation.
* Implementation B: Due to the price calculation being embedded in ShoppingCart, I don't think this would change anything.

* I don't think the code needs to be changed at this point, because how instances of CartEntry isn't described, and therefore, this might already be accounted for in "quantity". Nevertheless, Implementation B is easier to modify.


Which implementation better adheres to the single responsibility principle?
* Implementation B.
Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled?
* Implementation B has less dependencies and the movement of information within its classes is more controlled.
//

Revisiting Hotel

* Adjusted code so that block_discount is no longer a constant but needs to be entered in at the time that a block reservation is being made. Default is 15% but is changeable.
Refactoring Ideas:
* Room Class: remove cost from attr_reader and rework the code to only access the class method of self.cost instead to remove redundancy.
* Reservation Class: examine what information needs to accessible from other classes and revisit attr_reader.
* Reservation Class: consider moving some of the functionality out of the initialize method.
* DateMediator Class: Guess what? look at attr_reader and what actually needs to be accessed from outside of it! I seriously think none of its attributes need to be readable from outside.
* Consider moving reservations and reserved blocks into Reservation Class out of Booker Class. The accompanying methods might have too much reach into instances of Reservation.
* Consider moving room_ids into Room Class out of Booker Class. Like when the instance of Room is created, move it over.
* Detangle

Refactoring Ideas:
* Room Class: remove cost from attr_reader and rework the code to only access the class method of self.cost instead to remove redundancy.
* Reservation Class: examine what information needs to accessible from other classes and revisit attr_reader.
* Reservation Class: consider moving some of the functionality out of the initialize method.
* DateMediator Class: Guess what? look at attr_reader and what actually needs to be accessed from outside of it! I seriously think none of its attributes need to be readable from outside.
* Consider moving reservations and reserved blocks into Reservation Class out of Booker Class. The accompanying methods might have too much reach into instances of Reservation.
* Consider moving room_ids into Room Class out of Booker Class. Like when the instance of Room is created, move it over.
* Detangle

Booker:
* What is this class's responsibility?
* (Booker does too much) It populates the hotel by creating instances of rooms, it instigates reservations, it instigates soft reservations aka blocks, it lists the rooms available for a given date range, it selects the room for a reservation, it transforms soft reservations (blocks) into actual reservations, it validates incoming data.
* Is this class responsible for exactly one thing?
* Lol, no, it's responsible for loads of things.
* Does this class take on any responsibility that should be delegated to "lower level" classes?
* Yes. Tons!
* Is there code in other classes that directly manipulates this class's instance variables?
* No, no one reaches into Booker, but it's too closely dependent on Reservations and Blocks specifically so I worry about detangling them.

* When do classes take on multiple roles?
I think Booker takes on too many roles. I think it might be good to split its reporting methods from it's activity methods.

* Changes I think I need to make to improve the design:
* I want to make another class called BookingReporter that simply stores and reports back the information from reservations and bookings.
In detail:
* Move room_array, reservations, blocks and their respective reporting methods into that class
* Adjust the tests to reflect the creation of this class
* My concern is that this class and Booker might be too dependent on each other because Booker needs to access the stored reservations and blocks for several things.

Reservation:
* What is this class's responsibility?
* Reservation holds instance-specific information for each reservation which includes the calculation of the cost of the reservation.
* Is this class responsible for exactly one thing?
* No. It adapts for a possible block discount when calculating the cost.
* Does this class take on any responsibility that should be delegated to "lower level" classes?
* There are no lower level classes, but I feel that its functions could be broken down into smaller, single-responsibility methods.
* Is there code in other classes that directly manipulates this class's instance variables?
* I fear that this might be the case, because all of the instance variables are accessible.

Block:
* What is this class's responsibility?
* Block creates instances of itself.
* Is this class responsible for exactly one thing?
* Yes
* Does this class take on any responsibility that should be delegated to "lower level" classes?
* No.
* Is there code in other classes that directly manipulates this class's instance variables?
* Looking at its accessible variables, I think so.

DateMediator:
* What is this class's responsibility?
* DateMediator checks to see the level of conflict between two date ranges.
* Is this class responsible for exactly one thing?
* Yes. Although it can check for both hard conflicts and overlapping dates that are permissible between the check-out time and the check-in time.
* Does this class take on any responsibility that should be delegated to "lower level" classes?
* No, it might be the only true single-responsibility class in the entire program.
* Is there code in other classes that directly manipulates this class's instance variables?
* No, classes just call the methods within it, but almost all of its variables are accessible.

Binary file added lib/.DS_Store
Binary file not shown.
27 changes: 27 additions & 0 deletions lib/block.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# require_relative 'room'

class Block

attr_accessor :number_of_rooms

attr_reader :start_date, :end_date, :discount, :block_label

def initialize(start_date:, end_date:, discount: , block_label: , number_of_rooms:)

@start_date = start_date
@end_date = end_date

if start_date.class != Date || end_date.class != Date
raise ArgumentError.new "dates must be instances of Date"
end

@discount = discount

@block_label = block_label

@number_of_rooms = number_of_rooms

end


end
148 changes: 148 additions & 0 deletions lib/booker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
require 'date'
require_relative 'room'
require_relative 'reservation'
require_relative 'date_mediator'
require_relative 'block'
require_relative 'reporter'


class Booker

attr_reader :HOTEL_CAPACITY

attr_accessor :rooms, :reservations, :reserved_blocks

HOTEL_CAPACITY = 20

def initialize
@rooms = populate_hotel
@reservations = []
@reserved_blocks = []
end

def populate_hotel
rooms_array = []
i = 1
while i <= HOTEL_CAPACITY do
rooms_array << Room.new(id: i)
i = i + 1
end
return rooms_array
end


def new_reservation(start_date: Date.today, end_date: Date.today + 1)

digested_start_date = date_digester(start_date)
digested_end_date = date_digester(end_date)

date_validator(digested_start_date, digested_end_date)

reservation_id = reservations.length + 1

selected_room = room_picker(range_start: digested_start_date, range_end: digested_end_date)

unless (1..HOTEL_CAPACITY).include? selected_room
raise ArgumentError.new "no room assigned for this reservation"
end

reservations << Reservation.new(start_date: digested_start_date, end_date: digested_end_date, room_id: selected_room, reservation_id: reservation_id)

end

def new_block(start_date: Date.today, end_date: Date.today + 1 , number_of_rooms: 2, block_label: 1, block_discount: 15)

digested_start_date = date_digester(start_date)
digested_end_date = date_digester(end_date)

date_validator(digested_start_date, digested_end_date)

if number_of_rooms < 2 || number_of_rooms > 5
raise ArgumentError.new "invalid number of rooms requested for block"
end

if lists_available_rooms_for_range(range_start: digested_start_date, range_end: digested_end_date).length < number_of_rooms
raise ArgumentError.new "block exceeds hotel capacity"
end

block_label = block_labeler

number_of_rooms.times do

selected_room = room_picker(range_start: digested_start_date, range_end: digested_end_date)

unless (1..HOTEL_CAPACITY).include? selected_room
raise ArgumentError.new "no room assigned for this reservation"
end

reservations << Reservation.new(start_date: digested_start_date, end_date: digested_end_date, room_id: selected_room, reservation_id: nil, block_label: block_label, block_discount: block_discount)

end
reserved_blocks << Block.new(start_date: start_date, end_date: end_date, discount: block_discount, block_label: block_label, number_of_rooms: number_of_rooms)

end

def new_reservation_from_block(block_label)

reservations.each_with_index do |reservation, index|
if reservation.block_label == block_label
reservation.reservation_id = reservations[index]
break
end
end

reserved_blocks.each do |block|
if block_label == block.block_label
block.number_of_rooms -= 1
end
end

end

def block_labeler
block_labels = []
@reserved_blocks.each do |block|
block_labels << block.block_label
end
if !(block_labels.empty?)
block_labels.uniq!
return_statement = block_labels.max + 1
return return_statement
else
return 1
end
end

def date_digester(date)
if date.class != Date
digested_date = Date.parse("#{date}")
else
digested_date = date
end
return digested_date
end

def date_validator(first_date, second_date)
# works with instances of date
if first_date > second_date
raise ArgumentError.new "Invalid date range"
end
end

def room_picker(range_start: , range_end: )
range_start = range_start
range_end = range_end
available_rooms = Reporter.lists_available_rooms_for_range(range_start: range_start, range_end: range_end)
if available_rooms.length > 0
return available_rooms[0]
else
return nil
end

end

def self.hotel_capacity
return HOTEL_CAPACITY
end

end
46 changes: 46 additions & 0 deletions lib/date_mediator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require "date"

class DateMediator

attr_reader :range_start, :range_end, :start_date, :end_date

def initialize(range_start:, range_end:, start_date:, end_date:)

# Checks that the dates being checked are in the appropriate format
# The range_start and range_end come from previous reservations and had better not be anything other than instances of time
@range_start = range_start
@range_end = range_end
@start_date = start_date
@end_date = end_date

if (start_date.class != Date || end_date.class != Date)
raise ArgumentError.new "start and end dates are not instances of date"
end
end

def main_function
conflict_statement = 0
check_start = start_date
# this measures the conflicts created by the new dates against the already booked range
while check_start <= end_date do
if check_start.between?(range_start, range_end)
conflict_statement += 1
check_start += 1
else
check_start += 1
end
end
return conflict_statement
end

def conflicting_dates
# this erases the conflict created by dates that are touching the range but not actually treading upon it
conflict = main_function
if conflict == 1 && (@end_date == range_start || @start_date == range_end)
revised_conflict_statement = 0
else return conflict
end
return revised_conflict_statement
end

end
2 changes: 2 additions & 0 deletions lib/library_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# helper to hold connections in between library files
# Abandoned, only Booker requires a ridiculus umber of connections, everything else is pretty self-sustaining
Loading