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.
Files changed (204) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +15 -13
  4. data/Appraisals +23 -0
  5. data/CHANGES.md +20 -3
  6. data/Gemfile +5 -7
  7. data/README.md +317 -236
  8. data/Rakefile +1 -1
  9. data/TODO.md +3 -0
  10. data/cells.gemspec +19 -28
  11. data/gemfiles/rails3.2.gemfile +14 -0
  12. data/gemfiles/rails4.0.gemfile +14 -0
  13. data/gemfiles/rails4.1.gemfile +15 -0
  14. data/gemfiles/rails4.2.gemfile +14 -0
  15. data/lib/cell.rb +28 -21
  16. data/lib/cell/caching.rb +10 -22
  17. data/lib/cell/caching/notification.rb +15 -0
  18. data/lib/cell/concept.rb +4 -69
  19. data/lib/cell/development.rb +11 -0
  20. data/lib/{cells → cell}/engines.rb +1 -1
  21. data/lib/cell/layout.rb +20 -0
  22. data/lib/cell/partial.rb +17 -0
  23. data/lib/cell/{base/prefixes.rb → prefixes.rb} +1 -1
  24. data/lib/cell/rails.rb +58 -50
  25. data/lib/cell/railtie.rb +60 -0
  26. data/lib/cell/{base/self_contained.rb → self_contained.rb} +1 -1
  27. data/lib/cell/templates.rb +60 -0
  28. data/lib/cell/test_case.rb +4 -162
  29. data/lib/cell/testing.rb +15 -0
  30. data/lib/cell/twin.rb +11 -29
  31. data/lib/cell/version.rb +10 -0
  32. data/lib/cell/view_model.rb +196 -88
  33. data/lib/cells.rb +1 -20
  34. data/lib/rails/generators/cell/cell_generator.rb +43 -0
  35. data/lib/rails/generators/cell/templates/cell.rb.erb +8 -0
  36. data/lib/{generators/templates/concept → rails/generators/cell/templates}/view.erb +0 -0
  37. data/lib/{generators/templates/concept → rails/generators/cell/templates}/view.haml +0 -0
  38. data/lib/rails/generators/cell/templates/view.slim +2 -0
  39. data/lib/rails/generators/concept/concept_generator.rb +38 -0
  40. data/lib/{generators/templates/concept/cell.rb → rails/generators/concept/templates/concept.rb.erb} +2 -2
  41. data/lib/{generators → rails/generators/concept}/templates/view.erb +0 -0
  42. data/lib/{generators → rails/generators/concept}/templates/view.haml +0 -0
  43. data/lib/rails/generators/concept/templates/view.slim +2 -0
  44. data/lib/rails/generators/test_unit/cell/cell_generator.rb +21 -0
  45. data/lib/{generators/templates/cell_test.rb → rails/generators/test_unit/cell/templates/unit_test.rb.erb} +3 -3
  46. data/lib/rails/generators/test_unit/concept/concept_generator.rb +21 -0
  47. data/lib/rails/generators/test_unit/concept/templates/unit_test.rb.erb +11 -0
  48. data/lib/{cells → tasks}/cells.rake +0 -0
  49. data/test/builder_test.rb +58 -0
  50. data/test/caching_test.rb +298 -0
  51. data/test/cell_benchmark.rb +32 -0
  52. data/test/cell_generator_test.rb +51 -82
  53. data/test/cell_test.rb +8 -23
  54. data/test/concept_generator_test.rb +22 -13
  55. data/test/concept_test.rb +41 -75
  56. data/test/dummy/app/views/musician/hamlet.html.erb +1 -0
  57. data/test/dummy/config/application.rb +21 -8
  58. data/test/{app/cells/bassist/play.html.erb → fixtures/bassist/play.erb} +0 -0
  59. data/test/fixtures/concepts/record/views/layout.erb +1 -0
  60. data/test/{app → fixtures}/concepts/record/views/show.erb +0 -0
  61. data/test/{app → fixtures}/concepts/record/views/song.erb +0 -0
  62. data/test/fixtures/inherit_views_test/popper/tap.erb +1 -0
  63. data/test/fixtures/inherit_views_test/tapper/play.erb +1 -0
  64. data/test/fixtures/inherit_views_test/tapper/tap.erb +1 -0
  65. data/test/fixtures/partial_test/with_partial/show.erb +1 -0
  66. data/test/fixtures/partials/_show.html.erb +1 -0
  67. data/test/fixtures/partials/_show.xml.erb +1 -0
  68. data/test/fixtures/song/ivar.erb +1 -0
  69. data/test/fixtures/song/show.erb +1 -0
  70. data/test/fixtures/song/with_erb.erb +4 -0
  71. data/test/fixtures/song/with_html.erb +1 -0
  72. data/test/fixtures/song/with_locals.erb +2 -0
  73. data/test/fixtures/song_with_layout/happy.erb +1 -0
  74. data/test/fixtures/song_with_layout/merry.erb +1 -0
  75. data/test/fixtures/song_with_layout/show.erb +1 -0
  76. data/test/fixtures/song_with_layout/show_with_layout.erb +1 -0
  77. data/test/fixtures/templates_caching_test/song/show.erb +1 -0
  78. data/test/fixtures/url_helper_test/song/edit.erb +8 -0
  79. data/test/fixtures/url_helper_test/song/with_block.erb +2 -0
  80. data/test/fixtures/url_helper_test/song/with_capture.erb +4 -0
  81. data/test/fixtures/url_helper_test/song/with_content_tag.erb +6 -0
  82. data/test/fixtures/url_helper_test/song/with_form_for_block.erb +3 -0
  83. data/test/fixtures/url_helper_test/song/with_link_to.erb +3 -0
  84. data/test/layout_test.rb +57 -0
  85. data/test/partial_test.rb +27 -0
  86. data/test/prefixes_test.rb +36 -10
  87. data/test/public_test.rb +42 -0
  88. data/test/rails_extensions_test.rb +51 -0
  89. data/test/render_test.rb +103 -0
  90. data/test/templates_test.rb +45 -0
  91. data/test/test_case_test.rb +21 -122
  92. data/test/test_helper.rb +37 -33
  93. data/test/twin_test.rb +3 -7
  94. data/test/url_helper_test.rb +89 -0
  95. metadata +92 -357
  96. data/gemfiles/Gemfile.rails3-0 +0 -7
  97. data/gemfiles/Gemfile.rails3-1 +0 -7
  98. data/gemfiles/Gemfile.rails3-2 +0 -7
  99. data/gemfiles/Gemfile.rails4-0 +0 -12
  100. data/gemfiles/Gemfile.rails4-1 +0 -12
  101. data/lib/cell/base.rb +0 -82
  102. data/lib/cell/base/view.rb +0 -15
  103. data/lib/cell/builder.rb +0 -71
  104. data/lib/cell/deprecations.rb +0 -41
  105. data/lib/cell/dsl.rb +0 -7
  106. data/lib/cell/rack.rb +0 -32
  107. data/lib/cell/rails/helper_api.rb +0 -37
  108. data/lib/cell/rails/view_model.rb +0 -159
  109. data/lib/cell/rails3_0_strategy.rb +0 -82
  110. data/lib/cell/rails3_1_strategy.rb +0 -40
  111. data/lib/cell/rails4_0_strategy.rb +0 -39
  112. data/lib/cell/rails4_1_strategy.rb +0 -40
  113. data/lib/cell/rendering.rb +0 -109
  114. data/lib/cells/rails.rb +0 -86
  115. data/lib/cells/railtie.rb +0 -38
  116. data/lib/cells/version.rb +0 -3
  117. data/lib/generators/USAGE +0 -30
  118. data/lib/generators/cells/base.rb +0 -22
  119. data/lib/generators/cells/cell_generator.rb +0 -15
  120. data/lib/generators/cells/view_generator.rb +0 -18
  121. data/lib/generators/erb/cell_generator.rb +0 -15
  122. data/lib/generators/erb/concept_generator.rb +0 -17
  123. data/lib/generators/haml/cell_generator.rb +0 -17
  124. data/lib/generators/haml/concept_generator.rb +0 -17
  125. data/lib/generators/rails/cell_generator.rb +0 -16
  126. data/lib/generators/rails/concept_generator.rb +0 -16
  127. data/lib/generators/slim/cell_generator.rb +0 -17
  128. data/lib/generators/templates/cell.rb +0 -9
  129. data/lib/generators/templates/view.slim +0 -4
  130. data/lib/generators/test_unit/cell_generator.rb +0 -14
  131. data/lib/generators/trailblazer/base.rb +0 -21
  132. data/lib/generators/trailblazer/view_generator.rb +0 -18
  133. data/test/app/cells/album/views/cover.haml +0 -1
  134. data/test/app/cells/bad_guitarist/_dii.html.erb +0 -1
  135. data/test/app/cells/bad_guitarist_cell.rb +0 -2
  136. data/test/app/cells/bassist/_dii.html.erb +0 -1
  137. data/test/app/cells/bassist/ahem.html.erb +0 -1
  138. data/test/app/cells/bassist/compose.html.erb +0 -1
  139. data/test/app/cells/bassist/contact_form.html.erb +0 -1
  140. data/test/app/cells/bassist/form_for.erb +0 -3
  141. data/test/app/cells/bassist/form_for_in_haml.haml +0 -2
  142. data/test/app/cells/bassist/jam.html.erb +0 -3
  143. data/test/app/cells/bassist/play.js.erb +0 -1
  144. data/test/app/cells/bassist/pose.html.erb +0 -1
  145. data/test/app/cells/bassist/promote.html.erb +0 -1
  146. data/test/app/cells/bassist/provoke.html.erb +0 -1
  147. data/test/app/cells/bassist/shout.html.erb +0 -1
  148. data/test/app/cells/bassist/sing.html.haml +0 -1
  149. data/test/app/cells/bassist/slap.html.erb +0 -1
  150. data/test/app/cells/bassist/yell.en.html.erb +0 -1
  151. data/test/app/cells/bassist_cell.rb +0 -25
  152. data/test/app/cells/club_security.rb +0 -2
  153. data/test/app/cells/club_security/guard/help.html.erb +0 -1
  154. data/test/app/cells/club_security/guard_cell.rb +0 -6
  155. data/test/app/cells/club_security/medic/help.html.erb +0 -1
  156. data/test/app/cells/club_security/medic_cell.rb +0 -8
  157. data/test/app/cells/layouts/b.erb +0 -1
  158. data/test/app/cells/layouts/metal.html.erb +0 -1
  159. data/test/app/cells/rails_helper_api_test/bassist/edit.html.erb +0 -5
  160. data/test/app/cells/shouter/sing.html.erb +0 -1
  161. data/test/app/cells/song/dashboard.haml +0 -7
  162. data/test/app/cells/song/details.html.haml +0 -1
  163. data/test/app/cells/song/info.html.haml +0 -1
  164. data/test/app/cells/song/lyrics.html.haml +0 -6
  165. data/test/app/cells/song/plays.haml +0 -1
  166. data/test/app/cells/song/scale.haml +0 -1
  167. data/test/app/cells/song/show.html.haml +0 -3
  168. data/test/app/cells/song/title.html.haml +0 -1
  169. data/test/app/cells/trumpeter/promote.html.erb +0 -1
  170. data/test/app/cells/trumpeter_cell.rb +0 -8
  171. data/test/app/cells/view_model_test/comments/show.haml +0 -7
  172. data/test/app/concepts/record/views/layout.haml +0 -2
  173. data/test/app/views/shared/_dong.html.erb +0 -1
  174. data/test/cell_module_test.rb +0 -170
  175. data/test/cells_module_test.rb +0 -27
  176. data/test/deprecations_test.rb +0 -101
  177. data/test/dummy/app/helpers/application_helper.rb +0 -2
  178. data/test/dummy/app/views/musician/hamlet.html.haml +0 -1
  179. data/test/dummy/config/environments/development.rb +0 -16
  180. data/test/dummy/config/environments/production.rb +0 -46
  181. data/test/dummy/config/environments/test.rb +0 -33
  182. data/test/dummy/db/test.sqlite3 +0 -0
  183. data/test/dummy/label/app/cells/label/show.erb +0 -1
  184. data/test/dummy/label/app/cells/label_cell.rb +0 -5
  185. data/test/dummy/label/label.gemspec +0 -20
  186. data/test/dummy/label/lib/label.rb +0 -4
  187. data/test/dummy/label/lib/label/version.rb +0 -3
  188. data/test/dummy/public/404.html +0 -26
  189. data/test/dummy/public/422.html +0 -26
  190. data/test/dummy/public/500.html +0 -26
  191. data/test/dummy/public/favicon.ico +0 -0
  192. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  193. data/test/helper_test.rb +0 -81
  194. data/test/rack_test.rb +0 -32
  195. data/test/rails/asset_pipeline_test.rb +0 -20
  196. data/test/rails/caching_test.rb +0 -456
  197. data/test/rails/cells_test.rb +0 -119
  198. data/test/rails/forms_test.rb +0 -75
  199. data/test/rails/integration_test.rb +0 -299
  200. data/test/rails/render_test.rb +0 -189
  201. data/test/rails/view_model_test.rb +0 -226
  202. data/test/rails/view_test.rb +0 -49
  203. data/test/rails_helper_api_test.rb +0 -58
  204. data/test/self_contained_test.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1733d66b3c4c59a504f758006fbc1a4391b04827
4
- data.tar.gz: 2ba5c6cfba87ec5f9df760f15e2f40fc5a4db44d
3
+ metadata.gz: 26d77a00fc2075f5869dedf7b99b55afc2df0eec
4
+ data.tar.gz: 139402563ae105fc1c501f2a848a7b7f0491452d
5
5
  SHA512:
6
- metadata.gz: 66b45a320031b1d401a8a4714f4e31f6944d17302118b3343810f0533c4d588a76f08428d295bbd8811048d36c3a66817e915a958831c8f580d476c82225f689
7
- data.tar.gz: c18f7d86f22a3d2e4fb5ac0671c0a1aa92ae467dfaaea2fbb9c7e0c8e5888fa20ffcf6673a27b93a95b67dd2d89f59c8ff4e880c4e0f575fa1279e0c257e753f
6
+ metadata.gz: 5b5d3fae08a561a5b24d976e137d8e91d9172487318d4621d281295cd45eecd61ac81820aa27c64afc3a9385e891354efbc1c8c371daeb14b0dabf9d3c121f9d
7
+ data.tar.gz: c933f9a2a9e4fc9850ad5257e725e5070a2b248070dfa0bd012d095016b6866adea340a85910b09fe65c1352911d838c29e63e92c5e4ee1525f90fb9ef7bcd95
data/.gitignore CHANGED
@@ -3,7 +3,7 @@ pkg
3
3
  *.gem
4
4
  .*~
5
5
  .bundle
6
- Gemfile*.lock
6
+ *.lock
7
7
  test/dummy/log/
8
8
  test/dummy/tmp/
9
9
  /.rvmrc
@@ -1,19 +1,21 @@
1
+ language: ruby
1
2
  rvm:
2
- - 1.9.3
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/Gemfile.rails3-0
9
- - gemfiles/Gemfile.rails3-1
10
- - gemfiles/Gemfile.rails3-2
11
- - gemfiles/Gemfile.rails4-0
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
@@ -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
- ## 3.11.3
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
- #gem "rails" , :path => "../rayls"
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 a separate MVC component. They look and feel like controllers, can run arbitrary code in an action and render views.
8
+ Cells allow you to encapsulate parts of your page into separate MVC components. These components are called _view models_.
9
9
 
10
- While they improve your overall software architecture by abstracting logic into an encapsulated OOP instance, cells also maximise reuseability within or across projects.
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
- Basically, cells can be rendered anywhere in your code. Most people use them in views to replace a helper/partial/filter mess, as a mailer renderer substitute or hook them to routes and completely bypass `ActionController`.
12
+ As you have already noticed we use _cell_ and _view model_ interchangeably here.
13
13
 
14
14
 
15
- ## View Models
15
+ ## The Book
16
16
 
17
- Since version 3.9 cells comes with two "dialects": You can still use a cell like a controller. However, the new [view model](https://github.com/apotonick/cells#view-models-explained) "dialect" supercedes the traditional cell. It allows you to treat a cell more object-oriented while providing an alternative approach to helpers.
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
- While the old dialect still works, we strongly recommend using a cell as a view model.
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.0. For 2.3 support [see below](#rails-23-note).
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
- Creating a cell is nothing more than
75
+ Use the bundled generator to set up a cell.
47
76
 
48
77
  ```shell
49
- rails generate cell cart show -e haml
78
+ rails generate cell comment
50
79
  ```
51
80
 
52
81
  ```
53
82
  create app/cells/
54
- create app/cells/cart
55
- create app/cells/cart_cell.rb
56
- create app/cells/cart/show.html.haml
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
- ## Render the cell
89
+ ## Rendering View Models
63
90
 
64
- Now, render your cart. Why not put it in `layouts/application.html.erb` for now?
91
+ Suppose we are to render a "partial" for `Comment` model
65
92
 
66
- ```erb
67
- <div id="header">
68
- <%= render_cell :cart, :show, :user => @current_user %>
93
+ ```ruby
94
+ @comment = Comment.find(1)
69
95
  ```
70
96
 
71
- Feels like rendering a controller action. For good encapsulation we pass the current `user` from outside into the cell - a dependency injection.
97
+ Cells brings you one helper method `#cell` to be used in your controller views or layouts.
72
98
 
73
- ## Code
99
+ ```haml
100
+ = cell(:comment, @comment)
101
+ ```
74
102
 
75
- Time to improve our cell code. Let's start with `app/cells/cart_cell.rb`:
103
+ This is the short form of rendering a cell. Simple, isn't it?
76
104
 
77
- ```ruby
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
- render # renders show.html.haml
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
- Is that a controller? Hell, yeah. We even got a `#render` method as we know it from the good ol' `ActionController`.
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
- Since a plain call to `#render` will start rendering `app/cells/cart/show.html.haml` we should put some meaningful markup there.
154
+ Let's check out the `show.haml` view to see how they work.
94
155
 
95
156
  ```haml
96
- #cart
97
- You have #{@items.size} items in your shopping cart.
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
- ### ERB? Haml? Builder?
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
- Yes, Cells support all template types that are supported by Rails itself. Remember- it's a controller!
172
+ This is how a typical view looks in a view model.
103
173
 
104
- ### Helpers
174
+ ```haml
175
+ -# app/cells/comment/show.haml
176
+
177
+ %h1 Comment
105
178
 
106
- Yes, Cells have helpers just like controllers. If you need some specific helper, do
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 CartCell < Cell::Rails
110
- helper MyExtraHelper
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
- and it will be around in your cart views.
254
+ As `:show` is the default action, you don't have to specify it.
114
255
 
115
- ### Partials?
256
+ ```ruby
257
+ cell(:comment, @comment).call
258
+ ```
116
259
 
117
- In Cells, everything template file is a _view_. You're still free to render views within views (aka "partial") but we just call it "_view_". There's no need to have two different types of views. Whenever you're tempted to render a partial, use the cells term `view`.
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
- / app/cells/comment/show.haml
263
+ = cell(:comment, @comment)
264
+ ```
121
265
 
122
- %h1 All comments
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
- %p
125
- = render :view => 'items'
268
+ ```haml
269
+ = cell(:comment, @comment).avatar
126
270
  ```
127
271
 
128
- ## File Structure
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
- TODO: rails g concept Song => show.haml,
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::Rails`. If you want to change this, specify your new class name in `config/application.rb`:
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
- ### Installation
829
+ ## Capture Support
757
830
 
758
- ```shell
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
- ```shell
765
- script/generate cells_install
766
- ```
834
+ ## Undocumented Features
767
835
 
768
- ## Capture Support
836
+ *(Please don't read this section!)*
769
837
 
770
- If you need a global `#content_for` use the [cells-capture](https://github.com/apotonick/cells-capture) gem.
838
+ ### Rendering Global Partials
771
839
 
772
- ## More features
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
- Cells can do more.
842
+ This works by including `Partial` and the corresponding `:partial` option.
775
843
 
776
- * __No Limits__. Have as many cells in your page as you need - no limitation to your `render_cell` calls.
777
- * __Cell Nesting__. Have complex cell hierarchies as you can call `render_cell` within cells, too.
844
+ ```ruby
845
+ class Cell < Cell::ViewModel
846
+ include Partial
778
847
 
779
- Go for it, you'll love it!
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