Rails how to handle error and exceptions in model - ruby-on-rails

So I'm parsing data from twitter api in rails using the twitter library, and sometimes the response from api might be like this:
{
error: "Invalid parameter"
}
And the model will raise an exception, right now I'm silently catch it and put the error.message into the log, how do I pass this exception to the controller so I can display it on the view? Thanks.
UPDATE:
The error is likely to happen because I'm allowing my customer to build queries, and they might put advanced queries like "https://search.twitter.com/search.json?since_id=1&&q=near:NYC%20within:15mi" which is supported by the twitter webpage but not by it's API. So I want to catch these kinda of error and display a flash message so the user can have some feedback.

I guess you could an attr_accessor. Something like twitter_errors.
In your model:
attr_accessor :twitter_errors
begin
#your twitter code here
rescue
self.twitter_errors = "whatever"
end
And in your controller, set the flash if #model.twitter_errors isn't empty.

The typical way is to use ActiveModel::Errors.
ActiveRecord uses this mixin extensively for validations. So in an ActiveRecord object you have access to errors.add(:base, TwitterError.to_s). You can use this to set the error when it is caught. And then you should be able to access it via the controller/view using ar_object.errors.
(There are already some helpers for displaying errors like this the docs have much more info)

Related

Handling Ruby Errors with multiple methods that must be executed if one fails

I've inherited a codebase where they have a call method to send out a document via multiple channels - FTP, SMS, and an Email service.
The way its currently set up is that if the email distribution fails, it will exit out of the method and ensure the other channels run. There currently seems to be very basic error handling and thats about it.
I need it to log an error then move on to the following methods -- without hopefully rewriting all the existing code
How can I go about doing this? Will the following work?
the main call method:
def call
add_customers_to_statement_distribution
statement.final!
copy_generated_pdf_to_final_version
copy_detailed_csv_to_final_version
copy_png_thumbnails_with_days_key_to_public_storage
copy_statement_to_public_storage
disribute_statement_by_email
DistributeToFTP.call(statement)
NotifyCustomersBySms.call(statement)
end
The method with my possible rescue statement added in:
def distribute_statement_by_email
DistributeStatementByEmail.call(statement_distribution,
SENDER_EMAIL,
final_customers_with_csv,
with_csv: true)
DistributeStatementByEmail.call(statement_distribution,
SENDER_EMAIL,
final_customers_without_csv,
with_csv: false)
rescue StandardDistributionError => error
error.new("Statement distributed by email has raised an issue: "\
"#{error.message}")
end
I want to make sure I handle the error in a graceful way.
Thanks!

How to handle validation errors in the view with Rails models?

Looking at the guide it seems like validation errors are handled by the model object's errors attribute so that if a validation fails, the error message goes to that attribute key in the errors hash. However, in my app, the validation failure results in a hard fail as in I see the exception page instead of it going back to the view. Is there something I can set to have it fail "softly"?
Alright try to find reason of failure and then try to put these statements inside ,begin rescue block.
Something like:
begin
# iceberg
rescue
# lifeboat
# maybe add error in the object
# something like: object.errors.add(:email, "Not valid")
end
What do think?

Rails - Logging events with tags or types and getting an email alert - best practices?

Wondering if someone could offer some best practice on logging techniques they use and maybe tagging certain events using airbrake or newrelic or loggly, or something of that nature?
For example, lets say I have events that should never hit my controller in theory, because they are protected on the front end - like a regular user being able to manage an admin. I prevent these controls from being outputted on the front end using ruby if statements. So if my controller gets hit with a request like this, then I know that either that code isn't working right, or someone is doing some hacking with the request being sent.
Either way I want to know. I just found myself writing:
#TODO: Log this. Either the front end is broken or the user is sending hacked requests
Wondering if anyone can offer some insight as to how they handle it, maybe with a third party logging tool, maybe with some sort of tag that I could set up email alerts with said tool?
Morgan from Airbrake here. We don't have tagging for Airbrake Exceptions.
One way to solve this with our service is to send a custom error to Airbrake from your controller.
The error will trigger an Airbrake notification email and you will be notified.
For Example:
# find me in app/controllers/some_controller.rb
# custom error
class ControllerAccessError < StandardError
end
def action
...
msg = 'front end is broken or the user is sending hacked request'
e = ControllerAccessError.new(msg)
Airbrake.notify_or_ignore(e)
end
Here is some more info on manually sending errors to airbrake with ruby:
https://github.com/airbrake/airbrake/wiki/Using-Airbrake-with-plain-Ruby
Jason from Loggly here. You can also setup a new Logger and then log anything you like. This uses pure Ruby code without any proprietary libraries. For example:
logger.warn("Regular user should not have access")
Even better, you can use JSON fields to make for easy reporting and filtering. I'm not a Ruby programmer, but I think it'd look something like this?
logger.warn({:username => username, :type => "Access Violation", :message => "Regular user should not have access"}.to_json);
In Loggly, you can setup alerts to be sent over email whenever you get a message matching this search
json.type:"Access Violation"

Defensively Handling ActiveRecord::RecordNotFound

I'm wondering what a best practice is for defensively handling ActiveRecord throwing a RecordNotFound exception. It seems that the default Rails behavior is to render a 404 error. That works fine when we're dealing with getting content-- it makes intuitive sense. When I'm doing something like creating an associated record and the parent model's record no longer exists, however, throwing a 404 seems more cryptic.
What if I wanted to, instead, return a validation error complaining that the record no longer exists? How might I go about doing this?
You should really never have a 404 error on your website: you should try to catch all errors and handle them gracefully.
It depends where you're getting the error. If you're in a controller, say, in the edit action, you can just use a rescue, perhaps in conjunction with .exists? so that no error is even actually thrown. Something like this:
def edit
if Model.exists?(params[:id])
#model = Model.find(params[:id])
else
flash[:error] = "Unable to find model with ID #{params[:id]}!"
redirect_to models_path
end
end
You can add a model level validation for this as well. Let's say you have a User which belongs to a Group (i.e, it has a group_id). This validation will ensure that not only a group_id is set, but that it maps to a real Group record.
# User.rb
validates_presence_of: group
That way if you're just throwing params in to a .create! or .save! method, the validation will fail and you'll get a standard Rails error message.
But, your controller should then use the non-exception throwing versions (.create and .save) and again you can do something like above to see if the object is valid, then set a flash if it's not.
Use validates_existence gem
https://github.com/perfectline/validates_existence

Rendering Error Messages in Custom Controller Action

I've added the following to my controller's create action:
def create
BatchUser.mass_insert_v2(params[:batch_user][:batch_name], params[:batch_user] [:batch_description], params[:batch_user][:quantity])
redirect_to batch_users_path
end
'mass_insert_v2', in my BatchUser model, starts like this:
def self.mass_insert_v2(batch_name, batch_description, quantity)
#batch_create = BatchUser.create! :batch_name => batch_name, :batch_description => batch_description
...
end
And then goes on to create X user accounts with random usernames and passwords. I've chosen this route because I found raw sql insertion to be faster than using activerecord on its own.
The problem I have is that I'm struggling to render my error messages. For example, batch_name must be present and unique.
I do get an error screen:
ActiveRecord::RecordInvalid in BatchUsersController#create
But no errors show.
Previously, I've had this check in my controller:
respond_to do |format|
if #batch_user.save
....
else
....
But that doesn't seem to work anymore. What can I do to display the errors on the page??
The create! (create with bang!) will throw an exception if the object fails validations. Unless you are planning on catching this exception and handling you might be better off just using create and then checking if the object was created successfully (has an id) and/or has errors.
There are several railsy ways of handling the finding and rendering error messages so you can experiment. However, knowing the following along with the important note above will help get you on track I think:
#batch_user = BatchUser.create(attr_hash) #will give you an object to work with instead of throwing an exception.
If you have an existing object:
#batch_user.valid? #will trigger the validations only and set the object's errors attribute with any relevant data and return a boolean value.
#batch_user.errors #gives you access to the error data (in 3.1 ActiveModel::Errors object)
As far as actually rendering the errors, like I said there are several options. I prefer either putting those error messages (obj.errors.full_messages) in the flash or using something like the dynamic_form plugin.

Resources