diff --git a/.gitignore b/.gitignore index 5e1422c9c..09463d230 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ build-iPhoneSimulator/ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc +coverage + +.DS_Store 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..399afa279 --- /dev/null +++ b/design-activity.md @@ -0,0 +1,20 @@ +1. Implementation A has three classes: CartEntry, ShoppingCart, and Order. +Implementation B has the same three classes as well. + +2. For implementation A, CartEntry initializes the unit price and quantity for each item. ShoppingCart stores the entries of each item in an array. Order creates a new instance of a shopping cart and then calculates the total price of all the items in a cart with the sales tax. + +For implementation B, CartEntry initializes the unit price and quantity for each item. ShoppingCart stores the entry of each item in an array and also calculates the price of each item. Order creates a new instance of a shopping cart and calculates the total price of each cart with all the items and the sales tax. + +3. For implementation A, the CartEntry establishes the quantity and price of each item. ShoppingCart stores each entry. These two classes are needed for the Order class which creates a new instance of a shopping cart and then goes through each entry in the cart to calculate the sum of all the items and their prices. It returns the total price. + +For implementation B, CartEntry is responsible for establishing the unit price and and quantity of each item. Then it calculates the price of each type of item depending on the quantity ordered. ShoppingCart stores the entry of each item in a shopping cart. It also calculates the price of each entry by calling on the price method in CartEntry and then adding up the sums. Order creates a new instance of shopping cart. It calculates the subtotal by calling on the price method from ShoppingCart. Then it calculates the overall cost by multiplying the subtotal to the sales tax and adding that to the original subtotal amount. + +4. CartEntry stores each item's price and quantity. ShoppingCart stores all the entries. Order retrieves the information from CartEntry and ShoppingCart to return the total price of each cart. Both store the same info. However, I think implementation A does a better job of creating single responsibility for each class without relying too heavily on the Order class to calculate the total price. However, the subtotal variable in implementation B is much clearer on what is being calculated vs, implementation A where the variable sum is used. I wasn't sure why sum was being returned at first. + +5. Initially, it seemed logical to compute the the price in "lower level" classes like ShoppingCart and CartEntry. However that creates a tie between all the classes and if one is changed, then the others must be modified as well. In the first implementation, ShoppingCart doesn't rely on price being calculated in CartEntry and then Order doesn't rely on ShoppingCart for price to be calculated in order for the total price to be calculated. Total price does not directly manipulate the instance variables of the other classes. + +6. If items were bought in bulk, I think this would be done in a new method called bulk. I think because method A is more loosely coupled than B, it would be easier to implement in A. + +7. Implementation A. + +8. Implementation A. Although I think implementation B is a little easier to read because the variable subtotal in the method total_price was clearer to me. diff --git a/lib/block.rb b/lib/block.rb new file mode 100644 index 000000000..efe251f29 --- /dev/null +++ b/lib/block.rb @@ -0,0 +1,23 @@ +require 'date' +require 'securerandom' + +class Block + attr_reader :check_in_date, :check_out_date, :id, :block_of_rooms + attr_accessor :rooms + + BLOCKRATE = 100 + + def initialize(check_in_date, check_out_date, block_of_rooms) + @id = SecureRandom.uuid + @check_in_date = check_in_date + @check_out_date = check_out_date + @block_of_rooms = block_of_rooms + @rooms = [] + end + + def valid_block_of_rooms + if block_of_rooms > 5 + raise ArgumentError, 'Blocks can have up to 5 rooms.' + end + end +end diff --git a/lib/hotel.rb b/lib/hotel.rb new file mode 100644 index 000000000..54f48f0fe --- /dev/null +++ b/lib/hotel.rb @@ -0,0 +1,5 @@ +ROOM_LIST = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + +def hotel + return "hotel" +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..17ab64b37 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,32 @@ +# Keeps track of 1 reservation +require_relative 'room' +require 'date' + +# module Hotel +class Reservation + attr_reader :check_in_date, :check_out_date, :room_number, :nights_stayed + + def initialize(check_in_date, check_out_date, room_number) + @check_in_date = check_in_date + @check_out_date = check_out_date + @room_number = room_number + raise StandardError, 'The end date cannot be before the start date.' if check_out_date <= check_in_date + end + + def nights_stayed + date_range = check_out_date - check_in_date + return date_range.to_i + end +end + +class BlockReservation < Reservation + def initialize(check_in_date, check_out_date, room_number) + super + end + + def block_rate + date_range = @check_out_date - @check_in_date + date_range.to_i + end +end +# end diff --git a/lib/reservation_tracker.rb b/lib/reservation_tracker.rb new file mode 100644 index 000000000..75e912aed --- /dev/null +++ b/lib/reservation_tracker.rb @@ -0,0 +1,108 @@ +require 'pry' +require 'date' +require_relative 'room' +require_relative 'block' +require_relative 'reservation' +# Keeps track of the list of reservations +# module Hotel +class ReservationTracker + attr_reader :rooms, :check_in_date, :check_out_date, :reservations, :unreserved_rooms, :blocks + + def initialize + @rooms = create_rooms + @reservations = [] + @unreserved_rooms = [] + @blocks = [] + # @existing_reservations = [] + + + end + + # creates and accesses the list of all of the rooms in the hotel + def create_rooms + rooms = [] + (1..20).each do |room| + room_number = Room.new(room) + rooms << room_number + end + return rooms + end + + # creates reservation and adds the reservation to the list of reservations + def create_reservation(check_in_date, check_out_date, room_number) + reservation = Reservation.new(check_in_date, check_out_date, room_number) + @reservations << reservation + # @reserved_rooms << room + # return reservation + end + + def create_a_block(check_in_date, check_out_date, block_of_rooms) + # rooms_available is an arrary from rooms_not_reserved + rooms_available = rooms_not_reserved(check_in_date, + check_out_date) + block_reservation = Block.new(check_in_date, + check_out_date, + block_of_rooms) + block_reservation.rooms = rooms_available.pop(block_of_rooms) + @blocks << block_reservation + + return block_reservation + end + + # don't think i need this anymore + # def is_a_room_available?(date) + # @reservations.each do |reservation | + # return false if reservation.check_in_date >= date && reservation.check_out_date > date + # end + # # binding.pry + # return true + # end + + # accesses the list of reservations for a specific date + def list_of_reservations(date) + return @reservations.find_all { |reservation| reservation.nights_stayed == date } + end + + # calculates the total cost for a given reservation + def cost_for_given_reservation(reservation) + total_cost = reservation.nights_stayed * Room::ROOM_RATE + return total_cost + end + + # checks to see if a room is available and reserves the first available room for a given date range. Uses the rooms_not reserved method. + def reserve_room(check_in_date, check_out_date) + # set the rooms_not_reserved method and arguments to a local variable. + unreserved_rooms = rooms_not_reserved(check_in_date, check_out_date) + if unreserved_rooms.length == 0 + raise ArgumentError, 'No Available Rooms for Given Dates' + else + # uses the create reservation method if there are available rooms + create_reservation(check_in_date, check_out_date, unreserved_rooms[0]) + # binding.pry + end + end + + # accesses a list of rooms that are not reserved for a given date range + def rooms_not_reserved(check_in_date, check_out_date) + # if the reservations array is empty, this returns an array of available room numbers. If there are no reservations, all rooms can be reserved. + return @rooms.map { |room| room.room_number } if @reservations.empty? + unreserved_rooms = @rooms.map { |room| room.room_number } + # if @reservation.empty? is not empty + @reservations.each { |reservation| + if (reservation.check_in_date >= check_in_date && reservation.check_in_date <= check_out_date) || (reservation.check_out_date >= check_in_date && reservation.check_out_date <= check_out_date) + unreserved_rooms.delete(reservation.room_number) + end + } + + @blocks.each { |block| + if (block.check_in_date >= check_in_date && block.check_in_date <= check_out_date) || (block.check_out_date >= check_in_date && block.check_out_date <= check_out_date) + unreserved_rooms - block.rooms + end + } + # unreserved_rooms returns an empty array if nothing is availible in the given date range or it will return an array of available room numbers in date range given. + # for use in the reserve_room method + return unreserved_rooms + end + +end +# end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..3772b8d0b --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,11 @@ +# Keeps track of 1 room_ +class Room + attr_reader :room_number + + ROOM_RATE = 200 + + def initialize(room_number) + @room_number = room_number + end + +end diff --git a/spec/block_spec.rb b/spec/block_spec.rb new file mode 100644 index 000000000..12f21ca36 --- /dev/null +++ b/spec/block_spec.rb @@ -0,0 +1,32 @@ +require 'simplecov' +SimpleCov.start +require_relative 'spec_helper' + +describe 'Block Class' do + it 'creates a new instance of a block' do + check_in_date = Date.new(2018, 9, 5) + check_out_date = Date.new(2018, 9, 8) + new_block = Block.new(check_in_date, check_out_date, 5) + expect(new_block).must_be_instance_of Block + end + + it 'charges a rate of 100 for the BLOCKRATE' do + Block::BLOCKRATE.must_equal 100 + end + + it 'block of rooms must equal 5' do + check_in_date = Date.new(2018, 9, 5) + check_out_date = Date.new(2018, 9, 8) + new_block = Block.new(check_in_date, check_out_date, 5) + expect(new_block.block_of_rooms).must_equal 5 + end +end + +describe 'validate block of rooms' do + it 'raises an Argument Error, if there are more than 5 rooms in a block' do + check_in_date = Date.new(2018, 9, 5) + check_out_date = Date.new(2018, 9, 8) + new_block = Block.new(check_in_date, check_out_date, 6) + expect { new_block.valid_block_of_rooms }.must_raise ArgumentError + end +end diff --git a/spec/hotel_spec.rb b/spec/hotel_spec.rb new file mode 100644 index 000000000..ea73c9b0f --- /dev/null +++ b/spec/hotel_spec.rb @@ -0,0 +1,8 @@ +require_relative 'spec_helper' + +describe 'hotel' do + it 'prints hotel' do + result = hotel() + expect(result).must_equal 'hotel' + end +end diff --git a/spec/reservation_spec.rb b/spec/reservation_spec.rb new file mode 100644 index 000000000..e4fefb78b --- /dev/null +++ b/spec/reservation_spec.rb @@ -0,0 +1,54 @@ +require 'simplecov' +SimpleCov.start +require 'pry' +require 'time' + +require_relative 'spec_helper' + +describe 'Reservation' do + describe 'initialize' do + check_in_date = Date.new(2018, 9, 5) + check_out_date = Date.new(2018, 9, 8) + room_number = Room.new(15) + reservation = Reservation.new(check_in_date, check_out_date, room_number) + + it 'takes a check in date, check out date, and room number' do + + expect(reservation).must_respond_to :check_in_date + expect(reservation.check_in_date).must_equal check_in_date + + expect(reservation).must_respond_to :check_out_date + expect(reservation.check_out_date).must_equal check_out_date + + expect(reservation).must_respond_to :room_number + expect(reservation.room_number).must_equal room_number + end + + it 'raises an ArgumentError for an invalid date range' do + expect{ (Reservation.new(check_out_date, check_in_date)) }.must_raise StandardError + end + end + + describe 'nights stayed method' do + check_in_date = Date.new(2018, 9, 6) + check_out_date = Date.new(2018, 9, 10) + room_number = 15 + reservation = Reservation.new(check_in_date, check_out_date, room_number) + + it 'calculates the number of nights stayed correctly' do + + expect(reservation.nights_stayed).must_equal 4 + end + end +end + +describe 'BlockReservation' do + it 'instantiates a block reservation' do + check_in_date = Date.new(2018, 9, 6) + check_out_date = Date.new(2018, 9, 10) + room_number = 15 + block_reservation = BlockReservation.new(check_in_date, check_out_date, room_number) + expect(block_reservation).must_be_instance_of BlockReservation + end + +end diff --git a/spec/reservation_tracker_spec.rb b/spec/reservation_tracker_spec.rb new file mode 100644 index 000000000..1bbbc7dc8 --- /dev/null +++ b/spec/reservation_tracker_spec.rb @@ -0,0 +1,179 @@ +require 'simplecov' +SimpleCov.start +require 'pry' + +require_relative 'spec_helper' + +describe 'ReservationTracker class' do + describe 'initialize' do + before do + @reservation_tracker = ReservationTracker.new + end + + it 'has an array of all rooms in the hotel' do + expect(@reservation_tracker.rooms).must_be_kind_of Array + end + + it 'has 20 rooms in the hotel' do + expect(@reservation_tracker.rooms.length).must_equal 20 + end + + it 'has an array of all the reservations in the hotel' do + expect(@reservation_tracker.reservations).must_be_kind_of Array + end + + it 'has an array of all the unreserved rooms in the hotel' do + expect(@reservation_tracker.unreserved_rooms).must_be_kind_of Array + end + + # it 'should be able to access the list of rooms' do + # binding.pry + # expect(@reservation_tracker.rooms[0]).must_equal 1 + # expect(@reservation_tracker.rooms[20]).must_equal 20 + # end + end + + describe 'create reservation method' do + before do + @reservation_tracker = ReservationTracker.new + end + + it 'reserves a room in a given date range' do + # check_in_date = Date.today + # check_out_date = Date.today + 5 + date = Date.today + room = Room.new(15) + expect(@reservation_tracker.create_reservation(date, date + 5, room)).must_be_kind_of Array + end + + it "should be able to add a reservation" do + reservation = Reservation.new(Date.new(2018,9,5), Date.new(2018,9,10), 15) + @reservation_tracker.reservations << reservation + @reservation_tracker.reservations.must_equal [reservation] + end + + # it "creates a reservation with a specific date range" do + # check_in_date = Date.new(2018,9,5) + # check_out_date = Date.new(2018,9,10) + # reservation = Reservation.new(check_in_date, check_out_date, 15) + # @reservation_tracker.reservations[check_in_date].must_equal Date.new(2018,9,5) + # @reservation_tracker.reservations[check_out_date].must_equal Date.new(2018,9,10) + # end + end + + describe 'create a block method' do + before do + @reservation_tracker = ReservationTracker.new + end + + it 'should create a new block' do + date = Date.today + @reservation_tracker.create_rooms + @reservation_tracker.create_a_block(date + 2, date + 7, 5) + # binding.pry + expect(@reservation_tracker.blocks.length ).must_equal 1 + end + + it 'should create an array of rooms for the block' do + date = Date.today + @reservation_tracker.create_rooms + @reservation_tracker.create_a_block(date + 2, date + 7, 5) + expect(@reservation_tracker.blocks).must_be_kind_of Array + end + + end + + # don't think I need this since I check this in the reserve_room method. + # describe 'is a room available method' do + # before do + # @reservation_tracker = ReservationTracker.new + # #.create_reservation(check_in_date, check_out_date, room) + # + # # @date = Date.today + # # @room = Room.new(15) + # end + # + # it 'can check its availability on a specific date' do + # + # check_in_date = Date.new(2018, 9, 6) + # check_out_date = Date.new(2018, 9, 10) + # room = 15 + # reservation = Reservation.new(check_in_date, check_out_date, room) + # + # expect(@reservation_tracker.is_a_room_available?(Date.new(2018, 9, 7))).must_equal false + # expect(@reservation_tracker.is_a_room_available?(Date.new(2018, 9, 11))).must_equal true + # end + # + # it 'has a room to be available on the last day of a reservation' do + # check_in_date = Date.new(2018, 9, 6) + # check_out_date = Date.new(2018, 9, 10) + # # room = 15 + # # reservation = Reservation.new(check_in_date, check_out_date, room) + # expect(@reservation_tracker.is_a_room_available?(Date.new(2018, 9, 10))).must_equal true + # end + # end + + describe 'list of reservations method' do + before do + @reservation_tracker = ReservationTracker.new + end + + it 'shows the list of reservations for a specific date' do + date = Date.new(2018, 9, 7) + + expect(@reservation_tracker.list_of_reservations(date)).must_be_kind_of Array + end + end + + describe 'cost for given reservation method' do + before do + @reservation_tracker = ReservationTracker.new + end + + it 'calculates the cost of a specific reservation' do + check_in_date = Date.new(2018, 9, 6) + check_out_date = Date.new(2018, 9, 10) + room = 15 + reservation = Reservation.new(check_in_date, check_out_date, room) + + expect(@reservation_tracker.cost_for_given_reservation(reservation)).must_be_kind_of Integer + expect(@reservation_tracker.cost_for_given_reservation(reservation)).must_equal 800 + end + end + + describe 'reserve_room method' do + before do + @reservation_tracker = ReservationTracker.new + @date = Date.today + 20.times do |i| + @reservation_tracker.reserve_room(@date, @date + 5) + end + end + + it "Raises ArgumentError if there are no available rooms" do + + expect { @reservation_tracker.reserve_room(@date, @date + 5) }.must_raise ArgumentError + end + end + + describe 'rooms not reserved method' do + before do + @reservation_tracker = ReservationTracker.new + end + + it 'returns an array of rooms that are not reserved for a given date range' do + check_in_date = Date.new(2018, 9, 7) + check_out_date = Date.new(2018, 9, 10) + expect(@reservation_tracker.rooms_not_reserved(check_in_date, check_out_date)).must_be_kind_of Array + end + + it 'returns a list of rooms not reserved' do + date = Date.today + @reservation_tracker.create_rooms + @reservation_tracker.create_reservation(date, date + 5, 5) + @reservation_tracker.create_reservation(date, date + 5, 10) + @reservation_tracker.create_reservation(date + 10, date + 25, 15) + @reservation_tracker.rooms_not_reserved(date, date + 5).must_equal [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + end + end +end diff --git a/spec/room_spec.rb b/spec/room_spec.rb new file mode 100644 index 000000000..190c1defb --- /dev/null +++ b/spec/room_spec.rb @@ -0,0 +1,15 @@ +require 'simplecov' +SimpleCov.start +require_relative 'spec_helper' + + +describe 'Initialize' do + it 'takes a room number' do + room_number = 1 + room = Room.new(room_number) + + expect(room).must_respond_to :room_number + expect(room.room_number).must_equal room_number + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4d1e3fdc8..3b732f2ae 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,6 @@ +require 'simplecov' +SimpleCov.start + require 'minitest' require 'minitest/autorun' require 'minitest/reporters' @@ -6,3 +9,8 @@ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # Require_relative your lib files here! +require_relative '../lib/hotel' +require_relative '../lib/room' +require_relative '../lib/reservation_tracker' +require_relative '../lib/reservation' +require_relative '../lib/block'