Yield in Erb Without Rails

yield in ERB without rails

I don't think you can - Rails provides that infrastructure as part of actionpack.

What you may be able to do is take actionpack and add it into your script.

Alternatively you could roll a lightweight templating system yourself.

Alternatively alternatively use Rails or Merb or Sinatra.

How do I yield from an ERB code block without rendering it?

ERB has an internal buffer, which makes using blocks a bit more complicated, as you can see in your code example.

Rails provides a capture method, which allows you to capture a string inside this buffer and return it from a block.

So your helper would become the following:

def make_backwards
capture do
yield.reverse
end
end

Rails: about yield

Without any arguments, yield will render the template of the current controller/action. So if you're on the cars/show page, it will render views/cars/show.html.erb.

When you pass yield an argument, it lets you define content in your templates that you want to be rendered outside of that template. For example, if your cars/show page has a specific html snippet that you want to render in the footer, you could add the following to your show template and the car_general layout:

show.html.erb:

<% content_for :footer do %>
This content will show up in the footer section
<% end %>

layouts/car_general.html.erb

<%= yield :footer %>

The Rails Guide has a good section on using yield and content_for: http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield

The API documentation for content_for is helpful too and has some other examples to follow. Note that it's for Rails 3.1.1 , but this functionality has not changed much since 2.3, if at all and should still apply for 3.0.x and 3.1.x.

Is it possible to evaluate a yield statement in an html.erb template using the ERB library?

You need to pass the block you want to context wherein you create the Binding that you'll use with the template:

require 'erb'

def render(name)
TEMPLATE.result(binding)
end

render('evianpring') { 'blocks' }
# => "evianpring yields to the power of blocks!"

Let's dive into why this works. From the binding docs:

Objects of class Binding encapsulate the execution context at some
particular place in the code and retain this context for future use.
The variables, methods, value of self, and possibly an iterator block
that can be accessed in this context are all retained.

So what's available in execution context of the binding were creating here?

def render(name)
TEMPLATE.result(binding)
end
  • Any variables local to #render() like name
  • Any globals like TEMPLATE
  • Any block we pass to #render()

Hence if we want to use yield will need to pass a block to #render()

Ruby erb templates with yield

This problem is independent of ERB and is because of the way yield works. Yield expects to be called within the message body and expects a block to yield it. Let's take this example

# This is equivalent to 
# def func
# ERB.new('<%= yield %>').result(binding)
# end

def test_print
yield
end

If we call the method without a block

irb(main):038:0> test_print
LocalJumpError: no block given (yield)
from (irb):36:in `test_print'
from (irb):38
from /Users/agupta/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
irb(main):039:0>

If we call the method with block

irb(main):039:0> test_print { "hello world" }
=> "hello world"
irb(main):040:0>

In the latter case

ERB.new('<%= yield %>').result(binding) { 123 } 

Your block is not being passed as yield is outside the message body and you cannot do

irb(main):042:0> yield.tap { "hello world" }
LocalJumpError: no block given (yield)
from (irb):42
from /Users/agupta/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
irb(main):043:0>

What does “yield” do?

It tells Rails to put your view content to this block (which is called by yield) at that place in the layout file.

Checkout the rails guide to learn more about the ActionView.

https://guides.rubyonrails.org/action_view_overview.html

As pointed out by @Aleksei Matiushkin, yield is pure ruby, so you should also learn more about that in your own time.

Here's a (my) visual presentation to explain what happened on that line:

view.html.erb:

<p>Hello there!</p>
<p>I'm a content from view</p>

layout.html.erb:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<%= yield %>
</body>
</html>

Now the results will be like this:

<!DOCTYPE html>
<html>
<head>
</head>

<body>
<p>Hello there!</p>
<p>I'm a content from view</p>
</body>
</html>

Ruby method that yields one ERB template over another

I hope I got you question.
The following link should answer your question

How can I use views and layouts with Ruby and ERB (not Rails)?

Rails yield and content_for wieird behaviour, `yield :filter` only work if placed after default yield

Thanks everyone on reddit and @Ezhil i found a solution, this is what i did

what i did is i placed the content_for capture group before the render like this

#index.html.erb
<% content_for :filters do %>
<h2> Content for Filters </h2>
<% end %>

<%= render 'layouts/sub_header' do %>
<h2> Content For Yield </h2>
<% end %>

and in my partial

#layouts/_subheader.html.erb 
<div class="subheader">
<%= content_for :filters %>
<%= yield %>
</div>

It's because the block isn't executed until the first yield, so there's no content captured until that runs.



Related Topics



Leave a reply



Submit