Why are these two methods that seem the same operate differently? - ruby

My method exist­s_else takes two parameters: base and fallback. If base is nil, it returns fallback. If it's not nil, it returns base. A call to exist­s_else(true, false) should return true.
If I use a standard looking if-statement, true is returned like I thought it would be:
def exist­s_else(bas­e, fallb­ack)
unless base.­nil?
base
else
fallb­ack
end
end
a = true
exists_els­e( a, false­ )
# => true
If I use the inline implementation shown below, it returns false.
def exist­s_else(base, fallback)
base unles­s base.nil­? else fallback
end
a = true
exists_els­e( a, false­ )
# => false
Why does does it return false in the inline implementation?

Your assertion that
base unles­s base.nil­? else fallback
is supposed to be equivalent to the long-form unless statement is not true; in fact, you cannot use else inside a post condition. Ruby is interpreting the code as:
def exist­s_else(base, fallback)
base unles­s base.nil­?
else fallback
end
If you type this (or the version without the newline, as in your question) into IRB, Ruby gives the following warning:
warning: else without rescue is useless
That is to say, Ruby is trying to interpret the else as part of exception handling code, as in
def exists_else(base, fallback)
base unless base.nil
rescue ArgumentError => e
# code to handle ArgumentError here
else
# code to handle other exceptions here
end

You can not use an else statement when you're trying to do it in one line. When an else is necessary you must use the extended version.
Ruby thinks that the else in this case is related to error handling. You have to stick to your initial unless-end method.

I prefer this syntax to evaluate true/false checks:
condition(s) ? true_value : false_value
In your case, it would look like:
def exists_else(base, fallback)
base.nil? ? fallback : base
end
a = true
puts exists_else(a, false) # => true

Related

Switch Case in Ruby

I am trying to do a case statement. The code looks like this:
def true?(verbosity)
verb = verbosity.to_s
case verb
when verb.match?('yes')
true
when verb.match?('y')
return true
when verb.match('ja')
true
when verb.match?('j')
true
when verb.to_i(10).eql?(1)
true
else
false
end
end
Regardless of what I write in the case statement, even when the debugger says options[:verbosity] is "yes", the case statement instantly jumps to false and leaves the function. I even added explicit casting to it as string.
How do I have to write the statement to get a valid evaluation?
In this form, when you want to evaluate all case conditions separately, you should omit verb in the beginning, like this:
case
when verb.match?('yes')
true
when verb.match?('ja')
true
# ...
that said, don't you think it would be easier to read and nicer if you used regular expression magic to make this whole method much shorter? I'm thinking of something like this:
def true?(verbosity)
verb = verbosity.to_s
verb.match?(/yes|y|ja|j/i) || verb.to_i.eql?(1)
end
Here's a direct fix:
def true?(verbosity)
verb = verbosity.to_s
# If you wish to reference `verb` below in the branches like this,
# then DON'T make it the subject of the `case` statement:
case
when verb.match?('yes')
true
when verb.match?('y')
return true
when verb.match('ja')
true
when verb.match?('j')
true
when verb.to_i(10).eql?(1)
true
else
false
end
end
Or, here's a cleaner use of a case statement (without changing any behaviour above):
def true?(verbosity)
case verbosity.to_s
when /yes/, /y/, /ja/, '1'
true
else
false
end
end
...But this is doesn't quite do what you want, since it will return true for e.g. "yellow", but false for e.g. "TRUE". So, how about:
def true?(verbosity)
case verbosity.to_s
when /\A(yes|y|ja|1)\z/i
true
else
false
end
end
..But at this point, you may be thinking - why bother with a case statement here at all?? The other answer has already shown how you can take this a step further by removing the case statement; or alternatively you could even do this in 1 line and without a regex:
def true?(verbosity)
%w[yes y ja j 1].include?(verbosity.to_s.downcase)
end
Also, I note that there is some ambiguity in your post about whether this parameter is supposed to be called options[:verbose] or options[:verbosity]. It's unclear whether or not this has manifested as a bug in your complete code, but I felt it's worth mentioning.

Ruby way to check if a string is not blank?

What's the best way to check if a variable is not blank in an else if condition in Ruby (not Rails)?
elsif not variable.to_s.empty?
# do something
end
or
elsif !variable.to_s.empty?
# do something
end
or
elsif variable.to_s.length > 0
# do something
end
string = ""
unless string.to_s.strip.empty?
# ...
end
I just found out that ''.empty? returns true but ' '.empty? returns false. Even to_s.length for ' ' is not zero.
Maybe it is better to use strip as ' '.strip.empty?
You can use either
unless var.empty?
#do sth
end
or
unless var == ""
#do sth
end
or all of these with if and a negator !.
The source of the empty? method is analogous to the following:
def empty?
return length == 0
end
So, you can safely use
any_string.length != 0
Anyway, using that code inside an else if is a bit verbose, I would encourage you to define the present? method inside the String class.
class String
def present?
!empty?
end
end
Now you can write your code the following way:
if some_condition
# do something
elsif variable.to_s.present?
# do something else
end
This way you get a clear code, without using negations or unless who are hard to read.
Of course, there is one problem here, I took the present? name (and method) from Rails. present? returns true if the object is not blank, but strings with tabs or spaces (white characters) are considered blanks. So, this present? will return true to for the following strings:
"".present? # => false
" ".present? # => true
"\t\n\r".present? # => true
" blah ".present? # => true
It depends on what you want, high chances are that you want to get true for the first 3 strings, and false for the later. You could use #RamanSM approach and use strip to avoid empty spaces
class String
def present?
!strip.empty?
end
end
now, present? returns false for strings with white spaces
"".present? # => false
" ".present? # => false
"\t\n\r".present? # => false
" blah ".present? # => true
Note: Consider that String.present? is present in the ActiveSupport library (which ships with rails) if you add ActiveSupport or use Rails you should use ActiveSupport implementation instead.
If you prefer if to unless...
If you know your variable will be a String...if str[0]
With nil check...if str && str[0] OR if str&.[](0) (I prefer the latter but it might look odd to some people and requires Ruby >= 2.3).
Also...I'd be very careful about calling #to_s on anything because you could end up with unexpected results. If str turns out to be something that you weren't expecting...
str = false
str.to_s[0] # => 'f' (i.e. truthy)
str.to_s.empty? # => false
str = nil
str.to_s[0] # => nil (i.e. falsey)
str.to_s.empty? # => true
I think this caution applies to usage of #to_s in the other answer here as well. Exceptions can be your friend.
For the string (say abc) which is not defined/undefined we should check for abc.nil?
otherwise abc.blank? will throw (NoMethodError) undefined method empty? for nil:NilClass error

be false vs be_falsey in rspec [duplicate]

This question already has an answer here:
What is the difference between "be_true" and "be true" in RSpec
(1 answer)
Closed 8 years ago.
I know be false will just return true if the expected value is a pure false value, wherease be_falsey will return true if the expected value is either false or nil.
However, I fail to understand when would I use one or the other. I fail to see an example where be_falsey would be more useful than be false.
There are lots of situations in Ruby where you get a nil result and want to treat it like false. For example, suppose we have a method that handles an options hash, and we want the absence of an option to be the same as the option set to false:
def verbose?(opts)
opts[:verbose]
end
opts = { verbose: false }
expect(verbose?(opts)).to be_falsey # => PASS
opts = { verbose: true }
expect(verbose?(opts)).to be_falsey # => FAIL
opts = {}
expect(verbose?(opts)).to be_falsey # => PASS
Obviously this is a simplistic example (you could argue that verbose? should always return true or false), but similar scenarios are common in Ruby.
One example I can think of is for a unit test for a method. The method would be used in such a way that you are not really interested in the result, but only whether it is truthy/falsey.
Consider a method that checks if there is a record with a given value is in the db:
Class Foo
def self.has_value? value
Foo.find(value: value)
end
This method would be used as:
if Foo.has_value?(value)
...
Now, you could write a test like this:
let(:foo ){ create :foo )
expect(Foo.has_value?(1)).to == foo
But this obscures the intent of the method. The method's intent is not to find a foo. It is to tell you whether a foo with a given value exists or not.
To express the intent of the method it might be better to use
expect(Foo.has_value(1).to be_truthy
expect(Foo.has_value(2).to be_falsey
In Rails, if model.save is often used. This is the same pattern.

How to decide whether an optional argument was given or not in a ruby method

I have a method with an optional argument. How can I decide whether the Argument was given or not?
I came up with the following solutions. I am asking this question since I am not entirely satisfied with any of them. Exists there a better one?
nil as default value
def m(a= nil)
if a.nil?
...
end
end
The drawback with this one is, that it cannot be decided whether no argument or nil was given.
custom NoArgument as default value
class NoArgument
end
def m(a= NoArgument.new)
if NoArgument === a
...
end
end
Whether nil was given can be decided, but the same problem exists for instances of NoArgument.
Evaluating the size of an ellipsis
def m(*a)
raise ArgumentError if m.size > 1
if m.size == 1
...
end
end
In this variant it can be always decided whether the optional argument was given.
However the Proc#arity of this method has changed from 1 to -1 (not true, see the comment). It still has the disadvantage of beeing worse to document and needing to manually raise the ArgumentError.
Jorg W Mittag has the following code snippet that can do what you want:
def foo(bar = (bar_set = true; :baz))
if bar_set
# optional argument was supplied
end
end
How about
NO_ARGUMENT = Object.new
def m(a = NO_ARGUMENT)
if a.equal?(NO_ARGUMENT)
#no argument given
end
end

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?

Resources