Conversation
…? and contains methods.
…onality (total_cost).
…ionality and tests including: find_block_by_block_key, update_block, and reserve_from_block.
…cov errors. Wrapped Wave 3.
kaidamasaki
left a comment
There was a problem hiding this comment.
Good job! Here are a few ways you can clean up your code.
I was kind of long-winded here. It's been a long week. If anything doesn't make sense please Slack me.
| require_relative 'frontdesk.rb' | ||
| require_relative 'reservation.rb' |
There was a problem hiding this comment.
You shouldn't require_relative files that you don't directly reference in here.
Specifically this causes a circular dependency which can confuse Ruby. (frontdesk.rb requires date.rb which requires frontdesk.rb.)
Why would you decide to keep a system that's this fragile? No idea. I hope they change it for Ruby 3.0!
| @@ -0,0 +1,22 @@ | |||
| require 'date' | |||
There was a problem hiding this comment.
Please avoid naming files the same things as built in Ruby gems you're using. This can confuse Ruby and cause errors.
Also, we try to make sure that if there's a single class in a file that the name matches it (but snake_case instead of CamelCase).
| class DateRange | ||
|
|
||
| def contains(date) | ||
| (date >= @start_date && date < @end_date) ? true : false |
There was a problem hiding this comment.
Please use explicit an return; also, your condition is already a boolean so you can return it directly:
| (date >= @start_date && date < @end_date) ? true : false | |
| return date >= @start_date && date < @end_date |
| def no_conflict?(new_start, new_end) | ||
| if @end_date == new_start || new_start > @end_date || new_end < @start_date || new_end == @start_date | ||
| return true | ||
| elsif new_start >= @start_date && new_start < @end_date || new_end > @start_date | ||
| return false | ||
| end | ||
| end |
There was a problem hiding this comment.
One thing to note about ifs that don't have an else. They have an implied else that produces nil.
So this winds up being this when you make that explicit:
def no_conflict?(new_start, new_end)
if @end_date == new_start || new_start > @end_date || new_end < @start_date || new_end == @start_date
return true
elsif new_start >= @start_date && new_start < @end_date || new_end > @start_date
return false
end
return nil
end
endBecause nil is falsey this is roughly equivalent to:
def no_conflict?(new_start, new_end)
if @end_date == new_start || new_start > @end_date || new_end < @start_date || new_end == @start_date
return true
elsif new_start >= @start_date && new_start < @end_date || new_end > @start_date
return false
end
return false
end
endSince the elsif and the else blocks both produce false then you can omit them entirely and just return your condition:
| def no_conflict?(new_start, new_end) | |
| if @end_date == new_start || new_start > @end_date || new_end < @start_date || new_end == @start_date | |
| return true | |
| elsif new_start >= @start_date && new_start < @end_date || new_end > @start_date | |
| return false | |
| end | |
| end | |
| def no_conflict?(new_start, new_end) | |
| return @end_date == new_start || new_start > @end_date || new_end < @start_date || new_end == @start_date | |
| end |
| def assign_room(new_reservation) | ||
| taken_rooms = [] | ||
| all_rooms = @rooms.dup | ||
| @all_reservations.each do |reservation| | ||
| reservation.no_conflict?(new_reservation.start_date, new_reservation.end_date) ? taken_rooms << reservation.assigned_room : next | ||
| end | ||
| room_to_assign = (all_rooms - taken_rooms.flatten) | ||
| assigning = room_to_assign.sample(new_reservation.num_rooms) | ||
| taken_rooms.include?(assigning) | ||
| assigning = room_to_assign.sample(new_reservation.num_rooms) | ||
| end |
There was a problem hiding this comment.
You can simplify this using reject:
| def assign_room(new_reservation) | |
| taken_rooms = [] | |
| all_rooms = @rooms.dup | |
| @all_reservations.each do |reservation| | |
| reservation.no_conflict?(new_reservation.start_date, new_reservation.end_date) ? taken_rooms << reservation.assigned_room : next | |
| end | |
| room_to_assign = (all_rooms - taken_rooms.flatten) | |
| assigning = room_to_assign.sample(new_reservation.num_rooms) | |
| taken_rooms.include?(assigning) | |
| assigning = room_to_assign.sample(new_reservation.num_rooms) | |
| end | |
| def assign_room(new_reservation) | |
| return @all_reservations.reject do |reservation| | |
| reservation.no_conflict?(new_reservation.start_date, new_reservation.end_date) | |
| end.sample | |
| end |
There was a problem hiding this comment.
Also, at least reading this it looks like you're rejecting all of the rooms that don't conflict which confuses me.
| new_reservation = Hotel::Reservation.new(start_date: start_date, end_date: end_date, num_rooms: num_rooms) | ||
| new_reservation.assigned_room = assign_room(new_reservation) | ||
| if new_reservation.num_rooms > 1 | ||
| raise ArgumentError.new("Invalid room reservation request: if you would still like #{num_rooms} rooms please reserve as a block.") |
There was a problem hiding this comment.
🎉 Bad arguments in the ArgumentError message! 🎉
I probably would have just left num_rooms off as an argument though, since it has to be 1.
| def update_block(date, block_key) | ||
| found_block = find_block_by_block_key(date, block_key) | ||
| if found_block[0].num_rooms == 0 | ||
| raise ArgumentError.new("Invalid room reservation request: all rooms in this block have been reserved.") |
There was a problem hiding this comment.
This isn't really an ArgumentError since nothing was wrong with the arguments themselves.
I'd recommend making a custom error class like this:
module Hotel
class BlockFullError < StandardError
end
endAnd then using it like this:
| raise ArgumentError.new("Invalid room reservation request: all rooms in this block have been reserved.") | |
| raise BlockFullError.new("Invalid room reservation request: all rooms in this block have been reserved.") |
| def total_cost | ||
| @num_rooms == 1 ? final_bill = (@end_date - @start_date)*@num_rooms*COST : final_bill = (@end_date - @start_date)*@num_rooms*COST*(1 - @discount) | ||
| return final_bill | ||
| end |
There was a problem hiding this comment.
You seem very fond of ternary operators but you got a little carried away here:
| def total_cost | |
| @num_rooms == 1 ? final_bill = (@end_date - @start_date)*@num_rooms*COST : final_bill = (@end_date - @start_date)*@num_rooms*COST*(1 - @discount) | |
| return final_bill | |
| end | |
| def total_cost | |
| if @num_rooms == 1 | |
| return (@end_date - @start_date) * @num_rooms * COST | |
| else | |
| return (@end_date - @start_date) * @num_rooms * COST * (1 - @discount) | |
| end | |
| end |
When code is complex breaking it up into multiple lines can help make it much easier to read.
If you're going to do something like this as a ternary I'd recommend factoring out the assignment like this (since it can be easy to overlook and changes state):
| def total_cost | |
| @num_rooms == 1 ? final_bill = (@end_date - @start_date)*@num_rooms*COST : final_bill = (@end_date - @start_date)*@num_rooms*COST*(1 - @discount) | |
| return final_bill | |
| end | |
| def total_cost | |
| final_bill = @num_rooms == 1 ? (@end_date - @start_date) * @num_rooms * COST : (@end_date - @start_date) * @num_rooms * COST * (1 - @discount) | |
| return final_bill | |
| end |
In fact, looking at this code more makes it clear there's a lot of duplication between the two branches so it would be reasonable to use a ternary to calculate the multiplier though I'd still want to assign it to an intermediate variable:
| def total_cost | |
| @num_rooms == 1 ? final_bill = (@end_date - @start_date)*@num_rooms*COST : final_bill = (@end_date - @start_date)*@num_rooms*COST*(1 - @discount) | |
| return final_bill | |
| end | |
| def total_cost | |
| multiplier = @num_rooms == 1 ? (1 - @discount) : 1 | |
| return (@end_date - @start_date) * @num_rooms * COST * multiplier | |
| end |
| end | ||
|
|
||
| it "can return an empty reservation collection when initialized (before reservations have been added)" do | ||
| expect(@front_desk.all_reservations.empty?).must_equal true |
HotelSection 1: Major Learning Goals
Section 2: Code Review and Testing Requirements
Section 3: Feature Requirements
Overall Feedback
Additional FeedbackGreat work overall! You've built your first project with minimal starting code. This represents an incredible milestone in your journey, and you should be proud of yourself! I am particularly impressed by the way that you made good use of I do see some room for improvement around simplifying your code. Complex logic can make it really difficult to tell if there are bugs hiding in your code. Code Style Bonus AwardsWas the code particularly impressive in code style for any of these reasons (or more...?)
|
Assignment Submission: Hotel
Congratulations! You're submitting your assignment. Please reflect on the assignment with these questions.
Reflection