About the Scope of Default View Helpers
In this article, I’ll explain what scope the methods defined in Rails view helpers apply to,
how to change this scope, and a reasonable step-by-step procedure for safely changing the scope in an existing application.
Methods Defined in Helpers Can Be Called from Any View
For example, suppose you have UsersHelper#display_name
defined like this:
# app/helpers/users_helper.rb
module UsersHelper
def display_name(user)
"#{user.lastname} #{user.firstname}"
end
end
This method can be used not only in views rendered by UsersController
, but also in views rendered by other controllers:
# app/controllers/posts_controller.rb
class PostsController
def index
@posts = Post.all
end
end
<% # app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
<p><%= post.content %></p>
<small><%= display_name(post.user) %></small> <!-- display_name is usable -->
<% end %>
In other words, methods defined in view helpers are basically usable in any view.
You might just accept this as “that’s the spec,” but because the scope is so broad, there are concerns.
Some issues include:
- To confirm that a helper method is unused, you must check the entire application.
- It’s difficult to predict behavior when methods with the same name are defined in different helpers.
Rails provides an option to address such concerns.
The config.action_controller.include_all_helpers
Option
As noted above, by default Rails controllers load all helpers.
This behavior depends on the setting of config.action_controller.include_all_helpers
.
https://guides.rubyonrails.org/configuring.html#config-action-controller-include-all-helpers
Configures whether all view helpers are available everywhere or are scoped to the corresponding controller.
The default value is true
.
So, if you set this option in config/application.rb
, the behavior changes:
module Example
class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version.
config.load_defaults 8.0
+
+ config.action_controller.include_all_helpers = false
With this setting, a method like display_name
defined in UsersHelper
can no longer be called from a view rendered by PostsController
:
# app/helpers/users_helper.rb
module UsersHelper
def display_name(user)
"#{user.lastname} #{user.firstname}"
end
end
# app/controllers/posts_controller.rb
class PostsController
def index
@posts = Post.all
end
end
<% # app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
<p><%= post.content %></p>
<!-- Cannot be called! -->
<small><%= display_name(post.user) %></small> <!-- undefined method 'display_name' for ... -->
<% end %>
How Is the Applicable Helper Determined?
Suppose we have the following controller and helper inheritance structure:
app/
├── controllers
│ ├── admin
│ │ ├── application_controller.rb
│ │ └── users_controller.rb
│ ├── application_controller.rb
│ ├── posts_controller.rb
│ └── users_controller.rb
└── helpers
├── admin
│ ├── application_helper.rb
│ └── users_helper.rb
├── application_helper.rb
├── posts_helper.rb
└── users_helper.rb
class ApplicationController < ActionController::Base; end
class UsersController < ApplicationController; end
class PostsController < ApplicationController; end
class Admin::ApplicationController < ApplicationController; end
class Admin::UsersController < Admin::ApplicationController; end
In the context of UsersController
, the following helpers are included:
UsersHelper
-
ApplicationHelper
(always)
This depends on controller inheritance.
UsersController
automatically includes UsersHelper
, and ApplicationController
includes ApplicationHelper
.
Thus, UsersController
has both UsersHelper
and ApplicationHelper
methods available.
As another example, in the context of Admin::UsersController
, the included helpers are:
Admin::UsersHelper
Admin::ApplicationHelper
ApplicationHelper
This aligns with the inheritance chain:
Admin::UsersHelper
, Admin::ApplicationHelper
, and then ApplicationHelper
.
This behavior is the same as the template lookup inheritance mechanism used for rendering templates and partials:
https://guides.rubyonrails.org/layouts_and_rendering.html#template-inheritance
Migrating an Existing Application to include_all_helpers = false
This setting is very useful.
Here’s a migration path for existing applications.
First Step
The first step is, of course, to set config.action_controller.include_all_helpers = false
:
module Example
class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version.
config.load_defaults 8.0
+
+ config.action_controller.include_all_helpers = false
With this alone, you’ll get errors in places where helper methods “happened to be available.”
As a temporary measure, include all helpers in ApplicationHelper
to maintain existing behavior:
# app/helpers/application_helper.rb
module ApplicationHelper
include Admin::ApplicationHelper
include Admin::UsersHelper
include PostsHelper
include UsersHelper
end
(In many apps, all controllers ultimately inherit from ApplicationController
, so ApplicationHelper
is available in every view.)
Narrowing Things Down Gradually
From here, reduce dependencies step by step.
For example, if everything under Admin::\*
inherits from Admin::ApplicationController
, group them there:
# app/helpers/application_helper.rb
module ApplicationHelper
include Admin::ApplicationHelper
include PostsHelper
include UsersHelper
end
# app/helpers/admin/application_helper.rb
module Admin::ApplicationHelper
include Admin::UsersHelper
end
Then, remove helpers from ApplicationHelper
if they’re only used in a specific controller:
module ApplicationHelper
include Admin::ApplicationHelper
- include PostsHelper
include UsersHelper
end
On the other hand, for utility methods used across multiple contexts, move them into ApplicationHelper
:
module ApplicationHelper
include Admin::ApplicationHelper
- include UsersHelper
+
+ def very_useful_helper_method(user)
+ #
+ end
end
module UsersHelper
- def very_useful_helper_method(user)
- #
- end
end
By making these adjustments, you can converge toward a scope that is “only available where needed.”
When Helper Methods from Gems Stop Working
This may surface with gems like font-awesome-rails.
That gem provides the view helper method fa_icon
.
But when config.action_controller.include_all_helpers = false
, you must explicitly include it.
Simply include it in ApplicationHelper
, and you’re done.
This makes usage more explicit, which is a good thing for maintainability:
# app/helpers/application_helper.rb
module ApplicationHelper
include FontAwesome::Rails::IconHelper
end
Summary
- By default, methods defined in view helpers can be called from any controller’s view.
- Setting
config.action_controller.include_all_helpers = false
restricts loaded helpers to those corresponding to the controller (and its inheritance chain). -
ApplicationHelper
is always included regardless of the setting, making it a good place for common methods. - For migration, the safe path is: “collect everything in
ApplicationHelper
first → then gradually move and reduce.”
Disclaimer
The content in this article was verified with Rails 8.0.2.1,
but the behavior is basically the same in many past versions.
(Strictly speaking, this assumes Rails 3.1 or later, when include_all_helpers
was introduced.)
Top comments (0)