before_filter question

If one places a before_filter in class ApplicationController, when is the before filter called and for which methods.

I believe it gets called before any "action" ... but how does rails know that the method is an action?

Ralph Shnelvar wrote:

If one places a before_filter in class ApplicationController, when is the before filter called and for which methods.

Does http://www.railsapi.com/doc/rails-v2.3.8/classes/ActionController/Filters/ClassMethods.html answer your question?

I believe it gets called before any "action" ... but how does rails know that the method is an action?

All (public) controller methods are in principle actions in Rails. If you have ones that aren't, get them out of the controller or make them private.

Best,

Hi Ralph,

Good question. Methods in controllers are considered actions only if they're connected to a url. Urls are connected to controller methods by using routes. The routes.rb file lists all of the routes that will connect urls to controllers and actions.

In your routes, when you use map.connect (in Rails 1.x, 2.x) you directly connect a url to an action method. In Rails 3.0, this is done with match() instead of connect, but the idea is the same.

If you use map.resources then you're connecting a whole slew of urls to a certain actions in your controller. Check the docs for details, but this is the "restful" style of URL handling, and means that these methods in your controller will become actions:

index new create edit update show destroy

Finally, be warned that in Rails 1.x and 2.x, when you generate a new Rails app, by default a pattern-match connector is written for you, exposing ALL of your methods as actions, which is probably not what you want. You should remove or comment those out.

Jeff

purpleworkshops.com

Jeff Cohen wrote: [...]

Good question. Methods in controllers are considered actions only if they're connected to a url.

That's not true at all. For example, if you have

class PlaysController < ApplicationController   def write_shakespeare   ...   end end

then (unless I'm badly mistaken) you can get to the write_shakespeare method by (say) calling

redirect_to :controller => 'plays', :action => 'write_shakespeare'

regardless of what's in your routes file. There may not be a direct URL to that action, but that doesn't prevent it from being an action.

Best,

Marnen Laibow-Koser wrote:

Ralph Shnelvar wrote:

If one places a before_filter in class ApplicationController, when is the before filter called and for which methods.

Does http://www.railsapi.com/doc/rails-v2.3.8/classes/ActionController/Filters/ClassMethods.html answer your question?

It certainly helps but it did not address my question directly.

Here's another question: Is the before_filter ever called before methods in ApplicationController?

In fact would it be true to say that no method of a controller is inherently an action or not an action? An action is a verb not a noun so it is not correct to say that a method is or is not an action. An action (initiated via a url or redirect_to for example) results in calling a method of a controller and it is under these circumstances that the filters are called. Calling a method directly from code will not result in filters being called.

Colin

Ralph Shnelvar wrote: [...]

Here's another question: Is the before_filter ever called before methods in ApplicationController?

Yes. (Try it and find out!) Most authentication libraries work this way. Doesn't Devise?

Best,

Colin Law wrote:

Marnen Laibow-Koser wrote:

Ralph Shnelvar wrote: [...]

Here's another question: Is the before_filter ever called before methods in ApplicationController?

Yes. (Try it and find out!) Most authentication libraries work this way. Doesn't Devise?

Best, -- Marnen Laibow-Koser http://www.marnen.org marnen@marnen.org

Sent from my iPhone

What prevents infinite recursion?

- - - - - class ApplicationController < ActionController::Base   before_filter :fetch_logged_user   before_filter :authenticate, :except => :login

# Filters added to this controller apply to all controllers in the application. # Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base   before_filter :fetch_logged_user   before_filter :authenticate, :except => :login

  helper :all # include all helpers, all the time   protect_from_forgery

  def fetch_logged_user     unless session[:user_id].blank?       @logged_user = User.find(session[:user_id])     end

  rescue ActiveRecord::RecordNotFound   end

  def is_logged_in_user?     !@logged_user.nil?   end

protected   def authenticate     debugger     unless @logged_user       #unauthorized access       redirect_to :controller => :welcome, :status => 401       return false     end   end end

class ExplanationController < ApplicationController   def whatIsThisGameAbout     x = is_logged_in_user?     debugger     x   end

protected   def authenticate     debugger     x=1   end

end - - - - -

I am pretty sure that when Rails calls the action whatIsThisGameAbout that when is_logged_in_user? is invoked that the before_filters are NOT invoked again before is_logged_in_user? is invoked.

Ralph Shnelvar wrote: [...]

What prevents infinite recursion?

What would cause infinite recursion?

- - - - - class ApplicationController < ActionController::Base   before_filter :fetch_logged_user   before_filter :authenticate, :except => :login

# Filters added to this controller apply to all controllers in the application. # Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base   before_filter :fetch_logged_user   before_filter :authenticate, :except => :login

  helper :all # include all helpers, all the time   protect_from_forgery

  def fetch_logged_user     unless session[:user_id].blank?       @logged_user = User.find(session[:user_id])     end

  rescue ActiveRecord::RecordNotFound   end

  def is_logged_in_user?     !@logged_user.nil?   end

protected   def authenticate     debugger     unless @logged_user       #unauthorized access       redirect_to :controller => :welcome, :status => 401       return false     end   end end

class ExplanationController < ApplicationController   def whatIsThisGameAbout

Hey! Watch the camelCase! That should be what_is_this_game_about . Actually, it should probably game_info or something.

    x = is_logged_in_user?     debugger     x   end

protected   def authenticate     debugger     x=1   end

end - - - - -

I am pretty sure that when Rails calls the action whatIsThisGameAbout that when is_logged_in_user? is invoked that the before_filters are NOT invoked again before is_logged_in_user? is invoked.

You're quite right. When you do a straight method call, the filter chain is not invoked -- after all, it's just a straight method call, and it works just like it does anywhere else in Ruby. It's only invoked when you call a controller method *as an action* -- that is, when you go through the whole Rails stack, as with a Web request or with render :controller, :action -- that the filter chain is invoked.

Best,

Marnen Laibow-Koser wrote:

Colin Law wrote:

�end end

then (unless I'm badly mistaken) you can get to the write_shakespeare method by (say) calling

redirect_to :controller => 'plays', :action => 'write_shakespeare'

regardless of what's in your routes file. �There may not be a direct URL to that action, but that doesn't prevent it from being an action.

In fact would it be true to say that no method of a controller is inherently an action or not an action?

No. In Rails, every public controller method is, by definition, an action. Period. That's all.

OK, I now have to modify my answer. With Ralph's latest question, I see what you're getting at. So, here's the modified answer:

In Rails, every public controller method can, by definition, *behave as* an action -- that is, it can be called with suitable use of render :controller, :action. It can also be called with a regular Ruby-style method call, in which case it does not behave as an action.

Whether a public controller method behaves as an action depends not on the method itself but on how it's called.

That said, if you have a controller method (such as Ralph's fetch_logged_user) that is not meant to be called as an action, it is an extremely good idea to make it non-public (that is, private or protected) so that it cannot be called as an action accidentally (or maliciously).

Best,

Marnen Laibow-Koser wrote:

That said, if you have a controller method (such as Ralph's fetch_logged_user) that is not meant to be called as an action, it is an extremely good idea to make it non-public (that is, private or protected) so that it cannot be called as an action accidentally (or maliciously).

Ok ... that sent up alarm bells.

So ... how can I prevent Rails from calling, say,      def fetch_logged_user        # black blah blah      end or      def is_logged_in_user?        !@logged_user.nil?      end and yet allow all other controllers access to those two functions?

Ralph Shnelvar wrote:

Marnen Laibow-Koser wrote: > That said, if you have a controller method (such as Ralph's > fetch_logged_user) that is not meant to be called as an action, it is an > extremely good idea to make it non-public (that is, private or > protected) so that it cannot be called as an action accidentally (or > maliciously).

Ok ... that sent up alarm bells.

So ... how can I prevent Rails from calling, say,      def fetch_logged_user        # black blah blah      end or      def is_logged_in_user?        !@logged_user.nil?      end and yet allow all other controllers access to those two functions?

As Marnen said, make it private or protected. Like this:

   protected      def fetch_logged_user        ...      end

Bob