Rails ActionController Execute Same Code for Every Action - ruby-on-rails

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

Related

How do I initialise objects in a partial view for use by multiple controllers?

I hope this is something obvious that I've just consistently overlooked and the community can set me on the right path.
I have a news article controller, but I want to be able to use a "common" ticker list on different views. How do I initialise this "#article_list" if I'm using the partial in a few controllers? Apparently it is of the opinion that using a helper is not the solution, since helpers are just for view logic. So where do I put this initialiser that would be available to every controller as required? I shouldn't put them in application controller should I?
You can use before_filter method, i.e. something like this:
class ApplicationController < ActionController::Base
def set_article_list
#article_list = ArticleList.all # or any onther selection
end
end
class NewsArticleController < ApplicationController
before_filter :set_article_list, only: :action1
def action1
end
end
class AnotherNewsArticleController < ApplicationController
before_filter :set_article_list, only: :another_action1
def another_action1
end
end
UPDATE:
Indeed, there will be problem with a fat ApplicationController. To avoid it it's possible to use module (almost #carolclarinet describe it below):
module ArticleList
def set_article_list
#article_list = ArticleList.all # or any onther selection
end
end
class NewsArticleController < ApplicationController
include ArticleList
before_filter :set_article_list, only: :action1
def action1
end
end
class AnotherNewsArticleController < ApplicationController
include ArticleList
before_filter :set_article_list, only: :another_action1
def another_action1
end
end
And
You can create, essentially, a query object that is only responsible for returning what you need for #article_list, for example, building off of Psylone's answer:
class ArticleList
def ticker_articles
ArticleList.all # or any onther selection
end
end
This class could go in lib, app/models, app/query_objects, app/models/query_objects, wherever it makes sense for you. This is a bit outside The Rails Way so there's no convention about where these types of objects should live.
Then in whatever controller you need this, do:
#article_list = ArticleList.new.ticker_articles
For more explanation of query objects, see this codeclimate article #4. Depending on what you're doing to set #article_list, this might also be called a service object (#2) or something else entirely. No matter what you call it though, its responsibility would be to return the value you need for #article_list and that's it.

Filter to execute before render but after controller?

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

How to change the default path of view files in a Rails 3 controller?

I have a controller called ProjectsController. Its actions, by default, look for views inside app/views/projects. I'd like to change that path for all methods (index, show, new, edit etc...) in the controller.
For example:
class ProjectsController < ApplicationController
#I'd like to be able to do something like this
views_path 'views/mycustomfolder'
def index
#some code
end
def show
#some code
end
def new
#some code
end
def edit
#some code
end
end
Please note I am not changing each method with render but defining a default path for all of them. Is this possible? If so, how?
Thank you!
See ActionView::ViewPaths::ClassMethods#prepend_view_path.
class ProjectsController < ApplicationController
prepend_view_path 'app/views/mycustomfolder'
...
You can do this inside your controller:
def self.controller_path
"mycustomfolder"
end
If there's no built-in method for this, perhaps you can override render for that controller?
class MyController < ApplicationController
# actions ..
private
def render(*args)
options = args.extract_options!
options[:template] = "/mycustomfolder/#{params[:action]}"
super(*(args << options))
end
end
Not sure how well this works out in practice, or if it works at all.
You can add something like:
paths.app.views << "app/views/myspecialdir"
in the config/application.rb file to have rails look in another directory for view templates. The one caveat is that it'll still look for view files that match the controller. So if you have a controller named HomeController with the above config for the views it'll look for something named "app/views/myspecialdir/home/index.html.erb" to render.
If you want to change the default path for all your views at app level, you could do something like following -
class ApplicationController < ActionController::Base
before_action :set_views
private
def set_views
prepend_view_path "#{Rails.root.join('app', 'views', 'new_views')}"
end
end
And write all your views in the folder new_views following the same directory structure as original.
P.S. - This answer is inspired from #mmell's answer.
The accepted answer no longer works for me. After much wailing and gnashing of teeth, I've managed to find that if render is not called in the action, then the default_render method is called. In my case, I needed to override the default_render message in my controller as follows:
def default_render
render "path/to/views/#{action_name.to_s}"
end

will initialize break the layout settings in rails?

In one of the controller, I need a specific layout. I added layout at the beginning. It works well.
But if I add an initialize function for some controller-based variable. Rails seems just ignore the layout command.
Is anyone have same problem? How can I fix it?
class AdminsController < ApplicationController
layout "layout_admins"
def initialize
#Title = "Admins"
end
def index
....... some code here
end
end
initialize is used internally to Rails to, well, initialize a new instance of your controller so it can then serve requests on it. By defining this method in this particular manner, you are breaking Rails.
There is a way through! A light at the end of the tunnel. A pot of gold at the end of the rainbow:
def initialize
#title = "Admins"
super
end
See that little super call there? That'll call the superclass's initialize method, doing exactly what Rails would do otherwise. Now that we've covered how to do it your way, let's cover how to do it the "officially sanctioned" Rails way:
class AdminsController < ApplicationController
before_filter :set_title
# your actions go here
private
def set_title
#title = "Title"
end
end
Yes, it's a little more code but it'll result in less frustration by others who gaze upon your code. This is the conventional way of doing it and I strongly encourage following conventions rather than doing "magic".
EDIT: If you're using Rails 5 then you'll need to use before_action instead of before_filter.
I'm not sure exactly how layout works its magic, but I'm willing to bet it's in a yield block in the ActionController#initialize method. So your overriding of initialize would explain the problem.
Looks like you have too options here:
Close out your new definition with super to call the ActionController initialize which should use the layout defined in the class.
eg:
def initialize
#Title = "Admins"
super
end
Use a before filter to initialize your variables. This is the Rails Way of initializing values in a controller
class AdminsController < ApplicationController
layout "layout_admins"
before_filter :set_title
def set_title
#Title = "Admins"
end
def index
....... some code here
end
end

calling methods dynamically inside a controller

I have the following scenario
I want to add methods dynamically to a controller. All my method names are in a table . Please refer the following example
-table (method_names)-
1 - Walk
2 - Speek
3 - Run
and I have a controller
class UsersController < ApplicationController
def index
end
end
Inside this index action i want to call my methods dynamically. Those methods were actually implemented else ware.
I have another controller like
class ActionImplementController < ApplicationController
def walk
puts "I'm walking"
end
def speek
puts "I'm sppeking"
end
def run
puts "I'm running"
end
end
** I have done something like below and its working
class UsersController < ApplicationController
def index
a = eval("ActionImplementController.new.run")
end
end
But my question is , is this the right way or is there anyother way to do this
Thanks in advance
cheers
sameera
While the first answer works, i would prefer something like this
module ImplementsActions
def run
...
end
def walk
..
end
def ...
end
and then in your controller write
class UsersController < ActionController::Base
include ImplementsActions
# now you can just use run/speek/walk
def index
run
end
end
Much cleaner because the code can be shared, but it is defined where you need it.
I think it's generally best to avoid the use of eval. If you can, I would make all your methods class methods and then run them like so:
def index
ActionImplementController.send :run
# ActionImplementController.new.send(:run) works if you can't use class methods
end

Resources