Pragmatic Rails: Thoughts on Views, Inheritance, View Inheritance and Rails 3.0.4
Thursday, December 23rd, 2010While hacking on Cells for Rails 3 with Yehuda earlier this year we were discussing if Cells’ view inheritance will be superseded by Rails.Yehuda patiently postponed any work on it with the words “we will do that for you”
Apparently, he didn’t lie.
It seems that view inheritance will be available directly in Rails 3.0.4, which is a fantastic improvement for Rails.
In this post I’d like to discuss
- What is view inheritance and how does it help?
- What’s the problem when view inheritance is tightly bound to controllers?
- How shared components with inheritance can help
What is View Inheritance?
Say we have a PostsController in a blog app. It shows a list of posts. Wow. We also have a derived PrivatePostsController, here’s the inheritance chain.
PrivatePostsController < PostsController < ...
Both controllers have an #index method.
class PostsController < ApplicationController def index @posts = Posts.public_posts_for_all_of_yer end class PrivatePostsController < PostsController def index @posts = Posts.private_posts end
While the PostsController does have an index view, its child PrivatePostsController doesn’t.
|-- posts | |-- index.html.haml |-- private_posts |-- backend
Now, when the private controller wants to render its index view, it first looks in its own views directory. If it can’t find a suitable view, it just travels up the inheritance chain. It finds the parent’s index view, and uses this.
Cool, this is view inheritance.
Inheritance and partials?
The newly introduced mechanism also applies to partials, which is pretty awesome.
If the index view would use a partial to show a list of navigation links, the PrivatePostController could “overwrite” just this piece of the page by dropping a separate partial in its view directory.
|-- posts | |-- index.html.haml | |-- _navigation.html.haml |-- private_posts | |-- _navigation.html.haml
Overriding view pieces done simple. I’m happy this finally found its way into Rails! Good job, artemave.
Use case: Sidebar widgets
Usually we need shared partials whenever we want a reusable view component.

What about a status box in our app exposing helpful links to the reader? If an admin user is logged in, he (or her) will get links to edit new, unread posts and his own drafts.
An editor might see links to read new posts assigned to him. His job is just proof-reading.
This is a typical setup in almost every Rails app, small boxes display different informations according to the authenticated user.
Usually this ends up in a helper that delegates to different partials, where lots of code is put into that partials.
def render_status_box if current_user.admin? render :partial => "shared/admin_box" elsif current_user.editor? render :partial => "shared/editor_box" # and so on...
The problem here is, you simply cannot use inheritance anywhere. Neither is it possible to derive helpers (they are modules), nor could view inheritance be applied here.
You can’t inherit shared partials
Why that? Well, the shared partial and the rendering controller are not related in any way. Nevertheless, the controller’s ancestor chain is inspected when trying to find an “inheritable” shared partial, which is simply the wrong place.
In other words, the partial lookup will climb up the inheritance chain of the controller using the partial, and that’s definitly the wrong place to search.
The bottom line: When it comes to reuseable view components, Rails’ view inheritance is still insufficient.
Reuseability with inheritance
So, what do to now? The answer, as usual: Divide and conquer. Split your view into object-oriented components and get back the power of inheritance.
You’re still with me? Cool.
First, I create a cell that provides the basic functionalities – generating a user greeting and compiling a list of links for the user.
class UserCell < Cell::Rails def box @user_greeting = user_greeting @post_links = links render :layout => :box end def user_greeting "#{current_user.name} (#{current_user.role})" end def links current_user.unread_posts.collect do |p| [p.title, post_path(p)] end end end
If you’re new to cells, you might read this introducing post.
How to get that in my app?
Now I embedd the cell in the application layout.
#sidebar = render_cell :user, :box
This basically calls the #box state of our UserCell, which
- compiles the greeting message in
#user_greeting. Note how this removes the need for pushing complex helpers into the view.
- prepares a list of post links in
#links. While we could do the setup in the view, too, I prefer placing that kind of code to the controller, since its job is aggregating data. It also improves unit-testability and makes it easy to inherit and override!
The render statement finally parses the cell’s view.
|-- app | |-- cells | | |-- user | | | |-- box.html.haml
The box view could look like this.
Hey, #{@user_greeting}, how are you?
Check that:
%ul
- @post_links.each do |p|
= %li #{link_to *p}
And we get a list of links pointing to posts which the logged in user hasn’t read, yet. Yeah.
Override a helper, inherit a view!

When an admin user is logged in, we want a slightly different list, showing links to edit posts.
Let’s use inheritance to do just that!
class AdminCell < UserCell def links current_user.pending_posts.collect do |p| [p.title, edit_post_path(p)] end end end
This is all, we just overwrite the helper. Everything else is inherited from the UserCell. The methods, the states, the views.
No deciders in your views!
Now, your application layout shouldn’t end up with deciders figuring out which cell to render.
#sidebar
- if current_user.admin?
= render_cell :admin, :box
- elsif current_user.editor?
= render_cell :user, :boxCells provides you with a builder to prevent your view from being cluttered.
class UserCell < Cell::Rails build do |opts| AdminCell if current_user.admin? end
That’s all! Now, calls to #render_cell(:user, ...) will query the builder and internally create the correct cell for you.
It’s more than a feature
I hope the examples showed how view inheritance can help cleaning up your code while providing an object-oriented view layer with real inheritance. This is nothing exotic – many cells users have reported to use the techniques discussed in this post successfully.
And now, lemme quote my friend Kevin from Austin: Merry Christmas and all that stuff!

