before_filter not cancelling action - ruby-on-rails

I'm having trouble getting before filters to work in a Rails app I have recently upgraded from 1.9(?) to 2.3.11. To try and debug it, I have put a before_filter in a controller:
before_filter :false_filter
and the following in application_controller.rb:
def false_filter
puts "false filter running"
false
end
I then call the method from either cucumber/webrat or a browser, and while the filter is getting called (I can see the puts outputting the message), the filter chain isn't getting terminated.
I'm wondering if there's some boilerplate code that hasn't been generated. Can anyone suggest where to look?

Nothing pays any attention to a before-filter's return value. If you want to stop processing, you have to render something from your filter or redirect to somewhere else, from the fine guide:
If a before filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter they are also cancelled.
The same text appears in the 5.2.0 guide.
This behavior does make sense, if the filter chain doesn't complete (i.e. stops filtering part way through) then you'd end up calling controller methods with things not set up the way they were expecting them to be and that would just cause pain, suffering, and confusion and that wouldn't be at all friendly or fun.

Related

Rails: Model.find() or Model.find_by_id() to avoid RecordNotFound

I just realized I had a very hard to find bug on my website. I frequently use Model.find to retrieve data from my database.
A year ago I merged three websites causing a lot of redirections that needed to be handled. To do I created a "catch all"-functionality in my application controller as this:
around_filter :catch_not_found
def catch_not_found
yield
rescue ActiveRecord::RecordNotFound
require 'functions/redirections'
handle_redirection(request.path)
end
in addition I have this at the bottom of my routes.rb:
match '*not_found_path', :to => 'redirections#not_found_catcher', via: :get, as: :redirect_catcher, :constraints => lambda{|req| req.path !~ /\.(png|gif|jpg|txt|js|css)$/ }
Redirection-controller has:
def not_found_catcher
handle_redirection(request.path)
end
I am not sure these things are relevant in this question but I guess it is better to tell.
My actual problem
I frequently use Model.find to retrieve data from my database. Let's say I have a Product-model with a controller like this:
def show
#product = Product.find(params[:id])
#product.country = Country.find(...some id that does not exist...)
end
# View
<%= #product.country.name %>
This is something I use in some 700+ places in my application. What I realized today was that even though the Product model will be found. Calling the Country.find() and NOT find something causes a RecordNotFound, which in turn causes a 404 error.
I have made my app around the expectation that #product.country = nil if it couldn't find that Country in the .find-search. I know now that is not the case - it will create a RecordNotFound. Basically, if I load the Product#show I will get a 404-page where I would expect to get a 500-error (since #product.country = nil and nil.name should not work).
My question
My big question now. Am I doing things wrong in my app, should I always use Model.find_by_id for queries like my Country.find(...some id...)? What is the best practise here?
Or, does the problem lie within my catch all in the Application Controller?
To answer your questions:
should I always use Model.find_by_id
If you want to find by an id, use Country.find(...some id...). If you want to find be something else, use eg. Country.find_by(name: 'Australia'). The find_by_name syntax is no longer favoured in Rails 4.
But that's an aside, and is not your problem.
Or, does the problem lie within my catch all in the Application Controller?
Yeah, that sounds like a recipe for pain to me. I'm not sure what specifically you're doing or what the nature of your redirections is, but based on the vague sense I get of what you're trying to do, here's how I'd approach it:
Your Rails app shouldn't be responsible for redirecting routes from your previous websites / applications. That should be the responsibility of your webserver (eg nginx or apache or whatever).
Essentially you want to make a big fat list of all the URLs you want to redirect FROM, and where you want to redirect them TO, and then format them in the way your webserver expects, and configure your webserver to do the redirects for you. Search for eg "301 redirect nginx" or "301 redirect apache" to find out info on how to set that up.
If you've got a lot of URLs to redirect, you'll likely want to generate the list with code (most of the logic should already be there in your handle_redirection(request.path) method).
Once you've run that code and generated the list, you can throw that code away, your webserver will be handling the redirects form the old sites, and your rails app can happily go on with no knowledge of the previous sites / URLs, and no dangerous catch-all logic in your application controller.
That is a very interesting way to handle exceptions...
In Rails you use rescue_from to handle exceptions on the controller layer:
class ApplicationController < ActionController::Base
rescue_from SomeError, with: :oh_noes
private def oh_noes
render text: 'Oh no.'
end
end
However Rails already handles some exceptions by serving static html pages (among them ActiveRecord::RecordNotFound). Which you can override with dynamic handlers.
However as #joshua.paling already pointed out you should be handling the redirects on the server level instead of in your application.

Programmatically detect and catch infinite redirect loops in Rails

I am currently trying to determine the cause of a nasty redirect bug in a Rails app. The specifics are documented here, although I'm not looking for a specific solution here on StackOverflow. Rather, while I am working on fixing this, I'd like to develop a generic way to catch, log, and investigate infinite redirect loops in a Rails app.
I have an idea here, but I'd still like to see if there are any tried and true techniques.
My idea:
Override Rails' redirect_to to "log" redirects in the session:
def redirect_to(destination)
session[:redirects] << {destination: destination, timestamp: Time.now}
if is_inifinite_redirect?(session[:redirects])
render "a_redirect_error_page"
else
super
end
end
Then, have some sort of analysis of the redirects array to determine if there is an infinite loop:
def is_inifinite_redirect?(redirects)
recent = redirects.last(21) # 21 is the max redirects allowed by Chrome
return recent.odds.map(&:destination).uniq.length == 1 && \
recent.evens.map(&:destination).uniq.length == 1 && \
(recent.last.timestamp - recent.first.timestamp < 10.seconds)
end
I agree that tests should in theory prevent you running into infinite redirects loops. But I understand that tests are not the answer to your question.
I think you can consider an infinite redirect loop as soon as you have two redirects in a row with the same arguments. I think there is no good reason to wait for more redirects.
My idea is to store the arguments of the redirect into the flash. If the arguments in the flash are still the same when the next redirect happens then you are in a loop. If there was a redirect to another location or a normal page was rendered in between then that location would not match or the flash would be empty:
def redirect_to(*args)
if flash[:redirected_with_args] == args
raise "Infinited redirect loop detected: #{args.inspect}"
else
flash[:redirected_with_args] = args
super
end
end
The tried and true technique is to write tests to ensure that your app works the way you expect it to. This particular problem would be easily detected by a failing controller test, and/or a failing integration test.
There's nothing wrong with adding the code you've got above in order to help you debug this particular situation (if it does), but the real solution here for a production app is to have tests so that you don't get these redirect loops.

rails the best way to saving page duration and page loading speed

Hi I'm a beginner of rails and I'm not good at English. so if there is some total nonsense please understand..
I'm trying to record loading speed and page duration in every pages.
I made a database "pages" and method "savepage" in my "Page" model.
To save in every page I put "savepage" method in application controller.
Page.rb
def self.savepage
.
.
.
end
application_controller.rb
before_filter :dosave
def dosave
Page.savepage
end
these kind of format..
My question is
1. am I doing correct? using before_filter to do save in very first of loading process?
2. to save after loading all the contents in a page what should I use?
3. to save after user leave this page what should I use?
I saw before_destroy and after_filter, but I can't find what it is... what filter means.... what action means destroy....
thank you in advance!
before_filter is the first thing which loads before giving request to controller.But your need is completely different . Fundamentally filter are used boolean checking.If certain method is true,it will run otherwise it may not. This filter are further extended and we put code into that filters.(And Even sometimes it is consider as best practice) .
Now, before_filter :dosave might be right but is it not true way of knowing page(UI) loading process. I suggest you to use javascript call or use some manually created helper methods and place it into view .erb files.
May be this will interest you
https://github.com/grosser/record_activities
Log user activities in ROR
what action means ?
Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
Source : http://guides.rubyonrails.org/action_controller_overview.html
I highly suggest you to read above documentation. It is very necessary for you and it covers topic which you asked here.`
And one more thing,
what is action destroy ?
This is simply an action method just like new. Since, rails follow Convention over configuration ( and its developer too) so they put code which do some delete destroy or some destruction. This make thing simple,otherwise more configuration will require which is against rails policy.

How to stop a rails controller from executing?

I have the following piece of code
def show
unless logged_in?
login_required
return
end
#some additional code
#that should only execute
#if user is logged in
end
This works perfectly.
Now I'd like to move the login check into a before filter.
The problem is, that when I return from a method outside of show, it doesn't stop the execution of show... how do i stop show from going through with the code from an external method (i.e. one that could be called from a before filter)?
Thanks!
In rails versions 2.0.1 and above, you need to redirect or send a response to halt execution of the action.
From Agile Web Development with Rails Errata:
#45840: The following is incorrect:
"If a before filter returns false, processing of the filter chain terminates, and the action is not run. A filter may also render output or redirect requests, in which case the original action never gets invoked."
A before_filter does not stop processing the filter chain on on return false any longer.
From release notes of Rails 2.0.1:
* Changed before_filter halting to happen automatically on render or redirect but no longer on simply returning false [David Heinemeier Hansson]
--Rob Christie
redirect_to, render, and head will all halt execution. For example, head :ok will respond to the request with only the OK HTTP response code, and the action will not execute.
If you return false from a before_filter, then execution of the request will immediately stop.
If you just make your login_required method return false (or redirect) if they aren't logged in, and make it return true if they are, then just before_filter :login_required, it should work perfectly.
Edit: As Lenry states below, this will not work in Rails 2.0.1+
Instead, to stop the request use head :ok in your code
To halt callback chain in Rails 5 you can use
throw :abort

Rails resource_controller with interruption?

I want to use the before action to optionally interrupt the processing if it fails to comply certain criteria (e.g. the object is not owned by such user etc)
I need to use this because I have a more complicated scenario that I
need the object to be loaded before the access rights could be
determined, so I would have situations where I want to interrupt the
action if it is invalid access, anyway I could achieve this?
OK, this is something I was thinking about myself when working with RC.
the usual RC action is something like:
def show
load_object
before :show
response_for :show
rescue ActiveRecord::RecordNotFound
response_for :show_fails
end
So suppose you want to interrupt the show just after load_object if some conditions fail.
The best way to do it that I could think of (except for modifying RC :) is:
use before_filter to check the condition
in the the before_filter use the object or collection helpers (according to the action). this way the load_object/load_collection in the RC action implementation will use the same value cached from your usage of the helper so no extra queries will be made.
Assuming you are referring to before_filter:
Any render or redirect call should abort the filter chain and the execution of the action. So simply put your access control filter after the one to load 'stuff', then render an error message (with an appropriate status code for good web karma and to prevent a w3c beat down).
It's just another area of rails that works fine as long as you don't think too hard.
Any help or am I missing the point?
Vitaly's approach is probably correct, but I have a interesting over-engineered approach too, so to post as a reference:
Use around_filter
At the before hook, throw AccessDeniedException
Capture the exception in the around filter
That would do the job as well.
If you find yourself writing before_filters for precondition checks very often, you might find Aegis useful. It allows you to define access rules in a single file so you can see at a glance who may access what.
It was also built for easy integration with resource_controller.

Resources