Words and Code

One writer’s journey from words to code.

Composing Microscopic Rails Views With Cells


I crossed over to the dark side awhile ago, and I can hide it no longer. And when I say “the dark side”, what I mean is JavaScript. Surprise, surprise — I am now haunted by semicolons everywhere I go! I’ve been working a lot with Ember and a little bit with React over the past six months, and have both struggled and enjoyed integrating these frameworks with a Rails API. At first, coming back to Rails after a week or so of Ember was just like coming home: warm, comforting, and familiar. Yet the wild west of braces and semicolons called out to me. The more that I worked outside of Rails, the more comfortable I became with more functional programming concepts (read: concepts that are abstracted away such that you don’t ever have to think about them in the Rails framework). After awhile, I noticed that coming back to Rails was less like coming back to something familiar, but instead something that often seemed limiting and constricting in its conventions.

As much as I love the convention over configuration format that Rails brings to the table, there are times when I wish that there was a better way to do something. A lot of my desires lie at the crux of two forms of programming that I respect, and yet see both benefits and drawbacks in: functional programming and object-oriented programming. While JavaScript is an OOP language, libraries such as Redux or Nuclear JS emphasize more functional programming techniques like unidirectional data flow, for example. Ember is very much an object-oriented framework and, similar to Rails, is massive with a decent amount of abstraction hiding what’s actually going on. Yet Ember is also heading in a more functional direction, embracing the unidirectional dataflow approach used in Redux, which Ember refers to as “data down, actions up”.

Without veering too far off topic here, what I’m trying to get at here is this: we can learn a lot from observing how other frameworks (and even other languages!) handle their code when things get messy. I’ve come to respect Ember a lot over the past few months, and one of the many reasons I do is because of how the framework handles encapsulation. Ember has a pretty fantastic way of packaging up view logic into a single, standalone piece of code known as a component. Ember components are powerful because they can be rendered as many times as you need, and contain the logic for how something should appear. This means that we can iterate through an array of objects and render the same component, an encapsulation of how that object should appear, and only change the code in one place should we need to do so. I love how components work, and I’ve wistfully longed for a way to do something similar in Rails views. It turns out that someone else also wanted the same thing I did, except that he actually built it — thank you, Nick! And now we get to play with his creation, which is aptly called the cells gem.

Components of A Cell


The cells gem is packed with a cornucopia of functionality. Seriously: it does so many things — all the things! But before we go down the path of all the bells and whistles that this gem provides us with, it’s important to understand what it is meant to do at its most simplest level.

So much of the time, our Rails views are the first files to devolve into a complete and utter mess, with if else conditional statements in slim or erb that make it hard to read and really shouldn’t even belong there. But at the same time, we don’t really want to put them in our controller either, and it seems strange to make a model responsible for knowing how to present itself (and effectively blur the lines between the Model and View in a MVC framework). As it stands, partials and helpers are the only good solution to this problem, but they’re not really “good” solutions by any means.

Rails views are meant to be dumb — that is to say, they shouldn’t be responsible for determing the logic for how to render anything. Theoretically, you should just pass them some information, and they should spit it out in the correct format. But with partials, this can easily get out of control; we quickly end up with “decider logic”, or some sort of conditional statement that determines how the view should render itself. If we get a little queasy just thinking about this, that’s a good thing: logic should live in a class, not a view of any kind!

And that’s where the cells gem comes in. At its core, it gives us an easy way to create a kind of Ruby object that has a single responsibility: rendering a template. Yup, you read that correctly: an object that renders a view. Or, as the documentation refers to them, view models:

“Think of cells, or view models, as small Rails controllers, but without any HTTP coupling. Cells embrace all presentation and rendering logic to present a fragment of the UI. The scope of that fragment is up to you: it can embrace an entire page, a single comment container in a thread or just an avatar image link.”

So far, this isn’t anything too complicated, right? But what does this look like in practice? Let’s take a quick peek.

In our bookstore application, we have a navigation panel that the user sees when they log in. We’ll probably have a button or tab that will reveal past addresses that are associated with the current user’s account. The annoying thing about addresses is that we probably want to format them different, depending on whether they have two street addresses (think apartment number, suite, etc.), or if they have an international address, or maybe for some other edge case that we haven’t even run into or considered quite yet. Ultimately, it would be pretty great if we could just take all the logic of how and whether to render an address and pass it off to an “address component” that would be responsible for handling this.

Using the cells API, we can do exactly that. And this is what it would look like in a template, using a helper:

1
<%= cell(:address, @address) %>

The cell method takes two arguments here, the first of which is a symbol and effectively tells the method where to look for the object that will be responsible for rendering our @address instance. And the second argument is the @address instance itself. The helper above is actually calling this method invocation under the hood:

1
AddressCell.(@address).()

Things start to get really fancy when we start using the same cell to render a whole bunch of addresses. In fact, we can actually just pass off a whole collection of addresses to our cell, which then allows us to use our cell helper inside of our UsersController:

1
2
3
4
5
class UsersController < ApplicationController
  def index
      @addresses = cell(:address, collection: current_user.addresses)
  end
end

This might seem kind of strange at first, but once we let go of the comfort that is Rails convention, we might realize something: this is just nothing more than object-oriented programming! We have an object that has a single reponsibility: rendering a view. In a way, this is pretty darn Ruby-eqsue, isn’t it? If we can encapsulate a unit of work into a service, why not capture a piece of functionality into an object, especially if that functionality is going to be repeated in our application multiple times!

Okay, so how exactly does this model somehow render a view? We need to know how this magic happens! It’s time to put these cells under the microscope and compose a cell ourselves.

Cells Under A Microscope


Getting set up with cells is pretty straightforward. First and foremost, we’ll need to add gem 'cells', "~> 4.0.0" to our Gemfile.

One important thing to remember is that this gem supports various templating engines, but it’s actually up to us to specify which one we plan on using. In our case, we’ll stick to simple erb, which means we’ll need to add gem 'cells-erb' before we can run bundle. Other templating options include Haml (gem 'cells-haml') and Slim (gem 'cells-slim'). As long as we’re using Rails, this is all we need to do. However, if we want to add cells to a Ruby project that is not using Rails, we’ll need to specifically include the templating format in our Cell class (for example, include Cell::Slim).

But we’ll stick with our Rails cell for now. We can very quickly create a cell by using a handy generator and run a single command to create our address cell:

1
rails generate cell address

This will create a few things for us. First, it will generate a cell model at the path app/cells/address_cell.rb. It will also create a view template at app/cells/address/show.erb. The structure of these files is crucial, since the call method will lookup the correct template from the correct cell model, and expects everything to be structured according the name of the cell we’re specifying.

Now we can get down to some cellular business. Let’s take a closer look at our cell model, at address_cell.rb. Here’s what it starts off as:

1
2
3
4
5
class AddressCell < Cell::ViewModel
  def show
      render
  end
end

The show method (our only method at the moment), maps to the address/show.erb file that is our actual view. This method calls render, which will invoke rendering for the show.erb view.

This is a cell at its most basic level, and theoretically, it’s all you really need for it to work. However, we can start to get fancy here and add properties, and private methods that we might want to use in our view:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AddressCell < Cell::ViewModel
  property :user
  property :address_1
  property :address_2
  property :locality
  property :region
  property :postal_code
  property :country

  def show
      render
  end

  private
  def email_link
      link_to user.email, user
  end
end

We have access to the cell’s model in this context via its attribute reader; we can read the value of the model’s attributes by using property. This simply delegates to the attribute on the model itself, which means that property :user merely delegates to model.user. It’s worth mentioning that our model is the very same instance that we passed to our cell helper — in this case, our @address instance.

Now that we have our cell model ready to go, we can write our view. We can put any rendering logic into this single view, and render it accordingly. More importantly, if we want to change how all our addresses are rendered, we can just change the logic in one place:

1
2
3
4
5
6
7
8
9
10
11
<div class="detailed-address-cell">
  <p><%= address_1 %></p>
  <% if address_2.present? %>
      <p><%= address_2 %></p>
  <% end %>
  <p><%= locality %>, <%= region %> <%= postal_code %></p>
  <p><%= country %></p>
  <% if email_link.present? %>
      <p><%= email_link %></p>
  <% end %>
</div>

And what if we decided to have another cell template that was less detailed, perhaps one that would map to an index action in our AddressCell model? We would simply create a app/cells/address/index.erb template:

1
2
3
<div class="address-cell">
  <p><%= locality %>, <%= region %> <%= postal_code %></p>
</div>

And make sure that it mapped to an index action in our AddressCell model:

1
2
3
4
5
class AddressCell < Cell::ViewModel
  def index
      render
  end
end

Which would allow us to call it using the call style, where we could specify the action that we wanted to call:

1
cell(:address, @address).(:index)

In this case, because we’re not using the default AddressCell#show action, we need to explicitly specify which action in our model we want to call. But we can still reuse our cell over and over again for different situations, which is super cool!

Cellular Tips And Tricks

It’s hard to cover everything that the cells gem does without rewriting the documentation, but there are a few interesting tricks up this gem’s sleeves that seem like they’d be fun to play with.

Collections

So far, we’ve been using the cell helper to render a single object instance at a time. But earlier, we had wanted to pass a collection of Address instances to a controller action. There’s an easy way to do this by passing the collection option to the cell method. Rather than the second argument being an instance of @address, we can pass a collection of Address instances:

1
cell(:address, collection: current_user.addresses)

This will by default call the show method for each of the addresses associated with the current_user. If we wanted to use our index action to instead render our address/index.erb per address instance, we can specify this by using the method option:

1
cell(:address, collection: current_user.addresses, method: :index)

HTML Escaping

One of the default behaviors of cells is to not escape any HTML, at any point. But, if we had a property that was actually going to return HTML that needed to be converted to escaped strings, we could simply include the Escaped module:

1
2
3
4
5
6
7
class AddressCell < Cell::ViewModel
  include Escaped

  def show
      render
  end
end

HTML escaping is fairly well-explained in the gem’s documentation.

Rendering Locals

The super awesome thing about the render method is that it’s not complicated, at all. There are multiple ways of writing the same thing. If we’re rendering the show method, we can simply call render:

1
2
3
def show
  render
end

And if we’re rendering something other than show, like index, we can either tell the method the name of the template to render, or specify which view to render:

1
2
3
4
render :index
render view: :index

# both of these calls will do the same thing

The only other piece of functionality we would need to pass to the render method is locals, which can be passed in as a hash to the method directly:

1
render locals: { style: "background-color: yellow;" }

So easy to write and remember!

It’s also worth noting that I only just learned about this gem, which means that I haven’t played around with it a whole lot. There’s probably many different ways to go about implementing it, some of which will be specific to a given application. But, it’s worth exploring to see if it’s a useful tool for replacing some ugly, repetitive view logic you might have polluting your codebase. And whether you decide to add cells to your application or not, it’s still pretty cool to see how people in our community are taking concepts and techniques from other frameworks and languages and applying them in interesting ways to Rails. To me, it’s a good reminder that we should never stop examining other ways of doing things — even if that means subjecting ourselves to more semicolons that we might like!


tl;dr?

  • A cell is a Ruby object that can render a template, which becomes a powerful form of encapsulation. By default, its call to render will proxy to the object’s show method, but can be overriden if necessary. Read more on the gem’s Github page.
  • The cells gem is one the most well-documented ones I’ve seen. The documentation covers everything, from the basics of its API, as well as how to test, cache, and troubleshoot the gem.
  • Nick Sutterer, the author of cells, wrote a great blog post highlighting the best practices of the gem’s implementation in Rails. Check it out over here!