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!" } end
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 } end
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! end
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! end
The view is identical to the layout views you were using before, with ActionView.
!!! %html %head %title= "Gemgem" = stylesheet_link_tag 'application', media: 'all' = javascript_include_tag 'application' %body = 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 # .. end
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] end
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 call
ed 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) end
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!
Great work!
LikeLike
Hmm I still need to convert my old cells
16 old cells left
LikeLike
Looks like great evolution of cells!! Congrats!!
LikeLike