diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..8ac21a816 Binary files /dev/null and b/.DS_Store differ diff --git a/design-activity.md b/design-activity.md new file mode 100644 index 000000000..d42c34176 --- /dev/null +++ b/design-activity.md @@ -0,0 +1,15 @@ +Question | Answer +:------------- | :------------- +What classes does each implementation include? Are the lists the same? | Each implementation includes the same classes - CartEntry, ShoppingCart, and Order. +Write down a sentence to describe each class. | CartEntry: Calculates price for each type of item purchased (e.g. apples). ShoppingCart: Stores the list of items being purchased and calculates the total cost. Order: Calculates the total price with sales tax. +How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. | A ShoppingCart has many CartEntries, and an Order has one ShoppingCart. +What data does each class store? How (if at all) does this differ between the two implementations? | CartEntry stores unit price and quantity, ShoppingCart stores the entries, and Order stores the cart cost plus tax. The data stored does not differ between the two implementations. +What methods does each class have? How (if at all) does this differ between the two implementations? | In both implementations, the purpose of the program is to calculate the cost. However, in the first implementation, the entire cost is calculated in the Order 'master class.' in the second implementation, the calculations are spread across the three classes. +Consider the Order#total_price method. In each implementation: +Question | Answer +:------------- | :------------- +Is logic to compute the price delegated to "lower level" classes like ShoppingCart and CartEntry, or is it retained in Order? | In implementation A, the logic to compute the price is entirely retained in Order. In implementation B, the logic is delegated across all the classes based on what information they store individually. +Does total_price directly manipulate the instance variables of other classes? | Implementation A: total price directly manipulates instance variables in CartEntry and ShoppingCart. Implementation B: total price does not directly manipulate instance variables in other classes. +If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? | There would no longer be a direct relationship between unit price and quantity - at some point (whatever is deemed a 'bulk amount' for quantity), the unit price would decrease. Implementation B would be easier to change because it does not directly rely on the instance variables in the CartEntry class. +Which implementation better adheres to the single responsibility principle? | Implementation B. +Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? | Implementation B. The calculations in implementation A have a lot of dependencies on other classes. \ No newline at end of file diff --git a/false, b/false, new file mode 100644 index 000000000..e69de29bb diff --git a/lib/date_range.rb b/lib/date_range.rb new file mode 100644 index 000000000..cfb36282b --- /dev/null +++ b/lib/date_range.rb @@ -0,0 +1,39 @@ +require 'date' + +module Hotel + class DateRange + attr_reader :start_date, :end_date + + #why did I have to make these arguments 'required' in order for my tests to work? + def initialize(input_start_date, input_end_date) + @start_date = Date.parse(input_start_date) + @end_date = Date.parse(input_end_date) + if @end_date <= @start_date + raise ArgumentError.new("the end date is before the start date!") + end + end + + #do not include end date in range because another reservation can include that same date as the start date + def createDateArray + @date_array = (@start_date...@end_date).to_a + return @date_array + end + + def duration + duration = (@end_date - @start_date).to_i + return duration + end + + #friend/'mentor' helped with refactoring overlap code + #she explained the single '&' is 'bitwise' code and is used more often in lower-level languages. It is comparing the object's date range array with the inputted object's date range array and pushes those similarities into an array. If they have no similarities (e.g. the array is empty), it returns false (indicating there is no overlap). If the array contains any dates, it returns true (indicating overlap) because the array will be filled with overlapping dates. + #typically 'other' is used, but for my understanding I used the parameter 'input' + def overlap?(input) + if self.createDateArray & input.createDateArray != [] + return true + else + return false + end + end + end +end + diff --git a/lib/hotel_booker.rb b/lib/hotel_booker.rb new file mode 100644 index 000000000..53d1ca02e --- /dev/null +++ b/lib/hotel_booker.rb @@ -0,0 +1,46 @@ +require 'date' + +module Hotel + class HotelBooker + attr_reader :rooms + attr_accessor :reservations + + def initialize(rooms:, reservations: []) + @rooms = (1..20).to_a + @reservations = [] || reservations + end + + #selects first available room for reservation, shovels into reservations array + #if there were no available rooms, the array would be empty! therefore, if the length of the array is 0, the program raises an ArgumentError. + #instructor helped me fix this code and the test in our 1:1 (had been returning a Hash) + def createReservation(room_number, date_range) + available_rooms_list = rooms_available?(date_range) + if available_rooms_list.length == 0 + raise ArgumentError.new("no available rooms for those dates!") + end + + new_reservation = Reservation.new(room_number: available_rooms_list.first, date_range: date_range) + return @reservations << new_reservation + end + + #checks the date_range + def reservationsByDate(date) + @reservations.each do |reservation_object| + if reservation_object.date_range.createDateArray.include?(date) + return reservation_object + end + end + end + + #first tried shoveling all room numbers EXCEPT those with overlapping date ranges into an empty array available_rooms, but program was returning nil values; .delete method worked (provide full array of rooms using @rooms instance variable, then delete as they appear to return an array with available dates) + def rooms_available?(date_range) + available_rooms = @rooms + @reservations.each do |reservation_object| + if reservation_object.date_range.overlap?(date_range) == true + available_rooms.delete(reservation_object.room_number) + end + end + return available_rooms + 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..e3ad3a56a --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,17 @@ +require 'date' + +module Hotel + class Reservation + ROOM_RATE = 200 + attr_reader :room_number, :date_range + + def initialize(room_number:, date_range:) + @room_number = room_number + @date_range = date_range + end + + def total_cost(nights) + return (ROOM_RATE * nights) + end + end +end diff --git a/test/date_range_test.rb b/test/date_range_test.rb new file mode 100644 index 000000000..cc56ba683 --- /dev/null +++ b/test/date_range_test.rb @@ -0,0 +1,47 @@ +require_relative 'test_helper' +require 'rake' + +describe "DateRange class" do + #similar to before statement in trip_dispatcher 'build_test_dispatcher' + #new_range is instance of DateRange + before do + def new_range(start_date, end_date) + return Hotel::DateRange.new(start_date, end_date) + end + end + + describe "DateRange instantiation" do + before do + @start_date = "2001/2/3" + @end_date = "2001/2/5" + @new_range = new_range(@start_date, @end_date) + end + + it "is an instance of DateRange" do + expect(@new_range).must_be_instance_of Hotel::DateRange + end + + it "raises an argument error if dates are invalid" do + expect{new_range("2001/2/5", "2001/2/3")}.must_raise ArgumentError + end + + it "creates an array of all dates, excluding reservation end date" do + test_range = @new_range.createDateArray + expect(test_range).must_be_kind_of Array + expect(test_range).wont_include @new_range.end_date + end + + #test includes one array with dates that overlap with the range_array and one array with dates that do not. The test for the overlapping dates returns true and the test for the non-overlapping dates returns false. + it "makes sure dates don't overlap" do + range_array = new_range("2001/2/3", "2001/2/5") + test_array_with_overlap = new_range("2001/2/4", "2001/2/6") + test_array_with_no_overlap = new_range("2001/5/14", "2001/5/17") + + overlap_test = @new_range.overlap?(test_array_with_overlap) + no_overlap_test = @new_range.overlap?(test_array_with_no_overlap) + + expect(overlap_test).must_equal true + expect(no_overlap_test).must_equal false + end + end +end diff --git a/test/hotel_booker_test.rb b/test/hotel_booker_test.rb new file mode 100644 index 000000000..bcd7d9d46 --- /dev/null +++ b/test/hotel_booker_test.rb @@ -0,0 +1,63 @@ +require_relative 'test_helper' +require 'rake' + +describe "HotelBooker class" do + before do + @date_range = Hotel::DateRange.new("2001/2/3", "2001/2/5") + @reservations = Hotel::Reservation.new(room_number: 1, date_range: @date_range) + @hotel_booker = Hotel::HotelBooker.new( + rooms: (1..20).to_a, + reservations: @reservations) + end + + describe "HotelBooker instantiation" do + it "is an instance of HotelBooker" do + expect(@hotel_booker).must_be_instance_of Hotel::HotelBooker + end + + it "lists all rooms in the hotel" do + expect(@hotel_booker.rooms.count).must_equal 20 + end + end + + #added @reservations.room_number parameter to try to debug the Hash error when raising an ArgumentError when all rooms are taken for a range range; might delete + #deleted before/do statement which contained @hotel_reservation = @hotel_booker.createReservation(@reservations.room_number, @date_range); it was causing errors + describe "HotelBooker methods" do + it "stores new reservations in the reservations array" do + @hotel_reservation = @hotel_booker.createReservation(@reservations.room_number, @date_range) + expect(@hotel_reservation).must_be_kind_of Array + expect(@hotel_reservation[0]).must_be_instance_of Hotel::Reservation + end + + #shovels the reservations for a specified date into an array + it "selects all reservations on a specified date" do + @hotel_reservation = @hotel_booker.createReservation(@reservations.room_number, @date_range) + list_of_reservations = [] + list_of_reservations << @hotel_booker.reservationsByDate(Date.parse("2001/2/3")) + + expect(list_of_reservations).must_be_kind_of Array + expect(list_of_reservations[0]).must_be_instance_of Hotel::Reservation + end + + #code finds the first available room, so the array will equal the remaining rooms 2-20 + it "can view a list of rooms that are not reserved for a given date range" do + @hotel_reservation = @hotel_booker.createReservation(@reservations.room_number, @date_range) + + dates = Hotel::DateRange.new("2001/2/3", "2001/2/5") + + reservation = Hotel::Reservation.new(room_number: 1, date_range: dates) + + expect(@hotel_booker.rooms_available?(dates)).must_equal (2..20).to_a + end + + #loops 20 times to create 20 different reservations to book all rooms; raises ArgumentError because no rooms available + it "raises an exception if program tries to reserve a room during a date range when all rooms are reserved" do + #room_number = 1 + 20.times do + @hotel_booker.createReservation(@reservations.room_number, @date_range) + #room_number += 1 + end + expect{@hotel_booker.createReservation(@reservations.room_number, @date_range)}.must_raise ArgumentError + end + end +end diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..b0c9d5803 --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,29 @@ +require_relative 'test_helper' +require 'rake' + +describe "Reservation class" do + describe "Reservation instantiation" do + before do + @date_range = Hotel::DateRange.new("2001/2/3", "2001/2/5") + @reservation = Hotel::Reservation.new( + room_number: 1, + date_range: @date_range + ) + end + + it "is an instance of Reservation" do + expect(@reservation).must_be_instance_of Hotel::Reservation + end + + it "date_range is an instance of DateRange" do + puts @reservation.date_range + expect(@reservation.date_range).must_be_instance_of Hotel::DateRange + end + + it "finds duration of stay and total cost of reservation" do + nights = @reservation.date_range.duration + total_cost = @reservation.total_cost(nights) + expect(total_cost).must_equal 400 + end + end +end diff --git a/test/simplecovtests.txt b/test/simplecovtests.txt new file mode 100644 index 000000000..bd0cbb607 --- /dev/null +++ b/test/simplecovtests.txt @@ -0,0 +1,10 @@ +1. What are SimpleCov and Guard? + a. SimpleCov - a tool to check what lines of code your tests cover (green). + b. Guard - Autorake - runs the tests affected by code changes, when you save +2. How might you use them in the Hotel project? + a. SimpleCov - Identify places in your code you're not testing! + i. refactoring - can help you identify blocks of code that will never be executed and aren't needed + b. Guard - see if the change you just wrote caused a test to pass or fail +3. Where do SimpleCov & Guard not help you in testing/ + a. SimpleCov - doesn't tell you if you covered all scenarios/edge cases (test quality) + b. Guard - causes the SimpleCov report to only cover the most recent tests run (run rake manually to override this!) diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..a7f37640c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,15 @@ # Add simplecov +require 'simplecov' +SimpleCov.start do + add_filter 'test/' +end + require "minitest" require "minitest/autorun" require "minitest/reporters" Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new -# require_relative your lib files here! +require_relative "../lib/date_range" +require_relative "../lib/reservation" +require_relative "../lib/hotel_booker"