Skip to content

Commit

Permalink
pass yielded arguments to block for ActionView::Base#render with :lay…
Browse files Browse the repository at this point in the history
…out [#847 state:resolved]

Signed-off-by: Joshua Peek <josh@joshpeek.com>
  • Loading branch information
ryanb authored and josh committed Aug 18, 2008
1 parent dbb0abf commit 38c7d73
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 28 deletions.
23 changes: 8 additions & 15 deletions actionpack/lib/action_view/base.rb
Expand Up @@ -246,12 +246,18 @@ def render(options = {}, local_assigns = {}, &block) #:nodoc:

if partial_layout = options.delete(:layout)
if block_given?
wrap_content_for_layout capture(&block) do
begin
@_proc_for_layout = block
concat(render(options.merge(:partial => partial_layout)))
ensure
@_proc_for_layout = nil
end
else
wrap_content_for_layout render(options) do
begin
original_content_for_layout, @content_for_layout = @content_for_layout, render(options)
render(options.merge(:partial => partial_layout))
ensure
@content_for_layout = original_content_for_layout
end
end
elsif options[:file]
Expand Down Expand Up @@ -367,13 +373,6 @@ def render_inline(text, local_assigns = {}, type = nil)
InlineTemplate.new(text, type).render(self, local_assigns)
end

def wrap_content_for_layout(content)
original_content_for_layout, @content_for_layout = @content_for_layout, content
yield
ensure
@content_for_layout = original_content_for_layout
end

# Evaluate the local assigns and pushes them to the view.
def evaluate_assigns
unless @assigns_added
Expand All @@ -392,11 +391,5 @@ def set_controller_content_type(content_type)
controller.response.content_type ||= content_type
end
end

def execute(method, local_assigns = {})
send(method, local_assigns) do |*names|
instance_variable_get "@content_for_#{names.first || 'layout'}"
end
end
end
end
38 changes: 36 additions & 2 deletions actionpack/lib/action_view/partials.rb
Expand Up @@ -68,7 +68,7 @@ module ActionView
#
# <%# app/views/users/_editor.html.erb &>
# <div id="editor">
# Deadline: $<%= user.deadline %>
# Deadline: <%= user.deadline %>
# <%= yield %>
# </div>
#
Expand All @@ -82,7 +82,7 @@ module ActionView
#
# Here's the editor:
# <div id="editor">
# Deadline: $<%= user.deadline %>
# Deadline: <%= user.deadline %>
# Name: <%= user.name %>
# </div>
#
Expand All @@ -101,6 +101,40 @@ module ActionView
# </div>
#
# As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
#
# If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
# an array to layout and treat it as an enumerable.
#
# <%# app/views/users/_user.html.erb &>
# <div class="user">
# Budget: $<%= user.budget %>
# <%= yield user %>
# </div>
#
# <%# app/views/users/index.html.erb &>
# <% render :layout => @users do |user| %>
# Title: <%= user.title %>
# <% end %>
#
# This will render the layout for each user and yield to the block, passing the user, each time.
#
# You can also yield multiple times in one layout and use block arguments to differentiate the sections.
#
# <%# app/views/users/_user.html.erb &>
# <div class="user">
# <%= yield user, :header %>
# Budget: $<%= user.budget %>
# <%= yield user, :footer %>
# </div>
#
# <%# app/views/users/index.html.erb &>
# <% render :layout => @users do |user, section| %>
# <%- case section when :header -%>
# Title: <%= user.title %>
# <%- when :footer -%>
# Deadline: <%= user.deadline %>
# <%- end -%>
# <% end %>
module Partials
extend ActiveSupport::Memoizable

Expand Down
9 changes: 8 additions & 1 deletion actionpack/lib/action_view/renderable.rb
Expand Up @@ -31,7 +31,14 @@ def render(view, local_assigns = {})

view.send(:evaluate_assigns)
view.send(:set_controller_content_type, mime_type) if respond_to?(:mime_type)
view.send(:execute, method_name(local_assigns), local_assigns)

view.send(method_name(local_assigns), local_assigns) do |*names|
if proc = view.instance_variable_get("@_proc_for_layout")
view.capture(*names, &proc)
else
view.instance_variable_get("@content_for_#{names.first || 'layout'}")
end
end
end

def method_name(local_assigns)
Expand Down
19 changes: 9 additions & 10 deletions actionpack/test/controller/layout_test.rb
Expand Up @@ -41,19 +41,19 @@ def setup

@request.host = "www.nextangle.com"
end

def test_application_layout_is_default_when_no_controller_match
@controller = ProductController.new
get :hello
assert_equal 'layout_test.rhtml hello.rhtml', @response.body
end

def test_controller_name_layout_name_match
@controller = ItemController.new
get :hello
assert_equal 'item.rhtml hello.rhtml', @response.body
end

def test_third_party_template_library_auto_discovers_layout
ThirdPartyTemplateLibraryController.view_paths.reload!
@controller = ThirdPartyTemplateLibraryController.new
Expand All @@ -63,14 +63,14 @@ def test_third_party_template_library_auto_discovers_layout
assert_response :success
assert_equal 'Mab', @response.body
end

def test_namespaced_controllers_auto_detect_layouts
@controller = ControllerNameSpace::NestedController.new
get :hello
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end

def test_namespaced_controllers_auto_detect_layouts
@controller = MultipleExtensions.new
get :hello
Expand Down Expand Up @@ -115,7 +115,7 @@ def test_add_regexp_to_exempt_from_layout

def test_rhtml_exempt_from_layout_status_should_prevent_layout_render
ActionController::Base.exempt_from_layout :rhtml

assert @controller.send!(:template_exempt_from_layout?, 'test.rhtml')
assert @controller.send!(:template_exempt_from_layout?, 'hello.rhtml')

Expand Down Expand Up @@ -156,19 +156,19 @@ def test_layout_set_when_using_default_layout
get :hello
assert_equal 'layouts/layout_test', @response.layout
end

def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
assert_equal 'layouts/item', @response.layout
end

def test_layout_set_when_using_render
@controller = SetsLayoutInRenderController.new
get :hello
assert_equal 'layouts/third_party_template_library', @response.layout
end

def test_layout_is_not_set_when_none_rendered
@controller = RendersNoLayoutController.new
get :hello
Expand Down Expand Up @@ -249,4 +249,3 @@ def test_symlinked_layout_is_rendered
assert_equal "layouts/symlinked/symlinked_layout", @response.layout
end
end

9 changes: 9 additions & 0 deletions actionpack/test/controller/new_render_test.rb
Expand Up @@ -435,6 +435,10 @@ def render_using_layout_around_block
render :action => "using_layout_around_block"
end

def render_using_layout_around_block_with_args
render :action => "using_layout_around_block_with_args"
end

def render_using_layout_around_block_in_main_layout_and_within_content_for_layout
render :action => "using_layout_around_block"
end
Expand Down Expand Up @@ -969,4 +973,9 @@ def test_using_layout_around_block_in_main_layout_and_within_content_for_layout
get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout
assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body
end

def test_using_layout_around_block_with_args
get :render_using_layout_around_block_with_args
assert_equal "Before\narg1arg2\nAfter", @response.body
end
end
@@ -0,0 +1,3 @@
Before
<%= yield 'arg1', 'arg2' %>
After
@@ -0,0 +1 @@
<% render(:layout => "layout_for_block_with_args") do |*args| %><%= args.join %><% end %>

1 comment on commit 38c7d73

@supaspoida
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome, excited to play with it. Thanks Ryan!

Please sign in to comment.