cells 4.0.0.beta6 → 4.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +4 -0
- data/README.md +316 -371
- data/cells.gemspec +1 -3
- data/lib/cell/caching.rb +1 -0
- data/lib/cell/rails.rb +7 -0
- data/lib/cell/railtie.rb +2 -5
- data/lib/cell/version.rb +1 -1
- data/lib/cell/view_model.rb +24 -30
- data/test/builder_test.rb +10 -7
- data/test/caching_test.rb +47 -40
- data/test/rails4.2/app/cells/song_cell.rb +7 -0
- data/test/rails4.2/app/controllers/songs_controller.rb +8 -0
- data/test/rails4.2/test/integration/formtastic_test.rb +1 -0
- data/test/rails4.2/test/integration/simple_form_test.rb +1 -0
- data/test/rails4.2/test/integration/url_helper_test.rb +16 -5
- data/test/templates_test.rb +1 -1
- data/test/test_helper.rb +2 -0
- data/test/twin_test.rb +20 -20
- metadata +4 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c08042fbea7ac33f3607605e1e290c0956b4312
|
4
|
+
data.tar.gz: 017db4d48f5a0eaab84f4ecb8fc014b9470f21f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 841380b7a18d52879c7f0323816cf78bec726d520478c55a52d8366fc9706974440572e96a1fa6298daa4a056c22f720a359c1e637a2df2c6547f40c3c34c021
|
7
|
+
data.tar.gz: da654a5b2dceec48cab59f4b89f3c40a9e2b326bc92d0040d1ccbcac0361ec91b7756501fd21dfdb612484b1453fde248925772cf3771eed61bc26ec083441d3
|
data/CHANGES.md
CHANGED
@@ -18,6 +18,10 @@
|
|
18
18
|
|
19
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.
|
20
20
|
|
21
|
+
## 4.0.0.rc1
|
22
|
+
|
23
|
+
* Move delegations of `#url_options` etc. to the railtie, which makes it work.
|
24
|
+
|
21
25
|
## 4.0.0.beta6
|
22
26
|
|
23
27
|
* Removed `ViewModel::template_engine`. This is now done explicitly by including `Cell::Erb`, etc. and happens automatically in a Rails environment.
|
data/README.md
CHANGED
@@ -1,64 +1,87 @@
|
|
1
1
|
# Cells
|
2
2
|
|
3
|
-
*View Components for Rails.*
|
4
|
-
|
3
|
+
*View Components for Ruby and Rails.*
|
5
4
|
|
6
5
|
## Overview
|
7
6
|
|
8
|
-
Cells allow you to encapsulate parts of your
|
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.
|
9
8
|
|
10
|
-
|
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).
|
11
10
|
|
12
|
-
|
11
|
+
## This is not Cells 3.x!
|
13
12
|
|
13
|
+
Temporary note: This is the README and API for Cells 4. Many things have improved. If you want to upgrade, [follow this guide](https://github.com/apotonick/cells/wiki/From-Cells-3-to-Cells-4---Upgrading-Guide). When in trouble, join us on the IRC (Freenode) #trailblazer channel.
|
14
14
|
|
15
|
-
## The Book
|
16
15
|
|
17
|
-
|
16
|
+
## Rendering Cells
|
18
17
|
|
19
|
-
|
20
|
-
![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
|
21
|
-
</a>
|
18
|
+
You can render cells anywhere and as many as you want, in views, controllers, composites, mailers, etc.
|
22
19
|
|
23
|
-
|
24
|
-
* Advanced Cells API (chapter 4 and 6).
|
25
|
-
* Testing Cells (chapter 4 and 6).
|
26
|
-
* Cells Pagination with AJAX (chapter 6).
|
27
|
-
* View Caching and Expiring (chapter 7).
|
20
|
+
Rendering a cell in Rails ironically happens via a helper.
|
28
21
|
|
29
|
-
|
22
|
+
```ruby
|
23
|
+
<%= cell(:comment, @comment) %>
|
24
|
+
```
|
30
25
|
|
31
|
-
|
26
|
+
This boils down to the following invocation, that can be used to render cells in *any other Ruby* environment.
|
32
27
|
|
33
|
-
|
28
|
+
```ruby
|
29
|
+
CommentCell.(@comment).()
|
30
|
+
```
|
34
31
|
|
35
|
-
|
32
|
+
In Rails you have the same helper API for views and controllers.
|
36
33
|
|
37
|
-
|
34
|
+
```ruby
|
35
|
+
class DasboardController < ApplicationController
|
36
|
+
def dashboard
|
37
|
+
@comments = cell(:comment, Comment.recent).()
|
38
|
+
@traffic = cell(:report, TrafficReport.find(1))
|
39
|
+
end
|
40
|
+
```
|
38
41
|
|
42
|
+
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.
|
39
43
|
|
40
|
-
##
|
44
|
+
## Cell Class
|
41
45
|
|
42
|
-
|
46
|
+
A cell is a light-weight class with one or multiple methods that render views.
|
43
47
|
|
44
48
|
```ruby
|
45
|
-
|
49
|
+
class Comment::Cell < Cell::ViewModel
|
50
|
+
property :body
|
51
|
+
property :author
|
52
|
+
|
53
|
+
def show
|
54
|
+
render
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def author_link
|
59
|
+
link_to "#{author.email}", author
|
60
|
+
end
|
61
|
+
end
|
46
62
|
```
|
47
63
|
|
48
|
-
|
64
|
+
Here, `show` is the only public method. By calling `render` it will invoke rendering for the `show` view.
|
49
65
|
|
50
|
-
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).
|
51
66
|
|
52
|
-
|
53
|
-
|
67
|
+
## Logicless Views
|
68
|
+
|
69
|
+
Views come packaged with the cell and can be ERB, Haml, or Slim.
|
70
|
+
|
71
|
+
```erb
|
72
|
+
<h3>New Comment</h3>
|
73
|
+
<%= body %>
|
74
|
+
|
75
|
+
By <%= author_link %>
|
54
76
|
```
|
55
77
|
|
56
|
-
The
|
78
|
+
The concept of "helpers" that get strangely copied from modules to the view does not exist in Cells anymore.
|
57
79
|
|
80
|
+
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.
|
58
81
|
|
59
|
-
## File
|
82
|
+
## File Structure
|
60
83
|
|
61
|
-
|
84
|
+
In Rails, cells are placed in `app/cells` or `app/concepts/`. Every cell has their own directory where it keeps views, assets and code.
|
62
85
|
|
63
86
|
```
|
64
87
|
app
|
@@ -69,100 +92,321 @@ app
|
|
69
92
|
│ │ ├── list.haml
|
70
93
|
```
|
71
94
|
|
95
|
+
The discussed `show` view would reside in `app/cells/comment/show.haml`. However, you can set [any set of view paths](#view-paths) you want.
|
72
96
|
|
73
|
-
## Generate
|
74
97
|
|
75
|
-
|
98
|
+
## Invocation Styles
|
76
99
|
|
77
|
-
|
78
|
-
|
100
|
+
In order to make a cell render, you have to call the rendering methods. While you could call the method directly, the prefered way is the _call style_.
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
cell(:comment, @song).() # calls CommentCell#show.
|
104
|
+
cell(:comment, @song).(:index) # calls CommentCell#index.
|
79
105
|
```
|
80
106
|
|
107
|
+
The call style respects caching.
|
108
|
+
|
109
|
+
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.
|
110
|
+
|
111
|
+
## Parameters
|
112
|
+
|
113
|
+
You can pass in as many parameters as you need. Per convention, this is a hash.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
cell(:comment, @song, volume: 99, genre: "Jazz Fusion")
|
81
117
|
```
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
118
|
+
|
119
|
+
Options can be accessed via the `@options` instance variable.
|
120
|
+
|
121
|
+
Naturally, you may also pass arbitrary options into the call itself. Those will be simple method arguments.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
cell(:comment, @song).(:show, volume: 99)
|
86
125
|
```
|
87
126
|
|
127
|
+
Then, the `show` method signature changes to `def show(options)`.
|
128
|
+
|
88
129
|
|
89
|
-
##
|
130
|
+
## Testing
|
90
131
|
|
91
|
-
|
132
|
+
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.
|
92
133
|
|
93
134
|
```ruby
|
94
|
-
|
135
|
+
html = CommentCell.(@comment).()
|
136
|
+
Capybara.string(html).must_have_css "h3"
|
95
137
|
```
|
96
138
|
|
97
|
-
|
139
|
+
It is completely up to you how you test, whether it's RSpec, MiniTest or whatever. All the cell does is return HTML.
|
98
140
|
|
99
|
-
|
100
|
-
|
141
|
+
[In Rails, there's support](http://trailblazerb.org/gems/cells/testing.html) for TestUnit, MiniTest and RSpec available, along with Capybara integration.
|
142
|
+
|
143
|
+
## Installation
|
144
|
+
|
145
|
+
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.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
gem 'cells', "~> 4.0.0"
|
101
149
|
```
|
102
150
|
|
103
|
-
|
151
|
+
(Note: we use Cells in production with Rails 3.2 and Haml and it works great.)
|
104
152
|
|
105
|
-
|
153
|
+
Various template engines are supported but need to be added to your Gemfile.
|
106
154
|
|
107
|
-
|
155
|
+
* [cells-erb](https://github.com/trailblazer/cells-erb)
|
156
|
+
* [cells-haml](https://github.com/trailblazer/cells-haml)
|
157
|
+
* [cells-slim](https://github.com/trailblazer/cells-slim)
|
108
158
|
|
109
|
-
```
|
110
|
-
|
159
|
+
```ruby
|
160
|
+
gem "cells-erb"
|
161
|
+
```
|
162
|
+
|
163
|
+
In Rails, this is all you need to do. In other environments, you need to include the respective module into your cells.
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
class CommentCell < Cell::ViewModel
|
167
|
+
include ::Cell::Erb # or Cell::Haml, or Cell::Slim
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
## Concept Cells
|
172
|
+
|
173
|
+
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`.
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
class Comment::Cell < Cell::Concept
|
177
|
+
# ..
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
Their directory structure looks as follows.
|
182
|
+
|
183
|
+
```
|
184
|
+
app
|
185
|
+
├── concepts
|
186
|
+
│ ├── comment
|
187
|
+
│ │ ├── cell.rb
|
188
|
+
│ │ ├── views
|
189
|
+
│ │ │ ├── show.haml
|
190
|
+
```
|
191
|
+
|
192
|
+
This integrates with Trailblazer where classes for one _concept_ sit in the same directory. Please [read the book](http://leanpub.com/trailblazer) to learn how to use cells with Trailblazer.
|
193
|
+
|
194
|
+
Concept cells are rendered using the `concept` helper.
|
195
|
+
|
196
|
+
```erb
|
197
|
+
<%= concept("comment/cell", @comment) %>
|
198
|
+
```
|
199
|
+
|
200
|
+
Other than that, normal cells and concept cells are identical.
|
201
|
+
|
202
|
+
## Namespaces
|
203
|
+
|
204
|
+
Cells can be namespaced as well. This is used for [concept cells](#concept-cells), too.
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
module Admin
|
208
|
+
class CommentCell < Cell::ViewModel
|
111
209
|
```
|
112
210
|
|
113
|
-
|
114
|
-
|
211
|
+
Invocation in Rails would happen as follows.
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
cell("admin/comment", @comment).()
|
215
|
+
```
|
115
216
|
|
116
|
-
|
217
|
+
Views will be searched in `app/cells/admin/comment` per default.
|
117
218
|
|
118
|
-
Please [refer to the docs](#invocation-styles) for different ways of invoking view models.
|
119
219
|
|
220
|
+
## Rails Helper API
|
120
221
|
|
121
|
-
|
222
|
+
Even in a non-Rails environment, Cells provides the Rails view API and allows using all Rails helpers.
|
122
223
|
|
123
|
-
|
224
|
+
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.
|
124
225
|
|
125
226
|
```ruby
|
126
227
|
class CommentCell < Cell::ViewModel
|
127
|
-
|
128
|
-
|
228
|
+
include ActionView::Helpers::UrlHelper
|
229
|
+
include ActionView::Helpers::CaptureHelper
|
230
|
+
|
231
|
+
def author_link
|
232
|
+
content_tag :div, link_to(author.name, author)
|
129
233
|
end
|
234
|
+
```
|
235
|
+
|
236
|
+
As always, you can use helpers in cells and in views.
|
237
|
+
|
238
|
+
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.
|
239
|
+
|
240
|
+
|
241
|
+
## View Paths
|
242
|
+
|
243
|
+
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.
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
class CommentCell < Cell::ViewModel
|
247
|
+
self.view_paths = "lib/views"
|
130
248
|
end
|
131
249
|
```
|
132
250
|
|
133
|
-
|
251
|
+
## Asset Packaging
|
252
|
+
|
253
|
+
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](http://trailblazerb.org/gems/cells/rails.html#asset-pipeline).
|
254
|
+
|
255
|
+
## Render API
|
256
|
+
|
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.
|
134
266
|
|
135
267
|
```ruby
|
136
268
|
def show
|
137
|
-
|
269
|
+
render + render(:additional)
|
138
270
|
end
|
139
271
|
```
|
140
272
|
|
141
|
-
|
273
|
+
You can even render other cells _within_ a cell using the exact same API.
|
142
274
|
|
143
|
-
|
275
|
+
```ruby
|
276
|
+
def about
|
277
|
+
cell(:profile, model.author).()
|
278
|
+
end
|
279
|
+
```
|
144
280
|
|
145
|
-
|
281
|
+
This works both in cell views and on the instance, in states.
|
146
282
|
|
147
|
-
In these examples, we're using HAML.
|
148
283
|
|
149
|
-
|
284
|
+
## Collections
|
285
|
+
|
286
|
+
In order to render collections, Cells comes with a shortcut.
|
150
287
|
|
288
|
+
```ruby
|
289
|
+
comments = Comment.all #=> three comments.
|
290
|
+
cell(:comment, collection: comments)
|
291
|
+
```
|
151
292
|
|
152
|
-
|
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`.
|
153
294
|
|
154
|
-
|
295
|
+
```ruby
|
296
|
+
cell(:comment, collection: comments, method: :list)
|
297
|
+
```
|
155
298
|
|
156
|
-
|
157
|
-
-# app/cells/comment/show.haml
|
299
|
+
Note that you _don't_ need to invoke call here, the `:collection` behavior internally handles that for you.
|
158
300
|
|
159
|
-
|
301
|
+
Additional options are passed to every cell constructor.
|
160
302
|
|
161
|
-
|
162
|
-
|
163
|
-
= link_to model.author.name, model.author
|
303
|
+
```ruby
|
304
|
+
cell(:comment, collection: comments, style: "awesome", volume: "loud")
|
164
305
|
```
|
165
306
|
|
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.
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
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}" }
|
331
|
+
```
|
332
|
+
|
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.
|
346
|
+
|
347
|
+
|
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
|
374
|
+
|
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.
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
gem 'cells', "~> 4.0.0"
|
379
|
+
```
|
380
|
+
|
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).
|
384
|
+
|
385
|
+
```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
|
+
|
166
410
|
Cells provides you the view _model_ via the `#model` method. Here, this returns the `Comment` instance passed into the constructor.
|
167
411
|
|
168
412
|
Of course, this view is a mess and needs be get cleaned up!
|
@@ -243,67 +487,9 @@ template_engine
|
|
243
487
|
view_paths
|
244
488
|
|
245
489
|
|
246
|
-
## Invocation styles
|
247
|
-
|
248
|
-
The explicit, long form allows you rendering cells in views, in controllers, mailers, etc.
|
249
|
-
|
250
|
-
```ruby
|
251
|
-
cell(:comment, @comment).call(:show)
|
252
|
-
```
|
253
|
-
|
254
|
-
As `:show` is the default action, you don't have to specify it.
|
255
|
-
|
256
|
-
```ruby
|
257
|
-
cell(:comment, @comment).call
|
258
|
-
```
|
259
490
|
|
260
|
-
In views, the template engine will automatically call `cell.to_s`. It does that for every object passed in as a placeholder. `ViewModel#to_s` exists and is aliased to `#call`, which allows to omit that part in a view.
|
261
491
|
|
262
|
-
```haml
|
263
|
-
= cell(:comment, @comment)
|
264
|
-
```
|
265
492
|
|
266
|
-
If you want, you can also call public methods directly on your cell. Note that this does _not_ respect caching, though.
|
267
|
-
|
268
|
-
```haml
|
269
|
-
= cell(:comment, @comment).avatar
|
270
|
-
```
|
271
|
-
|
272
|
-
## Passing Options
|
273
|
-
|
274
|
-
There's several ways to inject additional state into your cell.
|
275
|
-
|
276
|
-
### Object Style
|
277
|
-
|
278
|
-
Cells can receive any set of options you need. Usually, a hash containing additional options is passed as the last argument.
|
279
|
-
|
280
|
-
```ruby
|
281
|
-
cell(:comment, @comment, layout: :fancy)
|
282
|
-
```
|
283
|
-
|
284
|
-
The third argument is accessable via `#options` in the instance.
|
285
|
-
|
286
|
-
```ruby
|
287
|
-
def show
|
288
|
-
render layout: options[:layout]
|
289
|
-
end
|
290
|
-
```
|
291
|
-
|
292
|
-
### Functional Style
|
293
|
-
|
294
|
-
You can also pass options to the action method itself, making your cell a bit more functional with less state.
|
295
|
-
|
296
|
-
```ruby
|
297
|
-
cell(:comment, @comment).call(:show, layout: :fancy)
|
298
|
-
```
|
299
|
-
|
300
|
-
Make sure the method is ready to process those arguments.
|
301
|
-
|
302
|
-
```ruby
|
303
|
-
def show(layout=:default)
|
304
|
-
render layout: layout
|
305
|
-
end
|
306
|
-
```
|
307
493
|
|
308
494
|
## Collections
|
309
495
|
|
@@ -369,42 +555,6 @@ Multiple calls to `::builds` will be ORed. If no block returns a class, the orig
|
|
369
555
|
|
370
556
|
|
371
557
|
|
372
|
-
# TODO: merge stuff below!
|
373
|
-
|
374
|
-
## File Structure
|
375
|
-
|
376
|
-
In Cells 3.10 we introduce a new _optional_ file structure integrating with [Trailblazer](https://github.com/apotonick/trailblazer)'s "concept-oriented" layout.
|
377
|
-
|
378
|
-
This new file layout makes a cell fully **self-contained** so it can be moved around just by grabbing one single directory.
|
379
|
-
|
380
|
-
Activate it with
|
381
|
-
|
382
|
-
```ruby
|
383
|
-
class Comment::Cell
|
384
|
-
self_contained!
|
385
|
-
|
386
|
-
# ...
|
387
|
-
end
|
388
|
-
```
|
389
|
-
|
390
|
-
Now, the cell directory ideally looks like the following.
|
391
|
-
|
392
|
-
```
|
393
|
-
app
|
394
|
-
├── cells
|
395
|
-
│ ├── comment
|
396
|
-
│ │ ├── cell.rb
|
397
|
-
│ │ ├── views
|
398
|
-
│ │ │ ├── show.haml
|
399
|
-
│ │ │ ├── list.haml
|
400
|
-
```
|
401
|
-
|
402
|
-
|
403
|
-
Here, cell class and associated views are in the same self-contained `comment` directory.
|
404
|
-
|
405
|
-
You can use the new views directory along with leaving your cell _class_ at `app/cells/comment_cell.rb`, if you fancy that.
|
406
|
-
|
407
|
-
|
408
558
|
## Asset Pipeline
|
409
559
|
|
410
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".
|
@@ -481,156 +631,7 @@ class Comment::FormCell < Cell::Rails
|
|
481
631
|
When rendering views in `FormCell`, the view directories to look for templates will be inherited.
|
482
632
|
|
483
633
|
|
484
|
-
## Caching
|
485
|
-
|
486
|
-
Cells allow you to cache per state. It's simple: the rendered result of a state method is cached and expired as you configure it.
|
487
|
-
|
488
|
-
To cache forever, don't configure anything
|
489
|
-
|
490
|
-
```ruby
|
491
|
-
class CartCell < Cell::Rails
|
492
|
-
cache :show
|
493
|
-
|
494
|
-
def show
|
495
|
-
render
|
496
|
-
end
|
497
|
-
```
|
498
634
|
|
499
|
-
This will run `#show` only once, after that the rendered view comes from the cache.
|
500
|
-
|
501
|
-
|
502
|
-
### Cache Options
|
503
|
-
|
504
|
-
Note that you can pass arbitrary options through to your cache store. Symbols are evaluated as instance methods, callable objects (e.g. lambdas) are evaluated in the cell instance context allowing you to call instance methods and access instance variables. All arguments passed to your state (e.g. via `render_cell`) are propagated to the block.
|
505
|
-
|
506
|
-
```ruby
|
507
|
-
cache :show, :expires_in => 10.minutes
|
508
|
-
```
|
509
|
-
|
510
|
-
If you need dynamic options evaluated at render-time, use a lambda.
|
511
|
-
|
512
|
-
```ruby
|
513
|
-
cache :show, :tags => lambda { |*args| tags }
|
514
|
-
```
|
515
|
-
|
516
|
-
If you don't like blocks, use instance methods instead.
|
517
|
-
|
518
|
-
```ruby
|
519
|
-
class CartCell < Cell::Rails
|
520
|
-
cache :show, :tags => :cache_tags
|
521
|
-
|
522
|
-
def cache_tags(*args)
|
523
|
-
# do your magic..
|
524
|
-
end
|
525
|
-
```
|
526
|
-
|
527
|
-
### Conditional Caching
|
528
|
-
|
529
|
-
The +:if+ option lets you define a condition. If it doesn't return a true value, caching for that state is skipped.
|
530
|
-
|
531
|
-
```ruby
|
532
|
-
cache :show, :if => lambda { |*| has_changed? }
|
533
|
-
```
|
534
|
-
|
535
|
-
### Cache Keys
|
536
|
-
|
537
|
-
You can expand the state's cache key by appending a versioner block to the `::cache` call. This way you can expire state caches yourself.
|
538
|
-
|
539
|
-
```ruby
|
540
|
-
class CartCell < Cell::Rails
|
541
|
-
cache :show do |options|
|
542
|
-
order.id
|
543
|
-
end
|
544
|
-
```
|
545
|
-
|
546
|
-
The versioner block is executed in the cell instance context, allowing you to access all stakeholder objects you need to compute a cache key. The return value is appended to the state key: `"cells/cart/show/1"`.
|
547
|
-
|
548
|
-
As everywhere in Rails, you can also return an array.
|
549
|
-
|
550
|
-
```ruby
|
551
|
-
class CartCell < Cell::Rails
|
552
|
-
cache :show do |options|
|
553
|
-
[id, options[:items].md5]
|
554
|
-
end
|
555
|
-
```
|
556
|
-
|
557
|
-
Resulting in: `"cells/cart/show/1/0ecb1360644ce665a4ef"`.
|
558
|
-
|
559
|
-
|
560
|
-
### Debugging Cache
|
561
|
-
|
562
|
-
When caching is turned on, you might wanna see notifications. Just like a controller, Cells gives you the following notifications.
|
563
|
-
|
564
|
-
* `write_fragment.action_controller` for cache miss.
|
565
|
-
* `read_fragment.action_controller` for cache hits.
|
566
|
-
|
567
|
-
To activate notifications, include the `Notifications` module in your cell.
|
568
|
-
|
569
|
-
```ruby
|
570
|
-
class Comment::Cell < Cell::Rails
|
571
|
-
include Cell::Caching::Notifications
|
572
|
-
```
|
573
|
-
|
574
|
-
### Inheritance
|
575
|
-
|
576
|
-
Cache configuration is inherited to derived cells.
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
### A Note On Fragment Caching
|
581
|
-
|
582
|
-
Fragment caching is [not implemented in Cells per design](http://nicksda.apotomo.de/2011/02/rails-misapprehensions-caching-views-is-not-the-views-job/) - Cells tries to move caching to the class layer enforcing an object-oriented design rather than cluttering your views with caching blocks.
|
583
|
-
|
584
|
-
If you need to cache a part of your view, implement that as another cell state.
|
585
|
-
|
586
|
-
|
587
|
-
### Testing Caching
|
588
|
-
|
589
|
-
If you want to test it in `development`, you need to put `config.action_controller.perform_caching = true` in `development.rb` to see the effect.
|
590
|
-
|
591
|
-
|
592
|
-
## Testing
|
593
|
-
|
594
|
-
Another big advantage compared to monolithic controller/helper/partial piles is the ability to test your cells isolated.
|
595
|
-
|
596
|
-
### Test::Unit
|
597
|
-
|
598
|
-
So what if you wanna test the cart cell? Use the generated `test/cells/cart_cell_test.rb` test.
|
599
|
-
|
600
|
-
```ruby
|
601
|
-
class CartCellTest < Cell::TestCase
|
602
|
-
test "show" do
|
603
|
-
invoke :show, :user => @user_fixture
|
604
|
-
assert_select "#cart", "You have 3 items in your shopping cart."
|
605
|
-
end
|
606
|
-
```
|
607
|
-
|
608
|
-
Don't forget to put `require 'cell/test_case'` in your project's `test/test_helper.rb` file.
|
609
|
-
|
610
|
-
Then, run your tests with
|
611
|
-
|
612
|
-
```shell
|
613
|
-
rake test:cells
|
614
|
-
```
|
615
|
-
|
616
|
-
That's easy, clean and strongly improves your component-driven software quality. How'd you do that with partials?
|
617
|
-
|
618
|
-
|
619
|
-
### RSpec
|
620
|
-
|
621
|
-
If you prefer RSpec examples, use the [rspec-cells](http://github.com/apotonick/rspec-cells) gem for specing.
|
622
|
-
|
623
|
-
```ruby
|
624
|
-
it "should render the posts count" do
|
625
|
-
render_cell(:posts, :count).should have_selector("p", :content => "4 posts!")
|
626
|
-
end
|
627
|
-
```
|
628
|
-
|
629
|
-
To run your specs we got a rake task, too!
|
630
|
-
|
631
|
-
```shell
|
632
|
-
rake spec:cells
|
633
|
-
```
|
634
635
|
|
635
636
|
|
636
637
|
### Call
|
@@ -743,48 +744,7 @@ end
|
|
743
744
|
|
744
745
|
This will simply render the `author.haml` template in the same context as the `show` view, meaning you might use helpers, again.
|
745
746
|
|
746
|
-
### Encapsulation
|
747
747
|
|
748
|
-
If in doubt, encapsulate nested parts of your view into a separate cell. You can use the `#cell` method in your cell to instantiate a nested cell.
|
749
|
-
|
750
|
-
Designing view models to create kickass UIs for your domain layer is discussed in 50+ pages in [my upcoming book](http://nicksda.apotomo.de).
|
751
|
-
|
752
|
-
### Alternative Instantiation
|
753
|
-
|
754
|
-
You don't need to pass in a model, it can also be a hash for a composition.
|
755
|
-
|
756
|
-
```ruby
|
757
|
-
cell(album, song: song, composer: album.composer)
|
758
|
-
```
|
759
|
-
|
760
|
-
This will create two readers in the cell for you automatically: `#song` and `#composer`.
|
761
|
-
|
762
|
-
|
763
|
-
Note that we are still working on a declarative API for compositions. It will be similar to the one found in Reform, Disposable::Twin and Representable:
|
764
|
-
|
765
|
-
```ruby
|
766
|
-
property :title, on: :song
|
767
|
-
property :last_name, on: :composer
|
768
|
-
```
|
769
|
-
|
770
|
-
|
771
|
-
## Mountable Cells
|
772
|
-
|
773
|
-
Cells 3.8 got rid of the ActionController dependency. This essentially means you can mount Cells to routes or use them like a Rack middleware. All you need to do is derive from Cell::Base.
|
774
|
-
|
775
|
-
```ruby
|
776
|
-
class PostCell < Cell::Base
|
777
|
-
...
|
778
|
-
end
|
779
|
-
```
|
780
|
-
|
781
|
-
In your `routes.rb` file, mount the cell like a Rack app.
|
782
|
-
|
783
|
-
```ruby
|
784
|
-
match "/posts" => proc { |env|
|
785
|
-
[ 200, {}, [ Cell::Base.render_cell_for(:post, :show) ]]
|
786
|
-
}
|
787
|
-
```
|
788
748
|
|
789
749
|
### Cells in ActionMailer
|
790
750
|
|
@@ -815,21 +775,6 @@ module MyApp
|
|
815
775
|
end
|
816
776
|
```
|
817
777
|
|
818
|
-
### Base Path
|
819
|
-
|
820
|
-
You can configure the cells path in case your cells don't reside in `app/cells`.
|
821
|
-
|
822
|
-
```ruby
|
823
|
-
config.generators do |g|
|
824
|
-
g.base_cell_path "app/widgets"
|
825
|
-
end
|
826
|
-
```
|
827
|
-
|
828
|
-
|
829
|
-
## Capture Support
|
830
|
-
|
831
|
-
If you need a global `#content_for` use the [cells-capture](https://github.com/apotonick/cells-capture) gem.
|
832
|
-
|
833
778
|
|
834
779
|
## Undocumented Features
|
835
780
|
|