Cells bring clean re-use to your Rails views

Re-using logic in Rails views has long been much more difficult than it needs to be. Any non-trivial web app has stateful UI elements that recur throughout the site like a shopping cart or a navigation area customized for the logged-in user. The need for views that can easily be embedded in other views is pervasive, but the ability to do this cleanly and easily in Rails is simply not there.

Which is why there is, or was, the now-deprecated render_component method. Render_component was conceptually sound – views could be rendered within views using their own controllers – but it was fatally flawed by its need to repeat the entire Rails request cycle, making it intolerably slow.

Many Rails projects resort to partials with before_filters to fill the gap, but such an approach has its own problems. Shared partials bloat the application_helper with their initialization code, and since they can see all of the instance variables of the controller methods that precede them – an unfortunate design decision, I think – they tend to be rife with dependencies on the controllers that initially hosted them, the views that initially contained them, and the helpers that they depend upon. It is rare, in my experience, to find substantial partials that are readily sharable without considerable, painful, debugging to extricate them from the web of dependencies they weave.

Like Rails views themselves, shared partials just don’t feel anything like OOP and don’t provide the clean reusability of real objects, like those found by using ActiveRecord. Which is why there is the Cells plugin.

Cells were created by Ezra Zygmuntowicz, co-founder of EngineYard, father of Merb and all around stellar guy in the Rails world. Recently, they have been brought to release 1.0 status by the work of Peter Bex and Nick Sutterer. Cells make it simple to embed views within views, while maintaining the ability to have a controller that initializes those views and – very importantly – they are fast. In my testing, Cells rendered about 4 times faster than components and rendering 1,000 Cells within a view still netted sub-second response times in a development environment.

[Update, March 25, 2008: Engines is now optional.] Installation of Cells can be achieved by first installing the Engines plugin, which it depends upon for its rendering capabilities. Engines was once scorned for its Rails monkey-patching ways, but its now a well-behaved plugin that offers a number of interesting dimensions of reuse possibilities in its own right.

Create a Rails project and configure a database.yml file in the usual way to see Cells in action.

Next, install right off the trunk of both plugin the projects:


script/plugin install http://svn.rails-engines.org/engines/trunk/
mv vendor/plugins/trunk vendor/plugins/engines
script/plugin install http://cells.rubyforge.org/svn/cells_plugin/trunk
mv vendor/plugins/trunk vendor/plugins/cells

Cells are conceptually similar to controllers and views, but they are not exposed to Rails routing. They get their own branch in the app tree and no generators exist for them as of this writing, so go ahead and create a folder under app called “cells”.

In that folder, we can create our first cell controller. The naming convention should be familiar. We’ll simply create a file called first_cell.rb. Inside that file, add the following code:

class FirstCell < Cell::Base
def index
end
end

Next, create a sub-folder of cells called “first”, This is where our first cell’s views will live. Depending on your version of Rails, you can call the file “index.rhtml” or “index.html.erb”. Put whatever markup or text you feel like putting in that file.

To generate a regular old Rails view to host this, we can simply do this:

script/generate controller host index

Then, in app/views/host/index.html.erb (or, .rhtml), we can embed our first cell. For kicks, we’ll render it 100 times:

<% 100.times do %>
<%= render_cell :first, :index %>
<% end %>

Launch your Rails app and navigate to http://localhost:3000/host/index and visually caress your fine work. Congratulations, you have discovered Rails view re-use made fast and simple.

Cells require all arguments passed to them, beyond the name of the cell and its action, to be explicit. So, any hash values passed in to a cell are magically added to a hash within the cell called @opts. So, if our cell call became this,

<%= render_cell :first, :index, :some_arg => :some_value %>

that would result in @opts[:some_arg] being populated and available in our first cell controller and views. Cells also allow inheritance and encourage composition by their speed and utility.

Create cells within cells and you can quickly build up a repository of rich interface elements like tree controls, grids, and date pickers using your own home-grown designs or leveraging some excellent Javascript library like Ext, Dojo, jQuery or YUI. Rails developers need not be the poor kids on the block with just a few sparse UI controls, unlike our Java and .NET brethren.

Whether you want your view re-use at the level of page regions or rich UI widgets – or both – Cells can get you there today. Your apps will become instantly more maintainable and you will be able to expand your toolbox to have bigger, better building blocks for the UI’s yet to be crafted in your future.

Resources:
http://cells.rubyforge.org/ (examples are here: http://cells.rubyforge.org/rdoc/index.html)
http://rails-engines.org/

Other interesting developments in Rails view concepts:
http://www.caboo.se/articles/2007/8/23/simple-presenters
http://erector.rubyforge.org/

If you are looking for top-notch Ruby on Rails development and project management with an emphasis on distinctive user experiences, email mike.pence@gmail.com.

21 thoughts on “Cells bring clean re-use to your Rails views

  1. Mike introduced me to cells in a collective project, and I’ve been very pleased with their clarity and performance.

    It’s solid and it fills what many of us feel is a glaring hole in the Rails toolbox.

  2. This is an excellent post! Thanks for your advocacy efforts, Mike! Cells can really use the publicity.

    For those who are interested in an example plugin that uses Cells to great effect should also take a look at the project I started to work on Cells for: http://formbuilder.rubyforge.org (we will set up a “real” website soon). It has been tested on Rails 1.2 only, by the way.

    I’d also like to note that there are plans to make the Engines plug-in optional. When I get an opportunity to work with Cells again I will probably do it. (Formbuilder _is_ an engine, so there it’s an advantage)

  3. It is not clear to me why one should use cells instead of partials. The only reason I caught was for OO?

    Also when should a cell be used instead of a partial and likewise a partial instead of a cell?

  4. Sounds good. However, I find erb fairly gross and prefer Markaby. With Markaby I can already define ‘cells’ ala:

    def panel(heading,block&)
    markaby do
    div do
    h1 heading
    hr
    text capture(&block)
    end
    end
    end

    Markaby’s one and only drawback that I’ve come across is it’s slow. But the time I save not dealing with HTML is worth it. I encourage anyone to give it a try.

    Also similar is Errector: http://erector.rubyforge.org/rdoc/

  5. @Rob O: When you also need additional logic associated with your partials, it’s ugly and unmanageable to put that logic in your templates. The typical Rails way to solve that is to use helpers instead, but that’s really ugly when you also have complex (or large amounts of) HTML to output.

    Also, rendering partials requires a lot of intimacy with your controllers if you want to keep using Rails helpers – a very annoying aspect of those is that they assume you have instance variables (@user, for example) while partials locals are _not_ instance variables, but simply local variables. Of course, instance variables are specific to controllers, so your partials already would have to know what controllers they can be rendered by (or your controllers need to know which partials may possibly be used in their views) All this becomes very tough if you have deeply nested and/or recursively rendered partials.

    I hope you find this explanation useful. If not, feel free to ask for more details on IRC or the Cells mailing lists – more people read those.

  6. A partial is just a dumb template!
    Every time you render a cell, it executes code attached to a view and renders that view. You’re used to that from controller actions.
    So, you could either see cells as “mini-controllers” or as partials with attached code blocks. Cells are “intelligent partials”.

    You cannot do this level of code encapsulation with partials, since partials rely on preparing code in a controller method.

    Mike: You rock.

  7. Very exciting! I created a new rails 2.0.2 app, added the plugins, and one cell with one method. The cell seems to unpredictably return the value of the called method instead of rendering the cell view. So you’re right – not working with rails 2 yet. I love the way its shaping up, however!!

  8. Great idea, one thing that i dont like is the engines plug-in which is needed to work with cells. I would really prefer if this would become optional.

    One Question
    Is it possible to user other template engines (perhaps HAML) for cell views ?

  9. I am still not sure how cell is so different from partial…would like to read and use more of this to see the difference

  10. @Moritz: Yes, haml is possible. We use ActionView to render templates, so any templating you can use in Rails, you can use in Cells.

    re: engines plugin; see my previous post.

  11. Well, slap me upside the head with a wet walleye. I posted a comment earlier claiming that cells was not working with rails 2.0 because the result of a cell method sometimes gets returned instead of rendering the view. WRONG!

    I just read through the cells mailing list (at rubyforge.org/mail/?group_id=2978) and saw that this is a feature, not a bug. If the cell method returns a String, the String value is not rendered and is returned to the ERB stream (or whatever called the cell). If you really want the cell’s method view rendered instead, simply make sure the method returns anything but a String (nil, perhaps).

    While not obvious, it is certainly a useful feature. And it means that I can re-install the cells plugin for my rails 2.0.2 app and start to use it in earnest.

  12. Looks great but can anyone get the example to work ? I keep getting the following exception:

    undefined local variable or method `logger’ for FirstCell:Class

    Any help gratefully received

Leave a reply to Jim James Cancel reply