Handling nil (null) values in nested relations in Rails 2.3.x
The project currently I'm working has the following code in the view:
<%= product.provider.name %>
The above code is get the 'provider' for a product and display his开发者_如何学JAVA/her name. But my question was sometimes this code fails when'provider' gets nil. (I know it's little unusual but since I'm working with a legacy db this happens)
So to check the nil validation I have written the following code (in my ApplicationHelper)
def t(obj, attr)
obj.nil? ? "" : obj.send(attr.to_sym)
end
Now what I do is something like this:
<%= t(product.provider, "name") %>
Even though this works, I ran in to another problem, I found this code:
<%= product.provider.provider_type.title %>
The problem here is, in the above code either 'provider' or 'provider_type' can be nil.
What I'm looking at is an exception handling mechanism to handle any number of nested relationship.
Or is this a completely wrong path to handle nil values in nested relations? I'm running on Rails 2.3.8.
This is a common problem. You might want to check these:
- Object.try
- andand
Examples:
# With try
product.provider.try(:name)
product.provider.try(:provider_type).try(:title)
# With andand
product.provider.andand.name
You can rescue all exceptions:
<%= product.provider.provider_type.title rescue nil %>
since we can use #try methods or handle the exceptions inline, this method of usage breaks the Law of Demeter: Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
views should get the ready-to-use ojbects/variables, not chains. so you can try to use delegate method within models and cover other else with helpers. keet your views as clean as possible, look at them from designer's point of view.
I solved the problem this way:
class Object
def unless_nil(default = nil, &block)
if nil?
default
else
block[self]
end
end
end
product.provider.unless_nil(&:name)
product.provider.unless_nil(&:provider_type).unless_nil(&:title)
product.provider.unless_nil("Not specified", &:name)
It is also quite convenient to have Object#unless_blank
, Array#unless_empty
, Hash#unless_empty
.
Added:
Meanwhile using full block is even more convenient in some cases:
some_variable =
product.provider.unless_nil do |provider|
# some complex logic here using provider
end
I like it more than:
provider = product.provider
some_variable =
if provider
# ...
end
or using product.provider
everywhere. Matter of taste though.
精彩评论