I have a method in my ApplicationController that is part of a before_filter. How do I identify which controller is calling that method and can I pass arguments to it?
Presumably worst case, I can create a new object where I use controller names and values, then call it directly in the before_filter method with NewObject.find(:first, :conditions => ['controller_name = ?', controller_name], but that smells very bad.
So I'm open to ideas. Thanks in advance.
psuedo-short-code:
class ApplicationController < ActionController::Base
before_filter :someMethod
....
def someMethod
Do stuff
end
class SomeController < ApplicationController
# presumably the before_filter runs here
#someValueIWantToPass = some.value
...
params[:controller] and params[:action] contain the controller and action requested, and are available from inside a filter.
Using self.class will tell you which controller has called the before_filter.
class HomeController < ApplicationController
before_filter :awesome
def index
render :text => #blah
end
def awesome
#blah = self.class
end
end
will render out "HomeController"
Related
I am trying to access one variable set in a method from another method. This is in Rails 1.9 code below.
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :method1
before_filter :method2
def method1
#remote_user = 'dev_user'
end
def method2
unless #remote_user.present?
render :status => 401, :text => "Authentication failed"
false
end
end
When attempting to access it in the second method, it is always returning blank. The 401 is always returning with the text "Authentication failed". Could someone advise on to what I am doing wrong?
if you need to guarantee that a method is called before another on before_filter, do something like this:
before_filter :fn3
def fn3
fn1
fn2
end
from -> How can I specify the order that before_filters are executed?
Hope it helps
EDITED
Better yet, use prepend_before_filter for anything you needs to be resolved before the before_filter method.
in your case:
class ApplicationController < ActionController::Base
protect_from_forgery
prepend_before_filter :method1
before_filter :method2
def method1
#remote_user = 'dev_user'
end
def method2
unless #remote_user.present?
render :status => 401, :text => "Authentication failed"
false
end
end
To ensure correct order of before_filter you can use prepend_before_filter or append_before_filter. Imho prepend is the default behaviour, so your filters are executed in reverse order.
So to fix this you will have to write:
class ApplicationController < ActionController::Base
before_filter :method2
prepend_before_filter :method1
You could just write before_filter twice (in this order) and it would be fine, but this is imho more expresssive. First write all the before_filters for which the order does not matter, and then prepend the one that needs to be first.
Alternatively you could write
class ApplicationController < ActionController::Base
before_filter :method1
append_before_filter :method2
which does exactly the same, but makes sure method2 is executed last. Whichever you prefer :)
Also note that filters defined in other controllers (deriving from ApplicationController) will normally be executed first!
Suppose I have some logic in a base controller to pass information to the view to build something like a breadcrumb:
class ContextAwareController < ApplicationController
after_filter :build_breadcrumb
def build_breadcumb
#...
end
end
I want this build_breadcrumb method to run after the main controller logic, but before the view is rendered.
The above code runs too late, but a before_filter would be too early.
Can anybody suggest a way to accomplish this without explicitly calling build_breadcumb at the end of each of the actions in the child controllers?
Thanks
I had the same problem and solved it like this:
class ApplicationController < ActionController::Base
def render *args
add_breadcrumbs
super
end
end
There are also some gems to achieve this. One of them is rails3_before_render.
It works similarly to filters, for example:
class PostsController < ApplicationController
before_render :ping, :except => [:destroy]
def index; end
def new; end
def show; end
def destroy; end
private
def ping
Rails.logger.info "Ping-Pong actions"
end
end
(code snipped copied from gem documentation)
I believe rendering starts when render is called, and there's no default way to defer it. Here's one thing you could do:
filters are applied in the same order declared. So make a second after-filter that calls render with an array args stored in a class variable. Then anywhere you would normally call render, set the variable.
If we're overriding render, we're not really using the filter chain at all, so it might be simpler to determine which action we're in using the #_action_name.
StuffController < ApplicationController
def my_filter
# Do the stuff
end
def render(*args)
my_filter if #_action_name == "show"
super
end
end
You can use alias_method_chain like this
alias_method_chain :render, :before_render_action
this will create 2 methods :render_with_before_render_action and :render_without_before_render_action. If you call render, then :render_with_before_render_action will be called. You can override this method
def render_with_before_render_action(*options, &block)
<your code>
render_without_before_render_action(*options, &block)
end
If you don't want your code to be executed and you want to have default render then you should directly call the :render_without_before_render_action
You can do like this to fake a before_render:
class StuffController < ApplicationController
before_filter :my_filter, only: [:index, :show]
def my_filter
#my_filter = true
end
def _my_filter
# Do the actual stuff here
end
def render(*args)
_my_filter if #my_filter
super
end
end
Thanks to #joshua-muheim for the tip about using render
In appllication controller i have couple of methods that works with requested controller and action name.
To follow DRY principe, i want to define share variables with those params.
class ApplicationController < ActionController::Base
##requested_action = params[:action]
##requested_controller = params[:controller]
end
But i get error: undefined local variable or method "params" for ApplicationController:Class
Why i can't do this and how can i achieve my goal?
I believe you already have controller_name and action_name variables defined by Rails for this purpose.
If you want to do it by your way, you must define it as a before filter as params comes to existence only after the request is made. You can do something like this
class ApplicationController < ActionController::Base
before_filter :set_action_and_controller
def set_action_and_controller
#controller_name = params[:controller]
#action_name = params[:action]
end
end
You can access them as #controller_name and #action_name. But controller_name and action_name are already defined in Rails. You can use them directly.
Use instance methods instead:
class ApplicationController < ActionController::Base
def requested_action
params[:action] if params
end
end
You can use before_filter option.
class ApplicationController < ActionController::Base
before_filter :set_share_variable
protected
def set_share_variable
#requested_action = params[:action]
#requested_controller = params[:controller]
end
end
I want to create a method that will be available in all controllers, and views.
This method actually makes a db call to get data back, so since it won't be used all the time I want it be be lazily loaded.
class ApplicationController < ActionController::Base
def some_method
#object = customdb.call_method(....)
end
end
To make it lazily loaded do I just do?
#object ||= ....
And how do I propagate this to all controllers and view pages?
Use helper_method in ApplicationController to make some_method available in any controller or view:
class ApplicationController < ActionController::Base
helper_method :some_method
def some_method
#object ||= customdb.call_method(....)
end
end
The ||= does per request caching, not lazy loading. Lazy loading is the deferred initialization pattern.
Big scopes and controller methods in views are code smells. Best to minimize object scope and view logic.
You could use the memoize feature provided by ActiveSupport
http://api.rubyonrails.org/classes/ActiveSupport/Memoizable.html
http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization
So possibly something like:
class ApplicationController < ActionController::Base
def some_method
#object = customdb.call_method(....)
end
memoize :some_method
end
If you want the result to be accessible across accross requests, you have to store the results in a class variable:
class ApplicationController < ActionController::Base
helper_method :city_list
def city_list
##city_list =|| City.order(:name)
end
end
The result is lazy loaded and cached.
PS: The variable ##city_list is a class variable.
You should use a before_filter
class ApplicationController < ActionController::Base
before_filter :some_method
def some_method
#object = customdb.call_method(....)
end
end
It will be triggered in all your controllers.
Note that you can skip this before filter using skip_before_filter :some_method wherever you want. You can even prevent only some actions to be preceded by this filter.
Because this will be triggered before all decided controller actions, you won't need your line:
#object ||= ....
Since #object will be instantiated.
To the rails experts out there I was wondering where/how you would execute the same code for every action in your web application? If you can point me to an article or provide a short code snippet I would greatly appreciate it.
Thanks in advance to anyone who can help.
Use a filter in your ApplicationController to run the code for every action in your application. All your controllers descend from ApplicationController, so putting the filter there will ensure the filter gets run.
class ApplicationController
before_filter :verify_security_token
def verify_security_token; puts "Run"; end;
end
It sounds to me like you're talking about filters.
class MyController < ActionController::Base
before_filter :execute_this_for_every_action
def index
#foo = #bar
end
def new
#foo = #bar.to_s
end
def execute_this_for_every_action
#bar = :baz
end
end
You can put the filter on the ApplicationController, too, if you want every controller to run it.
before_filter if you want the code to be executed "before" each action.
If you want the action to be declared each time you use it, you can put it in ApplicationController and call the method in any controller.
Another approach is to use helpers like:
module PersonHelper
def eat
{.. some code ..}
end
end
And in your controller:
class MyController < ActionController::Base
include PersonHelper
def index
eat
end
end