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

Isabel Suchanek -- Carets #25

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c90c20d
Set up project and test files
isabeldepapel Sep 5, 2017
5921835
Set up initialize and self.all (and tests) for Room class
isabeldepapel Sep 5, 2017
7152f16
Initialized Reservation class (with tests)
isabeldepapel Sep 5, 2017
42d4a85
Added reserve method to Room class
isabeldepapel Sep 5, 2017
b7550c5
Added hotel class
isabeldepapel Sep 6, 2017
2fca0c9
Implemented all_reservations metho in hotel and added more error chec…
isabeldepapel Sep 6, 2017
3709c03
Finished wave 1
isabeldepapel Sep 6, 2017
f788ac1
Commented out exceptions focused on input type instead of business logic
isabeldepapel Sep 6, 2017
25dc256
Added find available rooms for date range method
isabeldepapel Sep 6, 2017
a89bc7e
Added is_booked method to Room class
isabeldepapel Sep 6, 2017
13531e4
Refactored find_avail_rooms method
isabeldepapel Sep 6, 2017
6f52656
Finished wave 2
isabeldepapel Sep 6, 2017
9fe74cf
Set up Block class
isabeldepapel Sep 7, 2017
75ff9a6
Added rate attribute to Reservation class
isabeldepapel Sep 7, 2017
c8e3547
Added reservable module
isabeldepapel Sep 7, 2017
432323b
Added methods to find available rooms in Block class
isabeldepapel Sep 8, 2017
f20a7d0
Added reserve method in Block and modified Room.reserve to return Res…
isabeldepapel Sep 8, 2017
61f4fd3
Finished wave 3, code needs cleaned up
isabeldepapel Sep 8, 2017
cdbe45d
Refactored is_booked and is_blocked methods
isabeldepapel Sep 8, 2017
ab468cd
Added discounted_cost to Block class and cleaned up some code
isabeldepapel Sep 9, 2017
d03b7a1
Refactored room tests and reservable.rb
isabeldepapel Sep 9, 2017
88e3857
Added self.all and self.find methods to Block, Resv, and Room classes
isabeldepapel Sep 10, 2017
815ac80
Refactored to use self methods and class vars
isabeldepapel Sep 11, 2017
dcc4b06
Added edge case to reserve test
isabeldepapel Sep 11, 2017
e7c017a
Added design activity questions
isabeldepapel Sep 30, 2017
92ab683
Loosened coupling between Hotel & Room classes
isabeldepapel Oct 1, 2017
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 = false
t.test_files = FileList['specs/*_spec.rb']
end

task default: :test
77 changes: 77 additions & 0 deletions lib/block.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require_relative 'reservation'
require_relative 'room'
require_relative 'reservable'

module Hotel

class Block
include Reservable

@@all_blocks = []

MAX_ROOMS = 5

attr_accessor :check_in, :check_out, :discount, :room_block, :block_id

def initialize(check_in, check_out, discount, room_block)
# check input
check_dates(check_in, check_out)
check_room_block(room_block, check_in, check_out)
check_discount(discount)

@block_id = @@all_blocks.length + 1
@check_in = check_in
@check_out = check_out

@discount = discount # discount as decimal (e.g. 0.8 for 80% of orig rate)
@room_block = room_block # array of rooms

# add block to each room
room_block.cycle(1) { |room| room.blocks << self }

@@all_blocks << self

end

def room_available?(room)
raise ArgumentError.new("Room #{room.room_num} isn't in the block") if !room_block.include?(room)

return !room.booked?(check_in, check_out)
end

def find_avail_in_block
return room_block.select { |room| room_available?(room) }
end

def reserve(room)
raise ArgumentError.new("Room #{room.room_num} isn't in the block") if !room_block.include?(room)

raise ArgumentError.new("Room #{room.room_num} isn't available for the selected dates") if room.booked?(check_in, check_out)

discounted_rate = discount * room.rate
return room.reserve(check_in, check_out, discounted_rate)
end

def self.all

Choose a reason for hiding this comment

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

Good following of the existing pattern, it makes it easier to add a CSV or database later.

return @@all_blocks
end

def self.clear
@@all_blocks = []
end

def self.find(block_id)
blocks = self.all

blocks.each do |block|
if block.block_id == block_id
return block
end
end

return nil
end

end # end of Block class

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

module Hotel

class Hotel
include Reservable

NUM_ROOMS = 20

attr_reader :rooms

def initialize(num_rooms = NUM_ROOMS)
# check input
check_room_num(num_rooms)

@rooms = {}

# loop through num_rooms and add rooms to hash
(1..num_rooms).each do |num|
@rooms[num] = ::Hotel::Room.new(num)
end

end

def reserve(start_date, end_date, room)

Choose a reason for hiding this comment

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

Just an observation, a user would need to identify a room object first before trying to reserve it. Why not just have them provide a room number and have this method first find the room.

That way the user doesn't have to know about Room objects or the Room class.

check_dates(start_date, end_date)

raise ArgumentError.new("Room #{room.room_num} isn't available for the selected dates") if room.booked?(start_date, end_date) || room.blocked?(start_date, end_date)

return room.reserve(start_date, end_date)

end

def block(start_date, end_date, discount, rooms)
raise ArgumentError.new("One or more rooms is unavailable for the selected dates") if rooms.any? { |room| room.booked?(start_date, end_date) || room.blocked?(start_date, end_date) }

return Hotel::Block.new(start_date, end_date, discount, rooms)
end

def find_reservations_by_date(date)
return ::Hotel::Reservation.find_by_date(date)
end

def find_avail_rooms(start_date, end_date)
# returns a hash of rooms available in the date range

# check input
check_dates(start_date, end_date)

return rooms.reject { |room_num, room| room.booked?(start_date, end_date) || room.blocked?(start_date, end_date) }

end

end # end of Hotel class

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

module Reservable

def check_dates(start_date, end_date)

raise TypeError.new("#{start_date} must be of type Date") if start_date.class != Date
raise TypeError.new("#{end_date} must be of type Date") if end_date.class != Date
raise ArgumentError.new("Check out must be later than check in") if start_date >= end_date
raise ArgumentError.new("Can't reserve a room for a date before today") if start_date < Date.today

end

def check_room_num(num)
raise TypeError.new("#{num} must of type Integer") if num.class != Integer
raise ArgumentError.new("Invalid number of rooms") if num < 1
end

def check_room(room)
raise TypeError.new("#{room} must be of type Hotel::Room") if room.class != Hotel::Room
end

def check_room_block(room_block, check_in, check_out)
raise TypeError.new("Block of rooms must be an array of Room objects") if room_block.class != Array || room_block[0].class != Hotel::Room

raise ArgumentError.new("Invalid number of rooms in block") if room_block.length > Hotel::Block::MAX_ROOMS || room_block.length < 1

raise ArgumentError.new("Can't have unavailable rooms in the block") if room_block.any? { |room| room.booked?(check_in, check_out) || room.blocked?(check_in, check_out) }
end

def check_rate(rate)
raise TypeError.new("#{rate} must be of type Integer") if rate.class != Integer
raise ArgumentError.new("Rate must be greater than 0") if rate < 1
end

def check_discount(discount)
# must be decimal representing percentage of full cost (e.g. 0.8 for 80% of orig rate)
raise TypeError.new("#{discount} must be of type Float") if discount.class != Float
raise ArgumentError.new("Not a discounted rate") if discount >= 1 || discount <= 0
end

def include?(date)
return date >= @check_in && date < @check_out
end

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

module Hotel

class Reservation
include Comparable
include Reservable

@@all_reservations = []

attr_accessor :check_in, :check_out, :room_num, :rate, :reservation_id

def initialize(check_in, check_out, room, rate = room.rate)

check_dates(check_in, check_out)
check_room(room)

@reservation_id = @@all_reservations.length + 1
@check_in = check_in
@check_out = check_out
@room_num = room.room_num
@rate = rate

@@all_reservations << self
end

def ==(other_reservation)
return check_in == other_reservation.check_in && check_out == other_reservation.check_out && room_num == other_reservation.room_num
end

# def include?(date)
# return date >= check_in && date < check_out
# end

def total_cost
num_nights = (check_out - check_in).to_i
return num_nights * rate
end

def self.all
return @@all_reservations
end

def self.find(reservation_id)
reservations = self.all

reservations.each do |reservation|
if reservation.reservation_id == reservation_id
return reservation
end
end

return nil
end

def self.clear
# clears all reservations (for testing)
@@all_reservations = []
end

def self.find_by_date(date)
raise TypeError.new("#{date} must be of type Date") if date.class != Date

return @@all_reservations.select { |reservation| reservation.include?(date)}

end

def to_s
# return human readable representation
s = "Reservation id: #{reservation_id}\n"
s += "Room number: #{room_num}\n"
s += "Check-in: #{check_in}\n"
s += "Check-out: #{check_out}\n"
s += "Total cost: #{total_cost}\n"

return s

end

end # end of Reservation class
end
117 changes: 117 additions & 0 deletions lib/room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
require_relative 'reservation'
require_relative 'reservable'

module Hotel

class Room
include Reservable
include Comparable

@@all_rooms = []

DEFAULT_RATE = 200

attr_reader :room_num, :reservations, :blocks
attr_accessor :rate

def initialize(room_num, rate = DEFAULT_RATE)

check_room_num(room_num)
check_rate(rate)

@room_num = room_num
@reservations = []
@blocks = []
@rate = rate

@@all_rooms << self

end

def <=>(other_room)
room_num <=> other_room.room_num

Choose a reason for hiding this comment

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

Nice!

end

def reserve(start_date, end_date, rate_to_charge = rate)

raise ArgumentError.new("Room #{room_num} isn't available for the given dates") if booked?(start_date, end_date)

new_reservation = ::Hotel::Reservation.new(start_date, end_date, self, rate_to_charge)
reservations << new_reservation#.reservation_id

return new_reservation

end

def booked?(start_date, end_date = start_date.next_day)

return array_include_date?(reservations, start_date, end_date)

end

def blocked?(start_date, end_date = start_date.next_day)

return array_include_date?(blocks, start_date, end_date)

end

def self.all
# @all_rooms.sort!
return @@all_rooms
end

def self.find(room_num)
rooms = self.all

rooms.each do |room|
if room.room_num == room_num
return room
end
end

return nil
end

def self.clear
@@all_rooms = []
end

def to_s
# return human readable representation
s = "Room number: #{room_num}\n"
s += "Rate per night: $#{rate}\n"
s += "Reservations:\n"

reservations.each do |reservation|
s += reservation.to_s
end

# s += "Blocks:\n"
# blocks.each do |block|
# s += block.to_s
# end

return (s += "\n")

end

private

def array_include_date?(array, start_date, end_date)

Choose a reason for hiding this comment

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

This method is a little too generic, what is the array parameter supposed to be?

It is however very DRY.

# don't include final date since check-out doesn't conflict with check-in of a new reservation
date_range = (start_date...end_date).to_a

array.each do |item|
date_range.each do |date|
if item.include?(date)
return true
end
end
end

return false
end

end # end of Room class

end
Loading