diff --git a/design-activity.md b/design-activity.md new file mode 100644 index 000000000..e008a39f3 --- /dev/null +++ b/design-activity.md @@ -0,0 +1,42 @@ +What classes does each implementation include? Are the lists the same? +ShoppingCart, Order, and CartEntry are classes in both implementations. + +Write down a sentence to describe each class. +CartEntry- calculates the price of an item added to a cart based on unit price and quantity. +ShoppingCart- calculates the price of total items in a cart +Order- calculates the total price of a cart including 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. +ShoppingCart holds many CartEntry objects and calculates the price of all of them. Order takes one shopping cart object and calculates final price with tax. + +What data does each class store? How (if at all) does this differ between the two implementations? Cart entry stores the data for the unit price and quantity. Shopping cart stores the data for all the entries. Order stores the data for the shopping cart. In the first implementation only order can get data about the price. In Implementaiton B they all have information about the price. + + +What methods does each class have? How (if at all) does this differ between the two implementations?In implmentation A the shopping cart and cart entry have no methods. However, in implementation B they both have pricing methods. In both implementaitons Order has total price method. + +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? +Does total_price directly manipulate the instance variables of other classes? + +In implementation A logic is not delegated to lower classes. However, in implementaiton b logic is delegated to lower classes. Total price does not directlymanipulate the instance variables of the other classes. + +If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? + +You would have to add an if statement to add a discount if the quantity is higher than the bulk amount. It is easier to modify 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 than implementation A. In implementation A the order is connected to both shoppingcart and cart entry then shopping cart is also connected to cart entry. In Implementation B order is connected to only shopping cart and shopping cart is connected to cart entry. + + +what changes you would need to make to improve this design, and how the resulting design would be an improvement. + +my find_reservation_by_date is doing a lot. It is finding the days that are included in between the start date/end date. Then checking if the date is included into that. I decided to add a method in reservation that creates an array with all the dates. That way in find_reservation_by_date it only needs to check if the date is included in that array. + + + + + + diff --git a/lib/calendar.rb b/lib/calendar.rb new file mode 100644 index 000000000..b6b34ae07 --- /dev/null +++ b/lib/calendar.rb @@ -0,0 +1,16 @@ +class Calendar + attr_reader :start_date, :end_date + + def initialize(start_date:, end_date:) + @start_date = start_date + @end_date = end_date + + if end_date < start_date + raise ArgumentError.new("Invalid Date Range (end date is before start date)") + end + end + + def overlap?(current_reservation) + return !(current_reservation.start_date >= end_date || current_reservation.end_date <= start_date) + end +end diff --git a/lib/hotel_booker.rb b/lib/hotel_booker.rb new file mode 100644 index 000000000..37308a452 --- /dev/null +++ b/lib/hotel_booker.rb @@ -0,0 +1,55 @@ + +require_relative 'reservation' +require_relative 'calendar' + +class NoAvailableRoomsError < StandardError +end +class HotelBooker + attr_reader :rooms, :reservations + + def initialize + @rooms = (1..20).map { |i| i } + + @reservations = {} + + @rooms.each do |room| + @reservations[room] = [] + end + end + + def find_available_room(start_date, end_date) + available_rooms = [] + reservations.each do |room, reservations| + if reservations.empty? + available_rooms << room + else + date_range = Calendar.new(start_date: start_date, end_date: end_date) + if reservations.all? { |reservation| !date_range.overlap?(reservation) } + available_rooms << room + end + end + end + + return available_rooms + end + + def create_reservation(id, start_date, end_date) + found_rooms = find_available_room(start_date, end_date) + + if found_rooms.empty? + raise NoAvailableRoomsError + else + new_reservation = Reservation.new(id: id, start_date: start_date, end_date: end_date, room: found_rooms.first) + reservations[found_rooms.first] = reservations[found_rooms.first] << new_reservation + + return new_reservation + end + end + + def find_reservation_by_date(date) + reservations_for_date = reservations.map do |room, reservation| + reservation.find_all { |reservation| reservation.calculate_days.include?(date)} + end + return reservations_for_date.flatten + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..024e5f404 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,27 @@ +class Reservation + attr_reader :id, :start_date, :end_date, :room, :date_range,:reservation_days + + def initialize(id:, start_date:, end_date:, room:) + @id = id + @start_date = start_date + @end_date = end_date + @room = room + end + + def total_cost + 200 * (end_date - start_date).to_i + end + + def calculate_days + days = [] + current_date = end_date + while current_date >= start_date + days << current_date + current_date -= 1 + end + return days + end + + + +end diff --git a/refactors.txt b/refactors.txt new file mode 100644 index 000000000..e75761135 --- /dev/null +++ b/refactors.txt @@ -0,0 +1,10 @@ +Refactor list +- improve find_reservation_by_date by creating another method that checks if the date is included in the range +-make the classes less coupled +-refactor all methods to be shorter/more efficient +-add a module +-reword test desciptions +-make method that calculates nights +-check all names and make sure they make sense + + diff --git a/test/calendar_test.rb b/test/calendar_test.rb new file mode 100644 index 000000000..844580641 --- /dev/null +++ b/test/calendar_test.rb @@ -0,0 +1,56 @@ +require_relative "test_helper" + +describe "wave 2" do + describe "initialize " do + it "raises an argument error for invalid date range" do + expect { Calendar.new(start_date: Date.new(2019, 2, 9), end_date: Date.new(2019, 2, 3)) }.must_raise ArgumentError + end + end + describe "overlap?" do + before do + @reservation = Reservation.new(id: 1, start_date: Date.new(2019, 2, 5), end_date: Date.new(2019, 2, 9), room: 5) + end + + it "returns true if new reservation endate overlaps " do + date_range = Calendar.new(start_date: Date.new(2019, 2, 3), end_date: Date.new(2019, 2, 6)) + expect(date_range.overlap?(@reservation)).must_equal true + end + + it " returns true if new reserveration start date overlaps" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 6), end_date: Date.new(2019, 2, 11)) + expect(date_range.overlap?(@reservation)).must_equal true + end + + it " returns true if new reservation start_date and end_date both overlap" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 6), end_date: Date.new(2019, 2, 8)) + expect(date_range.overlap?(@reservation)).must_equal true + end + it "returns true if new reservation includes both start_date and endate" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 3), end_date: Date.new(2019, 2, 14)) + expect(date_range.overlap?(@reservation)).must_equal true + end + it "returns true if new reservation is exactly the same" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 5), end_date: Date.new(2019, 2, 9)) + expect(date_range.overlap?(@reservation)).must_equal true + end + + it "returns false if new reservation is completely before" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 1), end_date: Date.new(2019, 2, 3)) + expect(date_range.overlap?(@reservation)).must_equal false + end + it "returns false if new reservation is completely after" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 11), end_date: Date.new(2019, 2, 15)) + expect(date_range.overlap?(@reservation)).must_equal false + end + + it "returns false if new reservation starts on end date " do + date_range = Calendar.new(start_date: Date.new(2019, 2, 9), end_date: Date.new(2019, 2, 16)) + expect(date_range.overlap?(@reservation)).must_equal false + end + + it "returns false if new reservation ends on start date" do + date_range = Calendar.new(start_date: Date.new(2019, 2, 1), end_date: Date.new(2019, 2, 5)) + expect(date_range.overlap?(@reservation)).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..d68af2bf1 --- /dev/null +++ b/test/hotel_booker_test.rb @@ -0,0 +1,108 @@ +require_relative "test_helper" + +describe "Wave 1 " do + describe "initialize" do + before do + @hotel = HotelBooker.new + end + it "creates 20 rooms " do + expect(@hotel.rooms.length).must_equal 20 + end + it " returns rooms that are integers" do + expect(@hotel.rooms).must_be_instance_of Array + @hotel.rooms.each do |room| + expect(room).must_be_instance_of Integer + end + end + end + + describe " create_reservation" do + it "returns a reservation" do + hotel = HotelBooker.new + reservation = hotel.create_reservation(1, Date.new(2019, 2, 3), Date.new(2019, 2, 6)) + expect(reservation).must_be_instance_of Reservation + end + + it "new reservation is added to reservations" do + hotel = HotelBooker.new + hotel.create_reservation(1, Date.new(2019, 2, 5), Date.new(2019, 2, 9)) + expect(hotel.reservations[1].length).must_equal 1 + end + it "doesnt make conflicting reservations for the same room" do + hotel = HotelBooker.new + hotel.create_reservation(1, Date.new(2019, 2, 6), Date.new(2019, 2, 11)) + hotel.create_reservation(4, Date.new(2019, 2, 7), Date.new(2019, 2, 10)) + + expect(hotel.reservations[1].length).must_equal 1 + expect(hotel.reservations[2].length).must_equal 1 + end + it "new reservations can start on checkout days" do + hotel = HotelBooker.new + hotel.create_reservation(1, Date.new(2019, 2, 6), Date.new(2019, 2, 11)) + hotel.create_reservation(2, Date.new(2019, 2, 1), Date.new(2019, 2, 6)) + expect(hotel.reservations[1].length).must_equal 2 + end + it "produces an error if all the rooms are booked" do + hotel = HotelBooker.new + 20.times do |i| + hotel.create_reservation(i, Date.new(2019, 2, 6), Date.new(2019, 2, 11)) + end + expect { hotel.create_reservation(21, Date.new(2019, 2, 6), Date.new(2019, 2, 11)) }.must_raise NoAvailableRoomsError + end + end + + describe "find_reservation_by_date" do + before do + @hotel = HotelBooker.new + @hotel.create_reservation(1, Date.new(2019, 2, 6), Date.new(2019, 2, 11)) + @hotel.create_reservation(2, Date.new(2019, 2, 1), Date.new(2019, 2, 6)) + @hotel.create_reservation(3, Date.new(2019, 2, 3), Date.new(2019, 2, 12)) + @hotel.create_reservation(4, Date.new(2019, 2, 7), Date.new(2019, 2, 10)) + @hotel.create_reservation(5, Date.new(2019, 2, 8), Date.new(2019, 2, 13)) + end + it "returns an array of reservations" do + found_reservations = @hotel.find_reservation_by_date(Date.new(2019, 2, 10)) + expect(found_reservations).must_be_instance_of Array + found_reservations.each do |reservation| + expect(reservation).must_be_instance_of Reservation + end + end + it "all returned reservations inlcude the date" do + found_reservations = @hotel.find_reservation_by_date(Date.new(2019, 2, 6)) + expect(found_reservations[0].id).must_equal 1 + expect(found_reservations[1].id).must_equal 2 + expect(found_reservations[2].id).must_equal 3 + expect(found_reservations[3]).must_be_nil + end + end +end +describe "Wave 2" do + describe "find_available_room" do + it "returns an array of available rooms" do + hotel = HotelBooker.new + available_rooms = hotel.find_available_room(Date.new(2019, 2, 5), Date.new(2019, 2, 9)) + expect(available_rooms).must_be_instance_of Array + end + + it "returns all the rooms when the array is empty" do + hotel = HotelBooker.new + available_rooms = hotel.find_available_room(Date.new(2019, 2, 5), Date.new(2019, 2, 9)) + expect(available_rooms.length).must_equal 20 + end + + it "returns only empty rooms " do + hotel = HotelBooker.new + + hotel.create_reservation(1, Date.new(2019, 2, 6), Date.new(2019, 2, 11)) + + hotel.create_reservation(2, Date.new(2019, 2, 1), Date.new(2019, 2, 8)) + + hotel.create_reservation(3, Date.new(2019, 2, 8), Date.new(2019, 2, 12)) + hotel.create_reservation(4, Date.new(2019, 2, 11), Date.new(2019, 2, 14)) + hotel.create_reservation(5, Date.new(2019, 2, 11), Date.new(2019, 2, 14)) + available_rooms = hotel.find_available_room(Date.new(2019, 2, 5), Date.new(2019, 2, 9)) + expect(available_rooms[0]).must_equal 3 + expect(available_rooms.length).must_equal 18 + end + end +end diff --git a/test/reservation_test.rb b/test/reservation_test.rb new file mode 100644 index 000000000..7acf15536 --- /dev/null +++ b/test/reservation_test.rb @@ -0,0 +1,35 @@ +require_relative "test_helper" + +describe "total_cost method" do + before do + @start_time = Date.new(2019, 9, 6) + @end_time = Date.new(2019, 9, 9) + @reservation = Reservation.new(id: 1, start_date: @start_time, end_date: @end_time, room: 5) + end + it "returns an instance of an integer" do + expect(@reservation.total_cost).must_be_instance_of Integer + end + + it "returns accurate calculation of total cost" do + expect(@reservation.total_cost).must_equal 600 + end +end + +describe "calculate days method" do + before do + @start_time = Date.new(2019, 2, 1) + @end_time = Date.new(2019, 2, 5) + @reservation = Reservation.new(id: 1, start_date: @start_time, end_date: @end_time, room: 5) + end + + it " returns an array" do + expect(@reservation.calculate_days).must_be_instance_of Array + end + it "returns accurate information for days included in reservation" do + days = @reservation.calculate_days + expect(days.length).must_equal 5 + [Date.new(2019, 2, 1), Date.new(2019, 2, 2), Date.new(2019, 2, 3), Date.new(2019, 2, 4), Date.new(2019, 2, 5)].each do |date| + expect(days.include?(date)).must_equal true + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index c3a7695cf..ae4290beb 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,8 +1,18 @@ # 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/calendar' +require_relative '../lib/hotel_booker' +require_relative '../lib/reservation'