My app is has regular users and admins. It has a few rendering helpers which display widgets:
module ApplicationHelper
def widget
"widget"
end
end
For admins, there are extended versions of these widgets:
module AdminHelper
def widget
"admin-widget"
end
end
The app is configured not to include all helpers by default through config.action_controller.include_all_helpers = false
.
When an admin views a page - same controller, same view template -, I want to render an extended version of it by overloading helpers with their AdminHelper version. How do I overload ApplicationHelper with AdminHelper on a per-request basis if the currently logged in user is an admin?
I have tried
class ApplicationController < ActionController::Base
before_action :include_backend_helpers
protected
def include_backend_helpers
self.class.helper "pica/backend" if admin_signed_in?
end
end
but this doesn't work unless ApplicationController is reloaded between requests.
Trying to dynamically load code to override the widget is the wrong approach to take. Instead, the widget
method should decide which version to display on a request-by-request basis. Before we look at some ways to do that, let's explain why the module overriding doesn't work.
ApplicationController
is loaded when your Rails application starts. This automatically includes the ApplicationHelper
module, which defines the widget
method. The AdminHelper
isn't loaded because of the config.action_controller.include_all_helpers
setting - by setting it to false
Rails will only load a helper with the same name as the controller.
When a regular user calls the widget
method, the string "widget"
is returned. But as soon as an admin visits the site the include_backend_helpers
callback loads your AdminHelper
module, overriding the widget
method. The admin gets the string "admin-widget"
returned - but so does every future visitor to the site! The widget
method isn't differentiating based on user types.
You could solve this by reloading the ApplicationController
after each request, but that takes time and will make performance suck. You might be able to unload the module , but that seems like an approach fraught with difficulty. You could also try reloading the helper on each request:
def include_backend_helpers
if admin_signed_in?
self.class.helper "admin/backend"
else
self.class.helper "user/backend"
end
end
But I'm not sure that would work, and I wouldn't recommend it.
Instead, it seems that it's the widget's job to decide what's appropriate to display. For instance, if this was a navigation component, it should be smart enough to only show admin links. You can still break it out into separate methods:
module ApplicationHelper
def widget
if admin_signed_in?
user_widget
else
admin_widget
end
end
private
def user_widget
"widget"
end
def admin_widget
"admin-widget"
end
end
You can use a similar approach with Rails partials - you could have a _navigation.html.erb
partial that includes either _user_navigation.html.erb
or _admin_navigation.html.erb
based on admin_signed_in?
. Your view includes `<%= render "navigation" %>", and doesn't worry about the internal details.
If you have enough code that the above starts to feel unwieldy, then you should look at Cells for Rails . It's a gem that makes it easier to build encapsulated display components - combinations of controller logic and view code.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.