Last Updated: February 25, 2016
·
8.044K
· lankz

ActionController Concerns and View Inheritance

View inheritance (sometimes called template inheritance) is a neat feature of Rails/ActionController that seems to be often neglected, poorly understood or passed over as not being useful — but when used properly it can really help DRY up and unclutter your views.

The way it works is simple enough: when checking the filesystem for a template or partial, Rails will walk up the inheritance hierarchy of the controller until it finds something. For example, consider the following:

class AuthenticatedController < ApplicationController
  before_filter :authenticate_user!
end

class UsersController < AuthenticatedController
  # some awesome code...
end

When rendering a partial — let's say _table.html.erb, the filesystem will be checked in the following order:

app/views/users/_table.html.erb
app/views/authenticated/_table.html.erb
app/views/application/_table.html.erb

The first file found will be used. The same goes for templates (such as index.html.erb).

All is well and good until you find yourself in a situation where direct inheritance doesn't make sense. If you've made use of the concerns directory and abstracted out chucks of reusable controller code (nice work) — effectively multiple inheritance — you'll find you loose the functionality above.

The culprit is the #parent_prefixes method, which recurses the inheritance hierarchy using #superclass. Fear not — it's easy to mess with:

# app/controllers/concerns/authenticating_controller.rb

module AuthenticatingController
  extend ActiveSupport::Concern

  included do
    before_filter :authenticate_user!
  end

  module ClassMethods
    def parent_prefixes
      @parent_prefixes ||= super.unshift('authenticated')
    end
  end
end
module UsersController < ApplicationController
  include AuthenticatingController

  # the same awesome code...
end

I should mention that it looks like this method of overriding #parent_prefixes is slated for the chopping block — it will be replaced with #local_prefixes in Rails 4.3 and 5.0.