Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a79e8c4
create spec and lib files for hotel booker, calendar, reservation, an…
goeunpark Sep 5, 2018
73609dc
HotelBooker initates 20 rooms, can list all rooms, can reserve a room…
goeunpark Sep 5, 2018
44059e3
renamed booked_dates to date_range, started spec and method calls
goeunpark Sep 7, 2018
d0ba0bf
overlap? method in date_range returns boolean if two date ranges overlap
goeunpark Sep 8, 2018
d4e5b6d
overlaps? method in date_range returns boolean
goeunpark Sep 8, 2018
62b0679
test for raising standard error for invalid date ranges is red
goeunpark Sep 8, 2018
b054817
raises standard error for invalid date ranges: green
goeunpark Sep 8, 2018
aaedaaf
raises ArgumentError if dates argument are not Dates
goeunpark Sep 8, 2018
fe5d223
change arguments in reservation to date_range, not check_in, check_ou…
goeunpark Sep 8, 2018
bfbb588
find_reservation in hotel_booker passes tests
goeunpark Sep 8, 2018
fe6c4f2
split up tests for overlaps? in date_range
goeunpark Sep 9, 2018
caaf57b
unreserved_rooms method in hotel_booker failes test
goeunpark Sep 9, 2018
c0cee89
initial pass for unreserved_rooms method in hotel_booker
goeunpark Sep 9, 2018
b0fc136
clean up Room and room_spec files
goeunpark Sep 10, 2018
a431409
cleaned up Reservation and Reservation_spec files
goeunpark Sep 10, 2018
4859efe
updated unreserved_rooms method testing in hotel_booker_spec
goeunpark Sep 10, 2018
e8de7fe
start tests for create_block method in hotel_booker
goeunpark Sep 10, 2018
4f89d83
hotel_booker can create a block of rooms
goeunpark Sep 10, 2018
abeee86
reservation dates match the date range of the block test passes
goeunpark Sep 10, 2018
5a90808
start make_block_reservation method
goeunpark Sep 10, 2018
c4d7e68
can book a block reservation
goeunpark Sep 10, 2018
a963903
create unreserved_block_rooms method and passing test
goeunpark Sep 10, 2018
e781d16
add a refactors file
goeunpark Sep 10, 2018
eca8b38
remove unnecessary namespacing
goeunpark Sep 30, 2018
1be5ba3
refactor checking available rooms into its own method
goeunpark Sep 30, 2018
8278260
start design-activity responses
goeunpark Sep 30, 2018
108afe7
add bolded questions
goeunpark Sep 30, 2018
df6bd13
finish design-activity questions
goeunpark Sep 30, 2018
821a8db
refactor unreserved_block_rooms method
goeunpark Sep 30, 2018
96f44ed
refactor / design in changing cost of room when creating blocks
goeunpark Oct 1, 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
49 changes: 49 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
### What classes does each implementation include? Are the lists the same?

Each implementation includes the classes CartEntry, ShoppingCart, and Order. In Implementation A, CartEntry and ShoppingCart both have initialize as only methods and class Order has initialize and total_price. In Implementation B, CartEntry and ShoppingCart has price method in addition.

### Write down a sentence to describe each class.

CartEntry initializes with the unity price and quantity of the entry. ShoppingCart initializes with an empty array of entries. Order initializes by creating a new instance of ShoppingCart and has a method to calculate the total price. In Implementation B, each class can calculate their own price on their own (whether it's price for entry, price for shopping cart, or total price in order 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.

CartEntry is added to an array in ShoppingCart. Order can calculate the total price from the shopping cart's entries. In implementation B, the price is calculated in every class so that Order doesn't need to do the calculating logic using CartEntry and ShoppingCart's instance variable and can instead get the subtotal by using @cart.price.

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

CartEntry stores the unit_price and quantity, ShoppingCart stores an array of CartEntry objects. Order stores one instance of ShoppingCart. There are additional methods in implementation B (the price method) but those calculate and return the sum, not store the data.

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

Other than initialize, CartEntry and ShoppingCart have a price method that can return the price of each entry or cart in implementation B. In implementation A, only Order had a total_price method which did not adhere to single responsibility and used variables from ShoppingCart and CartEntry.


### 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, it's retained in Order. In implementation B, it's delegated to CartEntry and ShoppingCart.

### Does total_price directly manipulate the instance variables of other classes?

In A, yes. in B, no.

### If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?

Implementation B makes the change easier to modify the code in CartEntry (such as a conditional loop) instead of trying to make that logic work in Order.

### 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. The total_price in Order doesn't require knowing the unit price and quantity of each CartEntry.

### Changes made in Hotel

In my original HotelBooker class, I had a method for make_block and directly changed the cost of the Room as such:

` available[i].cost = discount `

To make this code less coupled, I created a wrapping method in Room `set_discount` so HotelBooker wouldn't directly handle the instance variable from Room.
22 changes: 22 additions & 0 deletions lib/date_range.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'date'

module Hotel
class DateRange
attr_reader :check_in, :check_out, :dates_booked

def initialize(check_in, check_out)
if check_in >= check_out
raise StandardError, "Your check in and check out date is not valid!"
elsif !check_in.is_a?(Date) || !check_out.is_a?(Date)
raise ArgumentError, "You must input Dates in the date range!"
end
@check_in = check_in
@check_out = check_out
@dates_booked = (check_in...check_out).to_a
end

def overlaps?(range)
@dates_booked & range.dates_booked == [] ? false : true
end
end
end
135 changes: 135 additions & 0 deletions lib/hotel_booker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
require 'date'
require_relative 'room'
require_relative 'reservation'
require_relative 'date_range'

module Hotel
class HotelBooker
attr_accessor :rooms, :reservations, :unreserved_block, :reserved_block
NUM_ROOMS = 20
BLOCK_MAX = 5
Copy link

Choose a reason for hiding this comment

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

YES CONSTANTS


def initialize()
@unreserved_block = []
@reserved_block = []
@reservations = []
@rooms = []

NUM_ROOMS.times do |i|
@rooms << Room.new(i+1)
end
end

def available?(available_rooms)
if available_rooms.empty?
raise StandardError, "There are no more available rooms for this date range!"
end
end

def range(check_in, check_out)
DateRange.new(check_in, check_out)
end

# arguments for check_in and check_out are Strings
def make_reservation(id, check_in, check_out)
check_in = Date.parse(check_in)
check_out = Date.parse(check_out)
reservation = Reservation.new(id, range(check_in, check_out))

available = unreserved_rooms(check_in, check_out)
available?(available)

reservation.room = available[0]
@reservations << reservation
end


def make_block(rooms, discount, check_in, check_out)
check_in = Date.parse(check_in)
check_out = Date.parse(check_out)
date_range = range(check_in, check_out)
available = unreserved_rooms(check_in, check_out)
if rooms > BLOCK_MAX
raise StandardError, "Five is the maximum number of rooms in a block."
elsif available.length < rooms
raise StandardError, "There are not enough available rooms to create a block."
end

rooms.times do |i|
reservation = Reservation.new("BLOCK ROOM #{i+1}", date_range)
available[i].set_discount(discount)
reservation.room = available[i]
@unreserved_block << reservation
end

return @unreserved_block
end


def make_block_reservation(id)
available?(@unreserved_block)
@reserved_block << @unreserved_block[0]
@unreserved_block.delete_at(0)
end


# arguments for check_in and check_out are Dates
def unreserved_rooms(check_in, check_out)
reserved_rooms = []
unreserved_block_rooms = []
reserved_block_rooms = []
new_range = range(check_in, check_out)

@reservations.each do |reservation|
if reservation.date_range.overlaps?(new_range)
reserved_rooms << reservation.room
end
end

@unreserved_block.each do |reservation|
if reservation.date_range.overlaps?(new_range)
unreserved_block_rooms << reservation.room
end
end

@reserved_block.each do |reservation|
if reservation.date_range.overlaps?(new_range)
reserved_block_rooms << reservation.room
end
end

unreserved = @rooms - reserved_rooms - unreserved_block_rooms - reserved_block_rooms

return unreserved
end

def unreserved_block_rooms
unreserved_block_rooms =
@unreserved_block.map { |reservation| reservation.room }
return unreserved_block_rooms
end


def find_reservations(date)
matching_reservations = []
date = Date.parse(date)

@reservations.each do |reservation|
reservation_dates = reservation.date_range.dates_booked
if reservation_dates.include?(date)
matching_reservations << reservation
end
end
Copy link

Choose a reason for hiding this comment

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

Instead of using .each, might be good opportunity to use select


@reserved_block.each do |reservation|
reservation_dates = reservation.date_range.dates_booked
if reservation_dates.include?(date)
matching_reservations << reservation
end
end

return matching_reservations
end

end
end
25 changes: 25 additions & 0 deletions lib/reservation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require_relative 'room'

module Hotel
class Reservation
attr_reader :id, :date_range
attr_accessor :room

def initialize(id, date_range)
@id = id
@date_range = date_range
# placeholder room initation
@room = Room.new(0)
end

def cost
@cost = calculate_total_cost
end

def calculate_total_cost
total_days = @date_range.dates_booked.length
total_cost = @room.cost * total_days
return total_cost
end
end
end
16 changes: 16 additions & 0 deletions lib/room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Hotel
class Room
attr_reader :id
attr_accessor :cost

def initialize(id)
@id = id
@cost = 200
end

def set_discount(new_cost)
@cost = new_cost
end

end
end
7 changes: 7 additions & 0 deletions refactors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
1. I...lowkey cheated and made a collection of Reservations instead of a collection of Rooms. I would rewrite this part by making a subclass under Room called BookedRoom?
Copy link

Choose a reason for hiding this comment

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

Not really sure what you mean by this tbh!


2. HotelBooker has a lot of WET code because I have to loop through several data structures (@unreserved_block, @reserved_block, @reservations)!! If I had focused the Wave 3 blocks of rooms on the ROOMS instead of reservations, probably wouldn't have created this mess ¯\_(ツ)_/¯
Copy link

Choose a reason for hiding this comment

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

👍


3. there are certain check_in and check_out arguments that are strings (as in Hotel::HotelBooker.new.make_reservation) and some that are an instance of Date (as in Hotel::HotelBooker.new.unreserved_rooms). Other arguments pass in a Hotel::DateRange (such as Hotel::Reservations). note to future goeun: make this less terrible
Copy link

Choose a reason for hiding this comment

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

Would absolutely agree with this


4. How many ways can you say "range"? ;__;
74 changes: 74 additions & 0 deletions spec/date_range_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require_relative 'spec_helper'

describe "DateRange Class" do
before do
@date1 = Date.parse('2018-09-05')
@date2 = Date.parse('2018-09-09')
@date3 = Date.parse('2018-09-10')
@date4 = Date.parse('2018-09-11')

@date_range1 = Hotel::DateRange.new(@date1, @date2)
@date_range2 = Hotel::DateRange.new(@date1, @date3)
@date_range3 = Hotel::DateRange.new(@date2, @date3)
@date_range4 = Hotel::DateRange.new(@date3, @date4)
end

describe "Date Range initation " do
it "is an instance of Date Range" do
expect(@date_range1).must_be_kind_of Hotel::DateRange
end

it "passes Dates as arguments" do
expect(@date_range1.check_in).must_be_kind_of Date
expect(@date_range2.check_out).must_be_kind_of Date
end

it "raises a StandardError if invalid date range is provided" do
expect{ Hotel::DateRange.new(@date1, @date1) }.must_raise StandardError
expect{ Hotel::DateRange.new(@date3, @date1) }.must_raise StandardError
end
it "raises an ArgumentError if user do not put in a Date" do
expect{ Hotel::DateRange.new(@date1, '12/08/23') }.must_raise ArgumentError
expect{ Hotel::DateRange.new('12/06/30', @date1) }.must_raise ArgumentError
expect{ Hotel::DateRange.new('12/06/30', '12/08/23') }.must_raise ArgumentError
end

it "creates an array of Dates between check_in and check_out & excluding check_out" do
expect(@date_range3.dates_booked).must_be_kind_of Array
expect(@date_range3.dates_booked.length).must_equal 1
expect(@date_range3.dates_booked[0]).must_equal @date2
expect(@date_range2.dates_booked.length).must_equal 5
expect(@date_range2.dates_booked[0]).must_equal @date1
end
end

describe "overlaps? method to check if ranges overlap" do
it "returns true if date ranges are the same" do
expect(@date_range1.overlaps?(@date_range1)).must_equal true
end

it "returns true if date ranges overlaps in the front" do
expect(@date_range1.overlaps?(@date_range2)).must_equal true
end

it "returns true if date ranges overlaps in the back" do
expect(@date_range2.overlaps?(@date_range1)).must_equal true
end

it "returns true if one range is completely contained in another range" do
expect(@date_range2.overlaps?(@date_range3)).must_equal true
end

it "returns false if one range is completely after" do
expect(@date_range1.overlaps?(@date_range4)).must_equal false
end

it "returns false if one range is completely before" do
expect(@date_range4.overlaps?(@date_range1)).must_equal false
end
it "returns false if a range starts on another's check in date" do
expect(@date_range1.overlaps?(@date_range3)).must_equal false
expect(@date_range2.overlaps?(@date_range4)).must_equal false
end
end
end
Copy link

Choose a reason for hiding this comment

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

I love this DateRange class because it isolated the overlapping date logic to this one class, which is complex and tested in isolation... Beautiful :* truly inspiring :*

Loading