How does the yield magic work in ActionView?

后端 未结 3 1577
名媛妹妹
名媛妹妹 2021-01-14 14:55

I was looking at how content_for works and observed the block.call in the capture_erb_with_buffer method. It apparently magically writes to the buf

相关标签:
3条回答
  • 2021-01-14 15:15

    This little tiny method called execute in ActionView::Base explains it all. http://google.com/codesearch/p?hl=en#m8Vht-lU3vE/vendor/rails/actionpack/lib/action_view/base.rb&q=capture_helper.rb&d=5&l=337

      def execute(template)
        send(template.method, template.locals) do |*names|
          instance_variable_get "@content_for_#{names.first || 'layout'}"
        end
      end
    

    The do |*names|... end block is the one receiving the yield. You'll notice that the @content_for_#{names.first} matches up with the variable being set in the content_for process.

    It's called from AV::TemplateHandlers::Compilable in #render, and I would assume other places as well.

      def render(template)
        @view.send :execute, template
      end
    

    http://google.com/codesearch/p?hl=en#m8Vht-lU3vE/vendor/rails/actionpack/lib/action_view/template_handlers/compilable.rb&q=execute&exact_package=git://github.com/payalgupta/todo-list.git&sa=N&cd=17&ct=rc&l=28

    0 讨论(0)
  • 2021-01-14 15:26

    Simply:

    calling yield in a method executes code that was passed to the method via a block.

    For instance

    def my_method
      yield
    end
    
    my_method { puts "Hello" }
    my_method { puts "World" }
    

    these 5 lines will produce the following output to the screen

    Hello
    World
    

    See the following page for a nice discussion of yield in Ruby: Ruby Yield

    0 讨论(0)
  • 2021-01-14 15:40

    I'm not certain how yield functions on the ERB level, but I do know how it works when applied to layouts.

    Heres a sample layout.html.erb file:

    <html>
      <head>
        <title> <%= @title || 'Plain Title' %> </title>
        <%= yield :head %>
      </head>
    <body>
      <div id='menu'>
        <%= yield :menu %>
      </div>
      <div id='content'>
        <%= yield %>
      </div>
      <div id='footer'>
        <%= yield :footer %>
      </div>
    </body>
    

    I have defined 4 yields(:head, :menu, :footer, and default) and an instance variable @title.

    Now controller actions can render views that slot into these spots. Note that the view renders before the layout, so I can define a variable like @title in a view and have it defined in the layout.

    Sample views: An About page

    <% @title = 'About' %>
    <% content_for :menu do %>
      <%= link_to 'Back to Home', :action => :home %>
    <% end %>
    
    We rock!
    
    <% content_for :footer do %>
      An Illinois based company.
    <% end %>
    

    An Edit page

    <% @title = 'Edit' %>
    <% content_for :head do %>
      <style type='text/css'> .edit_form div {display:inline-block;} </style>
    <% end %>
    
    <% form_for :thing, :html => {:class => 'edit_form'} do |f| %>
       ...
    <% end %>
    

    You can mix and match what yields you want to put data in, and what occurs in the content_for :something will be inserted in the matching yield :something in the layout file.

    It even works for partials, a partial can insert its own content_for :something block which will get added with any other content_for calls.

    0 讨论(0)
提交回复
热议问题