Skip to content

nyc-ruby-meetup/cells

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cells

View Components for Rails.

Overview

Say you’re writing a Rails online shop - the shopping cart is reappearing again and again in every view. You’re thinking about a clean solution for that part. A mixture of controller code, before-filters, partials and helpers?

No. That sucks. Take Cells.

Cells are View Components for Rails. They look and feel like controllers. They don’t have no DoubleRenderError. They can be rendered everywhere in your controllers or views. They are cacheable, testable, fast and wonderful. They bring back OOP to your view and improve your software design.

And the best: You can have as many cells in your page as you need!

Installation

It’s a gem!

Rails 3:

gem install cells

Rails 2.3:

gem install cells -v 3.3.5

Generate

Creating a cell is nothing more than

$ rails generate cell ShoppingCart display
  create  app/cells/
  create  app/cells/shopping_cart
  create  app/cells/shopping_cart_cell.rb
  create  app/cells/shopping_cart/display.html.erb
  create  test/cells/shopping_cart_test.rb

That looks very familiar.

Render the cell

Now, render your cart. Why not put it in layouts/application.html.erb for now?

<div id="header">
  <%= render_cell :shopping_cart, :display, :user => @current_user %>

Feels like rendering a controller action. As additional encapsulation we pass the current user from outside. Call it knowledge hiding.

Code

Time to improve our cell code. Let’s start with app/cells/shopping_cart_cell.rb:

class ShoppingCartCell < Cell::Rails 
  def display 
    user    = options[:user]        # #options exposes options passed to #render_cell.
    @items  = user.items_in_cart

    render  # renders display.html.erb
  end
end

Is that a controller? Hell, yeah. We even got a render method as we know it from the good ol’ ActionController.

Views

Since a plain call to render will start rendering app/cells/shopping_cart/display.html.erb we should put some meaningful markup there.

<div id="cart">
  You have <%= @items.size %> items in your shopping cart. 
</div>

Haml? Builder?

Yes, Cells support all template types that are supported by Rails itself. Remember- it’s a controller!

Helpers

Yes, Cells have helpers just like controllers. If you need some specific helper, do

class ShoppingCartCell < Cell::Rails 
  helper MyExtraHelper

and it will be around in your cart views.

Partials?

Yeah, we do support rendering partials in views. Nevertheless, we discourage partials at all.

The distinction between partials and views is making things more complex, so why should we have two kinds of view types? Use ordinary views instead, they’re fine.

%p
  = render :view => 'items'

View Inheritance

This is where OOP comes back to your view.

  • Inherit code into your cells by deriving more abstract cells.

  • Inherit views from ancesting cells.

Builders

Let render_cell take care of creating the right cell. Just configure your super-cell properly.

class LoginCell < Cell::Rails
  build do
    UnauthorizedUserCell unless logged_in?
  end

A call to

render_cell(:login, :box)

will render the configured UnauthorizedUserCell instead of the original LoginCell if the login test fails.

Caching

Cells do strict view caching. No cluttered fragment caching. Add

class ShoppingCartCell < Cell::Rails 
  cache :display, :expires_in => 10.minutes

and your cart will be re-rendered after 10 minutes.

There are multiple advanced options for expiring your view caches, including an expiration lambda.

class ShoppingCartCell < Cell::Rails 
  cache :display do |cell|
    Item.still_valid?
  end

Testing

Another big advantage compared to monolithic controller/helper/partial piles is the ability to test your cells isolated.

Test::Unit

So what if you wanna test the cart cell? Use the generated test/cells/shopping_cart_cell_test.rb test.

class ShoppingCartCellTest < Cell::TestCase
  test "display" do
    invoke :display, :user => @user_fixture
    assert_select "#cart", "You have 3 items in your shopping cart."
  end

Run your tests with

$ rake test:cells

That’s easy, clean and strongly improves your component-driven software quality. How’d you do that with partials?

RSpec

If you prefer RSpec examples, use the rspec-cells gem for specing.

it "should render the posts count" do
  render_cell(:posts, :count).should have_selector("p", :content => "4 posts!")
end

To run your specs we got a rake task, too!

$ rake spec:cells

Rails 2.3 note

In order to copy the cells rake tasks to your app, run

$ script/generate cells_install

More features

Cells can do more.

No Limits

Have as many cells in your page as you need - no limitation to your render_cell calls.

View Inheritance

Inherit view files dynamically from parent cells.

Cell Nesting

Have complex cell hierarchies as you can call render_cell within cells, too.

Go for it, you’ll love it!

LICENSE

Copyright © 2007-2010, Nick Sutterer

Copyright © 2007-2008, Solide ICT by Peter Bex and Bob Leers

Released under the MIT License.