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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c08042fbea7ac33f3607605e1e290c0956b4312
4
- data.tar.gz: 017db4d48f5a0eaab84f4ecb8fc014b9470f21f0
3
+ metadata.gz: 22c7de3a30ac46478e9d4f4ef37c4b8fe1b49b17
4
+ data.tar.gz: 00b8a25633e2495ff4368ffe53e0c8f404ba7d8d
5
5
  SHA512:
6
- metadata.gz: 841380b7a18d52879c7f0323816cf78bec726d520478c55a52d8366fc9706974440572e96a1fa6298daa4a056c22f720a359c1e637a2df2c6547f40c3c34c021
7
- data.tar.gz: da654a5b2dceec48cab59f4b89f3c40a9e2b326bc92d0040d1ccbcac0361ec91b7756501fd21dfdb612484b1453fde248925772cf3771eed61bc26ec083441d3
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.2+ is fully supported, in older versions some form helpers do not work. Let us know if you need this.
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:** Output is only escaped once, when using a reader method _in the view_. This highly speeds up rendering and removes the need to use `html_safe` on every string in the stack.
7
- * **Template Engines:** There's now _one_ template engine (e.g. ERB or HAML) per cell class. It can be set using `ViewModel::template_engine=`. In 99.9% of all cases a single application uses one single template engine application-wide, there's no need to manage code and waste lookup time for two alternative engines within one cell.
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
- * When using HAML, we do not use any of HAML's helper hacks to "fix" ActionView and XSS. While you might not note this, it removes tons of code from our stack.
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 #=> &lt;script&gt;Dangerous&lt;/script&gt;
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
- ## Nested Cells
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 + render(:additional)
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
- This works both in cell views and on the instance, in states.
282
-
311
+ Without options, this will render the state name, e.g. `show.erb`.
283
312
 
284
- ## Collections
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
- comments = Comment.all #=> three comments.
290
- cell(:comment, collection: comments)
316
+ render :index
317
+ render view: :index
291
318
  ```
292
319
 
293
- 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`.
320
+ If you need locals, pass them to `#render`.
294
321
 
295
322
  ```ruby
296
- cell(:comment, collection: comments, method: :list)
323
+ render locals: {style: "border: solid;"}
297
324
  ```
298
325
 
299
- Note that you _don't_ need to invoke call here, the `:collection` behavior internally handles that for you.
326
+ ## Layouts
300
327
 
301
- Additional options are passed to every cell constructor.
328
+ Every view can be wrapped by a layout. Either pass it when rendering.
302
329
 
303
330
  ```ruby
304
- cell(:comment, collection: comments, style: "awesome", volume: "loud")
331
+ render layout: :default
305
332
  ```
306
333
 
307
- ## Caching
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
- cache :show
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
- Caching is documented [here](http://trailblazerb.org/gems/cells/caching.html) and in chapter 8 of the [Trailblazer book](http://leanpub.com/trailblazer).
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
- ## The Book
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 run with all Rails >= 3.2. Lower versions of Rails will still run with Cells, but you will get in trouble with the helpers.
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
- gem 'cells', "~> 4.0.0"
349
+ <%= render :index %>
379
350
  ```
380
351
 
381
- ## Prerequisites
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
- gem "cells-haml", github: 'trailblazer/cells-haml'
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
- The methods we call in the view now need to be defined in the cell instance.
360
+ You can even render other cells _within_ a cell using the exact same API.
429
361
 
430
362
  ```ruby
431
- class CommentCell < Cell::ViewModel
432
- def show
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
- See how you can use helpers in a cell instance?
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
- ## Automatic Properties
371
+ ## View Inheritance
459
372
 
460
- Often, as in the `#body` method, you simply need to delegate properties from the model. This can be done automatically using `::property`.
373
+ Cells can inherit code from each other with Ruby's inheritance.
461
374
 
462
375
  ```ruby
463
376
  class CommentCell < Cell::ViewModel
464
- def show
465
- render
466
- end
467
-
468
- private
469
- property :body
470
- property :author
377
+ end
471
378
 
472
- def author_link
473
- link_to author.name, author
474
- end
379
+ class PostCell < CommentCell
475
380
  end
476
381
  ```
477
382
 
478
- Readers are automatically created when defined with `::property`.
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
- You can render a collection of models where each item is rendered using a cell.
403
+ In order to render collections, Cells comes with a shortcut.
497
404
 
498
405
  ```ruby
499
- = cell(:song, collection: Song.all)
406
+ comments = Comment.all #=> three comments.
407
+ cell(:comment, collection: comments)
500
408
  ```
501
409
 
502
- Note that there is no `.call` needed. This is identical to the following snippet.
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
- - Song.all.each do |song|
506
- = cell(:song, song).call(:show)
413
+ cell(:comment, collection: comments, method: :list)
507
414
  ```
508
415
 
509
- Options are passed to every cell.
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
- The collection invocation per default calls `#show`. Use `:method` if you need another method to be called.
418
+ Additional options are passed to every cell constructor.
516
419
 
517
420
  ```ruby
518
- = cell(:song, collection: Song.all, method: :detail)
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 SongCell < Cell::ViewModel
431
+ class CommentCell < Cell::ViewModel
529
432
  builds do |model, options|
530
- HitCell if model.is_a?(Hit)
531
- EverGreenCell if model.is_a?(Evergreen)
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` helpers takes care of instantiating the right cell class for you.
438
+ The `#cell` helper takes care of instantiating the right cell class for you.
540
439
 
541
440
  ```ruby
542
- cell(:song, Hit.find(1)) #=> creates a HitCell.
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(:song, collection: [@hit, @song]) #=> renders HitCell, then SongCell.
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
- Note how the block is run in the global view's context, allowing you to use global helpers like `content_for`.
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
- def show
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
- You create your cell as follows.
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
- cell("song", Song.find(1), in_stock?: true)
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
- ```ruby
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 twin will simply use the passed `:title` and not copy the title from the song model, making it really easy to test edge cases in your view model.
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
- class Twin < Cell::Twin # this is your decorator
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
- The same logic can now be used in a cell, a JSON or XML API endpoint or in the model layer.
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
- class SongCell < Cell::ViewModel
733
- include TimeagoHelper
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
- This will simply render the `author.haml` template in the same context as the `show` view, meaning you might use helpers, again.
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
- Now `Rails::Engine`s can contribute to Cells view paths. By default, any 'app/cells' found inside any Engine is automatically included into Cells view paths. If you need to, you can customize the view paths changing/appending to the `'app/cell_views'` path configuration. See the `Cell::EngineIntegration` for more details.
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
- ## Generator Options
486
+ <a href="https://leanpub.com/trailblazer">
487
+ ![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
488
+ </a>
763
489
 
764
- By default, generated cells inherit from `Cell::ViewModel`. If you want to change this, specify your new class name in `config/application.rb`:
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
- ### Base Class
496
+ More chapters are coming.
767
497
 
768
- ```ruby
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