Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8f9a474
Ignore git file for converage
Lindseyls Mar 5, 2018
9f24be4
All the ruby and spec files have been created for my hotel project.
Lindseyls Mar 5, 2018
7f3a1d8
Created the instance initialize method and also the test for the method.
Lindseyls Mar 7, 2018
a567f32
Revised room.rb and room_spec.rb to only list the iindividual rooms a…
Lindseyls Mar 8, 2018
13943bb
updated the spec_helper file to include the updated SimpleCov code an…
Lindseyls Mar 8, 2018
9c841ad
Updated room spec to include a test on the rate method.
Lindseyls Mar 9, 2018
cb63646
Updated the initialize method for the admin rile and admin spec file.
Lindseyls Mar 9, 2018
ad28157
Udpated the initialize for reservation Ruby filea and spec file.
Lindseyls Mar 9, 2018
ad460da
Updated the spec file for the Reservation class.
Lindseyls Mar 9, 2018
1398951
Updated the reservation class and the spec file to raise an ArgumentE…
Lindseyls Mar 10, 2018
98462d3
Updated the Admin spec file and the Admin#add_reservation method to a…
Lindseyls Mar 10, 2018
d415c72
Updated spec file for Admin.
Lindseyls Mar 10, 2018
2f1c8a5
Updated the ArguementError message just a little and also the spec fi…
Lindseyls Mar 10, 2018
4c4de80
Updated the helper methods in the Reservation class. One to help chec…
Lindseyls Mar 10, 2018
24ae7f6
Updated the spec file for Admin#add_reservation method. Also, updated…
Lindseyls Mar 10, 2018
ec2d781
Updated the Admin#list_reservation method and the spec file.
Lindseyls Mar 10, 2018
c1688eb
Checked coverage and noticed the spec file test for raisinge Argument…
Lindseyls Mar 10, 2018
db9bd1c
Updated the Admin class and test to find the available rooms. Also, a…
Lindseyls Mar 11, 2018
1221e05
Updated the spec files so that the coverage is 100%.
Lindseyls Mar 12, 2018
2d3e449
Created and updated design-activity.md. Responded to all the prompts.
Lindseyls Apr 1, 2018
3080b7a
Re-designed the Hotel project with more OOD and single responsiblity.
Lindseyls Apr 2, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
9 changes: 9 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -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
88 changes: 88 additions & 0 deletions design-activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!-- 04/01/18 -->

<!-- 1. What classes does each implementation include? Are the lists the same? -->

Implementation A classes: CartEntry, ShoppingCart and Order
Implementation B classes: CartEntry, ShoppingCart and Order

The list of classes between implementation A and B are the same.

<!-- 2. Write down a sentence to describe each class. -->

Implementation A classes: Both the CartEntry and ShoppingCart classes carry one single responsibility. However, all the logic to carry out those various responsibilities, such as calculating the subtotal and the total, are located in one place inside the Order class. The Order class is the "master" class that is in charge of all behavior, and other classes are only used to store state.
Implementation B classes: The logic has been delegated to "low level" classes, CartEntry and ShoppingCart. The Order class no longer carries all the behavior.

<!-- 3. How do the classes relate to each other? -->

Implementation A classes: The Order class creates an instance of the ShoppingCart class in its initialize. Then in the total_price method it carries out all the logic by finding each entry's unit price and quantity and calculating the subtotal and then the final total with tax included.

Implementation B classes: Each class carries its own singal responsibility. The CartEntry class takes in each entry's quantity and price and calculates that entry's total price. Then, the ShoppingCart class adds up all entries and calculates the subtotal. Finally, the Order class calculates the final total with the sales tax.

<!-- 4. What data does each class store? How (if at all) does this differ between the two implementations? -->

Implementation A classes:
CartEntry - unit_price & quantity
ShoppingCart - all entries
Order - the sales tax, all entries, the price of each entry, the subtotal and the final total with the sales tax

Implementation B classes:
CartEntry - unit_price & quantity and the price of each entry
ShoppingCart - all entries and the subtotal
Order - the sales tax, all entries and the final total with the sales tax

The Order class in implementation A carries all the responsibilities. While the responsibility is spread out evenly between all three classes in implementation B.

<!-- 5. What methods does each class have? How (if at all) does this differ between the two implementations? -->

Implementation A classes:
CartEntry - initialize
ShoppingCart - initialize
Order - initialize and total_price

Implementation B classes:
CartEntry - initialize and price
ShoppingCart - initialize and price
Order - initialize and total_price

The Order class in implementation A carries all the responsibilities. While the responsibility of calculating the prices are spread out evenly between all three classes in implementation B.

<!-- Consider the Order#total_price method. In each implementation: -->

<!-- 6. Is logic to compute the price delegated to "lower level" classes like ShoppingCart and CartEntry, or is it retained in Order? -->

Implementation A classes: the logic to compute the price is retained in Order.

Implementation B classes: the logic to compute the price is delegated to "lower level" classes.

<!-- 7. Does total_price directly manipulate the instance variables of other classes? -->

Implementation A classes: yes

Implementation B classes: no

<!-- 8. If we decide items are cheaper if bought in bulk, how would this change the code? Which implementation is easier to modify? -->

If items are cheaper when bought in bulk, then we could apply a discount to quantities that are equal or over a certain specific amount. This would be easier to modify in implementation B because the of how logic is evenly delegated between each class, the discount can be added in any of the three classes and its methods. Whereas in implementation A, the only class that the logic can go into is the Order class.

<!-- 9. Which implementation better adheres to the single responsibility principle? -->

Implementation B better adheres to the single responsibility principles because each class is responsible for one thing and the dependency the Order class has on the other two classes is very loose.

<!-- 10. Bonus question once you've read Metz ch. 3: Which implementation is more loosely coupled? -->

Continuing off of my previous answer, implementation B is more loosely coupled because if there is a change in one class it will not affect the other two classes. Especially in the Order class. Whereas in implementation A, if any changes were to be done to the first two classes, this could drastically change the code of Order class because it depends on both "low level" classes.


<!-- Hotel Revisited: 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. -->

Based on the answers to the above questions, one of the places in my Hotel project where a class takes on multiple roles is the Admin class. The Admin class has dependency on both the Room and Reservation class. Also, the private method to check the date range and to see if the date inputs are valid are repeated in both the Admin and Reservation class.

Here are the steps I took to make my Hotel project more Object Oriented with single responsibilities:

-> Created new files date_range.rb and date_range_spec.rb
-> Created the DateRange class to remove the date_range method from the Reservation and Admin classes. Also, the date_check method, which stayed as a private method.
-> Updated the Reservation class to not be dependent on the Room class and created instance variables, rate.
-> Updated the Admin class to not be dependent of the Room class.
-> Updated the specs accordingly and made sure the dependency is as loose as possible.

Afte the re-design that I made, I believe the logic is no longer heavily within Admin class. Although the logic could be spread out more evenly onto the Room class. The Admin class is now only dependent on the Revservation class. Maybe if I am able to add the block function for Hotel wave 3 then I will consider moving more logic into the Room class.
Binary file added lib/.DS_Store
Binary file not shown.
78 changes: 78 additions & 0 deletions lib/admin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
require_relative 'reservation'

module Hotel
class Admin
attr_reader :rooms, :reservations, :room_rate

def initialize
@rooms = (1..20).to_a
@room_rate = 200
@reservations = []
end

def add_reservation(room, check_in, check_out)

# Check that the room exists
unless @rooms.include? room
raise ArgumentError.new("Invalid room number: #{room}")
end

# Check that the room is available
unless available_rooms(check_in, check_out).include? room
raise ArgumentError.new("Room #{room} already has a reservation between #{check_in} and #{check_out}")
end

reservation = Reservation.new(check_in, check_out, room, @room_rate)
@reservations << reservation

return reservation
end

def list_reservations(date)

reserved_list = []

@reservations.each do |reservation|
if reservation.check_in <= date && reservation.check_out >= date
reserved_list << reservation
end
end

return reserved_list

end

def available_rooms(check_in, check_out)

DateRange.new(check_in, check_out)

available_list = []

(check_in...check_out).each do |date|
available_list << empty_rooms(date)
end

available_rooms = available_list[0]
(available_list.length - 1).times do |i|
available_rooms = available_rooms & available_list[i + 1]
end

return available_rooms

end

private

# check to see if the given date input has any avilable empty rooms
def empty_rooms(date)
reservation_list = list_reservations(date)
reserved_rooms = []

reservation_list.each do |reservation|
reserved_rooms << reservation.room
end
return @rooms - reserved_rooms
end

end # end of Admin class
end # end of Hotel module
33 changes: 33 additions & 0 deletions lib/date_range.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module Hotel
class DateRange
class InvalidDateRange < StandardError ; end

attr_reader :check_in, :check_out

def initialize(check_in, check_out)
unless check_out > check_in
raise InvalidDateRange.new("Invalid dates #{check_in} to #{check_out}")
end

@check_in = date_check(check_in)
@check_out = date_check(check_out)
end

def nights
return @check_out - @check_in
end

private

# check to see if the check_in and check_out dates are invalid
def date_check(date)
if date.class == String
date = Date.parse(date)
elsif date.class != Date
raise ArgumentError.new("Invalid date: #{date}. Please enter date in the form of '2018, 03, 10'")
end
return date
end

end
end
18 changes: 18 additions & 0 deletions lib/reservation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require_relative 'date_range'

module Hotel
class Reservation < DateRange
attr_reader :room, :rate

def initialize(check_in, check_out, room, rate)
super(check_in, check_out)
@room = room
@rate = rate
end

def total_cost
return nights * @rate
end

end # end of Reservation class
end # end of Hotel module
20 changes: 20 additions & 0 deletions lib/room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

module Hotel
class Room
attr_reader :room, :rate

def initialize(room, rate)
@room = hotel_room(room)
@rate = rate
end

# raises ArgumentError if the room number is not between 1 and 20
def hotel_room(room)
unless (1..20).include? room
raise ArgumentError.new("Invalid room number: #{room}")
end
@room = room
end

end # end of Room class
end # end of Hotel module
Binary file added specs/.DS_Store
Binary file not shown.
117 changes: 117 additions & 0 deletions specs/admin_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
require_relative 'spec_helper'

describe "Admin class" do

before do
@admin = Hotel::Admin.new
end

describe "initialize" do

it "must be an instance of admin" do
@admin.must_be_instance_of Hotel::Admin
end

it "has access to a list of all 20 rooms" do
@admin.must_respond_to :rooms
@admin.rooms.must_be_kind_of Array
@admin.rooms.length.must_equal 20
end

it "tracks room rate" do
@admin.must_respond_to :room_rate
@admin.room_rate.must_equal 200
end

it "access to reservations" do
@admin.must_respond_to :reservations
@admin.reservations.must_be_kind_of Array
@admin.reservations.length.must_equal 0
end

end # end of describe "initialize"

describe "add_reservation method" do
before do
@check_in = Date.new(2018, 4, 14)
@check_out = Date.new(2018, 4, 21)
@room = 9
@reservation = @admin.add_reservation(@room, @check_in, @check_out)
end

it "creates a instance of reservation with the check_in and check_out dates" do
@reservation.must_be_instance_of Hotel::Reservation
end

it "raises an error for an invalid room" do
[105, -12, "foo"].each do |room|
proc {
@admin.add_reservation(room, @check_in, @check_out)
}.must_raise ArgumentError
end
end

it "raises an error if no rooms are available" do
@admin.add_reservation(10, @check_in, @check_out)

proc {
@admin.add_reservation(10, @check_in, @check_out)
}.must_raise ArgumentError
end

it "correctly list the reserved room" do
@reservation.check_in.must_equal @check_in
@reservation.check_out.must_equal @check_out
@reservation.check_in.must_be_kind_of Date
@reservation.check_out.must_be_kind_of Date
@reservation.room.must_be_kind_of Integer
@reservation.total_cost.must_equal 1400
end

it "accurately addes the reserved room to the reservation list" do
number_of_reserved_rooms = @admin.reservations.length

@admin.reservations.length.must_equal number_of_reserved_rooms
end

end # end of describe "add_reservation method" do

describe "list_reservations method" do
before do
@admin.add_reservation(1, Date.new(2018, 3, 22), Date.new(2018, 3, 24))
@admin.add_reservation(2, Date.new(2018, 4, 21), Date.new(2018, 4, 24))
@admin.add_reservation(3, Date.new(2018, 4, 22), Date.new(2018, 4, 24))
end

it "correctly adds all the new reservations" do
reservations = @admin.list_reservations(Date.new(2018, 4, 22))

@admin.reservations.length.must_equal 3
reservations.length.must_equal 2
end

it "returns zero if no reservations are found" do
reservations = @admin.list_reservations(Date.new(2018, 3, 10))

reservations.length.must_equal 0
end

end # end of describe "list_reservations method"

describe "available_rooms method" do
before do
@check_in = Date.new(2018, 4, 14)
@check_out = Date.new(2018, 4, 21)
end

it "lists out all the empty rooms with the given date range" do
@admin.add_reservation(12, @check_in, @check_out)

available_list = @admin.available_rooms(@check_in, @check_out)

available_list.length.must_equal 19
available_list.must_be_kind_of Array
end

end # end of describe "available_rooms method"
end # end of describe "Admin class"
Loading