How to log exceptions automatically in Rails? - ruby-on-rails-3

If there is an exception in a controller action and rescue is used, Rails does not show the exception either in the browser (which is O.K), nor does it write the exception to the log file.
Is there a way to get Rails to write the exception to the log file in this case?
Example:
def some_action
2/0
rescue
#this code is called, but Rails does not write the exception to the log
end

You're rescuing the exception, so it won't log it because nothing bad actually happened since it was, in fact, "rescued".
You can put the following code within your rescue block to put an entry in the log:
logger.warn "Exception rescued!"
You can read more about using the logger in the Rails Guides.

I ended up using rescue_from in the ApplicationController, logging the exception message and backtrace, and then using params[:controller] and params[:action] to determine what controller/action to redirect to.
For example, if PostsController#show exception'ed out, I would redirect to PostsController#index from within rescue_from. It's been working so far, so it doesn't seem to be a bad thing to do redirect_to from within rescue_from. Time will let me know, I'm sure! (Just need to make sure that my redirects don't cause some infinite loops!)
And just in someone is interested in this hack (try at your own risk!):
class ApplicationController < ActionController::Base
def determine_redirect_to_path(controller,action)
...
end
rescue_from StandardError do |exception|
backtrace_size = exception.backtrace.size
if backtrace_size >= 2 then max_range = 2
elsif backtrace_size >= 1 then max_range = 1
end
if max_range > 0
s = "rescued_from:: #{params[:controller]}##{params[:action]}: #{exception.inspect}\n#{exception.backtrace[0..max_range].to_s}\n"
logger.error s
end
redirect_to determine_redirect_to_path(params[:controller],params[:action])
end
end

Related

Rails 5 Minitest ActionView::Template::Error: nil is not a valid asset source

I upgraded my Rails Application from 4.2 -> 5.0.0.1.
Other TESTS works fine (e.g. Model, Helper, Feature), but havinf trouble with my Controller Test.
I have read about Keyword arguments in controller & integration tests in Rails 5. So I changed the code structure as given below...
ActionView::Template::Error: nil is not a valid asset source
setup do
#logo = plogos(:main_logo)
end
test "should get edit" do
puts #logo.id // just to check...working fine
get :edit, params: {id: #logo.id}
assert_response :success
end
But I got new error with ActionView.
Is there anyone encountered and fixed the same issue, please help!
Thank you!
You may want to add some logtrace, probably it hints you where it went wrong.
May it be that the main_logo-fixture doesn't have an image? Since Rails 5 image_tag raises this error when given an nil-value, see also: Rails, "nil is not a valid asset source" for a particular image_tag (Carrierwave)
Besides that, typically the new scaffolded code would look as follows:
require 'test_helper'
class LogosControllerTest < ActionDispatch::IntegrationTest
setup do
#logo = plogos(:main_logo)
end
#...
test "should get edit" do
get edit_logo_url(#logo)
assert_response :success
end
#...
end

Rails Quick and simple way to check valid URL (not format, but actual link)

I am setting session[:return_to] for a lot of my routes to take users back to where they were before. I'm trying to create a generic checker to make sure that the link is valid, not the format, (http://somevalidurl) I don't care about that but that the actual link exists.
The main problem is when they are on a show page and then go to the edit page and delete an entry, the return_to takes them back to show page and we get a routing error because the entry no longer exists.
I would think that Rails had some sort of system for this in place.
I have something like this:
Net::HTTP.get_response(URI.parse(session[:return_to])) rescue false
But that takes forever to check and the page ends up taking a long time to refresh.
Ideas are welcome.
Checking if a previous url is valid is unnecessary, because such cases are not common routes to use your app. They are just exceptions. You only need a plan when exceptions happens, instead of dealing with it in every common route.
Simple solution: redirect to root
class ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
redirect_to root_path, alert: "The record does not exist"
end
end
Advanced solution: redirect to controller's index page
class ApplicationController
rescue_from ActiveRecord::RecordNotFound do |exception|
# The message like: "Couldn't find foo with id=123"
klass = exception.message.split(' ')[2]
if klass
# suppose your index path is conventional foos_path
path = send "#{klass.pluralize.underscore}_path"
else
path = root_path
end
redirect_to path
end
end

How to rescue custom exceptions coming from a middleware in Rails 3.2?

I have a Rails 3.2 application that uses Apartment, which is used as a middleware. Apartment throws an Apartment::SchemaNotFound exception and there is no way to rescue it with rescue_from from the ApplicationController. I thought I'd use config.exceptions_app as described in point #3 in this blog post, but I can't set the router as the exception app, I assume I have to create my own.
So the question is: How do I proceed?
We've purposefully left Apartment pretty minimal to allow you the handle the exception itself without really needing any Rails specific setup.
I'd do something similar to what #jenn is doing above, but I wouldn't bother setting a rack env and dealing with it later, just handle the response completely in rack.
It's typical for instance that you maybe just want to redirect back to / on SchemaNotFound
You could do something like
module MyApp
class Apartment < ::Apartment::Elevators::Subdomain
def call(env)
super
rescue ::Apartment::TenantNotFound
[302, {'Location' => '/'}, []]
end
end
end
This is a pretty raw handling of the exception. If you need something to happen more on the Rails side, then #jenn's answer should also work.
Check out the Rack for more details
I had a similar problem with another piece of middleware throwing a custom exception, so I haven't actually looked at Apartment at all, but maybe something like this:
#app/middleware/apartment/rescued_apartment_middleware.rb
module Apartment
class RescuedApartmentMiddleware < Apartment::Middleware
def call(env)
begin
super
rescue Apartment::SchemaNotFound
env[:apartment_schema_not_found] = true # to be later referenced in your ApplicationController
#app.call(env) # the middleware call method should return this, but it was probably short-circuited by the raise
end
end
end
end
Then in your environment:
config.middleware.use(Apartment::RescuedApartmentMiddleware, etc)
To access the env variable you set from ApplicationController or any controller:
if request.env[:apartment_schema_not_found]
#handle
end
Combo of How to rescue from a OAuth::Unauthorized exception in a Ruby on Rails application? and How do I access the Rack environment from within Rails?

Rails 3 + PostgreSQL + RSpec: App works fine but RSpec example fails

I am developing this Rails 3.2 application using the Apartment gem as middleware. The application itself works perfectly and all the RSpec examples also work perfectly when ran individually. However, when I run all the tests at the same time using the bundle exec rspec command, there are two examples that fail in two different controller specs and they do exactly the same thing. Here are the two examples in question:
In the issues_controller_spec.rb file:
describe "GET 'new'" do
# ...
context "for authenticated users" do
before(:each) do
controller.log_in(create(:user))
get :new
end
# ...
it "should create a new issue instance and put it in an instance variable" do
assigns(:issue).should be_an_instance_of Issue
end
end
end
In the users_controller_spec.rb file:
describe "GET 'new'" do
# ...
context "for authenticated users" do
# ...
context "for admin users" do
before(:each) do
admin = create(:admin)
admin.add_role :admin
controller.log_in(admin)
get :new
end
# ...
it "should create a new User instance and put it in an instance variable" do
assigns(:user).should be_an_instance_of User
end
end
end
end
These two examples are affected by a before hook:
before(:each) do
client = create(:client)
#request.host = "#{client.account_name}.lvh.me"
end
When creating a new Client, there is an after_create callback:
# Create the client database (Apartment) for multi-tenancy
def create_client_database
begin
Apartment::Database.create(self.account_name)
rescue Apartment::SchemaExists
return
rescue
self.destroy
end
end
And there is where the examples fail. Now if I remove the begin...rescue...end block and keep the line Apartment::Database.create(self.account_name) I get the following exception in the failling examples:
ActiveRecord::StatementInvalid:
PG::Error: ERROR: current transaction is aborted, commands ignored until end of transaction block
: SET search_path TO public
Again, if I run the examples individually, they pass but if I run all the examples, the two examples above fail.
Does anyone know what I am doing wrong please?
Note: The whole application code can be found here.
I solved this problem by wrapping the line client = create(:client) in a begin, rescue, end block like so:
before(:each) do
begin
client = create(:client)
rescue
client = Client.create!(attributes_for(:client))
end
#request.host = "#{client.account_name}.lvh.me"
end
I don't know how or why this works but I know it works.

Devise - Run checks after user logged in, redirect if error

I'm working on an e-commerce application. When a user logs into my app, I want to make a check to my external subscription handler and make sure that their subscription is still active and not expired/failed/whatever.
I successfully figured out how to use a Warden callback in my initializers/devise.rb to perform a check on the model after login. However, if there is a problem, I want to log them out again and redirect to a certain page that tells them what to do next.
Here is what I have. I know I can't use redirect_to from the callback. Given that, what is the best way to do what I'm trying to do?
Warden::Manager.after_authentication do |user, auth, opts|
begin
user.check_active_subscription # this works, and will raise one of several exceptions if something is goofy
rescue
redirect_to "/account/expired" # obviously this won't work, but see what I'm trying to do?
end
end
Just let the callback raise the exception and rescue from it in your controller. E.g.:
Warden::Manager.after_authentication do |user, auth, opts|
user.check_active_subscription
end
class SessionsController < ApplicationController
def create
# Authenticate
rescue SubscriptionExpiredException
# Logout
redirect_to "/account/expired"
end
end
You could also use rescue_from in your ApplicationController like this:
class ApplicationController
rescue_from SubscriptionExpiredException, :with => :deny_access
def deny_access
redirect_to "/account/expired"
end
end