cells 3.4.3 → 3.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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