This is a real world example how to use Rails Cells, the popular gem for creating reusable view components. The following post is clean and all-ages. Let’s just all be friends, no bashing, no f-words today, just a brief HOWTO.

It is different from what you might have learned from Rails so far. This doesn’t mean it is wrong. Just think about it. If you feel it’s shit, let me know. If you like this approach, I wanna know as well.

What are sidebar elements?

Usually reoccurring components like a “Recent posts” box in a blog application are what we refer to as sidebar element.

The exemplary blog application with a reusable sidebar element.

The exemplary blog application used in this post(s) can be found on github and runs with Rails 3.

So what we do today is a sidebar box. It contains

  • links to a certain subset of recent posts
  • tags to click, which will filter the post list

No partials and helpers today

As we’re learning a new paradigm today, we’re not gonna do the traditional approach to implement the box. Using partials and helpers might do it as well, but, let’s look forward.

The first step is to generate a cell, which exposes the same properties as a real controller.

blog$ rails g cells:cell Posts recent --haml
      create  app/cells/posts_cell.rb
      create  app/cells/posts/recent.html.haml
      create  test/cells/posts_cell_test.rb

Ok, a cell class, a view, a test. Great.

In the second step, I want to render a list of recents posts. My file app/cells/posts_cell.rb looks like this.

class PostsCell < Cell::Base
  def recent
    @posts  = Post.recent
    render
  end

Like a controller.

Cells are controllers.

So what do we do here?

  • We aggregate the items to display by delegating to the model.
  • A call to #render instructs the cell state to render its view in app/cells/posts/recent.html.haml.

The view is simple as well.

%h3 Recent Posts
 
%ul
  - for p in @posts
    %li #{link_to p.title, p}
  • We simply iterate over the posts and link to the real article.
  • Note that we can use helpers, logic, everything in cells views.

As a last step, we render the cell in the application layout.

%body
  %h1 The Incredible Cells Blog
 
  #sidebar
    = render_cell :posts, :recent

That’s all for creating a render-only sidebar component. You could have done this with your traditional tools. However, we already got a couple of benefits.

  • The cell is reusable throughout your project and even between different projects. Just plug the app/cells/posts directory into another app and you’re done. I will post separately about that.
  • No controller pollution as the cell computes and renders in its own separate instance.
  • We have better testability – another post on that one, promised!

Have a tag-cloud widget

One of Cells strength is when it comes to writing composed widgets. The tag cloud sitting above the recent posts list should be a separate cell state – meaning a separate method and a view.

class PostsCell < Cell::Base
  def recent
  # ...
 
  def tag_cloud
    @tags = Post.tag_counts_on(:tags)
    render
  end
end

Not hard to see that we might need another view in app/cells/posts/tag_cloud.html.haml.

- tag_cloud(@tags, %w(css1 css2)) do |tag, css_class|
  = link_to tag.name, "?tag=#{tag.name}", :class => css_class

Yeah, we can even use helpers from other gems, like the acts_as_taggable_on helpers. We’re not limited in any way, dude.

To actually show the tag cloud inside our box, let’s plug the tags into the recent.haml view for now.

%h3 Recent Posts
 
= render :state => :tag_cloud
 
%ul
  - for p in @posts
    %li #{link_to p.title, p}

Wow, we basically can embed actions within actions – this is fuc… simply great.

Interaction, babe!

When clicking a tag the “Recent posts” list should update, showing only posts matching that tag. This is something called interactivity – the cell in your controller view processes user gestures!

Not a big deal. And, if you think this is breaking MVC: it is not. This is MVC.

Let’s see how the cell controller handles that.

class PostsCell < Cell::Base
  def recent
    tag     = params[:tag]
    @posts  = tag ? Post.tagged_with(tag) : Post.recent
    render
  end
 
  def tag_cloud
  # ...
end

The cell controller is exactly the place where this VC-gluecode should live. We don’t need work-arounds like presenters or helpers here, we prefer true MVC.

Discussion

We just learned the basic usage of Cells, including

  • Generating and writing simple render-only cells.
  • Composing nested cells by calling render :state in cell views.
  • Processing user input by using #params in the cell state.

And we got plenty of stuff to check out, like

  • Caching cells to boost your application performance and simultaneously improving your architecture.
  • Writing functional tests and unit tests for cells to create reliable components that can be plugged into any controller and just work!
  • putting cells into Rails engines to maximize your modularity. Imagine real widgets for a blog application shipped as gems and running seamless in your views.

Now, comment! And, hey parents: Writing posts without using swearwords SUCKS!

Tags: ,

23 Responses to “Let’s write a Reusable Sidebar Component in Rails 3!”

  1. Hey,

    thanks, I was waiting for a simple and clear example like this to understand *one of* the benefits of Cells, I may use this in a project refactoring soon, I’ll tell you if it fits my needs (I’m almost sure it does, though, since it exactly the same as this sidebar component example!).

    Cheers and congrats for Cells and this simple-but-to-the-point tutorial, I think you really need these kind words after all these *jealous* people that yelled at you!


  2. atros

    Rails was so monolithic. Now it’s getting much more modular with Rails3, bundler an cells. We will start to see more and more sharing of small controls. A new era has begun. Great post.

    Devise an Conquer!

  3. Thanks for your work on this, Nick. I’m planning to use Cells in a current project. I’m curious about using this with javascript calls. What if I want to pull in this sidebar via JS?
    Are cells routable like that?


  4. Greg

    I’ll definitely use Cells in my new projects. Using helpers or before filters to load data for side bar components is very awkward compared to Cells approach.


  5. Jello

    Do Cells work with Sinatra?


  6. nick

    @Rymaï, @atros, @Jim and @Greg: Thanks dudes, for your nice and motivating comments! :-)

    @Jim: What you want is Apotomo. I will release the 1.0 and announce it in a couple o’ days. Lemme know if you run into any trouble!

    @Jello: Well, I once worked on that: http://github.com/apotonick/cells-sinatra – do you think this is helpful?

  7. Thanks for Cells, it’s what Rails really missed.

  8. Great post, We’ve used apotomo on our last two projects, all the widgets are using apotomo from now on :)
    The reusable thing is really helpful btw.
    Less headaches and cleaner code.
    Thank You (beer is on me)

  9. Thanks nick I’ll checkout Apotomo. One thing that stands out about cells is the structure. Why put view files anywhere but in the app/views directory?
    The way it is, I anticipate headaches with maintenance for any number of cells over a handful.
    Why do cells views live where they do instead of in app/views?


  10. nick

    @Jim: Cells are separated in app/cells. Imagine you had a PostsController and a PostsCell – we couldn’t discern between controller and cell views if they were in the same directory.

    Beside that, we simply don’t like the way Rails organizes the VC assets ;-)

    What problems do you see in the maintenance?

    As a hint, remember you can always do “submodules” like

    app/cells/
      posts/
        form/
        recent/
    

    where you group views logically in cells. Does that help?

    BTW- writing a post how to switch from cells to Apotomo!

  11. The maintenance problems I see is that we have views in 2 locations. Having an app/cells directory makes sense, but I’d rather organize my views together in app/views/cells (and maybe configurable if that namespace exists).

    It’s not a deal-breaker, but it just doesn’t feel right to have views split up.


  12. nick

    @Jim: Just do Cell::Base.prepend_view_path "app/views/cells", how do you like that?


  13. angel

    can you forget the partials and only works with cells or there’s a reason for keep working with partial??…I don’t like partial, I don’t like his syntax, I don’t like his speed and the ideas behind this…….


  14. nick

    @angel: Uhm, the good thing here is, it’s up to you ;-)

    I personally would not use Cells everywhere, sometimes a stupid partial is simply enough.

    As a rule of thumb I’d say use a cell when

    • your partial uses a lot of locals or instance variables
    • you find out there’s too much logic in it
    • you wanna test that partial separately
    • caching the partial with fragment caching gets a problem
    • reusing the partial in another controller is required
    • your helper+partial code is too tightly coupled
  15. I’m fixing my sidebar to use cells as we speak and I’m amazed by how much I prefer this approach.

    It’s my new favourite gem.

    I have a question though, is there a simple rails way to return an array of all the cells in my app? I want to iterate over it


  16. nick

    @stephen: Thanks, glad you like it!

    Concering your registry-question… hmm. Can’t think of any elegant way right now, I’m afraid you have to search the app/cells directories. We had a CellRegistry years ago but then I learned to use Rails’ autoloading :-)

    My recommendation: post this question to the mailing list , there might be a solution around already. Enjoy, and Cheers!

  17. Very helpful introduction.

    All the examples use a cell in the place you would use a partial view, called from a full view.

    Can you also use a cell instead of a top-level view? Can you call render_cell in a controller? And would you want to? I kind of think you might sometimes, although I’m having trouble describing the circumstances.


  18. nick

    @Jonathan: You might call render_cell in a controller, which you could use to replace the top-level view. Some people already do this in order to separate clearly between controller (HTTP, data) and presentation (cell).

  19. [...] still on the fence about how elegant this is, and there are other ways of doing this, but I wanted to write something myself — and so far, I’ve not [...]


  20. Andrew

    A great gem! I have one issue, I’m using the declarative_auth gem and at some point want to call the permitted_to? method. However this method wants to be aware of the controller in order to obtain the defined authentication rules, is there a way to tell declarative_auth which controller is being referenced?


  21. nick

    @Andrew: Thanks! Can you send a link to the permitted_to? method? Maybe you open an issue on github?


  22. Harun

    Great work. I am using this on my current project for sidebars and some widgets on main viewport area of the page. Its really working out well. Rails being monolithic was not giving me this freedom and had to write custom code to achieve reusable components.

Leave a Reply