diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..d171b97be Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index e76f80e9e..0036110d9 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ This project is both a culmination of our Intro to Ruby unit and our first stage **It is expected that you will not be able to complete all requirements.** The three waves are organized by difficulty and relevance to the learning goals, and should be tackled in order. +### Hints + +We have included some [optional design scaffolding](https://github.com/AdaGold/hotel/blob/design-scaffolding/design-scaffolding-notes.md) for this project, to help you get started if you don't know where to start, or to provide inspiration if you're a little stuck. Any student should feel free to use this scaffolding in whatever way is most helpful to them. However, **we recommend that you spend at least 1 full day thinking about design before reaching for this scaffolding**, to get practice thinking about this type of problem independently. + ## Getting Started We will use the same project structure we used for the previous project. Library code (such as classes) should be in files in the `lib` folder, and tests should be in files in the `test` folder. @@ -99,7 +103,7 @@ Spend **no more than 1 hour** answering those questions and adjusting your proje ## Functional Requirements -### Wave 1: Tracking Reservations +### Wave One: Tracking Reservations In this wave, write the functionality for the system to track valid reservations, so that a user of the hotel system can make and find valid bookings for their hotel. @@ -141,7 +145,7 @@ Remember: Your job is to only build the classes that store information and handl -### Wave 2: Room Availability +### Wave Two: Room Availability #### User Stories @@ -154,7 +158,7 @@ Remember: Your job is to only build the classes that store information and handl - A reservation is allowed start on the same day that another reservation for the same room ends -### Wave 3: Hotel Blocks +### Wave Three: Hotel Blocks If you are not familiar with what a block of hotel rooms, here is a brief description: diff --git a/feedback.md b/feedback.md index 05dd3c165..e714c975d 100644 --- a/feedback.md +++ b/feedback.md @@ -1,104 +1,55 @@ # Hotel - - - - - - -## Section 1: Major Learning Goals - - - -| Criteria | yes/no, and optionally any details/lines of code to reference | -| --- | --- | -Practices SRP by having at least two separate classes with distinct responsibilities, and test files for these two classes | ✔️ -Overall, demonstrates understanding instance variables vs. local variables. (There aren't unnecessarily too many instance variables, when it should be a local variable) | ✔️ -For each test file, tests demonstrate an understanding of instantiating objects properly, and using Arrange-Act-Assert | ✔️ -Practices pseudocode and TDD, and reflected on it by filling out the reflection questions | ✔️ -Practices git with at least 15 small commits and meaningful commit messages | ✔️ - -## Section 2: Code Review and Testing Requirements - -| Criteria | yes/no, and optionally any details/lines of code to reference | -| --- | --- | -There is a class that represents a reservation, and a second class that holds/manages a collection of reservations through composition (instance variable) | ✔️ -The logic for checking if a reservation's date overlaps with another reservation's date is complex logic that is separated into method(s) (and potentially class(es)) | ✔️ -The logic for checking if a reservation's date overlaps with another reservation's date has unit tests | ✔️ -All of the given tests run and pass | ✔️ -A test coverage tool is installed and used, and shows 95% test coverage | ✔️ - -## Section 3: Feature Requirements - -| Feature Requirement: There is a method that... | yes/no | -| --- | --- | -gives back a list of rooms, and it's tested | ✔️ -creates a specific reservation for a room for a given date range, and it has nominal test cases | ✔️ -creates a specific reservation for a room for a given date range, and it tests an edge case, such as no available room, or invalid date range | ✔️ -gives back a list of reservations on a given date. Its tests include a test that makes several reservations for a given date | ✔️ -calculates the total price for a reservation | ✔️ -gives back a list of available rooms for a given date range, and it has nominal test cases | ✔️ -gives back a list of available rooms for a given date range, and it has edge test cases, such as no available room, or invalid date range | ✔️ -creates a block of rooms | ✔️ -reserves a room from a block | ✔️ +## What We're Looking For + + + +### Test Inspection + + + +Workflow | yes / yes but no test / no +--- | --- +**Wave 1** | +List rooms | +Reserve a room for a given date range | +Reserve a room (edge case) | +List reservations for a given date | +Calculate reservation price | +Invalid date range produces an error | +**Wave 2** | +View available rooms for a given date range | +Reserving a room that is not available produces an error | +**Wave 3** | +Create a block of rooms | +Check if a block has rooms | +Reserve a room from a block | + +### Code Review + +**Baseline** | Feedback +--- | --- +Used git regularly | +Answer comprehension questions | +At least 95% test coverage | +**Design** | +Each class is responsible for a single piece of the program | +Classes are loosely coupled | +**Fundamentals** | +Names variables, classes and modules appropriately | +Understanding of variable scope - local vs instance | +Can create complex logical structures utilizing variables | +Appropriately uses methods to break down tasks into smaller simpler tasks | +Appropriately uses iterators and `Enumerable` methods | +Appropriately writes and utilizes classes | +Appropriately utilizes modules as a namespace | +**Wrap Up** | +There is a refactors.txt file | +The file provides a roadmap to future changes | ## Overall Feedback -| Overall Feedback | Criteria | yes/no | -| --- | --- | --- | -| Green (Meets/Exceeds Standards) | 14+ total in all sections | -| Yellow (Approaches Standards) | 9-13 total in all sections | -| Red (Not at Standard) | 0-8 total in all sections, or assignment is breaking/doesn’t run with less than 5 minutes of debugging | - -### Additional Feedback - - - Great work overall! You've built your first project with minimal starting code. This represents an incredible milestone in your journey, and you should be proud of yourself! I am particularly impressed by the way that you... I do see some room for improvement around... - -## Code Style Bonus Awards - - - -Was the code particularly impressive in code style for any of these reasons (or more...?) - -| Quality | Yes? | -| --- | --- | -| Perfect Indentation | ✅ -| Elegant/Clever | ✅ -| Descriptive/Readable | ✅ -| Concise | ✅ -| Logical/Organized | ✅ diff --git a/lib/block.rb b/lib/block.rb new file mode 100644 index 000000000..0a96db962 --- /dev/null +++ b/lib/block.rb @@ -0,0 +1,54 @@ +require 'date' + +require_relative 'reservation' +require_relative 'room' + +module Hotel + class Block < Reservation + + attr_reader :rooms, :rate + + def initialize(rooms, stay_begin, stay_end, rate) + super(room, stay_begin, stay_end, rate) + + @rooms = rooms.to_h { |room| [room, false]} + @rate = rate + + claim_rooms + end + + def any_available? + available_rooms = @rooms.select { |room, bool| bool == false} + if available_rooms.length > 0 + return true + else + return false + end + end + + def reserve_room(first, last) + + if first != @stay_begin || last != @stay_end + raise ArgumentError.new "#{first} - #{last} is not a valid date range!" + end + + available_rooms = @rooms.reject { |room, bool| bool == true} + if available_rooms.length == 0 + raise ArgumentError.new("There are no rooms in the block available to book!") + else + @rooms[available_rooms.keys.first] = true + end + + end + + private + + def claim_rooms + @rooms.each do |room, bool| + reservation = Hotel::Reservation.new(room, @stay_begin, @stay_end, @cost) + room.reservations << reservation + end + end + + end +end \ No newline at end of file diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..06f5c5ae6 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,34 @@ +require 'date' + +require_relative 'reservation_manager' + +module Hotel + class Reservation + + attr_reader :room, :stay_begin, :stay_end, :cost + + def initialize(room, stay_begin, stay_end, cost = 200) + @room = room + @stay_begin = stay_begin + @stay_end = stay_end + @cost = (((stay_end - stay_begin).to_i / 24) - 1) * cost + + verify_date(@stay_begin, @stay_end) + end + + def overlapping?(first, last) + @stay_begin <= last && first <= @stay_end + end + + private + + def verify_date(stay_begin, stay_end) + if stay_begin - Date.today < 0 + raise ArgumentError.new("#{stay_begin} is before today. You cannot reserve a room for a past date.") + elsif stay_begin - stay_end > 0 + raise ArgumentError.new("#{stay_end} is before #{stay_end}. The end date must be after the begin date.") + end + end + + end +end \ No newline at end of file diff --git a/lib/reservation_manager.rb b/lib/reservation_manager.rb new file mode 100644 index 000000000..dcb3f6d77 --- /dev/null +++ b/lib/reservation_manager.rb @@ -0,0 +1,91 @@ +require 'date' + +require_relative 'block' + +# require_relative 'reservation' +# require_relative 'room' + +module Hotel + class ReservationManager + + attr_reader :rooms + + def initialize + @rooms = create_rooms + end + + def new_reservation(first, last) + available_rooms = @rooms.select { |room| room.available?(first, last)} + if available_rooms.length == 0 + raise ArgumentError.new "There are no available rooms to book!" + end + + reservation = Reservation.new(available_rooms[0], first, last) + available_rooms[0].reservations << reservation + end + + def new_block(rooms, block_begin, block_end, rate) + if rooms.length > 5 + raise ArgumentError.new("#{rooms.length} is more than the maximum number of allowed rooms for a block.") + end + + rooms.each do |room| + if !room.available?(block_begin, block_end) + raise ArgumentError.new("Room #{room.number} is not available for the dates #{block_begin} - #{block_end}.") + end + end + + block = Hotel::Block.new(rooms, block_begin, block_end, rate) + + end + + def reserve_block_room(block, stay_begin, stay_end) + block.reserve_room(stay_begin, stay_end) + end + + def all_reservations_by_room(num, first, last) + room = @rooms.select { |room| room.number == num} + + matching_reservations = room[0].reservations.select { |reservation| reservation.overlapping?(first, last)} + + return matching_reservations + end + + def all_reservations_by_date(first, last) + matching_reservations = [] + + @rooms.each do |room| + matches = room.reservations.select { |reservation| reservation.overlapping?(first, last)} + matches.each do |match| + matching_reservations << match + end + end + + return matching_reservations + end + + def rooms_list + list = "" + @rooms.each do |room| + list += "Room Number: #{room.number}\n" + end + return list + end + + def room_list_by_availability(first, last) + available_rooms = @rooms.select { |room| room.available?(first, last)} + + return available_rooms + end + + private + + def create_rooms + rooms = [] + 20.times do |i| + rooms << Hotel::Room.new(i + 1) + end + return rooms + end + end +end \ No newline at end of file diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..d5d5f886f --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,36 @@ +require 'date' + +require_relative 'reservation_manager' +require_relative 'reservation' + +module Hotel + class Room + + attr_reader :number, :reservations, :block_reservations, :is_block_claimed + + def initialize(number) + @number = number + @reservations = [] + end + + def available?(stay_begin, stay_end) + overlapping_reservations = self.reservations.select { |reservation| reservation.overlapping?(stay_begin, stay_end)} + + if overlapping_reservations.length > 0 + return false + else + return true + end + end + + def find_reservations(first, last) + matching_reservation = @reservations.select { |reservation| reservation.overlapping?(first, last)} + if matching_reservation.length > 0 + return matching_reservation[0] + else + return nil + end + end + + end +end \ No newline at end of file diff --git a/refactors.txt b/refactors.txt new file mode 100644 index 000000000..96ad90009 --- /dev/null +++ b/refactors.txt @@ -0,0 +1,3 @@ +Find better/more consistent naming conventions for my stay begin/stay end date objects. +More clearly defining the responsibility of the reservation manager class as well as it's relationship with the other classes. +Figuring out what weird thing is going on with my date subtraction during the calculation of stay cost. \ No newline at end of file diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/test/.DS_Store differ diff --git a/test/block_test.rb b/test/block_test.rb new file mode 100644 index 000000000..a52e719eb --- /dev/null +++ b/test/block_test.rb @@ -0,0 +1,42 @@ +require_relative 'test_helper' + +describe "block tests" do + before do + @stay_begin = Date.new(2020, 5, 20) + @stay_end = Date.new(2020, 5, 28) + @manager = Hotel::ReservationManager.new + @manager.new_reservation(@stay_begin, @stay_end) + @available_rooms = @manager.room_list_by_availability(Date.new(2021, 1, 10), Date.new(2021, 1, 20)) + @block = @manager.new_block(@available_rooms[0..4], Date.new(2021, 1, 10), Date.new(2021, 1, 20), 100) + end + + describe "initialize" do + it "can be created" do + expect(@block).must_be_instance_of Hotel::Block + end + + it "has it's own provided rate" do + expect(@block.rate).must_equal 100 + end + end + + describe "room reservation" do + it "a room in the block can be reserved with the block and given dates" do + room_objects = @block.rooms.keys + @manager.reserve_block_room(@block, Date.new(2021, 1, 10), Date.new(2021, 1, 20)) + expect(@block.rooms[room_objects[0]]).must_equal true + end + + it "will raise an argument error if there are no rooms left to book in the block" do + expect{ + 6.times do + @manager.reserve_block_room(@block, Date.new(2021, 1, 10), Date.new(2021, 1, 20)) + end + }.must_raise ArgumentError + end + + it "will raise an argument error if you try to book a room without the proper block dates" do + expect{@manager.reserve_block_room(@block, Date.new(2020, 5, 13), Date.new(2020, 5, 30))}.must_raise ArgumentError + end + end +end \ No newline at end of file diff --git a/test/reservation_manager_test.rb b/test/reservation_manager_test.rb new file mode 100644 index 000000000..6a421262b --- /dev/null +++ b/test/reservation_manager_test.rb @@ -0,0 +1,119 @@ +require_relative 'test_helper' + +describe "reservation manager test" do + before do + @stay_begin = Date.new(2020, 5, 20) + @stay_end = Date.new(2020, 5, 28) + @manager = Hotel::ReservationManager.new + @manager.new_reservation(@stay_begin, @stay_end) + end + + describe "initialize" do + + it "can be instantiated" do + expect(@manager).must_be_instance_of Hotel::ReservationManager + end + + it "will create an array of 20 rooms" do + expect(@manager.rooms.length).must_equal 20 + end + + it "will populate the array with room objects" do + expect(@manager.rooms[2]).must_be_instance_of Hotel::Room + end + + end + + describe "create reservation" do + + it "will create an instance of the reservation class" do + expect(@manager.rooms[0].reservations[-1]).must_be_instance_of Hotel::Reservation + end + + it "will raise an argument error if the reservation starts before the current day" do + start = Date.new(1990, 1, 1) + expect{@manager.new_reservation(start, @stay_end)}.must_raise ArgumentError + end + + it "will raise an argument error if the end date is before the start date" do + start = Date.new(2020, 6, 20) + expect{@manager.new_reservation(start, @stay_end)}.must_raise ArgumentError + end + + it "will select an available room for the reservation dates" do + 19.times do + @manager.new_reservation(@stay_begin, @stay_end) + end + + expect(@manager.rooms[19].number).must_equal 20 + end + + it "will raise an argument error if there are no available rooms to book for the given dates" do + expect{ + 21.times do + @manager.new_reservation(@stay_begin, @stay_end) + end + }.must_raise ArgumentError + end + + end + + describe "room information" do + + it "will return a string containing a list of all rooms" do + expect(@manager.rooms_list).must_be_instance_of String + end + + it "will have a list of 20 lines" do + list = @manager.rooms_list + expect(list.split("\n").length).must_equal 20 + end + + it "will return a collection of all rooms available for a given date range" do + @manager.new_reservation(@stay_begin, @stay_end) + expect(@manager.room_list_by_availability(@stay_begin, @stay_end).length).must_equal 18 + end + + end + + describe "find reservations" do + + it "will provide a collection of reservations matching a given date range and a room number" do + 2.times do + @manager.new_reservation(Date.new(2020, 7, 22), Date.new(2020, 7, 29)) + end + expect(@manager.all_reservations_by_room(1, Date.new(2020, 5, 26), Date.new(2020, 9, 14)).length).must_equal 2 + end + + it "will provide a collection of reservations matching a given date range" do + @manager.new_reservation(@stay_begin, @stay_end) + expect(@manager.all_reservations_by_date(@stay_begin, @stay_end).length).must_equal 2 + end + + end + + describe "create block reservation" do + before do + @available_rooms = @manager.room_list_by_availability(Date.new(2021, 1, 10), Date.new(2021, 1, 20)) + @block = @manager.new_block(@available_rooms[0..4], Date.new(2021, 1, 10), Date.new(2021, 1, 20), 100) + end + + it "will create a block hotel reservation for the given dates" do + expect(@block).must_be_instance_of Hotel::Block + end + + it "will select the correct number of available rooms" do + expect(@block.rooms.length).must_equal 5 + end + + it "will adjust the price of the reservation according to the given rate" do + expect(@block.rate).must_equal 100 + end + + it "cannot have more than 5 rooms" do + expect{@manager.new_block(@available_rooms[0..5], Date.new(2021, 1, 10), Date.new(2021, 1, 20), 100)}.must_raise ArgumentError + end + + end + +end \ No newline at end of file diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..20ed23068 --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,23 @@ +require_relative 'test_helper' + +describe "reservation tests" do + before do + @reservation = Hotel::Reservation.new(3, Date.new(2020, 5, 20), Date.new(2020, 10, 20)) + end + + describe "initialize" do + it "can be instantiated" do + expect(@reservation).must_be_instance_of Hotel::Reservation + end + + it "will calculate the cost of the stay" do + expect(@reservation.cost).must_equal 1000 + end + + it "will store the start and end dates of the reservation" do + expect(@reservation.stay_begin).must_equal Date.new(2020, 5, 20) + expect(@reservation.stay_end).must_equal Date.new(2020, 10, 20) + end + + end +end \ No newline at end of file diff --git a/test/room_test.rb b/test/room_test.rb new file mode 100644 index 000000000..003227157 --- /dev/null +++ b/test/room_test.rb @@ -0,0 +1,31 @@ +require_relative 'test_helper' + +describe "room test" do + before do + @stay_begin = Date.new(2020, 5, 20) + @stay_end = Date.new(2020, 5, 28) + @manager = Hotel::ReservationManager.new + @manager.new_reservation(@stay_begin, @stay_end) + end + + describe "initialize" do + it "can be be created when an instance of reservation manager is instantiated." do + expect(@manager.rooms[0]).must_be_instance_of Hotel::Room + end + + it "can have an array of all reservation associated with it" do + expect(@manager.rooms[0].reservations).must_be_instance_of Array + end + + it "will have a number" do + expect(@manager.rooms[1].number).must_equal 2 + end + end + + describe "room methods" do + it "will find the reservation that matches a given date range" do + expect(@manager.rooms[0].find_reservations(@stay_begin, @stay_end)).must_be_instance_of Hotel::Reservation + end + end + +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..3afc7c605 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,17 @@ # Add simplecov +require 'simplecov' +SimpleCov.start do + add_filter 'test/' +end require "minitest" require "minitest/autorun" require "minitest/reporters" +require 'date' Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # require_relative your lib files here! +require_relative '../lib/reservation_manager' +require_relative '../lib/room' +require_relative '../lib/reservation' +require_relative '../lib/block'