Reusing Rails Controllers is a Good Thing

If there were such a thing as the Ten Commandments of programming, code reuse would surely be included. Now you’re probably thinking I’ve lost my mind, because any good developer knows that code reuse is a pipe dream. But that’s because you are thinking on the macro level and not the micro level. At the micro level, code reuse has altogether more pleasant acronym, DRY, or do not repeat yourself.

The tussle in the Rails community over components illustrated this tension well. On one extreme, advocates dreamed of creating plug-and-play components that could be reused across multiple applications. On the other extreme, detractors sneered at code reuse as a hopeless endeavor and vowed to strip out all traces of components from the the Rails 2.0 release. Left out in the cold was the view that reusing code, specifically controllers, within a single application was a good thing.

Reusing Controllers

Rails is built using the Model-View-Controller pattern, which is designed to segment an application into controllers, models and views. Rails also adds in the concept of filters, which are pieces of code that run before and after controllers.

The Rails community encourages reuse of models, filters and views, but seems to actively discourage the reuse of controllers if the Ruby on Rails book is any indication:

When Rails was initially released, it came with a system for creating components. Unfortunately, the implementation of components left a lot to be desired: performance
was poor, and there were unanticipated side effects. As a result,
components are being phased out.

Instead, the common wisdom now is to synthesize component-like functionality
using a combination of before filters and partials. Use the before filter to set up the context for the partial, and then render the fragment you want using
a regular render :partial call.

Like much conventional wisdom, this advice is hogwash.

Filters + Partials != Controllers

The problem with just using filters and partials is that it only applies to a subset of web applications. A good example, and of course the one used in the Ruby on Rails book, is a shopping website. The focal point of most shopping websites is a shopping cart. The point of the application is to make it easy for users to add things to the cart, modify the cart and hopefully buy the contents of the cart. Since the cart plays such a crucial role, it often make sense to have a filter that setups the cart so each controller has easy access to it. A nice side affect of this approach, is that views also have access to the cart, as described in the quote above.

But for many other types of applications, filters and partials can’t make up for controllers. For example, take a look at the Boulder community on MapBuzz. The top-left side of the page is rendered by the community controller, the comments on the bottom left by a comment controller and the map listings on the right are by a map browser controller. If you log-in, then a couple of additional tabs are added to the page, each rendered by its own controller.

This type of composition is quite common in Web 2.0 applications. Take most social networking sites – they’ll mix together news feeds, discussion boards, friends/friends lists, pictures,etc., in a variety of different ways depending on the current page.

The design problem is that any given controller can be called in two different contexts:

  • When the whole page is rendered via a Browser page refresh
  • When just the controller is rendered via an Ajax call

Trying to do this with just filters and helpers is a non-starter, because you end up with one big controller that needs to run different filters depending on the context of the call.

The better approach is to divide your controllers into logical units, and then have a separate page controller for the entire page. When the page is rendered, the page controllers should delegate rendering the various sub-parts (for example, tabs) of the page to the appropriate controller. When just one of the sub-parts of the page needs to be rerendered, due to an Ajax call, then you directly call the appropriate controller.

Performance

In Rails, a controller or view can call another controller using the much maligned render_component method. Part of the problem is the method is misnamed. It no longer has anything to do with rendering components – instead its used to invoke another controller. Therefore, it would be more appropriately named render_controller, call_controller, invoke_controller, etc.

Assuming you agree with my so far, reading the Rails documentation for render_component with certainly give you pause:

Components should be used with care. They‘re significantly slower than simply splitting reusable parts into partials and conceptually more complicated. Don‘t use components as a way of separating concerns inside a single application. Instead, reserve components to those rare cases where you truly have reusable view and controller elements that can be employed across many applications at once.

So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters.

Undoubtedly this was true once upon a time. Is it still? There is one way to find out – run a test. I created a new Rails application using the built-in generators and then added the following simple code:

controller/main_controller.rb

class MainController < ApplicationController
  def get_without_controller
    a = 1
  end
  
  def get_with_controller
    a = 1
  end
end

controller/sidebar_controller.rb

class SidebarController < ApplicationController
  def get
    render(:partial => 'sidebar/content')
  end
end

views/main/get_without_controller.html.erb

<p>Some fun content goes here</p>
<div class="sidebar">
  <%= render(:partial => 'sidebar/content') %>
</div>

views/main/get_with_controller.html.erb

<p>Some fun content goes here</p>
<div class="sidebar">
  <%= render_component(:controller => SidebarController,
                       :action => 'get') %>
</div>

views/sidebar/_content.html.erb

<p>Hi there</p>

There are two paths through this application:

  • GET ‘/main/get_without_controller.rb’
  • GET ‘/main/get_with_controller.rb’

In case its not obvious, get_without_controller usesrender(:partial) to include the sidebar content while get_with_controller uses render_component. Using both benchmark and ruby-prof, I ran each method 100 times using a souped up integration test (more about that in a future post). The results, using Rails 2.02 on Ruby 1.8.4 on WindowsXP on a Pentium M laptop (about 3 years old) are:

Method
100 Requests (s)
1 Request (s)

get_without_controller
0.30
0.0030

get_with_controller
0.45
0.0045

 

So using components is 50% slower, but the overhead is a miniscule 0.0015 seconds per request. That overhead is obviously lost in a real application. Of course you have to be careful when using render_component to not try and do to much per HTTP request – but the same is true using filters and partials.

DRY Up Your Controllers

In truth, render_component is the most primitive way imaginable of reusing controllers. But it does let you to DRY up your Rails application by letting you create more cohesive controllers that can be reused within a single website. For most websites you won’t need this functionality, but when you do, there isn’t a substitute for it and don’t let anyone browbeat you into thinking there is.

  1. February 11, 2008

    Great post.

    I’ve been avoiding the ‘render_component’ stuff purely from what I’ve read about the awful performance.

    At the same time I’ve been missing the way Java JSP/Servlets handles “includes” – which is superior to the :partial/filter alternative recommended by Rails.

    I will definitely experiment with ‘render_component’ to replace my existing mix of “render :partial” and “periodically_call_remote”.

    Reply
  2. February 12, 2008

    Code reuse is not a hopeless endeavour unless you have less-than-competent programmers. Reusable controllers are not a pipe dream. My MVC framework for web applications has a set of 30 page controllers which service over 2000 transactions. No only that, but each page is rendered via an XSL stylesheet, and there are only 11 stylesheets in my XSL library. How’s that for reusability?

    The catch? My framework is not RoR, it is Radicore (http://www.radicore.org). It is not written in the Ruby language, it uses PHP. It does not use ActiveRecord, it uses a data dictionary. It does not use an ORM as each object relates directly to a database table.

    Reply
  3. February 12, 2008

    It is not actually unusual to be reusing codes. Especially for practicality’s sake. It even shows how much talent one has because you get to make codes so versatile.

    Reply
  4. February 12, 2008

    Thank you for sharing the knowledge on Rails controller. I learned a lot of things regarding this matter. In terms of application, models and views are well explained.

    Reply
  5. February 13, 2008

    At work, we’ve got several independent units on each page and we’ve taken to wrapping the reusable units up inside helpers.

    The helper’s then free to do pretty much anything to render its output. It can:

    * Just render a partial
    * Select and then render a partial depending on, say, whether a user is logged in.
    * Toddle off to the database and fetch a bunch of objects to render
    * Go the whole render_component hog.

    The key thing is to pull the act of rendering the partial up into the helper – you write your views to a simple, uniform interface and keep your logic in .rb files.

    I’d rather see code like:

    < %= comment_widgets @comments %>

    than

    < %= render :partial => ‘comment’, :collection => @comments %>

    especially when I can implement comment_widgets and comment_widget in a declarative style like:

    widget :comment

    Implementation of widget is left as an exercise for the interested reader; it’s not exactly tricky.

    Reply
  6. Charlie Savage –
    February 13, 2008

    Hi Tony,

    Actually I think its the other way around – code reuse is only possible if you have competent programmers.

    For [SIAS](http://cfis.savagexi.com/articles/2006/03/21/rest-leading-to-rails), we used XSL stylesheets on the server side to convert XML to HTML. It would have been great to move the XSL transformation to the client, but back then there was no way it was going to work. In the end I’ve moved away from XSL, because writing programs in XML is fairly awful. What have your experiences been?

    Reply
  7. Charlie Savage –
    February 13, 2008

    Viktor – So I take it you’re not a believer in [YAGNI](http://en.wikipedia.org/wiki/You_Ain't_Gonna_Need_It)?

    Obviously code reuse is a good thing within an application – it just seems that fact was lost in the Rails community in the big hubbub over components.

    Reply
  8. Charlie Savage –
    February 13, 2008

    Hey Peyton – No problem…

    Reply
  9. Charlie Savage –
    February 13, 2008

    Welcome back Piers – I was hoping you’d share your experiences with Typo and the move away from using sidebar controllers to using a system based on partials. Did you draw any conclusions in the process?

    As far as using helpers instead of partials I’m torn. I see where you are coming from, but I really find generating HTML using ERB much more pleasant. I suppose you’d point on that you just have your helper render the partial, but then you’re adding an extra level on indirection that doesn’t gain you much.

    You’re only one step away from self rendering [components](http://blog.caboo.se/articles/2007/4/8/heresy-and-turtles-all-the-way-down-with-avi-bryant) though…

    Reply
  10. February 14, 2008

    I’m not sure that self rendering components is a particularly bad way to go though. I’m not a big fan of templating in general and ERB in particular – I prefer Haml for my personal work because it DRYs up so much of the noise of HTML/ERB, which helps you see ugliness in the code that much quicker.

    I think it’s instructive too, to think about how you’d go about implementing something like Facebook in rails, where pages like the profile page are pretty much entirely componentized. You’re pretty much obliged to have some kind of component system where components know how to render themselves independently of the rest of the page.

    Typo’s sidebars are a cut down version of this approach – the best trick they have up their sleeves, to my way of thinking anyway is the way their configuration system works. There’s a standardised set of setting types which allows an administrative view to reflect on the sidebar’s class and generate a form for each instance of the sidebar. I’m still rather proud of that. I’m thinking of extending it to a wider typo plugin system which will fire callbacks at various points in the request/render cycle, allowing plugins to customize various almost any aspect of the process. The use case I have in mind is a plugin to implement captchas, both because a captcha system will go into typo’s core over my cold dead body and because a well implemented captcha scheme needs to plug into various parts of the request/render cycle.

    Which is tangential to what you’re writing about, but, I hope, still interesting.

    Reply
  11. February 26, 2008

    Actually, Engines is not heavy at all, especially since Rails 2. It’s basically regular Rails Plugins with some monkeypatching of the class finding methods, nothing more.

    Also, the main reason “components” are slow is that (afaik) you can’t pass complete objects to them. This would mean you create a wall between your current controller and the one you’re rendering as a component, which means you will need to hit the database everytime you want to use the same object in different components. *this* is the main performance bottleneck: the request stack only allows you to pass strings (via the params hash)

    Cells do not have this problem, you can just pass *real objects* to the Cells. Besides performance this has the benefit of being able to pass modified-but-not-yet-saved objects to the components (for rendering validation errors, for example, something I’ve done in a Cells-using project of mine)

    Reply
  12. Anton
    March 4, 2008

    Now that REST is the core of Rails 2.0 its even more an issue to think about render_component.
    I personaly have the impression it slows dones the performance more than 50% but however. I am using it even I am not really happy with the req/sec.

    It would be good to know how you can tune it at least a little bit and still following DRY and REST.

    Reply

Leave a Reply

Your email address will not be published.

Top