ruby: rubocop complains on converting if else to case statement - ruby

I have following if else statement that rubocop complained about Style/CaseLikeIf: Convert if-elsif to case-when
if obj.is_a? Rest::Headers
obj
elsif obj.is_a? Hash
Rest::Headers.new(**obj)
elsif obj.is_a? Array
Rest::Headers.new(**obj.to_h)
else
raise 'Invalid object type for Headers!'
end
I converted it into case statement
case
when (obj.is_a? Rest::Headers)
obj
when (obj.is_a? Hash)
Rest::Headers.new(**obj)
when (obj.is_a? Array)
Rest::Headers.new(**obj.to_h)
else
raise 'Invalid object type for Headers!'
end
But now rubocop complained Do not use empty case condition, instead use an if expression. Does anyone know what is wrong with my case statement?

Ruby wants you to convert to a case that matches on the types themselves. case in Ruby doesn't just match on equality. A statement of the form
case a
when b
...
end
The case will call b === a (note the triple equals here, which does not mean equality) and trigger the when block if it's true.
=== is defined on Object to be equivalent to ==, but many classes override it.
Regular expressions use it to pattern match against the right-hand side
Ranges check whether the right-hand side is included in the range
Classes check whether the right-hand side is_a? instance of the class.
And it's that third bullet point that Rubocop suggests you use.
case obj
when Rest::Headers
obj
when Hash
Rest::Headers.new(**obj)
when Array
Rest::Headers.new(**obj.to_h)
else
raise 'Invalid object type for Headers!'
end
Note that I never explicitly call is_a? here; the case block automagically knows to do that.

Related

Is there a comparison Operator that returns true if both values are set and equal: false if both are nil

Is there an ideomatic or shorter way to "compare two values but don't allow them to be nil".
One way would be:
(!left.nil? || !right.nil?) && (left == right)
One of the nil checks could be omitted, here, but I left it in the example, to show the intent better.
Is there a method in Ruby core, on Kernel or BasicObject, next to equal?, eql?, == and such that matches this?
I aim at something similar to what e.g. minitest assert_equal does (edited for brevity, nil-check at line 5):
def assert_equal exp, act, msg = nil
msg = message(msg, E) { diff exp, act }
result = assert exp == act, msg
if nil == exp then
refute_nil exp, "Use assert_nil if expecting nil."
end
result
end
In my case this is for an autorization system where I repeatedly have to guard against both sides being nil. E.g.
class ContactPolicy < ApplicationPolicy
def add?
!actor.id.nil? && subject.id == actor.id
end
end
After repeating that pattern in various forms, I wondered if there is not a better Ruby-ism for this. I am now leaning towards a refactoring using null-objects that have a def ==(other) which always return false. The question of whether this "equal but not nil" remains interesting though.
Possible Approaches
The optimal answer will depend on why you think either value might be nil, and other aspects of your code that are not shown in your original question. Without that context, it appears that this is primarily an attempt to guard against nil values raising a NameError exception, while also preventing your expression from resolving to (nil == nil) == true. If that's the case, you can take one of the following approaches, among others:
Rescue all possible exceptions.
Rescue NameError explicitly.
Avoid exception handling, and use a conditional expression chain to test whether your variables are both defined and not-nil.
Handle NameError Exceptions from Undefined Variables
You might rescue all exceptions, but this is generally considered a poor approach in the general case.
# Swallow anything descended from Exception. This is
# common in the wild, so it's idiomatic by definition,
# but it can cast too wide of a net sometimes, and may
# lead to unexpected problems.
left == right rescue nil
It would be better to rescue NameError, but it's up to you to figure out how the program should handle the exception. Perhaps you will decide to set the variables, and then retry the rescued block, but there are certainly other approaches.
# Rescue just NameError; other exceptions would still
# get raised when necessary.
begin
left == right
rescue NameError => e
# handle the exception
end
Avoiding Undefined Variables
There are a number of situations where Ruby will autovivify variables within a given scope, but your example isn't one of them. In this case, you can rely on operator precedence to ensure your values are defined using the defined? keyword.
For example, assuming left and right are either undefined? or can respond_to? :nil?, then you can use the following expression to:
Ensure both left and right are defined. Returns "expression" if they are both defined variables within an array literal.
Ensure neither of the values in [left, right] are nil using Enumerable#none? (which is mixed into Array) and the Object#nil? method that Array inherits.
Check equality of your two defined, non-nil variables.
defined? [left, right] and
[left, right].none? &:nil? and
left == right
#=> nil
Based on your question, the expectation is that when your variables resolve to nil == nil the whole expression should still return false.
The same conditional expression also guards against both variables being set to nil rather than simply being undefined. For example:
# Test with nil values.
left, right = nil, nil
defined? [left, right] and
[left, right].none? &:nil? and
left == right
#=> false

Can `nil` be matched on a `case/when` statement, with an explicit `when nil` branch?

I have a Ruby case/when statement, which takes different options for a parameter.
There are three valid cases: symbols, integers, and nil; anything else is invalid.
In the most obvious form, there are four cases:
case param
when Symbol
run_symbol_code
when Integer
run_integer_code
when nil
# do nothing
else
raise_error
end
now, I was wondering: is there any way to avoid the nil case? Originally I thought about:
case param
when Symbol
run_symbol_code
when Integer
run_integer_code
when Object
raise_error
end
however, this doesn't work, because nil is actually an object. There's also the option to use raise_error if param in the last case, but I don't find it very appealing.
Is there any way to use only three cases?
In cases like this in which nil results in do nothing I would prefer a Guard Clause because it is easy to read and understand.
def method(param)
return if param.nil?
case param
when Symbol
run_symbol_code
when Integer
run_integer_code
else
raise_error
end
end
Of course, it doesn't reduce the number of checks.
In ruby you can add condition at the end of case, iterators statements.
like following
case param
when Symbol
run_symbol_code
when Integer
run_integer_code
else
raise_error
end unless param.nil?
Note:- end unless param.nil? in such cases 'case', iterator will run only when condition permits i.e Case will run only when param is not nil.

Why `TrueClass === true` evaluates to `true` but `true === TrueClass` evaluates to `false`? [duplicate]

In Ruby, what is the difference between == and ===? The RDoc says
Case Equality—For class Object,
effectively the same as calling #==,
but typically overridden by
descendents to provide meaningful
semantics in case statements.
Is #== the same as ==? And could you provide an example of when/how this is used in case statements?
The two really have nothing to do with each other. In particular, #== is the equality operator and #=== has absolutely nothing to with equality. Personally, I find it rather unfortunate that #=== looks so similar to #==, uses the equals sign and is often called the case equality operator, triple equals operator or threequals operator when it really has nothing to do with equality.
I call #=== the case subsumption operator (it's the best I could come up with, I'm open to suggestions, especially from native English speakers).
The best way to describe a === b is "if I have a drawer labeled a, does it make sense to put b in it?"
So, for example, Module#=== tests whether b.is_a?(a). If you have Integer === 2, does it make sense to put 2 in a box labeled Integer? Yes, it does. What about Integer === 'hello'? Obviously not.
Another example is Regexp#===. It tests for a match. Does it make sense to put 'hello' in a box labeled /el+/? Yes, it does.
For collections such as ranges, Range#=== is defined as a membership test: it makes sense to put an element in a box labeled with a collection if that element is in the collection.
So, that's what #=== does: it tests whether the argument can be subsumed under the receiver.
What does that have to with case expressions? Simple:
case foo
when bar
baz
end
is the same as
if bar === foo
baz
end
Yes, by #== the docs mean "the instance method == of the current object".
=== is used in case statements as such:
case obj
when x
foo
when y
bar
end
Is the same as
if x === obj
foo
elsif y === obj
bar
end
Some classes that define their own === are Range (to act like include?), Class (to act like obj.is_a?(klass)) and Regexp (to act like =~ except returning a boolean). Some classes that don't define their own === are the numeric classes and String.
So
case x
when 0
puts "Lots"
when Numeric
puts(100.0 / x)
when /^\d+$/
puts(100.0 / x.to_f)
default
raise ArgumentError, "x is not a number or numeric string"
end
is the same as
if 0 == x
puts "Lots"
elsif x.is_a? Numeric
puts(100.0 / x)
elsif x =~ /^\d+$/
puts(100.0 / x.to_f)
else
raise ArgumentError, "x is not a number or numeric string"
end
Fun fact, === is also used to match exceptions in rescue
Here is an example
class Example
def self.===(exception)
puts "Triple equals has been called."
true
end
end
raise rescue Example
# => prints "Triple equals has been called."
# => no exception raised
This is used to match system errors.
SystemCallError.=== has been defined to return true when the two have the same errno. With this system call errors with the same error number, such as Errno::EAGAIN and Errno::EWOULDBLOCK, can both be rescued by listing just one of them.

What test Ruby perform on the `when` clause statement before calling `.===` operator?

Ruby uses === operator on the case/when type execution style.Now It also known that Ruby depending on the type
of the thing present in the when clause, calls the respective .=== method.
Say when statement contains the class names, then the rule is - it will use Module#===, which will return true if the right side is an instance of,
or subclass of, the left side. One example with this context is:
Here instance of test occurs
obj = 'hello'
#=> "hello"
case obj
when String
print 'It is a string'
when Fixnum
print 'It is a number'
else
print 'It is not a string'
end
#It is a string
#=> nil
Here subclass of test occurs
num = 10
#=> 10
case num
when Numeric
puts "Right class"
else
puts "Wrong class"
end
#Right class
#=> nil
Now when contains String literals then String#=== is called, which in turn checks if left and right handside
literal are same(same chracter in same sequence) or not.
a = "abc"
#=> "abc"
case a
when "def" then p "Hi"
when "abc" then p "found"
else "not found"
end
#"found"
#=> "found"
The all logic is too cool. Now my query is with case/when structure -
How does ruby know if when holding class, or String literals or
anything valid at runtime?
or
What test does it perform before calling the respective .===
operator on the thing when holding currently.
EDIT
Before understanding the Case/when working principal,let me clear the below which when does while it gets its turn.
String.===("abc") #=> true
Because "abc" is an instance of String class. - Am I right?
Now I tried the below just to check who is whose super class.
10.class #=> Fixnum
Fixnum.superclass #=> Integer
Integer.superclass #=> Numeric
Numeric.superclass #=> Object
Humm. That means the below returns true as Fixnum is also the indirect subclass of Numeric. - Am I right?
Numeric.===(10) #=> true
But why then the below outputs contradictory to the above?
Numeric.===(Fixnum) #=> false
Trying to be more specific to my query as below :
When we are calling Numeric.===(10) and String.===("abc") . I think we are sending not "abc" and 10 rather "abc".class and 10.class.
10.===(10) #=> true
Numeric.===(10) #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. 10.===(10) is just like
10 ==(10) comparison. But Numeric.===(10) outputs true as class of 10 is the subclass of Numeric.
"abc".===("abc") #=> true
String.===("abc") #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. "abc".===("abc") is just like simple string literal comparison "abc" ==("abc") comparison. But String.===("abc") outputs true as "abc" which is an instance of String.
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
I might be 100% wrong, In that case please correct me.
I'll try to explain what #Lee Jarvis also explaines.
class Someclass
end
s = Someclass.new
p s.methods.sort
#[:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone,(...)
Look at the 5th method. My Someclass instance has a ===method out of nowhere.
Actually, it has 56 methods and I did not define one of them. They are inherited from Object; every class in Ruby inherits from Object. For class Object (and my Someclass), #=== is effectively the same as calling #==, but (as the docs say) #=== is typically overwritten by descendants to provide meaningful semantics in case statements.
So ruby does nothing smart, it just sends the object in question a === message (or calls the ===method if you prefer that).
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
It doesn't. It simply calls the === method. That's it. That's just how object-orientation works, and it is nothing specific to Ruby, every OO language works the same way: you call a method on an object, and the object decides how to react. (Or, in a class-based language like Ruby, PHP, Java, C#, C++, Python etc. the class of the object decides.)
Different objects may react in different ways. Classes check whether the argument is an instance of themselves, Regexps check whether the argument is matched by them, Ranges check whether the argument is covered by them.
It's just basic method dispatch.

Double ampersand in Ruby

I am using the authlogic gem with Ruby on Rails, and I have been using the following to obtain the id of the user that is currently logged in:
current_user = UserSession.find
id = current_user && current_user.record.id
I'm not understanding how current_user && current_user.record.id returns the current user id. I would think this would return a boolean. Can someone explain how this works?
There is no Boolean type in Ruby; Ruby has a rather simple view of truth (or more precisely, it has a rather simple view of falsehood).
the false object, which is the singleton instance of FalseClass is considered falsy
the nil object, which is the singleton instance of NilClass is falsy
every other object is truthy (including, obviously, the true object, which is the singleton instance of TrueClass)
[BTW: this means that a lot of objects that are considered falsy in some other languages, are actually truthy in Ruby, like the integer 0, the real value 0.0, the empty string, the empty array, the empty hash, the character 'F']
So, the Boolean operators &&, ||, and and or do not return Boolean values. Instead they return the first object that determines the outcome of the expression.
(They are also short-circuiting, which means that they only evaluate the minimum sub-expressions that are needed to determine the outcome of the expression. So, an alternate formulation would be that they return the result of the last expression that was evaluated. Which, in turn, is analogous to what blocks, methods, class bodies and module bodies do.)
So, what does it mean to return the first object that determines the outcome? That's simple, really: the result of the expression
a && b
is truthy if both a and b are truthy, otherwise it is falsy. So, if a is falsy, it is completely irrelevant what b is: the result will be falsy either way. So, we might just as well simply return a. (Remember, a doesn't have to be false, it could also be nil and the programmer might want to know which one of the two it was.)
If, OTOH, a is truthy (IOW it is neither nil nor false), then the result of the whole expression is solely dependent on b: if b is truthy, the whole result will be truthy, otherwise if b is falsy, the whole result will be falsy. So, we might just as well return b itself, instead of first converting it to a Boolean.
|| and or are analogous or more precisely dual to && and and.
You posted this example:
id = current_user && current_user.record.id
Here, the author isn't even expecting current_user to be a Boolean value! Instead, he expects it to be either a User or nil. But that's perfectly fine, because a User is truthy and nil is falsy, so they still can be used in a Boolean expression.
The basic intention is that the author wants to prevent a NoMethodError exception being raised, if he tries to call #record on nil.
An alternative way of expressing this would be
id = current_user.record.id unless current_user.nil?
If you want all the gory details, check out Section 11.1 (page 36) of the Draft ISO Ruby Specification or the excutable specifications of the RubySpec project. (Here's the one for &&.)
I wrote a pure Ruby implementation of Ruby's Boolean operators and conditional expressions once for fun. The meat of the implementations is these two mixins.
The logical and is short circuiting. That means that if the construct is X && Y, and X is false then Y never gets checked because the whole thing is certainly going to be yield false.
That code is saying, essentially:
if (current_user is TRUE) {
id = current_user.record.id;
]
Here's some console output showing you get the second value if the first is true:
irb(main):005:0> true && 9
=> 9
and nil if the first is nil:
irb(main):008:0> nil && 9
=> nil

Resources