Overlap method for Hotel Reservation system Ruby - ruby

I am refactoring code for a hotel reservation system, specifically a method to list all the vacancies in the hotel, for a school project. Our instructor provided his solution and unfortunately he and the rest of our school is on break until Monday (when our refactoring assignment is due). StackOverflow, I could utilize your help.
Essentially, my question boils down to why was bang (!) used in the following code.
def overlap?(other)
return !(other.checkout <= #checkin || other.checkin >= #checkout)
end
This code is a method within Module Hotel and class DateRange. It is called in another class to check for vacancies for a given date range, see below.
overlap_reservations = #reservations.select do |res|
res.overlaps(dates)
end
In my mind, I would have not utilized bang. But I am a newbie and am blind to the interplay here. Any help you can provide is appreciated.

This code suffers several severe code smells/glitches, that should never appear in the good ruby code.
def overlap?(other)
return !(other.checkout <= #checkin || other.checkin >= #checkout)
end
one should never use explicit return in the last line of ruby method. Ruby will return the value, the last line was evaluated to, automatically.
one should try to simplify the conditions as possible, since the human brain is vulnerable to double/triple conditions with negations.
one should not mix ! negation with a disjunction, since it is expanded to a conjunction.
I doubt the code above is even correct, if we are talking about commonly used “checkins” and “checkouts.”
The summing up:
def overlap?(other)
# boolean for whether other checkout overlaps
co = other.checkout >= #checkin && other.checkout <= #checkout
# boolean for whether other checkin overlaps
ci = other.checkin >= #checkin && other.checkin <= #checkout
# return a result
ci || co
end
or, using the whole power of ruby:
def overlap?(other)
ci_co_range = #checkin..#checkout
ci_co_range.cover?(other.checkout) || ci_co_range.cover?(other.checkin)
end
or (from your forthcoming Christmas lesson :)
def overlap?(other)
[other.checkout, other.checkin].any? do |co_ci|
(#checkin..#checkout).cover?(co_ci)
end
end
Documentation: Range, Enumerable#any?.

Related

Rubocop claims for too many 'if' statements

I am measuring my code for a Rails app using Rubocop. This is my code:
def ratings_treatment
if #rating.advertise_id
advertise = Advertise.find(advertise_id)
rating_average(advertise)
elsif #rating.product_id
product = Product.find(product_id)
rating_average(product)
elsif #rating.combo_id
combo = Combo.find(combo_id)
rating_average(combo)
else
establishment = Establishment.find(#rating.establishment_id)
rating_average(establishment)
end
end
It does not pass a test regarding if. It is claimed that there are too many if statements.
I thought of splitting the code into some checker methods, but was wondering if there is some better way to get good metrics and still write good code.
I think you could use a switch statement to assign the argument for rating_average:
def ratings_treatment
average = case
when #rating.advertise_id then Advertise.find(advertise_id)
when #rating.product_id then Product.find(product_id)
when #rating.combo_id then Combo.find(combo_id)
else Establishment.find(#rating.establishment_id)
end
rating_average average
end
Although Rubocop will complain with
Style/EmptyCaseCondition: Do not use empty case condition, instead use
an if expression
You could also simplify your if expression:
def ratings_treatment
average = if #rating.advertise_id
Advertise.find advertise_id
elsif #rating.product_id
Product.find product_id
elsif #rating.combo_id
Combo.find combo_id
else
Establishment.find #rating.establishment_id
end
rating_average average
end
If your #rating has associations set up then you could say:
def ratings_treatment
obj = #rating.advertise || #rating.product || #rating.combo || #rating.establishment
rating_average(obj)
end
or even:
def ratings_treatment
rating_average(#rating.advertise || #rating.product || #rating.combo || #rating.establishment)
end
or perhaps something fancy (and overkill for only four branches):
def ratings_treatment
rating_average(
%w[advertise product combo establishment].lazy.map { |m| #rating.send(m) }.find(&:present?)
)
end
or maybe even:
def rated_object
to_result = ->(m) { #rating.send(m) }
%w[advertise product combo establishment]
.lazy
.map(&to_result)
.find(&:present?)
end
def ratings_treatment
rating_average(rated_object)
end
I'd go so far as to suggest that something like the above rated_object method really should be a method on #rating. You should be able to ask a rating for the thing that was rated without having to manually spin through all the possibilities.
You could even patch that lazy.map... stuff into Enumerable if you wanted something in Enumerable that behaved like e.m1 || e.m2 || e.m3 || ... (been there, done that) all the way down to short circuiting.
I have no idea what Rubocop would make of any of these shenanigans though.
This is a classic case of a style violation that has a deeper underlying cause, i.e. a modelling problem. In your case, it looks like what you're actually trying to achieve is a polymorphic association. If you change your Rating to:
# app/models/rating.rb
class Rating < ApplicationRecord
belongs_to :rateable, polymorphic: true
end
you can have the Rating relate to any number of things, by simply adding this to the other end of the association:
# app/models/product.rb
class Product < ApplicationRecord
has_many :pictures, as: :rateable
end
Your original method, if it is even needed anymore, becomes:
def ratings_treatment
rating_average(#rating.rateable)
end

Is instantiating an array in condition a bad programming habit?

I often see this kind of condition :
if ([CONSTANT_1, CONSTANT_2, CONSTANT_3].contains? var && a_boolean) #...
instead of :
if ((var == CONSTANT_1 || var == CONSTANT_2 || var == CONSTANT_3) var && a_boolean) #...
I understant that the first condition is more elegant, but does creating an array only to check a condition is bad or does the resource used are negligible enough to be ignored ?
The former pattern does exert more memory pressure and CPU overhead since it has to allocate that array each time this code is run.
That said, this isn't necessarily a problem unless it's a particularly hot code path. If this is a method that is called frequently deep in a library somewhere, I'd probably elect for the second version to avoid the extra allocations, but for business code with a relatively low call rate, I think the readability of the first is acceptable.
The second version (like most complicated conditions) is error-prone. It's too easy to forget whether you need parentheses or how boolean logic works. The pattern of "is the variable one of these values and if so is this other thing true?" can be especially tricky, so it certainly makes sense to simplify.
However, I don't think the Array class includes contains?, does it? In my tests, I used include?:
CONSTANT_1 = 1
CONSTANT_2 = 2
CONSTANT_3 = 3.14
a_boolean = true
var = 3.14
puts "true" if [CONSTANT_1, CONSTANT_2, CONSTANT_3].include?(var) && a_boolean
Notice the parenthesis around var are necessary so that include? isn't searching for var && a_boolean instead of just var.
The only real problem I see with this idiom is that it would be a bit nicer to have the var come first. In SQL you might write something like:
var in (CONSTANT_1, CONSTANT_2, CONSTANT_3) and a_boolean
That seems just a little more natural to read.
If you are really worried about efficiency and you do this test many times, it might be better to set up a hash:
constant_test = {}
[CONSTANT_1, CONSTANT_2, CONSTANT_3].each do |k|
constant_test[k] = true
end
puts "true" if constant_test[var] && a_boolean
This has the advantage of being a hash lookup instead of a loop each time you test. (See also: this answer.)
Ideally, you wouldn't see either one of those. Instead, you'd use something like this as would be more idiomatic and more testable by breaking the conditional into small pieces of functionality.
I realize this example isn't quite the same if you're using, but it's close enough to show what I mean.
class Thing
def initialize(var)
#var = var
end
def a_method
return 'yes' if has_constaints? && boolean?
'no'
end
private
def has_constaints?
#var == CONSTANT_1 || #var == CONSTANT_2 || #var == CONSTANT_3
end
def boolean?
#var && a_boolean
end
end

Using eval function in Ruby to call other functions

I'm using below class with processQuestion function to call other methods.
This function is called by calling CONSTANTS of other classes.
# Is responsible for executing a particular question. Question types are in the Question object. A question will
# always have a responding method in this class. That method will take the parameters defined by the question and
# should provide the answer in the format expected.
class QuestionProcessor
NO_ROUTE = "NO SUCH ROUTE"
def initialize(routeList)
#routeList = routeList
end
# Finds the method and runs it. This should provide the answer object
def processQuestion(question)
return eval("get"+question.command+"(question)")
end
# Finds the total distance using the exact stations specified, or returns NO_ROUTE if no route was stored in the route list
# this method ignores the constraints and actions
def getDistance(question)
distance = 0
currentStation = nil
question.parameters.each do |nextStation|
if (! currentStation.nil?)
route = #routeList.getDirectRoute(currentStation, nextStation)
if (route.nil?)
return NO_ROUTE
end
distance += route.distance
end
currentStation = nextStation;
end
return distance;
end
# Finds the shortest route possible for the given constraint. This method requires a constraint and action to be provided
def getShortestRoute(question)
startStation = question.parameters[0]
endStation = question.parameters[1]
routeProcessor = ShortestRouteProcessor.new(#routeList, question.constraint, question.action)
routeProcessor.getRoute(startStation, endStation)
return routeProcessor.shortestRoute == Constants::INTEGER_MAX ? NO_ROUTE : routeProcessor.shortestRoute
end
# Counts the number of routes based on the condition provided. Intended to count the number of routes, but could potentially provide a total distance
# or anything else produced by the action.
def getCountRoutes(question)
startStation = question.parameters[0]
endStation = question.parameters[1]
routeProcessor = RouteProcessor.new(#routeList, question.constraint, question.action)
routeProcessor.getRoute(startStation, endStation)
return routeProcessor.totalSuccessfulRoutes
end
end
I thought this is a good approach to remain DRY but I hear eval is evil.
Is this good approach or should I look for other ways in a more object oriented way?
In this case you may safely use send instead of eval, like in this example:
def processQuestion(question)
return send("get#{question.command}", question)
end
Just be aware that send may be as dangerous as eval if you do not sanitize your input (question.command in this case).
If possible, do a white-list filtering before calling send (or eval), otherwise someone could pass a command which does something you do not want to do.
There is a function in ruby for exactly this reason, the send function. It is part of the Object class so everything has it.
read more here:
http://ruby-doc.org/core-2.1.1/Object.html#method-i-send
for metaprogramming I recommend you read this whole tutorial:
https://rubymonk.com/learning/books/2-metaprogramming-ruby/

Is there a way using TracePoint in ruby 2.0 to get the time between call and return of methods?

I'm trying to figure out if I can get the time it takes for a method to execute using TracePoint in ruby 2.0. Any help is appreciated.
Update
I want to clarify this question. My goal is to get the time it takes for all methods to execute even if you don't know what those methods will be. I've found this to be quite tricky. Deivid's response below involves setting a t0 and t1 variables in a shared context, then setting the time values on call and return. While this works for a simple example, it becomes unmanageable when trying to log the time of all method calls in a more complex ruby program. Take for example the following program:
class C
def self.slow
C.fast
sleep 3
end
def self.fast
end
end
C.slow
In this program, it is only possible to monitor t0 and t1 times if you keep track of the method names being called. In an even more complex program such as a Rails app with many stack frames where you do not know in advance all of the methods that will be executed, it is not as obvious as to the best way to monitor and print all call and return times.
The solution 'may' involve keeping a hash of call times where the key is some combination of the Thread.current.object_id and the tp.method_id.. I have not found the correct key though that is unique enough to ensure that the return time can be matched to the caller, considering you may have recursive method calls that create non-standard call and return situations.
class C
def self.slow n
return C.fast if n == 0
sleep 1
slow n-1
end
def self.fast
end
end
#times = {}
traceCall = TracePoint.new(:call, :return) do |tp|
key = "#{tp.defined_class}_#{tp.method_id}_#{caller(0).size}"
if tp.event == :call
#times[key] = Time.now
else
#times[key] = Time.now - #times[key]
end
end.enable do
C.slow 3
end
p #times

Ruby case statement with multiple variables using an Array

I'd like to compare multiple variables for a case statement, and am currently thinking overriding the case equals operator (===) for Array is the best way to do it. Is this the best way?
Here is an example use case:
def deposit_apr deposit,apr
# deposit: can be nil or 2 length Array of [nil or Float, String]
# apr: can be nil or Float
case [deposit,apr]
when [[Float,String],Float]
puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
when [[nil,String],Float]
puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
when [[Float,String],nil]
puts "#{deposit[0]} #{deposit[1]}"
else
puts 'N/A'
end
end
The only problem is the Array case equals operator doesn't apply the case equal to the elements of the Array.
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A
It will if I override, but am not sure what I'd be breaking if I did:
class Array
def ===(other)
result = true
self.zip(other) {|bp,ap| result &&= bp === ap}
result
end
end
Now, it all works:
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR
Am I missing something?
I found this question because I was looking to run a case statement on multiple variables, but, going through the following, came to the conclusion that needing to compare multiple variables might suggest that a different approach is needed. (I went back to my own code with this conclusion, and found that even a Hash is helping me write code that is easier to understand.)
Gems today use "no monkey patching" as a selling point. Overriding an operator is probably not the right approach. Monkey patching is great for experimentation, but it's too easy for things to go awry.
Also, there's a lot of type-checking. In a language that is designed for Duck Typing, this clearly indicates the need for a different approach. For example, what happens if I pass in integer values instead of floats? We'd get an 'N/A', even though that's not likely what we're looking for.
You'll notice that the example given in the question is difficult to read. We should be able to find a way to represent this logic more clearly to the reader (and to the writer, when they revisit the code again in a few months and have to puzzle out what's going on).
And finally, since there are multiple numbers with associated logic, it seems like there's at least one value object-type class (Deposit) that wants to be written.
For cleanliness, I'm going to assume that a nil APR can be considered a 0.0% APR.
class Deposit
def initialize(amount, unit='USD', options={})
#amount = amount.to_f # `nil` => 0.0
#unit = unit.to_s # Example assumes unit is always present
#apr = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
end
end
Once we have our Deposit object, we can implement the print logic without needing case statements at all.
class Deposit
# ... lines omitted
def to_s
string = "#{#amount} #{#unit}"
string << ", #{#apr * 100.0}% APR" if #apr > 0.0
string
end
end
d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"
e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"
f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"
Conclusion: If you're comparing multiple variables in a case statement, use that as a smell to suggest a deeper design issue. Multiple-variable cases might indicate that there's an object that wants to be created.
If you are worried about breaking something by changing Array behavior, and certainly that's a reasonable worry, then just put your revised operator in a subclass of Array.
it's definitely not the best way. even more - you should not redefine methods of standart classes as core functionality may depend on it - have fun debugging then.
defensive style is nice(with lot of type checks and whatnot) but it usually hurts performance and readability.
if you know that you will not pass anything else than bunch of floats and strings to that method - why do you need all those checks for?
IMO use exception catching and fix the source of problem, don't try to fix the problem somewhere in the middle

Resources