Rails controllers hierarchy and views
Organizing your controllers
Few weeks ago I found this post explaining how DHH oranizes controllers in his apps. I also found this(it’s in russian) nice post with a nice example of the controllers hierarchy. The main idea is to organize controllers the same way our website works. For example if there’re users on our website and users have posts the controllers structure should be:
# config/routes.rb
resources :users do
scope module: :users do
resoures :posts
end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
...
end
# app/controllers/users/posts_controller.rb
module Users
class PostsController < ApplicationController
...
end
end
This structure allows us to keep controllers simple and clean, but requires to write some extra code.
Problem
The problem is that even if we organize our controller’s modules the same way our routes are, we don’t want to use the same structure for views. We want to avoid adding the views/users/posts
directory for our views and keep them in views/posts
. Of course we could just specify the view path explicitly by calling render
method:
# app/controllers/users/posts_controller.rb
module Users
class PostsController < ApplicationController
def index
render 'posts/index'
end
end
end
And do the same thing for render
inside our views:
# app/views/posts/index.html.slim
- @posts.each do |post|
= render partial: 'posts/post', locals: { post: post }
Solution
Instead of specifying each view’s path, we could add some magic:
# app/controllers/users/posts_controller.rb
module Users
class PostsController < ApplicationController
before_filter { lookup_context.prefixes << 'posts' }
end
end
# app/views/posts/index.html.slim
- @posts.each do |post|
= render partial: 'post', locals: { post: post }
That’s better! At first Rails will try to find index
at views/users/posts
path. If it’s not found, it will look into views/posts
. But can we make it prettier? Maybe add some DSL? Sure!
# app/controllers/concerns/views_path.rb
module ViewsPath
def add_views_path(path)
before_filter do
lookup_context.prefixes << path
end
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
extend ViewsPath
...
end
# app/controllers/users/posts_controller.rb
module Users
class PostsController < ApplicationController
add_views_path :posts
end
end
Update: Gem
Added gem that patches your ActionController::Base and allows to use add_views_path
in yours controllers.
# Gemfile
gem 'rails_views_path'