ruby syntactic sugar: dealing with nils - ruby

probably asked already but I couldn't find it.. here are 2 common situation (for me while programming rails..) that are frustrating to write in ruby:
"a string".match(/abc(.+)abc/)[1]
in this case I get an error because the string doesn't match, therefore the [] operator is called upon nil. What I'd like to find is a nicer alternative to the following:
temp="a string".match(/abc(.+)abc/); temp.nil? ? nil : temp[1]
in brief, if it didn't match simply return nil without the error
The second situation is this one:
var = something.very.long.and.tedious.to.write
var = something.other if var.nil?
In this case I want to assign something to var only if it's not nil, in case it's nil I'll assign something.other..
Any suggestion?
Thanks!

In Ruby on Rails you have the try method available on any Object. According to the API:
Invokes the method identified by the symbol method, passing it any arguments and/or the block specified, just like the regular Ruby Object#send does.
Unlike that method however, a NoMethodError exception will not be raised and nil will be returned instead, if the receiving object is a nil object or NilClass.
So for the first question you can do this:
"a string".match(/abc(.+)abc/).try(:[], 1)
And it will either give you [1] or nil without error.

Forget that Python atavism!
"a string"[/abc(.+)abc/,1] # => nil

"a string"[/abc(.+)abc/, 1]
# => nil
"abc123abc"[/abc(.+)abc/, 1]
# => "123"
And:
var = something.very.long.and.tedious.to.write || something.other
Please note that or has a different operator precedence than || and || should be preferred for this kind of usage. The or operator is for flow control usage, such as ARGV[0] or abort('Missing parameter').

For the first I'd recommend ick's maybe (equivalent to andand)
"a string".match(/abc(.+)abc/).maybe[1]
I am not sure I understand the second one, you want this?
var = something.very.long.and.tedious.to.write || something.other

"a string".match(/foo(bar)/).to_a[1]
NilClass#to_a returns an empty array, and indexing outside of it gives you nil values.
Alternatively (what I do) you can splat the matches:
_, some, more = "a string".match(/foo(bar)(jim)/).to_a

For the first question, I think Bob's answer is good.
For the second question,
var = something.very.long.and.tedious.to.write.instance_eval{nil? ? something.other : self}

Related

Why does irb echo the right hand side of an assignment instead of the return value in the case of a setter method?

It seems surprising that in all other instances irb will echo the return value of a method. Why does assignment via a setter behave differently?
I'm using Ruby 2.2.2.
irb(main):001:0> def x=(value); puts "puts_from_x"; "returned_string_from_x"; end
=> nil
irb(main):002:0> self.x = 3
puts_from_x
=> 3
update
It has dawned on me that it echoes the rhs because that's the actual return value. Why is this?
Following the #Matz reply in this thread :
Setters always return the value they were originally assigned
It's a design choice. We defined the value of the assignment as the
value of the right hand expression, not the return value from the
assigning method.
Haven't found a concrete answer why, but this page as a discussion about it.
Specifically:
If you define an « assignment-like » method (with an equal sign at the end), Ruby will execute the method when you call it, but will always return the supplied parameter and never the result of the method.
Think about something like this, that many people would be used to seeing from other languages like C:
foo.x = foo.y = 3
You'd assume foo.x and foo.y were both set to 3, and because of this Ruby "feature", you'd be right.
edit: Arup's post has a good link to a why...

Best way to prevent returning last evaluated expression

Suppose I want to write a method in ruby whose last line is a method call but I do not want to return its return value. Is there a more elegant way to accomplish this other than adding a nil after the call?
def f(param)
# some extra logic with param
g(param) # I don't want to return the return of g
end
If you want to make it "poke you in the eye" explicit, just say "this method doesn't return anything":
def f(param)
# some extra logic with param
g(param) # I don't want to return the return of g
return
end
f(x) will still evaluate to nil but a bare return is an unambiguous way to say "this method doesn't return anything of interest", a trailing nil means that "this method explicitly returns nil" and that's not quite the same as not returning anything of use.
No, but if it is important that f indeed returns nil, and not whatever g(param) returns, then nothing is more elegant than spelling that out with a nil on the last line. Why would you want to obfuscate this away? Most of the time, elegance is in the explicit and the obvious.
A few tenants from The Zen of Python come to mind:
Explicit is better than implicit.
Simple is better than complex.
Readability counts.
No. If you want to return nil, the last expression has to evaluate to nil. You can do this with a terminating nil line or by surrounding the method body in nil.tap {} or however else you like, but it's pretty straightforward — the last expression evaluated gets returned.
As the others have said, no. However, if you want to avoid adding another line, you have a couple of options:
g(param); nil
g(param) && nil
The first will always cause f to return nil; the second will return false (if g returns false) or nil (if g returns a truthy value).
No, there is no other way than to either explicitly return nil or evaluate some other expression which implicitly evaluates to nil (e.g. ()).
If you want to add some kind of semantic marker that shows that you explicitly want to ignore the return value, you could invent some convention for that, e.g.:
def f(param)
# some extra logic with param
g(param) # I don't want to return the return of g
()
end
or
def f(param)
# some extra logic with param
g(param) # I don't want to return the return of g
_=_
end
which will make those cases easily grepable but probably won't aid much in understanding.
This is a design choice of Ruby which it shares with many other expression-based languages: the value of a block/subroutine/procedure/function/method is the value of the last expression evaluated inside the block. That's how it works in Lisp, for example.
Note that there are other choices as well. E.g. in Smalltalk, the return value of a method must be explicitly returned using the ↑ operator, otherwise the return value is nil. In E, which is heavily focused on security, this is even a conscious design choice: automatically returning the value of the last expression is considered a potential information leak.

`non-object-ness` of `nil` in ruby

From one of the online resource of Ruby I found the below statement:
The special object nil is, indeed, an object (it’s the only instance of a class called
NilClass). But in practice, it’s also a kind of non-object. The boolean value of nil is
false, but that’s just the start of its non-object-ness.
While nil responds to method calls as below,like other objects,what non-objectness author tried to say :
nil.to_s #=> ""
nil.to_i #=> 0
nil.object_id #=> 4
nil.inspect #=> "nil"
Can anyone help me here to understand the philosophy - non-object-ness of nil ?
nil is equivalent with null in other languages. Usually, null should not treated as a sane value.
However - as you may noticed - the syntax of Ruby language does everything over the method calls on objects, a lot more things than Python. Determining a sanity of values is a part of it.
Consider the following example
def foobar(arg)
if arg < 1
return nil
else
return "Oh, hi!"
end
end
res = foobar(rand(2))
puts res unless res.nil?
As you see, in the last line I check the nil-ness of the result with calling a nil? method. This is a most effective way to do it, because comparation operators can be overloaded and can do a very different things. The nil? returns with true only if the value can be treated as nil (usually, if the value is nil - but nil? method is overridable too, even if it is highly discouraged. Developers usually are not override this method).
Another useful property of nil it is has a to_s method, so you can x = "#{nil} and it results an empty string.
If nil weren't be an object, you cannot call nil? or other useful functions on that, but you can faced with a NullPointerException like in Java or a segmentation fault like in C/C++. And usually it is pointless.

Using ! on a method in Ruby

I am currently using capitalize!:
user_input.capitalize!
But, if the input is capitalized, it returns a nil.
I know I can use the method without !, but it will make a copy of the object and pass that instead of changing the object.
user_input.capitalize
How can I use !, but have it work for all cases?
I tried to downcase everything, then capitalize but the same problem exists in some cases.
What is the best way to capitalize all entries?
This is the context:
The user inputs "works", "Works", "WORKS", or "WoRkS"
case 1) user_input = "works"
case 2) user_input = "Works"
case 3) user_input = "WORKS"
case 4) user_input = "WoRkS"
user_input.capitalize! (case 1,3,4 = "Works"; case 2 = nil)
! methods, by convention, modify the receiver and return nil if they make no changes. The safest way to do it is to use two lines:
user_name.capitalize!
user_name
or #tap:
user_name.tap do |s|
s.capitalize!
end
Although, #tap is overkill here.
If all you care about is that the input gets capitalized, use capitalize! and don't worry about the nil.
If you can't have a nil at a certain point in your code (for some reason), don't use capitalize!. Instead do this:
user_input = user_input.capitalize
At the end of the day, either way is fine, idiomatic and will get the input into the state you want it. In and of itself, a nil return value isn't a bad thing - although you may not always want it as a final return value.

Converting an empty string into nil in Ruby

I have a string called word and a function called infinitive such that
word.infinitive would return another string on some occasions and an empty string otherwise
I am trying to find an elegant ruby one line expression for the code-snippet below
if word.infinitive == ""
return word
else return word.infinitive
Had infinitive returned nil instead of "", I could have done something like
(word.infinitive or word)
But since it does not, I can't take advantage of the short-circuit OR
Ideally I would want
1) a single expression that I could easily embed in other code
2) the function infinitive being called only once
3) to not add any custom gems or plugins into my code
The ActiveSupport presence method converts an empty (or blank?) string to nil. It's designed for your exact use case:
word.infinitive.presence || word
Note that you can easily use ActiveSupport outside of rails:
require 'active_support/core_ext/object/blank'
You can use a regex like this article suggests
(word.infinitive[/.+/m] or word) #Fancy way to convert "" to nil
If you're not ashamed of monkeypatching and abusing syntax, this would work:
class String
def | x
if empty? then x else self end
end
end
Then you can say word.infinitive | word, which actually scans fairly naturally, if you ask me.
However, I think a better idea would be to modify the infinitive method, or add a version of it that returns the word unchanged.
Edit: Here's a possibly more elegant solution:
[word.infinitive, word].find {|x| not x.empty?}
Do the right thing - fix infinitive so that it returns nils instead of blank strings, or wrap it with your own interface if you really can't touch it.
Or you can monkeypatch a new function to String without having to abuse syntax.
class String
def squash
self.empty? ? nil : self
end
end
Now you can do
puts var.squash or "default text"
I'm not a native english speaker so I don't know if squash is the best word. Is there a word that would better convey the idea of "turn into nil only if empty"?
You could use the ternary operator (boolean ? true-val : false-val) with String#empty?
return word.infinitive.empty? ? word : word.infinitive
if you only want to call infinitive once:
return (inf = word.infinitive).empty? ? word : inf
You may also want to consider adding some memoization to your code.

Resources