可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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.
回答2:
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
回答3:
Check Ick's maybe
:
a.b.maybe.c("d").maybe.e
or using a block:
a.b.maybe { |b| b.c("d").e }
回答4:
"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
回答5:
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?
回答6:
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 }
回答7:
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 }
回答8:
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
回答9:
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/