• Stars
    star
    3,061
  • Rank 14,075 (Top 0.3 %)
  • Language
    Ruby
  • Created over 15 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

View components for Ruby and Rails.

Cells

View Components for Ruby and Rails.

Zulip Chat TRB Newsletter Build Status Gem Version

Overview

Cells allow you to encapsulate parts of your UI into components into view models. View models, or cells, are simple ruby classes that can render templates.

Nevertheless, a cell gives you more than just a template renderer. They allow proper OOP, polymorphic builders, nesting, view inheritance, using Rails helpers, asset packaging to bundle JS, CSS or images, simple distribution via gems or Rails engines, encapsulated testing, caching, and integrate with Trailblazer.

Full Documentation

Cells is part of the Trailblazer framework. Full documentation is available on the project site.

Cells is completely decoupled from Rails. However, Rails-specific functionality is to be found here.

Rendering Cells

You can render cells anywhere and as many as you want, in views, controllers, composites, mailers, etc.

Rendering a cell in Rails ironically happens via a helper.

<%= cell(:comment, @comment) %>

This boils down to the following invocation, that can be used to render cells in any other Ruby environment.

CommentCell.(@comment).()

You can also pass the cell class in explicitly:

<%= cell(CommentCell, @comment) %>

In Rails you have the same helper API for views and controllers.

class DashboardController < ApplicationController
  def dashboard
    @comments = cell(:comment, collection: Comment.recent)
    @traffic  = cell(:report, TrafficReport.find(1)).()
  end

Usually, you'd pass in one or more objects you want the cell to present. That can be an ActiveRecord model, a ROM instance or any kind of PORO you fancy.

Cell Class

A cell is a light-weight class with one or multiple methods that render views.

class CommentCell < Cell::ViewModel
  property :body
  property :author

  def show
    render
  end

private
  def author_link
    link_to "#{author.email}", author
  end
end

Here, show is the only public method. By calling render it will invoke rendering for the show view.

Logicless Views

Views come packaged with the cell and can be ERB, Haml, or Slim.

<h3>New Comment</h3>
  <%= body %>

By <%= author_link %>

The concept of "helpers" that get strangely copied from modules to the view does not exist in Cells anymore.

Methods called in the view are directly called on the cell instance. You're free to use loops and deciders in views, even instance variables are allowed, but Cells tries to push you gently towards method invocations to access data in the view.

File Structure

In Rails, cells are placed in app/cells or app/concepts/. Every cell has their own directory where it keeps views, assets and code.

app
โ”œโ”€โ”€ cells
โ”‚   โ”œโ”€โ”€ comment_cell.rb
โ”‚   โ”œโ”€โ”€ comment
โ”‚   โ”‚   โ”œโ”€โ”€ show.haml
โ”‚   โ”‚   โ”œโ”€โ”€ list.haml

The discussed show view would reside in app/cells/comment/show.haml. However, you can set any set of view paths you want.

Invocation Styles

In order to make a cell render, you have to call the rendering methods. While you could call the method directly, the preferred way is the call style.

cell(:comment, @song).()       # calls CommentCell#show.
cell(:comment, @song).(:index) # calls CommentCell#index.

The call style respects caching.

Keep in mind that cell(..) really gives you the cell object. In case you want to reuse the cell, need setup logic, etc. that's completely up to you.

Parameters

You can pass in as many parameters as you need. Per convention, this is a hash.

cell(:comment, @song, volume: 99, genre: "Jazz Fusion")

Options can be accessed via the @options instance variable.

Naturally, you may also pass arbitrary options into the call itself. Those will be simple method arguments.

cell(:comment, @song).(:show, volume: 99)

Then, the show method signature changes to def show(options).

Testing

A huge benefit from "all this encapsulation" is that you can easily write tests for your components. The API does not change and everything is exactly as it would be in production.

html = CommentCell.(@comment).()
Capybara.string(html).must_have_css "h3"

It is completely up to you how you test, whether it's RSpec, MiniTest or whatever. All the cell does is return HTML.

In Rails, there's support for TestUnit, MiniTest and RSpec available, along with Capybara integration.

Properties

The cell's model is available via the model reader. You can have automatic readers to the model's fields by using ::property.

class CommentCell < Cell::ViewModel
  property :author # delegates to model.author

  def author_link
    link_to author.name, author
  end
end

HTML Escaping

Cells per default does no HTML escaping, anywhere. Include Escaped to make property readers return escaped strings.

class CommentCell < Cell::ViewModel
  include Escaped

  property :title
end

song.title                 #=> "<script>Dangerous</script>"
Comment::Cell.(song).title #=> &lt;script&gt;Dangerous&lt;/script&gt;

Properties and escaping are documented here.

Installation

Cells runs with any framework.

gem "cells"

For Rails, please use the cells-rails gem. It supports Rails >= 4.0.

gem "cells-rails"

Lower versions of Rails will still run with Cells, but you will get in trouble with the helpers. (Note: we use Cells in production with Rails 3.2 and Haml and it works great.)

Various template engines are supported but need to be added to your Gemfile.

gem "cells-erb"

In Rails, this is all you need to do. In other environments, you need to include the respective module into your cells.

class CommentCell < Cell::ViewModel
  include ::Cell::Erb # or Cell::Hamlit, or Cell::Haml, or Cell::Slim
end

Namespaces

Cells can be namespaced as well.

module Admin
  class CommentCell < Cell::ViewModel

Invocation in Rails would happen as follows.

cell("admin/comment", @comment).()

Views will be searched in app/cells/admin/comment per default.

Rails Helper API

Even in a non-Rails environment, Cells provides the Rails view API and allows using all Rails helpers.

You have to include all helper modules into your cell class. You can then use link_to, simple_form_for or whatever you feel like.

class CommentCell < Cell::ViewModel
  include ActionView::Helpers::UrlHelper
  include ActionView::Helpers::CaptureHelper

  def author_link
    content_tag :div, link_to(author.name, author)
  end

As always, you can use helpers in cells and in views.

You might run into problems with wrong escaping or missing URL helpers. This is not Cells' fault but Rails suboptimal way of implementing and interfacing their helpers. Please open the actionview gem helper code and try figuring out the problem yourself before bombarding us with issues because helper xyz doesn't work.

View Paths

In Rails, the view path is automatically set to app/cells/ or app/concepts/. You can append or set view paths by using ::view_paths. Of course, this works in any Ruby environment.

class CommentCell < Cell::ViewModel
  self.view_paths = "lib/views"
end

Asset Packaging

Cells can easily ship with their own JavaScript, CSS and more and be part of Rails' asset pipeline. Bundling assets into a cell allows you to implement super encapsulated widgets that are stand-alone. Asset pipeline is documented here.

Render API

Unlike Rails, the #render method only provides a handful of options you gotta learn.

def show
  render
end

Without options, this will render the state name, e.g. show.erb.

You can provide a view name manually. The following calls are identical.

render :index
render view: :index

If you need locals, pass them to #render.

render locals: {style: "border: solid;"}

Layouts

Every view can be wrapped by a layout. Either pass it when rendering.

render layout: :default

Or configure it on the class-level.

class CommentCell < Cell::ViewModel
  layout :default

The layout is treated as a view and will be searched in the same directories.

Nested Cells

Cells love to render. You can render as many views as you need in a cell state or view.

<%= render :index %>

The #render method really just returns the rendered template string, allowing you all kind of modification.

def show
  render + render(:additional)
end

You can even render other cells within a cell using the exact same API.

def about
  cell(:profile, model.author).()
end

This works both in cell views and on the instance, in states.

View Inheritance

You can not only inherit code across cell classes, but also views. This is extremely helpful if you want to override parts of your UI, only. It's documented here.

Collections

In order to render collections, Cells comes with a shortcut.

comments = Comment.all #=> three comments.
cell(:comment, collection: comments).()

This will invoke cell(:comment, comment).() three times and concatenate the rendered output automatically.

Learn more about collections here.

Builder

Builders allow instantiating different cell classes for different models and options. They introduce polymorphism into cells.

class CommentCell < Cell::ViewModel
  include ::Cell::Builder

  builds do |model, options|
    case model
    when Post; PostCell
    when Comment; CommentCell
    end
  end

The #cell helper takes care of instantiating the right cell class for you.

cell(:comment, Post.find(1)) #=> creates a PostCell.

Learn more about builders here.

Caching

For every cell class you can define caching per state. Without any configuration the cell will run and render the state once. In following invocations, the cached fragment is returned.

class CommentCell < Cell::ViewModel
  cache :show
  # ..
end

The ::cache method will forward options to the caching engine.

cache :show, expires_in: 10.minutes

You can also compute your own cache key, use dynamic keys, cache tags, and conditionals using :if. Caching is documented here and in chapter 8 of the Trailblazer book.

The Book

Cells is part of the Trailblazer project. Please buy my book to support the development and to learn all the cool stuff about Cells. The book discusses many use cases of Cells.

![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
  • Basic view models, replacing helpers, and how to structure your view into cell components (chapter 2 and 4).
  • Advanced Cells API (chapter 4 and 6).
  • Testing Cells (chapter 4 and 6).
  • Cells Pagination with AJAX (chapter 6).
  • View Caching and Expiring (chapter 8).

The book picks up where the README leaves off. Go grab a copy and support us - it talks about object- and view design and covers all aspects of the API.

This is not Cells 3.x!

Temporary note: This is the README and API for Cells 4. Many things have improved. If you want to upgrade, follow this guide. When in trouble, join the Zulip channel.

LICENSE

Copyright (c) 2007-2020, Nick Sutterer

Copyright (c) 2007-2008, Solide ICT by Peter Bex and Bob Leers

Released under the MIT License.

More Repositories

1

trailblazer

The advanced business logic framework for Ruby.
Ruby
3,380
star
2

reform

Form objects decoupled from models.
Ruby
2,486
star
3

roar

Parse and render REST API documents using representers.
Ruby
1,844
star
4

roar-rails

Use Roar's representers in Rails.
Ruby
233
star
5

trailblazer-rails

Trailblazer in Rails.
Ruby
174
star
6

reform-rails

Automatically load and include all common Rails form features.
Ruby
98
star
7

trailblazer-operation

Trailblazer's Operation implementation.
Ruby
86
star
8

formular

Form builder for Ruby. Fast, Furious, and Framework-Agnostic.
Ruby
81
star
9

trailblazer-activity

Model business workflows and run them.
Ruby
70
star
10

cells-rails

Rails support for Cells.
Ruby
53
star
11

roar-jsonapi

JSON API support for Roar.
Ruby
42
star
12

trailblazer-finder

Find, filter and aggregate via ActiveRecord or Sequel.
Ruby
37
star
13

trailblazer-cells

Trailblazer's file structure for Cells.
Ruby
26
star
14

rspec-trailblazer

RSpec Matchers for strong and non-verbose operation tests.
Ruby
24
star
15

trailblazer.github.io

The TRAILBLAZER.TO website. Please PR against the f6 branch. ๐Ÿ’‹
HTML
23
star
16

trailblazer-endpoint

Generic HTTP endpoints to deal with different results from your operations.
Ruby
21
star
17

guides

Finally! Learn everything about operations, Trailblazer, Reform and Cells with our guides.
Ruby
21
star
18

cells-slim

Ruby
17
star
19

trailblazer-loader

Require concept files, models and more without any autoloading magic.
Ruby
17
star
20

tamarama

The stack we fancy: Sinatra/Grape, Trailblazer, Cells and Sequel.
Ruby
13
star
21

trailblazer-developer

Debugging, tracing and workflow management tools for Trailblazer developers.
Ruby
13
star
22

trailblazer-activity-dsl-linear

Simple DSL to compile Trailblazer activities.
Ruby
11
star
23

trailblazer-compat

This gem provides a seamless-erโ„ข upgrade from TRB 1.1 to 2.x.
Ruby
10
star
24

trailblazer-generator

CLI generators for operations, contracts, cells, and much more.
Ruby
10
star
25

trailblazer-test

Super strong, non-verbose tests for your operations.
Ruby
10
star
26

trailblazer-macro

Ruby
9
star
27

cells-erb

ERB support for Cells.
Ruby
9
star
28

cells-hamlit

Hamlit integration for Cells
Ruby
7
star
29

trailblazer-macro-contract

Reform and Dry::Validation integration for Trailblazer's operation.
Ruby
6
star
30

cells-haml

Haml integration for Cells
Ruby
5
star
31

blog-example

A publishing workflow implemented with Trailblazer.
Ruby
5
star
32

api-docs

Website for Trailblazer >= 2.1.
JavaScript
5
star
33

tutorial

The code for our tutorials.
Ruby
4
star
34

trailblazer-story

Painless test factories. / ...like a mix of cucumber and traits - delicious!
Ruby
3
star
35

meta

Ruby
3
star
36

website

The trailblazer.to website
HTML
3
star
37

trailblazer-context

Argument-specific data structures for Trailblazer.
Ruby
3
star
38

trailblazer-transform

Transform, parse, coerce, validate, it's all just a chain of transformations. Replacement/extension for Reform.
Ruby
3
star
39

trailblazer-disposable

Domain object layer.
Ruby
2
star
40

tyrant

Authentication library.
Ruby
2
star
41

trailblazer-option

Generate callables in Trailblazer!
Ruby
2
star
42

cells-mailer

Provides mail functionality for the Cells gem.
Ruby
2
star
43

cfp-app

Ruby
1
star
44

trailblazer-declarative

Generic DSL providing schemas and inheritance.
Ruby
1
star
45

trailblazer-errors

Generic errors object used in operations and contracts.
Ruby
1
star
46

trailblazer-pro-rails

Rails support for TRB PRO.
Ruby
1
star
47

blog9

Blog application with approve/reject workflow.
Ruby
1
star
48

trailblazer-future

Bridge gem to upgrade from 2.0 to 2.1
Ruby
1
star
49

trailblazer-args

Scoped, mutable and immutable keyword arguments for callables.
Ruby
1
star