cells 4.0.0.rc1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +8 -5
- data/README.md +130 -408
- data/lib/cell.rb +1 -0
- data/lib/cell/concept.rb +20 -13
- data/lib/cell/escaped.rb +27 -0
- data/lib/cell/rails.rb +4 -0
- data/lib/cell/railtie.rb +3 -3
- data/lib/cell/version.rb +1 -1
- data/test/concept_test.rb +10 -3
- data/test/property_test.rb +41 -0
- data/test/rails4.2/app/cells/form_for_cell.rb +0 -4
- data/test/rails4.2/app/cells/formtastic_cell.rb +0 -4
- data/test/rails4.2/app/cells/simple_form_cell.rb +0 -4
- data/test/rails4.2/app/cells/song/with_escaped.erb +1 -0
- data/test/rails4.2/app/cells/song_cell.rb +6 -0
- data/test/rails4.2/app/controllers/songs_controller.rb +8 -0
- data/test/rails4.2/app/models/song.rb +1 -1
- data/test/rails4.2/app/views/songs/with_escaped.html.erb +1 -0
- data/test/rails4.2/config/routes.rb +3 -0
- data/test/rails4.2/test/integration/controller_test.rb +5 -1
- data/test/rails4.2/test/integration/url_helper_test.rb +11 -0
- metadata +8 -10
- data/lib/cell/engines.rb +0 -61
- data/test/rails4.2/public/404.html +0 -67
- data/test/rails4.2/public/422.html +0 -67
- data/test/rails4.2/public/500.html +0 -66
- data/test/rails4.2/public/favicon.ico +0 -0
- data/test/rails4.2/public/robots.txt +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22c7de3a30ac46478e9d4f4ef37c4b8fe1b49b17
|
4
|
+
data.tar.gz: 00b8a25633e2495ff4368ffe53e0c8f404ba7d8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 991d17fcb0eae9e9261a6f978651601eb2e99053ac95dc5a3621054ccfaf6ebb0003fb628b8d6a974bc6f8af668ba82a9d7de2f66c80f33454f070301bf87efc
|
7
|
+
data.tar.gz: 9cc7f11fe2703b49721d3a222637d48f13ace7323d7399e876e04e3c43b688fff1419de2c586b01cc342addcedb16f638325590efd76378578114f3e56704ae9
|
data/CHANGES.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
## 4.0.0
|
2
2
|
|
3
|
-
* **Rails Support:** Rails
|
3
|
+
* **Rails Support:** Rails 4.0+ is fully supported, in older versions some form helpers do not work. Let us know how you fixed this.
|
4
4
|
* **State args:** View models don't use state args. Options are passed into the constructor and saved there. That means that caching callbacks no longer receive arguments as everything is available via the instance itself.
|
5
5
|
* `ViewModel.new(song: song)` won't automatically create a reader `#song`. You have to configure the cell to use a Struct twin {TODO: document}
|
6
|
-
* **HTML Escaping:**
|
7
|
-
* **Template Engines:** There's now _one_ template engine (e.g. ERB or HAML) per cell class. It can be set
|
6
|
+
* **HTML Escaping:** Escaping only happens for defined `property`s when `Escaped` is included.
|
7
|
+
* **Template Engines:** There's now _one_ template engine (e.g. ERB or HAML) per cell class. It can be set by including the respective module (e.g. `Cell::Erb`) into the cell class. This happens automatically in Rails.
|
8
8
|
* **File Naming**. The default filename just uses the engine suffix, e.g. `show.haml`. If you have two different engine formats (e.g. `show.haml` and `show.erb`), use the `format:` option: `render format: :erb`.
|
9
9
|
If you need to render a specific mime type, provide the filename: `render view: "show.html"`.
|
10
10
|
* Builder blocks are no longer executed in controller context but in the context they were defined. This is to remove any dependencies to the controller. If you need e.g. `params`, pass them into the `#cell(..)` call.
|
@@ -13,10 +13,13 @@
|
|
13
13
|
### Removed
|
14
14
|
|
15
15
|
* `Cell::Rails` and `Cell::Base` got removed. Every cell is `ViewModel` or `Concept` now.
|
16
|
+
* All methods from `AbstractController` are gone. This might give you trouble in case you were using `helper_method`. You don't need this anymore - every method included in the cell class is a "helper" in the view (it's one and the same method call).
|
16
17
|
|
17
|
-
### Internals
|
18
18
|
|
19
|
-
|
19
|
+
## 4.0.0.rc2
|
20
|
+
|
21
|
+
* Include `#protect_from_forgery?` into Rails cells. It returns false currently.
|
22
|
+
* Fix `Concept#cell` which now instantiates a cell, not a concept cell.
|
20
23
|
|
21
24
|
## 4.0.0.rc1
|
22
25
|
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
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.
|
8
8
|
|
9
|
-
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](http://trailblazerb.org/gems/cells/rails.html#asset-pipeline) to bundle JS, CSS or images, simple distribution via gems or Rails engines, encapsulated testing, [caching](#caching), and [integrate with Trailblazer](#concept-cells).
|
9
|
+
Nevertheless, a cell gives you more than just a template renderer. They allow proper OOP, polymorphic builders, [nesting](#nested-cells), view inheritance, using Rails helpers, [asset packaging](http://trailblazerb.org/gems/cells/rails.html#asset-pipeline) to bundle JS, CSS or images, simple distribution via gems or Rails engines, encapsulated testing, [caching](#caching), and [integrate with Trailblazer](#concept-cells).
|
10
10
|
|
11
11
|
## This is not Cells 3.x!
|
12
12
|
|
@@ -34,8 +34,8 @@ In Rails you have the same helper API for views and controllers.
|
|
34
34
|
```ruby
|
35
35
|
class DasboardController < ApplicationController
|
36
36
|
def dashboard
|
37
|
-
@comments = cell(:comment, Comment.recent)
|
38
|
-
@traffic = cell(:report, TrafficReport.find(1))
|
37
|
+
@comments = cell(:comment, collection: Comment.recent)
|
38
|
+
@traffic = cell(:report, TrafficReport.find(1)).()
|
39
39
|
end
|
40
40
|
```
|
41
41
|
|
@@ -140,6 +140,37 @@ It is completely up to you how you test, whether it's RSpec, MiniTest or whateve
|
|
140
140
|
|
141
141
|
[In Rails, there's support](http://trailblazerb.org/gems/cells/testing.html) for TestUnit, MiniTest and RSpec available, along with Capybara integration.
|
142
142
|
|
143
|
+
## Properties
|
144
|
+
|
145
|
+
The cell's model is available via the `model` reader. You can have automatic readers to the model's fields by uing `::property`.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class CommentCell < Cell::ViewModel
|
149
|
+
property :author # delegates to model.author
|
150
|
+
|
151
|
+
def author_link
|
152
|
+
link_to author.name, author
|
153
|
+
end
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
## HTML Escaping
|
158
|
+
|
159
|
+
Cells per default does no HTML escaping, anywhere. Include `Escaped` to make property readers return escaped strings.
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
class CommentCell < Cell::ViewModel
|
163
|
+
include Escaped
|
164
|
+
|
165
|
+
property :title
|
166
|
+
end
|
167
|
+
|
168
|
+
song.title #=> "<script>Dangerous</script>"
|
169
|
+
Comment::Cell.(song).title #=> <script>Dangerous</script>
|
170
|
+
```
|
171
|
+
|
172
|
+
Properties and escaping are [documented here](http://trailblazerb.org/gems/cells/api.html#html-escaping).
|
173
|
+
|
143
174
|
## Installation
|
144
175
|
|
145
176
|
Cells run with all Rails >= 4.0. Lower versions of Rails will still run with Cells, but you will get in trouble with the helpers.
|
@@ -153,7 +184,7 @@ gem 'cells', "~> 4.0.0"
|
|
153
184
|
Various template engines are supported but need to be added to your Gemfile.
|
154
185
|
|
155
186
|
* [cells-erb](https://github.com/trailblazer/cells-erb)
|
156
|
-
* [cells-haml](https://github.com/trailblazer/cells-haml)
|
187
|
+
* [cells-haml](https://github.com/trailblazer/cells-haml) Make sure to bundle Haml 4.1: `gem "haml", github: "haml/haml", ref: "7c7c169"`.
|
157
188
|
* [cells-slim](https://github.com/trailblazer/cells-slim)
|
158
189
|
|
159
190
|
```ruby
|
@@ -168,6 +199,21 @@ class CommentCell < Cell::ViewModel
|
|
168
199
|
end
|
169
200
|
```
|
170
201
|
|
202
|
+
## Generators
|
203
|
+
|
204
|
+
In Rails, you can generate cells and concept cells.
|
205
|
+
|
206
|
+
```shell
|
207
|
+
rails generate cell comment
|
208
|
+
```
|
209
|
+
|
210
|
+
Or.
|
211
|
+
|
212
|
+
```shell
|
213
|
+
rails generate concept comment
|
214
|
+
```
|
215
|
+
|
216
|
+
|
171
217
|
## Concept Cells
|
172
218
|
|
173
219
|
To have real self-contained cells you should use the new _concept cell_ which follows the [Trailblazer](http://trailblazerb.org) naming style. Concept cells need to be derived from `Cell::Concept`, sit in a namespace and are usually named `Cell`.
|
@@ -254,268 +300,125 @@ Cells can easily ship with their own JavaScript, CSS and more and be part of Rai
|
|
254
300
|
|
255
301
|
## Render API
|
256
302
|
|
257
|
-
|
258
|
-
|
259
|
-
Cells love to render. You can render as many views as you need in a cell state or view.
|
260
|
-
|
261
|
-
```ruby
|
262
|
-
<%= render :index %>
|
263
|
-
```
|
264
|
-
|
265
|
-
The `#render` method really just returns the rendered template string, allowing you all kind of modification.
|
303
|
+
Unlike Rails, the `#render` method only provides a handful of options you gotta learn.
|
266
304
|
|
267
305
|
```ruby
|
268
306
|
def show
|
269
|
-
render
|
270
|
-
end
|
271
|
-
```
|
272
|
-
|
273
|
-
You can even render other cells _within_ a cell using the exact same API.
|
274
|
-
|
275
|
-
```ruby
|
276
|
-
def about
|
277
|
-
cell(:profile, model.author).()
|
307
|
+
render
|
278
308
|
end
|
279
309
|
```
|
280
310
|
|
281
|
-
|
282
|
-
|
311
|
+
Without options, this will render the state name, e.g. `show.erb`.
|
283
312
|
|
284
|
-
|
285
|
-
|
286
|
-
In order to render collections, Cells comes with a shortcut.
|
313
|
+
You can provide a view name manually. The following calls are identical.
|
287
314
|
|
288
315
|
```ruby
|
289
|
-
|
290
|
-
|
316
|
+
render :index
|
317
|
+
render view: :index
|
291
318
|
```
|
292
319
|
|
293
|
-
|
320
|
+
If you need locals, pass them to `#render`.
|
294
321
|
|
295
322
|
```ruby
|
296
|
-
|
323
|
+
render locals: {style: "border: solid;"}
|
297
324
|
```
|
298
325
|
|
299
|
-
|
326
|
+
## Layouts
|
300
327
|
|
301
|
-
|
328
|
+
Every view can be wrapped by a layout. Either pass it when rendering.
|
302
329
|
|
303
330
|
```ruby
|
304
|
-
|
331
|
+
render layout: :default
|
305
332
|
```
|
306
333
|
|
307
|
-
|
308
|
-
|
309
|
-
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.
|
334
|
+
Or configure it on the class-level.
|
310
335
|
|
311
336
|
```ruby
|
312
337
|
class CommentCell < Cell::ViewModel
|
313
|
-
|
314
|
-
|
315
|
-
# ..
|
316
|
-
end
|
317
|
-
```
|
318
|
-
|
319
|
-
The `::cache` method will forward options to the caching engine.
|
320
|
-
|
321
|
-
```ruby
|
322
|
-
cache :show, expires_in: 10.minutes
|
323
|
-
```
|
324
|
-
|
325
|
-
You can also compute your own cache key, use dynamic keys, cache tags, and so on.
|
326
|
-
|
327
|
-
```ruby
|
328
|
-
cache :show { |model, options| "comment/#{model.id}/#{model.updated_at}" }
|
329
|
-
cache :show, :if => lambda { |*| has_changed? }
|
330
|
-
cache :show, :tags: lambda { |model, options| "comment-#{model.id}" }
|
338
|
+
layout :default
|
331
339
|
```
|
332
340
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
---------------------------------------
|
337
|
-
|
338
|
-
This is available via the `model` method. Declarative `::property`s give you readers to the model.
|
339
|
-
|
340
|
-
|
341
|
-
Cells allow you to encapsulate parts of your page into separate MVC components. These components are called _view models_.
|
342
|
-
|
343
|
-
You can render view models anywhere in your code. Mostly, cells are used in views to replace a helper/partial/filter mess, as a mailer renderer substitute or they get hooked to routes to completely bypass `ActionController`.
|
344
|
-
|
345
|
-
As you have already noticed we use _cell_ and _view model_ interchangeably here.
|
341
|
+
The layout is treated as a view and will be searched in the same directories.
|
346
342
|
|
347
343
|
|
348
|
-
##
|
349
|
-
|
350
|
-
Cells is part of the [Trailblazer project](https://github.com/apotonick/trailblazer). Please [buy my book](https://leanpub.com/trailblazer) to support the development and to learn all the cool stuff about Cells. The book discusses the following.
|
351
|
-
|
352
|
-
<a href="https://leanpub.com/trailblazer">
|
353
|
-
![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
|
354
|
-
</a>
|
355
|
-
|
356
|
-
* Basic view models, replacing helpers, and how to structure your view into cell components (chapter 2 and 4).
|
357
|
-
* Advanced Cells API (chapter 4 and 6).
|
358
|
-
* Testing Cells (chapter 4 and 6).
|
359
|
-
* Cells Pagination with AJAX (chapter 6).
|
360
|
-
* View Caching and Expiring (chapter 7).
|
361
|
-
|
362
|
-
More chapters are coming.
|
363
|
-
|
364
|
-
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.
|
365
|
-
|
366
|
-
## No ActionView
|
367
|
-
|
368
|
-
Starting with Cells 4.0 we no longer use `ActionView` as a template engine. Removing this jurassic dependency cuts down Cells' rendering code to less than 50 lines and improves rendering speed by 300%!
|
369
|
-
|
370
|
-
**Note for Cells 3.x:** This README only documents Cells 4.0. Please [read the old README if you're using Cells 3.x](https://github.com/apotonick/cells/tree/31f6ed82b87b3f92613698442fae6fd61cc16de9#cells).
|
371
|
-
|
372
|
-
|
373
|
-
## Installation
|
344
|
+
## Nested Cells
|
374
345
|
|
375
|
-
Cells
|
346
|
+
Cells love to render. You can render as many views as you need in a cell state or view.
|
376
347
|
|
377
348
|
```ruby
|
378
|
-
|
349
|
+
<%= render :index %>
|
379
350
|
```
|
380
351
|
|
381
|
-
|
382
|
-
|
383
|
-
Cells comes bundled with ERB support. To render HAML, you have to include the [cells-haml](https://github.com/trailblazer/cells-haml) gem. The same for [cells-slim](https://github.com/trailblazer/cells-slim). Currently, they are only available as github dependencies, they will be released soon (early 2015).
|
352
|
+
The `#render` method really just returns the rendered template string, allowing you all kind of modification.
|
384
353
|
|
385
354
|
```ruby
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
The template engine extensions fix severe bugs in combination with Rails helpers and the respective engine. Time will tell if we can convince the template teams to merge these fixes.
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
## Generate
|
394
|
-
|
395
|
-
Use the bundled generator to set up a cell.
|
396
|
-
|
397
|
-
```shell
|
398
|
-
rails generate cell comment
|
399
|
-
```
|
400
|
-
|
401
|
-
```
|
402
|
-
create app/cells/
|
403
|
-
create app/cells/comment
|
404
|
-
create app/cells/comment_cell.rb
|
405
|
-
create app/cells/comment/show.erb
|
406
|
-
```
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
Cells provides you the view _model_ via the `#model` method. Here, this returns the `Comment` instance passed into the constructor.
|
411
|
-
|
412
|
-
Of course, this view is a mess and needs be get cleaned up!
|
413
|
-
|
414
|
-
## Logicless Views
|
415
|
-
|
416
|
-
This is how a typical view looks in a view model.
|
417
|
-
|
418
|
-
```haml
|
419
|
-
-# app/cells/comment/show.haml
|
420
|
-
|
421
|
-
%h1 Comment
|
422
|
-
|
423
|
-
= body
|
424
|
-
By
|
425
|
-
= author_link
|
355
|
+
def show
|
356
|
+
render + render(:additional)
|
357
|
+
end
|
426
358
|
```
|
427
359
|
|
428
|
-
|
360
|
+
You can even render other cells _within_ a cell using the exact same API.
|
429
361
|
|
430
362
|
```ruby
|
431
|
-
|
432
|
-
|
433
|
-
render
|
434
|
-
end
|
435
|
-
|
436
|
-
private
|
437
|
-
|
438
|
-
def body
|
439
|
-
model.body
|
440
|
-
end
|
441
|
-
|
442
|
-
def author_link
|
443
|
-
link_to model.author.name, model.author
|
444
|
-
end
|
363
|
+
def about
|
364
|
+
cell(:profile, model.author).()
|
445
365
|
end
|
446
366
|
```
|
447
367
|
|
448
|
-
|
449
|
-
|
450
|
-
## No Helpers
|
451
|
-
|
452
|
-
The difference to conventional Rails views is that every method called in a view is directly called on the cell instance. The cell instance _is_ the rendering context. This allows a very object-oriented and clean way to implement views.
|
453
|
-
|
454
|
-
Helpers as known from conventional Rails where methods and variables get copied between view and controller no longer exist in Cells.
|
368
|
+
This works both in cell views and on the instance, in states.
|
455
369
|
|
456
|
-
Note that you can still use helpers like `link_to` and all the other friends - you have to _include_ them into the cell class, though.
|
457
370
|
|
458
|
-
##
|
371
|
+
## View Inheritance
|
459
372
|
|
460
|
-
|
373
|
+
Cells can inherit code from each other with Ruby's inheritance.
|
461
374
|
|
462
375
|
```ruby
|
463
376
|
class CommentCell < Cell::ViewModel
|
464
|
-
|
465
|
-
render
|
466
|
-
end
|
467
|
-
|
468
|
-
private
|
469
|
-
property :body
|
470
|
-
property :author
|
377
|
+
end
|
471
378
|
|
472
|
-
|
473
|
-
link_to author.name, author
|
474
|
-
end
|
379
|
+
class PostCell < CommentCell
|
475
380
|
end
|
476
381
|
```
|
477
382
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
## Render
|
482
|
-
|
483
|
-
multiple times allowed
|
484
|
-
:view
|
485
|
-
:format ".html"
|
486
|
-
template_engine
|
487
|
-
view_paths
|
383
|
+
Even cooler, `PostCell` will now inherit views from `CommentCell`.
|
488
384
|
|
385
|
+
```ruby
|
386
|
+
PostCell.prefixes #=> ["app/cells/post", "app/cells/comment"]
|
387
|
+
```
|
489
388
|
|
389
|
+
When views can be found in the local `post` directory, they will be looked up in `comment`. This starts to become helpful when using [composed cells](#nested-cells).
|
490
390
|
|
391
|
+
If you only want to inherit views, not the entire class, use `::inherit_views`.
|
491
392
|
|
393
|
+
```ruby
|
394
|
+
class PostCell < Cell::ViewModel
|
395
|
+
inherit_views Comment::Cell
|
396
|
+
end
|
492
397
|
|
398
|
+
PostCell.prefixes #=> ["app/cells/post", "app/cells/comment"]
|
399
|
+
```
|
493
400
|
|
494
401
|
## Collections
|
495
402
|
|
496
|
-
|
403
|
+
In order to render collections, Cells comes with a shortcut.
|
497
404
|
|
498
405
|
```ruby
|
499
|
-
=
|
406
|
+
comments = Comment.all #=> three comments.
|
407
|
+
cell(:comment, collection: comments)
|
500
408
|
```
|
501
409
|
|
502
|
-
|
410
|
+
This will invoke `cell(:comment, song).()` three times and concatenate the rendered output automatically. In case you don't want `show` but another state rendered, use `:method`.
|
503
411
|
|
504
412
|
```ruby
|
505
|
-
|
506
|
-
= cell(:song, song).call(:show)
|
413
|
+
cell(:comment, collection: comments, method: :list)
|
507
414
|
```
|
508
415
|
|
509
|
-
|
510
|
-
|
511
|
-
```ruby
|
512
|
-
= cell(:song, collection: Song.all, genre: "Heavy Metal", user: current_user)
|
513
|
-
```
|
416
|
+
Note that you _don't_ need to invoke call here, the `:collection` behavior internally handles that for you.
|
514
417
|
|
515
|
-
|
418
|
+
Additional options are passed to every cell constructor.
|
516
419
|
|
517
420
|
```ruby
|
518
|
-
|
421
|
+
cell(:comment, collection: comments, style: "awesome", volume: "loud")
|
519
422
|
```
|
520
423
|
|
521
424
|
## Builder
|
@@ -525,255 +428,74 @@ Often, it is good practice to replace decider code from views or classes into se
|
|
525
428
|
Builders allow instantiating different cell classes for different models and options.
|
526
429
|
|
527
430
|
```ruby
|
528
|
-
class
|
431
|
+
class CommentCell < Cell::ViewModel
|
529
432
|
builds do |model, options|
|
530
|
-
|
531
|
-
|
433
|
+
PostCell if model.is_a?(Post)
|
434
|
+
CommentCell if model.is_a?(Comment)
|
532
435
|
end
|
533
|
-
|
534
|
-
def show
|
535
|
-
# ..
|
536
|
-
end
|
537
436
|
```
|
538
437
|
|
539
|
-
The `#cell`
|
438
|
+
The `#cell` helper takes care of instantiating the right cell class for you.
|
540
439
|
|
541
440
|
```ruby
|
542
|
-
cell(:
|
441
|
+
cell(:comment, Post.find(1)) #=> creates a PostCell.
|
543
442
|
```
|
544
443
|
|
545
444
|
This also works with collections.
|
546
445
|
|
547
446
|
```ruby
|
548
|
-
cell(:
|
549
|
-
```
|
550
|
-
|
551
|
-
Multiple calls to `::builds` will be ORed. If no block returns a class, the original class will be used (`SongCell`). Builders are inherited.
|
552
|
-
|
553
|
-
|
554
|
-
## View Inheritance
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
## Asset Pipeline
|
559
|
-
|
560
|
-
Cells can also package their own assets like JavaScript, CoffeeScript, Sass and stylesheets. When configured, those files go directly into Rails' asset pipeline. This is a great way to clean up your assets by pushing scripts and styles into the component they belong to. It makes it so much easier to find out which files are actually involved per "widget".
|
561
|
-
|
562
|
-
Note: This feature is **still experimental** and the API (file name conventions, configuration, etc.) might change.
|
563
|
-
|
564
|
-
Assets per default sit in the cell's `assets/` directory.
|
565
|
-
|
566
|
-
```
|
567
|
-
app
|
568
|
-
├── cells
|
569
|
-
│ ├── comment
|
570
|
-
│ │ ├── views
|
571
|
-
│ │ ├── ..
|
572
|
-
│ │ ├── assets
|
573
|
-
│ │ │ ├── comment.js.coffee
|
574
|
-
│ │ │ ├── comment.css.sass
|
575
|
-
```
|
576
|
-
|
577
|
-
Adding the assets files to the asset pipeline currently involves two steps (I know it feels a bit clumsy, but I'm sure we'll find a way to make it better soon).
|
578
|
-
|
579
|
-
1. Tell Rails that this cell provides its own self-contained assets.
|
580
|
-
|
581
|
-
```ruby
|
582
|
-
Gemgem::Application.configure do
|
583
|
-
# ...
|
584
|
-
|
585
|
-
config.cells.with_assets = %w(comment)
|
586
|
-
```
|
587
|
-
|
588
|
-
This will add `app/cells/comment/assets/` to the asset pipeline's paths.
|
589
|
-
|
590
|
-
2. Include the assets in `application.js` and `application.css.sass`
|
591
|
-
|
592
|
-
In `app/assets/application.js`, you have to add the cell assets manually.
|
593
|
-
|
594
|
-
```javascript
|
595
|
-
//=# require comments
|
596
|
-
```
|
597
|
-
|
598
|
-
Same goes into `app/assets/application.css.sass`.
|
599
|
-
|
600
|
-
```sass
|
601
|
-
@import 'comments';
|
602
|
-
```
|
603
|
-
|
604
|
-
In future versions, we wanna improve this by automatically including cell assets and avoiding name clashes. If you have ideas, suggestions, I'd love to hear them.
|
605
|
-
|
606
|
-
## View Inheritance
|
607
|
-
|
608
|
-
This is where OOP comes back to your view.
|
609
|
-
|
610
|
-
* __Inherit code__ into your cells by deriving more abstract cells.
|
611
|
-
* __Inherit views__ from parent cells.
|
612
|
-
|
613
|
-
|
614
|
-
### Sharing Views
|
615
|
-
|
616
|
-
Sometimes it is handy to reuse an existing view directory from another cell, to avoid a growing number of directories. You could derive the new cell and thus inherit the view paths.
|
617
|
-
|
618
|
-
```ruby
|
619
|
-
class Comment::FormCell < CommentCell
|
620
|
-
```
|
621
|
-
|
622
|
-
This does not only allow view inheritance, but will also inherit all the code from `CommentCell`. This might not be what you want.
|
623
|
-
|
624
|
-
If you're just after inheriting the _views_, use `::inherit_views`.
|
625
|
-
|
626
|
-
```ruby
|
627
|
-
class Comment::FormCell < Cell::Rails
|
628
|
-
inherit_views CommentCell
|
629
|
-
```
|
630
|
-
|
631
|
-
When rendering views in `FormCell`, the view directories to look for templates will be inherited.
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
### Call
|
638
|
-
|
639
|
-
The `#call` method also accepts a block and yields `self` (the cell instance) to it. This is extremely helpful for using `content_for` outside of the cell.
|
640
|
-
|
641
|
-
```ruby
|
642
|
-
= cell(:song, Song.last).call(:show) do |cell|
|
643
|
-
content_for :footer, cell.footer
|
447
|
+
cell(:comment, collection: [@post, @comment]) #=> renders PostCell, then CommentCell.
|
644
448
|
```
|
645
449
|
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
## Using Decorators (Twins)
|
650
|
-
|
651
|
-
You need to include the `disposable` gem in order to use this.
|
652
|
-
|
653
|
-
````ruby
|
654
|
-
gem "disposable"
|
655
|
-
```
|
656
|
-
|
657
|
-
With Cells 3.12, a new experimental concept enters the stage: Decorators in view models. As the view model should only contain logic related to presentation (which can get quite a bit), decorators - called _Twins_ - can be defined and automatically setup for your model.
|
658
|
-
|
659
|
-
Twins are a general concept in Trailblazer and are used everywhere where representers, forms, operations or cells need additional logic that has to be shared between layers. So, this extra step allows re-using your decorator for presentations other than the cell, e.g. in a JSON API, tests, etc.
|
660
|
-
|
661
|
-
Also, logic that simply doesn't belong to in a view-related class goes into a twin. That could be code to figure out if a user in logged in.
|
662
|
-
|
663
|
-
```ruby
|
664
|
-
class SongCell < Cell::ViewModel
|
665
|
-
include Properties
|
666
|
-
|
667
|
-
class Twin < Cell::Twin # this is your decorator
|
668
|
-
property :title
|
669
|
-
property :id
|
670
|
-
option :in_stock?
|
671
|
-
end
|
450
|
+
Multiple calls to `::builds` will be ORed. If no block returns a class, the original class will be used (`CommentCell`). Builders are inherited.
|
672
451
|
|
673
|
-
properties Twin
|
674
452
|
|
675
|
-
|
676
|
-
if in_stock?
|
677
|
-
"You're lucky #{title} (#{id}) is in stock!"
|
678
|
-
end
|
679
|
-
end
|
680
|
-
end
|
681
|
-
```
|
682
|
-
|
683
|
-
In this example, we define the twin _in_ the cell itself. That could be done anywhere, as long as you tell the cell where to find the twin (`properties Twin`).
|
684
|
-
|
685
|
-
### Creating A Twin Cell
|
453
|
+
## Caching
|
686
454
|
|
687
|
-
|
455
|
+
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.
|
688
456
|
|
689
457
|
```ruby
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
Internally, a twin is created from the arguments and passed to the view model. The view model cell now only works on the twin, not on the model anymore.
|
694
|
-
|
695
|
-
The twin simply acts as a delegator between the cell and the model: attributes defined with `property` are copied from the model, `option` values _have_ to be passed explicitely to the constructor. The twin defines an _interface_ for using your cell.
|
696
|
-
|
697
|
-
Another awesome thing is that you can now easily test your cell by "mocking" values.
|
458
|
+
class CommentCell < Cell::ViewModel
|
459
|
+
cache :show
|
698
460
|
|
699
|
-
|
700
|
-
it "renders nicely" do
|
701
|
-
cell("song", song, in_stock?: true, title: "Mocked Song Title").must_match ...
|
461
|
+
# ..
|
702
462
|
end
|
703
463
|
```
|
704
464
|
|
705
|
-
The
|
706
|
-
|
707
|
-
### Extending Decorators
|
708
|
-
|
709
|
-
A decorator without any logic only gives you a tiny improvement, they become really helpful when including your own decorator logic.
|
465
|
+
The `::cache` method will forward options to the caching engine.
|
710
466
|
|
711
467
|
```ruby
|
712
|
-
|
713
|
-
property :title
|
714
|
-
property :id
|
715
|
-
option :in_stock?
|
716
|
-
|
717
|
-
def title
|
718
|
-
super.downcase # super to retrieve the original title from model!
|
719
|
-
end
|
720
|
-
end
|
468
|
+
cache :show, expires_in: 10.minutes
|
721
469
|
```
|
722
470
|
|
723
|
-
|
724
|
-
|
725
|
-
Note: If there's enough interest, this could also be extended to work with draper and other decoration gems.
|
726
|
-
|
727
|
-
### Nested Rendering
|
728
|
-
|
729
|
-
When extracting parts of your view into a partial, as we did for the author section, you're free to render additional views using `#render`. Again, wrap render calls in instance methods, otherwise you'll end up with too much logic in your view.
|
471
|
+
You can also compute your own cache key, use dynamic keys, cache tags, and so on.
|
730
472
|
|
731
473
|
```ruby
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
property :title
|
736
|
-
|
737
|
-
# ...
|
738
|
-
|
739
|
-
def author_box
|
740
|
-
render :author # same as render view: :author
|
741
|
-
end
|
742
|
-
end
|
474
|
+
cache :show { |model, options| "comment/#{model.id}/#{model.updated_at}" }
|
475
|
+
cache :show, :if => lambda { |*| has_changed? }
|
476
|
+
cache :show, :tags: lambda { |model, options| "comment-#{model.id}" }
|
743
477
|
```
|
744
478
|
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
### Cells in ActionMailer
|
750
|
-
|
751
|
-
ActionMailer doesn't have request object, so if you inherit from Cell::Rails you will receive an error. Cell::Base will fix that problem, but you will not be able to use any of routes inside your cells.
|
752
|
-
|
753
|
-
You can fix that with [actionmailer_with_request](https://github.com/weppos/actionmailer_with_request) which (suprise!) brings request object to the ActionMailer.
|
754
|
-
|
755
|
-
|
479
|
+
Caching is documented [here](http://trailblazerb.org/gems/cells/caching.html) and in chapter 8 of the [Trailblazer book](http://leanpub.com/trailblazer).
|
756
480
|
|
757
|
-
## Cells is Rails::Engine aware!
|
758
481
|
|
759
|
-
|
482
|
+
## The Book
|
760
483
|
|
484
|
+
Cells is part of the [Trailblazer project](https://github.com/apotonick/trailblazer). Please [buy my book](https://leanpub.com/trailblazer) to support the development and to learn all the cool stuff about Cells. The book discusses the following.
|
761
485
|
|
762
|
-
|
486
|
+
<a href="https://leanpub.com/trailblazer">
|
487
|
+
![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
|
488
|
+
</a>
|
763
489
|
|
764
|
-
|
490
|
+
* Basic view models, replacing helpers, and how to structure your view into cell components (chapter 2 and 4).
|
491
|
+
* Advanced Cells API (chapter 4 and 6).
|
492
|
+
* Testing Cells (chapter 4 and 6).
|
493
|
+
* Cells Pagination with AJAX (chapter 6).
|
494
|
+
* View Caching and Expiring (chapter 8).
|
765
495
|
|
766
|
-
|
496
|
+
More chapters are coming.
|
767
497
|
|
768
|
-
|
769
|
-
module MyApp
|
770
|
-
class Application < Rails::Application
|
771
|
-
config.generators do |g|
|
772
|
-
g.base_cell_class "ApplicationCell"
|
773
|
-
end
|
774
|
-
end
|
775
|
-
end
|
776
|
-
```
|
498
|
+
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.
|
777
499
|
|
778
500
|
|
779
501
|
## Undocumented Features
|