forked from AdaGold/hotel
-
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
sammi-jo's HOTBOOK (nodes) #28
Open
sjlee3157
wants to merge
59
commits into
Ada-C10:master
Choose a base branch
from
sjlee3157:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
9ae2d21
Added class files, spec files, spec helper, and Guardfile for envi setup
sjlee3157 c619148
Added reservation.rb and spec
sjlee3157 b43773d
Updated Wave1 lib file setup
sjlee3157 a22d58f
Updated Wave1 spec files setup
sjlee3157 d285e3b
Added pseudocode.md for code design
sjlee3157 d5a339d
Wave1 hotel_spec tests written
sjlee3157 57b76b1
Wave1 hotel_spec tests written
sjlee3157 91403fd
Merge branch 'master' of https://github.com/sjlee3157/hotel
sjlee3157 e095f19
Hotel class passes specs
sjlee3157 1fe9023
Hotel class passes specs
sjlee3157 792761a
Deleted loader class and spec
sjlee3157 ea85f45
Added DateRange class
sjlee3157 00e2798
Finished DateRange class & spec for Wave 1, tests pass
sjlee3157 2043e50
Added to_range method in DateRange, test passes
sjlee3157 b7ef34b
Good chunk of design refactors for Wave 1
sjlee3157 fcc08ba
Nvm - don't need daterange.to_range
sjlee3157 d4da006
Wave 1 Reservation class done, tests passing
sjlee3157 1a71d74
Updates to DateRange- specs passing, 100% cov
sjlee3157 336113a
Wave1 Book class complete, tests pass
sjlee3157 c4ca4d0
Wave1 Hotel class done, tests pass.
sjlee3157 163211e
Added data/ and room_numbers.csv
sjlee3157 9c00164
Hotel now loads CSV file of room numbers, tests pass
sjlee3157 198151f
Added support folder for test data files
sjlee3157 210d953
Added private save_reservation method to Res class, tests pass
sjlee3157 659cc3f
Added conflict? method in DateRange, tests pass
sjlee3157 a271409
Merge branch 'master' of https://github.com/sjlee3157/hotel
sjlee3157 7106eff
Created list_avail_rooms method in Book, tests pass, support data added
sjlee3157 4790fbe
Added suggested room and error checking functionality to new reservat…
sjlee3157 a2b717b
Tests passing for Wave 2 requirements
sjlee3157 007fc01
Added empty files for new Block class
sjlee3157 f64f67a
Finished basic methods and initialize for Block, tests pass
sjlee3157 1063289
Fixed mistake in to_range method--should be exclusive
sjlee3157 a8e62c0
Hotel.find_rate class method still needs to be tested after CSV load …
sjlee3157 c74aac3
Ready to work on and test block methods in book.rb
sjlee3157 dbb6b69
Added code to load reservations from CSV, not yet tested
sjlee3157 b00e918
Better comments explaining range as overnights
sjlee3157 25a7649
Added CSV loading method, tests pass
sjlee3157 2157e3b
Daterange now has conflict? method of its own
sjlee3157 712d117
Reservation range bug fixed - no longer includes checkout date
sjlee3157 6ec25ee
private validate method checks all arguments
sjlee3157 f2868ab
modified test reservation data
sjlee3157 b5fa688
Added custom errors in errors.rb to HotBook module
sjlee3157 20e9c2f
Book class can now identify reservation and block conflicts
sjlee3157 f7efff1
Added hotbook.rb file
sjlee3157 65c20ba
Book class can make new block and check for conflicts
sjlee3157 f81e93b
Book class can make new block reservations and check for conflicts
sjlee3157 fa642ea
all current specs passing and 100% coverage
sjlee3157 097f6b7
Tested many new edge cases in Book.rb
sjlee3157 d9b0806
Deleted pseudocode.rb
sjlee3157 9249d5f
Notes added to refactors.txt
sjlee3157 6addaa5
Added new reservation nominal case in book sepc
sjlee3157 cf6fff8
Deleted pseudocode.md
sjlee3157 2ac71d8
Notes added to refactors.txt
sjlee3157 2f55f2d
Added new reservation nominal case in book sepc
sjlee3157 3959abe
Tagged a todo - inconsistency: daterange.conflict?(other) vs. block.c…
sjlee3157 ed6bead
Renamed Book class to BookingsManager
sjlee3157 379c1f2
Merge branch 'master' of https://github.com/sjlee3157/hotel
sjlee3157 64be846
Added new items to refactors list
sjlee3157 c884592
Added design-activity.md
sjlee3157 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Design Activity | ||
https://github.com/Ada-Developers-Academy/textbook-curriculum/blob/master/02-intermediate-ruby/exercises/hotel-revisited.md | ||
|
||
## Prompts | ||
|
||
#### What classes does each implementation include? Are the lists the same? | ||
Both implementations have the same three classes: `CartEntry`, `ShoppingCart`, and `Order`. | ||
|
||
#### Write down a sentence to describe each class. | ||
| Class | A | B | | ||
| :------------ | :------------- | :------------- | | ||
| CartEntry | **State:** knows its unit price and its quantity. | **State:** knows its unit price and its quantity. <br><br> **Behavior:** can calculate its own price. | | ||
| ShoppingCart | **State:** stores an array of CartEntries. | **State:** stores an array of CartEntries. <br><br> **Behavior:** can ask each CartEntry for its price, and then calculate the subtotal (price of all entries in the array). | | ||
| Order | **State:** knows the SALES TAX constant, stores an instance of the ShoppingCart class. <br><br> **Behavior:** can calculate a CartEntries price, can calculate a ShoppingCart's price, and can assemble the two into a total price. | **State:** knows the SALES TAX constant, stores an instance of the ShoppingCart class. <br><br> **Behavior:** can ask the ShoppingCart for its price (subtotal), and then calculate the total price. | | ||
|
||
("stores" == "knows") | ||
|
||
#### How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. | ||
|
||
The difference between A and B is that the Order class is concerned with *how* (A) versus *what* (B). | ||
|
||
In A, the Order class knows too much outside of its "jurisdiction" -- it knows *how* to calculate CartEntry's price and *how* to calculate ShoppingCart's price. In A, classes are tightly coupled. | ||
|
||
In B, the Order class asks CartEntry *what* its price is, asks ShoppingCart *what* its price is, and, from there, knows *how* to calculate a total price. In B, classes are loosely coupled. | ||
|
||
#### What **data** does each class store? How (if at all) does this differ between the two implementations? | ||
Classes store the **State** described in the above table. | ||
|
||
#### What **methods** does each class have? How (if at all) does this differ between the two implementations? | ||
Classes have methods for the **Behavior** described in the above table. | ||
|
||
#### 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`? | ||
|
||
A: the latter. B: the former. | ||
|
||
- Does `total_price` directly manipulate the instance variables of other classes? | ||
|
||
A: yes. B: no. | ||
|
||
#### If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? | ||
|
||
In the real world, like on Zazzle.com or CafePress.com, items are likely to each have unique wholesale price brackets based on quantity (though I suppose it could be something like 10% off if you spend $100 or more). Going with the former, we'd do this by adding some data - probably a price_table of quantities and prices, either an array of arrays or an array of hashes. | ||
|
||
To modify A, CartEntry's unit_price has to be changed to price_table, and Order's total_price method has to be changed to include an enumerable method to look up a price by a quantity. | ||
|
||
To modify B, CartEntry's unit_price also has to be changed to price_table, and CartEntry's price method also has to be changed to look up a price by a quantity. | ||
|
||
B is easier to modify because changes in one class do not necessitate changes in another class. The person making the change doesn't have to hunt through the code to figure out effects of the change. The change is more proportional to the cost of change. There's less risk of far-off, hidden, unwanted effects of change. | ||
|
||
#### Which implementation better adheres to the single responsibility principle? | ||
|
||
I think B does, but I also think that classes in both A and B are *seemingly* single-responsibility. In both A and B, CartEntry has price and quantity, ShoppingCart has entries, and Order has a cart and a total price. | ||
|
||
However, I think B is the better answer because A's CartEntry can and should shift the responsibility of knowing about the instance variables of other classes from itself to the classes in question. | ||
|
||
#### Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? | ||
|
||
B! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# gems the project needs | ||
require "csv" | ||
require "date" | ||
|
||
# Optional - for developer use | ||
require "pry" | ||
require "awesome_print" | ||
|
||
# project constants | ||
ROOM_NUMBERS_FILENAME = "data/room_numbers.csv" | ||
TEST_RESERVATION_FILENAME = "support/test_reservation_data.csv" | ||
RESERVATION_DATA_FILENAME = TEST_RESERVATION_FILENAME #"data/reservation_data.csv" | ||
|
||
# namespace module | ||
module HotBook; | ||
end | ||
|
||
# all of the classes that live in the module | ||
require_relative "lib/block.rb" | ||
require_relative "lib/bookingsmanager.rb" | ||
require_relative "lib/daterange.rb" | ||
require_relative "lib/errors.rb" | ||
require_relative "lib/hotel.rb" | ||
require_relative "lib/reservation.rb" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module HotBook | ||
class Block | ||
attr_reader :available, :rooms, :daterange, :room_rate | ||
|
||
def initialize(daterange:, rooms:, room_rate: 185.0) | ||
@daterange = daterange | ||
@rooms = rooms.map! {|room| room.upcase} | ||
@available = rooms.clone | ||
@room_rate = room_rate | ||
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. So Block has a set of rooms and a way to remove rooms from the list, and tell if the block conflicts, I feel like it should be able to reserve a room in the block as well, maybe then returning a reservation which your booking system could use to update reservations. |
||
# Removes a room from @available once it's reserved | ||
def disable(query) | ||
room = available.find { |room| room == query } # guaranteed to return 1 | ||
return available.delete(room) # returns value of what it deleted | ||
end | ||
|
||
# Does the block conflict with another block? | ||
# Is this being used?? | ||
def conflict?(other) | ||
return daterange.conflict?(other) | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
module HotBook | ||
# The BookingsManager class is responsible for: | ||
# holding all reservations and blocks, | ||
# searching through them, and | ||
# making new reservations and blocks. | ||
# It includes support methods for determining availability/conflicts. | ||
|
||
class BookingsManager | ||
attr_reader :reservations, :hotel, :blocks | ||
|
||
def initialize(hotel) # expects a dependency injection (HotBook::Hotel.new) | ||
@hotel = hotel | ||
@reservations = Reservation.from_csv(RESERVATION_DATA_FILENAME) | ||
@blocks = [] # a list of Blocks | ||
end | ||
|
||
def new_reservation(daterange, room_number: suggest_room(daterange)) | ||
validate(:daterange, daterange) | ||
validate(:room_number, room_number) | ||
room_number = room_number.upcase | ||
if room_taken?(daterange, room_number) | ||
raise RoomIsTakenError, "Room is already reserved some time within "\ | ||
"this daterange" | ||
elsif room_blocked?(daterange, room_number) | ||
raise RoomIsBlockedError, "Room is blocked some time within this "\ | ||
"daterange" | ||
end | ||
|
||
room_rate = hotel.find_rate(room_number) | ||
|
||
new_reservation = HotBook::Reservation.new(daterange: daterange, | ||
room_number: room_number, | ||
room_rate: room_rate) | ||
reservations << new_reservation | ||
return new_reservation | ||
end | ||
|
||
def new_block(daterange, rooms, discount_rate: 185.0) | ||
validate(:daterange, daterange) | ||
validate(:rooms, rooms) | ||
if rooms.size > 5 | ||
raise ArgumentError, "A Block cannot have more than 5 rooms" | ||
end | ||
# Cannot overlap or conflict with existing reservation | ||
rooms.each do |room_number| | ||
if room_taken?(daterange, room_number) | ||
raise RoomIsTakenError, "Room already has reservation during "\ | ||
"this daterange" | ||
elsif room_blocked?(daterange, room_number) | ||
raise BlockConflictError, "A block already exists on a room during " \ | ||
"this daterange" | ||
end | ||
end | ||
new_block = HotBook::Block.new(daterange: daterange, | ||
rooms: rooms, | ||
room_rate: discount_rate) # Default | ||
blocks << new_block | ||
return new_block | ||
end | ||
|
||
def new_block_reservation(block, room_number: block.available.first) | ||
validate(:block, block) | ||
# raise error if the block has no available reservations | ||
if block.available == [] || nil | ||
raise NoRoomsAvailableError, "This block is fully booked" | ||
end | ||
validate(:room_number, room_number) | ||
room_number = room_number.upcase | ||
# make sure the room number is part of the block | ||
unless block.rooms.include?(room_number) | ||
raise ArgumentError, "Room is not part of the given block" | ||
end | ||
# make sure the room is available | ||
unless block.available.include?(room_number) | ||
raise RoomIsTakenError, "Room already reserved during this block" | ||
end | ||
# Remove this room from its memory array of what's still available: | ||
block.disable(room_number) | ||
new_reservation = HotBook::Reservation.new(daterange: block.daterange, | ||
room_number: room_number, | ||
room_rate: block.room_rate) | ||
@reservations << new_reservation | ||
return new_reservation | ||
end | ||
|
||
# Returns the first room in the array of available rooms | ||
def suggest_room(daterange) | ||
validate(:daterange, daterange) | ||
available = public_avail_rooms(daterange) | ||
if available == nil || available == [] | ||
raise HotBook::NoRoomsAvailableError, "All rooms are booked " \ | ||
"during this daterange" | ||
end | ||
return available.first | ||
end | ||
|
||
# Returns an array of reservations (EXCLUDING checkout day) | ||
def list_by_nights(date) | ||
validate(:date, date) | ||
return reservations.select {|reservation| | ||
reservation.range.include?(date) } | ||
end | ||
|
||
# Returns array of room numbers that are publicly available during a daterange | ||
def public_avail_rooms(daterange) | ||
validate(:daterange, daterange) | ||
a = conflicting_reservations(daterange).map { |reservation| | ||
reservation.room_number } | ||
b = conflicting_blocks(daterange).flat_map { |block| block.rooms } | ||
available_rooms = hotel.room_numbers - a - b | ||
return available_rooms | ||
end | ||
|
||
# Searches all reservation dateranges for any conflict with given daterange, | ||
# returns true if room number is already part of a reservation | ||
def room_taken?(daterange, room_number) | ||
validate(:daterange, daterange) | ||
validate(:room_number, room_number) | ||
a = conflicting_reservations(daterange).map { |reservation| | ||
reservation.room_number } | ||
return a.include?(room_number) | ||
end | ||
|
||
# Searches all block dateranges for any conflict with given daterange, | ||
# and returns true if room number is already part of a block | ||
def room_blocked?(daterange, room_number) | ||
validate(:daterange, daterange) | ||
validate(:room_number, room_number) | ||
b = conflicting_blocks(daterange).flat_map { |block| block.rooms } | ||
return b.include?(room_number) | ||
end | ||
|
||
# Returns an array of reservations with a daterange conflict | ||
def conflicting_reservations(daterange) | ||
validate(:daterange, daterange) | ||
return reservations.select { |reservation| | ||
reservation.daterange.conflict?(daterange) } | ||
end | ||
|
||
#TODO: There's an inconsistency here-- daterange.conflict?(other) vs. block.conflict?(daterange)--PICK ONE! | ||
# Returns an array of blocks with a daterange conflict | ||
def conflicting_blocks(daterange) | ||
validate(:daterange, daterange) | ||
return blocks.select { |block| block.conflict?(daterange) } | ||
end | ||
|
||
private | ||
|
||
def validate(type, var) | ||
case type | ||
when :date | ||
raise ArgumentError, "Invalid date - use Date.parse (expected Date, " \ | ||
"not #{var.class})" unless var.is_a?(Date) | ||
when :room_number | ||
raise ArgumentError, "Invalid room number (expected String, " \ | ||
"not #{var.class})" unless var.is_a?(String) | ||
when :daterange | ||
raise ArgumentError, "Invalid daterange (expected HotBook::DateRange," \ | ||
" not #{var.class})" unless var.is_a?(HotBook::DateRange) | ||
when :rooms | ||
raise ArgumentError, "Invalid rooms (expected Array of " \ | ||
"Strings)" unless var.is_a?(Array) && var.first.is_a?(String) | ||
when :block | ||
raise ArgumentError, "Invalid block (expected HotBook::Block, " \ | ||
"not #{var.class})" unless var.is_a?(HotBook::Block) | ||
end | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
module HotBook | ||
# The DateRange class compares and does calculations on other DateRanges | ||
# Refer to Date gem docu to understand what date format to use (i.e. y-m-d) | ||
class DateRange | ||
attr_reader :start_date, :end_date | ||
|
||
def initialize(start_date:, end_date:) | ||
@start_date = Date.parse(start_date.to_s) | ||
@end_date = Date.parse(end_date.to_s) | ||
raise ArgumentError, "Invalid range #{self}: End must be > Start)" unless | ||
@end_date > @start_date | ||
end | ||
|
||
def duration | ||
return (end_date - start_date).to_i | ||
end | ||
|
||
# Does daterange conflict with another daterange? | ||
def conflict?(other) | ||
if start_date >= other.end_date || end_date <= other.start_date | ||
return false | ||
else | ||
return true | ||
end | ||
end | ||
|
||
# Range only includes overnights and EXCLUDES checkout day. | ||
# should change this to "contains(date)" | ||
def to_range | ||
return (@start_date...@end_date) | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module HotBook | ||
class RoomIsTakenError < StandardError | ||
end | ||
|
||
class RoomIsBlockedError < StandardError | ||
end | ||
|
||
class BlockConflictError < StandardError | ||
end | ||
|
||
class NoRoomsAvailableError < StandardError | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This file's a neat idea!