yield and provide() inside template - ruby-on-rails

Could anyone give clear explanation on how provide() works inside the view ? I have read official documentation but what really bothers me is this, if I define in the beginning of a template
<% provide(:title, 'Help') %>
and then later I have this line of code
<%= yield :title %>
what really happens in the background ? I know that yield is supposed to call code block. What would be code block in this context?

provide stores a block of markup in an identifier for later use. In this case, 'Help' in the symbol :title. The provide is enclosed in <% %> to indicate it is executing this code and not printing out in the view.
yield in this case just spits that block back out. The yield is enclosed in <%= %> to indicate it is being printed out into the view.
Think of it as setting a variable and printing out a variable.
See: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide
for more information. Note that provide is really a wrapper for content_for so that's where the good stuff is in that link.

Related

Difference between using provide() and assigning a variable for page titles in Rails?

Could someone explain why it is preferred when embedding ruby for things like page titles to use
<% provide(:title, 'Help') %>
and then using
<%= yield :title %>
rather than jus using a variable:
<% title = 'Help' %>
<%= title %>
I'm assuming its to do with the fact that you can yield before you have called provide() but if that is the case why is it not possible to call the variable before defining it?
Thanks :)
If you want to simply render a variable in the view, the second method will do.
However, provide and yield offer a various ways to build the rendered content. For, example, you pass instance variable like #posts which you may already assign a variable after a complicated algorithm, which you will not do in a view template.
http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
provide (or content_for) are used to pass some elements from the view to the layout, so if you have layout:
<html>
<head>
<title><%= yield :title %></title>
</head>
<body>
<%= yield %>
</body>
</html>
Than in all the views, you can set the content for title with provide or content for. Local variables cannot do this, as they only live in a given view.
provide stores a block of markup in an identifier for later use. In this case, 'Help' in the symbol :title. The provide is enclosed in <% %> to indicate it is executing this code and not printing out in the view.
yield in this case just spits that block back out. The yield is enclosed in <%= %> to indicate it is being printed out into the view.
Think of it as setting a variable and printing out a variable.
See: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide for more information. Note that provide is really a wrapper for content_for so that's where the good stuff is in that link.

Document partial parameters in Rails

Is there any standard or emerging standard to document the parameters that can be passed into a Rails partial ?
When _my_partial.html.erb expects a title and an elements local var passed with render 'my_partial', title: t, elements: e, there must be a common way to document their names, expected types and roles, without reading the whole partial code. Something like RDoc or Tomdoc for methods and classes. Isn't there ?
Edit: I've found a post whose author advocates initializing parameters with <% var ||= 'default_val' %> in the first lines of the partial, which is indeed a safe practice and a kind of in-code doc. Is there really no comment/parameter-declaration solution for this ?
At the beginning of your partial, simply call all the variables that are referenced.
# _my_partial.html.erb
<% title %> <--- first line of file
<% elements[0] %>
<h3><%= title %></h3>
<% elements.each do |element| %>
<p> etc ... </p>
Reasons why this is good for your project:
it does not rely on comments or non-code files
any developer on the project can quickly find out which variables are needed by looking at the top of the file in question
by calling the variables, you ensure that a missing variable will result in an exception.
elements is called with square brackets because we also want it to blow up if it's not an enumerable, right?
The practice of using <% var ||= 'default_val' %> is actually unsafe because it allows bugs to hide. You want your code to immediately blow up the moment something isn't done right. And if these variables should be passed, then you want the code to blow up when they're not there.

How do I yield results from multiple statements in a Ruby block?

I have a pair of Rails helpers, one of which is meant to accept a block and the other of which just renders a button. Here are simplified versions the helper definitions:
def nav_wrapper(nav_at, id, css_class, &block)
"<ul class="complicated">\n #{yield} \n</ul>".html_safe
end
def nav_btn(nav_at, caption, id = caption.downcase.dasherize)
"Nav button codes goes here".html_safe
end
I'm trying to set things up such that I can do something like this:
<%= nav_wrapper(#nav_at, "Top Nav", "class") do %>
<%= nav_btn(#nav_at, "Foo", "id") %>
<%= nav_btn(#nav_at, "Bar", "id") %>
<%= nav_wrapper(#nav_at, "Sub Nav", "class") do %>
<%= nav_btn(#nav_at, "SubFoo", "id") %>
<%= nav_btn(#nav_at, "SubBar", "id") %>
<% end %>
<% end %>
But, the yield in the nav_wrapper method only picks up the last statement of each block. So in this example, I get the Top Nav wrapper, Foo and Bar are skipped, I get the Sub Nav wrapper (being the last statement in the outer nav_wrapper block), SubFoo is skipped, and I get SubBar (being the last statement in the inner nav_wrapper block).
I know that the reason for this behavior is that the block of code is implicitly returning the last evaluated value, but I know there are lots of template helpers that render all the interstitial lines (form_for, for example). Can someone help me figure out what the magic trick is here?
When a ERB template is compiled, it is converted to code which adds strings to a buffer. (Look at the source code for ERB to see what I mean.) Methods from Action View like form_for execute the block, and then retrieve the text in the ERB buffer.
Open up the lib/ruby/1.9.1/gems/1.9.1 folder, and look for actionpack. Open up whatever version of Action Pack you have, and go to lib/action_view/helpers/capture_helper.rb. There is a method in there called capture, which is used by form_for to execute a block and retrieve the text generated by ERB.
If you are writing a Rails helper, then presumably capture will be available to your code. If not, try include ActionView::Helpers::CaptureHelper.

Is this the correct way of rendering a yield content_for?

<%= yield(:title_box) if content_for?(:title_box) %>
Or is the if condition useless here?
The condition check is not needed as if nothing to render there is nothing will be outputted. See docs

Rendering a variable with erb

I've got the following problem: I have rhtml (html minced together with ruby inside <% %> and <%= %> tags) stored in a database which I want to render. The information is acquired through a query. I need to be able to evaluate the information I get from the database as though as it was normal content inside the .erb-file. What I currently have:
<% #mymods.each do |mod| %>
<%= render_text(mod["html"])%>
<% end %>
Where mod["html"] is the variable containing the rhtml-code and #mymods an array of objects from the query. I have currently no idea what function I should use (render_text does, of course, not work).
Help is greatly appreciated.
/TZer0
You can use the ERB object to render text without the text being in a file.
Just pass the text with the <%= %> tags. You could put something like the following as an application_helper function.
def render_erb_text(text, args={})
b = binding
template = ERB.new(text, 0, "%<>")
template.result(b)
end
And then in your template
<%= render_erb_text("<%= %w(hi how are you).join(' - ') %>")%>
You might also consider rendering the text in your controller as you can handle any render errors better there than during view evaluation.
Take a look at the ERB documentation for more information regarding variable binding etc.
I'm not familiar with the details of how this works under the covers, but there could be some serious risk in running this code on bad or malicious database data. Evaluating ruby code from user input or any un-vetted source should be done very carefully, if at all.

Resources