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..9a8e84475 --- /dev/null +++ b/design-activity.md @@ -0,0 +1,73 @@ + +1. What classes does each implementation include? Are the lists the same? + + +Yes both implementations have the same classes: CartEntry, ShoppingCart and Order. + +2. Write down a sentence to describe each class. + +For Implementation A: +CartEntry assigns the variables unit_price and quantity and gives access to them from outside the class. +ShoppingCart is in charge of the entries in the cart and gives access to it from outside. +Order instantiate a new instance of ShoppingCart and it calculates all the entries in it making use of the constant SALES_TAX. + +For Implementation B: +CartEntry assigns the variables unit_price and quantity, but also calculates the price based on unit_price and quantity. +ShoppingCart is in charge of the entries but also calculates the price for all entries. +Order creates a new instance of Shopping Cart and calculates the total_price by using SALES_TAX and the method price from the ShoppingCart. + +3. How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. + +In Implementation A: +ShoppingCart stores instances of entries and then Order has an instance of shopping cart and calculates the price of each entry. + +In Implementation B: +ShoppingCart also gets the price of every entry and adds them to get the sum of the shopping cart. Then the Order gets an instance of ShoppingCart and calculates the total_price with tax. + +4. What data does each class store? How (if at all) does this differ between the two implementations? + + In implementation A only the class Order has the responsibility of calculating total_cost. Order does all the work in this implementation. + + In implementation B CartEntry calculates price, ShoppingCart returns sum and Order returns total_price.So each class does some work + + +5. What methods does each class have? How (if at all) does this differ between the two implementations? +All classes have initialize methods. In implementation A only the class Order has a total_price. +However in implementation B the CartEntry and the ShoppingCart have price methods and the Order has a total_price method. + +In Implementation B each class has their own responsibilities, while in Implementation A class Order does all the work + + + +6. 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? + + In Implementation A the price is retained in Order. + In Implementation B it is delegated to "lower level" classes. + + Does total_price directly manipulate the instance variables of other classes? + Yes for A and No for B. + + +7. If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? + +I believe B will be easier to change, since we could add it to the price method of the class CartEntry. + +8. Which implementation better adheres to the single responsibility principle? + +I think in implementation B by giving more responsibility to the lower classes. + +9. Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? + +In implementation B the classes are more loosely coupled, so class B has less dependency with the other classes. + +>>>>>Revisiting Hotel>>>>>> + +1. What is this class's responsibility? + You should be able to describe it in a single sentence. +2. Is this class responsible for exactly one thing? +3. Does this class take on any responsibility that should be delegated to "lower level" classes? +4. Is there code in other classes that directly manipulates this class's instance variables? + + +I have not done Wave 3 of Hotel, but I only have 3 classes and I believe they all have their own responsibilities, and I should not have to delegate to other "lower level" classes. My frontdesk and reservation classes do all the work, but i feel like they both handle their own responsibilities. diff --git a/hotel b/hotel new file mode 100644 index 000000000..e69de29bb diff --git a/lib/frontdesk.rb b/lib/frontdesk.rb new file mode 100644 index 000000000..673681737 --- /dev/null +++ b/lib/frontdesk.rb @@ -0,0 +1,59 @@ +require 'date' +require 'pry' +module Hotel + class FrontDesk + + ROOM_PRICE = 200 + attr_accessor :rooms, :reservations + + def initialize + @rooms = create_rooms + @reservations = [] + end + + def make_reservation(guest, check_in, check_out) + avail_rooms = get_available_rooms(check_in, check_out) + if avail_rooms.empty? + raise StandardError.new("No room is available for this date range") + else + reservation = Hotel::Reservation.new(guest,check_in, check_out, avail_rooms.sample) + @reservations << reservation + end + return reservation + end + + def get_available_rooms(date_begin, date_end) + available_rooms = @rooms.clone + + @reservations.each do |res| + overlap = (res.check_in...res.check_out).to_a & (date_begin...date_end).to_a + + if overlap[0] != nil + available_rooms.delete(res.room) + end + end + return available_rooms + end + + def get_reservation_by_date(date) + reservations_by_date = [] + @reservations.each do |res| + if res.include_date?(date) + reservations_by_date << res + end + end + return reservations_by_date + end + + private + + def create_rooms + rooms = [] + (1..20).each do |num| + rooms << Room.new(num) + end + return rooms + end + + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..2211ea141 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,44 @@ +require 'date' + +module Hotel + class Reservation + + attr_reader :guest, :check_in, :check_out, :room, :total_nights + + def initialize(guest, check_in, check_out, room) + @room = room + @check_in = validate_date(check_in) + @check_out = validate_date(check_out) + @guest = guest + @total_nights = calculate_length + end + + def include_date?(date) + date.between?(@check_in, @check_out - 1) + end + + def total_cost + @total_nights * 200 + end + + private + + def validate_date(date) + if date.is_a? (Date) + return date + else + raise ArgumentError.new "Invalid date" + end + end + + def calculate_length + if @check_in >= @check_out + raise ArgumentError.new "invalid dates" + else + length = @check_out - @check_in + return length.to_i + end + end + + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..8bd3956aa --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,23 @@ +require 'date' +module Hotel + class Room + attr_reader :number + attr_accessor :rate + + def initialize (number) + @number = check_room_number(number) + @rate = 200 + end + + private + + def check_room_number(number) + if number.is_a?(Integer) == false + raise ArgumentError.new "Room number must be an integer" + else + @number = number + end + end + + end +end diff --git a/specs/frontdesk_spec.rb b/specs/frontdesk_spec.rb new file mode 100644 index 000000000..9d29e731f --- /dev/null +++ b/specs/frontdesk_spec.rb @@ -0,0 +1,115 @@ +require_relative 'spec_helper' + +describe "Hotel::FrontDesk" do + before do + @guest = "Tom Hank" + end + describe "initialize" do + it "is an instance of FrontDesk" do + reservation = Hotel::FrontDesk.new.must_be_kind_of Hotel::FrontDesk + end + + it "establishes the base data structures when instantiated" do + reservation = Hotel::FrontDesk.new + end + + it "creates instance of rooms" do + rooms_in_hotel = Hotel::FrontDesk.new.rooms + rooms_in_hotel.must_be_instance_of Array + rooms_in_hotel.each do |room| + room.must_be_instance_of Hotel::Room + end + end + it "creates 20 rooms" do + rooms_in_hotel = Hotel::FrontDesk.new.rooms + rooms_in_hotel.length.must_equal 20 + end + + it "creates an empty array of reservations" do + reservations_in_hotel = Hotel::FrontDesk.new.reservations + reservations_in_hotel.must_be_kind_of Array + reservations_in_hotel.length.must_equal 0 + end + end + + describe "make_reservation" do + it "creates a reservation" do + frontdesk = Hotel::FrontDesk.new + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + new_res = frontdesk.make_reservation(@guest, check_in, check_out) + new_res.must_be_instance_of Hotel::Reservation + end + + it "adds a new reservation to the reservations array" do + frontdesk = Hotel::FrontDesk.new + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + frontdesk.make_reservation(@guest, check_in, check_out) + frontdesk.reservations.length.must_equal 1 + frontdesk.reservations.last.must_be_instance_of Hotel::Reservation + end + + it "uses check_in and check_out dates for reservation" do + # the dates on the reservation are the same ones that we passed into a method" + end + + it "assigns a room" do + #checks that the reservation instance has a room + end + + it "raises a StandardError when they are no available rooms left" do + frontdesk = Hotel::FrontDesk.new + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + + 20.times do + frontdesk.make_reservation(@guest, check_in, check_out) + end + proc{frontdesk.make_reservation(@guest, check_in, check_out)}.must_raise StandardError + end + end + + describe "validate_date" do + it "raises an ArgumentError when date is not in a valid format" do + + end + end + + describe "get_available_rooms" do + it "gets a list of available rooms" do + fd = Hotel::FrontDesk.new + date_begin = Date.new(2018, 03, 16) + date_end = Date.new(2018, 03, 19) + fd.make_reservation(@guest, date_begin, date_end) + rooms = fd.get_available_rooms(date_begin - 3, date_end- 1) + rooms.must_be_kind_of Array + end + end + + describe "get_reservation_by_date" do + it "gets a list of reservations for a particular date" do + fd = Hotel::FrontDesk.new + date_begin = Date.new(2018, 03, 16) + date_end = Date.new(2018, 03, 19) + fd.make_reservation(@guest, date_begin, date_end) + date_begin = Date.new(2018, 03, 14) + date_end = Date.new(2018, 03, 17) + fd.make_reservation(@guest, date_begin, date_end) + res = fd.get_reservation_by_date(Date.new(2018, 03, 16)) + res.length.must_equal 2 + end + + it "returns an empty array when there are no overlaping reservation" do + fd = Hotel::FrontDesk.new + date_begin = Date.new(2018, 03, 16) + date_end = Date.new(2018, 03, 19) + fd.make_reservation(@guest, date_begin, date_end) + date_begin = Date.new(2018, 03, 14) + date_end = Date.new(2018, 03, 17) + fd.make_reservation(@guest, date_begin, date_end) + res = fd.get_reservation_by_date(Date.new(2018, 03, 19)) + res.length.must_equal 0 + end + end +end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb new file mode 100644 index 000000000..19ffd27d3 --- /dev/null +++ b/specs/reservation_spec.rb @@ -0,0 +1,73 @@ +require_relative 'spec_helper' + +describe Hotel:: Reservation do + before do + @guest = "Tom Hank" + end + describe "can create a reservation instance" do + it "can be created" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 20) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(1)) + reservation.must_be_instance_of Hotel::Reservation + end + + it "raises an Argument Error for nil check_in" do + check_in = nil + check_out = Date.new(2018, 03, 20) + + error = proc{Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(1))}.must_raise ArgumentError + error.message.must_equal "Invalid date" + + end + + it "raises an Argument Error for nil check_out" do + check_in = Date.new(2018, 03, 16) + check_out = nil + + error = proc{Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(1))}.must_raise ArgumentError + error.message.must_equal "Invalid date" + end + + it "accurately calculates the length of a stay of 1 night" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(3)) + reservation.total_nights.must_equal 3 + end + end + + describe "include_date?" do + it "returns true if the date is between check_in and check_out" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(3)) + date = Date.new(2018, 03, 17) + reservation.include_date?(date).must_equal true + end + it "returns false if the date is not between check_in and check_out" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(3)) + date = Date.new(2018, 03, 20) + reservation.include_date?(date).must_equal false + + end + end + + describe "total_cost" do + it "calculates the total cost of one night" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 17) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(3)) + reservation.total_cost.must_equal 200 + end + + it "calculates the total cost of multiple nights" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + reservation = Hotel::Reservation.new(@guest, check_in, check_out, Hotel::Room.new(3)) + reservation.total_cost.must_equal 600 + end + end +end diff --git a/specs/room_spec.rb b/specs/room_spec.rb new file mode 100644 index 000000000..2265c9217 --- /dev/null +++ b/specs/room_spec.rb @@ -0,0 +1,19 @@ +require_relative 'spec_helper' + +describe Hotel::Room do + describe "Can create a room instance" do + it "can be created" do + room = Hotel::Room.new(1) + room.must_be_instance_of Hotel::Room + end + + it "raises an ArgumentError for invalid parameters" do + proc{Hotel::Room.new("A76")}.must_raise ArgumentError + end + + it "initializes with a cost of $200 per night" do + room = Hotel::Room.new (7) + room.rate.must_equal 200 + end + end +end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 000000000..d375a0f3b --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,15 @@ +require 'simplecov' +SimpleCov.start +require 'pry' +require 'date' +require 'minitest' +require 'minitest/autorun' + +require 'minitest/reporters' +require 'minitest/skip_dsl' +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +# Require_relative your lib files here! +require_relative '../lib/room' +require_relative '../lib/frontdesk' +require_relative '../lib/reservation'