cells 3.11.3 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +15 -13
- data/Appraisals +23 -0
- data/CHANGES.md +20 -3
- data/Gemfile +5 -7
- data/README.md +317 -236
- data/Rakefile +1 -1
- data/TODO.md +3 -0
- data/cells.gemspec +19 -28
- data/gemfiles/rails3.2.gemfile +14 -0
- data/gemfiles/rails4.0.gemfile +14 -0
- data/gemfiles/rails4.1.gemfile +15 -0
- data/gemfiles/rails4.2.gemfile +14 -0
- data/lib/cell.rb +28 -21
- data/lib/cell/caching.rb +10 -22
- data/lib/cell/caching/notification.rb +15 -0
- data/lib/cell/concept.rb +4 -69
- data/lib/cell/development.rb +11 -0
- data/lib/{cells → cell}/engines.rb +1 -1
- data/lib/cell/layout.rb +20 -0
- data/lib/cell/partial.rb +17 -0
- data/lib/cell/{base/prefixes.rb → prefixes.rb} +1 -1
- data/lib/cell/rails.rb +58 -50
- data/lib/cell/railtie.rb +60 -0
- data/lib/cell/{base/self_contained.rb → self_contained.rb} +1 -1
- data/lib/cell/templates.rb +60 -0
- data/lib/cell/test_case.rb +4 -162
- data/lib/cell/testing.rb +15 -0
- data/lib/cell/twin.rb +11 -29
- data/lib/cell/version.rb +10 -0
- data/lib/cell/view_model.rb +196 -88
- data/lib/cells.rb +1 -20
- data/lib/rails/generators/cell/cell_generator.rb +43 -0
- data/lib/rails/generators/cell/templates/cell.rb.erb +8 -0
- data/lib/{generators/templates/concept → rails/generators/cell/templates}/view.erb +0 -0
- data/lib/{generators/templates/concept → rails/generators/cell/templates}/view.haml +0 -0
- data/lib/rails/generators/cell/templates/view.slim +2 -0
- data/lib/rails/generators/concept/concept_generator.rb +38 -0
- data/lib/{generators/templates/concept/cell.rb → rails/generators/concept/templates/concept.rb.erb} +2 -2
- data/lib/{generators → rails/generators/concept}/templates/view.erb +0 -0
- data/lib/{generators → rails/generators/concept}/templates/view.haml +0 -0
- data/lib/rails/generators/concept/templates/view.slim +2 -0
- data/lib/rails/generators/test_unit/cell/cell_generator.rb +21 -0
- data/lib/{generators/templates/cell_test.rb → rails/generators/test_unit/cell/templates/unit_test.rb.erb} +3 -3
- data/lib/rails/generators/test_unit/concept/concept_generator.rb +21 -0
- data/lib/rails/generators/test_unit/concept/templates/unit_test.rb.erb +11 -0
- data/lib/{cells → tasks}/cells.rake +0 -0
- data/test/builder_test.rb +58 -0
- data/test/caching_test.rb +298 -0
- data/test/cell_benchmark.rb +32 -0
- data/test/cell_generator_test.rb +51 -82
- data/test/cell_test.rb +8 -23
- data/test/concept_generator_test.rb +22 -13
- data/test/concept_test.rb +41 -75
- data/test/dummy/app/views/musician/hamlet.html.erb +1 -0
- data/test/dummy/config/application.rb +21 -8
- data/test/{app/cells/bassist/play.html.erb → fixtures/bassist/play.erb} +0 -0
- data/test/fixtures/concepts/record/views/layout.erb +1 -0
- data/test/{app → fixtures}/concepts/record/views/show.erb +0 -0
- data/test/{app → fixtures}/concepts/record/views/song.erb +0 -0
- data/test/fixtures/inherit_views_test/popper/tap.erb +1 -0
- data/test/fixtures/inherit_views_test/tapper/play.erb +1 -0
- data/test/fixtures/inherit_views_test/tapper/tap.erb +1 -0
- data/test/fixtures/partial_test/with_partial/show.erb +1 -0
- data/test/fixtures/partials/_show.html.erb +1 -0
- data/test/fixtures/partials/_show.xml.erb +1 -0
- data/test/fixtures/song/ivar.erb +1 -0
- data/test/fixtures/song/show.erb +1 -0
- data/test/fixtures/song/with_erb.erb +4 -0
- data/test/fixtures/song/with_html.erb +1 -0
- data/test/fixtures/song/with_locals.erb +2 -0
- data/test/fixtures/song_with_layout/happy.erb +1 -0
- data/test/fixtures/song_with_layout/merry.erb +1 -0
- data/test/fixtures/song_with_layout/show.erb +1 -0
- data/test/fixtures/song_with_layout/show_with_layout.erb +1 -0
- data/test/fixtures/templates_caching_test/song/show.erb +1 -0
- data/test/fixtures/url_helper_test/song/edit.erb +8 -0
- data/test/fixtures/url_helper_test/song/with_block.erb +2 -0
- data/test/fixtures/url_helper_test/song/with_capture.erb +4 -0
- data/test/fixtures/url_helper_test/song/with_content_tag.erb +6 -0
- data/test/fixtures/url_helper_test/song/with_form_for_block.erb +3 -0
- data/test/fixtures/url_helper_test/song/with_link_to.erb +3 -0
- data/test/layout_test.rb +57 -0
- data/test/partial_test.rb +27 -0
- data/test/prefixes_test.rb +36 -10
- data/test/public_test.rb +42 -0
- data/test/rails_extensions_test.rb +51 -0
- data/test/render_test.rb +103 -0
- data/test/templates_test.rb +45 -0
- data/test/test_case_test.rb +21 -122
- data/test/test_helper.rb +37 -33
- data/test/twin_test.rb +3 -7
- data/test/url_helper_test.rb +89 -0
- metadata +92 -357
- data/gemfiles/Gemfile.rails3-0 +0 -7
- data/gemfiles/Gemfile.rails3-1 +0 -7
- data/gemfiles/Gemfile.rails3-2 +0 -7
- data/gemfiles/Gemfile.rails4-0 +0 -12
- data/gemfiles/Gemfile.rails4-1 +0 -12
- data/lib/cell/base.rb +0 -82
- data/lib/cell/base/view.rb +0 -15
- data/lib/cell/builder.rb +0 -71
- data/lib/cell/deprecations.rb +0 -41
- data/lib/cell/dsl.rb +0 -7
- data/lib/cell/rack.rb +0 -32
- data/lib/cell/rails/helper_api.rb +0 -37
- data/lib/cell/rails/view_model.rb +0 -159
- data/lib/cell/rails3_0_strategy.rb +0 -82
- data/lib/cell/rails3_1_strategy.rb +0 -40
- data/lib/cell/rails4_0_strategy.rb +0 -39
- data/lib/cell/rails4_1_strategy.rb +0 -40
- data/lib/cell/rendering.rb +0 -109
- data/lib/cells/rails.rb +0 -86
- data/lib/cells/railtie.rb +0 -38
- data/lib/cells/version.rb +0 -3
- data/lib/generators/USAGE +0 -30
- data/lib/generators/cells/base.rb +0 -22
- data/lib/generators/cells/cell_generator.rb +0 -15
- data/lib/generators/cells/view_generator.rb +0 -18
- data/lib/generators/erb/cell_generator.rb +0 -15
- data/lib/generators/erb/concept_generator.rb +0 -17
- data/lib/generators/haml/cell_generator.rb +0 -17
- data/lib/generators/haml/concept_generator.rb +0 -17
- data/lib/generators/rails/cell_generator.rb +0 -16
- data/lib/generators/rails/concept_generator.rb +0 -16
- data/lib/generators/slim/cell_generator.rb +0 -17
- data/lib/generators/templates/cell.rb +0 -9
- data/lib/generators/templates/view.slim +0 -4
- data/lib/generators/test_unit/cell_generator.rb +0 -14
- data/lib/generators/trailblazer/base.rb +0 -21
- data/lib/generators/trailblazer/view_generator.rb +0 -18
- data/test/app/cells/album/views/cover.haml +0 -1
- data/test/app/cells/bad_guitarist/_dii.html.erb +0 -1
- data/test/app/cells/bad_guitarist_cell.rb +0 -2
- data/test/app/cells/bassist/_dii.html.erb +0 -1
- data/test/app/cells/bassist/ahem.html.erb +0 -1
- data/test/app/cells/bassist/compose.html.erb +0 -1
- data/test/app/cells/bassist/contact_form.html.erb +0 -1
- data/test/app/cells/bassist/form_for.erb +0 -3
- data/test/app/cells/bassist/form_for_in_haml.haml +0 -2
- data/test/app/cells/bassist/jam.html.erb +0 -3
- data/test/app/cells/bassist/play.js.erb +0 -1
- data/test/app/cells/bassist/pose.html.erb +0 -1
- data/test/app/cells/bassist/promote.html.erb +0 -1
- data/test/app/cells/bassist/provoke.html.erb +0 -1
- data/test/app/cells/bassist/shout.html.erb +0 -1
- data/test/app/cells/bassist/sing.html.haml +0 -1
- data/test/app/cells/bassist/slap.html.erb +0 -1
- data/test/app/cells/bassist/yell.en.html.erb +0 -1
- data/test/app/cells/bassist_cell.rb +0 -25
- data/test/app/cells/club_security.rb +0 -2
- data/test/app/cells/club_security/guard/help.html.erb +0 -1
- data/test/app/cells/club_security/guard_cell.rb +0 -6
- data/test/app/cells/club_security/medic/help.html.erb +0 -1
- data/test/app/cells/club_security/medic_cell.rb +0 -8
- data/test/app/cells/layouts/b.erb +0 -1
- data/test/app/cells/layouts/metal.html.erb +0 -1
- data/test/app/cells/rails_helper_api_test/bassist/edit.html.erb +0 -5
- data/test/app/cells/shouter/sing.html.erb +0 -1
- data/test/app/cells/song/dashboard.haml +0 -7
- data/test/app/cells/song/details.html.haml +0 -1
- data/test/app/cells/song/info.html.haml +0 -1
- data/test/app/cells/song/lyrics.html.haml +0 -6
- data/test/app/cells/song/plays.haml +0 -1
- data/test/app/cells/song/scale.haml +0 -1
- data/test/app/cells/song/show.html.haml +0 -3
- data/test/app/cells/song/title.html.haml +0 -1
- data/test/app/cells/trumpeter/promote.html.erb +0 -1
- data/test/app/cells/trumpeter_cell.rb +0 -8
- data/test/app/cells/view_model_test/comments/show.haml +0 -7
- data/test/app/concepts/record/views/layout.haml +0 -2
- data/test/app/views/shared/_dong.html.erb +0 -1
- data/test/cell_module_test.rb +0 -170
- data/test/cells_module_test.rb +0 -27
- data/test/deprecations_test.rb +0 -101
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/musician/hamlet.html.haml +0 -1
- data/test/dummy/config/environments/development.rb +0 -16
- data/test/dummy/config/environments/production.rb +0 -46
- data/test/dummy/config/environments/test.rb +0 -33
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/label/app/cells/label/show.erb +0 -1
- data/test/dummy/label/app/cells/label_cell.rb +0 -5
- data/test/dummy/label/label.gemspec +0 -20
- data/test/dummy/label/lib/label.rb +0 -4
- data/test/dummy/label/lib/label/version.rb +0 -3
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/helper_test.rb +0 -81
- data/test/rack_test.rb +0 -32
- data/test/rails/asset_pipeline_test.rb +0 -20
- data/test/rails/caching_test.rb +0 -456
- data/test/rails/cells_test.rb +0 -119
- data/test/rails/forms_test.rb +0 -75
- data/test/rails/integration_test.rb +0 -299
- data/test/rails/render_test.rb +0 -189
- data/test/rails/view_model_test.rb +0 -226
- data/test/rails/view_test.rb +0 -49
- data/test/rails_helper_api_test.rb +0 -58
- data/test/self_contained_test.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26d77a00fc2075f5869dedf7b99b55afc2df0eec
|
4
|
+
data.tar.gz: 139402563ae105fc1c501f2a848a7b7f0491452d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b5d3fae08a561a5b24d976e137d8e91d9172487318d4621d281295cd45eecd61ac81820aa27c64afc3a9385e891354efbc1c8c371daeb14b0dabf9d3c121f9d
|
7
|
+
data.tar.gz: c933f9a2a9e4fc9850ad5257e725e5070a2b248070dfa0bd012d095016b6866adea340a85910b09fe65c1352911d838c29e63e92c5e4ee1525f90fb9ef7bcd95
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
+
language: ruby
|
1
2
|
rvm:
|
2
|
-
-
|
3
|
+
# - 2.2
|
4
|
+
- 2.1
|
3
5
|
- 2.0.0
|
6
|
+
- 1.9.3
|
4
7
|
- jruby-19mode
|
8
|
+
|
9
|
+
sudo: false
|
10
|
+
|
11
|
+
bundler_args: '--without local_development --jobs 3 --retry 3'
|
12
|
+
|
13
|
+
script: bundle exec rake
|
14
|
+
|
5
15
|
notifications:
|
6
16
|
irc: "irc.freenode.org#cells"
|
7
17
|
gemfile:
|
8
|
-
- gemfiles/
|
9
|
-
- gemfiles/
|
10
|
-
- gemfiles/
|
11
|
-
- gemfiles/
|
12
|
-
- gemfiles/Gemfile.rails4-1
|
13
|
-
|
14
|
-
matrix:
|
15
|
-
exclude:
|
16
|
-
- rvm: 2.0.0
|
17
|
-
gemfile: gemfiles/Gemfile.rails3-0
|
18
|
-
- rvm: 2.0.0
|
19
|
-
gemfile: gemfiles/Gemfile.rails3-1
|
18
|
+
- gemfiles/rails4.2.gemfile
|
19
|
+
- gemfiles/rails4.1.gemfile
|
20
|
+
- gemfiles/rails4.0.gemfile
|
21
|
+
- gemfiles/rails3.2.gemfile
|
data/Appraisals
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
appraise 'rails3.2' do
|
2
|
+
gem 'railties', github: 'rails/rails', branch: '3-2-stable'
|
3
|
+
gem 'tzinfo'
|
4
|
+
gem 'minitest', '4.7.5'
|
5
|
+
end
|
6
|
+
|
7
|
+
appraise "rails4.0" do
|
8
|
+
gem 'railties', github: 'rails/rails', branch: '4-0-stable'
|
9
|
+
gem 'activemodel', github: 'rails/rails', branch: '4-0-stable'
|
10
|
+
gem 'minitest', '4.7.5'
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise "rails4.1" do
|
14
|
+
gem 'railties', github: 'rails/rails', branch: '4-1-stable'
|
15
|
+
gem 'activemodel', github: 'rails/rails', branch: '4-1-stable'
|
16
|
+
gem 'minitest', '~> 5.2'
|
17
|
+
end
|
18
|
+
|
19
|
+
appraise "rails4.2" do
|
20
|
+
gem 'railties', github: 'rails/rails', branch: '4-2-stable'
|
21
|
+
gem 'activemodel', github: 'rails/rails', branch: '4-2-stable'
|
22
|
+
gem 'minitest', '~> 5.2'
|
23
|
+
end
|
data/CHANGES.md
CHANGED
@@ -1,6 +1,23 @@
|
|
1
|
-
##
|
1
|
+
## 4.0.0
|
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.
|
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
|
+
* `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.
|
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
|
+
If you need to render a specific mime type, provide the filename: `render view: "show.html"`.
|
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.
|
11
|
+
* Builders are now defined using `::builds`, not `::build`.
|
12
|
+
|
13
|
+
### Removed
|
14
|
+
|
15
|
+
* `Cell::Rails` and `Cell::Base` got removed. Every cell is `ViewModel` or `Concept` now.
|
16
|
+
|
17
|
+
### Internals
|
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.
|
2
20
|
|
3
|
-
* Make Cells work with Rails 4.2. Thanks to @pwim and @did!
|
4
21
|
|
5
22
|
## 3.11.2
|
6
23
|
|
@@ -36,8 +53,8 @@
|
|
36
53
|
* Concept cells look for layouts in their self-contained views directory.
|
37
54
|
* Add generator for Concept cells: `rails g concept Comment`
|
38
55
|
|
39
|
-
## 3.10.1
|
40
56
|
|
57
|
+
## 3.10.1
|
41
58
|
Allow packaging assets for Rails' asset pipeline into cells. This is still experimental but works great. I love it.
|
42
59
|
|
43
60
|
## 3.10.0
|
data/Gemfile
CHANGED
@@ -2,11 +2,9 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
|
5
|
+
gem 'minitest-reporters'
|
6
|
+
gem 'pry-byebug' , platforms: [:mri_20, :mri_21]
|
7
|
+
gem 'appraisal'
|
8
|
+
gem 'rake'
|
9
|
+
gem 'test_xml'
|
6
10
|
|
7
|
-
gem "rails", "~> 3.2.12"
|
8
|
-
gem 'minitest', '4.7.5'
|
9
|
-
|
10
|
-
# gem 'uber', :path => "../uber"
|
11
|
-
|
12
|
-
gem 'disposable', path: '../disposable'
|
data/README.md
CHANGED
@@ -5,28 +5,57 @@
|
|
5
5
|
|
6
6
|
## Overview
|
7
7
|
|
8
|
-
Cells allow you to encapsulate parts of your page into
|
8
|
+
Cells allow you to encapsulate parts of your page into separate MVC components. These components are called _view models_.
|
9
9
|
|
10
|
-
|
10
|
+
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`.
|
11
11
|
|
12
|
-
|
12
|
+
As you have already noticed we use _cell_ and _view model_ interchangeably here.
|
13
13
|
|
14
14
|
|
15
|
-
##
|
15
|
+
## The Book
|
16
16
|
|
17
|
-
|
17
|
+
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.
|
18
18
|
|
19
|
-
|
19
|
+
<a href="https://leanpub.com/trailblazer">
|
20
|
+
![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
|
21
|
+
</a>
|
22
|
+
|
23
|
+
* Basic view models, replacing helpers, and how to structure your view into cell components (chapter 2 and 3).
|
24
|
+
* Advanced Cells API (chapter 3 and 6).
|
25
|
+
* Testing Cells (chapter 3 and 6).
|
26
|
+
* Cells Pagination with AJAX (chapter 6).
|
27
|
+
* View Caching and Expiring (chapter 7).
|
28
|
+
|
29
|
+
More chapters are coming.
|
30
|
+
|
31
|
+
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.
|
32
|
+
|
33
|
+
## No ActionView
|
34
|
+
|
35
|
+
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%!
|
36
|
+
|
37
|
+
**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).
|
20
38
|
|
21
39
|
|
22
40
|
## Installation
|
23
41
|
|
24
|
-
Cells run with all Rails >= 3.
|
42
|
+
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.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
gem 'cells', "~> 4.0.0"
|
46
|
+
```
|
47
|
+
|
48
|
+
## Prerequisites
|
49
|
+
|
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).
|
25
51
|
|
26
52
|
```ruby
|
27
|
-
gem 'cells'
|
53
|
+
gem "cells-haml", github: 'trailblazer/cells-haml'
|
28
54
|
```
|
29
55
|
|
56
|
+
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.
|
57
|
+
|
58
|
+
|
30
59
|
## File Layout
|
31
60
|
|
32
61
|
Cells are placed in `app/cells`.
|
@@ -43,91 +72,306 @@ app
|
|
43
72
|
|
44
73
|
## Generate
|
45
74
|
|
46
|
-
|
75
|
+
Use the bundled generator to set up a cell.
|
47
76
|
|
48
77
|
```shell
|
49
|
-
rails generate cell
|
78
|
+
rails generate cell comment
|
50
79
|
```
|
51
80
|
|
52
81
|
```
|
53
82
|
create app/cells/
|
54
|
-
create app/cells/
|
55
|
-
create app/cells/
|
56
|
-
create app/cells/
|
57
|
-
create test/cells/cart_test.rb
|
83
|
+
create app/cells/comment
|
84
|
+
create app/cells/comment_cell.rb
|
85
|
+
create app/cells/comment/show.erb
|
58
86
|
```
|
59
87
|
|
60
|
-
That looks very familiar.
|
61
88
|
|
62
|
-
##
|
89
|
+
## Rendering View Models
|
63
90
|
|
64
|
-
|
91
|
+
Suppose we are to render a "partial" for `Comment` model
|
65
92
|
|
66
|
-
```
|
67
|
-
|
68
|
-
<%= render_cell :cart, :show, :user => @current_user %>
|
93
|
+
```ruby
|
94
|
+
@comment = Comment.find(1)
|
69
95
|
```
|
70
96
|
|
71
|
-
|
97
|
+
Cells brings you one helper method `#cell` to be used in your controller views or layouts.
|
72
98
|
|
73
|
-
|
99
|
+
```haml
|
100
|
+
= cell(:comment, @comment)
|
101
|
+
```
|
74
102
|
|
75
|
-
|
103
|
+
This is the short form of rendering a cell. Simple, isn't it?
|
76
104
|
|
77
|
-
|
78
|
-
class CartCell < Cell::Rails
|
79
|
-
def show(args)
|
80
|
-
user = args[:user]
|
81
|
-
@items = user.items_in_cart
|
105
|
+
Note that a view model _always_ requires a model in the constructor (or a composition). This doesn't have to be an `ActiveRecord` object but can be any type of Ruby object you want to present.
|
82
106
|
|
83
|
-
|
107
|
+
To understand invoking cells, here's the long form of it.
|
108
|
+
|
109
|
+
```haml
|
110
|
+
= cell(:comment, @comment).call(:show)
|
111
|
+
```
|
112
|
+
|
113
|
+
1. `#cell(..)` simply returns the cell instance. You can do whatever you want with it.
|
114
|
+
2. `.call(:show)` will invoke the `#show` method respecting caching settings.
|
115
|
+
|
116
|
+
When rendering cells in views, you can skip the `call` part as this is implicitely done by the template.
|
117
|
+
|
118
|
+
Please [refer to the docs](#invocation-styles) for different ways of invoking view models.
|
119
|
+
|
120
|
+
|
121
|
+
## View Model Classes
|
122
|
+
|
123
|
+
A view model is always implemented as a class. This gives you encapsulation, proper inheritance and namespacing out-of-the-box.
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
class CommentCell < Cell::ViewModel
|
127
|
+
def show
|
128
|
+
render
|
84
129
|
end
|
85
130
|
end
|
86
131
|
```
|
87
132
|
|
88
|
-
|
133
|
+
Calling `#render` will render the cell's `show.haml` template, located in `app/cells/comment`. Invoking `render` is explicit: this means, it really returns the rendered view string, allowing you to modify the HTML afterwards.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
def show
|
137
|
+
"<div>" + render + "</div>"
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
## Views In Theory
|
142
|
+
|
143
|
+
In Cells, we don't distinguish between _view_ or _partial_. Every view you render is a partial, every partial a view. You can render views inside views, compose complex UI blocks with multiple templates and go crazy. This is what cells _views_ are made for.
|
144
|
+
|
145
|
+
Cells supports all template engines that are supported by the excellent [tilt](https://github.com/rtomayko/tilt) gem - namely, this is ERB, HAML, Slim, and many more.
|
146
|
+
|
147
|
+
In these examples, we're using HAML.
|
148
|
+
|
149
|
+
BTW, Cells doesn't include the format into the view name. 99% of all cells render HTML anyway, so we prefer short names like `show.haml`.
|
89
150
|
|
90
151
|
|
91
|
-
## Views
|
152
|
+
## Views In Practice
|
92
153
|
|
93
|
-
|
154
|
+
Let's check out the `show.haml` view to see how they work.
|
94
155
|
|
95
156
|
```haml
|
96
|
-
|
97
|
-
|
157
|
+
-# app/cells/comment/show.haml
|
158
|
+
|
159
|
+
%h1 Comment
|
160
|
+
|
161
|
+
= model.body
|
162
|
+
By
|
163
|
+
= link_to model.author.name, model.author
|
98
164
|
```
|
99
165
|
|
100
|
-
|
166
|
+
Cells provides you the view _model_ via the `#model` method. Here, this returns the `Comment` instance passed into the constructor.
|
167
|
+
|
168
|
+
Of course, this view is a mess and needs be get cleaned up!
|
169
|
+
|
170
|
+
## Logicless Views
|
101
171
|
|
102
|
-
|
172
|
+
This is how a typical view looks in a view model.
|
103
173
|
|
104
|
-
|
174
|
+
```haml
|
175
|
+
-# app/cells/comment/show.haml
|
176
|
+
|
177
|
+
%h1 Comment
|
105
178
|
|
106
|
-
|
179
|
+
= body
|
180
|
+
By
|
181
|
+
= author_link
|
182
|
+
```
|
183
|
+
|
184
|
+
The methods we call in the view now need to be defined in the cell instance.
|
107
185
|
|
108
186
|
```ruby
|
109
|
-
class
|
110
|
-
|
187
|
+
class CommentCell < Cell::ViewModel
|
188
|
+
def show
|
189
|
+
render
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def body
|
195
|
+
model.body
|
196
|
+
end
|
197
|
+
|
198
|
+
def author_link
|
199
|
+
link_to model.author.name, model.author
|
200
|
+
end
|
201
|
+
end
|
202
|
+
```
|
203
|
+
|
204
|
+
See how you can use helpers in a cell instance?
|
205
|
+
|
206
|
+
## No Helpers
|
207
|
+
|
208
|
+
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.
|
209
|
+
|
210
|
+
Helpers as known from conventional Rails where methods and variables get copied between view and controller no longer exist in Cells.
|
211
|
+
|
212
|
+
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.
|
213
|
+
|
214
|
+
## Automatic Properties
|
215
|
+
|
216
|
+
Often, as in the `#body` method, you simply need to delegate properties from the model. This can be done automatically using `::property`.
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
class CommentCell < Cell::ViewModel
|
220
|
+
def show
|
221
|
+
render
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
property :body
|
226
|
+
property :author
|
227
|
+
|
228
|
+
def author_link
|
229
|
+
link_to author.name, author
|
230
|
+
end
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
Readers are automatically created when defined with `::property`.
|
235
|
+
|
236
|
+
|
237
|
+
## Render
|
238
|
+
|
239
|
+
multiple times allowed
|
240
|
+
:view
|
241
|
+
:format ".html"
|
242
|
+
template_engine
|
243
|
+
view_paths
|
244
|
+
|
245
|
+
|
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)
|
111
252
|
```
|
112
253
|
|
113
|
-
|
254
|
+
As `:show` is the default action, you don't have to specify it.
|
114
255
|
|
115
|
-
|
256
|
+
```ruby
|
257
|
+
cell(:comment, @comment).call
|
258
|
+
```
|
116
259
|
|
117
|
-
In
|
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.
|
118
261
|
|
119
262
|
```haml
|
120
|
-
|
263
|
+
= cell(:comment, @comment)
|
264
|
+
```
|
121
265
|
|
122
|
-
|
266
|
+
If you want, you can also call public methods directly on your cell. Note that this does _not_ respect caching, though.
|
123
267
|
|
124
|
-
|
125
|
-
|
268
|
+
```haml
|
269
|
+
= cell(:comment, @comment).avatar
|
126
270
|
```
|
127
271
|
|
128
|
-
##
|
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
|
+
|
308
|
+
## Collections
|
309
|
+
|
310
|
+
You can render a collection of models where each item is rendered using a cell.
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
= cell(:song, collection: Song.all)
|
314
|
+
```
|
315
|
+
|
316
|
+
Note that there is no `.call` needed. This is identical to the following snippet.
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
- Song.all.each do |song|
|
320
|
+
= cell(:song, song).call(:show)
|
321
|
+
```
|
322
|
+
|
323
|
+
Options are passed to every cell.
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
= cell(:song, collection: Song.all, genre: "Heavy Metal", user: current_user)
|
327
|
+
```
|
328
|
+
|
329
|
+
The collection invocation per default calls `#show`. Use `:method` if you need another method to be called.
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
= cell(:song, collection: Song.all, method: :detail)
|
333
|
+
```
|
334
|
+
|
335
|
+
## Builder
|
336
|
+
|
337
|
+
Often, it is good practice to replace decider code from views or classes into separate sub-cells. Or in case you want to render a polymorphic collection, builders come in handy.
|
338
|
+
|
339
|
+
Builders allow instantiating different cell classes for different models and options.
|
340
|
+
|
341
|
+
```ruby
|
342
|
+
class SongCell < Cell::ViewModel
|
343
|
+
builds do |model, options|
|
344
|
+
HitCell if model.is_a?(Hit)
|
345
|
+
EverGreenCell if model.is_a?(Evergreen)
|
346
|
+
end
|
347
|
+
|
348
|
+
def show
|
349
|
+
# ..
|
350
|
+
end
|
351
|
+
```
|
352
|
+
|
353
|
+
The `#cell` helpers takes care of instantiating the right cell class for you.
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
cell(:song, Hit.find(1)) #=> creates a HitCell.
|
357
|
+
```
|
358
|
+
|
359
|
+
This also works with collections.
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
cell(:song, collection: [@hit, @song]) #=> renders HitCell, then SongCell.
|
363
|
+
```
|
364
|
+
|
365
|
+
Multiple calls to `::builds` will be ORed. If no block returns a class, the original class will be used (`SongCell`). Builders are inherited.
|
366
|
+
|
367
|
+
|
368
|
+
## View Inheritance
|
369
|
+
|
129
370
|
|
130
|
-
|
371
|
+
|
372
|
+
# TODO: merge stuff below!
|
373
|
+
|
374
|
+
## File Structure
|
131
375
|
|
132
376
|
In Cells 3.10 we introduce a new _optional_ file structure integrating with [Trailblazer](https://github.com/apotonick/trailblazer)'s "concept-oriented" layout.
|
133
377
|
|
@@ -209,22 +453,6 @@ Adding the assets files to the asset pipeline currently involves two steps (I kn
|
|
209
453
|
|
210
454
|
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.
|
211
455
|
|
212
|
-
### Rendering Global Partials
|
213
|
-
|
214
|
-
Sometimes you need to render a global partial from `app/views` within a cell. For instance, the `gmaps4rails` helper depends on a global partial. While this breaks encapsulation it's still possible in cells - just add the global view path.
|
215
|
-
|
216
|
-
```ruby
|
217
|
-
class MapCell < Cell::Rails
|
218
|
-
append_view_path "app/views"
|
219
|
-
|
220
|
-
def show
|
221
|
-
render partial: 'shared/map_form'
|
222
|
-
end
|
223
|
-
```
|
224
|
-
|
225
|
-
Note that you have to use `render partial:` which will then look in the global view directory and render the partial found at `app/views/shared/map_form.html.haml`.
|
226
|
-
|
227
|
-
|
228
456
|
## View Inheritance
|
229
457
|
|
230
458
|
This is where OOP comes back to your view.
|
@@ -252,25 +480,6 @@ class Comment::FormCell < Cell::Rails
|
|
252
480
|
|
253
481
|
When rendering views in `FormCell`, the view directories to look for templates will be inherited.
|
254
482
|
|
255
|
-
### Builders
|
256
|
-
|
257
|
-
Let `render_cell` take care of creating the right cell. Just configure your super-cell properly.
|
258
|
-
|
259
|
-
```ruby
|
260
|
-
class LoginCell < Cell::Rails
|
261
|
-
build do
|
262
|
-
UnauthorizedUserCell unless logged_in?
|
263
|
-
end
|
264
|
-
```
|
265
|
-
|
266
|
-
A call to
|
267
|
-
|
268
|
-
```ruby
|
269
|
-
render_cell(:login, :box)
|
270
|
-
```
|
271
|
-
|
272
|
-
will render the configured `UnauthorizedUserCell` instead of the original `LoginCell` if the login test fails.
|
273
|
-
|
274
483
|
|
275
484
|
## Caching
|
276
485
|
|
@@ -423,100 +632,6 @@ To run your specs we got a rake task, too!
|
|
423
632
|
rake spec:cells
|
424
633
|
```
|
425
634
|
|
426
|
-
# View Models, Explained
|
427
|
-
|
428
|
-
View models supersede the old controller-like cells. View models feel more natural as they wrap domain models and then add decorating methods for the view.
|
429
|
-
|
430
|
-
They are also significantly faster since they don't need to copy helpers and instance variables to the view: The view model itself is the view context. That means, methods called in the view are invoked on your cell instance.
|
431
|
-
|
432
|
-
|
433
|
-
```ruby
|
434
|
-
# app/cells/song_cell.rb
|
435
|
-
class SongCell < Cell::ViewModel
|
436
|
-
end
|
437
|
-
```
|
438
|
-
|
439
|
-
### Creation
|
440
|
-
|
441
|
-
Instantiating the view model should happen in controllers and views, but you can virtually use them anywhere.
|
442
|
-
|
443
|
-
A default workflow for creating and rendering a view model looks as the following.
|
444
|
-
|
445
|
-
```ruby
|
446
|
-
song = Song.find(1)
|
447
|
-
|
448
|
-
@cell = cell(:song, song).call
|
449
|
-
```
|
450
|
-
|
451
|
-
The `#cell` helper gives you an instance of the `SongCell` cell and wraps the `song` object.
|
452
|
-
|
453
|
-
### Rendering
|
454
|
-
|
455
|
-
The `call` invocation instructs the cell to render. Internally, that runs `render_state(:show)` per default.
|
456
|
-
|
457
|
-
You can basically invoke any method you want on that cell. Nevertheless, a view model should only expose the `#show` method per convention, which is reflected by the `#call` alias.
|
458
|
-
|
459
|
-
It is important to understand this convention: Internally, you may render multiple views, combine them, use instance methods to render and format values, and so on. Externally, exposing only one "public", rendering method defines a strong interface for your view model.
|
460
|
-
|
461
|
-
```ruby
|
462
|
-
class SongCell < Cell::ViewModel
|
463
|
-
def show
|
464
|
-
render
|
465
|
-
end
|
466
|
-
end
|
467
|
-
```
|
468
|
-
|
469
|
-
The `render` call will render the cell's `show` view.
|
470
|
-
|
471
|
-
### Views
|
472
|
-
|
473
|
-
```haml
|
474
|
-
- # app/cells/song/show.haml
|
475
|
-
|
476
|
-
%h1 #{title}
|
477
|
-
|
478
|
-
%p Written at #{composed_at}
|
479
|
-
|
480
|
-
= author_box
|
481
|
-
```
|
482
|
-
|
483
|
-
We strongly recommend to only invoke _methods_ in views and _not_ to use instance variables and locals. In a view model template (or, view), methods are called on the view model instance itself, meaning you can easily expose "helpers" by defining instance methods.
|
484
|
-
|
485
|
-
### Helpers
|
486
|
-
|
487
|
-
```ruby
|
488
|
-
class SongCell < Cell::ViewModel
|
489
|
-
include TimeagoHelper
|
490
|
-
|
491
|
-
def show
|
492
|
-
render
|
493
|
-
end
|
494
|
-
|
495
|
-
def composed_at
|
496
|
-
timeago(model.created_at)
|
497
|
-
end
|
498
|
-
end
|
499
|
-
```
|
500
|
-
|
501
|
-
In other words, using `composed_at` in the view will call `SongCell#composed_at`. Note that you have to `include` additional helpers into the class.
|
502
|
-
|
503
|
-
The `#model` methods lets you access the wrapped `Song` instance we passed into the cell when creating it.
|
504
|
-
|
505
|
-
### Properties
|
506
|
-
|
507
|
-
Often, it is helpful to automatically expose some reader methods to the model. You can do that with `::property`.
|
508
|
-
|
509
|
-
```ruby
|
510
|
-
class SongCell < Cell::ViewModel
|
511
|
-
include TimeagoHelper
|
512
|
-
|
513
|
-
property :title
|
514
|
-
|
515
|
-
# ...
|
516
|
-
end
|
517
|
-
```
|
518
|
-
|
519
|
-
You can now safely use `#title` in the view (and, in the cell class), it is delegated to `model.title`.
|
520
635
|
|
521
636
|
### Call
|
522
637
|
|
@@ -677,48 +792,7 @@ ActionMailer doesn't have request object, so if you inherit from Cell::Rails you
|
|
677
792
|
|
678
793
|
You can fix that with [actionmailer_with_request](https://github.com/weppos/actionmailer_with_request) which (suprise!) brings request object to the ActionMailer.
|
679
794
|
|
680
|
-
## Using Rails Gems Like simple_form Outside Of Rails
|
681
|
-
|
682
|
-
Cells can be used outside of Rails. A new module brought in 3.8.5 provides the Rails view "API" making it possible to use gems like the popular [simple_form](https://github.com/plataformatec/simple_form) outside Rails!
|
683
|
-
|
684
|
-
All you need to do is providing the cell with some helpers, usually it's the polymorphic routing paths required by the gems.
|
685
|
-
|
686
|
-
```ruby
|
687
|
-
module RoutingHelpers
|
688
|
-
def musician_path(model)
|
689
|
-
"/musicians/#{model.id}"
|
690
|
-
end
|
691
|
-
end
|
692
|
-
```
|
693
|
-
|
694
|
-
Then, use the Cell::Rails::HelperAPI module and it should work fine (depending on the quality of the gem you're desiring to use).
|
695
|
-
|
696
|
-
```ruby
|
697
|
-
require 'cell/base'
|
698
|
-
require "cell/rails/helper_api"
|
699
|
-
require "simple_form"
|
700
|
-
|
701
|
-
class BassistCell < Cell::Base
|
702
|
-
include Cell::Rails::HelperAPI
|
703
|
-
|
704
|
-
self._helpers = RoutingHelpers
|
705
795
|
|
706
|
-
def show
|
707
|
-
@musician = Musician.find(:first)
|
708
|
-
end
|
709
|
-
end
|
710
|
-
```
|
711
|
-
|
712
|
-
Your views can now use the gem's helpers.
|
713
|
-
|
714
|
-
```erb
|
715
|
-
<%= simple_form_for @musician do |f| %>
|
716
|
-
<%= f.input :name %>
|
717
|
-
<%= f.button :submit %>
|
718
|
-
<% end %>
|
719
|
-
```
|
720
|
-
|
721
|
-
Note that this currently "only" works with Rails 3.2-4.0.
|
722
796
|
|
723
797
|
## Cells is Rails::Engine aware!
|
724
798
|
|
@@ -727,7 +801,7 @@ Now `Rails::Engine`s can contribute to Cells view paths. By default, any 'app/ce
|
|
727
801
|
|
728
802
|
## Generator Options
|
729
803
|
|
730
|
-
By default, generated cells inherit from `Cell::
|
804
|
+
By default, generated cells inherit from `Cell::ViewModel`. If you want to change this, specify your new class name in `config/application.rb`:
|
731
805
|
|
732
806
|
### Base Class
|
733
807
|
|
@@ -751,33 +825,40 @@ config.generators do |g|
|
|
751
825
|
end
|
752
826
|
```
|
753
827
|
|
754
|
-
## Rails 2.3 note
|
755
828
|
|
756
|
-
|
829
|
+
## Capture Support
|
757
830
|
|
758
|
-
|
759
|
-
gem install cells -v 3.3.9
|
760
|
-
```
|
831
|
+
If you need a global `#content_for` use the [cells-capture](https://github.com/apotonick/cells-capture) gem.
|
761
832
|
|
762
|
-
In order to copy the cells rake tasks to your app, run
|
763
833
|
|
764
|
-
|
765
|
-
script/generate cells_install
|
766
|
-
```
|
834
|
+
## Undocumented Features
|
767
835
|
|
768
|
-
|
836
|
+
*(Please don't read this section!)*
|
769
837
|
|
770
|
-
|
838
|
+
### Rendering Global Partials
|
771
839
|
|
772
|
-
|
840
|
+
Although not recommended, you can also render global partials from a cell. Be warned, though, that they will be rendered using our stack, and you might have to include helpers into your view model.
|
773
841
|
|
774
|
-
|
842
|
+
This works by including `Partial` and the corresponding `:partial` option.
|
775
843
|
|
776
|
-
|
777
|
-
|
844
|
+
```ruby
|
845
|
+
class Cell < Cell::ViewModel
|
846
|
+
include Partial
|
778
847
|
|
779
|
-
|
848
|
+
def show
|
849
|
+
render partial: "../views/shared/map.html" # app/views/shared/map.html.haml
|
850
|
+
end
|
851
|
+
```
|
852
|
+
|
853
|
+
The provided path is relative to your cell's `::view_paths` directory. The format has to be added to the file name, the template engine suffix will be used from the cell.
|
854
|
+
|
855
|
+
You can provide the format in the `render` call, too.
|
856
|
+
|
857
|
+
```ruby
|
858
|
+
render partial: "../views/shared/map", formats: [:html]
|
859
|
+
```
|
780
860
|
|
861
|
+
This was mainly added to provide compatibility with 3rd-party gems like [Kaminari and Cells](https://github.com/apotonick/kaminari-cells) that rely on rendering partials within a cell.
|
781
862
|
|
782
863
|
## LICENSE
|
783
864
|
|