diff --git a/Guardfile b/Guardfile index 6760f9177..fa59fc3ef 100644 --- a/Guardfile +++ b/Guardfile @@ -1,4 +1,4 @@ -guard :minitest, bundler: false, rubygems: false do +guard :minitest, bundler: false, autorun: false, rubygems: false do # with Minitest::Spec watch(%r{^spec/(.*)_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } diff --git a/design-activity.md b/design-activity.md new file mode 100644 index 000000000..418838c7e --- /dev/null +++ b/design-activity.md @@ -0,0 +1,28 @@ +What classes does each implementation include? Are the lists the same? +Write down a sentence to describe each class. + +The first implementation has a CartEntry class, a ShoppingCart class and an Order class with a total_price method. The second implementation has a price method in the CartEntry class that returns a sum based on unit price and quantity; a price method in the ShoppingCart class that collects the price for each entry and stores in variable named sum which will hold the price totals of all entries; there is a total price method in the Order class that takes the price of a ShoppingCart instance and stores into a variable called subtotal. This subtotal is added by itself and multiplied by a SALES_TAX to return a total for all items in cart. +---------- +How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. + +In the first implementation - To create an order instance, a ShoppingCart instance must also be created. They are tied together. This creates a coupling affect. The second implementation allows each class to calculate a sum. With the first class - the price of each item can be calculated based on its quantity. A shopping cart instance does not need to be created just to get the sum. There is also a price method that returns the sum of (the sum of all items). If one were to just want to see that price for an item based on its quantity, that is done entirely separate from the ShoppingCart class. Lastly there is a total price that DOES require information from another classes method to function. However a total price cannot be assessed without having a shopping cart thus this makes sense. +----- +What data does each class store? How (if at all) does this differ between the two implementations? +The second implementation's classes each have a method that calculates a sum. The first implementation - The order class stores dat of a shopping cart instance and calculates the price using the data from the CartEntry class. +----- +What methods does each class have? How (if at all) does this differ between the two implementations? +The first implementation has an instantiation and a total_price while while the second has a price method for each class except the Order class holds the totl price method. The second method removes dependencies on other classes. +----- +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? +The first implementation delegates the computation through its dependency on lower level classes like ShoppingCart and CartEntry while the second implementation retains that computation within Order. +---- +If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? The second implementation seems better because you can assign prices per item based on quantity before placing it in a cart. +---- +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? +Once you've responded to the prompts, git add design-activity.md and git commit! diff --git a/lib/hotel.rb b/lib/hotel.rb new file mode 100644 index 000000000..8439b5843 --- /dev/null +++ b/lib/hotel.rb @@ -0,0 +1,3 @@ +def hotel + return "this is a hotel" +end diff --git a/lib/psuedocode b/lib/psuedocode new file mode 100644 index 000000000..019010cf7 --- /dev/null +++ b/lib/psuedocode @@ -0,0 +1,70 @@ +RESERVATION_MNGER + +def list_rooms + returns array of all room instances +end + +def find_rooms(room_number) + returns room instance that matches room_number + ? call find_room instead? +end + +def find_available_room(specific_date) + returns an instance of room that is available for a specific date + how do i know it's available? + ask all of the reservations "is this room under any reservations for some specific date?" +end + +def reserve_room(room_number, start_date, end_date) + - takes room instance that matches room number + - make one new reservation for one room, for given start_date and end_date + - give the new reservation instance the room instance from step one + ? - changes availability for a period of time +end + +def list_reservations + - returns array of all reservation instances +end + +def find_reservation(reservation_id) + - returns reservation instance that matches reservation id + ? call find_reservation +end + +def get_total(reservation_id) + - calculates each room and number of nights reserved + - returns total +end + + +ROOM + +def current_status + - returns available or unavailable +end + +def availability + - returns date range of when room is available +end + +def cost(number_of_nights) + - returns cost * number of nights reserved +end + +RESERVATION + +def room_reserved + - what is the room associated with this reservation + - return room instance is associated with this reservation +end + +def reservation_date + - returns date reservation begins and ends + - example: 02/02/2018 - 02/04/2018 + - example: 2 days + ? tricky/fuzzy/needs to be investigated: what data type is this going to be? +end + +def total_room_cost + - calculates rooms_reserved * cost +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..9608453fe --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,20 @@ +require 'time' +require 'date' + +module Hotel + class Reservation + attr_accessor :room, :check_in, :check_out + + def initialize(input) + @room = input[:room] + @check_in = Date.strptime(input[:check_in], '%m/%d/%Y') + @check_out = Date.strptime(input[:check_out], '%m/%d/%Y') + end + + def room_reserved + return @room + end + + end + +end diff --git a/lib/reservation_mngr.rb b/lib/reservation_mngr.rb new file mode 100644 index 000000000..c2ec75a63 --- /dev/null +++ b/lib/reservation_mngr.rb @@ -0,0 +1,96 @@ +require 'time' +require 'date' +require 'pry' + + +require_relative 'reservation' + +module Hotel + + class ReservationManager < Reservation + + attr_reader :rooms, :reservations + + def initialize + @rooms = build_room_list + @reservations = [] + end + + def build_room_list + rooms = [] + 20.times do |i| + rooms << Hotel::Room.new({room_number: "#{i + 1}".to_i, price: 200}) + end + return rooms + end + + def check_dates(check_in, check_out) + raise ArgumentError if check_out == nil + raise ArgumentError if check_in == nil + raise ArgumentError if check_out < check_in + raise ArgumentError if check_in > check_out + end + + def find_room(check_in, check_out) + # find a way to use the find enumerable + # rm_avail = true + # @reservations.each do |reservation| + # if room.room_number == reservation.room.room_number + # if Date.strptime(check_in, '%m/%d/%Y') >= reservation.check_in && Date.strptime(check_out, '%m/%d/%Y') < reservation.check_out + # rm_avail = false + # break + # end + # else + # rm_avail = false + # end + # end + # if rm_avail + # return room + # end + check_in = Date.strptime(check_in, '%m/%d/%Y') + check_out = Date.strptime(check_out, '%m/%d/%Y') + + unavailable = @reservations.collect do |reservation| + if check_in >= reservation.check_in && check_in < reservation.check_out + reservation.room + end + end + if unavailable.length == 20 + return "No rooms available" + else + return @rooms[unavailable.length] + end + end + + + def create_reservation(check_in, check_out) + check_dates(check_in, check_out) + room = find_room(check_in, check_out) + reservation = Hotel::Reservation.new(room: room, check_in: check_in, check_out: check_out) + + + if reservation.room.room_number < 1 && reservation.room.room_number > 20 + raise ArgumentError + end + @reservations << reservation + return reservation + end + + def get_total(room, check_in, check_out) + @reservations.each do |reservation| + if reservation.room.nil? || reservation.room.room_number < 1 || reservation.room.room_number > 20 + raise ArgumentError + elsif reservation.room.room_number == room + if check_in != reservation.check_in || check_out != reservation.check_out + raise ArgumentError + else + date = reservation.check_out - reservation.check_in + price = reservation.room.price + return price * date + end + end + end + end + + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..4819f44bf --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,22 @@ +require 'time' + +module Hotel + + class Room + attr_accessor :room_number + attr_reader :price + + def initialize(input) + @room_number = input[:room_number] + if @room_number < 1 || @room_number > 20 + raise ArgumentError + end + @price = input[:price] + if @price != 200 + raise ArgumentError + end + end + + end + +end diff --git a/spec/refactor.txt b/spec/refactor.txt new file mode 100644 index 000000000..e37879a08 --- /dev/null +++ b/spec/refactor.txt @@ -0,0 +1,9 @@ +1. Move ArgumentErrors to check_dates method + +2. Refactor find_rooms method - make a new method to minimize nested loops + +3. Build rooms before creating reservation_mngr instances + +4. Change name of "rez" in reservation_mngr test ln 64 + +5. Change name of "@rez" in reservation_mngr test ln 46 diff --git a/spec/reservation_mngr_spec.rb b/spec/reservation_mngr_spec.rb new file mode 100644 index 000000000..1e0372767 --- /dev/null +++ b/spec/reservation_mngr_spec.rb @@ -0,0 +1,121 @@ +require_relative 'spec_helper' + +require 'date' + +describe 'ReservationManager class' do + + describe "ReservationManager instantiation" do + before do + @room = Hotel::Room.new( {room_number: 1, price: 200} ) + + @res_1 = Hotel::Reservation.new( { room: @room, check_in: "01/10/1988", check_out: "01/17/1988"} ) + + @front_desk = Hotel::ReservationManager.new() + end + + it "is an instance of ReservationManager" do + expect(@front_desk).must_be_kind_of Hotel::ReservationManager + end + + it "is set up for specific attributes and data types" do + [:rooms, :reservations].each do |prop| + expect(@front_desk).must_respond_to prop + end + + expect(@front_desk.rooms).must_be_kind_of Array + expect(@front_desk.reservations).must_be_kind_of Array + end + end + + describe "build_room_list" do + before do + @front_desk = Hotel::ReservationManager.new() + @room_list = @front_desk.build_room_list + end + + it "returns an array length of 20" do + expect(@room_list.length).must_equal 20 + end + end + + describe "find_room" do + before do + @front_desk = Hotel::ReservationManager.new() + @room_list = @front_desk.build_room_list + @front_desk.find_room("01/03/2018", "01/10/2018") + end + + it "returns a room that's available" do + res_1 = @front_desk.create_reservation("01/03/2018", "01/10/2018") + expect(res_1.room.room_number).must_equal 1 + end + + it "returns next available room" do + @res_1 = @front_desk.create_reservation("01/03/2018", "01/10/2018") + @res_2 = @front_desk.create_reservation("01/03/2018", "01/07/2018") + expect(@res_2.room.room_number).must_equal 2 + end + + it "returns length of array when all rooms reserved" do + 20.times do |i| + @front_desk.create_reservation("01/03/2018", "01/10/2018") + end + rez = @front_desk.reservations + expect(rez.length).must_equal 20 + end + + end + + + describe "create_reservation" do + before do + @room = Hotel::Room.new( {room_number: 1, price: 200} ) + @front_desk = Hotel::ReservationManager.new() + end + + it "throws an ArgumentError if check_out is before check_in" do + expect{@front_desk.create_reservation("01/10/2018", "01/09/2018")}.must_raise ArgumentError + end + + it "throws an ArgumentError if check_in is after check_out" do + expect{@front_desk.create_reservation("01/18/1988", "01/17/1988")}.must_raise ArgumentError + end + + it "throws an ArgumentError if check_in is nil" do + expect{@front_desk.create_reservation(nil, "01/17/1988")}.must_raise ArgumentError + end + + it "throws an ArgumentError if check_out is nil" do + expect{@front_desk.create_reservation("01/18/1988", nil)}.must_raise ArgumentError + end + + it "throws an ArgumentError if check_in and check_out are both nil" do + expect{@front_desk.create_reservation(nil, nil)}.must_raise ArgumentError + end + + it "stores reservation instance to class variable - reservations" do + @front_desk.create_reservation("01/10/2018", "01/17/2018") + res = @front_desk.reservations.length + expect(res).must_equal 1 + end + + it "returns a reservation" do + expect(@front_desk.create_reservation("01/10/2018", "01/17/2018")).must_be_kind_of Hotel::Reservation + end + end + + describe "get_total" do + before do + @front_desk = Hotel::ReservationManager.new() + @front_desk.create_reservation("01/10/1988", "01/17/1988") + end + + it "calculates total cost of reservation" do + expect(@front_desk.get_total(1, Date.strptime("01/10/1988", '%m/%d/%Y'), Date.strptime("01/17/1988", '%m/%d/%Y') ) ).must_equal 1400 + + @front_desk.create_reservation("01/15/1988", "01/20/1988") + + expect(@front_desk.get_total(2, Date.strptime("01/15/1988", '%m/%d/%Y'), Date.strptime("01/20/1988", '%m/%d/%Y') ) ).must_equal 1000 + end + end +end diff --git a/spec/reservation_spec.rb b/spec/reservation_spec.rb new file mode 100644 index 000000000..a39548567 --- /dev/null +++ b/spec/reservation_spec.rb @@ -0,0 +1,40 @@ +require_relative 'spec_helper' + +require 'date' + +describe 'Reservation class' do + + describe "Reservation instantiation" do + before do + @honeymoon_suite = Hotel::Room.new({room_number: 1, price: 200}) + @res_1 = Hotel::Reservation.new({room: @honeymoon_suite, check_in: "01/10/1988", check_out: "01/17/1988"}) + end + + it "is an instance of Reservation" do + expect(@res_1).must_be_kind_of Hotel::Reservation + end + + it "is set up for specific attributes and data types" do + [:room, :check_in, :check_out].each do |prop| + expect(@res_1).must_respond_to prop + end + + expect(@res_1.room).must_be_kind_of Hotel::Room + expect(@res_1.check_in).must_be_kind_of Date + expect(@res_1.check_out).must_be_kind_of Date + end + end + + describe 'room_reserved' do + before do + @honeymoon_suite = Hotel::Room.new({room_number: 1, price: 200}) + @res_2 = Hotel::Reservation.new({room: @honeymoon_suite, check_in: "01/10/1988", check_out: "01/17/1988"}) + end + + it "returns the room instance associated with this reservation" do + expect(@res_2).must_be_kind_of Hotel::Reservation + expect(@res_2.room_reserved).must_equal @honeymoon_suite + end + end + +end diff --git a/spec/room_spec.rb b/spec/room_spec.rb new file mode 100644 index 000000000..31f2bcf93 --- /dev/null +++ b/spec/room_spec.rb @@ -0,0 +1,43 @@ +require_relative 'spec_helper' + +describe 'Room class' do + + describe "Room instantiation" do + before do + @room = Hotel::Room.new({room_number: 1, price: 200}) + end + + it "it is an instance of Room" do + expect(@room).must_be_kind_of Hotel::Room + end + + it "throws an argument error with a invalid room_number" do + expect{ + Hotel::Room.new({room_number: 0, price: 200}) + }.must_raise ArgumentError + + expect{ + Hotel::Room.new({room_number: 21, price: 200}) + }.must_raise ArgumentError + end + + it "throws an argument error if price is not 200" do + expect{ + Hotel::Room.new({room_number: 1, price: 100}) + }.must_raise ArgumentError + + expect{ + Hotel::Room.new({room_number: 1, price: 400}) + }.must_raise ArgumentError + end + + it "is set up for specific attributes and data types" do + [:room_number, :price].each do |prop| + expect(@room).must_respond_to prop + end + + expect(@room.room_number).must_be_kind_of Integer + expect(@room.price).must_be_kind_of Integer + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4d1e3fdc8..962ea4094 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,15 @@ +require 'simplecov' +SimpleCov.start + require 'minitest' require 'minitest/autorun' require 'minitest/reporters' -# Add simplecov +require 'pry' + Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # Require_relative your lib files here! +require_relative '../lib/reservation' +require_relative '../lib/room' +require_relative '../lib/Reservation_mngr'