I am working on a cms, and have the need for dynamic template choice in the pages controller. I have a before filter that grabs the chosen template name in the user settings. Now I need to figure out how to render the correct layout using that instance variable.
Here is what I have so far:
#This sets #template to the template object. #template.name is "Default"
before_filter :get_template
layout "templates/#{#template.name.downcase.gsub(" ", "_")}"
#layout "templates/default" #This line renders fine
I'm getting the following error:
undefined method `name' for nil:NilClass
My guess is that the before_filter doesn't necessarily run 'before' the template is called.
Is there a better way that I should be trying to accomplish this? I do not really have experience in using many templates and choosing which one to render.
Thanks in advance!
Try this:
class PagesController < ApplicationController
def template_path
#... returns the template path, e.g. "layouts/theme_a"
end
def set_template
self.class.layout(template_path)
end
before_filter :set_template
end
get_template inside of application controller , so we can access from any controlller :
--------------------
class ApplicationController < ActionController::Base
#template=get_template
layout "templates/#{#template.name.downcase.gsub(" ", "_")}"
end
This should work
class ApplicationController < ActionController::Base
layout :set_custom_layout
def set_custom_layout
get_template
end
end
Related
I'm making a control panel (user accounts) in rails.
in the layout I need to show things like messages or notifications (facebook-like style).
the problem is these things require an access to database and I'm not sure where to put this code because it's not related to a controller, but the layout is shared with multiple controlers.
so where is the best place to put the code to fetch messages from database should I put in the layout itself (I don't think its right), or as helper ?
the best solution was to build a control-panel controller which handles authentication and permissions and loads common user data from database such as messages...
here is an example code
class ControlPanelController < ApplicationController
before_filter :authenticate_user!
before_filter :get_user_data
helper_method :mailbox
authorize_resource
protected
def get_user_data
#header_conversations=mailbox.inbox.limit(3)
#uevents= Event.scoped
#uevents= #uevents.after(Time.now)
end
def mailbox
#mailbox ||= current_user.mailbox
end
end
and then all the classes in my web application extends this class :)
I found a way is to use before_filter. by defining the filter in the ApplicationController (so that you can access it from any controller).
class ApplicationController < ActionController::Base
# ..
protected
def load_messages
#messages = Message.all
end
end
and then in the any controller:
class FooController < ApplicationController
before_filter :load_messages
def index
# #messages is set
end
end
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
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
I created a helper method for some simple calculation. This helper method will just return an integer. I need the helper in both controllers and views.
Unfortunately, it work well in views but not in controllers. I get the undefined local variable or method error. How can I fix it?
Thanks all
In order to use same methods in both controller and views Add you method in application_controller.rb and make it helper methods.
For example
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
#protect_from_forgery # See ActionController::RequestForgeryProtection for details
helper_method :current_user
def current_user
session[:user]
end
end
Now you can use method current_user in both controllers & views
I use a following solution. Because I think helper's methods shoud be stored in an appropriate helper module.
module TestHelper
def my_helper_method
#something
end
end
class SomeController < ApplicationController
def index
template.my_helper_method
end
end
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