diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..99d10a176 Binary files /dev/null and b/.DS_Store differ 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..cef5c40bc --- /dev/null +++ b/design-activity.md @@ -0,0 +1,43 @@ + +###Questions + +- What classes does each implementation include? Are the lists the same? + - Both implementations have CartEntry, ShoppingCart, and Order. The lists are the same. + +- Write down a sentence to describe each class. + - The CartEntry class initializes the item in the cart using a unit price and quantity. In implementation b, it also defines the price. + - The ShoppingCart class initializes the cart using the collection of entries In implementation b, it also defines the price. + - The Order class initializes the order and defines the method of finding the total price. + + +- How do the classes relate to each other? It might be helpful to draw a diagram on a whiteboard or piece of paper. + + +- What **data** does each class store? How (if at all) does this differ between the two implementations? + - CartEntry, ShoppingCart, and Order each has the initialize method. Order also has the total_price method. + - In implementation B, CartEntry and ShoppingCart also have a price method. + + +- What **methods** does each class have? How (if at all) does this differ between the two implementations? +- CartEntry has unit_price, quantity. ShoppingCart stores entries. Order stores total_price and cart. +- In implementation B, CartEntry and ShoppingCart also store price. + + +- 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? + + +- If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? + + +- Which implementation better adheres to the single responsibility principle? + + +- Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? + + + + Based on the answers to the above questions, 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. diff --git a/lib/block.rb b/lib/block.rb new file mode 100644 index 000000000..74d6fd8fc --- /dev/null +++ b/lib/block.rb @@ -0,0 +1,38 @@ +# require 'time' +# require_relative 'reservation' +# require_relative 'hotel_admin' +# +# module Hotel +# class Block +# attr_reader :block_start_date, :block_end_date, :room_amt +# +# def initialize(input) +# +# if input[:block_start_date] == nil || input[:block_end_date] == nil +# raise ArgumentError.new("Date must be a date.") +# end +# +# if input[:block_start_date] >= input[:block_end_date] +# raise ArgumentError.new("End date must be after start date.") +# end +# +# if input[:room_amt] > 5 || input[:room_amt] < 1 +# raise ArgumentError.new("Rooms must be between 1 and 5.") +# end +# +# # if input[:room_num] > Hotel::HotelAdmin.view_available_rooms(input[:block_start_date], input[:block_end_date]) +# # raise ArgumentError.new("There are not enough rooms available during that time.") +# # end +# +# if input[:room_amt] > 5 || input[:room_amt] < 1 +# raise ArgumentError.new("Rooms must be between 1 and 5.") +# end +# +# @block_start_date = input[:block_start_date] +# @block_end_date = input[:block_end_date] +# @room_num = input[:room_amt] +# end +# +# +# end +# end diff --git a/lib/hotel_admin.rb b/lib/hotel_admin.rb new file mode 100644 index 000000000..95468023a --- /dev/null +++ b/lib/hotel_admin.rb @@ -0,0 +1,116 @@ +require 'time' +require_relative 'reservation' + +module Hotel + class HotelAdmin + attr_reader :rooms, :reservations, :reservation, :block_start_date, :block_end_date, :room_amt, :blocks, :block, :rooms_held, :block_name + attr_accessor :status + + def initialize + @rooms = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] + @reservations = [] + @blocks = [] + @holds_reservations = [] + @rooms_held = [] + end + + def add_reservation(reservation) + #note: this is tightly coupled, I think. + @reservations << reservation + @reservation = reservation + return @reservation + end + + def calc_reservations(reservation) + reservation.price_night * reservation.calc_duration + end + + def access_reservations(day) + days_reservations = [] + @reservations.each do |reservation| + if (reservation.checkin_date...reservation.checkout_date).include?(day) + days_reservations << reservation + end + end + return days_reservations + end + + def view_available_rooms(checkin_request, checkout_request) + arr_available_rooms = @rooms.map{ |room| room } + date_range_requested = (checkin_request...checkout_request).map{ |date| date} + @reservations.each do |res| + date_range_requested.each do |day_requested| + if (res.checkin_date...res.checkout_date).include?(day_requested) + arr_available_rooms.delete(res.room_id) + end + end + end + return arr_available_rooms + end + + def reserve_available_room(checkin_request, checkout_request) + avail_room = view_available_rooms(checkin_request, checkout_request)[0] + add_reservation(Hotel::Reservation.new({checkin_date: checkin_request, checkout_date: checkout_request, room_id: avail_room})) + end + + def create_block(input) + + if input[:block_start_date] == nil || input[:block_end_date] == nil + raise ArgumentError.new("Date must be a date.") + end + + if input[:block_start_date] >= input[:block_end_date] + raise ArgumentError.new("End date must be after start date.") + end + + if input[:room_amt] > 5 || input[:room_amt] < 1 + raise ArgumentError.new("Rooms must be between 1 and 5.") + end + + if input[:room_amt] > 5 || input[:room_amt] < 1 + raise ArgumentError.new("Rooms must be between 1 and 5.") + end + + if input[:room_amt] > view_available_rooms(input[:block_start_date], input[:block_end_date]).count + raise ArgumentError.new("There are not enough rooms available during that time.") + end + @block_start_date = input[:block_start_date] + @block_end_date = input[:block_end_date] + @room_amt = input[:room_amt] + @block_name = input[:group_name] + @blocks << input + @block = input + input[:room_amt].times do |i| + avail_room = view_available_rooms(input[:block_start_date], input[:block_end_date])[0] + new_res = add_reservation(Hotel::Reservation.new({checkin_date: input[:block_start_date], checkout_date: input[:block_end_date], room_id: avail_room, status: "held", group_name: input[:group_name]})) + @rooms_held << avail_room + @holds_reservations << new_res + end + return @block + end + + def return_blocks_rooms(input) + @rooms_held + end + + def return_held_reservations + @holds_reservations + end + + def check_block_availability(group_name) + arr_available_held_rooms = [] + @holds_reservations + @holds_reservations.each do |res| + if res.status == "held" && res.party_name == group_name + arr_available_held_rooms << res + end + end + return arr_available_held_rooms + end + + def reserve_room_in_block(group_name) + block_res = check_block_availability(group_name)[0] + block_res.status = "booked" + end + end +end diff --git a/lib/reservation.rb b/lib/reservation.rb new file mode 100644 index 000000000..ecae74706 --- /dev/null +++ b/lib/reservation.rb @@ -0,0 +1,35 @@ +require 'time' + +module Hotel + class Reservation + attr_reader :checkin_date, :checkout_date, :room_id, :price_night, :party_name + attr_accessor :status + + def initialize(input) + + if input[:checkin_date] == nil || input[:checkout_date] == nil + raise ArgumentError.new("Date must be a date.") + end + + if input[:checkin_date] >= input[:checkout_date] + raise ArgumentError.new("End date must be after start date.") + end + + @checkin_date = input[:checkin_date] + @checkout_date = input[:checkout_date] + @room_id = input[:room_id] + @price_night = 200 + @status = input[:status] + @party_name = input[:group_name] + end + + def status + return @status + end + + def calc_duration + ((checkin_date...checkout_date).map{ |date| date}).count + end + + end +end diff --git a/lib/room.rb b/lib/room.rb new file mode 100644 index 000000000..30db78833 --- /dev/null +++ b/lib/room.rb @@ -0,0 +1,16 @@ +# +# module Hotel +# class Room +# attr_reader :room_num, :cost +# COST = 200 +# +# def initialize(room_num) +# @room_num = room_num +# end +# +# # def price +# # @cost = COST +# # end +# +# end +# end diff --git a/specs/block_spec.rb b/specs/block_spec.rb new file mode 100644 index 000000000..9650ce095 --- /dev/null +++ b/specs/block_spec.rb @@ -0,0 +1,71 @@ +# require_relative 'spec_helper' +# +# +# describe "Block class" do +# +# describe "Initializer" do +# before do +# @block = Hotel::Block.new({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_num: 5}) +# end +# +# it "is an instance of Hotel::Block" do +# @block.must_be_kind_of Hotel::Block +# end +# +# it "returns an error if block attempt has more than 5 rooms" do +# proc{ Hotel::Block.new({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_num: 6})}.must_raise ArgumentError +# end +# +# it "returns an error if block attempt has less than 1 room" do +# proc{ Hotel::Block.new({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_num: 0})}.must_raise ArgumentError +# end +# +# it "throws an argument error for a bad end date" do +# proc{ Hotel::Block.new({block_start_date: "2015-07-20", block_end_date: nil }) }.must_raise ArgumentError +# end +# +# it "throws an argument error for a bad start date" do +# proc{ Hotel::Block.new({block_start_date: nil, block_end_date: "2015-07-20", room_num: 3 }) }.must_raise ArgumentError +# end +# +# +# it "throws an argument error when end date isn't after start date" do +# proc{ Hotel::Block.new({block_start_date: "2015-07-20", block_end_date: "2015-07-20", room_num: 3 }) }.must_raise ArgumentError +# proc{ Hotel::Block.new({block_start_date: "2015-07-21", block_end_date: "2015-07-20", room_num: 3 }) }.must_raise ArgumentError +# end +# +# it "can create a block of rooms" do +# +# end +# +# it "only includes rooms that are available for the given date range" do +# +# end +# +# it "excludes rooms in the block for reservations by general public" do +# end +# +# end +# +# # describe "create_block method" do +# # +# # it "" do +# # end +# # +# # end +# +# describe "check_block_availability method" do +# +# it "can check whether a given block has any rooms available" do +# end +# +# end +# +# describe "reserve_room_in_block" do +# +# it "can reserve a room from within a block of rooms" do +# end +# +# end +# +# end diff --git a/specs/hotel_admin_spec.rb b/specs/hotel_admin_spec.rb new file mode 100644 index 000000000..0e095becf --- /dev/null +++ b/specs/hotel_admin_spec.rb @@ -0,0 +1,178 @@ +require_relative 'spec_helper' + +describe "HotelAdmin class" do + describe "Initializer" do + before do + @hotel_admin = Hotel::HotelAdmin.new + end + + it "is an instance of HotelAdmin" do + @hotel_admin.must_be_kind_of Hotel::HotelAdmin + end + + it "establishes the base data structures when instantiated" do + [:rooms, :reservations].each do |i| + @hotel_admin.must_respond_to i + end + @hotel_admin.rooms.must_be_kind_of Array + @hotel_admin.reservations.must_be_kind_of Array + end + + it "lists of all of the rooms in the hotel" do + @hotel_admin.rooms.must_equal [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] + @hotel_admin.rooms.first.must_equal 1 + @hotel_admin.rooms.last.must_equal 20 + end + end + + describe "dates reservations method" do + before do + @hotel_admin = Hotel::HotelAdmin.new + end + + it "can reserve a room for a given date range" do + new_res = @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + @hotel_admin.reservations.must_include new_res + end + + it "returns a list of reservations for a specific date" do + reservation_a = @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + reservation_b = @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-22", checkout_date: "2015-07-24", room_id: 2})) + @hotel_admin.access_reservations("2015-07-21").must_equal [reservation_a] + @hotel_admin.access_reservations("2015-07-22").must_equal [reservation_a, reservation_b] + @hotel_admin.access_reservations("2015-07-19").must_equal [] + @hotel_admin.access_reservations("2015-07-25").must_equal [] + end + end + + describe "calculates reservation cost" do + it "calculates the cost of a given reservation" do + hotel_admin = Hotel::HotelAdmin.new + reservation = hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + hotel_admin.calc_reservations(reservation).must_equal 1000 + hotel_admin.calc_reservations(reservation).must_be_instance_of Integer + end + end + + describe "can view a list of rooms that are not reserved for a given date range" do + it "can test 1" do + hotel_admin = Hotel::HotelAdmin.new + hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-22", checkout_date: "2015-07-24", room_id: 2})) + hotel_admin.view_available_rooms("2015-07-20", "2015-07-23").must_equal [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + end + end + + describe "can reserve an available room for a given date range" do + it "" do + hotel_admin = Hotel::HotelAdmin.new + hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-22", checkout_date: "2015-07-24", room_id: 2})) + new_res = hotel_admin.reserve_available_room("2015-07-20", "2015-07-23") + new_res.must_be_kind_of Hotel::Reservation + new_res.checkin_date.must_equal "2015-07-20" + new_res.checkout_date.must_equal "2015-07-23" + new_res.room_id.must_equal 3 + end + end + + describe "creates blocks" do + before do + @hotel_admin = Hotel::HotelAdmin.new + end + + #@block = Hotel::Block.new({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_amt: 5}) + + it "returns an error if block attempt has more than 5 rooms" do + proc{ @hotel_admin.create_block({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_amt: 6})}.must_raise ArgumentError + end + + it "returns an error if block attempt has more than 5 rooms" do + proc{ @hotel_admin.create_block({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_amt: 6})}.must_raise ArgumentError + end + + it "returns an error if block attempt has less than 1 room" do + proc{ @hotel_admin.create_block({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_amt: 0})}.must_raise ArgumentError + end + + it "throws an argument error for a bad end date" do + proc{ @hotel_admin.create_block({block_start_date: "2015-07-20", block_end_date: nil }) }.must_raise ArgumentError + end + + it "throws an argument error for a bad start date" do + proc{ @hotel_admin.create_block({block_start_date: nil, block_end_date: "2015-07-20", room_amt: 3 }) }.must_raise ArgumentError + end + + it "throws an argument error when end date isn't after start date" do + proc{ @hotel_admin.create_block({block_start_date: "2015-07-20", block_end_date: "2015-07-20", room_amt: 3 }) }.must_raise ArgumentError + proc{ @hotel_admin.create_block({block_start_date: "2015-07-21", block_end_date: "2015-07-20", room_amt: 3 }) }.must_raise ArgumentError + end + + it "throws an argument error when there aren't enough rooms available" do + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 2})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 3})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 4})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 5})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 6})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 7})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 8})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 9})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 10})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 11})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 12})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 13})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 14})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 15})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 16})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 17})) + proc{ @hotel_admin.create_block({block_start_date: "2015-07-23", block_end_date: "2015-07-29", room_amt: 5}) }.must_raise ArgumentError + end + + it "can create a block of rooms" do + block_new = @hotel_admin.create_block({block_start_date: "2015-07-26", block_end_date: "2015-07-29", room_amt: 4}) + @hotel_admin.create_block({block_start_date: "2015-07-20", block_end_date: "2015-07-25", room_amt: 3}) + block_new.must_be_instance_of Hash + block_new[:room_amt].must_be_instance_of Integer + block_new[:block_start_date].must_equal "2015-07-26" + @hotel_admin.blocks.must_be_instance_of Array + @hotel_admin.blocks.count.must_equal 2 + end + + it "excludes rooms in the block for reservations by general public" do + @hotel_admin.create_block({block_start_date: "2015-07-26", block_end_date: "2015-07-29", room_amt: 5}) + new_res = @hotel_admin.reserve_available_room("2015-07-26", "2015-07-27") + new_res.room_id.must_equal 6 + end + + it "can check whether a given block has any rooms available" do + @hotel_admin.create_block({block_start_date: "2015-09-20", block_end_date: "2015-09-25", room_amt: 4, group_name: "Prince"}) + @hotel_admin.check_block_availability("Prince").must_be_instance_of Array + @hotel_admin.check_block_availability("Prince").length.must_equal 4 + end + + it "can reserve a room from within a block of rooms" do + new_hold = @hotel_admin.create_block({block_start_date: "2015-07-26", block_end_date: "2015-07-29", room_amt: 5, group_name: "Bowie"}) + block_res = @hotel_admin.reserve_room_in_block("Bowie") + puts "XXXXXXXXXxxxxxxxxxx block_res xxxxxxxxxxXXXXXXXXX" + block_res.class.must_be_kind_of Hotel::Reservation + block_res.status.must_equal "booked" + end + + end + + describe "return_blocks_rooms method" do + before do + @hotel_admin = Hotel::HotelAdmin.new + end + + it "only includes rooms that are available for the given date range" do + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1})) + @hotel_admin.add_reservation(Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 2})) + new_hold = @hotel_admin.create_block({block_start_date: "2015-07-23", block_end_date: "2015-07-29", room_amt: 5}) + @hotel_admin.return_blocks_rooms(new_hold).count.must_equal 5 + @hotel_admin.return_blocks_rooms(new_hold).must_equal [3, 4, 5, 6, 7] + end + end + +end diff --git a/specs/reservation_spec.rb b/specs/reservation_spec.rb new file mode 100644 index 000000000..36f8ca2f6 --- /dev/null +++ b/specs/reservation_spec.rb @@ -0,0 +1,34 @@ +require_relative 'spec_helper' + +describe "Reservation class" do + describe "Initializer" do + + it "is an instance of Reservation" do + reservation = Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25", room_id: 1}) + reservation.must_be_kind_of Hotel::Reservation + reservation.class.must_equal Hotel::Reservation + end + + it "throws an argument error for a bad start date" do + proc{ Hotel::Reservation.new({checkin_date: nil, checkout_date: "2015-07-20" }) }.must_raise ArgumentError + end + + it "throws an argument error for a bad end date" do + proc{ Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: nil }) }.must_raise ArgumentError + end + + it "throws an argument error when end date isn't after start date" do + proc{ Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-20" }) }.must_raise ArgumentError + proc{ Hotel::Reservation.new({checkin_date: "2015-07-21", checkout_date: "2015-07-20" }) }.must_raise ArgumentError + end + end + + describe "calc_duration method" do + it "calculates the number of nights for the hotel stay" do + @reservation = Hotel::Reservation.new({checkin_date: "2015-07-20", checkout_date: "2015-07-25"}) + @reservation.calc_duration.must_equal 5 + @reservation.calc_duration.must_be_kind_of Integer + end + end + +end diff --git a/specs/room_spec.rb b/specs/room_spec.rb new file mode 100644 index 000000000..ac4d93d8c --- /dev/null +++ b/specs/room_spec.rb @@ -0,0 +1,26 @@ +# require_relative 'spec_helper' +# +# describe "Room class" do +# +# describe "Initializer" do +# it "Can be created" do +# room = Hotel::Room.new("unit_name") +# room.must_be_instance_of Hotel::Room +# end +# +# it "Is an instance of Room" do +# room = Hotel::Room.new("unit_name") +# room.class.must_equal Hotel::Room +# end +# +# end +# +# describe "price method" do +# it "returns price" do +# room = Hotel::Room.new("unit_name") +# room.price.must_equal 200 +# room.price.must_be_kind_of Integer +# end +# end +# +# end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 000000000..bfe0d866e --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,12 @@ +require 'simplecov' +SimpleCov.start +require 'time' +require 'minitest' +require 'minitest/autorun' +require 'minitest/reporters' + +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new + +require_relative '../lib/hotel_admin' +require_relative '../lib/reservation' +#require_relative '../lib/block'