views:

181

answers:

5

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 == 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?

A: 

No yuu 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 @item3.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 @item3.blank? ### true
Salil
A: 

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.

Steve Weet
A: 

Hi seaneshbaugh,

I Personally thinks that if you are checking nil in your views (and I think since the view is the untimate presentation layer nil should be checked in that level), you dont want to check it in controller. (but this will not apply for 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 a nil or not.

cheers, sameera

sameera207
+2  A: 

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.

Aaron Qian
A: 

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.

Sam Phillips