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

Amper: Kaitlin Forsman #23

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
cb02ee6
initial setup of lib and spec files so get ready to write tests and s…
kcforsman Mar 5, 2018
385592e
stubbed some tests for user class and wrote tests and code for user i…
kcforsman Mar 5, 2018
b9f7f43
added errors for invalid inputs to reserve_room method
kcforsman Mar 5, 2018
40a50e5
small refactor of tests and code
kcforsman Mar 5, 2018
31315fb
initialized reservation class with attributes: id, room_num, guest, s…
kcforsman Mar 5, 2018
3994a23
added night calculator method to reservation class
kcforsman Mar 5, 2018
9b9e88f
trying to structure how to store dates and reservations in relation t…
kcforsman Mar 6, 2018
1166bc7
finished main method and helper method that allows user to look up al…
kcforsman Mar 6, 2018
9556184
finished approach to calculating reservation cost and how user access…
kcforsman Mar 6, 2018
9d711e9
created a room class that has a number and calendar that dates can be…
kcforsman Mar 6, 2018
d5ca5a9
cleaned up unused but assigned variables and included a forgotten test
kcforsman Mar 6, 2018
fa322f7
refactored all room and reservation to take Range of dates instead of…
kcforsman Mar 6, 2018
8cc9a31
reworked, restructured, wrote tests and codes for method to present a…
kcforsman Mar 6, 2018
1ec19e7
added couple more tests to user_spec to check availability at the end…
kcforsman Mar 7, 2018
2d777b5
double-checked functionality of find_available room: it works and doe…
kcforsman Mar 7, 2018
2289465
correct version of helper spec and set up of new class files for a Block
kcforsman Mar 7, 2018
977b8dd
initialized Block spec and added notes to user for tests and content …
kcforsman Mar 8, 2018
4035cf4
added initial method for block rooms availibility
kcforsman Mar 8, 2018
c99adbc
finished block versions of find_available_rooms and reserve_room with…
kcforsman Mar 8, 2018
6865048
finished discounted calculate_reservation_cost method to finish first…
kcforsman Mar 8, 2018
f84fde8
stubbed out remaining tests I could think of for Block with the User …
kcforsman Mar 8, 2018
6b2607a
stubbed out remaining tests I could think of for Block within the Use…
kcforsman Mar 8, 2018
89358fb
finished functionality for creating a block within User and adjusting…
kcforsman Mar 8, 2018
001caa3
finished functionality for reserving a room from the block within the…
kcforsman Mar 10, 2018
daa21aa
finished last piece of functionality, allowing User to search for ava…
kcforsman Mar 10, 2018
bea1945
not sure what i'm committing
kcforsman Mar 10, 2018
a87ae30
while everything is still working: refactoring, pulled rooms out of i…
kcforsman Mar 12, 2018
dcd77c7
finished answering question for code analysis of Online Shopping Cart…
kcforsman Apr 1, 2018
2192c67
adjusted id assignment for blocks and reservationswith a class variab…
kcforsman Apr 1, 2018
659b5d0
changed code to eliminate other User's direct interaction with each R…
kcforsman Apr 1, 2018
cdcff7b
adjusted User class so it doesnt need to directly know a reservations…
kcforsman Apr 1, 2018
cfbcbec
deleted attr_reader for @reservations in Block class. it was used no …
kcforsman Apr 2, 2018
b4cee38
altered Room and Reservation class, so that reservation does directly…
kcforsman Apr 2, 2018
cd3e93b
updated design activity with what discussion of how to improve hotel'…
kcforsman Apr 2, 2018
e4ff02c
added some tests to up coverage percentage
kcforsman 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
37 changes: 37 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#Hotel Revisited
*****
### Questions for Online Shopping Cart Code Implementations:
* What classes does each implementation include? Are the lists the same?
- Both implementations have the same classes: CartEntry, ShoppingCart, and Order.
* Write down a sentence to describe each class.
- CartEntry appears to be a class for containing information about a specific item or product.
- ShoppingCart appears to be a class that stores a list of items or products.
- Order appears to be class for calculating total cost of the ShoppingCart, including SALES_TAX.
* How do the classes relate to each other? It might be helpful to draw a diagram.
- There doesn't appear to be any explicit relationship between the CartEntry and any of the other classes, but I would assume a ShoppingCart could potentially have one or more CartEntry instances.
- An instance of a Order within this implementation has-a ShoppingCart.
* What data does each class store? How (if at all) does this differ between the two implementations?
- The appears to be no difference between the two implementations for the data each class stores: CartEntry stores the \@quantity (of the product) and \@unit_price (cost per product), ShoppingCart stores \@entries (a list of some entries, in this code, like CartEntry instances), and Order stores \@cart (a ShoppingCart) and the constant, SALES_TAX.
* What methods does each class have? How (if at all) does this differ between the two implementations?
- Each implementation as an initialize method for the three classes.
- Implementation A: uses helper methods in each "lower level" class, CartEntry and ShoppingCart. Then, Order#total_price calculates the sum all entries together and adds on sales tax.
- Implementation B: uses no helper methods in any class. Instead, CartEntry and ShoppingCart have price instance methods that calculate the their own prices. Then, Order only has to add the sales tax cost to the price in Order#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 Implementation A, I would say the logic is solely retained in Order.
- In Implementation B, the logic is shared between all three classes, as such delegated to the "lower level" classes: ShoppingCart and CartEntry.
* Does total_price directly manipulate the instance variables of other classes?
- In Implementation A, yes, it does (helper methods).
- In Implementation B, no, it does not seem to.
* If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?
- You would need to add some conditional if quantity > some_amount then unit_price = unit_price * some_discount. This code could be added into the each loop of total_price for Implementation A or the instance method for price of CartEntry in Implementation B.
- As far as difficulty to modify the code to make this possible, I think it would be possible to apply to either method just as easily. It also depends on how complicated the discount logic is. You would need some type of discount for above a certain quantity. If the discount is the same no matter what the item, either method can be changed as easily as the other. If the discount is changes item to item, I think it would be easier to change Implementation B.
* Which implementation better adheres to the single responsibility principle?
- I believe Implementation B adheres better. The Order doesn't have to the responsibility of calculating each CartEntry price then summing all the Entries in the ShoppingCart together to then add on sales tax.
* Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled?
- Also, Implementation B. It doesn't have to call on the instance variables of CartEntry or ShoppingCart to find the total price for the order.
*****
### The Hotel Revision
I had a few unnecessary helper methods, so want to change the code, so the classes only have access to the ids of the other classes. This means changing the code so Room doesn't respond to :calendar or :cost, Reservation doesn't respond to :room or :date_range, and Block doesn't respond to :reservations. This is better because if later I change all the information in a class, I can just change that method within the class and the other classes wont know what know any different as long as it is getting the information it needs.

To revise Hotel, I changed the initialize arguments for Block and Reservation to take a hash instead of separate arguments. I changed the way the classes assign and track ids. Then, I worked on changing all the different classes approach to instance variables of other classes, so that except for ids and room_num, the other classes don't know too much about the others. (hopefully)
47 changes: 47 additions & 0 deletions lib/block.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'date'
require 'pry'

module Hotel
class Block < Reservation
@@id = 1
attr_reader :id
def initialize block_args
super(block_args)
@party = block_args[:party]
@rooms = block_args[:rooms]
@discount = block_args[:discount]
@reservations = []
@reserved_rooms = []
end

def find_available_rooms
available_rooms = []
return @rooms if @reserved_rooms.empty?
@rooms.each do |room|
next if @reserved_rooms.any? { |reserved_room| reserved_room == room }
available_rooms << room
end
raise StandardError.new("no rooms available") if available_rooms.empty?
available_rooms
end

def reserve_room(room_num, guest)
room = @rooms[room_num - 1]
raise StandardError.new("room not available") if !find_available_rooms.include?(room)
new_reservation = Reservation.new({id: @@id, room: room, guest: guest, date_range: @date_range})
@@id += 1
@reserved_rooms << room
@reservations << new_reservation
new_reservation
end

def calculate_reservation_cost(reservation_id)
reservation = find_reservation(reservation_id)
((1 - @discount) * reservation.calculate_reservation_cost).round(2)
end

def find_reservation(reservation_id)
@reservations.find { |reservation| reservation.id == reservation_id }
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 'date'

module Hotel
class Reservation
attr_reader :id
def initialize reservation_args
@id = reservation_args[:id]
@room = reservation_args[:room]
@guest = reservation_args[:guest]
@date_range = reservation_args[:date_range]
end

def calculate_nights
@date_range.to_a.length
end

def find_all_dates
@date_range.to_a
end

def calculate_reservation_cost
@room.calculate_cost(calculate_nights)
end
end
end
27 changes: 27 additions & 0 deletions lib/room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'date'
require 'pry'

module Hotel
class Room
attr_reader :room_num
def initialize(room_num, cost)
@room_num = room_num
@cost = cost
@calendar = []
end

def add_to_calendar(date_range)
dates = date_range.to_a
dates.each { | date | @calendar << date }
end

def is_available date_range
return false if @calendar.any?(date_range)
true
end

def calculate_cost nights
@cost * nights
end
end
end
100 changes: 100 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
require 'date'
require 'pry'

module Hotel
TOTAL_ROOMS = 20
class User
@@id = 1
attr_reader :rooms
def initialize(rooms)
if rooms.size != TOTAL_ROOMS
raise StandardError.new("incorrect number of rooms")
end
@rooms = rooms
@reservations = []
@calendar = {}

Choose a reason for hiding this comment

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

Interesting idea having a hash of the reservation dates, very efficient.

end
def find_available_rooms(start_date, end_date)
date_range = (start_date...end_date)
available_rooms = []
@rooms.each do |room|
available_rooms << room if room.is_available(date_range)
end
available_rooms
# need to deal with no rooms available
end

def reserve_room(room_num, guest, start_date, end_date)
valid_dates(start_date, end_date)
date_range = (start_date...end_date)
check_room_availibility(room_num, date_range)
room = @rooms[room_num - 1]
id = @reservations.length + 1
new_reservation = Reservation.new({id: @@id, room: room, guest: guest, date_range: date_range})
@@id += 1
add_to_calendar(new_reservation, date_range)
room.add_to_calendar(date_range)
@reservations << new_reservation
new_reservation
end

def valid_dates(start_date, end_date)
if start_date.class != Date || end_date.class != Date
raise StandardError.new("not a date")
elsif end_date < start_date
raise StandardError.new("End date (#{end_date}) comes before start date (#{start_date})")
elsif end_date == start_date
raise StandardError.new("End date (#{end_date}) is same as start date (#{start_date})")
end
end

def check_room_availibility(room_num, date_range)
calendar = @rooms[room_num - 1].is_available(date_range)
return true if calendar
if !calendar
raise StandardError.new("room already reserved") if date_range.include?(date)
end
end

def add_to_calendar(reservation, date_range)
date_range.each do |date|
@calendar[date] ? @calendar[date].push(reservation) : @calendar[date] = [reservation]
end
end

def find_reservations_for_given_date(date)
@calendar[date]
end

def find_reservation_cost(reservation_id)
reservation = find_reservation(reservation_id)
reservation.calculate_reservation_cost
end
def find_reservation(reservation_id)
@reservations.find { |reservation| reservation.id == reservation_id }
end

def create_room_block(rooms, party, start_date, end_date, discount)
raise StandardError.new('too many rooms for a block') if rooms.length > 5
raise StandardError.new('too few rooms for a block') if rooms.length < 2
valid_dates(start_date, end_date)
date_range = (start_date...end_date)
rooms.each { |room| check_room_availibility(room.room_num, date_range) }
id = @reservations.length + 1
new_block = Block.new({id: @@id, rooms: rooms, party: party, date_range: date_range, discount: discount})
@@id += 1
add_to_calendar(new_block, date_range)
rooms.each { |room| room.add_to_calendar(date_range) }
@reservations << new_block
new_block
end

def reserve_room_from_block(block, room_num, guest)
block.reserve_room(room_num, guest)
end

def check_block_room_availibility(block)
block.find_available_rooms
end
end
end
85 changes: 85 additions & 0 deletions specs/block_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
require_relative 'spec_helper'
require 'date'
require 'pry'

describe 'Block class' do
describe 'instantiation' do
before do
rooms = []
4.times {|x| rooms << Hotel::Room.new(x+1, 200) }
date_range = (Date.new(2018,3,22)...Date.new(2018,3,26))
@block = Hotel::Block.new({id: 1, rooms: rooms, party: "Fanime", date_range: date_range, discount: 0.2})
end
it 'can be initialized' do
@block.must_be_instance_of Hotel::Block
end
it 'inherits from User' do
@block.must_be_kind_of Hotel::Reservation
end
it 'has attributes: id, list of some rooms, reservations, party, date range, discount' do
# may not need this test stub....
@block.must_respond_to :id
@block.id.must_equal 1
end
end
describe 'find_available_rooms' do
before do
@rooms = []
4.times {|x| @rooms << Hotel::Room.new(x+1, 200) }
date_range = (Date.new(2018,3,22)...Date.new(2018,3,26))
@block = Hotel::Block.new({id: 1, rooms: @rooms, party: "Fanime", date_range: date_range, discount: 0.2})
end
it 'returns array of rooms available in block' do
available_rooms_in_block = @block.find_available_rooms

available_rooms_in_block.must_be_kind_of Array
available_rooms_in_block.must_equal @rooms
end
it 'handles (throw exception or returns nil) no rooms available' do
4.times do |x|
@block.reserve_room(x+1, "fan #{x+1}")
end

proc { @block.find_available_rooms }.must_raise StandardError
end
it 'exclude rooms that are already reserved' do
@block.reserve_room(1, "Fan 543")
@block.reserve_room(3, "Fan 564")

available_rooms_in_block = @block.find_available_rooms

available_rooms_in_block.wont_include @rooms[0]
available_rooms_in_block.wont_include @rooms[2]
end
end
describe 'reserve_room' do
before do
rooms = []
4.times {|x| rooms << Hotel::Room.new(x+1, 200) }
date_range = (Date.new(2018,3,22)...Date.new(2018,3,26))
@block = Hotel::Block.new({id: 1, rooms: rooms, party: "Fanime", date_range: date_range, discount: 0.2})
end
it 'returns a reservation for an available room' do
reservation = @block.reserve_room(1, "Fan 1")

reservation.must_be_instance_of Hotel::Reservation
end
it 'throws exception if room is unavailable' do
@block.reserve_room(2, "Jace Poe")
proc { @block.reserve_room(2, "Jade Poe")}.must_raise StandardError
end
end
describe 'calculate_reservation_cost' do
before do
rooms = []
4.times {|x| rooms << Hotel::Room.new(x+1, 200) }
date_range = (Date.new(2018,3,22)...Date.new(2018,3,26))
@block = Hotel::Block.new({id: 1, rooms: rooms, party: "Fanime", date_range: date_range, discount: 0.2})
end
it 'returns discounted cost for the reservation' do
reservation = @block.reserve_room(1, "Meka Starbright")

@block.calculate_reservation_cost(reservation.id).must_equal 640
end
end
end
54 changes: 54 additions & 0 deletions specs/reservation_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require_relative 'spec_helper'
require 'date'

describe 'Reservation' do
before do
date_range = (Date.new(2018,3,18)...Date.new(2018,3,26))
room = Hotel::Room.new(4, 200)
@reservation = Hotel::Reservation.new({id: 1, room: room, guest: "Bob", date_range: date_range})
end
describe 'initialization' do
it 'can be initialized' do
@reservation.must_be_instance_of Hotel::Reservation
end
it 'has attributes: id, room, guest, date_range' do
@reservation.id.must_equal 1
@reservation.id.must_be_kind_of Integer
# @reservation.room.must_be_instance_of Hotel::Room
end
end
describe 'calculate_nights' do
before do
date_range = (Date.new(2018,3,18)...Date.new(2018,3,26))
room = Hotel::Room.new(4, 200)
@reservation = Hotel::Reservation.new({id: 1, room: room, guest: "Bob", date_range: date_range})
end
it 'returns the number of nights' do
nights = @reservation.calculate_nights

nights.must_be_kind_of Integer
nights.must_equal 8
end
end
describe 'find_all_dates' do
it 'returns an array of all dates in reservation' do
date_range = (Date.new(2018,3,20)...Date.new(2018,3,22))
room = Hotel::Room.new(4, 200)
reservation = Hotel::Reservation.new({id: 1, room: room, guest: "Kaeli", date_range: date_range})
all_dates = reservation.find_all_dates

all_dates.must_be_kind_of Array
all_dates.must_equal [Date.new(2018,3,20), Date.new(2018,3,21)]
end
end

describe 'calculate_reservation_cost' do
it 'returns the cost of the reservation' do
date_range = (Date.new(2018,3,20)...Date.new(2018,3,25))
room = Hotel::Room.new(4, 200)
reservation = Hotel::Reservation.new({id: 1, room: room, guest: "Bob", date_range: date_range})

reservation.calculate_reservation_cost.must_equal 1000
end
end
end
Loading