What is this line of Rails code doing? - ruby-on-rails

def role?(role)
return !!self.roles.find_by_name(role.to_s.camelize)
end
Can you help me understand what's happening in the code above? I'm new to Rails/Ruby.
Thanks

It's negation (!) operator repeated twice.
Note that only ruby objects evaluating to false (in boolean expression) are nil and false itself.
Therefore,
some_role will be true, !some_role is false and !!some_role is true again.
nil is false, !nil is true and !!nil is false.
So, this is a "clever" way to check whether role returned from find_by_name is nil or not. (And therefore whether role with such name exists or not)
I guess, I don't have to tell you that doing this is bad for readability. You can always check if result is nil by normal means, like result.nil? or result == nil.

This is more readable. No need for the 'self' or 'return'. 'present?' is the opposite of 'nil?' so no negation is required.
def role?(role)
roles.find_by_name(role.to_s.camelize).present?
end

Related

Ruby on Rails - unless multiple conditions

I'm trying to substitute an expression unless the expression is one of two values.
def substitute_string (string)
string.gsub('abc', 'xyz') unless string == ('dabc' || 'eabc')
end
substitute_string('jjjjjabc')
=> 'jjjjjxyz'
substitute_string('dabc')
=> 'dabc'
substitute_string('eabc')
=> 'exyz'
I expected substitute_string('eabc') to return ('eabc') since I stated that in the unless block, which I passed two values.
I don't understand why this doesn't work, and what I can do to make 'eabc' return 'eabc'.
('dabc' || 'eabc') is a boolean expression that evaluates to true and returns 'dabc'.
Use two or's:
unless string == 'dabc' || string == 'eabc'
Or use =~ (regex pattern match)
unless string =~ /(dabc|eabc)/
Since you indicated you're using Rails, you can also use in? like this:
unless string.in? ['dabc', 'eabc']
It is because (1) 'dabc' || 'eabc' is equivalent to 'dabc', and nowhere in your code does 'eabc' appear in a meaningful way, and because (2) it only returns nil when the condition is met according to the way you used unless.
def substitute_string(string)
case string
when 'dabc', 'eabc' then string
else string.gsub('abc', 'xyz')
end
end
Apart from the fun of obscure technicalities about what is returned when and in what situations, I don't see a lot of merit in not being more explicit with the return. The very fact that this issue was brought and subsequently debated on SO is exactly why writing code (working code to be sure) in this obscure fashion will lead to confusion for developers interpreting this code, and leads to buggy software.
The only benefit I see to this is that it's on one line.
def substitute_string(string)
string.gsub('abc', 'xyz') unless ['dabc', 'eabc'].include?(string)
end
I personally would prefer the following as it makes it clear what your intentions are:
def substitute_string(string)
return string if ['dabc', 'eabc'].include?(string)
string.gsub('abc', 'xyz')
end
'dabc' || 'eabc' will always equal true since it just means condition or condition where condition is a string. Since a string is not nil or false it evaluates to true. You could check whether the string is in an array values instead:
def substitute_string(string)
string.gsub('abc', 'xyz') unless ['dabc', 'eabc'].include?(string)
end

Rails object returns false on present? but true on !nil?

I have a database object in active record. If I call object.find(1).present? it returns false, but it exists. Calling !object.find(1).nil? returns true.
Why is this? I thought !nil == present?.
nil? and present? are not opposites.
Many things are both not present? and not nil?, such as an empty string or empty array.
"".present? # => false
"".nil? # => false
[].present? # => false
[].nil? # => false
To better answer your question lets look at the implementation:
def present?
!blank?
end
We don't see nil? mentioned here, just blank?, so lets check out blank? as well:
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
So essentially, if the object responds_to empty? it will call out to that for the result. Objects which have an empty? method include Array, Hash, String, and Set.
Further Reading
The Blank/Present Source in ActiceSupport
A concise explanation of nil v. empty v. blank in Ruby on Rails

Ruby 1.9.2 non-existent hash element

I'm on Rails 3.0.x, Ruby 1.9.2 and needs a way to test for params that may or may not exists, e.g.,
params[:user] #exists
params[:user][:login] #may not exist
What's the proper Ruby syntax for the 2nd check so it doesn't barf?
Try following:
params.has_key? :user #=> true because exists
params[:user].has_key? :login #=> true if exist otherwise false
#WarHog has it right, pretty much. It's very unusual for an item in params to sometimes return a string but other times return a Hash, but regardless you can handle that easily enough:
if params.has_key?(:user) && params[:user].respond_to?(:has_key?)
do_something_with params[:user][:login]
end
Instead of respond_to? :has_key? you could also do respond_to? :[] or just is_a? Hash. Mostly a matter of preference.
You would just get nil in the second case.. that shouldn't be a problem, no?
e.g. params[:user][:login] just returns nil, which evaluates to false if the :user entry exists in the first Hash.
However if the nesting would be one or more levels deeper, and the missing hash entry was somewhere in the middle, you would have problems. e.g.:
params[:user][:missing_key][:something]
in that case Ruby would try to evaluate nil[:something] and raise an exception
you could do something like this:
begin
x = params[:user][:missing_key][:something]
rescue
x = nil
end
... which you could further abstract...

what does !! do in ruby?

I am using some code that uses this syntax (restful authentication).
def logged_in?
!!current_user()
end
Tried googling for this but it just seems to ignore "!!", will accept an answer that can tell me how to find info about searching for strings such as !! in google.
It's double negation. The first ! converts it to false if current_user is not nil or false. After that it converts it to true. So the result is always a boolean value and not the value of current_user. The result is always true if current_user is not false or nil. Otherwise it's false.
It's the negation operator, !, twice. This gives a value that has the same truth value, but is explicitly only a boolean.
In Ruby, there are many values that are considered to be "true" (anything besides false and nil), and a couple that are considered to be "false" (both false and nil). The first negation converts all true values to false, and all false values to true. The second negation then reverses that, leaving you with a value that has the same truth value as the original, but only allowing the actual values true or false.
This can be helpful if you don't want people to use logged_in? as a way to access the current user, but instead you want it to just return a boolean.
It it equivalent to the more explicit and verbose:
def logged_in?
if current_user()
true
else
false
end
end
This article seems to use the same code:
http://toolmantim.com/articles/bangbang_your_nil_is_dead
Basically, it looks like it's just meant to apply the not (!) operator twice -- essentially forcing the method to return true or false instead of true, false, or nil.
That is, if #current_user is nil, calling
def logged_in?
#current_user()
end
will return nil. Changing it to (single bang)
def logged_in?
!#current_user()
end
will return true, and changing further to (double bang)
def logged_in?
!!#current_user()
end
will return false -- which is more applicable (and usable) than nil.
Another way to write it would be:
def logged_in?
!#current_user().nil?
end
but that doesn't cover the case when #current_user is false, and I think the !! is nicer to look at.
The blog post covers it:
http://toolmantim.com/articles/bangbang_your_nil_is_dead

How to understand nil vs. empty vs. blank in Ruby

I find myself repeatedly looking for a clear definition of the differences of nil?, blank?, and empty? in Ruby on Rails. Here's the closest I've come:
blank? objects are false, empty, or a whitespace string. For example, "", " ", nil, [], and {} are blank.
nil? objects are instances of NilClass.
empty? objects are class-specific, and the definition varies from class to class. A string is empty if it has no characters, and an array is empty if it contains no items.
Is there anything missing, or a tighter comparison that can be made?
.nil? can be used on any object and is true if the object is nil.
.empty? can be used on strings, arrays and hashes and returns true if:
String length == 0
Array length == 0
Hash length == 0
Running .empty? on something that is nil will throw a NoMethodError.
That is where .blank? comes in. It is implemented by Rails and will operate on any object as well as work like .empty? on strings, arrays and hashes.
nil.blank? == true
false.blank? == true
[].blank? == true
{}.blank? == true
"".blank? == true
5.blank? == false
0.blank? == false
.blank? also evaluates true on strings which are non-empty but contain only whitespace:
" ".blank? == true
" ".empty? == false
Rails also provides .present?, which returns the negation of .blank?.
Array gotcha: blank? will return false even if all elements of an array are blank. To determine blankness in this case, use all? with blank?, for example:
[ nil, '' ].blank? == false
[ nil, '' ].all? &:blank? == true
I made this useful table with all the cases:
blank?, present? are provided by Rails.
Just extend Julian's table:
Ref: empty?blank?nil?傻傻分不清楚
Quick tip: !obj.blank? == obj.present?
Can be handy/easier on the eyes in some expressions
Everything that is nil? is blank?
Everything that is empty? is blank?
Nothing that is empty? is nil?
Nothing that is nil? is empty?
tl;dr -- only use blank? & present? unless you want to distinguish between "" and " "
One difference is that .nil? and .empty? are methods that are provided by the programming language Ruby, whereas .blank? is something added by the web development framework Rails.
A special case is when trying to assess if a boolean value is nil:
false.present? == false
false.blank? == true
false.nil? == false
In this case the recommendation would be to use .nil?
Just a little note about the any? recommendation: He's right that it's generally equivalent to !empty?. However, any? will return true to a string of just whitespace (ala " ").
And of course, see the 1.9 comment above, too.
Don't forget any? which is generally !empty?. In Rails I typically check for the presence of something at the end of a statement with if something or unless something then use blank? where needed since it seems to work everywhere.
nil? is a standard Ruby method that can be called on all objects and returns true if the object is nil:
b = nil
b.nil? # => true
empty? is a standard Ruby method that can be called on some objects such as Strings, Arrays and Hashes and returns true if these objects contain no element:
a = []
a.empty? # => true
b = ["2","4"]
b.empty? # => false
empty? cannot be called on nil objects.
blank? is a Rails method that can be called on nil objects as well as empty objects.
Everybody else has explained well what is the difference.
I would like to add in Ruby On Rails, it is better to use obj.blank? or obj.present? instead of obj.nil? or obj.empty?.
obj.blank? handles all types nil, '', [], {}, and returns true if values are not available and returns false if values are available on any type of object.
Though there n-number of answers available to this question but I liked the way its being explained here so posted one more answer :-)
Look at the data metric below - Its self explanatory with the various data-types used across available methods for it.
Reference: https://blog.appsignal.com/2018/09/11/differences-between-nil-empty-blank-and-present.html
exists? method can be used to check whether the data exists in the database or not. It returns boolean values either true or false.
Rails 4
an alternative to #corban-brook 's 'Array gotcha: blank?' for checking if an arrays only holds empty values and can be regarded as blank? true:
[ nil, '' ].all? &:blank? == true
one could also do:
[nil, '', "", " ",' '].reject(&:blank?).blank? == true
nil? can be used on any object. It determines if the object has any value or not, including 'blank' values.
For example:
example = nil
example.nil? # true
"".nil? # false
Basically nil? will only ever return true if the object is in fact equal to 'nil'.
empty? is only called on objects that are considered a collection. This includes things like strings (a collection of characters), hashes (a collection of key/value pairs) and arrays (a collection of arbitrary objects). empty? returns true is there are no items in the collection.
For example:
"".empty? # true
"hi".empty? # false
{}.empty? # true
{"" => ""}.empty? # false
[].empty? # true
[nil].empty? # false
nil.empty? # NoMethodError: undefined method `empty?' for nil:NilClass
Notice that empty? can't be called on nil objects as nil objects are not a collection and it will raise an exception.
Also notice that even if the items in a collection are blank, it does not mean a collection is empty.
blank? is basically a combination of nil? and empty? It's useful for checking objects that you assume are collections, but could also be nil.

Resources