Cells 4.1: Block Support, Better Collections, External Layouts!

After many weeks of work, I am happy to announce the 4.1 release of Cells, the popular view model gem for Ruby. In this release we are paving the way for the upcoming, super fast Cells 5.0 by tidying up the core, and adding some amazing new features. This all happened without any public API change!

Rails Support

Since Cells 4.0 the gem is fully decoupled from Rails and provides its own rendering stack. We decided to take this one step further and move all Rails specific code into cells-rails. If you are using Rails, make sure to include that library in your Gemfile.

gem "cells-rails"

A nice side-effect: We now provide a fix for bugs with Rails’ asset helpers, out-of-the-box! Using different asset configurations in production worked fine in controllers, but caused problems in Cells. By delegating all those helpers to the controller, this is now fixed in a very clean and also simple way.

Yielding Blocks

Many Cells users (and the usage count is growing every day) have asked for the ability to yield blocks or content in views. The render method now accepts a block.

def show
  render { "Yay!" }

In the corresponding view, you can then yield this block.

%h3 Yield!
  = yield #=> "Yay!"

This can be used to wrap one view into another without having to pass around locals.

def show
  render { render :nested }

As a logical conclusion, you can also pass a block from outside in – it will be passed as the show method’s block. Check this.

cell(:comment, comment) { "Yay!" }

To process this, you now need to pass through this block.

def show(&block)
  render &block # pass through!

This allows injecting content into a cell without any state.

Default Show

Since 99.9% of all cells have an identical show method, this is now provided per default via ViewModel#show. The implementation looks like the code snippet above.

No need to write this yourself anymore.

External Layouts

Cells are not only great for fragments of your page, but can also completely replace the entire ActionView stack, by using a layout cell.

A layout cell?

Yes, it’s super simple. A layout cell is really just an ordinary view model with a layout view.

  class LayoutCell < Cell::ViewModel
    # show is inherited!

The view is identical to the layout views you were using before, with ActionView.

    %title= "Gemgem"
    = stylesheet_link_tag 'application', media: 'all'
    = javascript_include_tag 'application'
    = yield

As you can see, nothing special. To use this layout cell, you can now leverage the new “external layout” feature.

First, you have to prepare your cell classes for that.

class CommentCell < Cell::ViewModel
  include Layout::External
  # ..

You can then inject a layout cell from the outside when calling the actual cell.

cell(:comment, comment, layout: LayoutCell)

Internally, this will first render the content and then yield it to the instantiated LayoutCell, which will wrap it in a layout. This is faster as ActionView and avoids global leaking state.

Context Object

When dealing with layouts (outer) and nested cells (within your actual cell), a problem was that is was hard to share common data. For example, let’s say you want the current user in all those cells, you can now use the context object.

cell(:comment, comment, context: {user: current_user})

The context object is usually a hash. It’s automatically passed on to all cells involved in this rendering invocation, as the optional external layout, or internal cell calls.

Within those cells, you can simply access the context method.

def show
  puts context[:user]

The context object has been around since version 3.3 but was limited to Rails internals – it’s now a generic state object and will help with advanced cell nesting in 4.2.

Better Collections

We’ve had cell collection rendering for quite a bit. However, the API was a bit clumsy, especially when you had to fine-tune how the collection is rendered.

Using the :collection option will now return a Collection instance. Per default, in views, you don’t have to change anything.

Nevertheless, in tests or controllers, this very instance needs to be called just like any other cell.

cell(:comment, collection: Comment.all).() # renders collection

To invoke a different state, you now use the normal cell API.

cell(:comment, collection: Comment.all).(:display) # calls #display.

With join, you can customize the rendering of the collection.

cell(:comment, collection: Comment.all).join do |cell, i|
  i.odd? cell.(:odd, i) : cell(:even, i) 

Each block iteration will be automatically concatenated for you. Very convenient, isn’t it? Oh, and it’s also faster than the old implementation.

The whole documentation is to be found here.

Enjoy your rock-solid views!


3 thoughts on “Cells 4.1: Block Support, Better Collections, External Layouts!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s