Why before_filter is not working?

I have been scratching my head on this one for most of the day.
Hopefully someone can help explain why before_filter isn’t working for
my codes.

In my Application controller, I have this:

before_filter :login_required, :except =>
[:newacct, :create_newacct, :passwd_reset ]

def login_required
unless session[:user_id]
flash[:notice] = “Please log in”
redirect_to new_session_url
end
end

I have the passwd_reset method defined in my Users controller.

My intention is if I call the method /users/passwd_reset it should
skip executing the login_required method in the Application
Controller, which in turn skip the login screen.

The result I have is I keep getting the login screen. My conclusion
was that the exception in the before_filter isn’t working for some
reason. Perhaps someone will be able to spot my error and help me
understand why it isn’t working as I intended it to.

Thanks in advance for the help.

Steve

maestro777 wrote:

In my Application controller, I have this:

before_filter :login_required, :except =>
[:newacct, :create_newacct, :passwd_reset ]

def login_required
unless session[:user_id]
flash[:notice] = “Please log in”
redirect_to new_session_url
end
end

I am not 100% sure… but:

It is correct to place the “login_required” method in your Application
controller, but I think, that you should place the “before_filter”
declaration inside your Users controller.

Have you tried that?

  • Carsten

I have the passwd_reset method defined in my Users controller.

My intention is if I call the method /users/passwd_reset it should
skip executing the login_required method in the Application
Controller, which in turn skip the login screen.

The result I have is I keep getting the login screen. My conclusion
was that the exception in the before_filter isn’t working for some
reason. Perhaps someone will be able to spot my error and help me
understand why it isn’t working as I intended it to.

Thanks in advance for the help.

Steve

Carsten is right. Here’s what’s happening (I believe):

Since the before_filter call is in application.rb, it’s actually getting
called for every action in every controller. The :except is not
working because it’s looking in application.rb for :passwd_reset, which
it is not finding.

One way to solve this is to move the before_filter statement to the
UsersController, so it would look like:

application.rb

def login_required

end

users_controller.rb

before_filter :login_required, :except => [:passwd_reset]

Another possibility, though I have not used it myself, is to use
skip_before_filter. In that case, you’d put the before_filter call back
in application.rb, but with no :only/:except, and then skip it where
appropriate in other controllers. That’s assuming I’m understanding
skip_before_filter correctly. I might not be.

One other thing to keep in mind is that filters need to return true or
false to indicate where the chain stops. I believe that if you don’t
specifically return one or the other, true is returned by default. So in
your case, you’d want to do

redirect_to new_session_url and return false

Peace,
Phillip

Frederick C. wrote:

On 6 May 2008, at 14:14, Phillip K. wrote:

One other thing to keep in mind is that filters need to return true or
false to indicate where the chain stops. I believe that if you don’t
specifically return one or the other, true is returned by default.
So in
your case, you’d want to do

not true as of rails 2.0. Return value is irrelevant, they halt if you
render or redirect.

Fred

Great! That’s what I actually expected in 1.x and wondered why it wasn’t
so.

Thanks for setting me straight, Fred!

Peace,
Phillip

On 6 May 2008, at 14:14, Phillip K. wrote:

One other thing to keep in mind is that filters need to return true or
false to indicate where the chain stops. I believe that if you don’t
specifically return one or the other, true is returned by default.
So in
your case, you’d want to do

not true as of rails 2.0. Return value is irrelevant, they halt if you
render or redirect.

Fred

Thanks all for the input.

The reason I’m putting the before_filter in the Application.rb is
simply that I want it to apply to all controllers. Since the purpose
is to check whether the user has a valid session before executing any
controller methods. I could move it to the Users controller but then I
would also need to put it in other controllers as well.

Phillip,
As for using the skip_before_filter as you suggested, I don’t think
that would work in my setup because it will skip the before_filter for
every methods in my Users controller, if I understand
skip_before_filter correctly. I only want it to apply to one or two
methods within the Users controller and not the entire Users
Controller. I could probably move all the methods to a separate
controller and put the skip_before_filter in that controller. But the
reason I want to keep that method within the Users controller is
because it is a user related method and I thought it best to keep it
within the same controller.

As I played more on this, I have come to think that perhaps it is my
routing config that is causing the issue. Let me explain.
I have a link in one of my views that calls up the passwd_reset method
like this:
href="/users/passwd_reset">forgot your password

When I click that link, the before_filter didn’t seem to catch that
exception, and it presented me with a login page, which isn’t what I
intended.

But if I change that link to /users/passwd_reset/0 the
before_filter exception will work. Apparently by adding an extra id to
the link url makes the difference. But the zero is redundant for me.
What I ended up doing is adding in the routing file the following:
map.connect “passwd_reset”, :controller => “users”, :action =>
“passwd_reset”

And then use the url link as simply /passwd_reset. And that works like
a charm. So my conclusion is that my before_filter problem has
something to do with the routing config, although I don’t really know
why or how. (routing is not an easy subject for me to grasp).
Incidentally I also have “map.resources users” at the top of my
route.rb

cheers,
Steve

maestro777 wrote:

Thanks all for the input.

The reason I’m putting the before_filter in the Application.rb is
simply that I want it to apply to all controllers. Since the purpose
is to check whether the user has a valid session before executing any
controller methods. I could move it to the Users controller but then I
would also need to put it in other controllers as well.

Right. The whole point of putting a before_filter call in application.rb
is so it automatically applies to all controllers that descend from
ApplicationController. I think where we are misunderstanding one another
is in the way you avoid the filter being run for a particular action in
a particular controller. What I’m understanding you to be saying is you
can specify, in application.rb, all of the actions to either include or
exclude regardless of the controller. So if I do this in application.rb

before_filter :login_required, :only => [:create, :update, :destroy]

then it would apply to :create, :update, and :destroy actions in any
controller. I have never understood it to work that way, but maybe it
does. Rather, my understanding is that if you specify :only or :except,
it will look in the “current” controller, that is, whichever one the
before_filter statement is found. So in my contrived case above, the
filter would actually never get called because you don’t have :create,
:update, :destroy actions in application.rb. On the other hand, in your
example where you have :except, it seems that it would always get called
because you don’t have the exceptions in your application.rb.

I certainly won’t be surprised to find out that my understanding is
wrong (I’m often wrong and have learned to accept it cheerfully), but I
will be surprised if that is actually how it works. It seems it would be
an extremely complicated way to go about things.

As for the skip_before_filter, the API docs say

You can control the actions to skip the filter for with the :only and
:except options, just like when you apply the filters.

so I assume you could do something like

skip_before_filter :login_required, :only => [:passwd_reset]

But as I said before, I have not made use of skip_before_filter, so I
might be blabbering on about something I shouldn’t be :slight_smile:

Please don’t take this the wrong way, but I’m not sure I’d be satisfied
with your route “solution”. To have to put a bogus value on your route
to get it to work suggests to me that something else is amiss.

Peace,
Phillip

On May 6, 10:49 am, Phillip K. [email protected]
wrote:

before_filter statement is found. So in my contrived case above, the
filter would actually never get called because you don’t have :create,
:update, :destroy actions in application.rb. On the other hand, in your
example where you have :except, it seems that it would always get called
because you don’t have the exceptions in your application.rb.

I certainly won’t be surprised to find out that my understanding is
wrong (I’m often wrong and have learned to accept it cheerfully), but I
will be surprised if that is actually how it works. It seems it would be
an extremely complicated way to go about things.

I am not an expert in ROR, so I am only taking a stab in the dark
here. It is my understanding that, from your example above, the
before_filter with the :only option will apply to the named actions
every controller. In other words the :create, :update, :destroy
actions in all the controllers, if this is declared in the
Application.rb. If it is declared in any other controllers, then it
will only apply to those actions within that controller. That is my
understanding of how the Application controller works. Then again I
may probably be totally off the target in my understanding, and it
wouldn’t surprise me either.

might be blabbering on about something I shouldn’t be :slight_smile:
You are right in this. For some reasons it totally slipped my mind
about using the :only options.

Please don’t take this the wrong way, but I’m not sure I’d be satisfied
with your route “solution”. To have to put a bogus value on your route
to get it to work suggests to me that something else is amiss.

Nah, I wouldn’t take it the wrong way. Always open to suggestions and
constructive criticisms.
I wouldn’t see it as putting a bogus value in the route but a way to
simplify the routing. Instead of using “/users/passwd_reset” in my
url, I use only “/passwd_reset”. Since there is only one such action
called passwd_reset. Looks better too. :slight_smile:

Anyway that wasn’t the reason why I did it. As I mentioned before, for
some reason my routing does not understand if I use “/user/
passwd_reset” as a link to call upon the passwd_reset action.
Apparently my route is expecting a /controller/action/id format.
(Probably due to the fact that I have declared “map.resources :users”
in the routing as well). So as a quick work-around based on limited
understanding of routing, I added the extra directive in my routing.
It “solved” my issue that I can now use “/passwd_reset” and it seems
to work with the exclusion in the before_filter as well.

Incidentally I tried what you previously suggested. Which was using
before_filter without exclusion in application.rb and place
“skip_before_filter…:only => …” in the User controller. It still
give me the same result as before.

Thanks for all your input.
Steve

On 6 May 2008, at 18:49, Phillip K. wrote:

your
example where you have :except, it seems that it would always get
called
because you don’t have the exceptions in your application.rb.

It doesn’t ‘look’ anywhere (Unsurprisingly rails is rather dynamic
about this).
When the action is processed, rails goes along the filter chain,
looking at each filter. for each filter it compares the :only
& :except values with the name of the current action and bails out if
appropriate
I tried sticking

def bang_filter
raise ‘Foo’
end

before_filter :bang_filter, :except => :index

in my application.rb and the filter is skipped properly

Fred

Frederick C. wrote:

On 6 May 2008, at 18:49, Phillip K. wrote:
It doesn’t ‘look’ anywhere (Unsurprisingly rails is rather dynamic
about this).
When the action is processed, rails goes along the filter chain,
looking at each filter. for each filter it compares the :only
& :except values with the name of the current action and bails out if
appropriate
I tried sticking

def bang_filter
raise ‘Foo’
end

before_filter :bang_filter, :except => :index

in my application.rb and the filter is skipped properly

Fred

Thanks for clarifying, Fred. It seems like a disaster waiting to
happen, though. One would need to be very careful about which actions
were listed in application.rb. Though, in the right circumstance, it
would definitely be DRY.

I guess some people are more knowledgeable because they take the time to
experiment. [Note to self: experiment before you post]

Peace,
Phillip

maestro777 wrote:

I am not an expert in ROR,

Me either. Which is why I’ve been waiting for one of them to chime in.
:slight_smile:

here. It is my understanding that, from your example above, the
before_filter with the :only option will apply to the named actions
every controller. In other words the :create, :update, :destroy
actions in all the controllers, if this is declared in the
Application.rb. If it is declared in any other controllers, then it
will only apply to those actions within that controller. That is my
understanding of how the Application controller works. Then again I
may probably be totally off the target in my understanding, and it
wouldn’t surprise me either.

Well it would certainly be good to know that for sure.

Nah, I wouldn’t take it the wrong way. Always open to suggestions and
constructive criticisms.
I wouldn’t see it as putting a bogus value in the route but a way to
simplify the routing. Instead of using “/users/passwd_reset” in my
url, I use only “/passwd_reset”. Since there is only one such action
called passwd_reset. Looks better too. :slight_smile:

My comment about a bogus value was in regards to the /0 you toyed with.
But it just dawned on me why it didn’t recognize /user/passwd_reset.
You said you have map.resources :user in your routes.rb file. Until you
add a member to it (and I don’t know the syntax off the top of my head),
that seems logical.

Thanks for all your input.
Steve

I’m glad it’s working for you the way you want it to.

Peace,
Phillip

On 6 May 2008, at 21:49, Phillip K. wrote:

Thanks for clarifying, Fred. It seems like a disaster waiting to
happen, though. One would need to be very careful about which actions
were listed in application.rb. Though, in the right circumstance, it
would definitely be DRY.

I would agree that it definitely wouldn’t be something I’d normally do
(apart from anything else it’s typically a combination of action &
controller that you want to exclude from an application wide filter,
and in that case it makes sense to me to have that exception in the
relevant controller).

Fred

I guess some people are more knowledgeable because they take the
time to
experiment. [Note to self: experiment before you post]

Always a good one :slight_smile:

On May 6, 1:38 pm, Phillip K. [email protected]
wrote:

My comment about a bogus value was in regards to the /0 you toyed with.
But it just dawned on me why it didn’t recognize /user/passwd_reset.
You said you have map.resources :user in your routes.rb file. Until you
add a member to it (and I don’t know the syntax off the top of my head),
that seems logical.

I’m pretty certain now that it has to do with my routing config. I
commented out the
map.resources :users in my route.rb and the before_filter works as
intended. Why this is so, I do not know. I need to read up more on the
map.resources to see how exactly it work behind the scene.

So the conclusion to my problem is that it is not an issue of
before_filter not working but rather an issue of routing.