diff --git a/.gitignore b/.gitignore index 5e1422c9c..1ded4cf46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +coverage + *.gem *.rbc /.config 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..53f50d8be --- /dev/null +++ b/design_activity.md @@ -0,0 +1,41 @@ +What classes does each implementation include? Are the lists the same? + Each implementation has the same classes CartEntry, ShoppingCart, and Order. + +Write down a sentence to describe each class. + CartEntry: the CartEntry is responsible for keeping track of the unit price and quantity of each cart item. + ShoppingCart:The shopping car is responsible for storing all the entries in an array. + Order: The order is responsible for storing the sales tax, creating a new shopping cart, and calculating the total price of each cart. + +How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. + Each class relies on another to either fill in its data or to use in a method to calculate data. + +What data does each class store? How (if at all) does this differ between the two implementations? + ShoppingCart is the only class that stores data it stores all the entries. this is the same among implementations + +What methods does each class have? How (if at all) does this differ between the two +implementations? + In implementation A Order is the only class that contains a method outside of initialize and it has a total_price method. + + In implementation B all of the classes have a method. CartEntry and ShoppingCart have a price method, which differ from each other and order class has a 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? + It is delegated to lower level classes + +Does total_price directly manipulate the instance variables of other classes? + yes. + +If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? + The CartEntry class would change to include constants with bulk pricing. this could possibly be done with a method and if bulk pricing the equation of total price would differ. I think implementation A would be easier to modify because it refers to unit_price and quantity only once outside of the CartEntry class. + +Which implementation better adheres to the single responsibility principle? + Implementation A + +Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? + Implementation A + + + Identify one place in your Hotel project where a class takes on multiple roles, or directly modifies the attributes of another class. + Describe in design-activity.md what changes you would need to make to improve this design, and how the resulting design would be an improvement. + The hotel class takes on multiple roles, it does not modify attributes from the reservation class but, along with storing the reservations and listing the reservations and rooms available it also determines whether a room is available which could be something that a room class could do. To improve this design I would add a room class which would take on storing the rooms and managing whether they were available or not. Then the hotel could call the method in the rooms class as it does in the reservations class to determine whether the room is available and add it to a reservation and/or list available rooms depending on what admin needed to do. diff --git a/lib/hotel.rb b/lib/hotel.rb new file mode 100644 index 000000000..7ce42c04a --- /dev/null +++ b/lib/hotel.rb @@ -0,0 +1,54 @@ +require_relative 'reservation' +require_relative 'rooms' +require 'pry' + +# class hotel should store and manage the room and reservation lists + +class Hotel + attr_reader :rooms, :reservations + # the ability to create a hotel instance + def initialize + @rooms = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]# 1-20 + @reservations = [] + end + + + # As an administrator, I can reserve a room for a given date range + # As an administrator, I can reserve an available room for a given date range + def create_reservation(start_date, end_date,room) + # create instance of reservation + # store the reservation + unless room_available?(start_date, end_date, room) + raise ArgumentError.new("Room number #{room} is not available") + end + + new_reservation = Reservation.new(start_date, end_date, room) + @reservations << new_reservation + end + + + + # As an administrator, I can access the list of reservations for a specific date + def list_reservations_for_specific_date(date) + # find reservations that contain the date + @reservations.select { |reservation| reservation.contains?(date) } + end + + # As an administrator, I can view a list of rooms that are not reserved for a given date range + def list_rooms_available(start_date,end_date) + rooms.select {|room| room_available?(start_date, end_date, room) } + end + + def room_available?(start_date, end_date,room) + # find all reservations for room + # check reservations for date + select_rooms = reservations.select { |reservation| reservation.room_number == room } + + select_rooms.each do |res| + if res.contains?(start_date) || res.contains?(end_date) || (res.start_date >= start_date && res.end_date <= end_date) + return false + end + end + return true + end #room_available +end #class diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..4bc4af489 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,48 @@ + +require 'date' +require 'pry' + +class Reservation +attr_reader :start_date, :end_date, :room_number + + RATE = 200 + + # the ability to create a reservation + # raise an error for the incorrect date range + def initialize(start_date, end_date, room_number) + @room_number = room_number + @start_date = start_date + @end_date = end_date + + + date_valid? + end # initialize + + #validate the dates for checkin and checkout + def date_valid? + unless @end_date == nil || @start_date == nil + raise ArgumentError.new('Invalid date range') if @end_date < @start_date + end + end + + def contains?(date) + # reservation dates are included or not included in params + # room is reserved or available + date >= start_date && date <= end_date + end + + + # calculate days booked to help determine the cost of the reservation + # days_booked should not include the last day of the reservation + # checkout day days_booked is the (end_date - start_date) + def days_booked + end_date - start_date + end + + # calculate total cost of reservation using the days booked and RATE + def total_cost + total_cost = (days_booked - 1)* RATE + return total_cost + end + +end #class diff --git a/lib/rooms.rb b/lib/rooms.rb new file mode 100644 index 000000000..e69de29bb diff --git a/specs/hotel_spec.rb b/specs/hotel_spec.rb new file mode 100644 index 000000000..8173de203 --- /dev/null +++ b/specs/hotel_spec.rb @@ -0,0 +1,150 @@ +require_relative 'spec_helper' +gem 'minitest', '>= 5.0.0' +require 'minitest/pride' +require 'awesome_print' +require 'pry' + +describe 'hotel class' do + + describe '#initialize' do + it "can create an instance of hotel" do + new_building = Hotel.new + new_building.must_be_instance_of Hotel + end + + it "has a list of 20 rooms" do + new_building = Hotel.new + new_building.rooms.length.must_equal 20 + end + + it "has a empty list of reservations" do + new_building = Hotel.new + new_building.reservations.length.must_equal 0 + end + end + + describe 'create_reservation' do + it "can create a reservation" do + new_building = Hotel.new + + new_res = new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),3) + + new_building.reservations.length.must_equal 1 + new_building.reservations.length.must_be :>, 0 + end + + it "can create multiple reservations" do + new_building = Hotel.new + + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),2) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),7) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),20) + + + new_building.reservations.length.must_equal 3 + new_building.reservations.length.must_be :>, 2 + end + + it "raises an ArgumentError when a room is not avaialable" do + new_building = Hotel.new + + proc { new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),1) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),1) + }.must_raise ArgumentError + end + + it "raises an ArgumentError when all the rooms are books" do + new_building = Hotel.new + + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),1) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),2) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),3) + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),4) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),5) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),6) + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),7) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),8) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),9) + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),10) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),11) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),12) + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),13) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),14) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),15) + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),16) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),17) + new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),18) + new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),19) + new_building.create_reservation(Date.new(2018,3,7),Date.new(2018,3,10),20) + + proc { new_building.create_reservation(Date.new(2018,3,8),Date.new(2018,3,11),3) + }.must_raise ArgumentError + end + + end + + describe 'list_reservations_for_specific_date' do + it "will list all reservations during the date passed in" do + new_building = Hotel.new + + + new_building.create_reservation(Date.new(2018,4,2),Date.new(2018,4,7),1) + new_building.create_reservation(Date.new(2018,9,1),Date.new(2018,9,6),1) + + + res = new_building.list_reservations_for_specific_date(Date.new(2018,4,4)) + res.must_be_kind_of Array + + res.length.must_equal 1 + + res_2 = new_building.list_reservations_for_specific_date(Date.new(2017,4,4)) + res_2.length.must_equal 0 + + + end + #two reservations that overlap and make sure it displays 2 + end + + + describe 'list_rooms_available' do + it "can list all available rooms" do + new_building = Hotel.new + + res_1 = new_building.create_reservation(Date.new(2018,4,2),Date.new(2018,4,7),2) + res_2 = new_building.create_reservation(Date.new(2018,4,1),Date.new(2018,4,6),1) + res_3 = new_building.create_reservation(Date.new(2018,4,3),Date.new(2018,4,5),3) + # new_building.create_reservation(Date.new(2018,5,7),Date.new(2018,5,14),1) + + list = new_building.list_rooms_available(Date.new(2018,4,3),Date.new(2018,4,6)) + + list.must_be_kind_of Array + list.wont_include 2 + list.wont_include 1 + list.wont_include 3 + end + end + + describe 'room_available?' do + it "returns true when room is available" do + new_building = Hotel.new + + res_0 = new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),1) + res_1 = new_building.create_reservation(Date.new(2018,4,2),Date.new(2018,4,7),2) + + res_2 = new_building.room_available?(Date.new(2018,3,6),Date.new(2018,3,9),2) + + res_2.must_equal true + end + + it "returns false when a room is not available" do + new_building = Hotel.new + + res_0 = new_building.create_reservation(Date.new(2018,3,6),Date.new(2018,3,9),1) + res_1 = new_building.create_reservation(Date.new(2018,4,2),Date.new(2018,4,7),2) + + res_2 = new_building.room_available?(Date.new(2018,3,6),Date.new(2018,3,9),1) + + res_2.must_equal false + end + end +end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb new file mode 100644 index 000000000..be4af501e --- /dev/null +++ b/specs/reservation_spec.rb @@ -0,0 +1,45 @@ +require_relative 'spec_helper' +gem 'minitest', '>= 5.0.0' +require 'minitest/pride' +require 'pry' + +describe 'reservations class' do + + describe 'initialize' do + it "can create an instance of reservation" do + + new_reservation = Reservation.new(Date.new(2018,2,5), Date.new(2018,2,6),3) + new_reservation.must_be_instance_of Reservation + end + + it "raises and ArgumentError if end_date is before start_date" do + + start_date = Date.new(2018,2,20), + end_date = Date.new(2018,2,2), + room_number = 4 + + + proc { + new_res = Reservation.new(start_date, end_date, room_number) + }.must_raise ArgumentError + + end + end + + describe 'days_booked' do + it "calculates days booked correctly" do + new_reservation = Reservation.new(Date.new(2018,3,10), Date.new(2018,3,15),3) + + new_reservation.days_booked.must_equal 5 + end + end + + describe 'total_cost' do + it "calculates total_cost correctly" do + new_reservation = Reservation.new(Date.new(2018,5,10), Date.new(2018,5,20),9) + + new_reservation.total_cost.must_equal 1800 + end + end + +end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 000000000..f21648442 --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,14 @@ +require 'simplecov' +SimpleCov.start + +gem 'minitest', '>= 5.0.0' +require 'minitest/pride' +require 'minitest/autorun' +require 'minitest/reporters' + +require_relative '../lib/hotel' +require_relative '../lib/reservation' + + + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new