I often face the situation when if condition A (for example !#object.nil?) is false, condition B, when checked, can raise an error (for example #object.some_method -> undefined method 'some_method' for nil:NilClass).
I tested it in console, but could't get some full data.
1) Is it safe to use and/&& when if first conditional is false, second can lead into an error?
2) What about or?
!#object.nil? && #object.some_method
#object && #object.some_method
Both of the above are valid patterns in Ruby. You will not get the "undefined method for nil" error, because the expression to the right of the && is never evaluated if the expression to the left of it evaluates to nil (or false).
You can also accomplish this check using postfix if or unless:
#object.some_method if #object
or
#object.some_method unless #object.nil?
are both valid alternatives.
Regarding or:
Using || (Rubyists generally avoid the keyword or operator) will not protect you from the error, because when the expression on the left side of the || evaluates to nil, the interpreter will actually continue on and attempt to evaluate the expression on the right side.
Second part of these lines will never be exected:
false && raise "this will never be raised"
true || raise "this will never be raised"
In your case, if you are using active support, you can do:
#object.try!(:some_method)
Related
I have a method that includes (among subsequent lines) the following code:
candidate_lines.each do |line|
return line if priority_1?(line) && line.marked_by?(self)
end
The methods in it, as far as I can tell, are irrelevant. What's driving me up the wall, is I have a test that should be skipping that part of the method simply by the specification I've given:
it "without any priority 1 lines, returns all priority 2 lines marked by itself" do
line1 = double :line, marked_by?: true
line2 = double :line, marked_by?: true
allow(joshua).to receive(:priority_1?).and_return(:false)
expect(joshua).to receive(:candidate_lines).and_return([line1, line2])
expect(joshua.tiebreak_lines).to eq [line1, line2]
end
Yet when I run the test, the return statement is getting triggered. I've tested it manually with puts statements immediately before the 'return line...' line, and it turns out that, while 'priority_1?(line)' returns false, for some reason 'priority_1?(line) && line.marked_by?(self)' is evaluating to true.
What's happening here?
You're confusing the symbol :false with the boolean value false
Because :false is neither false nor nil, it is truthy, that is as far as things like && or if are concerned, it is true.
I have absolutely no experience in Ruby, but am trying to debug an issue in FPM. Sometimes, when running FPM, I get the following stack trace:
output': undefined method `empty?' for nil:NilClass (NoMethodError)
I traced this error back to this line (rpm.rb, line 419):
if #epoch.nil? or #epoch.empty?
#logger.warn("no value for epoch is set, defaulting to nil")
return nil
end
This blog post says the following about the or operator:
The best way to think about the chains constructed with or is as
series of fallbacks: try this, if that fails try this, and so on
This leads me to believe that in the above code fragment, if epoch.nil? evaluates to true, it will not attempt to check epoch.empty?, but a simple codepad shows that to be wrong (http://codepad.org/qI7rLvBX)
str = nil
puts str.nil? or str.empty?
Output:
Line 2: undefined method `empty?' for nil:NilClass (NoMethodError)
true
if I change it the operator to ||, then the short-circuiting works correctly - if epoch.nil? evaluates to true, it does not attempt to evaluate epoch.empty?.
Am I misunderstanding something about the or operator? And is the way FPM is checking for nil or empty incorrect?
You could skip the whole problem by noting that nil.to_s is an empty string:
NilClass#to_s → ""
Always returns the empty string.
Given that, you could say:
if #epoch.to_s.empty?
This is a useful trick for avoiding a bunch of noisy nil? tests when dealing with strings.
It's a matter of operator precedence. || has a higher precedence than or.
You should write it like:
str = nil
puts (str.nil? or str.empty?)
puts str.nil? || str.empty?
See more in Difference between "or" and || in Ruby?
For the issue of FPM, I have tried under Ruby 1.8.6, 1.9.3, 2.0.0, and did not get error with or expression.
Experimenting with the conditional operator in ruby,
def nada
false ? true : nil
end
def err
false ? true : raise('false')
end
work as expected but
def reflection
false ? true : return false
end
produces a syntax error, unexpected keyword_false, expecting keyword_end
def reflection
false ? true : return(false)
end
and attempted with brackets syntax error, unexpected tLPAREN, expecting keyword_end
yet
def reflection
false ? true : (return false)
end
works as expected, and the more verbose if...then...else...end
def falsy
if false then true else return false end
end
also works as expected.
So what's up with the conditional (ternary) operator?
You can use it like this, by putting the entire return expression in parentheses:
def reflection
false ? true : (return false)
end
Of course, it does not make much sense used like this, but since you're experimenting (good!), the above works! The error is because of the way the Ruby grammar works I suppose - it expects a certain structure to form a valid expression.
UPDATE
Quoting some information from a draft specification:
An expression is a program construct which make up a statement (see 12
). A single expression can be a statement as an expression-statement
(see 12.2).12
NOTE A difference between an expression and a statement is that an
expression is ordinarily used where its value is required, but a
statement is ordinarily used where its value is not necessarily
required. However, there are some exceptions. For example, a
jump-expression (see 11.5.2.4) does not have a value, and the value
of the last statement of a compound-statement can be used.
NB. In the above, jump-expression includes return among others.
I think this is all related to the ruby parser.
ruby parses return as the else-expression of the ternary operator
ruby is then surprised when it finds false instead of end
wrapping return false in parentheses causes ruby to interpret the entire thing as the else-expression
return(false) doesn't work because ruby is still trying to interpret just the return part as the else-expression, and is surprised when it finds a left-parenthesis (updated)
Note: I don't think this is a great answer.
A great answer could, for example, explain the parse errors with reference to the ruby grammar.
The ternery operator is just that, an operator. You don't return from it. You return from functions. When you put a return in an if, you return from the function that the if is in. Since there is no variable awaiting assignment from the result of the if, there is no problem. When you return from the ternery operator, there is no value assigned to the variable.
I have a function similar to the following:
def check
return 2 == 2 || 3 != 2 || 4 != 5
end
My question is, will Ruby perform all the comparisons even though the first is true, and thus the function return true. My checks are much more intensive, so I'd like to know if I should break this out in a different way to avoid making all the checks every time.
irb(main):004:0> 2 == 2 || 3 != 2 || 4 != 5
=> true
Thank you.
Ruby uses short-circuit evaluation.
This applies to both || and &&.
With || the right operand is not evaluated if the left operand is truthy.
With && the right operand is not evaluated if the left operand is falsy.
|| short-circuits as soon as the first condition is true. So yes, it will help if you put the most expensive conditions at the end.
|| will by default short-circuit evaluate, meaning that once the first "true" expression is encountered it will stop evaluation (unless you explicitly state you want all expressions to evaluate with the 'or' operator).
reference:
http://en.wikipedia.org/wiki/Short-circuit_evaluation
As soon as one of the condition is true, the function will return.
You can test it yourself in irb, like this:
irb> p('Hello') || p('World')
As we know the function p prints its parameters(in an inspect manner) then returns them, so if the || short circuits, only "Hello" is printed, otherwise both "Hello" and "World" are printed.
You can also test the logical && operator, by using puts instead of p, as puts always returns nil.
BTW, irb is a perfect place to play around ruby. You can test everything there, except a small portion of concurrency.
This question already has answers here:
Difference between "and" and && in Ruby?
(8 answers)
Closed 3 years ago.
What's the difference between the or and || operators in Ruby? Or is it just preference?
It's a matter of operator precedence.
|| has a higher precedence than or.
So, in between the two you have other operators including ternary (? :) and assignment (=) so which one you choose can affect the outcome of statements.
Here's a ruby operator precedence table.
See this question for another example using and/&&.
Also, be aware of some nasty things that could happen:
a = false || true #=> true
a #=> true
a = false or true #=> true
a #=> false
Both of the previous two statements evaluate to true, but the second sets a to false since = precedence is lower than || but higher than or.
As the others have already explained, the only difference is the precedence. However, I would like to point out that there are actually two differences between the two:
and, or and not have much lower precedence than &&, || and !
and and or have the same precedence, while && has higher precedence than ||
In general, it is good style to avoid the use of and, or and not and use &&, || and ! instead. (The Rails core developers, for example, reject patches which use the keyword forms instead of the operator forms.)
The reason why they exist at all, is not for boolean formulae but for control flow. They made their way into Ruby via Perl's well-known do_this or do_that idiom, where do_this returns false or nil if there is an error and only then is do_that executed instead. (Analogous, there is also the do_this and then_do_that idiom.)
Examples:
download_file_via_fast_connection or download_via_slow_connection
download_latest_currency_rates and store_them_in_the_cache
Sometimes, this can make control flow a little bit more fluent than using if or unless.
It's easy to see why in this case the operators have the "wrong" (i.e. identical) precedence: they never show up together in the same expression anyway. And when they do show up together, you generally want them to be evaluated simply left-to-right.
and/or are for control flow.
Ruby will not allow this as valid syntax:
false || raise "Error"
However this is valid:
false or raise "Error"
You can make the first work, with () but using or is the correct method.
false || (raise "Error")
puts false or true --> prints: false
puts false || true --> prints: true
The way I use these operators:
||, && are for boolean logic. or, and are for control flow. E.g.
do_smth if may_be || may_be -- we evaluate the condition here
do_smth or do_smth_else -- we define the workflow, which is equivalent to
do_smth_else unless do_smth
to give a simple example:
> puts "a" && "b"
b
> puts 'a' and 'b'
a
A well-known idiom in Rails is render and return. It's a shortcut for saying return if render, while render && return won't work. See "Avoiding Double Render Errors" in the Rails documentation for more information.
or is NOT the same as ||. Use only || operator instead of the or operator.
Here are some reasons. The:
or operator has a lower precedence than ||.
or has a lower precedence than the = assignment operator.
and and or have the same precedence, while && has a higher precedence than ||.
Both or and || evaluate to true if either operand is true. They evaluate their second operand only if the first is false.
As with and, the only difference between or and || is their precedence.
Just to make life interesting, and and or have the same precedence, while && has a higher precedence than ||.
Just to add to mopoke's answer, it's also a matter of semantics. or is considered to be a good practice because it reads much better than ||.