Using ruby's 'case'/'when' method with || or && operators, etc - ruby

I just tried to run some code that looked like this
def get_proj4(srid, type=nil)
type.downcase! if type
case type
when nil || "epsg"
open("http://spatialreference.org/ref/epsg/#{srid}/proj4/").read
when "esri"
open("http://spatialreference.org/ref/esri/#{srid}/proj4/").read
end
end
and it didn't run properly, returning nil every time. wrapping the nil || "epsg" in parentheses didn't work either
It turns out ruby wouldn't allow me to use the || operator in this
Now I assume ruby takes the case/when method and ultimately breaks it down into a group of conditionals looking something like
x = type
if x == (nil || "epsg")
y = ...runs code...
elsif x == "esri"
y = ...
end
x = nil
y
but obviously it does not. What's going on here?
Thanks

The expression is evaluated first so when nil || "espg" is equivalent to when "espg"1 - it will never match nil.
To match either-or, separate the options with a comma:
case type
when nil, "espg" ..
when "esri" ..
Or, alternatively, perhaps normalize the value:
case (type || "espg")
when "espg" ..
when "esri" ..
Or use the other form that resembles an if-else:
case
when type.nil? || type == "espg" ..
when type == "esri" ..
Or some combination of everything :)
1 This is also the same reason why the example if is suspect. It should probably be written like:
if type.nil? || type == "espg"

Related

&& operator not stopping excution with nil

The class below defies my understanding that nil && 'foo' should return nil and not execute 'foo'
no matter what I tried, with or without parenthesis, #user.another_boolean always returns undefined method another_boolean for nil nilclass. I thought if #user is nil it should stop evaluating there and return nil.
class MyClass
def initialize(user, variable = nil)
#user = user
#variable = variable || user.try(:variable)
end
def result
#result ||= !!(#user &&
#variable &&
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean)
end
end
I also tried to look for the documentation of the && operator inside the ruby documentation but could only find a reference to and which shouldn't be the same thing given their precedence difference.
Any help much appreciated.
Ruby version: 2.2.5
Edit:
#user and #variable are rails model
Rails version: 4.2
It is standard practice in software for && to have a higher precedence than ||.
So the following are all logically equivalent:
b && a || c
a && b || c
c || b && a
c || a && b
Now, your code is a little longer:
#user &&
#variable &&
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean
But again we can group the && operators together to show what it's equivalent to:
(#user && #variable && #variable.a_boolean) ||
(#user.another_boolean?) ||
(#user.a_third_boolean? && instance_method_retuning_a_boolean)
Therefore if #user && #variable && #variable.a_boolean == false, then #user.another_boolean? will be evaluated.
I'm not clear what it is you're trying to achieve - so I don't know if the above logic is correct, or how one might "fix" it, but there's your explanation for why the method is being called.
Your expression has a form of:
aaa &&
bbb &&
bbb.foo ||
aaa.bar ||
aaa.baz && something
it may be reformatted as:
aaa && bbb && bbb.foo
||
aaa.bar
||
aaa.baz && something
It's the same, just whitespaces are laid out differently.
As you can see here, not all terms are protected by the initial aaa&&bbb test.
Most probably you meant this:
#result ||= !!( (
#user &&
#variable
)
&&
(
#variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean?
)
&& instance_method_retuning_a_boolean
)
I've added way too many parentheses than needed, but this way you exactly see what's going on.
Hi Yann and welcome to Stackoverflow. Let me give you some examples that may help you understand the reason for your observation.
You correctly stated that:
nil && true
=> nil
but if you chain additional operators without explicitly use brackets then the following happens:
nil && true || true
=> true
This is because the && operator has higher precedence so you could write the same thing like this, and then its clear why the expression does not stop after the first nil:
(nil && true) || true
I found this article pretty helpful: https://womanonrails.com/operator-precedence-ruby.
So for your case if we would put the brackets as it is now we would have the following:
(#user && #variable && #variable.a_boolean) ||
#user.another_boolean? ||
(#user.a_third_boolean? && instance_method_retuning_a_boolean)
This means that even if the first part of the expression results in false, the #user.another_boolean? still gets evaluated.
So what you probably want is putting brackets explicitly:
(#user && #variable) &&
(#variable.a_boolean || #user.another_boolean? || #user.a_third_boolean?) &&
instance_method_retuning_a_boolean
So now you have the first part, which will check if both #user and #variable are not nil. If any of those is nil, the second part will not be evaluated anymore.
Hope this brings some clarity.
You can probably avoid an overly complex boolean expression by adding a guard clause (or two) that separates the prerequisite conditions from the actual result:
def result
return unless #user
return unless #variable
#result ||= #variable.a_boolean ||
#user.another_boolean? ||
#user.a_third_boolean? && instance_method_retuning_a_boolean
end
I'm not sure if this returns the expected result, but you get the idea.

Using || in arguments in Ruby?

I just saw this code:
method_name(ARGV.shift || "somefile.txt")
which basically should first evalute ARGV[0] and if it doesn't exist then read from "somefile.txt".
My question was, can the && operator be also used here, and in what situations?
Also, does this concept of "passing this or this argument" to a method in Ruby have a name?
The short answer, logical operators are going to return one of its operands based on if it's truthy or falsey. In practice, nearly everything in Ruby will evaluate to true except false or nil.
Examples:
expr1 || expr2 will return (expr1 if not falsey) or (expr2)
In other words, the first truthy value or the final operand,
expr1 && expr2 will return (expr1 if falsey) or (expr2).
In other words, the first falsey value or the final operand.
As for an actual use case, a similar example would be:
Using the && operator to check for a flag in ARGV then passing the file name.
method_name(ARGV.include?('-w') && "write_file.txt")
It should be noted that this is probably not a widely accepted practice. (see comments)
However, preferring a user supplied value over a default value, by using ||, in this manner would be.
If && is used, then the argument would be nil when there is no ARGV[0] and "somefile.txt" when there is ARGV[0]. Note that elements of ARGV, if any, would be strings, so there is no possibility of ARGV[0] being nil or false when there is an element passed.
Generally, || and && are called "(short circuit) disjunction" and "(short circuit) conjunction", respectively.
A typical use case of || is to provide a default value:
foo = potentially_falesy_value || default
A typical use case of && is to provide a value that depends on the truthness of another value:
foo = hash[:bar] && hash[:bar][:baz]
|| is using for providing default values. || returns first "true" value. "True" value -- value that is interpreted as true boolean value in ruby. So first "true" value in the chain will be as the result of the expression. && returns first "false" value. Complete analogy. But it does not have such graceful application.
Apart from the obvious Boolean operator functionality, && can be used like you could in some languages such as JavaScript:
a = cond1 && cond2 && value # a is now value if cond1 and cond2,
# else nil or false (depends on cond1 and cond2)
It's not very readable (IMHO) when assigning non-Boolean variables, but it works.
first evalute ARGV[0] and if it doesn't exist then read from "somefile.txt". My ques
You are correct. The ARGV.shift || "somefile.txt" expression will evaluate to ARGV.shift if it returns some non-falsy value, and "somefile.txt" otherwise. Some other examples:
puts nil || "foo" # => "foo"
puts "foo" || "bar" # => "foo"
puts "foo" || nil # => "foo"
puts "foo" || raise # => "foo"
# and doesn't blow up, because the 'raise'
# is never evaluated
can the && operator be also used here
Sure, but is arguably of less practical value. It might be clearer to use an if in that case:
puts foo && bar
# is the same as:
if foo
puts bar
end
# or:
puts bar if foo
does this concept of "passing this or this argument to a method in Ruby" has a name?
I'm not sure if it has any 'official' name, but I commonly see this pattern being called 'default value' or 'fallback value'.

Ruby Newbie: Confused About Boolean Logic

I have an array, if I find a value in it, I want to execute a block of code. Also, if the array is nil, I want to execute that block. So the code I tried is:
if !array.respond_to? :index || array.index(str)
#some code
So if it's nil it's true, or if str is somewhere in the array, it's true, right? But if it finds the item at index 0, it doesn't enter the block. Also, according to irb false || 0 evalueates to 0. WTF?? I thought that everything was true except false and nil. I guess || does something odd that I'm not expecting??
My questions are: What's going on? What's a nice way to write a conditional that does what I want?
Using nil? and include? with an inline if seems most idiomatic to me.
#your code if arr.nil? || arr.include?(str)
if array.nil? || array.member?(s)
# ...
false || 0 evaluates to 0 because it's an or. False isn't truthy (obviously ;) but 0 is, so the expression is truthy.
Are you checking for a nil array or an empty one? If you've already declared the array it won't be nil even if it's empty. I'd write it like:
if array.empty? || array.include(str)
or if you really want to check for a nil array:
if array.nil? || array.include(str)
I'd use .include rather than .index to avoid getting a 0.
if array.nil?­ || array­.member?(str­)
#code block
end
The || operator almost reminds you of a coalesce.
Given a = false, b = :bacon
return a || b #returns :bacon

What is the difference between "if" statements with "then" at the end?

What's the difference between these two Ruby if statements when we put a then at the end of the if statement?
if(val == "hi") then
something.meth("hello")
else
something.meth("right")
end
and
if(val == "hi")
something.meth("hello")
else
something.meth("right")
end
then is a delimiter to help Ruby identify the condition and the true-part of the expression.
if condition then true-part else false-part end
then is optional unless you want to write an if expression in one line. For an if-else-end spanning multiple lines the newline acts as a delimiter to split the conditional from the true-part
# can't use newline as delimiter, need keywords
puts if (val == 1) then '1' else 'Not 1' end
# can use newline as delimiter
puts if (val == 1)
'1'
else
'Not 1'
end
Here's a quick tip that is not directly related to your question: in Ruby, there is no such thing as an if statement. In fact, in Ruby, there are no statements at all. Everything is an expression. An if expression returns the value of the last expression that was evaluated in the branch that was taken.
So, there is no need to write
if condition
foo(something)
else
foo(something_else)
end
This would better be written as
foo(
if condition
something
else
something_else
end
)
Or as a one-liner
foo(if condition then something else something_else end)
In your example:
something.meth(if val == 'hi' then 'hello' else 'right' end)
Note: Ruby also has a ternary operator (condition ? then_branch : else_branch) but that is completely unnecessary and should be avoided. The only reason why the ternary operator is needed in languages like C is because in C if is a statement and thus cannot return a value. You need the ternary operator, because it is an expression and is the only way to return a value from a conditional. But in Ruby, if is already an expression, so there is really no need for a ternary operator.
The then is only required if you want to write the if expression on one line:
if val == "hi" then something.meth("hello")
else something.meth("right")
end
That brackets in your example are not significant, you can skip them in either case.
See the Pickaxe Book for details.
The only time that I like to use then on a multi-line if/else (yes, I know it's not required) is when there are multiple conditions for the if, like so:
if some_thing? ||
(some_other_thing? && this_thing_too?) ||
or_even_this_thing_right_here?
then
some_record.do_something_awesome!
end
I find it to be much more readable than either of these (completely valid) options:
if some_thing? || (some_other_thing? && this_thing_too?) || or_even_this_thing_right_here?
some_record.do_something_awesome!
end
# or
if some_thing? ||
(some_other_thing? && this_thing_too?) ||
or_even_this_thing_right_here?
some_record.do_something_awesome!
end
Because it provides a visual delineation between the condition(s) of the if and the block to execute if the condition(s) evaluates to true.
There's no difference at all.
And, just FYI, your code can be optimized to
something.meth( val == 'hi' ? 'hello' : 'right' )

Ruby ternary operator without else

Is there a ruby idiom for "If do-this," and "do-this" just as a simple command?
for example, I'm currently doing
object.method ? a.action : nil
to leave the else clause empty, but I feel like there's probably a more idiomatic way of doing this that doesn't involve having to specify a nil at the end. (and alternatively, I feel like taking up multiple lines of code would be wasteful in this case.
As a general rule: you pretty much never need the ternary operator in Ruby. The reason why you need it in C, is because in C if is a statement, so if you want to return a value you have to use the ternary operator, which is an expression.
In Ruby, everything is an expression, there are no statements, which makes the ternary operator pretty much superfluous. You can always replace
cond ? then_branch : else_branch
with
if cond then then_branch else else_branch end
So, in your example:
object.method ? a.action : nil
is equivalent to
if object.method then a.action end
which as #Greg Campbell points out is in turn equivalent to the trailing if modifier form
a.action if object.method
Also, since the boolean operators in Ruby not just return true or false, but the value of the last evaluated expression, you can use them for control flow. This is an idiom imported from Perl, and would look like this:
object.method and a.action
a.action if object.method?
Greg's answer is the best, but for the record, and even more than in C, expressions and statements are equivalent in Ruby, so besides a.action if o.m? you can also do things like:
object.method? && a.action
You can write (a; b; c) if d or even
(a
b
c
) if d
or for that matter: (x; y; z) ? (a; b c) : (d; e; f)
There is no situation in Ruby where only a single statement or expression is allowed...
result = (<expression> && <true value>) || <false value>
value = 1
result = (value == 1 && 'one' ) || 'two'
result #=> 'one'
Explain: value == 1 && 'one' #=> returns last expression result, value is equals 1 so and section will be evaluated, and return 'one'.
value = 0
result = (value == 1 && 'one' ) || 'two'
result #=> 'two'
Explain: value != 1 and 'and' expression will not be evaluated, but instad will be used 'or' expression and it returns 'two'
Another way this can be done on the same line is:
if object.method; a.action end
This is considered bad style by Rubocop because it uses a semicolon to terminate the expression, but I find it more readable in some conditions than tacking on the if statement at the end. It is easier to overlook an if statement at the end and I don't always want to return something if the condition isn't true(as you are forced into with a ternary operator).
You can also be a bit more verbose and rubocop friendly:
if object.method then a.action end

Resources