Handling an ActiveRecord error if database is empty - ruby-on-rails

I'm working on a rails 4 app, and i have the following controller code
def index
#issue = Issue.find(1)
#sections = #issue.sections
#articles = #issue.articles
end
which breaks if the database is empty with the error: "Couldn't find Issue with id=1". What is the proper way to check for this in a way that if nothing is in the db it doesn't raise an error?

One method you can use is the exists? active record method, like so:
#issue = Issue.where(id: 1)
if #issue.exists?
# do something if it exists
else
# do something if it is missing
end
Side note: Since you're attempting to find by id, you don't necessarily need the .where portion; you can simply do: Issue.exists?(1).
exists? documentation on APIDoc

In most cases such exception is expected and recommenced. For example, you can rescue it with a custom 404 page.
Anyway, if you really don't want that, you can use find_by method which will output nil if nothing found
#issue = Issue.find_by(id: 1)

you can handle that exception in your controller
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
def record_not_found
flash[:alert] = "invalid information"
redirect_to root_url
end
or you can use a where clause
#issue = Issue.where(id: 1).first
now check for nil by
#issue.nil?

Related

Rails 4.0 record not found

on my edit action I never rise the record not found if the record does not exist. What I'm doing wrong.
Here is my edit action
class OffersController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
def show
#offer = Offer.find(params[:id])
end
def edit
#offer = Offer.find_by(edit_hash: params[:edit_hash])
#country = Country.find_by(name: #offer.country)
#states = State.find(:all, :conditions => { country_id: #country })
end
private
def record_not_found
render text: "404 Not Found", status: 404
end
end
I always get undefined method `country' for nil:NilClass for my unexist edit record.
Also I raise the record not found on my show action, but I would like to use the 404.html page that I have on my public folder. How can I use this file???
Thanks in advance
The problem is that your line #offer = Offer.find_by(edit_hash: params[:edit_hash]) is not responding with ActiveRecord::RecordNotFound. It's responding with nil.
You can see this by opening up your Rails console from your app's directory with rails c. In the console, put this in:
#offer = Offer.find_by(edit_hash: params[:edit_hash])
You'll see that its output is => nil. You can then type #offer and you'll see its output, again, is => nil. Now, put this line into the console:
#offer = Offer.find(99999)
You'll see that its output is ActiveRecord::RecordNotFound: Couldn't find Offer with id=99999.
To fix the problem, add a ! to your find_by calls, so they are like this:
#offer = Offer.find_by!(edit_hash: params[:edit_hash])
This will cause Rails to respond with ActiveRecord::RecordNotFound: ActiveRecord::RecordNotFound instead of nil.

How to rescue from bad url

I have a method in my application that finds a photo from the og:image tag of a link that is submitted. In my create action, I use the method photo_form_url, described below.
def photo_from_url(url)
if !Nokogiri::HTML(open(url)).css("meta[property='og:image']").blank?
photo_url = Nokogiri::HTML(open(url)).css("meta[property='og:image']").first.attributes["content"]
self.photo = URI.parse(photo_url)
self.save
end
end
However, this produces an error if a bad url is entered.
I tried to rescue as below, but this gives me an "undefined method redirect_to"
def photo_from_url(url)
begin
if !Nokogiri::HTML(open(url)).css("meta[property='og:image']").blank?
photo_url = Nokogiri::HTML(open(url)).css("meta[property='og:image']").first.attributes["content"]
self.photo = URI.parse(photo_url)
self.save
end
rescue OpenURI::HTTPError
redirect_to :back, notice: 'link's broken!'
end
end
What am I doing wrong?
According to your answer to my comment, your function photo_from_url is defined in the model. Trying to redirect a user within a model is not possible as shown by the error you are facing.
Bear in mind that your model can be called outside of a browsing session environment. EG:
tests
rake task
You should thus never, ever put any code that has to do with manipulating the user browser, or the user session within your models. This is the job of the controller.
So what you need to do is simply raise an exception or return a specific value in your model when you are encountering a bad url. And react to that exception / return value in your controller by redirecting the user. This ensure that anything that has to do with the user browser stays in the controller, and that you could implement a different behavior in a rake task if encountering the same error.
So, your model should do stuff, and raise errors when it can't :
# Link.rb
def photo_from_url(url)
if !Nokogiri::HTML(open(url)).css("meta[property='og:image']").blank?
photo_url = Nokogiri::HTML(open(url)).css("meta[property='og:image']").first.attributes["content"]
self.photo = URI.parse(photo_url)
self.save
end
end
Your controller should ask your model to do stuff, and deal with the user if there is a problem :
# link_controller
# in create
begin
link.photo_from_url(url)
rescue OpenURI::HTTPError
redirect_to :back, notice: 'link's broken!'
end

How to handle active record find params[:id] in controller?

I'm wondering what is best practive for handling this type of situation in ruby on rails.
users_controller.rb
def show
#user = User.find params[:id]
end
If user is not found it throws an exception which isn't catched anywhere so it will display some ugly things to enduser.
Solution would be pack it into begin...rescue...end block:
def show
begin
#user = User.find params[:id]
rescue
flash[:error] = "User not found"
redirect :action => :index
end
end
although I've never seen such code in any rails article or tutorial I've seen.
What is the proper way of handling this type of situations?
See docs rescue_from
It depends on your requirement.
But you want a general solution then you can have a rescue block in ApplicaionController which will handle the RecordNotFound exception.
You can do
def show
#user = User.find_by_id(params[:id])
unless #user
flash[:error] = "User not found"
redirect :action => :index
end
end
But you cant expect you will call a link with id which is not in the db from within the application. Please see the answer of the question
The development environment will show you ugly error messages, but a production environment will just give an HTTP 404 error (page not found) when the id is invalid.
I think that you may be able to fix this with
#user = User.find(params[:id] = current_user)

Ruby on Rails: Saving two things to the database in controller

My 'create' function in my 'Message' controller is something like this:
def create
#message = Message.new(params[:message])
#message2 = Message.new(params[:message])
#message.sender_deleted = false
#message2.sender_deleted = true
if #message2.save
...
else
logger.debug("SAVE DIDN'T WORK")
For whatever reason, message2 cannot be saved, but #message can. I believe this is because you need to save only a variable named #message, but I can't figure out how to get around this. I need to, on this save, save multiple things to the database - is there some other way to do this or am I doing this completely wrong?
Thanks for your help
There's no reason you can't save more than once in an action, though why you'd want to do such a thing is debatable. You'll want to put the saves in a transaction so you only save when both records are valid. save! will raise an exception when the save fails.
def create
#message = Message.new(params[:message].merge(:sender_deleted=>false))
#message2 = Message.new(params[:message].merge(:sender_deleted=>true))
Message.transaction do
#message.save!
#message2.save!
end
redirect_to .... # handle success here
rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid
# do what you need to deal with failed save here,
# e.g., set flash, log, etc.
render :action => :new
end
end

Error handling in rails

Oddly I'm having a hard time finding good docs about basic error handling in rails. I'd appreciate any good links as well as thoughts on a handling errors in a really basic method like this:
def self.get_record(id)
People.first( :conditions => ["id = ?", id] )
end
1) I could verify that id != nil, and that it's numeric.
2) I could also then verify that a record is found.
Is there anything else?
Are both #1 and #2 recommended practice? In both cases would you simply create a flash message with the error and display it, or is that giving away too much information?
As I'm sure you know, this is just like People.find(id), except that find raises an error.
However, People.find_by_id(id) returns nil if no record is found, which I suspect takes care of all you need. You don't need to check that what you put into ActiveRecord is the correct data type and the like; it handles SQL injection risks, so to check ahead of time would not affect actual behavior.
If we're just looking at the show action, though, there's an even more elegant way: rather than using find_by_id and checking for nil, use find, let an error bubble up, and let the controller catch it with rescue_from. (By default, in production, ActiveRecord::RecordNotFound will be caught and rescued by showing a generic 404, but you can customize this behavior if necessary.)
class UsersController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, :with => :not_found
def show
#user = User.find params[:id]
end
protected
def not_found
flash[:error] = "User not found"
redirect_to users_path
end
end
Code untested, for illustrative purposes only ;)
Don't do flash[:notice]'s insted just say that "Record not found"
The two things required by you can be done as follows:
1) I could verify that id != nil, and that it's numeric.
def self.get_record(id)
People.first( :conditions => ["id = ?", id] ) if id.integer? unless id.blank?
end
2) I could also then verify that a record is found.
def self.get_record(id)
#people = People.first( :conditions => ["id = ?", id] ) if id.integer? unless id.blank?
flash[:notice] = #people.blank? # this will print true/false depending on value in #people
end
Hope it works for you. :D

Resources