diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..deb52f2cd --- /dev/null +++ b/Rakefile @@ -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 diff --git a/design-activity.md b/design-activity.md new file mode 100644 index 000000000..f09b4b4d8 --- /dev/null +++ b/design-activity.md @@ -0,0 +1,73 @@ +# Activity: Evaluating Responsibility + +* **What classes does each implementation include? Are the lists the same?** + +Each implementation includes the following classes (They are the same): +CartEntry +ShoppingCart +Order + + +* **Write down a sentence to describe each class.** + +CartEntry: This class includes the quantity and price of each item in the ShoppingCart + +ShoppingCart: This class includes an array of entries. + +Order: This class calculates the total price of the order including sales tax. + +_In implementation B the CartEntry calculates the cost of the entry and the ShoppingCart calculated the total (with sales tax being included in the order). In implementation A the Order combines all the calculations for price._ + + +* **How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper.** + +ShoppingCart holds an array that can be filled with instances of CartEntry (or anything else for that matter, since it isn't hard coded into the ShoppingCart class.) Order has-a ShoppingCart + +* **What data does each class store? How (if at all) does this differ between the two implementations?** + +CartEntry: unit price and quantity of the item in the cart +ShoppingCart: collection of entries +Order: an instance of ShoppingCart, and the constant value of the SALES_TAX. + +The data stored in each class is the same for both classes, the difference is that in implementation A the Order is responsible for all the pricing calculations whereas in Implementation B, each class has a method to communicate the calculation of the price. + +* **What methods does each class have? How (if at all) does this differ between the two implementations?** + +A: +CartEntry: attr_accessor for unit price and quantity +ShoppingCart: attr_accessor for entries +Order: total_price to calculate the total price of the Order + +B: +CartEntry: same as A with additional price method to calculate the price of the CartEntry +ShoppingCart: same as A with an additional price method to calculate the sum of all the CartEntry prices +Order: same, but the total_price method receives a method from the ShoppingCart to receive the total price before tax and the uses that to calculate the total_price with tax. This is a better implementation since the calculation done on the entries is done in their respective methods instead of in the Order class. + +* **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?** +A: computing the price is all retained in Order +B: delegated to "lower level" classes + +* **Does total_price directly manipulate the instance variables of other classes?** +I struggle with this question in terms of what it means to manipulate an instance variable. total_price does not change the value of any of the instance variables from other classes, but it does make calculations using the instance variables. + +* **If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify?** + +We would need to write a conditional statement into the price. This would be easier to modify in implementation B. + +* **Which implementation better adheres to the single responsibility principle?** + +Implementation B better adheres to the single responsibility principle. + +* **Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled?** + +Implementation B is more loosely coupled. +------------------- + +# Revisiting Hotel Activity: + +A change that I decided to make to my Hotel project included changes within several classes. I created a DateRange class that was a superclass for Reservations and Blocks. Since Blocks and Reservations need to utilize the same features such as a check_in date, a check_out date, and a way to check for room availability, this allowed for one class to take care of all those features as well as check for date validity. This creates for code that is more DRY as well as classes that are easier to re-use and change. Within the Hotel class I changed to responsibility for checking for an overlap between the dates requested to check in and the rooms in the hotel to the Date Range class. My tests proved useful when I ran rake and found a few failures that were due to my change in code and they pointed me in the right direction. I found myself extremely grateful for the existence of tests! + + +-- create a date range class that is a superclass to reservation and block +-- take overlap from hotel class and put it into date range class. diff --git a/lib/block.rb b/lib/block.rb new file mode 100644 index 000000000..6f8b34b19 --- /dev/null +++ b/lib/block.rb @@ -0,0 +1,18 @@ +require_relative 'date_range.rb' + +module Hotel + class Block < DateRange + + attr_reader :check_in, :check_out, :id, :rooms_in_block, :discount + + def initialize(check_in, check_out, id) + super(check_in, check_out) + @id = id + @rooms_in_block = [] + end + + def add_room(room) + @rooms_in_block << room + end + end # end of block class +end # end of Hotel module diff --git a/lib/cli.rb b/lib/cli.rb new file mode 100644 index 000000000..9e7fb5a98 --- /dev/null +++ b/lib/cli.rb @@ -0,0 +1,298 @@ +require_relative 'hotel' +require_relative 'pricing' +require 'terminal-table' +require 'chronic' +require 'pry' + +module Hotel + class Cli + + attr_reader :hotel + + def initialize + @hotel = Hotel.new + end + + def begin + while true + run_program + end + end + + def run_program + begin + display_user_menu + choice = get_menu_option + + case choice + when "A" + reserve_room + when "B" + reserve_room_from_block + when "C" + make_block_of_rooms + when "D" + view_reservations + when "E" + view_all_reservations + when "F" + view_available_rooms + when "G" + view_available_rooms_in_block + when "H" + change_room_cost + when "I" + view_rooms + when "X" + abort("Thank you! Have a nice day!") + else + raise ArgumentError.new "not a valid choice" + end + rescue + puts "I'm sorry! Something went wrong! Please try again" + return true + end + end + + def display_user_menu + puts "\nMenu Options:\n + A. Book a room + B. Book a room from a block + C. Reserve a block of rooms + D. View reservations (and costs) by date + E. View all reservations + F. View available rooms + G. View available rooms in a block + H. Change the cost of a room + I. View all rooms in the hotel\n + X. Exit the program" + end + + def get_input + input = gets.chomp + return input + end + + def get_menu_option + choice = nil + until choice =~ (/^[A-Ia-i]|[Xx]$/) + print "\nPlease choose an option: " + choice = get_input + end + return choice.upcase + end + + ### Write in option of no room preference + # choice A + def reserve_room + check_in = get_date_for("check in", Date.today) + + check_out = get_date_for("check out", check_in + 1) + + print "Guest Name: " + guest = get_input + + print "Preferred Room (Type N for no preference): " + room_number = get_room + + res = hotel.make_reservation(guest, check_in, check_out, room_number) + + show_table_of_reservations([res.last]) + end + + # choice B + def reserve_room_from_block + block = get_block + + print "Guest Name: " + guest = get_input + + res = hotel.make_reservation_from_block(guest, block.id) + + show_table_of_reservations([res.last]) + end + + # choice C + def make_block_of_rooms + check_in = get_date_for("check in", Date.today) + + check_out = get_date_for("check out", check_in + 1) + + num_rooms = get_num_rooms + + block = hotel.make_block(check_in, check_out, num_rooms) + + puts "Here are the rooms reserved for block ID: #{block.id}" + + show_table_of_rooms(block.rooms_in_block) + end + + # choice D + def view_reservations + date = get_date_for("see reservations for") + reservations = hotel.get_res_by_date(date) + + show_table_of_reservations(reservations) + end + + # choice E + def view_all_reservations + show_table_of_reservations(hotel.reservations) + end + + # choice F + def view_available_rooms + + begin_date = get_date_for("begin", Date.today) + + end_date = get_date_for("end", begin_date + 1) + + available_rooms = hotel.get_available_rooms(begin_date, end_date) + + show_table_of_rooms(available_rooms) + end + + # choice G + def view_available_rooms_in_block + block = get_block + + puts "Rooms still available for block ID:#{block.id} :" + + rooms_available_in_block = hotel.get_available_rooms_from_block(block) + + show_table_of_rooms(rooms_available_in_block) + + end + + # choice H + def change_room_cost + room = get_room + print "New Cost: " + new_cost = get_value + + room.cost_per_night = new_cost + + puts "\nRoom #{room.number} has been changed to $#{room.cost_per_night} per night." + + view_rooms + end + + # choce I + def view_rooms + puts "\nHere is a current list of the rooms and their prices: " + + show_table_of_rooms(hotel.rooms) + end + + def get_num_rooms + num_rooms = nil + until num_rooms =~ (/[1-5]/) + print "How many rooms would you like to reserve? (max 5)" + num_rooms = get_input + end + return num_rooms.to_i + end + + def get_date_for(action, must_be_after_date = nil) + date = nil + + while date == nil + date = get_date(action, must_be_after_date) + end + + return date.to_date + end + + def get_date(action, must_be_after_date) + begin + print "What date would you like to #{action}? " + input = get_input + + date = Chronic.parse(input).to_date + + if (must_be_after_date != nil) && (date < must_be_after_date) + raise StandardError.new "Invalid Date" + end + rescue + puts "Invalid date" + date = nil + end + return date + end + + def get_block + while true + print "Block ID: " + block_id = get_value + + hotel.blocks.each do |block| + if block.id == block_id.to_i + return block + end + end + puts "I'm sorry, that ID doesn't exist." + end + end + + def get_value + value = gets.chomp + until value =~ (/\d/) + puts "Invalid. Please enter a number." + value = get_input + end + return value.to_i + end + + def get_room + print "\n\tRoom Number: " + room_number = get_input + until room_number =~ (/^([1-9]|1[0-9]|20|[Nn])$/) + puts "Invalid Room Number." + print "Room Number: " + room_number = get_input + end + + if room_number == "N" || room_number == "n" + return nil + end + + hotel.rooms.each do |room| + if room.number == room_number.to_i + return room + end + end + raise StandardError.new "No room found" + end + + def show_table_of_rooms(rooms_arry) + table = Terminal::Table.new do |t| + t << ["Room", "Cost per night"] + rooms_arry.each do |room| + t << :separator + t << [room.number, room.cost_per_night] + end + end + puts table + return table + end + + def show_table_of_reservations(reservations) + table = Terminal::Table.new do |t| + t << ["Guest Name", "Check_in", "Check_out", "Room", "Total Cost", "Block ID" ] + reservations.each do |res| + res.block_id ? block_id = res.block_id : block_id = nil + + t << :separator + t << [res.guest, res.check_in, res.check_out, res.room.number, Pricing.calc_cost(res), block_id] + end + end + puts table + return table + end + end +end # end of hotel module + + +# +interface = Hotel::Cli.new +interface.begin diff --git a/lib/date_range.rb b/lib/date_range.rb new file mode 100644 index 000000000..994aaab46 --- /dev/null +++ b/lib/date_range.rb @@ -0,0 +1,45 @@ + +module Hotel + class DateRange + + attr_reader :total_nights, :check_in, :check_out + + def initialize(check_in, check_out) + @check_in = check_valid_date(check_in) + @check_out = check_valid_date(check_out) + @total_nights = calculate_length + end + + def include_date?(date) + date.between?(@check_in, @check_out - 1) + end + + def calculate_length + if @check_in >= @check_out || @check_in < Date.today + raise ArgumentError.new "invalid dates" + else + length = @check_out - @check_in + return length.to_i + end + end + + def check_valid_date(date) + if date.is_a? (Date) + return date + else + raise ArgumentError.new "argument must be a Date. Current argument is #{date.class}" + end + end + + def overlap(other_res) + overlap = (@check_in...@check_out).to_a & (other_res.check_in...other_res.check_out).to_a + return overlap + end + + + + + + + end #end DateRange +end #end Hotel diff --git a/lib/hotel.rb b/lib/hotel.rb new file mode 100644 index 000000000..af16a1525 --- /dev/null +++ b/lib/hotel.rb @@ -0,0 +1,172 @@ +require_relative 'reservations' +require_relative 'rooms' +require_relative 'block' +require_relative 'date_range' + +require 'pry' + +module Hotel + class Hotel + + attr_reader :rooms, :reservations, :blocks + + def initialize + @rooms = [] + create_rooms + @reservations = [] + @blocks = [] + end + + def make_reservation(guest, check_in, check_out, room_requested = nil) + room = assign_room(check_in, check_out, room_requested) + reservations << Reservation.new(guest, check_in, check_out, room, false) + end + + def make_reservation_from_block(guest, block_id) + block = find_block_by_id(block_id) + + room = get_available_rooms_from_block(block)[0] + + reservations << Reservation.new(guest, block.check_in, block.check_out, room, block_id) + end + + def make_block(check_in, check_out, num_rooms) + id = assign_id + + block = Block.new(check_in, check_out, id) + + blocks << block + + if num_rooms.between?(1, 5) == false + raise StandardError.new "only possible to book 5 rooms in a block" + end + + num_rooms.times do + room = assign_room(check_in, check_out) + block.add_room(room) + end + + return block + end + + #THERE HAS TO BE A BETTER WAY TO DO THIS. + def assign_room(check_in, check_out, room_requested = nil) + available_rooms = get_available_rooms(check_in, check_out) + + raise StandardError.new "no more rooms available for that date" if available_rooms.empty? + + if room_requested.is_a? (Integer) + available_rooms.each do |empty_room| + if empty_room.number == room_requested + return empty_room + end + end + raise StandardError.new "This room is unavailable" + else + room = available_rooms[0] + return room + end + end + + def get_res_by_date(date) + res_by_date = [] + @reservations.each do |res| + if res.include_date?(date) + res_by_date << res + end + end + return res_by_date + end + + def get_available_rooms(check_in, check_out) + available_rooms = @rooms.clone + + res_date = DateRange.new(check_in, check_out) + + @reservations.each do |res| + unless res_date.overlap(res).empty? + available_rooms.delete(res.room) + end + end + + @blocks.each do |block| + unless res_date.overlap(block).empty? + block.rooms_in_block.each do |room| + available_rooms.delete(room) + end + end + end + + # @reservations.each do |res| + # overlap = (res.check_in...res.check_out).to_a & (date_begin...date_end).to_a + # + # if overlap[0] != nil + # available_rooms.delete(res.room) + # end + # end + # + # @blocks.each do |block| + # overlap = (block.check_in...block.check_out).to_a & (date_begin...date_end).to_a + # + # if overlap[0] != nil + # block.rooms_in_block.each do |room| + # available_rooms.delete(room) + # end + # end + # end + + return available_rooms + end + + def get_available_rooms_from_block(block) + + available_rooms = block.rooms_in_block + + reservations.each do |res| + if res.block_id == block.id + available_rooms.delete(res.room) + end + end + + return available_rooms + end + + private + + def find_block_by_id(block_id) + blocks.each do |block| + if block.id == block_id + return block + end + end + raise StandardError.new "this id doesn't exit" + end + + # def find_room_by_number(room_number) + # rooms.each do |room| + # if room.number == room_number + # return room + # end + # end + # raise StandardError.new "this room doesn't exist" + # end + + def assign_id + id = 111111 + + blocks.each do |block| + while block.id == id + id = rand(899999) + 100000 + end + end + return id + end + + def create_rooms + (1..20).each do |num| + @rooms << Room.new(num) + end + end + + end #end of class +end #end of module diff --git a/lib/pricing.rb b/lib/pricing.rb new file mode 100644 index 000000000..3394fab51 --- /dev/null +++ b/lib/pricing.rb @@ -0,0 +1,17 @@ +module Hotel + class Pricing + + def self.calc_cost(reservation) + total_nights = reservation.total_nights + + cost = (total_nights * 200.0) + + if reservation.block_id + cost -= cost * 0.1 + end + + return cost + end + + end +end diff --git a/lib/reservations.rb b/lib/reservations.rb new file mode 100644 index 000000000..5210f76fc --- /dev/null +++ b/lib/reservations.rb @@ -0,0 +1,53 @@ +require_relative 'date_range.rb' +require_relative 'rooms.rb' + +module Hotel + class Reservation < DateRange + + attr_reader :total_nights, :check_in, :check_out, :room, :block_id, :guest + + def initialize(guest, check_in, check_out, room, block_id = nil) + super(check_in, check_out) + @guest = guest + @room = room + @block_id = block_id + end + + # Reservations that are checking out on that date will return false since they are not staying that night at the hotel + + #CLASS INCLUDED IN DATE_RANGE + # def include_date?(date) + # date.between?(@check_in, @check_out - 1) + # end + + private + + #CLASSES BELOW NOW COVERED UNDER DATE_RANGE CLASS + + # def check_in_date(check_in) + # if check_in.is_a? (Date) + # @check_in = check_in + # else + # raise ArgumentError.new "check_in must be a Date" + # end + # end + # + # def check_out_date(check_out) + # if check_out.is_a? (Date) + # @check_out = check_out + # else + # raise ArgumentError.new "check_out must be a date" + # end + # end + + # def calculate_res_length + # if @check_in >= @check_out || @check_in < Date.today + # raise ArgumentError.new "invalid dates" + # else + # length = @check_out - @check_in + # return length.to_i + # end + # end + + end #end of Reservation class +end #end of Hotel module diff --git a/lib/rooms.rb b/lib/rooms.rb new file mode 100644 index 000000000..04b0e92af --- /dev/null +++ b/lib/rooms.rb @@ -0,0 +1,23 @@ +module Hotel + class Room + attr_reader :number + attr_accessor :cost_per_night + + def initialize(number) + @number = check_room_number(number) + @cost_per_night = 200 + end + + private + + # Raises ArgumentError if room number supplied is already taken by a room or if number supplied is not an Integer. Otherwise, sets the room number. + def check_room_number(number) + if number.is_a?(Integer) == false + raise ArgumentError.new "room number must be an integer" + else + @number = number + end + end + + end # end of Rooms class +end # end of Hotel module diff --git a/specs/block_spec.rb b/specs/block_spec.rb new file mode 100644 index 000000000..b148781f4 --- /dev/null +++ b/specs/block_spec.rb @@ -0,0 +1,56 @@ +require_relative 'spec_helper' +require 'pry' + +describe Hotel::Block do + describe "a block instance can be created" do + it "creates an instance of a block" do + Hotel::Block.new(Date.new(2018, 5, 12), Date.new(2018, 5, 18), 111123).must_be_instance_of Hotel::Block + + end + + it "initializes with an id number between 100000 and 999999" do + hotel = Hotel::Hotel.new + check_in = Date.new(2018, 1, 1) + 100.times do + hotel.make_block(check_in, check_in + 5, rand(1..5) ) + check_in += 3 + end + + ids = [] + + hotel.blocks.each do |block| + ids << block.id + end + + ids.each do |id| + id.between?(100000, 999999).must_equal true + end + + ids.uniq! + ids.length.must_equal 100 + end + end + + describe "attr_readers" do + before do + @check_in = Date.new(2018, 1, 12) + @check_out = Date.new(2018, 1, 14) + @block = Hotel::Block.new(@check_in, @check_out, 111123) + + @block.add_room(Hotel::Room.new(5)) + @block.add_room(Hotel::Room.new(13)) + end + + it "can read the check_in date, check_out date, id, rooms_in_block, and the discount" do + @block.check_in.must_equal @check_in + @block.check_out.must_equal @check_out + @block.id.must_equal 111123 + @block.rooms_in_block.length.must_equal 2 + + @block.rooms_in_block.each do |room| + room.must_be_instance_of Hotel::Room + end + end + end + +end diff --git a/specs/cli-spec.rb b/specs/cli-spec.rb new file mode 100644 index 000000000..8653a5e29 --- /dev/null +++ b/specs/cli-spec.rb @@ -0,0 +1,118 @@ +require_relative 'spec_helper' + +describe "Command Line Interface" do + describe "creating and beginning" do + before do + @interface = Hotel::Cli.new + end + + it "can create an instance" do + @interface.must_be_instance_of Hotel::Cli + end + + it "initializes with a hotel" do + @interface.hotel.must_be_instance_of Hotel::Hotel + end + end + + describe "display_user_menu" do + it "displays the user menu" do + interface = Hotel::Cli.new + interface.must_respond_to :display_user_menu + end + end + + describe "get menu option" do + it "accepts A as a user option" do + class Hotel::Cli + def get_input + return "A" + end + end + + interface = Hotel::Cli.new + interface.get_menu_option.must_equal "A" + end + it "accepts i as a user option and returns I" do + class Hotel::Cli + def get_input + return "i" + end + end + + interface = Hotel::Cli.new + interface.get_menu_option.must_equal "I" + end + it "accepts x as a user option and returns X" do + class Hotel::Cli + def get_input + return "x" + end + end + + interface = Hotel::Cli.new + interface.get_menu_option.must_equal "X" + end + end + + + describe "reserve room" do + before do + # @interface = Hotel::Cli.new + # class Hotel::Cli + # def get_date_for + # Date.new(2018, 5, 5) + # end + # def check_out + # Date.new(2018, 5, 8) + # end + # def room + # return 18 + # end + # def get_input + # return "Shaunna" + # end + # end + end + it "increases the reservation by one" do + # before = @interface.hotel.reservations.clone.length + # + # @interface.reserve_room + # + # after = @interface.hotel.reservations.length + # + # (before + 1).must_equal after + + + end + end + + + + + + # Doesn't properly test for content of table or that table shows up in the terminal + describe "show_tables" do + before do + @interface = Hotel::Cli.new + + check_in = Date.new(2018, 7, 6) + check_out = Date.new(2018, 7, 8) + + (1..10).each do |num| + @interface.hotel.make_reservation("guest", check_in, check_out, num) + end + end + + it "returns the table of rooms" do + table = @interface.show_table_of_rooms(@interface.hotel.rooms) + table.rows.length.must_equal 21 + end + + it "returns the table of reservations" do + table = @interface.show_table_of_reservations(@interface.hotel.reservations) + table.rows.length.must_equal 11 + end + end + +end diff --git a/specs/daterange_spec.rb b/specs/daterange_spec.rb new file mode 100644 index 000000000..6cdc593ed --- /dev/null +++ b/specs/daterange_spec.rb @@ -0,0 +1,55 @@ +require_relative 'spec_helper' + +require 'pry' + +describe Hotel::DateRange do + describe "an instance of DateRange can be created" do + before do + @check_in = Date.new(2018, 10, 5) + @check_out = Date.new(2018, 10, 7) + @date_range = Hotel::DateRange.new(@check_in, @check_out) + end + it "can be created" do + @date_range.must_be_instance_of Hotel::DateRange + end + + it "has a check-in, check_out" do + @date_range.check_in.must_equal @check_in + @date_range.check_out.must_equal @check_out + end + + it "accurately calculates the total nights included" do + @date_range.total_nights.must_equal 2 + end + + it "returns and error if the check in date is after the check in date or if the check in date is before today" do + proc{Hotel::DateRange.new(Date.today - 1, Date.new(2017, 12, 25))}.must_raise ArgumentError + + proc{Hotel::DateRange.new(Date.new(2017, 12, 26), Date.new(2017, 12, 25))}.must_raise ArgumentError + end + end + + describe "overlap" do + before do + @dr1 = Hotel::DateRange.new(Date.new(2018, 1, 1),(Date.new(2018, 1, 20))) + + @dr2 = Hotel::DateRange.new(Date.new(2018, 1, 5),(Date.new(2018, 1, 15))) + + @dr3 = Hotel::DateRange.new(Date.new(2018, 1, 17),(Date.new(2018, 1, 20))) + end + + it "returns an array" do + @dr1.overlap(@dr2).must_be_kind_of Array + end + + it "returns an empty array if no dates overlap" do + @dr2.overlap(@dr3).empty?.must_equal true + end + + it "returns a non-empty array if some dates overlap" do + @dr1.overlap(@dr3).empty?.must_equal false + end + + end + +end diff --git a/specs/hotel_spec.rb b/specs/hotel_spec.rb new file mode 100644 index 000000000..3a70a7dd9 --- /dev/null +++ b/specs/hotel_spec.rb @@ -0,0 +1,356 @@ +require_relative 'spec_helper' + +describe Hotel::Hotel do + describe "a hotel instance can be created" do + it "can be created" do + marriott = Hotel::Hotel.new + marriott.must_be_instance_of Hotel::Hotel + end + + it "creates 20 rooms upon initializing stored in an Array" do + marriott = Hotel::Hotel.new + marriott.rooms.length.must_equal 20 + marriott.rooms.must_be_kind_of Array + end + + it "creates 20 rooms that are room objects" do + marriott = Hotel::Hotel.new + marriott.rooms.each do |room| + room.must_be_instance_of Hotel::Room + end + end + + it "creates 20 hotels that can identify their room numbers" do + marriott = Hotel::Hotel.new + i = 1 + marriott.rooms.each do |room| + room.number.must_equal i + i += 1 + end + end + + it "initializes an empty Array to store blocks of rooms" do + fairmont = Hotel::Hotel.new + fairmont.blocks.must_be_kind_of Array + fairmont.blocks.length.must_equal 0 + end + end #end of initailize Hotel::Hotel tests + + describe "can make and retrieve reservations" do + before do + @hilton = Hotel::Hotel.new + + @hilton.make_reservation('Shaunna', Date.new(2018, 5, 7), Date.new(2018, 5, 10)) + @hilton.make_reservation('Shaunna', Date.new(2018, 4, 26), Date.new(2018, 4, 28)) + @hilton.make_reservation('Nathan', Date.new(2019, 12, 4), Date.new(2019, 12, 24)) + end + + it "can make a reservation from inside the hotel class" do + @hilton.reservations.each do |res| + res.must_be_instance_of Hotel::Reservation + end + end + + it "can retrieve an Array of all the reservations made" do + @hilton.reservations.length.must_equal 3 + @hilton.reservations.must_be_kind_of Array + end + + it "returns an Array of length zero if no reservations have been made" do + davenport = Hotel::Hotel.new + davenport.reservations.length.must_equal 0 + davenport.reservations.must_be_kind_of Array + end + + it "can make a reservation with room object by inputing the room number" do + hotel = Hotel::Hotel.new + hotel.make_reservation("guest", Date.new(2018, 6, 2), Date.new(2018, 6, 15), 13) + end + + it "raises a StandardError when trying to make a reservation for a room that is already reserved" do + victoria = Hotel::Hotel.new + victoria.make_reservation(" + guest", Date.new(2018, 6, 2), Date.new(2018, 6, 15), 11) + + proc{victoria.make_reservation("guest", Date.new(2018, 6, 6), Date.new(2018, 6, 20), 11)}.must_raise StandardError + end + + it "raises a StandardError when there are no available rooms left " do + sheraton = Hotel::Hotel.new + + check_in = Date.new(2018, 7, 12) + check_out = Date.new(2018, 7, 15) + + 20.times do + sheraton.make_reservation("guest", check_in, check_out) + end + + proc{sheraton.make_reservation("guest", check_in, check_out)}.must_raise StandardError + end + + it "will not raise an exception when trying to make a reservation for a room that starts on the same day another reservation ends" do + korpela = Hotel::Hotel.new + korpela.make_reservation(" + guest", Date.new(2018, 6, 2), Date.new(2018, 6, 15), 11) + + korpela.make_reservation("guest", Date.new(2018, 6, 15), Date.new(2018, 6, 20), 11) + + korpela.reservations.length.must_equal 2 + end + end # end of make and retrieve reservations + + describe "get_available_rooms_from_block" do + before do + @hotel = Hotel::Hotel.new + @check_in = Date.new(2018, 9, 7) + @check_out = Date.new(2018, 9, 13) + @hotel.make_block(@check_in, @check_out, 5) + @block = @hotel.blocks[0] + @id = @hotel.blocks[0].id + @hotel.make_reservation_from_block("guest", @id) + + end + + it "returns an Array of Rooms" do + rooms_in_block = @hotel.get_available_rooms_from_block(@block) + + rooms_in_block.must_be_kind_of Array + + rooms_in_block.each do |room| + room.must_be_instance_of Hotel::Room + end + + end + + it "only returns available Rooms" do + rooms_avail_before = @hotel.get_available_rooms_from_block(@block).clone + + 2.times do + @hotel.make_reservation_from_block("guest", @id) + end + + rooms_avail_after = @hotel.get_available_rooms_from_block(@block) + + rooms_avail_after.length.must_equal (rooms_avail_before.length - 2) + + rooms_left_in_block = @block.rooms_in_block + @hotel.reservations.each do |res| + rooms_left_in_block.delete(res.room) + end + + rooms_avail_after.must_equal rooms_left_in_block + + end + + it "returns an empty Array if no rooms are left" do + hotel = Hotel::Hotel.new + + check_in = Date.new(2018, 9, 15) + check_out = Date.new(2018, 9, 16) + hotel.make_block(check_in, check_out, 3) + block = hotel.blocks[0] + + 3.times do + hotel.make_reservation_from_block("guest", block.id) + end + + hotel.get_available_rooms_from_block(hotel.blocks[0]).length.must_equal 0 + + end + + end + + describe "make_reservation_from_block" do + before do + @hotel = Hotel::Hotel.new + @check_in = Date.new(2018, 9, 7) + @check_out = Date.new(2018, 9, 13) + @hotel.make_block(@check_in, @check_out, 5) + @id = @hotel.blocks[0].id + @hotel.make_reservation_from_block("guest", @id) + end + + it "can make a reservation from a block" do + @hotel.reservations.length.must_equal 1 + @hotel.reservations[0].must_be_instance_of Hotel::Reservation + end + + it "will make a reservation with a room that is reserved for the block" do + block_rooms = [] + @hotel.blocks[0].rooms_in_block.each do |room| + block_rooms << room + end + + block_rooms.include?(@hotel.reservations[0].room).must_equal true + end + + it "will make a reservation with the same date range as the block" do + @hotel.reservations[0].check_in.must_equal @check_in + + @hotel.reservations[0].check_out.must_equal @check_out + end + end + + describe "can make a block of rooms" do + it "can make a block that is passed into an Array" do + + motel8 = Hotel::Hotel.new + + motel8.make_block(Date.new(2018, 5, 5), Date.new(2018, 5, 8), 4) + + motel8.blocks.must_be_kind_of Array + + motel8.blocks.last.must_be_instance_of Hotel::Block + end + + it "will generate a unique id for the block booking" do + + ############ HOW??? ############ + + + end + + it "won't assign rooms to the block that aren't available during that date range" do + holidayinn = Hotel::Hotel.new + + check_in = Date.new(2018, 7, 6) + check_out = Date.new(2018, 7, 8) + + (1..15).each do |num| + holidayinn.make_reservation("guest", check_in, check_out, num) + end + + holidayinn.make_block(check_in, check_out, 5) + + holidayinn.blocks.each do |block| + block.rooms_in_block.each do |room| + room.number.between?(1, 15).must_equal false + end + end + end + + it "will only allow a block to reserve a maximum of five rooms" do + hotel = Hotel::Hotel.new + + check_in = Date.new(2018, 7, 6) + check_out = Date.new(2018, 7, 8) + + proc{hotel.make_block(check_in, check_out, 6)}.must_raise StandardError + + proc{hotel.make_block(check_in, check_out, 0)}.must_raise StandardError + end + + it "will make the rooms assigned to the block unavailable for reservation to the general public" do + + parador = Hotel::Hotel.new + + check_in = Date.new(2018, 7, 6) + check_out = Date.new(2018, 7, 8) + + parador.make_block(check_in, check_out, 4) + + room_numbers = [] + + parador.blocks.each do |block| + block.rooms_in_block.each do |room| + room_numbers << room.number + end + end + + proc{parador.make_reservation("guest", check_in, check_out, room_numbers[0])}.must_raise StandardError + + end + end + + describe "get_res_by_date" do + before do + check_in_day = [2, 2, 6, 4, 8, 6, 12, 23, 17, 2, 3, 5, 22, 29, 10, 10, 1] + check_out_day =[3, 10, 7, 8, 11, 7, 14, 30, 19, 4, 4, 6, 25, 30, 12, 20, 15] + + i = 0 + @carlisle = Hotel::Hotel.new + check_in_day.length.times do + @carlisle.make_reservation("guest", Date.new(2018, 4, check_in_day[i]), Date.new(2018, 4, check_out_day[i])) + i += 1 + end + end + + it "returns an Array of Reservations" do + @carlisle.get_res_by_date(Date.new(2018,4,10)).must_be_kind_of Array + @carlisle.get_res_by_date(Date.new(2018,4,8)).each do |res| + res.must_be_kind_of Hotel::Reservation + end + end + + it "returns a list of unique Reservations that are occupying rooms that night" do + reservations_4th = @carlisle.get_res_by_date(Date.new(2018,4,4)) + reservations_10th = @carlisle.get_res_by_date(Date.new(2018,4,10)) + reservations_30th = @carlisle.get_res_by_date(Date.new(2018,4,30)) + reservations_may = @carlisle.get_res_by_date(Date.new(2018,5,10)) + + reservations_4th.length.must_equal 3 + reservations_10th.length.must_equal 4 + reservations_may.length.must_equal 0 + reservations_30th.length.must_equal 0 + end + + end # end get_res_by_date + + # describe "find block by id" do + # it "returns an error if id doesn't exist" do + # hotel = Hotel::Hotel.new + # + # hotel.make_block(Date.new(2017, 12, 12), Date.new(2017, 12, 20), 4) + # + # proc{hotel.find_block_by_id(123456)}.must_raise StandardError + # end + # + # it "returns the block if the id exists" do + # hotel = Hotel::Hotel.new + # + # block = hotel.make_block(Date.new(2017, 12, 12), Date.new(2017, 12, 20), 4) + # + # hotel.make_block(Date.new(2018, 1, 12), Date.new(2017, 1, 20), 4) + # + # hotel.make_block(Date.new(2018, 12, 12), Date.new(2017, 12, 20), 4) + # + # hotel.find_block_by_id(111111).must_equal block + # end + # end + ########### HOW TO TEST THIS METHOD?? ######### + ## CURRENTLY TESTING FOR LENGTH ## + describe "get_available_rooms method" do + + it "returns an Array of Rooms" do + hotel = Hotel::Hotel.new + check_in = Date.new(2018, 4, 12) + check_out = Date.new(2018, 4, 25) + + hotel.get_available_rooms(check_in, check_out).must_be_kind_of Array + end + + it "returns 17 rooms when there are three room conflicts" do + hotel = Hotel::Hotel.new + hotel.make_reservation("guest", Date.new(2018, 9, 10), Date.new(2018, 9, 17)) + hotel.make_reservation("guest", Date.new(2018, 9, 15), Date.new(2018, 9, 23)) + hotel.make_reservation("guest", Date.new(2018, 9, 16), Date.new(2018, 9, 28)) + hotel.make_reservation("guest", Date.new(2018, 4, 16), Date.new(2018, 4, 18)) + + hotel.get_available_rooms(Date.new(2018, 9, 14), Date.new(2018, 9, 20)).length.must_equal 17 + end + + end + +end + + + + + + + + + + + +#This is a comment to stop the blinking diff --git a/specs/pricing_spec.rb b/specs/pricing_spec.rb new file mode 100644 index 000000000..68d7c65fe --- /dev/null +++ b/specs/pricing_spec.rb @@ -0,0 +1,37 @@ +require_relative 'spec_helper' + +describe Hotel::Pricing do + describe "method self.calc_cost(reservation)" do + it "returns a float value" do + reservation = Hotel::Reservation.new("guest", Date.new(2018, 11, 12), Date.new(2018, 11, 13), Hotel::Room.new(2)) + Hotel::Pricing.calc_cost(reservation).must_be_kind_of Float + end + + it "calculates the cost of one night to be 200" do + reservation = Hotel::Reservation.new("guest", Date.new(2018, 11, 12), Date.new(2018, 11, 13), Hotel::Room.new(5)) + + Hotel::Pricing.calc_cost(reservation).must_equal 200 + end + + it "accurately calculates the cost of multiple night reservations" do + reservation = Hotel::Reservation.new("guest", Date.new(2018, 11, 12), Date.new(2018, 11, 14), Hotel::Room.new(18)) + + res2 = Hotel::Reservation.new("guest", Date.new(2018, 11, 12), Date.new(2018, 11, 20), Hotel::Room.new(8) ) + + + Hotel::Pricing.calc_cost(reservation).must_equal 400 + + Hotel::Pricing.calc_cost(res2).must_equal 1600 + end + + it "returns a discounted value if room is reserved in a block" do + reservation = Hotel::Reservation.new("guest", Date.new(2018, 11, 12), Date.new(2018, 11, 14), Hotel::Room.new(18), 123456) + + Hotel::Pricing.calc_cost(reservation).must_equal 360 + end + + + end + + +end diff --git a/specs/reservations_spec.rb b/specs/reservations_spec.rb new file mode 100644 index 000000000..6e5ce3257 --- /dev/null +++ b/specs/reservations_spec.rb @@ -0,0 +1,110 @@ +require_relative 'spec_helper' + +describe Hotel::Reservation do + before do + @guest = "Ada Lovelace" + end + + describe "you can create a reservation instance" do + it "can be created" do + check_in = Date.new(2017, 11, 14) + check_out = Date.new(2017, 11, 17) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(1)) + reservation.must_be_instance_of Hotel::Reservation + end + + it "raises an ArgumentError for invalid check_in and check_out arguments" do + check_in = [2017, 11, 14] + check_out = [2017, 11, 16] + proc{Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(5))}.must_raise ArgumentError + proc{Hotel::Reservation.new(@guest, Date.new(2017, 11, 14), check_out, Hotel::Room.new(5))}.must_raise ArgumentError + end + + it "accurately calculates the length of a stay of 1 night" do + check_in = Date.new(2017, 11, 14) + check_out = Date.new(2017, 11, 17) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(3)) + reservation.total_nights.must_equal 3 + end + + it "accurately calculates the length of a stay of 1 year" do + check_in = Date.new(2017, 11, 14) + check_out = Date.new(2018, 11, 14) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(9)) + reservation.total_nights.must_equal 365 + end + + it "accurately calculates the length of stay of 7 days" do + reservation = Hotel::Reservation.new("guest", Date.new(2018, 11, 12), Date.new(2018, 11, 19), Hotel::Room.new(12)) + + reservation.total_nights.must_equal 7 + end + + it "raises an ArgumentError if the check in or check out date is too early" do + proc{Hotel::Reservation.new(@guest, Date.today - 1, Date.new(2017, 12, 25), Hotel::Room.new(5))}.must_raise ArgumentError + + proc{Hotel::Reservation.new(@guest, Date.new(2017, 12, 26), Date.new(2017, 12, 25), Hotel::Room.new(5))}.must_raise ArgumentError + end + end + + describe "Reader Methods" do + before do + check_in = Date.new(2018, 4, 1) + check_out = Date.new(2018, 4, 22) + @reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(17)) + end + + it "can retrieve the total_nights" do + @reservation.total_nights.must_equal 21 + end + + it "can retrieve the check_in date" do + @reservation.check_in.must_equal Date.new(2018, 4, 1) + end + + it "can retrieve the check_out date" do + @reservation.check_out.must_equal Date.new(2018, 4, 22) + end + + it "can retreive the room and room number" do + @reservation.room.must_be_kind_of Hotel::Room + @reservation.room.number.must_equal 17 + end + + # it "can retrieve the room" do + # + # end + end # end of reader Methods + + describe "method include_date?" do + before do + @reservation = Hotel::Reservation.new("guest", Date.new(2018, 2, 15), Date.new(2018, 2, 23), Hotel::Room.new(15)) + end + + it "returns true if the reservation includes that date" do + @reservation = Hotel::Reservation.new("guest", Date.new(2018, 2, 15), Date.new(2018, 2, 23), Hotel::Room.new(9)) + + @reservation.include_date?(Date.new(2018, 2, 20)).must_equal true + end + + it "returns false if the reservation doesn't include that date" do + @reservation.include_date?(Date.new(2018, 2, 25)).must_equal false + @reservation.include_date?(Date.new(2018, 2, 2)).must_equal false + end + + it "returns true if the check_in date is that date" do + @reservation.include_date?(Date.new(2018, 2, 15)).must_equal true + end + + it "returns false if the check_out date is that date" do + @reservation.include_date?(Date.new(2018, 2, 23)).must_equal false + end + + end #end of method include_date? + + + + + + +end diff --git a/specs/rooms_spec.rb b/specs/rooms_spec.rb new file mode 100644 index 000000000..b0e110fe9 --- /dev/null +++ b/specs/rooms_spec.rb @@ -0,0 +1,32 @@ +require_relative 'spec_helper' + +describe Hotel::Room do + describe "You can create a Room instance" do + it "Can be created" do + room = Hotel::Room.new(1) + room.must_be_instance_of Hotel::Room + end + it "raises an ArgumentError for invalid parameters" do + proc{Hotel::Room.new("A15")}.must_raise ArgumentError + end + it "initializes wiht a cost of 200 per night" do + room = Hotel::Room.new(5) + room.cost_per_night.must_equal 200 + end + end + + describe "attr accessors" do + it "can retrieve the room number using .number" do + room = Hotel::Room.new(5) + room.number.must_equal 5 + end + it "can retrieve and change the cost of a room per night" do + room = Hotel::Room.new(3) + room.cost_per_night.must_equal 200 + + room.cost_per_night = 150 + + room.cost_per_night.must_equal 150 + end + end +end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 000000000..a35f15150 --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,18 @@ +require 'simplecov' +SimpleCov.start + +require 'minitest' +require 'minitest/autorun' +require 'minitest/reporters' +require 'minitest/pride' +require 'minitest/skip_dsl' + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +require_relative '../lib/hotel.rb' +require_relative '../lib/reservations.rb' +require_relative '../lib/rooms.rb' +require_relative '../lib/pricing.rb' +require_relative '../lib/block.rb' +require_relative '../lib/date_range.rb' +# require_relative '../lib/cli.rb'