Checking for nil in view in Ruby on Rails
I've been working with Rails for a while now and one thing I find myself constantly doing is checking to see if some attribute or object is nil in my view code before I display it. I'm starting to wonder if this is always the best idea.
My rationale so far has been that since my application(s) rely on user input unexpected things can occur. If I've learned one thing from programming in general it's that users inputting things the programmer didn't think of is one of the biggest sources of run-time errors. By checking for nil values I'm hoping to sidestep that and have my views gracefully handle the problem.
The thing is though I typically for various reasons have similar nil or invalid value checks in either my model or controller code. I wouldn't call it code duplication in the strictest sense, but it just doesn't seem very DRY. If I've already checked for nil objects in my controller is it okay if my view just assumes the object truly isn't nil? For attributes that can be nil that are displayed it makes sense to me to check every time, but for the objects themselves I'm not sure what is the best practice.
Here's a simplified, but typical example of what I'm talking about:
controller code
def show
@item = Item.find_by_id(params[:id])
@folders = Folder.find(:all, :order => 'display_order')
if @item == nil or @item.folder 开发者_StackOverflow== nil
redirect_to(root_url) and return
end
end
view code
<% if @item != nil %>
display the item's attributes here
<% if @item.folder != nil %>
<%= link_to @item.folder.name, folder_path(@item.folder) %>
<% end %>
<% else %>
Oops! Looks like something went horribly wrong!
<% end %>
Is this a good idea or is it just silly?
No you should use
<% if @item.nil? %>
for example
@item1=nil
if @item1.nil? ### true
@item2 = ""
if @item2.nil? ### false
@item3 = []
if @item3.nil? ### false
@item4 = {}
if @item4.nil? ### false
To check An object is blank if it‘s false, empty, or a whitespace string.
use
<% if @item.blank? %>
ref:- this
for example
@item1=nil
if @item1.blank? #### true
@item2 = ""
if @item2.blank? #### true
@item3 = []
if @item3.blank? #### true
@item4 = {}
if @item4.blank? #### true
Your example code remade:
controller code. ( I assume this is ItemsController )
def show
# This will fail with 404 if item is not found
# You can config rails to pretty much render anything on Error 404
@item = Item.find(params[:id])
# doesn't seem to be used in the view
# @folders = Folder.find(:all, :order => 'display_order')
# this is not needed anymore, or should be in the Error 404 handler
#if @item == nil or @item.folder == nil
# redirect_to(root_url) and return
#end
end
view code, since the controller made sure we have @item
#display the item's attributes here
<%= item_folder_link(@item) %>
helper code:
# display link if the item has a folder
def item_folder_link(item)
# I assume folder.name should be a non-blank string
# You should properly validate this in folder model
link_to( item.folder.name, folder_path(item.folder) ) if item.folder
end
Anyway, I try to keep view very very simple. Usually if I see loops and conditionals in views, I try to refactor them into helpers.
Don't forget .try, which was added in Rails 2.3. This means that you can call something like the following:
@object.try(:name)
And if @object is nil, nothing will be returned. This is perhaps the built-in solution for sameera207's idea.
Ideally, you shouldn't be sending nil objects through to the view - however it's not always possible to avoid.
I personally think that if you are checking nil
in your views (and I think since the view is the ultimate presentation layer nil
should be checked in that level), you don't want to check it in the controller. (but this will not apply to all the places)
I would recommend you to create a method to check nil
(to make it little DRY) and pass your object and check if it is nil or not
something like:
def is_nil(object)
object.nil? ? '':object
end
and add it in the application controller and make it a helper (so that you can use it in both controllers and views)
(helper_method :is_nil
- add this line to your application controller)
and now you can pass the object you want to check if it is nil or not.
cheers,
sameera
Your controller is responsible for deciding which view is going to be rendered. If you can verify that your controller will never render this particular view without an item or item_folder then you do not need to check for nil values.
By can verify I mean that you have tests/specs that check which view is rendered for nil items and item_folders.
精彩评论