-
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
Naheed - Hotel - Edges #43
base: master
Are you sure you want to change the base?
Changes from all commits
6716534
96bc8b6
56c493e
6f7affe
e52bc34
3850726
f7ed4a7
eaacfbb
5d5c4a3
4ac7d30
d5e7a4b
1139f5c
8555dd5
93d59e4
0d19193
dfdb23d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
**What classes does each implementation include? Are the lists the same?** | ||
Both Implementations include the classes CartEntry, ShoppingCart, and Order. | ||
**Write down a sentence to describe each class.** | ||
- In both A and B, CartEntry creates an instance of an item or entry, which has the attributes unit_price and quantity. | ||
- In A, ShoppingCart contains an array of all entries. In B, it contains an array of all entries and calculates the total price of the cart. | ||
- In both, Order calculates the total price of the ShoppingCart including tax. | ||
**How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.** | ||
- Each class refers to data stored in other classes- total relies on items in shopping cart, tax, etc. | ||
**What data does each class store? How (if at all) does this differ between the two implementations?** | ||
- CartEntry stores unit_price and quantity. ShoppingCart stores entries, and in B, the price of the cart as well. Order stores the cart, the value of the sales tax, and the total price of the order. | ||
**What methods does each class have? How (if at all) does this differ between the two implementations?** | ||
- CartEntry initializes the item in both, but in B it also calculates the price of the item. ShoppingCart initializes the entries array, and in B it additionally calculates the price of the entire cart. Order calculates the total price. | ||
**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 A, computing the price is the responsibility of Order- although CartEntry stores the price and quantity as well as calculates the price * quantity, order repeats this method. In B, ShoppingCart is responsible for calculating the price of the cart, which makes more sense because it is an attribute of the cart itself, while the order should be responsible for multiplying that price by the SALES_TAX and returning that. | ||
**Does total_price directly manipulate the instance variables of other classes?** | ||
**If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?** | ||
- A would be easier to modify because the price is only calculated once, in the Order class. We could add code to the effect of "if quantity is larger than x, price is $y". | ||
**Which implementation better adheres to the single responsibility principle?** | ||
- A, because each class serves one purpose, but they still rely on and interact with other classes. Also, there is no repeated code. | ||
**Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled?** | ||
|
||
**Refactors** | ||
|
||
I had my total cost method in my BookingSystem class, while it should have been in the Reservation class. This can be copied into the Reservation class, but the references to this method will have to be changed to @reservation instead of @system in the tests as well as wherever this is referenced in other methods. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module Hotel | ||
class Block | ||
attr_accessor :block_id, :number_of_rooms, :start_date, :end_date, :price_per_night | ||
|
||
def initialize(block_id:, number_of_rooms:, start_date:, end_date:, price_per_night:) | ||
@block_id = block_id | ||
@number_of_rooms = number_of_rooms | ||
@start_date = start_date | ||
@end_date = end_date | ||
@price_per_night = price_per_night | ||
end | ||
|
||
# def assign_available_room(start_date, end_date) | ||
# if @number_of_rooms > 5 | ||
# raise ArgumentError, "Cannot block more than 5 rooms" | ||
# end | ||
# return available_rooms[0..(@number_of_rooms.to_i - 1)] | ||
# end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module Hotel | ||
class BlockReservation | ||
attr_accessor :block_id, :reservation_id, :room, :start_date, :end_date, :price_per_night | ||
|
||
def initialize(block_id:, reservation_id:, room:, start_date:, end_date:, price_per_night:) | ||
@block_id = block_id | ||
@reservation_id = reservation_id | ||
@room = room | ||
@start_date = start_date | ||
@end_date = end_date | ||
@price_per_night = price_per_night | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
module Hotel | ||
class BookingSystem | ||
attr_accessor :rooms, :reservations, :all_ids, :blocks | ||
|
||
def initialize | ||
@rooms = load_rooms | ||
@reservations = [] | ||
@all_ids = [] | ||
@blocks = [] | ||
end | ||
|
||
def load_rooms | ||
rooms = [] | ||
i = 1 | ||
|
||
20.times do | ||
rooms << i | ||
i += 1 | ||
end | ||
return rooms | ||
end | ||
|
||
def make_reservation(start_date, end_date) | ||
reservation = Hotel::Reservation.new(reservation_id: generate_id, room: assign_available_room(start_date, end_date), start_date: start_date, end_date: end_date, price_per_night: 200) | ||
@reservations << reservation | ||
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. I love the way you've broken each of these bits of functionality into separate methods! However, it's a little difficult to read all on one line like this. You might split it across multiple lines: reservation = Hotel::Reservation.new(
reservation_id: generate_id(),
room: assign_available_room(start_date, end_date),
start_date: start_date,
end_date: end_date,
price_per_night: 200
) I also like adding parentheses to the call to |
||
# binding.pry | ||
end | ||
|
||
def make_block(start_date, end_date, number_of_rooms) | ||
if number_of_rooms > 5 | ||
raise ArgumentError, "Cannot reserve more than 5 rooms" | ||
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. What if the number of rooms is less than 1? |
||
|
||
id = generate_id | ||
|
||
block = Hotel::Block.new(block_id: id, number_of_rooms: number_of_rooms, start_date: start_date, end_date: end_date, price_per_night: 150) | ||
@blocks << block | ||
|
||
number_of_rooms.times do | ||
block_reservation = Hotel::BlockReservation.new(block_id: id, reservation_id: nil, room: assign_available_room(start_date, end_date), start_date: start_date, end_date: end_date, price_per_night: 150) | ||
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. Same comment about line lengths on lines 36 and 40. |
||
@reservations << block_reservation | ||
end | ||
end | ||
|
||
def make_block_reservation(block_id) | ||
block_reservation = find_empty_block_reservation(block_id) | ||
block_reservation.reservation_id = generate_id | ||
return block_reservation | ||
end | ||
|
||
def assign_available_room(start_date, end_date) | ||
booked_rooms = [] | ||
available_rooms = [] | ||
@reservations.each do |reservation| | ||
if | ||
reservation.start_date < end_date && start_date < reservation.end_date | ||
booked_rooms << reservation.room | ||
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. Here your That simplifies this code, and makes it easier to test the logic in isolation. You also repeat this code on line 72, which means that if the requirements change or you discover a bug, you'll have to change it in two places. 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. This also an example of tight coupling, since 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. Why not call |
||
end | ||
end | ||
all_rooms = load_rooms | ||
available_rooms = all_rooms - booked_rooms | ||
if available_rooms[0] == nil | ||
raise ArgumentError, "No rooms available" | ||
end | ||
return available_rooms[0] | ||
end | ||
|
||
def search_reservations(start_date, end_date) | ||
reservations_within_date = [] | ||
@reservations.each do |reservation| | ||
if | ||
reservation.start_date < end_date && start_date < reservation.end_date | ||
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. This and the above method could be streamlined a little using the return @reservations.select do |reservation|
reservation.start_date < end_date && start_date < reservation.end_date
end |
||
reservations_within_date << reservation | ||
end | ||
end | ||
# binding.pry | ||
return reservations_within_date | ||
end | ||
|
||
def find_reservation(reservation_id) | ||
reservation = @reservations.find { |reservation| reservation.reservation_id == reservation_id } | ||
return reservation | ||
end | ||
|
||
def find_empty_block_reservation(block_id) | ||
block_reservation = @reservations.find { |block_reservation| block_reservation.block_id == block_id && block_reservation.reservation_id == nil} | ||
if block_reservation == nil | ||
raise ArgumentError, "all rooms in block have been reserved" | ||
end | ||
return block_reservation | ||
end | ||
|
||
# def find_block(block_id) | ||
# block = @reservations.find_all { |block_reservation| block_reservation.block_id == block_id } | ||
# return block | ||
# end | ||
|
||
def generate_id | ||
reservation_id = rand(1..100000) | ||
check_id(reservation_id) | ||
@all_ids << reservation_id | ||
return reservation_id | ||
end | ||
|
||
def check_id(reservation_id) | ||
if @all_ids.include?(reservation_id) | ||
raise ArgumentError, "reservation_id already exists" | ||
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. I don't know if raising an argument error is the right choice here, since the user didn't supply this ID, the program generated it. Instead you might regenerate the ID, or assign IDs sequentially so that there's no possibility of an collision (this is what Rails does). |
||
end | ||
|
||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module Hotel | ||
class Reservation | ||
attr_accessor :reservation_id, :room, :start_date, :end_date, :price_per_night, :total_cost | ||
|
||
def initialize(reservation_id:, room:, start_date:, end_date:, price_per_night:) | ||
@system = Hotel::BookingSystem.new | ||
@reservation_id = reservation_id | ||
@room = room | ||
@start_date = start_date | ||
@end_date = end_date | ||
@price_per_night = price_per_night | ||
end | ||
def total_cost(reservation_id) | ||
reservation = @system.find_reservation(reservation_id) | ||
nights = reservation.end_date - reservation.start_date | ||
total_cost = nights * reservation.price_per_night | ||
return total_cost.to_f.round(2) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Refactors: | ||
- consolidate block and block_reservation classes | ||
- rename block method names to be more descriptive | ||
- add more tests for edge cases |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require_relative 'spec_helper' | ||
require 'date' | ||
require 'pry' | ||
|
||
describe "Block class" do | ||
|
||
describe "Block instantiation" do | ||
before do | ||
@block = Hotel::Block.new(block_id: nil, number_of_rooms: nil, start_date: nil, end_date: nil, price_per_night: nil) | ||
end | ||
|
||
it "is an instance of block" do | ||
expect(@block).must_be_kind_of Hotel::Block | ||
end | ||
end | ||
end |
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.
The only difference between this and a regular reservation is that a block reservation has a block ID. One option that might simplify your design is to consolidate both in the
Reservation
class, and add in an optional block ID, which would benil
if this reservation isn't part of a block.