I'm just breaking into the ruby world and I could use a helping hand.
Suppose b
is nil
.
I'd like the following code to return nil
instead of a "NoMethodError: undefined method"
a.b.c("d").e
The first thing I tried was to overload 开发者_如何学编程NilClass's missing_method to simply return a nil. This is the behaviour I want except I don't want to be this intrusive.
I'd love it if I could do something like this
SafeNils.a.b.c("d").e
So it's like a clean way to locally overload the NilClass's behaviour.
I'd love to hear some ideas or great resources for digging in on this. I'm also quite open to other approaches as long as it's fairly clean.
Thank you very much.
You can use the inline rescue:
x = a.b.c("d").e rescue nil
x
will be nil
if b
is nil
.
Or, as someone commented in that link, you can use the andand gem (see docs):
x = a.andand.b.andand.c("d").andand.e
Starting from ruby v2.3.0 there is another way built into the language, the safe navigation operator (&.)
You can write: a&.b&.c&.d
and you will safely get nil if one of the calls in the chain returns nil.
You can read more about it here:
http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
I think you can find a great solution in rails but that solution follows a different approach. Take a look at the try method. It's a clean approach.
"try" is very clean, as lucapette said. More generally, you could also use a begin-rescue-end block too, depending on your situation.
begin
a.b.c("d").e
rescue NoMethodError=>e
return nil
end
Check Ick's maybe
:
a.b.maybe.c("d").maybe.e
or using a block:
a.b.maybe { |b| b.c("d").e }
Remark in advance: b
is a method, not a variable. So b 'is' not nil, it returns nil.
When 'b' is a method, why not modify b, so it returns something, what can handle nil.
See below for an example.
You may define the missing methods:
class A
def b
nil
end
end
class NilClass
def c(p);nil;end
def e;nil;end
end
a = A.new
a.b.c("d").e
But I think, a rescue may fit your need better:
class A
def b
nil
end
end
a = A.new
x = begin a.c.c("d").e
rescue NoMethodError
nil
end
An example, how you may define a nil-like example.
class A
def b
MyNil.new
end
end
class MyNil
def method_missing(m, *args, &block)
if nil.respond_to?(m)
nil.send(m)
else
self
end
end
#Simulate nils bahaviour.
def nil?;true;end
def inspect;nil.inspect;end
def to_s;nil;end
end
a = A.new
x = a.b.c("d").e
p x
puts x
p x.nil?
To use a safe_nils
similar to that you wrote:
def safe_nils &blk
return blk.call
rescue NoMethodError
return nil
end
safe_nils { a.b.c("d").e }
I've made the may_nil
gem for this. https://github.com/meesern/may_nil
Like @Sony Santos's safe_nils answer it uses a block to wrap the method chain and rescues NoMethodError, but will check for nil (and so will properly raise an exception for other classes).
require `may_nil`
may_nil {a.b.c("d").e} => nil (or the end result)
may_nil {0.b.c("d").e} => Exception: NoMethodError (b on Fixnum)
Unlike andand
or ike
's maybe
you don't need to pepper the method chain.
a.andand.b.andand.c("d").andand.e
==>
may_nil{ a.b.c("d").e }
One approach is to use inline assignment to local variables:
a && (ab = a.b) && (abcd = ab.c("d")) && abcd.e
For as long a chain as you've got here it isn't very elegant, but for a shorter chain it can be useful:
def date_updated(entry)
(updated = entry.updated) && updated.content
end
精彩评论