I have a method, create, which calls a private method get_title:
def create
#article = #user.articles.new(article_params)
#article.title = get_title(#article.link)
if #article.save
redirect_to user_path, success: "Article Saved."
else
flash.now[:danger] = "Failed to save article."
end
end
The get_title method works so long as there are no errors in the get request:
def get_title(url)
request = HTTParty.get(url)
document = Nokogiri::HTML(request.body)
title = document.at_css "title"
return title.text
end
I can catch the error with a begin/rescue before the Nokogiri call:
begin
HTTParty.get(url)
rescue HTTParty::Error
...
rescue StandardError
...
else
request = HTTParty.get(url)
end
I don't know how to stop the method and return the error to the create method.
How do I return an error from the rescue statement to the create method and display that error message in a flash?
What you probably want to do is "harden" this method so it doesn't explode unless something really unusual happens:
class TitleFetchError < StandardError
end
def get_title(url)
request = HTTParty.get(url)
Nokogiri::HTML(request.body).at_css('title').text
rescue HTTParty::Error => error
raise TitleFetchError, error.to_s
rescue StandardError => error
raise TitleFetchError, error.to_s
rescue ... (some other type of error, e.g. Nokogiri parse errors)
end
Where in my opinion that should actually be a method on the #article model, like #article.fetch_title which would do the same thing, using its own link property.
One thing to note is this pattern for writing controller actions helps simplify things:
def create
#article = #user.articles.new(article_params)
#article.title = get_title(#article.link)
#article.save!
redirect_to user_path, success: "Article Saved."
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved
redirect_to user_path, danger: "Failed to save article."
rescue TitleFetchError => error
redirect_to user_path, danger: "Failed to save article. Error: #{error}"
end
Where you have a nice clean "normal" path, and then your exceptions are handled on the side if/when that becomes necessary. This keeps the two flows separated and avoids additional nesting from the if.
Related
I'm trying to handle ActionController::UnpermittedParameters.
I've defined a block in ApplicationController
rescue_from ActionController::UnpermittedParameters do |error|
message = "Invalid parameter: %s. " % error.params.to_sentence
message << 'Please verify that the parameter name is valid and the values are the correct type.'
format.html { redirect_to :back, alert: 'You passed wrong params! ' + message }
end
But, when I execute code, which should be handled, the rescue block is not executed.
What am I doing wrong?
I guess your request is not of HTML format, so format.html { } block will not be executed.
Try to leave only redirect_to :back, alert: 'You passed wrong params! ' + message part
I don't know what was wrong about my question, but I write my own ErrorHandler module, which works fine now.
# Error module to Handle errors globally
# include Error::ErrorHandler in application_controller.rb
module Error
module ErrorHandler
def self.included(klass)
klass.class_eval do
rescue_from ActionController::UnpermittedParameters, with: :unpermitted_parameter
end
end
private
def unpermitted_parameter(error)
message = "You have entered an unpermitted parameter: %s. " % error.params.to_sentence
message << 'Please verify that the parameter name is valid and the values are the correct type.'
Rails.logger.info(Rainbow(" !!! UNPERMITTED: !!! #{error.to_s}").fg(:red))
respond_to do |format|
format.html { redirect_back fallback_location: { action: "index" },
:alert => message }
format.js
end
end
end
end
Is this the correct way to do a rescue in a block?
Also, is it the shortest?
def rescue_definition
begin
user.begin_rescue
rescue Example::ParameterValidationError => e
redirect_to :back, error: e.message_to_purchaser
rescue Example::ProcessingError => e
redirect_to :back, error: e.message_to_purchaser
rescue Example::Error
redirect_to :back, error: e.message_to_purchaser
else
if user
flash['success'] = 'OK'
else
flash['error'] = 'NO'
end
end
redirect_to :back
end
The idea to use begin, rescue, ensure is to put the methods than can generate an error in the begin block, then you can handle the errors in one o more rescue blocks and finally you can put an optional ensure block for sentences that you want to execute on both success or fail scenarios for example release resources.
When you want all the method be handled by begin, rescue, ensure blocks, then begin keyword is optional, you can use or not the keyword, since you are asking for the shortest option, you will need to do some minor changes to your code:
def rescue_definition
user.begin_rescue
# and any other stuff you want to execute
if user
flash['success'] = 'OK'
else
flash['error'] = 'NO'
end
redirect_to :back
rescue Example::ParameterValidationError => e
redirect_to :back, error: e.message_to_purchaser
rescue Example::ProcessingError => e
redirect_to :back, error: e.message_to_purchaser
rescue Example::Error
redirect_to :back, error: e.message_to_purchaser
end
If you want it even shorter you can rescue multiple exception types in one rescue block:
def rescue_definition
user.begin_rescue
# and any other stuff you want to execute
if user
flash['success'] = 'OK'
else
flash['error'] = 'NO'
end
redirect_to :back
rescue Example::ParameterValidationError, Example::ProcessingError, Example::Error => e
redirect_to :back, error: e.message_to_purchaser
end
Aguar's post handles the current implementation. If you handle all the errors the same way then you can have them inherit from a custom class and just catch them all with 1 rescue call.
def stuff
user.stuff
if user
flash['success'] = 'Ya'
else
flash['error'] = 'Nah'
end
redirect_to ....
rescue CustomClassError => e
redirect_to :back, error: e.message_to_purchaser
rescue NonCustomErrors => e
#handle it
end
This exception not handling this error.
There is no question update template, after question update it will redirect to index page, but when the model validation got error while saving the below message is showing.
def update
begin
programme..
......
Question.save
flash[:sussess] = "Question created"
redirect_to :action=>'index'
rescue ActionView::MissingTemplate
flash[:error] = "Duplicate question,exception due to the model validation"
redirect_to :action=>'index'
end
What is the way to correct the exception handling for the following scenerio.
In this scenario we need to handle in the bellow manner (ActiveRecord::RecordInvalid).
I solved it by adding this rescue to the code.
begin
.....
.....
rescue ActiveRecord::RecordInvalid => invalid
flash[:error] = t('flash_notice.qs_duplicate')
return redirect_to (:back)
end
Thank you.
def update
if #question.update(question_params)
redirect_to action: 'index'
else
render 'edit'
end
end
Assuming you have errors displaying on your form then just render the edit template.
I have a rails model with a start_time timestamp, which should obviously be in the future, which is what my validation should ensure.
appointment.rb
validate :start_time_in_future, :on => :create
private
def start_time_in_future
errors.add(:base, 'Start time must be in the future') unless self.start_time > Time.now
end
appointments_controller.rb
around_filter :start_time_in_future
def create
#appointment = Appointment.create(foo_params)
redirect_to #appointment
end
private
def start_time_in_future
begin
yield
rescue ActiveRecord::RecordInvalid => e
redirect_to request.referrer, :alert => e.message
end
end
And it all works just fine, but its so over the top. Can't I just have a custom validation that fails with a message instead of an exception?
I think you can do that by changing your create method like this
def create
#appointment = Appointment.new(foo_params)
if #appointment.save
redirect_to #appointment
else
render "new"
end
end
In your new template, just show error messages by retrieving it from #appointment object like this
#appointment.errors.full_messages
This is my fault and I feel like an idiot.
I made a class method called .confirm! that uses .save! with a bang.
If you want exceptions, use bang methods, if you don't, use .save and .create()
I can't redirect correctly, it keeps throwing me ActiveRecord::RecordInvalid but it should be redirecting :back to the original page.
def create_multiple
Product.transaction do
begin
#products = current_user.products.create!(params[:products].map { |_k, p| p.merge params[:product] })
redirect_to :back, :notice => "Success!"
rescue ActiveRecord::Rollback
redirect_to :back, :notice => "An error occured, please try again."
end
end
end
end
How do I get it to redirect?
If you want to catch the ActiveRecord::RecordInvalid exception, then why aren't you rescuing that instead of ActiveRecord::Rollback?
def create_multiple
Product.transaction do
#products = current_user.products.create! ...
end
notice = "Success!"
rescue ActiveRecord::RecordInvalid
notice = "An error occurred, please try again"
ensure
redirect_to :back, :notice => notice
end
I would write this function something like the above.