Is there a more idiomatic way for me to return a boolean in #survey_completed?
This is how I did things in C#, and I have always felt that the last ternary clause to return false was redundant, so perhaps Ruby has a better way of doing this?
This is how what my code currently looks like:
def survey_completed?
self.survey_completed_at ? true: false
end
def survey_completed_at
# Seeing as the survey is submitted all or nothing, the time the last response is persisted
# is when the survey is completed
last_response = self.responses.last
if last_response
last_response.created_at
end
end
You can use double negation:
def survey_completed?
!!survey_completed_at
end
def survey_completed?
!survey_completed_at.nil?
end
The idiomatic way to do this in Ruby is to not do it. Every object except for false and nil evaluates to true in a Boolean context. Thus your survey_completed_at function already serves the purpose of the survey_completed? function.
If you got a response for the survey, last_response.created_at will be something non-nil thus the function will evaluate to true in a Boolean context. If you didn't get a response and last_response is nil, the if will evaluate to nil and the function will evaluate to false in a Boolean context.
Related
I just found out that Ruby provides no way to override boolean conversion method.(How to create an Object who act as a False in Ruby)
I am trying to create an object, and track whenever it is passed to an if condition.
However, when I executed the following piece of code, 2222222222222222 wasn't being printed.
class Null
def ==(other)
p 2222222222222222
p caller
super
end
end
null = Null.new
if null
end
Does this mean that if doesn't call ==? Is there a method that if calls in an object to evaluate the Truth-yness of it?
Alternatively, is there a way to track where if object is used for object?
I'm making a large scale change and it would be unreasonable to string search all if conditions in our code base. I'd much rather create a special object and log/alert whenever if object is used.
There are only ever two objects considered "falsey": nil and false. Everything else is truthy.
So no, if my_obj doesn't call my_obj#== nor anything else. As long as the object is neither nil nor false, it's evaluated as true.
As far as I can tell, there's no way to track if an object is "evaluated as boolean", or used in if or unless.
An equivalent of if my_obj would be:
unless my_obj.equal? nil || my_obj.equal? false
# code
end
I have a paramter params[:input], and its value is coming as 'true' or 'false' (boolean values in string).
In database, the input values are stored as 1 and 0. I need to return boolean values based on input string values. I created a function below to return boolean values.
def status(input)
return unless input.present?
return false if ['false'].include?(input.downcase)
return true if ['true'].include?(input.downcase)
end
Not sure if this is the best way. Is there a better way to write this?
I would go with case. Since there is a binary state, you probably need to decide what value is to be chosen for a fallback. E. g. when no parameter is present, you are likely still in a need to return something. I would return false for everything save for 'true':
def status(input)
case input
when 'true' then true
when 'false' then false
else false
end
end
Or, even shorter (but less readable/idiomatic):
def status(input)
'true' == input
end
It seems like you want to cast a string to a boolean and you are using Ruby on Rails. Why don't you use the same method that Rails itself uses to translate user input into a boolean?
def status(input)
ActiveRecord::Type::Boolean.new.type_cast_from_user(value)
end
The name of the method changed in different versions of Rails. The above version is for Rails 4.2. Prior Rails 4.2 use ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) and since Rails 5 it is ActiveRecord::Type::Boolean.new.cast(value)
The advantage of using the build method it that it accepts not only the strings 'true' and 'false' but other common input and still returns a useful boolean.
The simplest solution, without overhead of switch statements, etc, is simply this:
def status(input)
input.is_a?(String) && input.casecmp?('true')
end
casecmp? ensures case-insensitivity, so 'true', 'TRUE', 'True', and any variation of different cases would all return a boolean true.
This could even be altered slightly more to simply ensure that input is a string by calling to_s on it, which will just return self if it is already a string.
def status(input)
input.to_s.casecmp?('true')
end
The first is a bit safer, as depending on the object passed, to_s could have been overridden and supply unpredictable behavior, though highly unlikely.
Another solution is using downcase and comparing with ==, but casecmp? is more optimized for comparison, and does not create an additional string as downcase would.
Documentation for case_cmp?
ActiveSupport extends Object with an instance method blank?:
class Object
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
end
Could !!empty? be written as empty? instead? Is this a stylistic choice so that it reads easily as a method returning a boolean? Or is there something else?
The reason for this is that !! coerces the response from empty to a boolean. Empty can be defined differently on different objects so it is possible that someone in rails could have defined .empty? to not return a boolean. Since .blank? needs to return a boolean the !! is needed to ensure that a boolean is returned.
It is a common way to convert a truthy versus falesy value into strict true and false.
It is a common approach in Ruby to call !!(something). The result of the calculation will be boolean, not nil or something else:
!!(true) # true
!!(false) # false
!!(nil) # false
!! is used to force falsey/truthy values to false/true:
irb(main):001:0> !!nil == false
=> true
In fact, it used to be empty?. Here's the commit that changed it to !!empty?: https://github.com/rails/rails/commit/126dc47665c65cd129967cbd8a5926dddd0aa514
From the comments:
Bartuz:
Why double !! ? It returns the TrueClass / FalseClass anynway
fxn:
Because it is a dynamic language and subject to polymorphism, you just can't rely on empty? returning singletons, what you need to guarantee is that you return one no matter what.
The "improved" implementation however is incomplete, because you could just as well implement ! to return a non-boolean value:
class Foo
def !
nil
end
end
Foo.new.blank? #=> nil
To handle both methods (empty? and !), it should be implemented as:
!!(respond_to?(:empty?) ? empty? : !self)
Can anybody tell me why a lot of Ruby boolean methods use this double negation convention?
!!(boolean expression)
The double negation ensures that no matter the initial value, you will always get true or false, never some mystery value.
This is handy because it avoids dangling references to objects you no longer require, or having to differentiate between two types of false value, nil and false.
Often you will see methods written like this:
def logged_in?
!!#session_user
end
This will return true or false and that value can be used for whatever purpose is required. Compare that with this:
def logged_in?
#session_user
end
In this case if you save the value, you're actually saving the whole #session_user object, which could be a fairly significant chunk of memory. This memory cannot be released until your reference to it falls out of scope. Since there is only one true and one false, there's no need for garbage collection.
Suppose you want to define a method that returns a boolean. For example, whether a string matches a regex.
class String
def include? regex; self =~ regex end
end
If you do the above, it will return nil when it does not match, and an integer when it matches. In many cases, that does not matter so much (nil is similar to false and true can be substituted for an integer). But if you really wanted a boolean as a return value, if you do
class String
def include? regex; !!(self =~ regex) end
end
it will return true when it matches, false when it does not.
In my opinion, Ruby is just a little too clever about Booleans. This is a workaround for the cases when the cleverness causes problems.
Here's the excessive cleverness: there's no Boolean class. Instead, there's a convention under which any value can serve as a boolean. In a context where a true-or-false value is required, nil or false means "false" and anything else means "true". The values true and false are not part of a special class, they're the sole members of their respective classes — they exist only to make code a little more readable.
I guess this works fine most of the time, but every once in a very long while you need a boolean value that is always true or false. For example, I recently needed to compare two boolean values and do something special if they were different; the only problem being, one of them was either true or false, while the other was either 1 or nil. Fix? Utilize the fact that !!nil == false and !!1 == true.
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?