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..2c9fa900e --- /dev/null +++ b/design-activity.md @@ -0,0 +1,51 @@ +1. What classes does each implementation include? Are the lists the same? + answer: Each implementation include three classes: CartEntry, ShoppingCart, Order. + in implementation A the total price is calculated only in order class and in implementation B the total price is calculated for each class in separate + +2. Write down a sentence to describe each class. + answer: + Implementation A: + class CartEntry: Store the unit price and quantity + class ShoppingCart: Store an array of entries + class Order: Store an instant of ShoppingCart and calculate order (price * quantity) after adding Tax + Implementation B: + class CartEntry: Store the unit price and quantity and calculate (price * quantity) + class ShoppingCart: Store an array of entries and calculate prices for all the entries + class Order: Adding Tax for what was already calculate in ShoppingCart class + +3. How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. +answer: class CartEntry store the unit price and quantity, class ShoppingCart store array of entries and class Order store ShoppingCart object and add Tax + +4. What data does each class store? How (if at all) does this differ between the two implementations? +answer: +Implementation A: +class CartEntry: Store the unit price and quantity +class ShoppingCart: Store an array of entries +class Order: Store an instant of ShoppingCart and calculate order (price * quantity) after adding Tax +Implementation B: +class CartEntry: Store the unit price and quantity and calculate (price * quantity) +class ShoppingCart: Store an array of entries and calculate prices for all the entries +class Order: Adding Tax for what was already calculate in ShoppingCart class + +5. What methods does each class have? How (if at all) does this differ between the two implementations? + answer: + Implementation A: + class CartEntry: initialize + class ShoppingCart: initialize + class Order: initialize, total_price + Implementation B: + class CartEntry: initialize, price + class ShoppingCart: initialize, price + class Order: initialize, total_price + +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? + - Does total_price directly manipulate the instance variables of other classes? + +7. If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? + answer: We will need to use an if statement and it will be easier to modify implementation A + +8. Which implementation better adheres to the single responsibility principle? + answer: Implementation B is better + +9. Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? diff --git a/lib/management.rb b/lib/management.rb new file mode 100644 index 000000000..88b93276e --- /dev/null +++ b/lib/management.rb @@ -0,0 +1,37 @@ +module Hotel + class Management + + # PRICE_PER_NIGHT = 200 + attr_reader :rooms, :price_per_night + + def initialize + @rooms = [] + (1..20).each do |num| + @rooms << Hotel::Room.new(num) + end + @reservations = [] + + end + + def reserve_room(room, check_in, check_out) + reservation = Hotel::Reservation.new(room, check_in, check_out) + @reservations << reservation + return reservation + end + + def get_reservations_for_date(date) + reservations_for_date = [] + @reservations.each do |reservation| + if reservation.dates.include?(date) #check_in == date + reservations_for_date << reservation + end + end + return reservations_for_date + end + + + + + + end # end of class +end # end of module diff --git a/lib/period.rb b/lib/period.rb new file mode 100644 index 000000000..c109d4801 --- /dev/null +++ b/lib/period.rb @@ -0,0 +1,25 @@ +require 'pry' +module Hotel + class Period + + attr_reader :check_in, :check_out + + def initialize(check_in, check_out) + @check_in = check_in + @check_out = check_out + end + + def is_valid? + (@check_out != nil && @check_in != nil) && !(@check_out <= @check_in) + end + + def num_of_nights + return check_out - check_in + end + + def include?(date) + return date >= check_in && date < check_out + end + + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..c0d694f06 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,17 @@ +module Hotel + class Reservation + + attr_reader :room, :dates, :total_cost + def initialize(room, check_in, check_out) + @room = room + @dates = Hotel::Period.new(check_in, check_out) + if !@dates.is_valid? || !@room.is_available(@dates) + raise ArgumentError.new("This dates are not availabe") + end + @room.mark_dates_as_reserved(@dates) + @total_cost = @dates.num_of_nights * @room.price_per_night + end + + + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..df13397a4 --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,38 @@ +module Hotel + class Room + + PRICE_PER_NIGHT = 200 + attr_reader :room_num, :price_per_night + + def initialize(num) + @room_num = num + @price_per_night = PRICE_PER_NIGHT + @reserved_dates = [] + end + + + + def mark_dates_as_reserved(reservation_period) + (reservation_period.check_in...reservation_period.check_out).each do |date| + @reserved_dates << date + end + end + + def is_reserved_for_date(date) + if date != nil + @reserved_dates.include?(date) + else + raise ArgumentError.new("Invalid date") + end + end + + def is_available(query_period) + (query_period.check_in...query_period.check_out).each do |date| + if is_reserved_for_date(date) + return false + end + end + return true + end + end +end diff --git a/specs/management_spec.rb b/specs/management_spec.rb new file mode 100644 index 000000000..b5c3cb754 --- /dev/null +++ b/specs/management_spec.rb @@ -0,0 +1,54 @@ +require_relative 'spec_helper' + +describe 'Management class' do + describe 'initialize' do + it "can be created" do + hotel_management = Hotel::Management.new + hotel_management.must_be_instance_of Hotel::Management + hotel_management.rooms.must_be_kind_of Array + reservations = Hotel::Management.new + reservations.must_be_instance_of Hotel::Management + end + it "can access the list of all of the rooms in the hotel" do + hotel = Hotel::Management.new + hotel.rooms.length.must_equal 20 + end + + it "there should be 20 total rooms" do + hotel = Hotel::Management.new + hotel.rooms.count.must_equal 20 + end + + end + describe 'reserve_room' do + it 'return reservation' do + room = Hotel::Room.new(1) + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 03, 03) + reservation = Hotel::Management.new.reserve_room(room, check_in, check_out) + reservation.dates.check_in.must_equal check_in + reservation.dates.check_out.must_equal check_out + end + + it 'raise an error if room is already reserved' do + room = Hotel::Room.new(1) + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 03, 03) + reservation = Hotel::Management.new.reserve_room(room, check_in, check_out) + proc{ Hotel::Management.new.reserve_room(room, check_in, check_out)}.must_raise ArgumentError + end + end + + describe 'get_reservations_for_date' do + it 'return an array of reservations for date' do + management = Hotel::Management.new + room = Hotel::Room.new(1) + date = Date.new(2018, 03, 02) + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 03, 03) + management.reserve_room(room, check_in, check_out) + management.get_reservations_for_date(date).must_be_kind_of Array + management.get_reservations_for_date(date).length.must_equal 1 + end + end +end diff --git a/specs/period_spec.rb b/specs/period_spec.rb new file mode 100644 index 000000000..955a82ccd --- /dev/null +++ b/specs/period_spec.rb @@ -0,0 +1,69 @@ +require_relative 'spec_helper' + +describe 'Period class' do + + describe 'initialize' do + it 'Can initialize check in and check out' do + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2017, 03, 03) + period = Hotel::Period.new(check_in, check_out) + period.check_in.must_equal check_in + period.check_out.must_equal check_out + period.must_be_instance_of Hotel::Period + end + + it "is an instance of Date" do + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2017, 03, 03) + check_in.must_be_kind_of Date + check_out.must_be_kind_of Date + end + end + + describe 'num_of_nights' do + it "accurately calculates the number of night" do + check_in = Date.new(2018, 03, 16) + check_out = Date.new(2018, 03, 19) + period = Hotel::Period.new(check_in, check_out) + period.num_of_nights.must_equal 3 + end + end + + describe "is_valid?" do + it 'cannot have negative number of days' do + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 02, 25) + Hotel::Period.new(check_in, check_out).is_valid?.must_equal false + end + + it 'cannot have 0 days' do + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 03, 01) + Hotel::Period.new(check_in, check_out).is_valid?.must_equal false + end + + it 'valid dates' do + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 03, 03) + Hotel::Period.new(check_in, check_out).is_valid?.must_equal true + end + end + + describe 'include?' do + it "returns true if the date is between check_in and check_out" do + check_in = Date.new(2018, 03, 01) + check_out = Date.new(2018, 03, 04) + period = Hotel::Period.new(check_in, check_out) + date = Date.new(2018, 03, 03) + period.include?(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, 01) + check_out = Date.new(2018, 03, 04) + period = Hotel::Period.new(check_in, check_out) + date = Date.new(2018, 03, 05) + period.include?(date).must_equal false + end + end +end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb new file mode 100644 index 000000000..32150120c --- /dev/null +++ b/specs/reservation_spec.rb @@ -0,0 +1,42 @@ +require_relative 'spec_helper' + +describe 'Reservation class' do + + describe 'initialize' do + it 'can be created' do + room = Hotel::Room.new(1) + check_in = Date.new(2018,03,01) + check_out = Date.new(2018,03,03) + dates = Hotel::Period.new(check_in, check_out) + reservation = Hotel::Reservation.new(room, check_in, check_out) + room.must_be_instance_of Hotel::Room + dates.must_be_instance_of Hotel::Period + end + it 'can calculate the total cost' do + reservation = Hotel::Reservation.new(Hotel::Room.new(1), Date.new(2018,03,01), Date.new(2018,03,03)) + room = Hotel::Room.new(1) + dates = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(dates) + price_per_night = room.price_per_night + total_cost = dates.num_of_nights * price_per_night + reservation.total_cost.must_equal 400 + end + it 'raise an error if the date range is not availabe' do + room = Hotel::Room.new(1) + dates = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + reservation = Hotel::Reservation.new(room, Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(dates) + proc{ Hotel::Reservation.new(room, Date.new(2018,03,01), Date.new(2018,03,02))}.must_raise ArgumentError + + # new_dates = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,02)) + end + + it 'raise an error if the date is in_valid' do + room = Hotel::Room.new(1) + dates = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,01)) + reservation = Hotel::Reservation.new(room, Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(dates) + proc{ Hotel::Reservation.new(room, Date.new(2018,03,01), Date.new(2018,03,02))}.must_raise ArgumentError + end + end +end diff --git a/specs/room_spec.rb b/specs/room_spec.rb new file mode 100644 index 000000000..679afe6a6 --- /dev/null +++ b/specs/room_spec.rb @@ -0,0 +1,68 @@ +require_relative 'spec_helper' +require 'pry' + +describe 'Room class' do + describe 'initialize' do + it 'can create an instance of a room' do + room = Hotel::Room.new(1) + room.must_be_instance_of Hotel::Room + room.room_num.must_equal 1 + end + end + + describe 'mark_dates_as_reserved' do + it 'should not mark the date as reserved' do + room = Hotel::Room.new(1) + period = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(period) + period2 = Hotel::Period.new(Date.new(2018,03,02), Date.new(2018,03,04)) + room.is_available(period2).must_equal false + end + it 'it should mark the date as reserved' do + room = Hotel::Room.new(1) + period = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(period) + period2 = Hotel::Period.new(Date.new(2018,03,03), Date.new(2018,03,05)) + room.is_available(period2).must_equal true + end + end + + describe 'is_reserved_for_date' do + it 'raise an error if the date is nil' do + room = Hotel::Room.new(1) + date = nil + proc {room.is_reserved_for_date(date)}.must_raise ArgumentError + end + it 'should return false if the date is include in the array' do + room = Hotel::Room.new(1) + period = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(period) + date = Date.new(2018,02,02) + room.is_reserved_for_date(date).must_equal false + end + it 'should return true if the date is include in the array' do + room = Hotel::Room.new(1) + period = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(period) + date = Date.new(2018,03,02) + room.is_reserved_for_date(date).must_equal true + end + end + + describe 'is_available' do + it 'return true if the range of dates are available' do + room = Hotel::Room.new(1) + period = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(period) + period2 = Hotel::Period.new(Date.new(2018,03,03), Date.new(2018,03,05)) + room.is_available(period2).must_equal true + end + it 'return false if the range of dates are not available' do + room = Hotel::Room.new(1) + period = Hotel::Period.new(Date.new(2018,03,01), Date.new(2018,03,03)) + room.mark_dates_as_reserved(period) + period2 = Hotel::Period.new(Date.new(2018,03,02), Date.new(2018,03,05)) + room.is_available(period2).must_equal false + end + end +end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 000000000..0a20f5a8c --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,18 @@ +require 'simplecov' +SimpleCov.start do + add_filter "/specs/" +end + +require 'minitest' +require 'minitest/autorun' +require 'minitest/reporters' +require 'awesome_print' +require 'date' +# Add simplecov + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +require_relative '../lib/room' +require_relative '../lib/reservation' +require_relative '../lib/period' +require_relative '../lib/management'