Skip to content

Instantly share code, notes, and snippets.

@kevcha
Last active December 29, 2022 07:53
Show Gist options
  • Save kevcha/458fcabcb271ab85c731 to your computer and use it in GitHub Desktop.
Save kevcha/458fcabcb271ab85c731 to your computer and use it in GitHub Desktop.
How to automatically share partials between namespaces in rails

When writing a rails app, we often have to deal with multiple namespaces. The most common case is having classic actions and some others in an admin namespace. Some actions may have same views between namespaces.

For exemple, you could have a ProjectsController with an action index :

class ProjectsController < ApplicationController
  def index
    # do something
  end
end

Calling /projects action renders app/views/projects/index.html.erb template.

Let's say you also have a Admin::ProjectsController with a index action :

class Admin::ProjectsController < ApplicationController
  def index
    # do something
  end
end

Calling admin/projects action renders app/views/admin/projects/index.html.erb template.

Those actions renders differents views, but what if we want to render the same template for both ?

  • You can call manually render 'projects/show' in the corresponding controller's action in admin namespace
class Admin::ProjectsController < ApplicationController
  def index
    # do something
    render 'projects/index'
  end
end
  • You can also call manually <%= render template: 'projects/show' %> in app/views/admin/projects/show.html.erb

Don't you think it could be better if we could find a mechanism to render automatically app/views/admin/projects/index.html.erb if exists, otherwise fallback to app/views/projects/index.html.erb ?

By default, paths prefixes where Rails will looks for templates (for a given controller) can be retreived with, in a rails console :

Admin::ProjectsController.new.send(:_prefixes)

In our case, it'll return ["admin/projects", "application"]. Admin::ProjectsController inherits from ApplicationController, that's why there is "application" in the returned array. Those element are sorted by priority. For the index action, Rails will first look in app/views/projects/index.html.erb, then app/views/application/index.html.erb, then raise a MissingTemplate exception.

We can add or remove elements to this array, overriding in a controller local_prefixes class method. If you want for the whole namespace to fallback to another path if view doesn't exist, then create a Admin::BaseController that Admin::ProjectsController will inherits from, with :

class Admin::BaseController < ApplicationController
  def self.local_prefixes
    [controller_path, controller_path.sub(/^admin\//, '')]
  end
end

and

class Admin::ProjectsController < Admin::BaseController
  def index
    # do something
  end
end

The local_prefixes method returns an array of prefixes. By default, this method is defined in action_view/view_path.rb :

# Override this method in your controller if you want to change paths prefixes for finding views.
# Prefixes defined here will still be added to parents' <tt>._prefixes</tt>.
def local_prefixes
  [controller_path]
end

Adding another element to this array will make rails search for partials along those paths. Don't forget about the priority. Here, Rails will first look for controller_path, which is "admin/projects" for Admin::ProjectsController, then if app/views/admin/projects/index.html/erb doesn't exist, will look for controller_path.sub(/^admin\//, ''), which will now be "projects"

We can check that calling in a rails console

Admin::ProjectsController.new.send(:_prefixes) #  `["admin/projects", "projects", "admin/base", "base", "application"]`
@edwardmp
Copy link

Neat, +1

@goalaleo
Copy link

goalaleo commented Oct 4, 2018

👍

@kostassof
Copy link

Thanks! :)

@zernel
Copy link

zernel commented Sep 8, 2020

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment