-
Notifications
You must be signed in to change notification settings - Fork 45
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
base: master
Are you sure you want to change the base?
Changes from 24 commits
c90c20d
5921835
7152f16
42d4a85
b7550c5
2fca0c9
3709c03
f788ac1
25dc256
a89bc7e
13531e4
6f52656
9fe74cf
75ff9a6
c8e3547
432323b
f20a7d0
61f4fd3
cdbe45d
ab468cd
d03b7a1
88e3857
815ac80
dcc4b06
e7c017a
92ab683
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,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 |
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 | ||
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 |
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) | ||
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. 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 |
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 |
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 |
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 | ||
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. 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) | ||
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 method is a little too generic, what is the 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 |
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.
Good following of the existing pattern, it makes it easier to add a CSV or database later.