cells 3.4.3 → 3.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,11 @@
1
+ h2. 3.4.4
2
+
3
+ h3. Changes
4
+ * Cells.setup now yields Cell::Base, so you can really call append_view_path and friends here.
5
+ * 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
6
+ * added TestCase#in_view to test helpers in a real cell view.
7
+
8
+
1
9
  h2. 3.4.3
2
10
 
3
11
  h3. Changes
data/Gemfile CHANGED
@@ -1,10 +1,11 @@
1
1
  source :gemcutter
2
2
  gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3' # needed in router_test, whatever.
3
3
 
4
- #gem "rails" , :path => "/home/nick/projects/rails"
5
- gem "rails", '3.0.0' #, :git => "http://github.com/rails/rails.git"
4
+ #gem "rails" , :path => "/home/nick/projects/rayls"
5
+ gem "rails", '~> 3.0'
6
6
  gem "haml"
7
7
 
8
8
  group :test do
9
9
  gem "shoulda"
10
10
  end
11
+ gem "rack", :git => "git://github.com/rack/rack.git"
@@ -94,6 +94,29 @@ The distinction between partials and views is making things more complex, so why
94
94
  = render :view => 'items'
95
95
 
96
96
 
97
+ == View Inheritance
98
+
99
+ This is where OOP comes back to your view.
100
+
101
+ * <b>Inherit code</b> into your cells by deriving more abstract cells.
102
+ * <b>Inherit views</b> from ancesting cells.
103
+
104
+ === Builders
105
+
106
+ Let +render_cell+ take care of creating the right cell. Just configure your super-cell properly.
107
+
108
+ class LoginCell < Cell::Rails
109
+ build do
110
+ UnauthorizedUserCell unless logged_in?
111
+ end
112
+
113
+ A call to
114
+
115
+ render_cell(:login, :box)
116
+
117
+ will render the configured +UnauthorizedUserCell+ instead of the original +LoginCell+ if the login test fails.
118
+
119
+
97
120
  == Caching
98
121
 
99
122
  Cells do strict view caching. No cluttered fragment caching. Add
@@ -10,9 +10,52 @@ module Cell
10
10
  cell.render_state(state)
11
11
  end
12
12
 
13
- # Creates a cell instance.
13
+ # Creates a cell instance. Note that this method calls builders which were attached to the
14
+ # class with Cell::Base.build - this might lead to a different cell being returned.
14
15
  def create_cell_for(controller, name, opts={})
15
- class_from_cell_name(name).new(controller, opts)
16
+ build_class_for(controller, class_from_cell_name(name), opts).
17
+ new(controller, opts)
18
+ end
19
+
20
+ # Adds a builder to the cell class. Builders are used in #render_cell to find out the concrete
21
+ # class for rendering. This is helpful if you frequently want to render subclasses according
22
+ # to different circumstances (e.g. login situations) and you don't want to place these deciders in
23
+ # your view code.
24
+ #
25
+ # Passes the opts hash from #render_cell into the block. The block is executed in controller context.
26
+ # Multiple build blocks are ORed, if no builder matches the building cell is used.
27
+ #
28
+ # Example:
29
+ #
30
+ # Consider two different user box cells in your app.
31
+ #
32
+ # class AuthorizedUserBox < UserInfoBox
33
+ # end
34
+ #
35
+ # class AdminUserBox < UserInfoBox
36
+ # end
37
+ #
38
+ # Now you don't want to have deciders all over your views - use a declarative builder.
39
+ #
40
+ # UserInfoBox.build do |opts|
41
+ # AuthorizedUserBox if user_signed_in?
42
+ # AdminUserBox if admin_signed_in?
43
+ # end
44
+ #
45
+ # In your view #render_cell will instantiate the right cell for you now.
46
+ def build(&block)
47
+ builders << block
48
+ end
49
+
50
+ def build_class_for(controller, target_class, opts)
51
+ target_class.builders.each do |blk|
52
+ res = controller.instance_exec(opts, &blk) and return res
53
+ end
54
+ target_class
55
+ end
56
+
57
+ def builders
58
+ @builders ||= []
16
59
  end
17
60
 
18
61
  # Return the default view path for +state+. Override this if you cell has a differing naming style.
@@ -48,8 +48,6 @@ module Cell
48
48
  include Rendering
49
49
  include Caching
50
50
 
51
-
52
- cattr_accessor :url_helpers ### TODO: discuss if we really need that or can handle that in cells.rb already.
53
51
  attr_reader :parent_controller
54
52
 
55
53
  abstract!
@@ -67,12 +65,10 @@ module Cell
67
65
 
68
66
  View.class_eval do
69
67
  include controller._helpers
70
- include Cell::Base.url_helpers if Cell::Rails.url_helpers
68
+ include controller._routes.url_helpers
71
69
  end
72
70
 
73
-
74
71
  @view_context_class ||= View
75
- ### DISCUSS: copy behaviour from abstract_controller/rendering-line 49? (helpers)
76
72
  end
77
73
 
78
74
  def self.controller_path
@@ -89,6 +89,27 @@ module Cell
89
89
  cell.instance_eval &block if block_given?
90
90
  cell
91
91
  end
92
+
93
+ # Execute the passed +block+ in a real view context of +cell_class+.
94
+ # Usually you'd test helpers here.
95
+ #
96
+ # Example:
97
+ #
98
+ # assert_equal("<h1>Modularity rocks.</h1>", in_view do content_tag(:h1, "Modularity rocks."))
99
+ def in_view(cell_class, &block)
100
+ subject = cell(cell_class, :block => block)
101
+ setup_test_states_in(subject) # add #in_view to subject cell.
102
+ subject.render_state(:in_view)
103
+ end
104
+
105
+ protected
106
+ def setup_test_states_in(cell)
107
+ cell.instance_eval do
108
+ def in_view
109
+ render :inline => "<%= instance_exec(&block) %>", :locals => {:block => @opts[:block]}
110
+ end
111
+ end
112
+ end
92
113
  end
93
114
 
94
115
  include TestMethods
@@ -97,6 +118,7 @@ module Cell
97
118
  include AssertSelect
98
119
 
99
120
  extend ActionController::TestCase::Behavior::ClassMethods
121
+ class_attribute :_controller_class
100
122
 
101
123
 
102
124
  attr_reader :last_invoke
@@ -66,40 +66,22 @@ require 'cell/rails'
66
66
  require 'cell/test_case' if Object.const_defined?("Rails") and Rails.env == "test"
67
67
 
68
68
  module Cells
69
- # Any config should be placed here using +mattr_accessor+.
70
-
71
69
  # Default view paths for Cells.
72
70
  DEFAULT_VIEW_PATHS = [
73
71
  File.join('app', 'cells'),
74
72
  File.join('app', 'cells', 'layouts')
75
73
  ]
76
74
 
77
- class << self
78
- # Holds paths in which Cells should look for cell views (i.e. view template files).
79
- #
80
- # == Default:
81
- #
82
- # * +app/cells+
83
- # * +app/cells/layouts+
84
- #
85
- def self.view_paths
86
- ::Cell::Base.view_paths
87
- end
88
- def self.view_paths=(paths)
89
- ::Cell::Base.view_paths = paths
90
- end
91
- end
92
-
93
- # Cells setup/configuration helper for initializer.
75
+ # Setup your special needs for Cells here. Use this to add new view paths.
94
76
  #
95
- # == Usage/Examples:
77
+ # Example:
96
78
  #
97
79
  # Cells.setup do |config|
98
- # config.cell_view_paths << Rails.root.join('lib', 'cells')
80
+ # config.append_view_path << "app/view_models"
99
81
  # end
100
82
  #
101
83
  def self.setup
102
- yield(self)
84
+ yield(Cell::Base)
103
85
  end
104
86
  end
105
87
 
@@ -115,8 +97,6 @@ class Cells::Railtie < Rails::Railtie
115
97
  Cell::Rails.class_eval do
116
98
  include app.routes.url_helpers
117
99
  end
118
-
119
- Cell::Base.url_helpers = app.routes.url_helpers
120
100
  end
121
101
 
122
102
  rake_tasks do
@@ -1,3 +1,3 @@
1
1
  module Cells
2
- VERSION = '3.4.3'
2
+ VERSION = '3.4.4'
3
3
  end
@@ -1,5 +1,16 @@
1
1
  require 'test_helper'
2
2
 
3
+ class MusicianCell < Cell::Base
4
+
5
+ end
6
+
7
+ class PianistCell < MusicianCell
8
+ end
9
+
10
+ class SingerCell < MusicianCell
11
+ end
12
+
13
+
3
14
  class CellModuleTest < ActiveSupport::TestCase
4
15
  include Cell::TestCase::TestMethods
5
16
 
@@ -20,9 +31,88 @@ class CellModuleTest < ActiveSupport::TestCase
20
31
  assert_equal "Doo", html
21
32
  assert flag
22
33
  end
34
+
35
+
23
36
  end
24
37
 
38
+ context "create_cell_for" do
39
+ should "call the cell's builders, eventually returning a different class" do
40
+ class DrummerCell < BassistCell
41
+ build do
42
+ BassistCell
43
+ end
44
+ end
45
+
46
+ assert_instance_of BassistCell, Cell::Base.create_cell_for(@controller, "cell_module_test/drummer", :play)
47
+ end
48
+ end
25
49
 
50
+ context "calling build" do
51
+ setup do
52
+ @controller.class_eval do
53
+ attr_accessor :bassist
54
+ end
55
+
56
+ MusicianCell.build do
57
+ BassistCell if bassist
58
+ end
59
+ end
60
+
61
+ teardown do
62
+ MusicianCell.class_eval do
63
+ @builders = false
64
+ end
65
+ end
66
+
67
+ should "execute the block in controller context" do
68
+ @controller.bassist = true
69
+ assert_equal BassistCell, Cell::Base.build_class_for(@controller, MusicianCell, {})
70
+ end
71
+
72
+ should "limit the builder to the receiving class" do
73
+ assert_equal PianistCell, Cell::Base.build_class_for(@controller, PianistCell, {}) # don't inherit anything.
74
+ @controller.bassist = true
75
+ assert_equal BassistCell, Cell::Base.build_class_for(@controller, MusicianCell, {})
76
+ end
77
+
78
+ should "chain build blocks and execute them by ORing them in the same order" do
79
+ MusicianCell.build do
80
+ PianistCell unless bassist
81
+ end
82
+
83
+ MusicianCell.build do
84
+ UnknownCell # should never be executed.
85
+ end
86
+
87
+ assert_equal PianistCell, Cell::Base.build_class_for(@controller, MusicianCell, {}) # bassist is false.
88
+ @controller.bassist = true
89
+ assert_equal BassistCell, Cell::Base.build_class_for(@controller, MusicianCell, {})
90
+ end
91
+
92
+ should "use the original cell if no builder matches" do
93
+ assert_equal MusicianCell, Cell::Base.build_class_for(@controller, MusicianCell, {}) # bassist is false.
94
+ end
95
+
96
+ should "stop at the first builder returning a valid cell" do
97
+
98
+ end
99
+
100
+ should "pass options to the block" do
101
+ BassistCell.build do |opts|
102
+ SingerCell if opts[:sing_the_song]
103
+ end
104
+ assert_equal BassistCell, Cell::Base.build_class_for(@controller, BassistCell, {})
105
+ assert_equal SingerCell, Cell::Base.build_class_for(@controller, BassistCell, {:sing_the_song => true})
106
+ end
107
+
108
+ should "create the original target class if no block matches" do
109
+ assert_equal PianistCell, Cell::Base.build_class_for(@controller, PianistCell, {})
110
+ end
111
+
112
+ should "builders should return an empty array per default" do
113
+ assert_equal [], PianistCell.builders
114
+ end
115
+ end
26
116
 
27
117
  should "provide possible_paths_for_state" do
28
118
  assert_equal ["bad_guitarist/play", "bassist/play", "cell/rails/play"], cell(:bad_guitarist).possible_paths_for_state(:play)
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class CellsModuleTest < ActiveSupport::TestCase
4
+ context "Cells" do
5
+ setup do
6
+ @old_view_paths = Cell::Base.view_paths.clone
7
+ end
8
+
9
+ should "provide .setup" do
10
+ Cells.setup do |c|
11
+ c.append_view_path "/road/to/nowhere"
12
+ end
13
+
14
+ assert_equal "/road/to/nowhere", Cell::Base.view_paths.last.to_s
15
+ end
16
+
17
+ teardown do
18
+ Cell::Base.view_paths = @old_view_paths
19
+ end
20
+ end
21
+ end
@@ -5,18 +5,16 @@ class RailsViewTest < ActiveSupport::TestCase
5
5
 
6
6
  context "A cell view" do
7
7
  context "calling render :partial" do
8
- should "render the cell partial in bassist/dii" do
9
- BassistCell.class_eval do
10
- def compose; @partial = "dii"; render; end
11
- end
12
- assert_equal "Dumm Dii", render_cell(:bassist, :compose)
8
+ should "render the local cell partial in bassist/dii" do
9
+ assert_equal("Dii", in_view(:bassist) do
10
+ render :partial => 'dii'
11
+ end)
13
12
  end
14
13
 
15
- should "render the cell partial in bad_guitarist/dii" do
16
- BassistCell.class_eval do
17
- def compose; @partial = "bad_guitarist/dii"; render; end
18
- end
19
- assert_equal "Dumm Dooom", render_cell(:bassist, :compose)
14
+ should "render the foreign cell partial in bad_guitarist/dii" do
15
+ assert_equal("Dooom", in_view(:bassist) do
16
+ render :partial => "bad_guitarist/dii"
17
+ end)
20
18
  end
21
19
  end
22
20
 
@@ -49,7 +49,7 @@ class TestCaseTest < Cell::TestCase
49
49
  assert_equal SingerCell, SingerCellTest.new(:cell_test).class.controller_class
50
50
  end
51
51
 
52
- context "with invoke" do
52
+ context "with #invoke" do
53
53
  setup do
54
54
  self.class.tests BassistCell
55
55
  end
@@ -73,6 +73,24 @@ class TestCaseTest < Cell::TestCase
73
73
  assert_select "a", "vd.com"
74
74
  end
75
75
  end
76
+
77
+ context "#setup_test_states_in" do
78
+ should "add the :in_view state" do
79
+ c = cell(:bassist, :block => lambda{"Cells rock."})
80
+ assert_not c.respond_to?(:in_view)
81
+
82
+ setup_test_states_in(c)
83
+ assert_equal "Cells rock.", c.render_state(:in_view)
84
+ end
85
+ end
86
+
87
+ context "#in_view" do
88
+ should "execute the block in a real view" do
89
+ content = "Cells rule."
90
+ @test.setup
91
+ assert_equal("<h1>Cells rule.</h1>", @test.in_view(:bassist) do content_tag("h1", content) end)
92
+ end
93
+ end
76
94
  end
77
95
  end
78
96
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 3
7
7
  - 4
8
- - 3
9
- version: 3.4.3
8
+ - 4
9
+ version: 3.4.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Nick Sutterer
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-13 00:00:00 +01:00
17
+ date: 2011-01-02 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -88,6 +88,7 @@ files:
88
88
  - test/dummy/app/views/musician/hamlet.html.haml
89
89
  - test/dummy/app/views/musician/featured.html.erb
90
90
  - test/dummy/app/helpers/application_helper.rb
91
+ - test/cells_module_test.rb
91
92
  - test/test_case_test.rb
92
93
  - test/helper_test.rb
93
94
  - test/cell_module_test.rb
@@ -184,6 +185,7 @@ test_files:
184
185
  - test/dummy/app/views/musician/hamlet.html.haml
185
186
  - test/dummy/app/views/musician/featured.html.erb
186
187
  - test/dummy/app/helpers/application_helper.rb
188
+ - test/cells_module_test.rb
187
189
  - test/test_case_test.rb
188
190
  - test/helper_test.rb
189
191
  - test/cell_module_test.rb