cells 3.8.8 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -1
  3. data/.travis.yml +5 -2
  4. data/CHANGES.textile +23 -15
  5. data/Gemfile +1 -1
  6. data/README.md +412 -0
  7. data/Rakefile +2 -2
  8. data/cells.gemspec +5 -6
  9. data/gemfiles/Gemfile.rails3-0 +2 -2
  10. data/gemfiles/Gemfile.rails3-1 +1 -1
  11. data/gemfiles/Gemfile.rails3-2 +1 -2
  12. data/gemfiles/Gemfile.rails4-0 +7 -0
  13. data/lib/cell.rb +27 -0
  14. data/lib/cell/base.rb +31 -18
  15. data/lib/cell/builder.rb +11 -10
  16. data/lib/cell/dsl.rb +7 -0
  17. data/lib/cell/rack.rb +5 -9
  18. data/lib/cell/rails.rb +19 -11
  19. data/lib/cell/rails/view_model.rb +115 -0
  20. data/lib/cell/rails3_0_strategy.rb +1 -1
  21. data/lib/cell/rails3_1_strategy.rb +1 -1
  22. data/lib/cell/rails4_0_strategy.rb +1 -2
  23. data/lib/cell/test_case.rb +11 -11
  24. data/lib/cells.rb +4 -3
  25. data/lib/cells/rails.rb +16 -3
  26. data/lib/cells/version.rb +1 -1
  27. data/test/app/cells/bassist_cell.rb +9 -1
  28. data/test/app/cells/rails_helper_api_test/bassist/edit.html.erb +3 -3
  29. data/test/app/cells/song/dashboard.haml +7 -0
  30. data/test/app/cells/song/details.html.haml +1 -0
  31. data/test/app/cells/song/info.html.haml +1 -0
  32. data/test/app/cells/song/lyrics.html.haml +6 -0
  33. data/test/app/cells/song/plays.haml +1 -0
  34. data/test/app/cells/song/show.html.haml +3 -0
  35. data/test/app/cells/song/title.html.haml +1 -0
  36. data/test/app/cells/view_model_test/comments/show.haml +7 -0
  37. data/test/cell_module_test.rb +39 -41
  38. data/test/cell_test.rb +28 -0
  39. data/test/dummy/app/views/musician/featured_with_block.html.erb +1 -1
  40. data/test/dummy/app/views/musician/title.erb +1 -0
  41. data/test/dummy/config/routes.rb +1 -0
  42. data/test/helper_test.rb +13 -10
  43. data/test/rails/caching_test.rb +75 -73
  44. data/test/rails/cells_test.rb +25 -23
  45. data/test/rails/integration_test.rb +80 -61
  46. data/test/rails/view_model_test.rb +119 -0
  47. data/test/rails_helper_api_test.rb +11 -13
  48. metadata +41 -61
  49. data/README.rdoc +0 -279
  50. data/about.yml +0 -7
  51. data/test/app/cells/producer/capture.html.erb +0 -1
  52. data/test/app/cells/producer/content_for.html.erb +0 -2
  53. data/test/rails/capture_test.rb +0 -70
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5f0e0a0f0e9e01a740baa8dcabe12f82876ca1ef
4
+ data.tar.gz: a0a66d1ecd8e09265efc3a8c5fe7e293bc8df5d1
5
+ SHA512:
6
+ metadata.gz: 33577d3b2e94dde50fb8f68ff2695fbaa1dbc73a04fb839fbb14e4ca215f83d01b854ec57454cf75488210a2ef8060312cdfc9ae865ece439af2fd84ab4df111
7
+ data.tar.gz: 7c03b94c1a6dee95e672a0411025e9fa7afb6414d4a3c43a4038b70e45f381f4bde3d9cf66a2364f953ef381b27220af690da67685f5aaddb8be8e09d4f3a4c0
data/.gitignore CHANGED
@@ -1,8 +1,9 @@
1
1
  .DS_Store
2
2
  pkg
3
+ *.gem
3
4
  .*~
4
5
  .bundle
5
6
  Gemfile*.lock
6
- test/dummy/log/*.log
7
+ test/dummy/log/
7
8
  test/dummy/tmp/
8
9
  /.rvmrc
data/.travis.yml CHANGED
@@ -1,8 +1,11 @@
1
- rvm: 1.9.2
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - jruby-19mode
2
5
  notifications:
3
6
  irc: "irc.freenode.org#cells"
4
7
  gemfile:
5
- - Gemfile
6
8
  - gemfiles/Gemfile.rails3-0
7
9
  - gemfiles/Gemfile.rails3-1
8
10
  - gemfiles/Gemfile.rails3-2
11
+ - gemfiles/Gemfile.rails4-0
data/CHANGES.textile CHANGED
@@ -1,3 +1,11 @@
1
+ h2. 3.9.0
2
+
3
+ * Cells in engines are now recognized under Rails 4.0.
4
+ * Introducing @#cell@ and @#cell_for@ to instantiate cells in ActionController and ActionView.
5
+ * Adding @Cell::Rails::ViewModel@ as a new "dialect" of working with cells.
6
+ * Add @Cell::Base#process_args@ which is called in the initializer to handle arguments passed into the constructor.
7
+ * Setting @controller in your @Cell::TestCase@ no longer get overridden by us.
8
+
1
9
  h2. 3.8.8
2
10
 
3
11
  * Maintenance release.
@@ -24,7 +32,7 @@ h2. 3.8.4
24
32
 
25
33
  h2. 3.8.3
26
34
 
27
- * Added @Engines.existent_directories_for@ to prevent Rails 3.0 from crashing when it detects engines.
35
+ * Added @Engines.existent_directories_for@ to prevent Rails 3.0 from crashing when it detects engines.
28
36
 
29
37
  h2. 3.8.2
30
38
 
@@ -40,7 +48,7 @@ h2. 3.8.0
40
48
  * New API (note that @controller@ isn't the first argument anymore):
41
49
  ** @Rails.create_cell_for(name, controller)@
42
50
  ** @Rails.render_cell_for(name, state, controller, *args)@
43
- * Moved builder methods to @Cell::Builder@ module.
51
+ * Moved builder methods to @Cell::Builder@ module.
44
52
  * @DEFAULT_VIEW_PATHS@ is now in @Cell::Base@.
45
53
  * Removed the monkey-patch that made state-args work in Rails <= 3.0.3. Upgrade to +3.0.4.
46
54
 
@@ -60,18 +68,18 @@ h2. 3.6.8
60
68
 
61
69
  h3. Changes
62
70
  * Removed <code>@opts</code>.
63
- * Deprecated @#options@ in favour of state-args.
71
+ * Deprecated @#options@ in favour of state-args.
64
72
 
65
73
  h2. 3.6.7
66
74
 
67
75
  h3. Changes
68
- * Added @view_assigns@ to TestCase.
69
-
76
+ * Added @view_assigns@ to TestCase.
77
+
70
78
  h2. 3.6.6
71
79
 
72
80
  h3. Changes
73
81
  * Added the @:format@ option for @#render@ which should be used with caution. Sorry for that.
74
- * Removed the useless @layouts/@ view path from Cell::Base.
82
+ * Removed the useless @layouts/@ view path from Cell::Base.
75
83
 
76
84
  h2. 3.6.5
77
85
 
@@ -79,7 +87,7 @@ h3. Bugfixes
79
87
  * `Cell::TestCase#invoke` now properly accepts state-args.
80
88
 
81
89
  h3. Changes
82
- * Added the `:if` option to `Base.cache` which allows adding a conditional proc or instance method to the cache definition. If it doesn't return true, caching for that state is skipped.
90
+ * Added the `:if` option to `Base.cache` which allows adding a conditional proc or instance method to the cache definition. If it doesn't return true, caching for that state is skipped.
83
91
 
84
92
 
85
93
  h2. 3.6.4
@@ -122,7 +130,7 @@ h3. Changes
122
130
 
123
131
 
124
132
  h2. 3.5.5
125
-
133
+
126
134
  h3. Bugfixes
127
135
  * The generator now places views of namespaced cells into the correct directory. E.g. `rails g Blog::Post display` puts views to `app/cells/blog/post/display.html.erb`.
128
136
 
@@ -131,7 +139,7 @@ h3. Changes
131
139
 
132
140
 
133
141
  h2. 3.5.4
134
-
142
+
135
143
  h3. Bugfixes
136
144
  * state-args work even if your state method receives optional arguments or default values, like @def show(user, age=18)@.
137
145
 
@@ -142,7 +150,7 @@ h3. Changes
142
150
 
143
151
 
144
152
  h2. 3.5.2
145
-
153
+
146
154
  h3. Bugfixes
147
155
  * Controller#render_cell now accepts multiple args as options.
148
156
 
@@ -155,7 +163,7 @@ h2. 3.5.1
155
163
  * No longer pass an explicit Proc but a versioner block to @Cell.Base.cache@. Example: @cache :show do "v1" end@
156
164
  * Caching.cache_key_for now uses @ActiveSupport::Cache.expand_cache_key@. Consequently, a key which used to be like @"cells/director/count/a=1/b=2"@ now is @cells/director/count/a=1&b=2@ and so on. Be warned that this might break your home-made cache expiry.
157
165
  * Controller#expire_cell_state now expects the cell class as first arg. Example: @expire_cell_state(DirectorCell, :count)@
158
-
166
+
159
167
  h3. Bugfixes
160
168
  * Passing options to @render :state@ in views finally works: @render({:state => :list_item}, item, i)@
161
169
 
@@ -165,26 +173,26 @@ h2. 3.5.0
165
173
  h3. Changes
166
174
  * Deprecated @opts, use #options now.
167
175
  * Added state-args. State methods can now receive the options as method arguments. This should be the prefered way of parameter exchange with the outer world.
168
- * #params, #request, and #config is now delegated to @parent_controller.
176
+ * #params, #request, and #config is now delegated to @parent_controller.
169
177
  * The generator now is invoked as @rails g cell ...@
170
178
  * The `--haml` option is no longer available.
171
179
  * The `-t` option now is compatible with the rest of rails generators, now it is used as alias for `--test-framework`. Use the `-e` option as an alias of `--template-engine`
172
180
  Thanks to Jorge Calás Lozano <calas@qvitta.net> for patching this in the most reasonable manner i could imagine.
173
181
  * Privatized @#find_family_view_for_state@, @#render_view_for@, and all *ize methods in Cell::Rails.
174
182
  * New signature: @#render_view_for(state, *args)@
175
-
183
+
176
184
  h2. 3.4.4
177
185
 
178
186
  h3. Changes
179
187
  * Cells.setup now yields Cell::Base, so you can really call append_view_path and friends here.
180
188
  * added Cell::Base.build for streamlining the process of deciders around #render_cell, "see here":http://nicksda.apotomo.de/2010/12/pragmatic-rails-thoughts-on-views-inheritance-view-inheritance-and-rails-304
181
189
  * added TestCase#in_view to test helpers in a real cell view.
182
-
190
+
183
191
 
184
192
  h2. 3.4.3
185
193
 
186
194
  h3. Changes
187
195
  * #render_cell now accepts a block which yields the cell instance before rendering.
188
-
196
+
189
197
  h3. Bugfixes
190
198
  * We no longer use TestTaskWithoutDescription in our rake tasks.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :gemcutter
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
data/README.md ADDED
@@ -0,0 +1,412 @@
1
+ # Cells
2
+
3
+ **View Components for Rails.**
4
+
5
+ ## Overview
6
+
7
+ Say you're writing a Rails online shop - the shopping cart is reappearing again and again in every view. You're thinking about a clean solution for that part. A mixture of controller code, before-filters, partials and helpers?
8
+
9
+ No. That sucks. Take Cells.
10
+
11
+ Cells are View Components for Rails. They look and feel like controllers. They don't have no `DoubleRenderError`. They can be rendered everywhere in your controllers or views. They are cacheable, testable, fast and wonderful. They bring back OOP to your view and improve your software design.
12
+
13
+ And the best: You can have as many cells in your page as you need!
14
+
15
+
16
+ Note: 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) "dialect" allows you to treat a cell more object-oriented while providing an alternative approach to helpers.
17
+
18
+ ## Installation
19
+
20
+ It's a gem!
21
+
22
+ Rails >= 3.0:
23
+
24
+ ```shell
25
+ gem install cells
26
+ ```
27
+
28
+ Rails 2.3:
29
+
30
+ ```shell
31
+ gem install cells -v 3.3.9
32
+ ```
33
+
34
+ ## Generate
35
+
36
+ Creating a cell is nothing more than
37
+
38
+ ```shell
39
+ rails generate cell cart show -e haml
40
+ ```
41
+
42
+ ```
43
+ create app/cells/
44
+ create app/cells/cart
45
+ create app/cells/cart_cell.rb
46
+ create app/cells/cart/show.html.haml
47
+ create test/cells/cart_test.rb
48
+ ```
49
+
50
+ That looks very familiar.
51
+
52
+ ## Render the cell
53
+
54
+ Now, render your cart. Why not put it in `layouts/application.html.erb` for now?
55
+
56
+ ```erb
57
+ <div id="header">
58
+ <%= render_cell :cart, :show, :user => @current_user %>
59
+ ```
60
+
61
+ Feels like rendering a controller action. For good encapsulation we pass the current `user` from outside into the cell - a dependency injection.
62
+
63
+ ## Code
64
+
65
+ Time to improve our cell code. Let's start with `app/cells/cart_cell.rb`:
66
+
67
+ ```ruby
68
+ class CartCell < Cell::Rails
69
+ def show(args)
70
+ user = args[:user]
71
+ @items = user.items_in_cart
72
+
73
+ render # renders show.html.haml
74
+ end
75
+ end
76
+ ```
77
+
78
+ Is that a controller? Hell, yeah. We even got a `#render` method as we know it from the good ol' `ActionController`.
79
+
80
+
81
+ ## Views
82
+
83
+ Since a plain call to `#render` will start rendering `app/cells/cart/show.html.haml` we should put some meaningful markup there.
84
+
85
+ ```haml
86
+ #cart
87
+ You have #{@items.size} items in your shopping cart.
88
+ ```
89
+
90
+ ### ERB? Haml? Builder?
91
+
92
+ Yes, Cells support all template types that are supported by Rails itself. Remember- it's a controller!
93
+
94
+ ### Helpers
95
+
96
+ Yes, Cells have helpers just like controllers. If you need some specific helper, do
97
+
98
+ ```ruby
99
+ class CartCell < Cell::Rails
100
+ helper MyExtraHelper
101
+ ```
102
+
103
+ and it will be around in your cart views.
104
+
105
+ ### Partials?
106
+
107
+ Yeah, we do support rendering partials in views. Nevertheless, we discourage _partials_ at all.
108
+
109
+ The distinction between partials and views is making things more complex, so why should we have two kinds of view types? Use ordinary views instead, they're fine.
110
+
111
+ ```haml
112
+ %p
113
+ = render :view => 'items'
114
+ ```
115
+
116
+ ### Rendering Global Partials
117
+
118
+ 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.
119
+
120
+ ```ruby
121
+ class MapCell < Cell::Rails
122
+ append_view_path "app/views"
123
+ ```
124
+
125
+
126
+ ## View Inheritance
127
+
128
+ This is where OOP comes back to your view.
129
+
130
+ * __Inherit code__ into your cells by deriving more abstract cells.
131
+ * __Inherit views__ from parent cells.
132
+
133
+ ### Builders
134
+
135
+ Let `render_cell` take care of creating the right cell. Just configure your super-cell properly.
136
+
137
+ ```ruby
138
+ class LoginCell < Cell::Rails
139
+ build do
140
+ UnauthorizedUserCell unless logged_in?
141
+ end
142
+ ```
143
+
144
+ A call to
145
+
146
+ ```ruby
147
+ render_cell(:login, :box)
148
+ ```
149
+
150
+ will render the configured `UnauthorizedUserCell` instead of the original `LoginCell` if the login test fails.
151
+
152
+
153
+ ## Caching
154
+
155
+ Cells do strict view caching. No cluttered fragment caching. Add
156
+
157
+ ```ruby
158
+ class CartCell < Cell::Rails
159
+ cache :show, :expires_in => 10.minutes
160
+ ```
161
+
162
+ and your cart will be re-rendered after 10 minutes.
163
+
164
+ You can expand the state's cache key - why not use a versioner block to do just this?
165
+
166
+ ```ruby
167
+ class CartCell < Cell::Rails
168
+ cache :show do |cell, options|
169
+ options[:items].md5
170
+ end
171
+ ```
172
+
173
+ The block's return value is appended to the state key: `"cells/cart/show/0ecb1360644ce665a4ef"`.
174
+
175
+ Check the [API to learn more](http://rdoc.info/gems/cells/Cell/Caching/ClassMethods#cache-instance_method).
176
+
177
+ *Reminder*: 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
178
+
179
+ ## Testing
180
+
181
+ Another big advantage compared to monolithic controller/helper/partial piles is the ability to test your cells isolated.
182
+
183
+ ### Test::Unit
184
+
185
+ So what if you wanna test the cart cell? Use the generated `test/cells/cart_cell_test.rb` test.
186
+
187
+ ```ruby
188
+ class CartCellTest < Cell::TestCase
189
+ test "show" do
190
+ invoke :show, :user => @user_fixture
191
+ assert_select "#cart", "You have 3 items in your shopping cart."
192
+ end
193
+ ```
194
+
195
+ Don't forget to put `require 'cell/test_case'` in your project's `test/test_helper.rb` file.
196
+
197
+ Then, run your tests with
198
+
199
+ ```shell
200
+ rake test:cells
201
+ ```
202
+
203
+ That's easy, clean and strongly improves your component-driven software quality. How'd you do that with partials?
204
+
205
+
206
+ ### RSpec
207
+
208
+ If you prefer RSpec examples, use the [rspec-cells](http://github.com/apotonick/rspec-cells) gem for specing.
209
+
210
+ ```ruby
211
+ it "should render the posts count" do
212
+ render_cell(:posts, :count).should have_selector("p", :content => "4 posts!")
213
+ end
214
+ ```
215
+
216
+ To run your specs we got a rake task, too!
217
+
218
+ ```shell
219
+ rake spec:cells
220
+ ```
221
+
222
+ # View Models
223
+
224
+ Cells 3.9 brings a new dialect to cells: view models (still experimental!).
225
+
226
+ Think of a view model as a cell decorating a model or a collection. In this mode, helpers are nothing more than instance methods of your cell class, making helpers predictable and scoped.
227
+
228
+ ```ruby
229
+ class SongCell < Cell::Rails
230
+ include Cell::Rails::ViewModel
231
+
232
+ property :title
233
+
234
+
235
+ def show
236
+ render
237
+ end
238
+
239
+ def self_link
240
+ link_to(title, song_url(model))
241
+ end
242
+ end
243
+ ```
244
+
245
+ ### Creation
246
+
247
+ Creating the view model should usually happen in the controller.
248
+
249
+ ```ruby
250
+ class DashboardController < ApplicationController
251
+
252
+ def index
253
+ @song = Song.find(1)
254
+
255
+ @cell = cell(:song, @song)
256
+ end
257
+ ```
258
+
259
+ You can now grab an instance of your cell using the `#cell` method. The 2nd argument will be the cell's decorated model.
260
+
261
+ Have a look at how to use this cell in your controller view.
262
+
263
+ ```haml
264
+ = @cell.show # renders its show view.
265
+ ```
266
+
267
+ You no longer use the `#render_cell` helper but call any method on that cell. Usually, this is a state (or "action") like `show`.
268
+
269
+ ### Helpers
270
+
271
+ Note that this doesn't have to be a rendering state, it could be any instance method (aka "helper").
272
+
273
+ ```haml
274
+ = @cell.self_link
275
+ ```
276
+
277
+ As all helpers are now instance methods, the `#self_link` example can use any existing helper (as the URL helpers) on the instance level.
278
+
279
+ Attributes declared using ``::property` are automatically delegated to the decorated model.
280
+
281
+ ```ruby
282
+ @cell.title # delegated to @song.title
283
+ ```
284
+
285
+ ### Views
286
+
287
+ This greatly reduces wiring in the cell view (which is still in `app/cells/song/show.haml`).
288
+
289
+ ```haml
290
+ %h1
291
+ = title
292
+
293
+ Bookmark! #{self_link}
294
+ ```
295
+
296
+ Making the cell instance itself the view context should be an interesting alternative for many views.
297
+
298
+
299
+ ## Mountable Cells
300
+
301
+ 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.
302
+
303
+ ```ruby
304
+ class PostCell < Cell::Base
305
+ ...
306
+ end
307
+ ```
308
+
309
+ In your `routes.rb` file, mount the cell like a Rack app.
310
+
311
+ ```ruby
312
+ match "/posts" => proc { |env|
313
+ [ 200, {}, [ Cell::Base.render_cell_for(:post, :show) ]]
314
+ }
315
+ ```
316
+
317
+ ### Cells in ActionMailer
318
+
319
+ ActionMailer doesn't have request object, so if you inherit from Cell::Rails you will receive an error. Cell::Base will fix that problem, but you will not be able to use any of routes inside your cells.
320
+
321
+ You can fix that with [actionmailer_with_request](https://github.com/weppos/actionmailer_with_request) which (suprise!) brings request object to the ActionMailer.
322
+
323
+ ## Using Rails Gems Like simple_form Outside Of Rails
324
+
325
+ 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!
326
+
327
+ All you need to do is providing the cell with some helpers, usually it's the polymorphic routing paths required by the gems.
328
+
329
+ ```ruby
330
+ module RoutingHelpers
331
+ def musician_path(model)
332
+ "/musicians/#{model.id}"
333
+ end
334
+ end
335
+ ```
336
+
337
+ Then, use the Cell::Rails::HelperAPI module and it should work fine (depending on the quality of the gem you're desiring to use).
338
+
339
+ ```ruby
340
+ require 'cell/base'
341
+ require "cell/rails/helper_api"
342
+ require "simple_form"
343
+
344
+ class BassistCell < Cell::Base
345
+ include Cell::Rails::HelperAPI
346
+
347
+ self._helpers = RoutingHelpers
348
+
349
+ def show
350
+ @musician = Musician.find(:first)
351
+ end
352
+ end
353
+ ```
354
+
355
+ Your views can now use the gem's helpers.
356
+
357
+ ```erb
358
+ <%= simple_form_for @musician do |f| %>
359
+ <%= f.input :name %>
360
+ <%= f.button :submit %>
361
+ <% end %>
362
+ ```
363
+
364
+ Note that this currently "only" works with Rails 3.2-4.0.
365
+
366
+ ## Cells is Rails::Engine aware!
367
+
368
+ Now `Rails::Engine`s can contribute to Cells view paths. By default, any 'app/cells' found inside any Engine is automatically included into Cells view paths. If you need to, you can customize the view paths changing/appending to the `'app/cell_views'` path configuration. See the `Cell::EngineIntegration` for more details.
369
+
370
+ ## Generators
371
+
372
+ By default, generated cells inherit from `Cell::Rails`. If you want to change this, specify your new class name in `config/application.rb`:
373
+
374
+ ```ruby
375
+ module MyApp
376
+ class Application < Rails::Application
377
+ config.generators do |g|
378
+ g.base_cell_class "ApplicationCell"
379
+ end
380
+ end
381
+ end
382
+ ```
383
+
384
+ ## Rails 2.3 note
385
+
386
+ In order to copy the cells rake tasks to your app, run
387
+
388
+ ```shell
389
+ script/generate cells_install
390
+ ```
391
+
392
+ ## Capture Support
393
+
394
+ If you need a global `#content_for` use the [cells-capture](https://github.com/apotonick/cells-capture) gem.
395
+
396
+ ## More features
397
+
398
+ Cells can do more.
399
+
400
+ * __No Limits__. Have as many cells in your page as you need - no limitation to your `render_cell` calls.
401
+ * __Cell Nesting__. Have complex cell hierarchies as you can call `render_cell` within cells, too.
402
+
403
+ Go for it, you'll love it!
404
+
405
+
406
+ ## LICENSE
407
+
408
+ Copyright (c) 2007-2013, Nick Sutterer
409
+
410
+ Copyright (c) 2007-2008, Solide ICT by Peter Bex and Bob Leers
411
+
412
+ Released under the MIT License.