Is !foo = expression legal in Ruby? - ruby

Note: I am not exactly sure what to name the question, so if someone has a better idea please edit it.
I will jump right into the question, since there isn't any fore-explaining required.
This code:
!foo = true
generates this warning
warning: found = in conditional, should be ==
I would understand if this was happening after an if or unless statement, but this couldn't be further away from them (exaggerating). I do realise I could use:
foo = true
!foo
I suppose, the warning isn't a big deal, but it is a bit irritating that Ruby is assuming I am doing something wrong—when I am not.
Questions:
Is this a bug?
Can the warning be disabled?
Thanks!

Is legal. Not a bug. The warning can be suppressed.
You can disable the warning with:
$VERBOSE = nil
It's interesting, $VERBOSE is a case where setting something to false does something different than setting it to nil.
By the way, the other answers, at least initially, tend to assume that Ruby parses the expression as
(!foo) = true
... but that's not the case. It is parsed as:
!(foo = true)
... and so it's doing exactly what the OP wanted. And there is no specification or ratified standard for Ruby, so if it works in MRI (the reference implementation) then it's legal.

As previous answers already suggested, that's not a valid thing of doing what you want to do.
!foo = true
evaluates as
!(foo = true)
That is, assign true to foo and get the negation of the result of that assignment, which boils down to
!true
or
false
If you want to store !true, it has to be
foo = !true
If you want to assign true to foo and the negation to another variable, it'd be
foo2 = !(foo = true)
and that will still cause a warning, because after all it is an assignment in a conditional.
I actually want to assign true to foo, and then get the opposite of foo on the stack
Doesn't really make much sense. You "get something on the stack" by assigning it to a variable, like foo2 in my example.
If the purpose here is to assign to an instance variable and return the negation from a method, then yes, you will have to first assign to the variable and then explicitly return the negation. This is not a bug in Ruby but actually a feature, and for the sake of clean code, you shouldn't be doing it in one line because it's basically indistinguishable from the common bug of using = when == was meant.

It's only a warning, and is evaluating as you expect. You can disable warnings temporarily by assigning $VERBOSE=nil.
save_verbose, $VERBOSE = $VERBOSE, nil
result = !foo = true
$VERBOSE = save_verbos
result
Other places on the net, suggest making a helper method ala
module Kernel
def silence_warnings
with_warnings(nil) { yield }
end
def with_warnings(flag)
old_verbose, $VERBOSE = $VERBOSE, flag
yield
ensure
$VERBOSE = old_verbose
end
end unless Kernel.respond_to? :silence_warnings
But, I just tried this in 1.9.2 and 1.8.7 and it was ineffective at suppressing the "warning: found = in conditional, should be =="

That's technically an invalid left hand assignment. You want
foo = !true
You can't assign a value to the opposite of the object. What is not foo? =)

This is a mistake in your code:
!var = anything
Is wrong. You're trying to assign to either TrueClass or FalseClass, which is (probably) what !var returns.
You want:
!var == true
Now you're doing the comparison (albeit an unnecessary one).

Related

Elegant way to tell if something is false?

Seeing that .nil? is so useful and makes code so readable, I tried .false? and was surprised it didn't exist.
Question
What is the most elegant / preferred / self-documenting / idiomatic way to check if something is false in ruby, without using any user-defined methods?
Example
A possible use case replacing; the s.false? in this:
def false?
self == false
end
s ||= "hello" # is the same as
s = "hello" if (s.nil? || s.false?)
Well, usually you just compare to false only if it is actually important whether the value is really false as in
if s == false
do_something
end
However, most of the time, people actually check for a value to be truthy or falsy as you often don't (need to) care for the strict difference between false or nil.
Here, you thus merely check whether a value is nil or false (that is: it's falsy) or if it is anything else (that is: it's truthy). This is encouraged by language idioms such as the checks done by if and unless as well as the common boolean operators such as || or &&.
do_something if s # called if s is anything else but false or nil
do_something unless s # called if s is either false or nil
Especially when accepting / expecting boolean values, a nil value is often handled as if it were false because of these Ruby language idioms.
What is the most elegant / preferred / self-documenting / idiomatic
way to check if something is false in ruby, without using any
user-defined methods?
I think s == false is your answer plain and simple.

Check for Ruby env variable [duplicate]

In Ruby, I am trying to write a line that uses a variable if it has been set, otherwise default to some value:
myvar = # assign it to ENV['MY_VAR'], otherwise assign it to 'foobar'
I could write this code like this:
if ENV['MY_VAR'].is_set? #whatever the function is to check if has been set
myvar = ENV['MY_VAR']
else
myvar = 'foobar'
end
But this is rather verbose, and I'm trying to write it in the most concise way possible. How can I do this?
myvar = ENV['MY_VAR'] || 'foobar'
N.B. This is slightly incorrect (if the hash can contain the value nil) but since ENV contains just strings it is probably good enough.
The most reliable way for a general Hash is to ask if it has the key:
myvar = h.has_key?('MY_VAR') ? h['MY_VAR'] : 'default'
If you don't care about nil or false values (i.e. you want to treat them the same as "not there"), then undur_gongor's approach is good (this should also be fine when h is ENV):
myvar = h['MY_VAR'] || 'foobar'
And if you want to allow nil to be in your Hash but pretend it isn't there (i.e. a nil value is the same as "not there") while allowing a false in your Hash:
myvar = h['MY_VAR'].nil? ? 'foobar' : h['MY_VAR']
In the end it really depends on your precise intent and you should choose the approach that matches your intent. The choice between if/else/end and ? : is, of course, a matter of taste and "concise" doesn't mean "least number of characters" so feel free to use a ternary or if block as desired.
hash.fetch(key) { default_value }
Will return the value if it exists, and return default_value if the key doesn't exist.
This works best for me:
ENV.fetch('VAR_NAME',"5445")
myvar = ENV.fetch('MY_VAR') { 'foobar' }
'foobar' being the default if ENV['MY_VAR'] is unset.
Although it's not relevant in the specific example you gave since you're really asking about hash keys, not variables, Ruby does give a way to check variable definition. Use the defined? keyword (it's not a method, but a keyword since it needs special handling by the interpreter), like so:
a = 1
defined? a
#=> "local-variable"
#a = 2
defined? #a
#=> "instance-variable"
##a = 3
defined? ##a
#=> "class-variable"
defined? blahblahblah
#=> nil
Hence you could do:
var = defined?(var) ? var : "default value here"
As far as I know, that's the only way other than an ugly begin/rescue/end block to define a variable in the way that you ask without risking a NameError. As I said, this doesn't apply to hashes since:
hash = {?a => 2, ?b => 3}
defined? hash[?c]
#=> "method"
i.e. you're checking that the method [] is defined rather than the key/value pair you're using it to access.
Another possible alternative, which will work even if ENV['MY_VAR'] turnsout to be a false value
myvar = ENV['MY_VAR'].presence || 'foobar'
The Demand gem which I wrote allows this to be extremely concise and DRY:
myvar = demand(ENV['MY_VAR'], 'foobar')
This will use ENV['MY_VAR'] only if it is present. That is, it will discard it just if it's nil, empty or a whitespace-only string, giving the default instead.
If a valid value for ENV['MY_VAR'] is falsy (such as false), or an invalid value is truthy (such as ""), then solutions like using || would not work.
I am new guy to Ruby, post the answer I found at 2021, maybe useful for someone.
check if env key exists:
include?(name) → true or false
has_key?(name) → true or false
member?(name) → true or false
key?(name) → true or false
get env with default value:
ENV.fetch(name, :default_val)
ref: https://docs.ruby-lang.org/en/master/ENV.html
myvar = ENV['MY_VAR'].is_set? ? ENV['MY_VAR'] : 'foobar'
This way you keep the .is_set? method.

Why is this not a syntax error?

If I do this:
(false true)
it fails with a syntax error, as I expect. But if I do this:
(false
true)
the code is executed, and it throws away the first condition and returns the result of the second.
Is this considered a bug or a feature?
Line endings are optional, so in this case, the return is causing the parser to interpret it as the following:
(false; true)
which evaluates to just:
(true)
If these were method calls then both would be evaluated, but only the last would be emitted. For example:
x = (p "hello"
p "world"
2)
This will output "hello" and "world" and x will equal 2
Parentheses are used for grouping, line breaks are used as expression separators. So, what you have here is simply a group of two expressions. There is nothing to reject.
This is useful because of this well-known idiom:
def foo(bar = (bar_set = true; :baz))
if bar_set
# optional argument was supplied
end
end
There is simply no other way in Ruby to figure out whether an optional argument was supplied or not.
Basically, this becomes interesting in the presence of side effects, such as assigning a variable in my example or printing to the screen in #32bitkid's example. In your example, there is no side effect, that's why you couldn't see what was actually going on.
Ruby can give you a warning if you have warnings on.
$VERBOSE = true
def foo
(false
true)
end
gives you
(irb):3: warning: unused literal ignored
In both Ruby 1.9.1 patchlevel 378 and Ruby 1.8.7 patchlevel 330.
See How can I run all Ruby scripts with warnings? for how to run all Ruby scripts with warnings on.

What does !! mean in ruby?

Just wondering what !! is in Ruby.
Not not.
It's used to convert a value to a boolean:
!!nil #=> false
!!"abc" #=> true
!!false #=> false
It's usually not necessary to use though since the only false values to Ruby are nil and false, so it's usually best to let that convention stand.
Think of it as
!(!some_val)
One thing that is it used for legitimately is preventing a huge chunk of data from being returned. For example you probably don't want to return 3MB of image data in your has_image? method, or you may not want to return your entire user object in the logged_in? method. Using !! converts these objects to a simple true/false.
It returns true if the object on the right is not nil and not false, false if it is nil or false
def logged_in?
!!#current_user
end
! means negate boolean state, two !s is nothing special, other than a double negation.
!true == false
# => true
It is commonly used to force a method to return a boolean. It will detect any kind of truthiness, such as string, integers and what not, and turn it into a boolean.
!"wtf"
# => false
!!"wtf"
# => true
A more real use case:
def title
"I return a string."
end
def title_exists?
!!title
end
This is useful when you want to make sure that a boolean is returned. IMHO it's kind of pointless, though, seeing that both if 'some string' and if true is the exact same flow, but some people find it useful to explicitly return a boolean.
Note that this idiom exists in other programming languages as well. C didn't have an intrinsic bool type, so all booleans were typed as int instead, with canonical values of 0 or 1. Takes this example (parentheses added for clarity):
!(1234) == 0
!(0) == 1
!(!(1234)) == 1
The "not-not" syntax converts any non-zero integer to 1, the canonical boolean true value.
In general, though, I find it much better to put in a reasonable comparison than to use this uncommon idiom:
int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
It's useful if you need to do an exclusive or. Copying from Matt Van Horn's answer with slight modifications:
1 ^ true
TypeError: can't convert true into Integer
!!1 ^ !!true
=> false
I used it to ensure two variables were either both nil, or both not nil.
raise "Inconsistency" if !!a ^ !!b
It is "double-negative", but the practice is being discouraged. If you're using rubocop, you'll see it complain on such code with a Style/DoubleNegation violation.
The rationale states:
As this is both cryptic and usually redundant, it should be avoided
[then paraphrasing:] Change !!something to !something.nil?
Understanding how it works can be useful if you need to convert, say, an enumeration into a boolean. I have code that does exactly that, using the classy_enum gem:
class LinkStatus < ClassyEnum::Base
def !
return true
end
end
class LinkStatus::No < LinkStatus
end
class LinkStatus::Claimed < LinkStatus
def !
return false
end
end
class LinkStatus::Confirmed < LinkStatus
def !
return false
end
end
class LinkStatus::Denied < LinkStatus
end
Then in service code I have, for example:
raise Application::Error unless !!object.link_status # => raises exception for "No" and "Denied" states.
Effectively the bangbang operator has become what I might otherwise have written as a method called to_bool.
Other answers have discussed what !! does and whether it is good practice or not.
However, none of the answers give the "standard Ruby way" of casting a value into a boolean.
true & variable
TrueClass, the class of the Ruby value true, implements a method &, which is documented as follows:
Returns false if obj is nil or false, true otherwise.
Why use a dirty double-negation when the standard library has you covered?

Is There a Better Way of Checking Nil or Length == 0 of a String in Ruby?

Is there a better way than the following to check to see if a string is nil OR has a length of 0 in Ruby?
if !my_string || my_string.length == 0
return true
else
return false
end
In C# there's the handy
string.IsNullOrEmpty(myString)
Anything similar to that in Ruby?
When I'm not worried about performance, I'll often use this:
if my_string.to_s == ''
# It's nil or empty
end
There are various variations, of course...
if my_string.to_s.strip.length == 0
# It's nil, empty, or just whitespace
end
If you are willing to require ActiveSupport you can just use the #blank? method, which is defined for both NilClass and String.
I like to do this as follows (in a non Rails/ActiveSupport environment):
variable.to_s.empty?
this works because:
nil.to_s == ""
"".to_s == ""
An alternative to jcoby's proposal would be:
class NilClass
def nil_or_empty?
true
end
end
class String
def nil_or_empty?
empty?
end
end
As it was said here before Rails (ActiveSupport) have a handy blank? method and it is implemented like this:
class Object
def blank?
respond_to?(:empty?) ? empty? : !self
end
end
Pretty easy to add to any ruby-based project.
The beauty of this solution is that it works auto-magicaly not only for Strings but also for Arrays and other types.
variable.blank? will do it.
It returns true if the string is empty or if the string is nil.
nil? can be omitted in boolean contexts. Generally, you can use this to replicate the C# code:
return my_string.nil? || my_string.empty?
First of all, beware of that method:
As Jesse Ezel says:
Brad Abrams
"The method might seem convenient, but most of the time I have found that this situation arises from trying to cover up deeper bugs.
Your code should stick to a particular protocol on the use of strings, and you should understand the use of the protocol in library code and in the code you are working with.
The NullOrEmpty protocol is typically a quick fix (so the real problem is still somewhere else, and you got two protocols in use) or it is a lack of expertise in a particular protocol when implementing new code (and again, you should really know what your return values are)."
And if you patch String class... be sure NilClass has not been patch either!
class NilClass
def empty?; true; end
end
Every class has a nil? method:
if a_variable.nil?
# the variable has a nil value
end
And strings have the empty? method:
if a_string.empty?
# the string is empty
}
Remember that a string does not equal nil when it is empty, so use the empty? method to check if a string is empty.
Another option is to convert nil to an empty result on the fly:
(my_string||'').empty?
Konrad Rudolph has the right answer.
If it really bugs you, monkey patch the String class or add it to a class/module of your choice. It's really not a good practice to monkey patch core objects unless you have a really compelling reason though.
class String
def self.nilorempty?(string)
string.nil? || string.empty?
end
end
Then you can do String.nilorempty? mystring
Check for Empty Strings in Plain Ruby While Avoiding NameError Exceptions
There are some good answers here, but you don't need ActiveSupport or monkey-patching to address the common use case here. For example:
my_string.to_s.empty? if defined? my_string
This will "do the right thing" if my_string is nil or an empty string, but will not raise a NameError exception if my_string is not defined. This is generally preferable to the more contrived:
my_string.to_s.empty? rescue NameError
or its more verbose ilk, because exceptions should really be saved for things you don't expect to happen. In this case, while it might be a common error, an undefined variable isn't really an exceptional circumstance, so it should be handled accordingly.
Your mileage may vary.
If you are using rails, you can use #present?
require 'rails'
nil.present? # ==> false (Works on nil)
''.present? # ==> false (Works on strings)
' '.present? # ==> false (Works on blank strings)
[].present? # ==> false(Works on arrays)
false.present? # ==> false (Works on boolean)
So, conversely to check for nil or zero length use !present?
!(nil.present?) # ==> true
!(''.present?) # ==> true
!(' '.present?) # ==> true
!([].present?) # ==> true
!(false.present?) # ==> true
Have you tried Refinements?
module Nothingness
refine String do
alias_method :nothing?, :empty?
end
refine NilClass do
alias_method :nothing?, :nil?
end
end
using Nothingness
return my_string.nothing?
In rails you can try #blank?.
Warning: it will give you positives when string consists of spaces:
nil.blank? # ==> true
''.blank? # ==> true
' '.blank? # ==> true
'false'.blank? # ==> false
Just wanted to point it out. Maybe it suits your needs
UPD. why am i getting old questions in my feed?
Sorry for necroposting.
For code golfers:
if my_string=~/./
p 'non-empty string'
else
p 'nil or empty string'
end
Or if you're not a golfer (requires ruby 2.3 or later):
if my_string&.size&.positive?
# nonzero? also works
p 'non-empty string'
else
p 'nil or empty string'
end

Resources