Reputation: 33795
In my /post/index.html
view, I have this line:
<%= post.status.upcase %>
Whenever there is a post
that has a status with a nil
value, I get this error:
NoMethodError at /
undefined method `upcase' for nil:NilClass
How do I safely handle all nil
values throughout my app so it doesn't throw an error like this, it just gracefully degrades or even just ignores it?
Do I have to go through every single view and every single object that may have an attribute that can be returned nil and add in exception handling for each?
Is there a "Rails-y" DRY way to handle this?
Upvotes: 1
Views: 9056
Reputation: 181
I just wanted to update this thread with one more option: Ruby now (as of 2.3) gives us a safe navigation operator, the &.
syntax.
So:
post.status.upcase
Would become:
post.status&.upcase
Similarly to Rail's try
method, the whole chain will return nil
if it encounters NoMethodError
on a nil
.
Upvotes: 6
Reputation: 16768
How is post
defined? Normally in a controller you would set an instance variable such as @post
. I will assume that post is somehow derived from a model, in which case what you really want to do is to ensure that you never save a post with a status of nil to your database. You should have a validation on the Post models to ensure that the status is always present, and then check that the Post object is valid when it is created. So something like this:
in your model:
class Post < ActiveRecord::Base
validates :status, presence: true
...
and then in your posts_controller.rb controller:
def create
...
if @post.save
#handle successful save
else
# handle unsuccessful save
end
this way you never end up with bad data in your database.
I guess an other (hacky) way to solve this problem would be to create a method in your model to ensure you never get a null from the status methos, so something like this:
class Post
...
def status
read_attibute(:status) || "" # return an empty string is status is null - this is a hack, I should fix this at some point by making sure the a status of nil is never saved to the database!
end
or based on your comment in an above answer, you could always monkey patch active record to never return nil, and instead return some default value for each class.
def read_attribute(attr_name)
attr_name = attr_name.to_s
if !(value = @attributes[attr_name]).nil?
if column = column_for_attribute(attr_name)
if unserializable_attribute?(attr_name, column)
unserialize_attribute(attr_name)
else
column.type_cast(value)
end
else
value
end
else
nil # return whatever you think is appropriate
end
end
Upvotes: 0
Reputation: 232
Put this in Application_controller.rb
rescue_from Exception::NoMethodError, :with => :render_error
private
def render_error
render :file => "#{Rails.root}/public/methodnotfound.html", :status => 404, :layout => false
end
And also create the html with the message "methodnotfound.html".
Upvotes: 0
Reputation: 19899
Ignoring nil is a bad idea IMHO. It will lead to subtle errors that will be impossible to track down. If, however what you rally want for your example is to output an empty string (or nothing at all) when status
is nil you have a couple of options. For what I think you're doing I prefer the first one, followed closely by the second and hate the last two.
post.status.to_s.upcase
post.status.try(:upcase)
post.status.present? ? post.status.upcase : 'NO STATUS. OH NO'
begin
post.status.upcase
rescue
'NO STATUS. OH NO'
end
Upvotes: 5