Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bff96e2
Created scaffolding for initial design concept
cmn53 Mar 5, 2018
a29fe47
Added Room#initialize and Room#add_reservation methods and tests. All…
cmn53 Mar 6, 2018
7cd30d3
Added Room#is_available? method. All Room tests passing
cmn53 Mar 7, 2018
9c0faf2
Added tests for initializing Admin class
cmn53 Mar 8, 2018
e0d2c8a
Added tests for Admin#reserve method.
cmn53 Mar 8, 2018
3a54f06
Added tests for Admin#find_reservations method
cmn53 Mar 8, 2018
ccf130c
Added DateRange class and modified other classes to accept instance o…
cmn53 Mar 8, 2018
47b4b7d
Modified Room#is_available? method to accept a date range and updated…
cmn53 Mar 8, 2018
482ab6a
Added Admin#find_available_rooms method and tests
cmn53 Mar 8, 2018
92e8ec0
Modified Admin#reserve_room method and tests to raise error for no av…
cmn53 Mar 8, 2018
28067bf
Added a BlockReservation class to inherit from Reservation. Ammended …
cmn53 Mar 8, 2018
7fb73b8
Added overlap methods to Reservation and DateRange classes and change…
cmn53 Mar 10, 2018
531c9e4
Deleted BlockReservation class - decided to include in Reservation
cmn53 Mar 10, 2018
de3d3f3
Added block_id to Reservation class
cmn53 Mar 10, 2018
08b8d2b
Refactored to eliminate most of what the room class 'knew'
cmn53 Mar 10, 2018
3dc1095
Updated tests for DateRange to include overlap? method
cmn53 Mar 10, 2018
36179c3
Updated Reservation tests to include block_id and block_status
cmn53 Mar 10, 2018
ddfc614
Reordered methods and tests in Admin class
cmn53 Mar 10, 2018
c95b523
Added helper method for assigning block ids
cmn53 Mar 10, 2018
22cabf1
Added test for Admin#create_block method
cmn53 Mar 10, 2018
ef2fc37
Added Admin#find_available_block_rooms method and tests
cmn53 Mar 10, 2018
47c6a98
Added tests for Admin#reserve_block_room method
cmn53 Mar 12, 2018
552ce4b
Added tests for Reservation#change_status method. Failing test for er…
cmn53 Mar 12, 2018
b8c9b9b
Changed Admin#next_block_id to eliminate error causeed by comparison …
cmn53 Mar 12, 2018
d8ba4cb
Added design activity markdown file
cmn53 Apr 2, 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
9 changes: 9 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require 'rake/testtask'

Rake::TestTask.new do |t|
t.libs = ["lib"]
t.warning = true
t.test_files = FileList['specs/*_spec.rb']
end

task default: :test
43 changes: 43 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
###### What classes does each implementation include? Are the lists the same?
Both implementations include CartEntry, ShoppingCart, and Order classes.

###### Write down a sentence to describe each class.
Implementation A:
* CartEntry stores the unit price and quantity for each item in a cart.
* ShoppingCart stores a list of the items in a cart.
* Order calculates the subtotal of all items in a cart, the sales tax, and the total.

Implementation B:
* CartEntry stores the unit price and quantity for each item in a cart and calculates the price of that item.
* ShoppingCart stores a list of the items in a cart and calculates the subtotal for all items in the cart.
* Order calculates the sales tax and total for the cart.

###### How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.
Implementation A: CartEntry and ShoppingCart are not dependent on any other class. Order is dependent on both ShoppingCart (to provide the list of items) and CartEntry (to provide the unit prices and quantities for each item).

Implementation B: CartEntry is not dependent on any other class. ShoppingCart is dependent on CartEntry to provide prices for the items in the cart. Order is dependent on ShoppingCart to provide a list of items in the cart and the cart subtotal.

###### What data does each class store? How (if at all) does this differ between the two implementations?
In both implementations CartEntry stores unit prices and quantities, Shopping Cart stores the list of items entered in the cart, and Order stores an instance of ShoppingCart.

###### What methods does each class have? How (if at all) does this differ between the two implementations?
In Implementation A, CartEntry and ShoppingCart have only initialization methods, and Order has an initialization method and method to calculate the total price of the order.

In Implementation B, CartEntry, ShoppingCart, and Order all have initialization methods and methods to calculate the price of an item, the subtotal for items in a shopping cart, and the total price of an order, respectively.

###### 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 the total price is all retained in the Order class. In Implementation B, the logic is delegated to the 'lower level' ShoppingCart and CartEntry classes.

* **Does total_price directly manipulate the instance variables of other classes?**
I'm a little confused by what is meant by 'manipulation' -- to my mind, manipulation implies that the value assigned to an instance variable is modified. The total_price method does not modify the value of another classes' instance variable in either implementation. However, in Implementation A, the total_price method reads the values of the CartEntry classes' instance variables. In Implementation B, the total_price method does not directly read the values of other classes' instance variables.

###### If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?
If items are cheaper if bought in bulk, we would need to modify the code to include a unit price that is dependent on the quantity. It would be easier to make the modification in Implementation B because the unit price could be altered within the existing CartEntry#price method (or alternatively, in a new CartEntry instance method), and no other changes to ShoppingCart or Order would be necessary. In Implementation A, a new instance method would probably be added to CartEntry to calculate the unit price based on the quantity, and the Order#total_price method would have to be modified to call that method rather than reading the value of CartEntry's unit price instance variable.

###### Which implementation better adheres to the single responsibility principle?
Implementation B better adheres to the single responsibility principle because the purpose of each class is better encapsulated (more wholly contained) within that class than in Implementation A.

###### Which implementation is more loosely coupled?
Implementation B is more loosely coupled.
<!-- -->
119 changes: 119 additions & 0 deletions lib/admin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
require_relative 'date_range'
require_relative 'room'
require_relative 'reservation'

module Hotel
class Admin
attr_reader :rooms, :reservations

def initialize
@rooms = load_rooms
@reservations = []
end

def load_rooms
rooms = []
(1..20).each do |num|
rooms << Room.new(num)
end

return rooms
end

def find_reservations(date)
reservations_by_date = @reservations.select do |res|
res.date_range.range.include?(date)
end

return reservations_by_date
end

def find_available_rooms(date_range)
conflicts = @reservations.select do |reservation|
reservation.overlap?(date_range)
end

unavailable_rooms = conflicts.map { |res| res.room }
available_rooms = @rooms - unavailable_rooms

return available_rooms
end

def reserve_room(date_range)
if find_available_rooms(date_range).empty?
raise StandardError.new("There are no available rooms for that date range.")
end

reservation_data = {
id: next_reservation_id,
date_range: date_range,
room: find_available_rooms(date_range).sample
}

new_reservation = Reservation.new(reservation_data)
@reservations << new_reservation

return new_reservation
end

def next_reservation_id
return 1 if @reservations.empty?
return @reservations.map { |res| res.id }.max + 1
end

def next_block_id
block_reservations = @reservations.select { |res| res.block_id != nil }

if block_reservations.empty?
return 1
else
max_block_id = block_reservations.map { |res| res.block_id }.max
return max_block_id + 1
end
end

def create_block(num_rooms, date_range)
if num_rooms > 5
raise ArgumentError.new("A block can include a maximum of 5 rooms.")
end

if find_available_rooms(date_range).length < num_rooms
raise StandardError.new("There are not enough available rooms to block for those dates.")
end

block_id = next_block_id

num_rooms.times do
reservation_data = {
id: next_reservation_id,
date_range: date_range,
room: find_available_rooms(date_range).sample,
block_id: block_id,
block_status: :blocked
}
new_block_reservation = Reservation.new(reservation_data)
@reservations << new_block_reservation
end
end

def find_available_block_rooms(block_id)
available_block_rooms = @reservations.select do |res|
res.block_id == block_id && res.block_status == :blocked
end

return available_block_rooms
end

def reserve_block_room(block_id)
available_rooms = find_available_block_rooms(block_id)

if available_rooms.empty?
raise StandardError.new("There are no available rooms in Block #{block_id}.")
end

available_rooms.first.change_status
end


end
end
30 changes: 30 additions & 0 deletions lib/date_range.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Hotel
class DateRange

attr_reader :start_date, :end_date

def initialize(start_date, end_date)
@start_date = Date.parse(start_date)
@end_date = Date.parse(end_date)

[@start_date, @end_date].each do |date|
if date.class != Date
raise StandardError.new("Invalid date format. Accepted formats include 'YYYY-MM-DD' or 'YYYY/MM/DD'.")
end
end

if @end_date <= @start_date
raise StandardError.new("End date must be at least one day later than start date.")
end
end

def range
return (@start_date...@end_date).to_a
end

def overlap?(other)
return !(other.range & self.range).empty?
end

end
end
37 changes: 37 additions & 0 deletions lib/reservation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Hotel
class Reservation

attr_reader :id, :date_range, :room, :block_id, :block_status

COST_PER_NIGHT = 200.0
BLOCK_DISCOUNT = 0.25

def initialize(input)
@id = input[:id]
@date_range = input[:date_range]
@room = input[:room]
@block_id = input[:block_id]
@block_status = input[:block_status]
end

def cost
nights = @date_range.range.count
cost = nights * COST_PER_NIGHT
cost *= (1 - BLOCK_DISCOUNT) if @block_id != nil

return cost
end

def overlap?(date_range)
return @date_range.overlap?(date_range)
end

def change_status
if @block_status == :blocked
@block_status = :reserved
elsif @block_status == :reserved
@block_status = :blocked
end
end
end
end
15 changes: 15 additions & 0 deletions lib/room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Hotel
class Room

attr_reader :number

def initialize(num)

if num < 1 || num > 20
raise ArgumentError.new("Room number must be between 1 and 20")
end

@number = num
end
end
end
Loading