cells 3.3.10 → 3.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/CHANGES +0 -8
  2. data/Gemfile +4 -1
  3. data/README.rdoc +91 -107
  4. data/Rakefile +0 -4
  5. data/lib/cell.rb +5 -6
  6. data/lib/{cells/cell → cell}/active_helper.rb +1 -1
  7. data/lib/cell/base.rb +134 -0
  8. data/lib/cell/base_methods.rb +100 -0
  9. data/lib/cell/caching.rb +153 -0
  10. data/lib/cell/rails.rb +239 -0
  11. data/lib/cells.rb +25 -3
  12. data/lib/cells/assertions_helper.rb +1 -1
  13. data/lib/cells/helpers/capture_helper.rb +3 -3
  14. data/lib/cells/rails.rb +65 -4
  15. data/lib/cells/version.rb +3 -1
  16. data/rails_generators/cell/cell_generator.rb +47 -35
  17. data/rails_generators/cell/templates/cell.rb +1 -1
  18. data/rails_generators/cells_install/cells_install_generator.rb +5 -3
  19. data/rails_generators/erb/cell_generator.rb +20 -0
  20. data/rails_generators/{cell → erb}/templates/view.html.erb +0 -0
  21. data/test/active_helper_test.rb +1 -0
  22. data/test/app/cells/bad_guitarist_cell.rb +2 -0
  23. data/test/app/cells/bassist_cell.rb +1 -1
  24. data/test/app/controllers/musician_controller.rb +16 -0
  25. data/test/assertions_helper_test.rb +8 -18
  26. data/test/base_methods_test.rb +40 -0
  27. data/test/cell_generator_test.rb +33 -21
  28. data/test/helper_test.rb +31 -123
  29. data/test/rails/caching_test.rb +215 -0
  30. data/test/rails/capture_test.rb +52 -0
  31. data/test/rails/cells_test.rb +88 -0
  32. data/test/rails/integration_test.rb +37 -0
  33. data/test/rails/render_test.rb +140 -0
  34. data/test/rails/router_test.rb +74 -0
  35. data/test/rails/view_test.rb +24 -0
  36. data/test/test_helper.rb +30 -29
  37. metadata +68 -133
  38. data/.gitignore +0 -3
  39. data/about.yml +0 -7
  40. data/cells.gemspec +0 -26
  41. data/lib/cells/cell.rb +0 -16
  42. data/lib/cells/cell/base.rb +0 -470
  43. data/lib/cells/cell/caching.rb +0 -163
  44. data/lib/cells/cell/test_case.rb +0 -158
  45. data/lib/cells/cell/view.rb +0 -55
  46. data/lib/cells/rails/action_controller.rb +0 -37
  47. data/lib/cells/rails/action_view.rb +0 -37
  48. data/rails/init.rb +0 -44
  49. data/rails_generators/cells_install/templates/tasks.rake +0 -6
  50. data/test/app/cells/a/another_state.html.erb +0 -1
  51. data/test/app/cells/a/existing_view.html.erb +0 -1
  52. data/test/app/cells/a/inherited_view.html.erb +0 -1
  53. data/test/app/cells/a/inherited_view.js.erb +0 -1
  54. data/test/app/cells/a/view_with_locals.html.erb +0 -1
  55. data/test/app/cells/a/view_with_render_call.html.erb +0 -1
  56. data/test/app/cells/b/existing_view.html.erb +0 -1
  57. data/test/app/cells/b/existing_view.js.erb +0 -1
  58. data/test/app/cells/b/layouts/metal.html.erb +0 -1
  59. data/test/app/cells/b/view_with_render_call.html.erb +0 -1
  60. data/test/app/cells/bassist/jam.html.erb +0 -3
  61. data/test/app/cells/bassist/play.html.erb +0 -1
  62. data/test/app/cells/cells_test_one/renamed_instance_view.html.erb +0 -1
  63. data/test/app/cells/cells_test_one/super_state.html.erb +0 -1
  64. data/test/app/cells/cells_test_one_cell.rb +0 -20
  65. data/test/app/cells/cells_test_two_cell.rb +0 -4
  66. data/test/app/cells/helper_using/state_using_application_helper.html.erb +0 -3
  67. data/test/app/cells/helper_using/state_with_automatic_helper_invocation.html.erb +0 -3
  68. data/test/app/cells/helper_using/state_with_helper_invocation.html.erb +0 -3
  69. data/test/app/cells/helper_using/state_with_helper_method_invocation.html.erb +0 -3
  70. data/test/app/cells/layouts/metal.html.erb +0 -1
  71. data/test/app/cells/my_child/hello.html.erb +0 -1
  72. data/test/app/cells/my_mother/bye.html.erb +0 -1
  73. data/test/app/cells/my_mother/hello.html.erb +0 -1
  74. data/test/app/cells/my_test/_broken_partial.html.erb +0 -1
  75. data/test/app/cells/my_test/_partial.html.erb +0 -1
  76. data/test/app/cells/my_test/state_with_instance_var.html.erb +0 -1
  77. data/test/app/cells/my_test/state_with_link_to.html.erb +0 -3
  78. data/test/app/cells/my_test/state_with_not_included_helper_method.html.erb +0 -8
  79. data/test/app/cells/my_test/view_containing_broken_partial.html.erb +0 -3
  80. data/test/app/cells/my_test/view_containing_nonexistant_partial.html.erb +0 -3
  81. data/test/app/cells/my_test/view_containing_partial.html.erb +0 -3
  82. data/test/app/cells/my_test/view_containing_partial_without_cell_name.html.erb +0 -3
  83. data/test/app/cells/my_test/view_in_local_test_views_dir.html.erb +0 -1
  84. data/test/app/cells/my_test/view_with_explicit_english_translation.en.html.erb +0 -1
  85. data/test/app/cells/my_test/view_with_explicit_english_translation.html.erb +0 -1
  86. data/test/app/cells/my_test/view_with_instance_var.html.erb +0 -4
  87. data/test/app/cells/really_module/nested/happy_state.html.erb +0 -1
  88. data/test/app/cells/really_module/nested_cell.rb +0 -11
  89. data/test/app/cells/simple/two_templates_state.html.mytpl +0 -1
  90. data/test/app/cells/simple_cell.rb +0 -7
  91. data/test/app/cells/test/beep.html.erb +0 -1
  92. data/test/app/cells/test/state_invoking_capture.html.erb +0 -7
  93. data/test/app/cells/test/state_invoking_content_for.html.erb +0 -7
  94. data/test/app/cells/test/state_invoking_content_for_twice.html.erb +0 -9
  95. data/test/app/cells/test/state_with_not_included_helper_method.html.erb +0 -8
  96. data/test/app/cells/two_helpers_including/state_using_another_helper.html.erb +0 -3
  97. data/test/bugs_test.rb +0 -23
  98. data/test/caching_test.rb +0 -270
  99. data/test/capture_helper_test.rb +0 -59
  100. data/test/cells_test.rb +0 -352
  101. data/test/rails_test.rb +0 -35
  102. data/test/render_test.rb +0 -305
  103. data/test/test_case_test.rb +0 -106
@@ -0,0 +1,153 @@
1
+ # encoding: utf-8
2
+
3
+ # To improve performance rendered state views can be cached using Rails' caching
4
+ # mechanism.
5
+ # If this it configured (e.g. using our fast friend memcached) all you have to do is to
6
+ # tell Cells which state to cache. You can further attach a proc to expire the
7
+ # cached view.
8
+ #
9
+ # As always I stole a lot of code, this time from Lance Ivy <cainlevy@gmail.com> and
10
+ # his fine components plugin at http://github.com/cainlevy/components.
11
+
12
+ module Cell
13
+ module Caching
14
+
15
+ def self.included(base) #:nodoc:
16
+ base.class_eval do
17
+ extend ClassMethods
18
+
19
+ alias_method_chain :render_state, :caching
20
+ end
21
+ end
22
+
23
+ module ClassMethods
24
+ # Activate caching for the state <tt>state</tt>. If no other options are passed
25
+ # the view will be cached forever.
26
+ #
27
+ # You may pass a Proc or a Symbol as cache expiration <tt>version_proc</tt>.
28
+ # This method is called every time the state is rendered, and is expected to return a
29
+ # Hash containing the cache key ingredients.
30
+ #
31
+ # Additional options will be passed directly to the cache store when caching the state.
32
+ # Useful for simply setting a TTL for a cached state.
33
+ # Note that you may omit the <tt>version_proc</tt>.
34
+ #
35
+ #
36
+ # Example:
37
+ # class CachingCell < ::Cell::Base
38
+ # cache :versioned_cached_state, Proc.new{ {:version => 0} }
39
+ # would result in the complete cache key
40
+ # cells/CachingCell/versioned_cached_state/version=0
41
+ #
42
+ # If you provide a symbol, you can access the cell instance directly in the versioning
43
+ # method:
44
+ #
45
+ # class CachingCell < ::Cell::Base
46
+ # cache :cached_state, :my_cache_version
47
+ #
48
+ # def my_cache_version
49
+ # { :user => current_user.id,
50
+ # :item_id => params[:item] }
51
+ # }
52
+ # end
53
+ # results in a very specific cache key, for customized caching:
54
+ # cells/CachingCell/cached_state/user=18/item_id=1
55
+ #
56
+ # You may also set a TTL only, e.g. when using the memcached store:
57
+ #
58
+ # cache :cached_state, :expires_in => 3.minutes
59
+ #
60
+ # Or use both, having a versioning proc <em>and</em> a TTL expiring the state as a fallback
61
+ # after a certain amount of time.
62
+ #
63
+ # cache :cached_state, Proc.new { {:version => 0} }, :expires_in => 10.minutes
64
+ #--
65
+ ### TODO: implement for string, nil.
66
+ ### DISCUSS: introduce return method #sweep ? so the Proc can explicitly
67
+ ### delegate re-rendering to the outside.
68
+ #--
69
+ def cache(state, version_proc=nil, cache_opts={})
70
+ if version_proc.is_a?(Hash)
71
+ cache_opts = version_proc
72
+ version_proc = nil
73
+ end
74
+
75
+ version_procs[state] = version_proc
76
+ cache_options[state] = cache_opts
77
+ end
78
+
79
+ def version_procs
80
+ @version_procs ||= {}
81
+ end
82
+
83
+ def cache_options
84
+ @cache_options ||= {}
85
+ end
86
+
87
+ def cache_store #:nodoc:
88
+ ::ActionController::Base.cache_store
89
+ end
90
+
91
+ def cache_key_for(cell_class, state, args = {}) #:nodoc:
92
+ key_pieces = [cell_class, state]
93
+
94
+ args.collect{|a,b| [a.to_s, b]}.sort.each{ |k,v| key_pieces << "#{k}=#{v}" }
95
+ key = key_pieces.join('/')
96
+
97
+ ::ActiveSupport::Cache.expand_cache_key(key, :cells)
98
+ end
99
+
100
+ def expire_cache_key(key, opts=nil)
101
+ cache_store.delete(key, opts)
102
+ end
103
+
104
+ def cache_configured?
105
+ ::ActionController::Base.cache_configured?
106
+ end
107
+ end
108
+
109
+ def render_state_with_caching(state, request=ActionDispatch::Request.new({}))
110
+ return render_state_without_caching(state, request) unless state_cached?(state)
111
+
112
+ key = cache_key(state, call_version_proc_for_state(state))
113
+ ### DISCUSS: see sweep discussion at #cache.
114
+
115
+ # cache hit:
116
+ if content = read_fragment(key)
117
+ return content
118
+ end
119
+ # re-render:
120
+ return write_fragment(key, render_state_without_caching(state, request), self.class.cache_options[state])
121
+ end
122
+
123
+ def read_fragment(key, cache_options = nil) #:nodoc:
124
+ returning self.class.cache_store.read(key, cache_options) do |content|
125
+ log "Cell Cache hit: #{key}" unless content.blank?
126
+ end
127
+ end
128
+
129
+ def write_fragment(key, content, cache_opts = nil) #:nodoc:
130
+ log "Cell Cache miss: #{key}"
131
+ self.class.cache_store.write(key, content, cache_opts)
132
+ content
133
+ end
134
+
135
+ # Call the versioning Proc for the respective state.
136
+ def call_version_proc_for_state(state)
137
+ version_proc = self.class.version_procs[state]
138
+
139
+ return {} unless version_proc # call to #cache was without any args.
140
+
141
+ return version_proc.call(self) if version_proc.kind_of?(Proc)
142
+ send(version_proc)
143
+ end
144
+
145
+ def cache_key(state, args = {}) #:nodoc:
146
+ self.class.cache_key_for(self.cell_name, state, args)
147
+ end
148
+
149
+ def state_cached?(state)
150
+ self.class.version_procs.has_key?(state)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,239 @@
1
+ require 'abstract_controller'
2
+ require 'action_controller'
3
+
4
+ module Cell
5
+ class Rails < ActionController::Metal
6
+ include BaseMethods
7
+ include AbstractController
8
+ include Rendering, Layouts, Helpers, Callbacks, Translation
9
+ include ActionController::RequestForgeryProtection
10
+ #include AbstractController::Logger
11
+
12
+
13
+ #include Cell::ActiveHelper
14
+ cattr_accessor :url_helpers ### TODO: discuss if we really need that or can handle that in cells.rb already.
15
+
16
+ abstract!
17
+
18
+ ### DISCUSS: should we pass the parent_controller here?
19
+ def initialize(parent_controller=nil, options={}) ### FIXME: move to BaseMethods.
20
+ @parent_controller = parent_controller
21
+ @opts = @options = options
22
+ end
23
+ attr_reader :parent_controller
24
+
25
+
26
+ def log(*args); end
27
+
28
+ class View < ActionView::Base
29
+ def render(options = {}, locals = {}, &block)
30
+ if options[:state] or options[:view]
31
+ return @_controller.render(options, &block)
32
+ end
33
+
34
+ super
35
+ end
36
+ end
37
+
38
+ def self.view_context_class
39
+ controller = self
40
+ # Unfortunately, there is currently an abstraction leak between AC::Base
41
+ # and AV::Base which requires having the URL helpers in both AC and AV.
42
+ # To do this safely at runtime for tests, we need to bump up the helper serial
43
+ # to that the old AV subclass isn't cached.
44
+ #
45
+ # TODO: Make this unnecessary
46
+ #if @controller
47
+ # @controller.singleton_class.send(:include, _routes.url_helpers)
48
+ # @controller.view_context_class = Class.new(@controller.view_context_class) do
49
+ # include _routes.url_helpers
50
+
51
+ View.class_eval do
52
+
53
+ include controller._helpers
54
+
55
+ include Cell::Base.url_helpers if Cell::Rails.respond_to?(:url_helpers) and Cell::Rails.url_helpers
56
+ end
57
+
58
+
59
+ @view_context_class ||= View
60
+ ### DISCUSS: copy behaviour from abstract_controller/rendering-line 49? (helpers)
61
+ end
62
+
63
+ def self.controller_path
64
+ @controller_path ||= name.sub(/Cell$/, '').underscore unless anonymous?
65
+ end
66
+
67
+ def process(*) # defined in AC::Metal.
68
+ self.response_body = super ### TODO: discuss with yehuda.
69
+ end
70
+
71
+ #attr_internal :request
72
+ delegate :request, :to => :parent_controller
73
+ delegate :config, :to => :parent_controller # DISCUSS: what if a cell has its own config (eg for assets, cells/bassist/images)?
74
+ # DISCUSS: let @controller point to @parent_controller in views, and @cell is the actual real controller?
75
+
76
+
77
+ def render_state(state, request=ActionDispatch::Request.new({})) ### FIXME: where to set Request if none given? leave blank?
78
+ rack_response = dispatch(state, parent_controller.request)
79
+
80
+ return rack_response[2].last if rack_response[2].kind_of?(Array) ### FIXME: HACK for testing, wtf is going on here?
81
+ rack_response[2] ### TODO: discuss with yehuda.
82
+ # rack_response in test mode: [nil, nil, ["Doo"]]
83
+ # rack_response in dev mode: [nil, nil, "<div>..."]
84
+ end
85
+ include Cell::Caching
86
+
87
+
88
+
89
+
90
+ class << self
91
+ def state2view_cache
92
+ @state2view_cache ||= {}
93
+ end
94
+ end
95
+
96
+
97
+ # Renders the view for the current state and returns the markup for the component.
98
+ # Usually called and returned at the end of a state method.
99
+ #
100
+ # ==== Options
101
+ # * <tt>:view</tt> - Specifies the name of the view file to render. Defaults to the current state name.
102
+ # * <tt>:template_format</tt> - Allows using a format different to <tt>:html</tt>.
103
+ # * <tt>:layout</tt> - If set to a valid filename inside your cell's view_paths, the current state view will be rendered inside the layout (as known from controller actions). Layouts should reside in <tt>app/cells/layouts</tt>.
104
+ # * <tt>:locals</tt> - Makes the named parameters available as variables in the view.
105
+ # * <tt>:text</tt> - Just renders plain text.
106
+ # * <tt>:inline</tt> - Renders an inline template as state view. See ActionView::Base#render for details.
107
+ # * <tt>:file</tt> - Specifies the name of the file template to render.
108
+ # * <tt>:nothing</tt> - Will make the component kinda invisible and doesn't invoke the rendering cycle.
109
+ # * <tt>:state</tt> - Instantly invokes another rendering cycle for the passed state and returns.
110
+ # Example:
111
+ # class MyCell < ::Cell::Base
112
+ # def my_first_state
113
+ # # ... do something
114
+ # render
115
+ # end
116
+ #
117
+ # will just render the view <tt>my_first_state.html</tt>.
118
+ #
119
+ # def my_first_state
120
+ # # ... do something
121
+ # render :view => :my_first_state, :layout => 'metal'
122
+ # end
123
+ #
124
+ # will also use the view <tt>my_first_state.html</tt> as template and even put it in the layout
125
+ # <tt>metal</tt> that's located at <tt>$RAILS_ROOT/app/cells/layouts/metal.html.erb</tt>.
126
+ #
127
+ # def say_your_name
128
+ # render :locals => {:name => "Nick"}
129
+ # end
130
+ #
131
+ # will make the variable +name+ available in the view <tt>say_your_name.html</tt>.
132
+ #
133
+ # def say_your_name
134
+ # render :nothing => true
135
+ # end
136
+ #
137
+ # will render an empty string thus keeping your name a secret.
138
+ #
139
+ #
140
+ # ==== Where have all the partials gone?
141
+ # In Cells we abandoned the term 'partial' in favor of plain 'views' - we don't need to distinguish
142
+ # between both terms. A cell view is both, a view and a kind of partial as it represents only a small
143
+ # part of the page.
144
+ # Just use <tt>:view</tt> and enjoy.
145
+ def render(opts={})
146
+ render_view_for(opts, self.action_name)
147
+ end
148
+
149
+
150
+
151
+ # Climbs up the inheritance hierarchy of the Cell, looking for a view
152
+ # for the current <tt>state</tt> in each level.
153
+ # As soon as a view file is found it is returned as an ActionView::Template
154
+ # instance.
155
+ ### DISCUSS: moved to Cell::View#find_template in rainhead's fork:
156
+ def find_family_view_for_state(state)
157
+ missing_template_exception = nil
158
+
159
+ possible_paths_for_state(state).each do |template_path|
160
+ # we need to catch MissingTemplate, since we want to try for all possible family views.
161
+ begin
162
+ template = find_template(template_path)
163
+ return template if template
164
+ rescue ::ActionView::MissingTemplate => missing_template_exception
165
+ end
166
+ end
167
+
168
+ raise missing_template_exception
169
+ end
170
+
171
+ # In production mode, the view for a state/template_format is cached.
172
+ ### DISCUSS: ActionView::Base already caches results for #pick_template, so maybe
173
+ ### we should just cache the family path for a state/format?
174
+ def find_family_view_for_state_with_caching(state)
175
+ return find_family_view_for_state(state) unless self.class.cache_configured?
176
+
177
+ # in production mode:
178
+ key = "#{state}/#{action_view.template_format}"
179
+ state2view = self.class.state2view_cache
180
+ state2view[key] || state2view[key] = find_family_view_for_state(state, action_view)
181
+ end
182
+
183
+
184
+
185
+
186
+
187
+ # Render the view belonging to the given state. Will raise ActionView::MissingTemplate
188
+ # if it can not find one of the requested view template. Note that this behaviour was
189
+ # introduced in cells 2.3 and replaces the former warning message.
190
+ def render_view_for(opts, state)
191
+ return '' if opts[:nothing]
192
+
193
+ ### TODO: dispatch dynamically:
194
+ if opts[:text] ### FIXME: generic option?
195
+ elsif opts[:inline]
196
+ elsif opts[:file]
197
+ elsif opts[:state] ### FIXME: generic option
198
+ opts[:text] = render_state(opts[:state])
199
+ else
200
+ # handle :layout, :template_format, :view
201
+ opts = defaultize_render_options_for(opts, state)
202
+
203
+ # set instance vars, include helpers:
204
+ #prepare_action_view_for(action_view, opts)
205
+
206
+ #template = find_family_view_for_state_with_caching(opts[:view], action_view)
207
+ template = find_family_view_for_state(opts[:view])
208
+ opts[:template] = template
209
+ end
210
+
211
+ opts = sanitize_render_options(opts)
212
+
213
+ render_to_string(opts)
214
+ end
215
+
216
+ # Defaultize the passed options from #render.
217
+ def defaultize_render_options_for(opts, state)
218
+ opts[:template_format] ||= self.class.default_template_format
219
+ opts[:view] ||= state
220
+ opts
221
+ end
222
+
223
+ def prepare_action_view_for(action_view, opts)
224
+ # make helpers available:
225
+ include_helpers_in_class(action_view.class)
226
+
227
+ import_active_helpers_into(action_view) # in Cells::Cell::ActiveHelper.
228
+
229
+ action_view.assigns = assigns_for_view # make instance vars available.
230
+ action_view.template_format = opts[:template_format]
231
+ end
232
+
233
+ # Prepares <tt>opts</tt> to be passed to ActionView::Base#render by removing
234
+ # unknown parameters.
235
+ def sanitize_render_options(opts)
236
+ opts.except!(:view, :state)
237
+ end
238
+ end
239
+ end
@@ -21,9 +21,14 @@ rescue
21
21
  require 'action_view'
22
22
  end
23
23
 
24
- require 'cells/cell'
25
- require 'cells/helpers'
24
+ require 'cell/base_methods'
25
+
26
26
  require 'cell'
27
+ require 'cells/rails' # helper.
28
+ require 'cell/rails'
29
+
30
+ require 'cells/helpers'
31
+
27
32
 
28
33
  module Cells
29
34
  # Any config should be placed here using +mattr_accessor+.
@@ -63,7 +68,24 @@ module Cells
63
68
  end
64
69
  end
65
70
 
71
+ Cell::Base = Cell::Rails
72
+
66
73
  Cell::Base.view_paths = Cells::DEFAULT_VIEW_PATHS if Cell::Base.view_paths.blank?
67
74
 
68
75
  require 'cells/rails'
69
- require 'cells/cell/test_case' if ENV['RAILS_ENV'] == 'test'
76
+
77
+
78
+ require "rails/railtie"
79
+ class Cells::Railtie < Rails::Railtie
80
+ initializer "cells.attach_router" do |app|
81
+ Cell::Rails.class_eval do
82
+ include app.routes.url_helpers
83
+ end
84
+
85
+ Cell::Base.url_helpers = app.routes.url_helpers
86
+ end
87
+
88
+ initializer "cells.add_load_path" do |app|
89
+ #ActiveSupport::Dependencies.load_paths << Rails.root.join(*%w[app cells])
90
+ end
91
+ end
@@ -40,7 +40,7 @@ module Cells
40
40
  # Example:
41
41
  # assert_equal "Banks kill planet!" cell(:news, :topic => :terror).latest_headline
42
42
  def cell(name, opts={}, &block)
43
- cell = Cell::Base.create_cell_for(@controller, name, opts)
43
+ cell = ::Cell::Base.create_cell_for(@controller, name, opts)
44
44
  cell.instance_eval &block if block_given?
45
45
  cell
46
46
  end
@@ -19,7 +19,7 @@ module Cells
19
19
  #
20
20
  # <%= @greeting %>
21
21
  def global_capture(name, &block)
22
- global_view = controller.instance_variable_get(:@template)
22
+ global_view = controller.parent_controller.view_context
23
23
  content = capture(&block)
24
24
  global_view.send(:instance_variable_set, :"@#{name}", content)
25
25
  end
@@ -38,8 +38,8 @@ module Cells
38
38
  #
39
39
  # <%= yield :greetings %>
40
40
  def global_content_for(name, content = nil, &block)
41
- # OMG.
42
- global_view = controller.instance_variable_get(:@template)
41
+ # OMG. that SUCKS.
42
+ global_view = controller.parent_controller.view_context
43
43
  ivar = :"@content_for_#{name}"
44
44
  content = capture(&block) if block_given?
45
45
  old_content = global_view.send(:'instance_variable_get', ivar)